271 srb.reset(rhi->newShaderResourceBindings());
275 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
uniformBuffer.get(), kUniformBlockSize)});
280 auto getShader = [](
const QString& name) {
282 return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
289 QString vert = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.vert.qsb" : (mode ==
Anatomical) ?
":/anatomical.vert.qsb"
290 : (mode ==
Dipole) ?
":/dipole.vert.qsb"
292 :
":/standard.vert.qsb";
294 QString frag = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.frag.qsb" : (mode ==
Anatomical) ?
":/anatomical.frag.qsb"
295 : (mode ==
Dipole) ?
":/dipole.frag.qsb"
297 :
":/standard.frag.qsb";
299 QShader vS = getShader(vert);
300 QShader fS = getShader(frag);
302 if (!vS.isValid() || !fS.isValid()) {
303 qWarning() <<
"BrainRenderer: Could not load shaders for mode" << mode << vert << frag;
308 auto pipeline = std::unique_ptr<QRhiGraphicsPipeline>(rhi->newGraphicsPipeline());
310 QRhiGraphicsPipeline::TargetBlend blend;
313 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
314 blend.dstColor = QRhiGraphicsPipeline::One;
315 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
316 blend.dstAlpha = QRhiGraphicsPipeline::One;
317 }
else if (mode ==
Dipole) {
319 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
320 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
321 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
322 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
325 auto setup = [&](QRhiGraphicsPipeline* p, QRhiGraphicsPipeline::CullMode cull) {
326 p->setShaderStages({{QRhiShaderStage::Vertex, vS}, {QRhiShaderStage::Fragment, fS}});
328 QRhiVertexInputLayout il;
333 {21 *
sizeof(float), QRhiVertexInputBinding::PerInstance}
337 {0, 0, QRhiVertexInputAttribute::Float3, 0},
338 {0, 1, QRhiVertexInputAttribute::Float3, 3 *
sizeof(float)},
342 {1, 2, QRhiVertexInputAttribute::Float4, 0},
343 {1, 3, QRhiVertexInputAttribute::Float4, 4 *
sizeof(float)},
344 {1, 4, QRhiVertexInputAttribute::Float4, 8 *
sizeof(float)},
345 {1, 5, QRhiVertexInputAttribute::Float4, 12 *
sizeof(float)},
347 {1, 6, QRhiVertexInputAttribute::Float4, 16 *
sizeof(float)},
349 {1, 7, QRhiVertexInputAttribute::Float, 20 *
sizeof(float)}});
351 il.setBindings({{36}});
352 il.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
353 {0, 1, QRhiVertexInputAttribute::Float3, 12},
354 {0, 2, QRhiVertexInputAttribute::UNormByte4, 24},
355 {0, 3, QRhiVertexInputAttribute::UNormByte4, 28},
356 {0, 4, QRhiVertexInputAttribute::Float, 32}});
359 p->setVertexInputLayout(il);
360 p->setShaderResourceBindings(
srb.get());
361 p->setRenderPassDescriptor(rp);
362 p->setSampleCount(sampleCount);
363 p->setCullMode(cull);
365 p->setTargetBlends({blend});
366 p->setDepthTest(
true);
367 p->setDepthWrite(
false);
368 }
else if (mode ==
XRay) {
369 p->setTargetBlends({blend});
370 p->setDepthTest(
false);
371 p->setDepthWrite(
false);
372 }
else if (mode ==
Dipole) {
373 p->setTargetBlends({blend});
374 p->setCullMode(QRhiGraphicsPipeline::None);
375 p->setDepthTest(
true);
376 p->setDepthWrite(
false);
378 p->setDepthTest(
true);
379 p->setDepthWrite(
true);
381 p->setFlags(QRhiGraphicsPipeline::UsesScissor);
386 auto pipelineBack = std::unique_ptr<QRhiGraphicsPipeline>(rhi->newGraphicsPipeline());
387 setup(pipelineBack.get(), QRhiGraphicsPipeline::Front);
389 setup(pipeline.get(), QRhiGraphicsPipeline::Back);
392 setup(pipeline.get(), QRhiGraphicsPipeline::None);
495 QRhiResourceUpdateBatch* u,
505 QImage frame = tightlyPackedRgba(overlay->
frame());
509 auto& k = d->videoOverlay;
510 k.currentUniformOffset = 0;
513 if (!k.initialized) {
515 QFile vFile(QStringLiteral(
":/video_overlay.vert.qsb"));
516 QFile fFile(QStringLiteral(
":/video_overlay.frag.qsb"));
517 if (!vFile.open(QIODevice::ReadOnly) || !fFile.open(QIODevice::ReadOnly)) {
518 qWarning() <<
"BrainRenderer: failed to open video overlay shaders";
521 QShader vShader = QShader::fromSerialized(vFile.readAll());
522 QShader fShader = QShader::fromSerialized(fFile.readAll());
523 if (!vShader.isValid() || !fShader.isValid()) {
524 qWarning() <<
"BrainRenderer: invalid video overlay shaders";
529 k.uniformBufferOffsetAlignment = rhi->ubufAlignment();
530 k.uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
531 QRhiBuffer::UniformBuffer,
532 64 * k.uniformBufferOffsetAlignment));
533 k.uniformBuffer->create();
536 constexpr int kVbSize = 4 * 5 *
sizeof(float);
537 k.vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
538 QRhiBuffer::VertexBuffer, kVbSize));
539 k.vertexBuffer->create();
542 constexpr int kIbSize = 6 *
sizeof(quint32);
543 k.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable,
544 QRhiBuffer::IndexBuffer, kIbSize));
545 k.indexBuffer->create();
548 k.sampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear,
550 QRhiSampler::ClampToEdge,
551 QRhiSampler::ClampToEdge));
555 k.texture.reset(rhi->newTexture(QRhiTexture::RGBA8, frame.size()));
557 k.textureSize = frame.size();
560 k.srb.reset(rhi->newShaderResourceBindings());
561 k.srb->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
562 QRhiShaderResourceBinding::VertexStage |
563 QRhiShaderResourceBinding::FragmentStage,
564 k.uniformBuffer.get(), kUniformBlockSize),
565 QRhiShaderResourceBinding::sampledTexture(1,
566 QRhiShaderResourceBinding::FragmentStage,
567 k.texture.get(), k.sampler.get())});
571 QRhiGraphicsPipeline::TargetBlend blend;
573 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
574 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
575 blend.srcAlpha = QRhiGraphicsPipeline::One;
576 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
578 k.pipeline.reset(rhi->newGraphicsPipeline());
579 k.pipeline->setShaderStages({{QRhiShaderStage::Vertex, vShader},
580 {QRhiShaderStage::Fragment, fShader}});
581 QRhiVertexInputLayout il;
582 il.setBindings({{5 *
sizeof(float)}});
583 il.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
584 {0, 1, QRhiVertexInputAttribute::Float2, 3 *
sizeof(float)}});
585 k.pipeline->setVertexInputLayout(il);
586 k.pipeline->setShaderResourceBindings(k.srb.get());
587 k.pipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
588 k.pipeline->setSampleCount(d->rtClear->sampleCount());
589 k.pipeline->setCullMode(QRhiGraphicsPipeline::None);
590 k.pipeline->setTargetBlends({blend});
591 k.pipeline->setDepthTest(
false);
592 k.pipeline->setDepthWrite(
false);
593 k.pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
594 k.pipeline->create();
596 QFile dvFile(QStringLiteral(
":/video_decal.vert.qsb"));
597 QFile dfFile(QStringLiteral(
":/video_decal.frag.qsb"));
598 if (!dvFile.open(QIODevice::ReadOnly) || !dfFile.open(QIODevice::ReadOnly)) {
599 qWarning() <<
"BrainRenderer: failed to open video decal shaders";
602 QShader dvShader = QShader::fromSerialized(dvFile.readAll());
603 QShader dfShader = QShader::fromSerialized(dfFile.readAll());
604 if (!dvShader.isValid() || !dfShader.isValid()) {
605 qWarning() <<
"BrainRenderer: invalid video decal shaders";
609 QRhiGraphicsPipeline::TargetBlend decalBlend;
610 decalBlend.enable =
true;
611 decalBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
612 decalBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
613 decalBlend.srcAlpha = QRhiGraphicsPipeline::One;
614 decalBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
616 k.surfacePipeline.reset(rhi->newGraphicsPipeline());
617 k.surfacePipeline->setShaderStages({{QRhiShaderStage::Vertex, dvShader},
618 {QRhiShaderStage::Fragment, dfShader}});
619 QRhiVertexInputLayout dil;
620 dil.setBindings({{36}});
621 dil.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
622 {0, 1, QRhiVertexInputAttribute::Float3, 12},
623 {0, 2, QRhiVertexInputAttribute::UNormByte4, 24},
624 {0, 3, QRhiVertexInputAttribute::UNormByte4, 28},
625 {0, 4, QRhiVertexInputAttribute::Float, 32}});
626 k.surfacePipeline->setVertexInputLayout(dil);
627 k.surfacePipeline->setShaderResourceBindings(k.srb.get());
628 k.surfacePipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
629 k.surfacePipeline->setSampleCount(d->rtClear->sampleCount());
630 k.surfacePipeline->setCullMode(QRhiGraphicsPipeline::None);
631 k.surfacePipeline->setTargetBlends({decalBlend});
632 k.surfacePipeline->setDepthTest(
true);
633 k.surfacePipeline->setDepthWrite(
false);
634 k.surfacePipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
635 k.surfacePipeline->create();
637 k.initialized =
true;
641 if (overlay->
isDepthEnabled() && !k.depthInitialized && k.initialized) {
642 QFile ddvFile(QStringLiteral(
":/video_decal_depth.vert.qsb"));
643 QFile ddfFile(QStringLiteral(
":/video_decal_depth.frag.qsb"));
644 if (ddvFile.open(QIODevice::ReadOnly) && ddfFile.open(QIODevice::ReadOnly)) {
645 QShader ddvShader = QShader::fromSerialized(ddvFile.readAll());
646 QShader ddfShader = QShader::fromSerialized(ddfFile.readAll());
647 if (ddvShader.isValid() && ddfShader.isValid()) {
650 k.depthSampler.reset(rhi->newSampler(
651 QRhiSampler::Linear, QRhiSampler::Linear,
653 QRhiSampler::ClampToEdge,
654 QRhiSampler::ClampToEdge));
655 k.depthSampler->create();
658 k.depthTexture.reset(rhi->newTexture(
659 QRhiTexture::RGBA8, QSize(1, 1), 1, QRhiTexture::MipMapped));
660 k.depthTexture->create();
661 k.depthTextureSize = QSize(1, 1);
664 k.srbDepth.reset(rhi->newShaderResourceBindings());
665 k.srbDepth->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
666 QRhiShaderResourceBinding::VertexStage |
667 QRhiShaderResourceBinding::FragmentStage,
668 k.uniformBuffer.get(), kUniformBlockSize),
669 QRhiShaderResourceBinding::sampledTexture(1,
670 QRhiShaderResourceBinding::FragmentStage,
671 k.texture.get(), k.sampler.get()),
672 QRhiShaderResourceBinding::sampledTexture(2,
673 QRhiShaderResourceBinding::VertexStage |
674 QRhiShaderResourceBinding::FragmentStage,
675 k.depthTexture.get(), k.depthSampler.get())});
676 k.srbDepth->create();
678 QRhiGraphicsPipeline::TargetBlend depthDecalBlend;
679 depthDecalBlend.enable =
true;
680 depthDecalBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
681 depthDecalBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
682 depthDecalBlend.srcAlpha = QRhiGraphicsPipeline::One;
683 depthDecalBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
685 k.surfaceDepthPipeline.reset(rhi->newGraphicsPipeline());
686 k.surfaceDepthPipeline->setShaderStages({{QRhiShaderStage::Vertex, ddvShader},
687 {QRhiShaderStage::Fragment, ddfShader}});
688 QRhiVertexInputLayout ddil;
689 ddil.setBindings({{36}});
690 ddil.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
691 {0, 1, QRhiVertexInputAttribute::Float3, 12},
692 {0, 2, QRhiVertexInputAttribute::UNormByte4, 24},
693 {0, 3, QRhiVertexInputAttribute::UNormByte4, 28},
694 {0, 4, QRhiVertexInputAttribute::Float, 32}});
695 k.surfaceDepthPipeline->setVertexInputLayout(ddil);
696 k.surfaceDepthPipeline->setShaderResourceBindings(k.srbDepth.get());
697 k.surfaceDepthPipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
698 k.surfaceDepthPipeline->setSampleCount(d->rtClear->sampleCount());
699 k.surfaceDepthPipeline->setCullMode(QRhiGraphicsPipeline::None);
700 k.surfaceDepthPipeline->setTargetBlends({depthDecalBlend});
701 k.surfaceDepthPipeline->setDepthTest(
false);
702 k.surfaceDepthPipeline->setDepthWrite(
false);
703 k.surfaceDepthPipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
704 k.surfaceDepthPipeline->create();
705 k.depthInitialized =
true;
711 if (!k.indexUploaded) {
712 const quint32 idx[6] = {0, 1, 2, 2, 1, 3};
713 u->uploadStaticBuffer(k.indexBuffer.get(), idx);
714 k.indexUploaded =
true;
718 if (frame.size() != k.textureSize) {
719 k.texture.reset(rhi->newTexture(QRhiTexture::RGBA8, frame.size()));
721 k.textureSize = frame.size();
723 k.srb->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
724 QRhiShaderResourceBinding::VertexStage |
725 QRhiShaderResourceBinding::FragmentStage,
726 k.uniformBuffer.get(), kUniformBlockSize),
727 QRhiShaderResourceBinding::sampledTexture(1,
728 QRhiShaderResourceBinding::FragmentStage,
729 k.texture.get(), k.sampler.get())});
732 if (k.srbDepth && k.depthTexture) {
733 k.srbDepth->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
734 QRhiShaderResourceBinding::VertexStage |
735 QRhiShaderResourceBinding::FragmentStage,
736 k.uniformBuffer.get(), kUniformBlockSize),
737 QRhiShaderResourceBinding::sampledTexture(1,
738 QRhiShaderResourceBinding::FragmentStage,
739 k.texture.get(), k.sampler.get()),
740 QRhiShaderResourceBinding::sampledTexture(2,
741 QRhiShaderResourceBinding::VertexStage |
742 QRhiShaderResourceBinding::FragmentStage,
743 k.depthTexture.get(), k.depthSampler.get())});
744 k.srbDepth->create();
746 k.uploadedFrameGen = std::numeric_limits<quint64>::max();
749 QRhiTextureSubresourceUploadDescription sub(frame);
750 QRhiTextureUploadDescription desc({0, 0, sub});
751 u->uploadTexture(k.texture.get(), desc);
757 QImage depthFrame = tightlyPackedRgba(overlay->
depthFrame());
758 if (!depthFrame.isNull()) {
759 if (depthFrame.size() != k.depthTextureSize) {
760 k.depthTexture.reset(rhi->newTexture(
761 QRhiTexture::RGBA8, depthFrame.size(), 1, QRhiTexture::MipMapped));
762 k.depthTexture->create();
763 k.depthTextureSize = depthFrame.size();
765 k.srbDepth->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
766 QRhiShaderResourceBinding::VertexStage |
767 QRhiShaderResourceBinding::FragmentStage,
768 k.uniformBuffer.get(), kUniformBlockSize),
769 QRhiShaderResourceBinding::sampledTexture(1,
770 QRhiShaderResourceBinding::FragmentStage,
771 k.texture.get(), k.sampler.get()),
772 QRhiShaderResourceBinding::sampledTexture(2,
773 QRhiShaderResourceBinding::VertexStage |
774 QRhiShaderResourceBinding::FragmentStage,
775 k.depthTexture.get(), k.depthSampler.get())});
776 k.srbDepth->create();
777 k.uploadedDepthFrameGen = std::numeric_limits<quint64>::max();
780 QRhiTextureSubresourceUploadDescription depthSub(depthFrame);
781 QRhiTextureUploadDescription depthDesc({0, 0, depthSub});
782 u->uploadTexture(k.depthTexture.get(), depthDesc);
783 u->generateMips(k.depthTexture.get());
799 auto& k = d->videoOverlay;
802 if (k.uniformBufferOffsetAlignment <= 0)
805 const int uniformOffset = k.currentUniformOffset;
806 k.currentUniformOffset += k.uniformBufferOffsetAlignment;
807 if (
static_cast<quint32
>(uniformOffset + kUniformBlockSize) > k.uniformBuffer->size())
809 QRhiResourceUpdateBatch* u = rhi->nextResourceUpdateBatch();
817 const QImage& frm = overlay->
frame();
818 const float aspect = (frm.height() > 0)
819 ?
static_cast<float>(frm.width()) / frm.height()
821 const float halfW = 0.5f * sizeM;
822 const float halfH = (aspect > 0.0f) ? halfW / aspect : halfW;
824 QVector3D viewDir = centre - data.
cameraPos;
825 if (viewDir.lengthSquared() < 1e-12f)
826 viewDir = QVector3D(0, 0, -1);
831 const QVector3D hint = overlay->
upHint();
833 if (hint.lengthSquared() > 1e-8f) {
835 up = (hint - QVector3D::dotProduct(hint, viewDir) * viewDir).normalized();
836 if (up.lengthSquared() < 1e-8f)
837 up = QVector3D(0.0f, 0.0f, 1.0f);
839 QVector3D worldUp(0.0f, 0.0f, 1.0f);
840 if (std::abs(QVector3D::dotProduct(viewDir, worldUp)) > 0.95f)
841 worldUp = QVector3D(0.0f, 1.0f, 0.0f);
842 up = QVector3D::crossProduct(
843 QVector3D::crossProduct(viewDir, worldUp), viewDir)
846 QVector3D right = QVector3D::crossProduct(viewDir, up).normalized();
848 const QVector3D c00 = centre - right * halfW - up * halfH;
849 const QVector3D c10 = centre + right * halfW - up * halfH;
850 const QVector3D c01 = centre - right * halfW + up * halfH;
851 const QVector3D c11 = centre + right * halfW + up * halfH;
853 const float verts[4 * 5] = {
854 c00.x(), c00.y(), c00.z(), 0.0f, 1.0f,
855 c10.x(), c10.y(), c10.z(), 1.0f, 1.0f,
856 c01.x(), c01.y(), c01.z(), 0.0f, 0.0f,
857 c11.x(), c11.y(), c11.z(), 1.0f, 0.0f};
858 u->updateDynamicBuffer(k.vertexBuffer.get(), 0,
sizeof(verts), verts);
864 float borderColor[4];
866 float pad0, pad1, pad2;
868 memcpy(ub.mvp, data.
mvp.constData(), 64);
869 ub.borderColor[0] = 0.0f;
870 ub.borderColor[1] = 0.85f;
871 ub.borderColor[2] = 1.0f;
872 ub.borderColor[3] = 1.0f;
873 ub.opacity = overlay->
opacity();
874 ub.pad0 = ub.pad1 = ub.pad2 = 0.0f;
875 u->updateDynamicBuffer(k.uniformBuffer.get(), uniformOffset,
sizeof(ub), &ub);
877 cb->resourceUpdate(u);
880 cb->setViewport(toViewport(data));
881 cb->setScissor(toScissor(data));
882 cb->setGraphicsPipeline(k.pipeline.get());
883 const QRhiCommandBuffer::DynamicOffset srbOffset = {0, uint32_t(uniformOffset)};
884 cb->setShaderResources(k.srb.get(), 1, &srbOffset);
885 const QRhiCommandBuffer::VertexInput vbuf(k.vertexBuffer.get(), 0);
886 cb->setVertexInput(0, 1, &vbuf, k.indexBuffer.get(), 0, QRhiCommandBuffer::IndexUInt32);
901 auto& k = d->videoOverlay;
902 if (!k.initialized || !k.surfacePipeline)
904 if (k.uniformBufferOffsetAlignment <= 0)
907 const int uniformOffset = k.currentUniformOffset;
908 k.currentUniformOffset += k.uniformBufferOffsetAlignment;
909 if (
static_cast<quint32
>(uniformOffset + kUniformBlockSize) > k.uniformBuffer->size())
911 QRhiResourceUpdateBatch* u = rhi->nextResourceUpdateBatch();
915 if (localNormal.lengthSquared() < 1e-12f) {
916 localNormal = QVector3D(0.0f, 0.0f, 1.0f);
918 localNormal.normalize();
923 QVector3D referenceUp = overlay->
upHint();
924 if (referenceUp.lengthSquared() < 1e-6f) {
926 referenceUp = QVector3D(0.0f, 0.0f, 1.0f);
927 if (std::abs(QVector3D::dotProduct(localNormal, referenceUp)) > 0.95f) {
928 referenceUp = QVector3D(1.0f, 0.0f, 0.0f);
932 referenceUp = (referenceUp - QVector3D::dotProduct(referenceUp, localNormal) * localNormal);
933 if (referenceUp.lengthSquared() < 1e-12f) {
934 referenceUp = QVector3D(0.0f, 0.0f, 1.0f);
936 referenceUp.normalize();
938 const QVector3D axisV = referenceUp;
939 const QVector3D axisU = QVector3D::crossProduct(axisV, localNormal).normalized();
949 float focusAndSize[4];
950 float axisUAndOpacity[4];
951 float axisVAndOffset[4];
952 float axisNAndDepth[4];
953 float cameraPosAndFacing[4];
954 float borderColor[4];
955 float depthParams[4];
957 memcpy(ub.mvp, data.
mvp.constData(), 64);
962 ub.axisUAndOpacity[0] = axisU.x();
963 ub.axisUAndOpacity[1] = axisU.y();
964 ub.axisUAndOpacity[2] = axisU.z();
965 ub.axisUAndOpacity[3] = overlay->
opacity();
966 ub.axisVAndOffset[0] = axisV.x();
967 ub.axisVAndOffset[1] = axisV.y();
968 ub.axisVAndOffset[2] = axisV.z();
969 ub.axisVAndOffset[3] = 0.00045f;
970 ub.axisNAndDepth[0] = localNormal.x();
971 ub.axisNAndDepth[1] = localNormal.y();
972 ub.axisNAndDepth[2] = localNormal.z();
973 ub.axisNAndDepth[3] = std::max(0.015f, overlay->
sizeMeters() * 0.45f);
974 ub.cameraPosAndFacing[0] = data.
cameraPos.x();
975 ub.cameraPosAndFacing[1] = data.
cameraPos.y();
976 ub.cameraPosAndFacing[2] = data.
cameraPos.z();
977 const QImage& decalFrame = overlay->
frame();
978 ub.cameraPosAndFacing[3] = (decalFrame.height() > 0)
979 ?
static_cast<float>(decalFrame.width()) / decalFrame.height()
981 ub.borderColor[0] = 0.0f;
982 ub.borderColor[1] = 0.85f;
983 ub.borderColor[2] = 1.0f;
984 ub.borderColor[3] = 1.0f;
985 ub.depthParams[0] = useDepth ? overlay->
depthScale() : 0.0f;
986 ub.depthParams[1] = useDepth ?
static_cast<float>(overlay->
depthSteps()) : 0.0f;
987 ub.depthParams[2] = useDepth ? 1.0f : 0.0f;
988 ub.depthParams[3] = 0.0f;
989 u->updateDynamicBuffer(k.uniformBuffer.get(), uniformOffset,
sizeof(ub), &ub);
990 cb->resourceUpdate(u);
992 cb->setViewport(toViewport(data));
993 cb->setScissor(toScissor(data));
995 cb->setGraphicsPipeline(k.surfaceDepthPipeline.get());
996 const QRhiCommandBuffer::DynamicOffset srbOffset = {0, uint32_t(uniformOffset)};
997 cb->setShaderResources(k.srbDepth.get(), 1, &srbOffset);
999 cb->setGraphicsPipeline(k.surfacePipeline.get());
1000 const QRhiCommandBuffer::DynamicOffset srbOffset = {0, uint32_t(uniformOffset)};
1001 cb->setShaderResources(k.srb.get(), 1, &srbOffset);
1003 const QRhiCommandBuffer::VertexInput vbuf(surface->
vertexBuffer(), 0);
1004 cb->setVertexInput(0, 1, &vbuf, surface->
indexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
1013 QRhiResourceUpdateBatch* u,
1022 auto& k = d->sliceRes;
1023 auto& sliceSlot = k.sliceSlots[slotIndex];
1026 sliceSlot.visible =
false;
1030 const QImage& img = slice->
image();
1032 sliceSlot.visible =
false;
1037 if (!k.initialized) {
1038 QFile vFile(QStringLiteral(
":/slice.vert.qsb"));
1039 QFile fFile(QStringLiteral(
":/slice.frag.qsb"));
1040 if (!vFile.open(QIODevice::ReadOnly) || !fFile.open(QIODevice::ReadOnly)) {
1041 qWarning() <<
"BrainRenderer: failed to open MRI slice shaders";
1044 QShader vShader = QShader::fromSerialized(vFile.readAll());
1045 QShader fShader = QShader::fromSerialized(fFile.readAll());
1046 if (!vShader.isValid() || !fShader.isValid()) {
1047 qWarning() <<
"BrainRenderer: invalid MRI slice shaders";
1051 k.uniformBufferOffsetAlignment = rhi->ubufAlignment();
1052 k.uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1053 QRhiBuffer::UniformBuffer,
1054 64 * k.uniformBufferOffsetAlignment));
1055 k.uniformBuffer->create();
1058 k.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable,
1059 QRhiBuffer::IndexBuffer,
1060 6 *
sizeof(quint32)));
1061 k.indexBuffer->create();
1063 k.sampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear,
1065 QRhiSampler::ClampToEdge,
1066 QRhiSampler::ClampToEdge));
1067 k.sampler->create();
1071 constexpr int kVbSize = 4 * 5 *
sizeof(float);
1072 k.vertexBuffer[i].reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1073 QRhiBuffer::VertexBuffer, kVbSize));
1074 k.vertexBuffer[i]->create();
1077 k.sliceSlots[i].texture.reset(rhi->newTexture(QRhiTexture::R8, QSize(1, 1)));
1078 k.sliceSlots[i].texture->create();
1079 k.sliceSlots[i].textureSize = QSize(1, 1);
1081 k.srb[i].reset(rhi->newShaderResourceBindings());
1082 k.srb[i]->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
1083 QRhiShaderResourceBinding::VertexStage |
1084 QRhiShaderResourceBinding::FragmentStage,
1085 k.uniformBuffer.get(), kUniformBlockSize),
1086 QRhiShaderResourceBinding::sampledTexture(1,
1087 QRhiShaderResourceBinding::FragmentStage,
1088 k.sliceSlots[i].texture.get(), k.sampler.get())});
1093 QRhiGraphicsPipeline::TargetBlend blend;
1094 blend.enable =
true;
1095 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
1096 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1097 blend.srcAlpha = QRhiGraphicsPipeline::One;
1098 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1100 k.pipeline.reset(rhi->newGraphicsPipeline());
1101 k.pipeline->setShaderStages({{QRhiShaderStage::Vertex, vShader},
1102 {QRhiShaderStage::Fragment, fShader}});
1103 QRhiVertexInputLayout il;
1104 il.setBindings({{5 *
sizeof(float)}});
1105 il.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
1106 {0, 1, QRhiVertexInputAttribute::Float2, 3 *
sizeof(float)}});
1107 k.pipeline->setVertexInputLayout(il);
1108 k.pipeline->setShaderResourceBindings(k.srb[0].get());
1109 k.pipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
1110 k.pipeline->setSampleCount(d->rtClear->sampleCount());
1111 k.pipeline->setCullMode(QRhiGraphicsPipeline::None);
1112 k.pipeline->setTargetBlends({blend});
1113 k.pipeline->setDepthTest(
true);
1114 k.pipeline->setDepthWrite(
false);
1115 k.pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
1116 k.pipeline->create();
1118 k.initialized =
true;
1122 if (!k.indexUploaded) {
1123 const quint32 idx[6] = {0, 1, 2, 2, 1, 3};
1124 u->uploadStaticBuffer(k.indexBuffer.get(), idx);
1125 k.indexUploaded =
true;
1130 QImage gray = img.convertToFormat(QImage::Format_Grayscale8);
1131 const QSize imgSize = gray.size();
1133 if (imgSize != sliceSlot.textureSize) {
1134 sliceSlot.texture.reset(rhi->newTexture(QRhiTexture::R8, imgSize));
1135 sliceSlot.texture->create();
1136 sliceSlot.textureSize = imgSize;
1138 k.srb[slotIndex]->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
1139 QRhiShaderResourceBinding::VertexStage |
1140 QRhiShaderResourceBinding::FragmentStage,
1141 k.uniformBuffer.get(), kUniformBlockSize),
1142 QRhiShaderResourceBinding::sampledTexture(1,
1143 QRhiShaderResourceBinding::FragmentStage,
1144 sliceSlot.texture.get(), k.sampler.get())});
1145 k.srb[slotIndex]->create();
1150 const qsizetype tightStride = qsizetype(gray.width());
1152 texData.resize(gray.height() * tightStride);
1153 for (
int y = 0; y < gray.height(); ++y) {
1154 memcpy(texData.data() + y * tightStride,
1155 gray.constScanLine(y),
1156 size_t(tightStride));
1158 QRhiTextureSubresourceUploadDescription sub(texData.constData(), texData.size());
1159 sub.setSourceSize(imgSize);
1160 QRhiTextureUploadDescription desc({0, 0, sub});
1161 u->uploadTexture(sliceSlot.texture.get(), desc);
1166 u->updateDynamicBuffer(k.vertexBuffer[slotIndex].get(), 0,
1167 static_cast<quint32
>(sliceSlot.vertices.size() *
sizeof(
float)),
1168 sliceSlot.vertices.constData());
1170 sliceSlot.opacity = slice->
opacity();
1173 sliceSlot.visible =
true;
1634 const QVector<BrainSurface*>& surfaces,
1635 const QString& groupName)
1637 auto& group = d->mergedGroups[groupName];
1641 if (group.surfaces.size() != surfaces.size()) {
1644 for (
int i = 0; i < surfaces.size(); ++i) {
1645 if (group.surfaces[i] != surfaces[i]) {
1655 if (!group.dirty && group.indexCount > 0) {
1657 bool anyChanged =
false;
1658 if (group.surfaceGenerations.size() != surfaces.size()) {
1661 for (
int i = 0; i < surfaces.size(); ++i) {
1662 if (surfaces[i] && surfaces[i]->vertexGeneration() != group.surfaceGenerations[i]) {
1678 for (
int si = 0; si < surfaces.size(); ++si)
1680 totalVerts += surfaces[si]->vertexDataRef().size();
1681 if (totalVerts != group.totalVertexCount) {
1685 float brainId = 0.0f;
1686 float nonBrainId = 100.0f;
1687 group.surfaceGenerations.resize(surfaces.size());
1689 for (
int si = 0; si < surfaces.size(); ++si) {
1697 const float id = isBrain ? brainId : nonBrainId;
1699 const int n = srcVerts.size();
1700 memcpy(dst, srcVerts.constData(), n *
sizeof(
VertexData));
1701 for (
int j = 0; j < n; ++j)
1702 dst[j].surfaceId =
id;
1708 group.gpuVertexDirty =
true;
1714 group.surfaces = surfaces;
1715 group.indexCount = 0;
1716 group.totalVertexCount = 0;
1717 group.dirty =
false;
1722 int totalIndices = 0;
1723 for (
int si = 0; si < surfaces.size(); ++si) {
1726 totalVerts += surfaces[si]->vertexDataRef().size();
1727 totalIndices += surfaces[si]->indexDataRef().size();
1730 group.indexCount = totalIndices;
1731 if (group.indexCount == 0)
1734 const quint32 vbufSize = totalVerts *
sizeof(
VertexData);
1735 const quint32 ibufSize = totalIndices *
sizeof(uint32_t);
1738 if (!group.vertexBuffer || group.vertexBuffer->size() < vbufSize) {
1739 group.vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1740 QRhiBuffer::VertexBuffer, vbufSize));
1741 group.vertexBuffer->create();
1743 if (!group.indexBuffer || group.indexBuffer->size() < ibufSize) {
1744 group.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1745 QRhiBuffer::IndexBuffer, ibufSize));
1746 group.indexBuffer->create();
1750 group.vertexRaw.resize(vbufSize);
1751 group.indexRaw.resize(ibufSize);
1752 group.surfaceGenerations.resize(surfaces.size());
1755 uint32_t* iDst =
reinterpret_cast<uint32_t*
>(group.indexRaw.data());
1756 float brainId = 0.0f;
1757 float nonBrainId = 100.0f;
1758 uint32_t vertexOffset = 0;
1759 for (
int si = 0; si < surfaces.size(); ++si) {
1768 const float id = isBrain ? brainId : nonBrainId;
1771 const int nv = srcVerts.size();
1772 const int ni = srcIdx.size();
1775 memcpy(vDst, srcVerts.constData(), nv *
sizeof(
VertexData));
1776 for (
int j = 0; j < nv; ++j)
1777 vDst[j].surfaceId =
id;
1781 const uint32_t* srcI = srcIdx.constData();
1782 for (
int j = 0; j < ni; ++j)
1783 iDst[j] = srcI[j] + vertexOffset;
1791 group.totalVertexCount = totalVerts;
1792 group.gpuVertexDirty =
true;
1793 group.gpuIndexDirty =
true;