58static constexpr int kNumShaderModes = 6;
62 void createResources(QRhi *rhi, QRhiRenderPassDescriptor *rp,
int sampleCount);
64 std::unique_ptr<QRhiShaderResourceBindings>
srb;
67 std::array<std::unique_ptr<QRhiGraphicsPipeline>, kNumShaderModes>
pipelines{};
83 std::unique_ptr<QRhiTextureRenderTarget>
rtClear;
85 std::unique_ptr<QRhiRenderPassDescriptor>
rpClear;
117 return QRhiViewport(d.viewportX, d.viewportY, d.viewportW, d.viewportH);
131 constexpr int kUniformSlotCount = 8192;
132 constexpr int kUniformBlockSize = 256;
135 constexpr int kOffsetMVP = 0;
136 constexpr int kOffsetCameraPos = 64;
137 constexpr int kOffsetSelected = 76;
138 constexpr int kOffsetLightDir = 80;
139 constexpr int kOffsetTissueType = 92;
140 constexpr int kOffsetLighting = 96;
141 constexpr int kOffsetOverlayMode = 100;
142 constexpr int kOffsetSelectedSurfaceId = 104;
152 : d(std::make_unique<
Impl>())
164 if (d->resourcesDirty) {
165 d->createResources(rhi, rp, sampleCount);
184 srb.reset(rhi->newShaderResourceBindings());
189 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
uniformBuffer.get(),kUniformBlockSize)
195 auto getShader = [](
const QString &name) {
197 return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
204 QString vert = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.vert.qsb" :
205 (mode ==
Anatomical) ?
":/anatomical.vert.qsb" :
206 (mode ==
Dipole) ?
":/dipole.vert.qsb" :
207 (mode ==
ShowNormals) ?
":/shownormals.vert.qsb" :
":/standard.vert.qsb";
209 QString frag = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.frag.qsb" :
210 (mode ==
Anatomical) ?
":/anatomical.frag.qsb" :
211 (mode ==
Dipole) ?
":/dipole.frag.qsb" :
212 (mode ==
ShowNormals) ?
":/shownormals.frag.qsb" :
":/standard.frag.qsb";
214 QShader vS = getShader(vert);
215 QShader fS = getShader(frag);
217 if (!vS.isValid() || !fS.isValid()) {
218 qWarning() <<
"BrainRenderer: Could not load shaders for mode" << mode << vert << frag;
223 auto pipeline = std::unique_ptr<QRhiGraphicsPipeline>(rhi->newGraphicsPipeline());
225 QRhiGraphicsPipeline::TargetBlend blend;
228 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
229 blend.dstColor = QRhiGraphicsPipeline::One;
230 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
231 blend.dstAlpha = QRhiGraphicsPipeline::One;
232 }
else if (mode ==
Dipole) {
234 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
235 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
236 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
237 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
240 auto setup = [&](QRhiGraphicsPipeline* p, QRhiGraphicsPipeline::CullMode cull) {
241 p->setShaderStages({{ QRhiShaderStage::Vertex, vS }, { QRhiShaderStage::Fragment, fS }});
243 QRhiVertexInputLayout il;
247 { 6 *
sizeof(float) },
248 { 21 *
sizeof(float), QRhiVertexInputBinding::PerInstance }
253 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
254 { 0, 1, QRhiVertexInputAttribute::Float3, 3 *
sizeof(float) },
258 { 1, 2, QRhiVertexInputAttribute::Float4, 0 },
259 { 1, 3, QRhiVertexInputAttribute::Float4, 4 *
sizeof(float) },
260 { 1, 4, QRhiVertexInputAttribute::Float4, 8 *
sizeof(float) },
261 { 1, 5, QRhiVertexInputAttribute::Float4, 12 *
sizeof(float) },
263 { 1, 6, QRhiVertexInputAttribute::Float4, 16 *
sizeof(float) },
265 { 1, 7, QRhiVertexInputAttribute::Float, 20 *
sizeof(float) }
268 il.setBindings({{ 36 }});
269 il.setAttributes({{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
270 { 0, 1, QRhiVertexInputAttribute::Float3, 12 },
271 { 0, 2, QRhiVertexInputAttribute::UNormByte4, 24 },
272 { 0, 3, QRhiVertexInputAttribute::UNormByte4, 28 },
273 { 0, 4, QRhiVertexInputAttribute::Float, 32 }});
276 p->setVertexInputLayout(il);
277 p->setShaderResourceBindings(
srb.get());
278 p->setRenderPassDescriptor(rp);
279 p->setSampleCount(sampleCount);
280 p->setCullMode(cull);
282 p->setTargetBlends({blend});
283 p->setDepthTest(
true);
284 p->setDepthWrite(
false);
285 }
else if (mode ==
XRay) {
286 p->setTargetBlends({blend});
287 p->setDepthTest(
false);
288 p->setDepthWrite(
false);
289 }
else if (mode ==
Dipole) {
290 p->setTargetBlends({blend});
291 p->setCullMode(QRhiGraphicsPipeline::None);
292 p->setDepthTest(
true);
293 p->setDepthWrite(
false);
295 p->setDepthTest(
true);
296 p->setDepthWrite(
true);
298 p->setFlags(QRhiGraphicsPipeline::UsesScissor);
303 auto pipelineBack = std::unique_ptr<QRhiGraphicsPipeline>(rhi->newGraphicsPipeline());
304 setup(pipelineBack.get(), QRhiGraphicsPipeline::Front);
306 setup(pipeline.get(), QRhiGraphicsPipeline::Back);
309 setup(pipeline.get(), QRhiGraphicsPipeline::None);
325 if (d->rtClear && d->rtSize == pixelSize && d->rtColorTex == colorTex)
328 d->rtSize = pixelSize;
329 d->rtColorTex = colorTex;
332 d->dsBuffer.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize));
333 d->dsBuffer->create();
335 QRhiColorAttachment colorAtt(colorTex);
336 QRhiTextureRenderTargetDescription desc(colorAtt);
337 desc.setDepthStencilBuffer(d->dsBuffer.get());
340 d->rtClear.reset(rhi->newTextureRenderTarget(desc));
341 d->rpClear.reset(d->rtClear->newCompatibleRenderPassDescriptor());
342 d->rtClear->setRenderPassDescriptor(d->rpClear.get());
343 d->rtClear->create();
346 d->rtPreserve.reset(rhi->newTextureRenderTarget(desc,
347 QRhiTextureRenderTarget::PreserveColorContents
348 | QRhiTextureRenderTarget::PreserveDepthStencilContents));
349 d->rpPreserve.reset(d->rtPreserve->newCompatibleRenderPassDescriptor());
350 d->rtPreserve->setRenderPassDescriptor(d->rpPreserve.get());
351 d->rtPreserve->create();
358 return d->rtClear.get();
363 return d->rtPreserve.get();
370 d->currentUniformOffset = 0;
372 auto *rt = d->rtClear.get();
373 cb->beginPass(rt, QColor(0, 0, 0), { 1.0f, 0 });
374 const int w = rt->pixelSize().width();
375 const int h = rt->pixelSize().height();
376 cb->setViewport(QRhiViewport(0, 0, w, h));
377 cb->setScissor(QRhiScissor(0, 0, w, h));
391 auto *rt = d->rtPreserve.get();
392 cb->beginPass(rt, QColor(0, 0, 0), { 1.0f, 0 });
393 const int w = rt->pixelSize().width();
394 const int h = rt->pixelSize().height();
395 cb->setViewport(QRhiViewport(0, 0, w, h));
396 cb->setScissor(QRhiScissor(0, 0, w, h));
410 if (!surface || !surface->
isVisible())
return;
412 auto *pipeline = d->pipelines[mode].get();
413 if (!pipeline)
return;
420 QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
423 int offset = d->currentUniformOffset;
424 d->currentUniformOffset += d->uniformBufferOffsetAlignment;
425 if (d->currentUniformOffset >= d->uniformBuffer->size()) {
426 qWarning(
"BrainRenderer: uniform buffer overflow (%d / %d bytes) — too many surfaces. Some draws will be skipped.",
427 d->currentUniformOffset, (
int)d->uniformBuffer->size());
435 float selected = surface->
isSelected() ? 1.0f : 0.0f;
436#ifndef __EMSCRIPTEN__
450 float lightingEnabled;
452 float selectedSurfaceId;
454 memcpy(ub.mvp, data.
mvp.constData(), 64);
455 memcpy(ub.cameraPos, &data.
cameraPos, 12);
456 ub.isSelected = selected;
457 memcpy(ub.lightDir, &data.
lightDir, 12);
458 ub.tissueType =
static_cast<float>(surface->
tissueType());
461 ub.selectedSurfaceId = -1.0f;
463 u->updateDynamicBuffer(d->uniformBuffer.get(), offset,
sizeof(ub), &ub);
465 cb->resourceUpdate(u);
470 cb->setViewport(toViewport(data));
471 cb->setScissor(toScissor(data));
473 auto draw = [&](QRhiGraphicsPipeline *p) {
474 cb->setGraphicsPipeline(p);
476 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(offset) };
477 cb->setShaderResources(d->srb.get(), 1, &srbOffset);
478 const QRhiCommandBuffer::VertexInput vbuf(surface->
vertexBuffer(), 0);
479 cb->setVertexInput(0, 1, &vbuf, surface->
indexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
496 if (!surface || !surface->
isVisible())
return -1;
498 int offset = d->currentUniformOffset;
499 d->currentUniformOffset += d->uniformBufferOffsetAlignment;
500 if (d->currentUniformOffset >= d->uniformBuffer->size()) {
501 qWarning(
"BrainRenderer: uniform buffer overflow in prepareSurfaceDraw");
505 float selected = surface->
isSelected() ? 1.0f : 0.0f;
517 float lightingEnabled;
519 float selectedSurfaceId;
521 memcpy(ub.mvp, data.
mvp.constData(), 64);
522 memcpy(ub.cameraPos, &data.
cameraPos, 12);
523 ub.isSelected = selected;
524 memcpy(ub.lightDir, &data.
lightDir, 12);
525 ub.tissueType =
static_cast<float>(surface->
tissueType());
528 ub.selectedSurfaceId = -1.0f;
530 u->updateDynamicBuffer(d->uniformBuffer.get(), offset,
sizeof(ub), &ub);
541 if (!surface || uniformOffset < 0)
return;
543 auto *pipeline = d->pipelines[mode].get();
544 if (!pipeline)
return;
546 auto draw = [&](QRhiGraphicsPipeline *p) {
547 cb->setGraphicsPipeline(p);
548 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(uniformOffset) };
549 cb->setShaderResources(d->srb.get(), 1, &srbOffset);
550 const QRhiCommandBuffer::VertexInput vbuf(surface->
vertexBuffer(), 0);
551 cb->setVertexInput(0, 1, &vbuf, surface->
indexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
568 auto *pipeline = d->pipelines[
Dipole].get();
569 if (!pipeline)
return;
571 QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
575 int offset = d->currentUniformOffset;
576 d->currentUniformOffset += d->uniformBufferOffsetAlignment;
577 if (d->currentUniformOffset >= d->uniformBuffer->size()) {
578 qWarning(
"BrainRenderer: uniform buffer overflow in renderDipoles");
589 float lightingEnabled;
591 memcpy(dub.mvp, data.
mvp.constData(), 64);
592 memcpy(dub.cameraPos, &data.
cameraPos, 12);
594 memcpy(dub.lightDir, &data.
lightDir, 12);
597 u->updateDynamicBuffer(d->uniformBuffer.get(), offset,
sizeof(dub), &dub);
599 cb->resourceUpdate(u);
602 cb->setViewport(toViewport(data));
603 cb->setScissor(toScissor(data));
605 cb->setGraphicsPipeline(pipeline);
607 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(offset) };
608 cb->setShaderResources(d->srb.get(), 1, &srbOffset);
610 const QRhiCommandBuffer::VertexInput bindings[2] = {
611 QRhiCommandBuffer::VertexInput(dipoles->
vertexBuffer(), 0),
615 cb->setVertexInput(0, 2, bindings, dipoles->
indexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
626 auto *pipeline = d->pipelines[
Dipole].get();
627 if (!pipeline)
return;
631 QRhiResourceUpdateBatch *uNodes = rhi->nextResourceUpdateBatch();
634 int offset = d->currentUniformOffset;
635 d->currentUniformOffset += d->uniformBufferOffsetAlignment;
636 if (d->currentUniformOffset >= d->uniformBuffer->size()) {
637 qWarning(
"BrainRenderer: uniform buffer overflow in renderNetwork (nodes)");
641 struct {
float mvp[16];
float cp[3];
float _p0;
float ld[3];
float _p1;
float le; } nub;
642 memcpy(nub.mvp, data.
mvp.constData(), 64);
643 memcpy(nub.cp, &data.
cameraPos, 12); nub._p0 = 0.0f;
644 memcpy(nub.ld, &data.
lightDir, 12); nub._p1 = 0.0f;
646 uNodes->updateDynamicBuffer(d->uniformBuffer.get(), offset,
sizeof(nub), &nub);
648 cb->resourceUpdate(uNodes);
649 cb->setViewport(toViewport(data));
650 cb->setScissor(toScissor(data));
652 cb->setGraphicsPipeline(pipeline);
654 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(offset) };
655 cb->setShaderResources(d->srb.get(), 1, &srbOffset);
657 const QRhiCommandBuffer::VertexInput nodeBindings[2] = {
662 cb->setVertexInput(0, 2, nodeBindings, network->
nodeIndexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
668 QRhiResourceUpdateBatch *uEdges = rhi->nextResourceUpdateBatch();
671 int offset = d->currentUniformOffset;
672 d->currentUniformOffset += d->uniformBufferOffsetAlignment;
673 if (d->currentUniformOffset >= d->uniformBuffer->size()) {
674 qWarning(
"BrainRenderer: uniform buffer overflow in renderNetwork (edges)");
678 struct {
float mvp[16];
float cp[3];
float _p0;
float ld[3];
float _p1;
float le; } eub;
679 memcpy(eub.mvp, data.
mvp.constData(), 64);
680 memcpy(eub.cp, &data.
cameraPos, 12); eub._p0 = 0.0f;
681 memcpy(eub.ld, &data.
lightDir, 12); eub._p1 = 0.0f;
683 uEdges->updateDynamicBuffer(d->uniformBuffer.get(), offset,
sizeof(eub), &eub);
685 cb->resourceUpdate(uEdges);
686 cb->setViewport(toViewport(data));
687 cb->setScissor(toScissor(data));
689 cb->setGraphicsPipeline(pipeline);
691 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(offset) };
692 cb->setShaderResources(d->srb.get(), 1, &srbOffset);
694 const QRhiCommandBuffer::VertexInput edgeBindings[2] = {
699 cb->setVertexInput(0, 2, edgeBindings, network->
edgeIndexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
716 const QVector<BrainSurface*> &surfaces,
717 const QString &groupName)
719 auto &group = d->mergedGroups[groupName];
723 if (group.surfaces.size() != surfaces.size()) {
726 for (
int i = 0; i < surfaces.size(); ++i) {
727 if (group.surfaces[i] != surfaces[i]) {
737 if (!group.dirty && group.indexCount > 0) {
739 bool anyChanged =
false;
740 if (group.surfaceGenerations.size() != surfaces.size()) {
743 for (
int i = 0; i < surfaces.size(); ++i) {
744 if (surfaces[i] && surfaces[i]->vertexGeneration() != group.surfaceGenerations[i]) {
760 for (
int si = 0; si < surfaces.size(); ++si)
761 if (surfaces[si]) totalVerts += surfaces[si]->vertexDataRef().size();
762 if (totalVerts != group.totalVertexCount) {
766 float brainId = 0.0f;
767 float nonBrainId = 100.0f;
768 group.surfaceGenerations.resize(surfaces.size());
770 for (
int si = 0; si < surfaces.size(); ++si) {
772 if (!surf) { brainId += 1.0f; nonBrainId += 1.0f;
continue; }
774 const float id = isBrain ? brainId : nonBrainId;
776 const int n = srcVerts.size();
777 memcpy(dst, srcVerts.constData(), n *
sizeof(
VertexData));
778 for (
int j = 0; j < n; ++j)
779 dst[j].surfaceId =
id;
785 group.gpuVertexDirty =
true;
791 group.surfaces = surfaces;
792 group.indexCount = 0;
793 group.totalVertexCount = 0;
799 int totalIndices = 0;
800 for (
int si = 0; si < surfaces.size(); ++si) {
801 if (!surfaces[si])
continue;
802 totalVerts += surfaces[si]->vertexDataRef().size();
803 totalIndices += surfaces[si]->indexDataRef().size();
806 group.indexCount = totalIndices;
807 if (group.indexCount == 0)
return;
809 const quint32 vbufSize = totalVerts *
sizeof(
VertexData);
810 const quint32 ibufSize = totalIndices *
sizeof(uint32_t);
813 if (!group.vertexBuffer || group.vertexBuffer->size() < vbufSize) {
814 group.vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
815 QRhiBuffer::VertexBuffer, vbufSize));
816 group.vertexBuffer->create();
818 if (!group.indexBuffer || group.indexBuffer->size() < ibufSize) {
819 group.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
820 QRhiBuffer::IndexBuffer, ibufSize));
821 group.indexBuffer->create();
825 group.vertexRaw.resize(vbufSize);
826 group.indexRaw.resize(ibufSize);
827 group.surfaceGenerations.resize(surfaces.size());
830 uint32_t *iDst =
reinterpret_cast<uint32_t*
>(group.indexRaw.data());
831 float brainId = 0.0f;
832 float nonBrainId = 100.0f;
833 uint32_t vertexOffset = 0;
834 for (
int si = 0; si < surfaces.size(); ++si) {
836 if (!surf) { brainId += 1.0f; nonBrainId += 1.0f;
continue; }
839 const float id = isBrain ? brainId : nonBrainId;
842 const int nv = srcVerts.size();
843 const int ni = srcIdx.size();
846 memcpy(vDst, srcVerts.constData(), nv *
sizeof(
VertexData));
847 for (
int j = 0; j < nv; ++j)
848 vDst[j].surfaceId =
id;
852 const uint32_t *srcI = srcIdx.constData();
853 for (
int j = 0; j < ni; ++j)
854 iDst[j] = srcI[j] + vertexOffset;
862 group.totalVertexCount = totalVerts;
863 group.gpuVertexDirty =
true;
864 group.gpuIndexDirty =
true;
871 auto it = d->mergedGroups.find(groupName);
872 if (it != d->mergedGroups.end()) {
873 it->second.dirty =
true;
881 auto it = d->mergedGroups.find(groupName);
882 return it != d->mergedGroups.end() && it->second.indexCount > 0;
889 const QString &groupName)
891 auto it = d->mergedGroups.find(groupName);
892 if (it == d->mergedGroups.end())
return;
893 auto &group = it->second;
895 if (group.indexCount == 0)
return;
897 auto *pipeline = d->pipelines[mode].get();
898 if (!pipeline)
return;
902 float selectedSurfaceId = -1.0f;
903 for (
int i = 0; i < group.surfaces.size(); ++i) {
904 if (group.surfaces[i] && group.surfaces[i]->isSelected()) {
905 if (group.surfaces[i]->selectedRegionId() == -1
906 && group.surfaces[i]->selectedVertexStart() < 0) {
908 selectedSurfaceId =
static_cast<float>(isBrain ? i : 100 + i);
914 QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
920 if (group.gpuVertexDirty) {
921 u->updateDynamicBuffer(group.vertexBuffer.get(), 0, group.vertexRaw.size(), group.vertexRaw.constData());
922 group.gpuVertexDirty =
false;
924 if (group.gpuIndexDirty) {
925 u->updateDynamicBuffer(group.indexBuffer.get(), 0, group.indexRaw.size(), group.indexRaw.constData());
926 group.gpuIndexDirty =
false;
929 int offset = d->currentUniformOffset;
930 d->currentUniformOffset += d->uniformBufferOffsetAlignment;
931 if (d->currentUniformOffset >= d->uniformBuffer->size()) {
932 qWarning(
"BrainRenderer: uniform buffer overflow in drawMergedSurfaces");
944 float lightingEnabled;
946 float selectedSurfaceId;
948 memcpy(ub.mvp, data.
mvp.constData(), 64);
949 memcpy(ub.cameraPos, &data.
cameraPos, 12);
950 ub.isSelected = 0.0f;
951 memcpy(ub.lightDir, &data.
lightDir, 12);
952 ub.tissueType = (!group.surfaces.isEmpty() && group.surfaces.first())
953 ?
static_cast<float>(group.surfaces.first()->tissueType()) : 0.0f;
956 ub.selectedSurfaceId = selectedSurfaceId;
958 u->updateDynamicBuffer(d->uniformBuffer.get(), offset,
sizeof(ub), &ub);
960 cb->resourceUpdate(u);
962 cb->setViewport(toViewport(data));
963 cb->setScissor(toScissor(data));
965 auto draw = [&](QRhiGraphicsPipeline *p) {
966 cb->setGraphicsPipeline(p);
967 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(offset) };
968 cb->setShaderResources(d->srb.get(), 1, &srbOffset);
969 const QRhiCommandBuffer::VertexInput vbuf(group.vertexBuffer.get(), 0);
970 cb->setVertexInput(0, 1, &vbuf, group.indexBuffer.get(), 0, QRhiCommandBuffer::IndexUInt32);
971 cb->drawIndexed(group.indexCount);
BrainRenderer class declaration.
BrainSurface class declaration.
DipoleObject class declaration.
NetworkObject class declaration.
Interleaved vertex attributes (position, normal, color, curvature) for brain surface GPU upload.
Renderable cortical surface mesh with per-vertex color, curvature data, and GPU buffer management.
uint32_t indexCount() const
const QVector< VertexData > & vertexDataRef() const
Const-ref access to CPU-side vertex data (used by merged rendering).
TissueType tissueType() const
int selectedVertexStart() const
QRhiBuffer * vertexBuffer() const
quint64 vertexGeneration() const
Monotonically increasing counter bumped whenever vertex data changes.
QRhiBuffer * indexBuffer() const
const QVector< uint32_t > & indexDataRef() const
Const-ref access to CPU-side index data (used by merged rendering).
int selectedRegionId() const
Renderable dipole arrow set with instanced GPU rendering for QRhi.
QRhiBuffer * instanceBuffer() const
int instanceCount() const
void updateBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
QRhiBuffer * indexBuffer() const
QRhiBuffer * vertexBuffer() const
Renderable network visualization for QRhi.
QRhiBuffer * nodeIndexBuffer() const
int nodeInstanceCount() const
int edgeIndexCount() const
void updateNodeBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
QRhiBuffer * edgeIndexBuffer() const
QRhiBuffer * nodeInstanceBuffer() const
int edgeInstanceCount() const
QRhiBuffer * edgeVertexBuffer() const
int nodeIndexCount() const
QRhiBuffer * nodeVertexBuffer() const
void updateEdgeBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
QRhiBuffer * edgeInstanceBuffer() const
std::unique_ptr< QRhiRenderBuffer > dsBuffer
std::array< std::unique_ptr< QRhiGraphicsPipeline >, kNumShaderModes > pipelinesBackColor
std::map< QString, MergedGroup > mergedGroups
std::unique_ptr< QRhiRenderPassDescriptor > rpPreserve
int uniformBufferOffsetAlignment
std::unique_ptr< QRhiShaderResourceBindings > srb
std::unique_ptr< QRhiTextureRenderTarget > rtPreserve
std::unique_ptr< QRhiBuffer > uniformBuffer
void createResources(QRhi *rhi, QRhiRenderPassDescriptor *rp, int sampleCount)
std::unique_ptr< QRhiTextureRenderTarget > rtClear
std::unique_ptr< QRhiRenderPassDescriptor > rpClear
std::array< std::unique_ptr< QRhiGraphicsPipeline >, kNumShaderModes > pipelines
QVector< BrainSurface * > surfaces
std::unique_ptr< QRhiBuffer > vertexBuffer
std::unique_ptr< QRhiBuffer > indexBuffer
QVector< quint64 > surfaceGenerations
void endPass(QRhiCommandBuffer *cb)
static constexpr ShaderMode Anatomical
bool hasMergedContent(const QString &groupName) const
QRhiRenderTarget * rtClear() const
static constexpr ShaderMode Holographic
void renderNetwork(QRhiCommandBuffer *cb, QRhi *rhi, const SceneData &data, NetworkObject *network)
void beginPreservingPass(QRhiCommandBuffer *cb)
static constexpr ShaderMode ShowNormals
void issueSurfaceDraw(QRhiCommandBuffer *cb, BrainSurface *surface, ShaderMode mode, int uniformOffset)
QRhiRenderTarget * rtPreserve() const
void drawMergedSurfaces(QRhiCommandBuffer *cb, QRhi *rhi, const SceneData &data, ShaderMode mode, const QString &groupName=QStringLiteral("default"))
int prepareSurfaceDraw(QRhiResourceUpdateBatch *u, const SceneData &data, BrainSurface *surface)
void renderSurface(QRhiCommandBuffer *cb, QRhi *rhi, const SceneData &data, BrainSurface *surface, ShaderMode mode)
void updateSceneUniforms(QRhi *rhi, const SceneData &data)
static constexpr ShaderMode Dipole
void ensureRenderTargets(QRhi *rhi, QRhiTexture *colorTex, const QSize &pixelSize)
void initialize(QRhi *rhi, QRhiRenderPassDescriptor *rp, int sampleCount)
void beginFrame(QRhiCommandBuffer *cb)
static constexpr ShaderMode Standard
void invalidateMergedGroup(const QString &groupName=QStringLiteral("default"))
void prepareMergedSurfaces(QRhi *rhi, QRhiResourceUpdateBatch *u, const QVector< BrainSurface * > &surfaces, const QString &groupName=QStringLiteral("default"))
void renderDipoles(QRhiCommandBuffer *cb, QRhi *rhi, const SceneData &data, DipoleObject *dipoles)
static constexpr ShaderMode XRay
Aggregated GPU resources and render state for the 3-D brain visualization scene.