415 srb.reset(
rhi->newShaderResourceBindings());
417 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
uniformBuffer.get(), kUniformBlockSize)});
422 auto getShader = [](
const QString& name) {
424 return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
431 QString vert = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.vert.qsb" : (mode ==
Anatomical) ?
":/anatomical.vert.qsb"
432 : (mode ==
Dipole) ?
":/dipole.vert.qsb"
434 :
":/standard.vert.qsb";
436 QString frag = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.frag.qsb" : (mode ==
Anatomical) ?
":/anatomical.frag.qsb"
437 : (mode ==
Dipole) ?
":/dipole.frag.qsb"
439 :
":/standard.frag.qsb";
441 QShader vS = getShader(vert);
442 QShader fS = getShader(frag);
444 if (!vS.isValid() || !fS.isValid()) {
445 qWarning() <<
"BrainRenderer: Could not load shaders for mode" << mode << vert << frag;
450 auto pipeline = std::unique_ptr<QRhiGraphicsPipeline>(
rhi->newGraphicsPipeline());
452 QRhiGraphicsPipeline::TargetBlend blend;
455 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
456 blend.dstColor = QRhiGraphicsPipeline::One;
457 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
458 blend.dstAlpha = QRhiGraphicsPipeline::One;
459 }
else if (mode ==
Dipole) {
461 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
462 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
463 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
464 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
467 auto setup = [&](QRhiGraphicsPipeline* p, QRhiGraphicsPipeline::CullMode cull) {
468 p->setShaderStages({{QRhiShaderStage::Vertex, vS}, {QRhiShaderStage::Fragment, fS}});
470 p->setVertexInputLayout(mode ==
Dipole ? instancedInputLayout() : surfaceInputLayout());
471 p->setShaderResourceBindings(
srb.get());
472 p->setRenderPassDescriptor(rp);
473 p->setSampleCount(sampleCount);
474 p->setCullMode(cull);
476 p->setTargetBlends({blend});
477 p->setDepthTest(
true);
478 p->setDepthWrite(
false);
479 }
else if (mode ==
XRay) {
480 p->setTargetBlends({blend});
481 p->setDepthTest(
false);
482 p->setDepthWrite(
false);
483 }
else if (mode ==
Dipole) {
484 p->setTargetBlends({blend});
485 p->setCullMode(QRhiGraphicsPipeline::None);
486 p->setDepthTest(
true);
487 p->setDepthWrite(
false);
489 p->setDepthTest(
true);
490 p->setDepthWrite(
true);
492 p->setFlags(QRhiGraphicsPipeline::UsesScissor);
497 auto pipelineBack = std::unique_ptr<QRhiGraphicsPipeline>(
rhi->newGraphicsPipeline());
498 setup(pipelineBack.get(), QRhiGraphicsPipeline::Front);
500 setup(pipeline.get(), QRhiGraphicsPipeline::Back);
503 setup(pipeline.get(), QRhiGraphicsPipeline::None);
509 const QShader evS = getShader(QStringLiteral(
":/electrode.vert.qsb"));
510 const QShader efS = getShader(QStringLiteral(
":/electrode.frag.qsb"));
512 if (evS.isValid() && efS.isValid()) {
514 electrodePipeline->setShaderStages({{QRhiShaderStage::Vertex, evS}, {QRhiShaderStage::Fragment, efS}});
525 qWarning() <<
"BrainRenderer: Could not load the electrode shaders";
660 QRhiResourceUpdateBatch* u,
667 if (!rhi || !u || d->insidePass(
"prepareVideoOverlay"))
670 QImage frame = tightlyPackedRgba(overlay->
frame());
674 auto& k = d->videoOverlay;
677 if (!k.initialized) {
679 QFile vFile(QStringLiteral(
":/video_overlay.vert.qsb"));
680 QFile fFile(QStringLiteral(
":/video_overlay.frag.qsb"));
681 if (!vFile.open(QIODevice::ReadOnly) || !fFile.open(QIODevice::ReadOnly)) {
682 qWarning() <<
"BrainRenderer: failed to open video overlay shaders";
685 QShader vShader = QShader::fromSerialized(vFile.readAll());
686 QShader fShader = QShader::fromSerialized(fFile.readAll());
687 if (!vShader.isValid() || !fShader.isValid()) {
688 qWarning() <<
"BrainRenderer: invalid video overlay shaders";
693 k.uniformSlotStride = rhi->ubufAligned(kUniformBlockSize);
694 k.uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
695 QRhiBuffer::UniformBuffer,
696 kVideoOverlaySlots * k.uniformSlotStride));
697 k.uniformBuffer->create();
700 k.vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
701 QRhiBuffer::VertexBuffer, kVideoOverlaySlots * kVideoQuadBytes));
702 k.vertexBuffer->create();
705 constexpr int kIbSize = 6 *
sizeof(quint32);
706 k.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable,
707 QRhiBuffer::IndexBuffer, kIbSize));
708 k.indexBuffer->create();
711 k.sampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear,
713 QRhiSampler::ClampToEdge,
714 QRhiSampler::ClampToEdge));
718 k.texture.reset(rhi->newTexture(QRhiTexture::RGBA8, frame.size()));
720 k.textureSize = frame.size();
723 k.srb.reset(rhi->newShaderResourceBindings());
724 k.srb->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
725 QRhiShaderResourceBinding::VertexStage |
726 QRhiShaderResourceBinding::FragmentStage,
727 k.uniformBuffer.get(), kUniformBlockSize),
728 QRhiShaderResourceBinding::sampledTexture(1,
729 QRhiShaderResourceBinding::FragmentStage,
730 k.texture.get(), k.sampler.get())});
734 QRhiGraphicsPipeline::TargetBlend blend;
736 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
737 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
738 blend.srcAlpha = QRhiGraphicsPipeline::One;
739 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
741 k.pipeline.reset(rhi->newGraphicsPipeline());
742 k.pipeline->setShaderStages({{QRhiShaderStage::Vertex, vShader},
743 {QRhiShaderStage::Fragment, fShader}});
744 QRhiVertexInputLayout il;
745 il.setBindings({{5 *
sizeof(float)}});
746 il.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
747 {0, 1, QRhiVertexInputAttribute::Float2, 3 *
sizeof(float)}});
748 k.pipeline->setVertexInputLayout(il);
749 k.pipeline->setShaderResourceBindings(k.srb.get());
750 k.pipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
751 k.pipeline->setSampleCount(d->rtClear->sampleCount());
752 k.pipeline->setCullMode(QRhiGraphicsPipeline::None);
753 k.pipeline->setTargetBlends({blend});
754 k.pipeline->setDepthTest(
false);
755 k.pipeline->setDepthWrite(
false);
756 k.pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
757 k.pipeline->create();
759 QFile dvFile(QStringLiteral(
":/video_decal.vert.qsb"));
760 QFile dfFile(QStringLiteral(
":/video_decal.frag.qsb"));
761 if (!dvFile.open(QIODevice::ReadOnly) || !dfFile.open(QIODevice::ReadOnly)) {
762 qWarning() <<
"BrainRenderer: failed to open video decal shaders";
765 QShader dvShader = QShader::fromSerialized(dvFile.readAll());
766 QShader dfShader = QShader::fromSerialized(dfFile.readAll());
767 if (!dvShader.isValid() || !dfShader.isValid()) {
768 qWarning() <<
"BrainRenderer: invalid video decal shaders";
772 QRhiGraphicsPipeline::TargetBlend decalBlend;
773 decalBlend.enable =
true;
774 decalBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
775 decalBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
776 decalBlend.srcAlpha = QRhiGraphicsPipeline::One;
777 decalBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
779 k.surfacePipeline.reset(rhi->newGraphicsPipeline());
780 k.surfacePipeline->setShaderStages({{QRhiShaderStage::Vertex, dvShader},
781 {QRhiShaderStage::Fragment, dfShader}});
782 k.surfacePipeline->setVertexInputLayout(surfaceInputLayout());
783 k.surfacePipeline->setShaderResourceBindings(k.srb.get());
784 k.surfacePipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
785 k.surfacePipeline->setSampleCount(d->rtClear->sampleCount());
786 k.surfacePipeline->setCullMode(QRhiGraphicsPipeline::None);
787 k.surfacePipeline->setTargetBlends({decalBlend});
788 k.surfacePipeline->setDepthTest(
true);
789 k.surfacePipeline->setDepthWrite(
false);
790 k.surfacePipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
791 k.surfacePipeline->create();
793 k.initialized =
true;
797 if (overlay->
isDepthEnabled() && !k.depthInitialized && k.initialized) {
798 QFile ddvFile(QStringLiteral(
":/video_decal_depth.vert.qsb"));
799 QFile ddfFile(QStringLiteral(
":/video_decal_depth.frag.qsb"));
800 if (ddvFile.open(QIODevice::ReadOnly) && ddfFile.open(QIODevice::ReadOnly)) {
801 QShader ddvShader = QShader::fromSerialized(ddvFile.readAll());
802 QShader ddfShader = QShader::fromSerialized(ddfFile.readAll());
803 if (ddvShader.isValid() && ddfShader.isValid()) {
806 k.depthSampler.reset(rhi->newSampler(
807 QRhiSampler::Linear, QRhiSampler::Linear,
809 QRhiSampler::ClampToEdge,
810 QRhiSampler::ClampToEdge));
811 k.depthSampler->create();
814 k.depthTexture.reset(rhi->newTexture(
815 QRhiTexture::RGBA8, QSize(1, 1), 1, QRhiTexture::MipMapped));
816 k.depthTexture->create();
817 k.depthTextureSize = QSize(1, 1);
820 k.srbDepth.reset(rhi->newShaderResourceBindings());
821 k.srbDepth->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
822 QRhiShaderResourceBinding::VertexStage |
823 QRhiShaderResourceBinding::FragmentStage,
824 k.uniformBuffer.get(), kUniformBlockSize),
825 QRhiShaderResourceBinding::sampledTexture(1,
826 QRhiShaderResourceBinding::FragmentStage,
827 k.texture.get(), k.sampler.get()),
828 QRhiShaderResourceBinding::sampledTexture(2,
829 QRhiShaderResourceBinding::VertexStage |
830 QRhiShaderResourceBinding::FragmentStage,
831 k.depthTexture.get(), k.depthSampler.get())});
832 k.srbDepth->create();
834 QRhiGraphicsPipeline::TargetBlend depthDecalBlend;
835 depthDecalBlend.enable =
true;
836 depthDecalBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
837 depthDecalBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
838 depthDecalBlend.srcAlpha = QRhiGraphicsPipeline::One;
839 depthDecalBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
841 k.surfaceDepthPipeline.reset(rhi->newGraphicsPipeline());
842 k.surfaceDepthPipeline->setShaderStages({{QRhiShaderStage::Vertex, ddvShader},
843 {QRhiShaderStage::Fragment, ddfShader}});
844 k.surfaceDepthPipeline->setVertexInputLayout(surfaceInputLayout());
845 k.surfaceDepthPipeline->setShaderResourceBindings(k.srbDepth.get());
846 k.surfaceDepthPipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
847 k.surfaceDepthPipeline->setSampleCount(d->rtClear->sampleCount());
848 k.surfaceDepthPipeline->setCullMode(QRhiGraphicsPipeline::None);
849 k.surfaceDepthPipeline->setTargetBlends({depthDecalBlend});
850 k.surfaceDepthPipeline->setDepthTest(
false);
851 k.surfaceDepthPipeline->setDepthWrite(
false);
852 k.surfaceDepthPipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
853 k.surfaceDepthPipeline->create();
854 k.depthInitialized =
true;
860 if (!k.indexUploaded) {
861 const quint32 idx[6] = {0, 1, 2, 2, 1, 3};
862 u->uploadStaticBuffer(k.indexBuffer.get(), idx);
863 k.indexUploaded =
true;
867 if (frame.size() != k.textureSize) {
868 k.texture.reset(rhi->newTexture(QRhiTexture::RGBA8, frame.size()));
870 k.textureSize = frame.size();
872 k.srb->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
873 QRhiShaderResourceBinding::VertexStage |
874 QRhiShaderResourceBinding::FragmentStage,
875 k.uniformBuffer.get(), kUniformBlockSize),
876 QRhiShaderResourceBinding::sampledTexture(1,
877 QRhiShaderResourceBinding::FragmentStage,
878 k.texture.get(), k.sampler.get())});
881 if (k.srbDepth && k.depthTexture) {
882 k.srbDepth->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
883 QRhiShaderResourceBinding::VertexStage |
884 QRhiShaderResourceBinding::FragmentStage,
885 k.uniformBuffer.get(), kUniformBlockSize),
886 QRhiShaderResourceBinding::sampledTexture(1,
887 QRhiShaderResourceBinding::FragmentStage,
888 k.texture.get(), k.sampler.get()),
889 QRhiShaderResourceBinding::sampledTexture(2,
890 QRhiShaderResourceBinding::VertexStage |
891 QRhiShaderResourceBinding::FragmentStage,
892 k.depthTexture.get(), k.depthSampler.get())});
893 k.srbDepth->create();
895 k.uploadedFrameGen = std::numeric_limits<quint64>::max();
898 QRhiTextureSubresourceUploadDescription sub(frame);
899 QRhiTextureUploadDescription desc({0, 0, sub});
900 u->uploadTexture(k.texture.get(), desc);
906 QImage depthFrame = tightlyPackedRgba(overlay->
depthFrame());
907 if (!depthFrame.isNull()) {
908 if (depthFrame.size() != k.depthTextureSize) {
909 k.depthTexture.reset(rhi->newTexture(
910 QRhiTexture::RGBA8, depthFrame.size(), 1, QRhiTexture::MipMapped));
911 k.depthTexture->create();
912 k.depthTextureSize = depthFrame.size();
914 k.srbDepth->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
915 QRhiShaderResourceBinding::VertexStage |
916 QRhiShaderResourceBinding::FragmentStage,
917 k.uniformBuffer.get(), kUniformBlockSize),
918 QRhiShaderResourceBinding::sampledTexture(1,
919 QRhiShaderResourceBinding::FragmentStage,
920 k.texture.get(), k.sampler.get()),
921 QRhiShaderResourceBinding::sampledTexture(2,
922 QRhiShaderResourceBinding::VertexStage |
923 QRhiShaderResourceBinding::FragmentStage,
924 k.depthTexture.get(), k.depthSampler.get())});
925 k.srbDepth->create();
926 k.uploadedDepthFrameGen = std::numeric_limits<quint64>::max();
929 QRhiTextureSubresourceUploadDescription depthSub(depthFrame);
930 QRhiTextureUploadDescription depthDesc({0, 0, depthSub});
931 u->uploadTexture(k.depthTexture.get(), depthDesc);
932 u->generateMips(k.depthTexture.get());
947 auto& k = d->videoOverlay;
948 if (!k.initialized || (surface && !k.surfacePipeline))
950 if (d->insidePass(
"prepareVideoOverlayDraw"))
952 if (surface && !d->uploadSurface(surface, u))
955 const int uniformOffset = k.currentUniformOffset;
956 if (
static_cast<quint32
>(uniformOffset + kUniformBlockSize) > k.uniformBuffer->size())
958 k.currentUniformOffset += k.uniformSlotStride;
967 const QImage& frm = overlay->
frame();
968 const float aspect = (frm.height() > 0)
969 ?
static_cast<float>(frm.width()) / frm.height()
971 const float halfW = 0.5f * sizeM;
972 const float halfH = (aspect > 0.0f) ? halfW / aspect : halfW;
974 QVector3D viewDir = centre - data.
cameraPos;
975 if (viewDir.lengthSquared() < 1e-12f)
976 viewDir = QVector3D(0, 0, -1);
981 const QVector3D hint = overlay->
upHint();
983 if (hint.lengthSquared() > 1e-8f) {
985 up = (hint - QVector3D::dotProduct(hint, viewDir) * viewDir).normalized();
986 if (up.lengthSquared() < 1e-8f)
987 up = QVector3D(0.0f, 0.0f, 1.0f);
989 QVector3D worldUp(0.0f, 0.0f, 1.0f);
990 if (std::abs(QVector3D::dotProduct(viewDir, worldUp)) > 0.95f)
991 worldUp = QVector3D(0.0f, 1.0f, 0.0f);
992 up = QVector3D::crossProduct(
993 QVector3D::crossProduct(viewDir, worldUp), viewDir)
996 QVector3D right = QVector3D::crossProduct(viewDir, up).normalized();
998 const QVector3D c00 = centre - right * halfW - up * halfH;
999 const QVector3D c10 = centre + right * halfW - up * halfH;
1000 const QVector3D c01 = centre - right * halfW + up * halfH;
1001 const QVector3D c11 = centre + right * halfW + up * halfH;
1003 const float verts[4 * 5] = {
1004 c00.x(), c00.y(), c00.z(), 0.0f, 1.0f,
1005 c10.x(), c10.y(), c10.z(), 1.0f, 1.0f,
1006 c01.x(), c01.y(), c01.z(), 0.0f, 0.0f,
1007 c11.x(), c11.y(), c11.z(), 1.0f, 0.0f};
1009 static_assert(
sizeof(verts) == kVideoQuadBytes);
1010 u->updateDynamicBuffer(k.vertexBuffer.get(), quint32(uniformOffset / k.uniformSlotStride) * kVideoQuadBytes,
sizeof(verts), verts);
1016 float borderColor[4];
1018 float pad0, pad1, pad2;
1020 memcpy(ub.mvp, data.
mvp.constData(), 64);
1021 ub.borderColor[0] = 0.0f;
1022 ub.borderColor[1] = 0.85f;
1023 ub.borderColor[2] = 1.0f;
1024 ub.borderColor[3] = 1.0f;
1025 ub.opacity = overlay->
opacity();
1026 ub.pad0 = ub.pad1 = ub.pad2 = 0.0f;
1027 u->updateDynamicBuffer(k.uniformBuffer.get(), uniformOffset,
sizeof(ub), &ub);
1028 return uniformOffset;
1033 if (localNormal.lengthSquared() < 1e-12f) {
1034 localNormal = QVector3D(0.0f, 0.0f, 1.0f);
1036 localNormal.normalize();
1041 QVector3D referenceUp = overlay->
upHint();
1042 if (referenceUp.lengthSquared() < 1e-6f) {
1044 referenceUp = QVector3D(0.0f, 0.0f, 1.0f);
1045 if (std::abs(QVector3D::dotProduct(localNormal, referenceUp)) > 0.95f) {
1046 referenceUp = QVector3D(1.0f, 0.0f, 0.0f);
1050 referenceUp = (referenceUp - QVector3D::dotProduct(referenceUp, localNormal) * localNormal);
1051 if (referenceUp.lengthSquared() < 1e-12f) {
1052 referenceUp = QVector3D(0.0f, 0.0f, 1.0f);
1054 referenceUp.normalize();
1056 const QVector3D axisV = referenceUp;
1057 const QVector3D axisU = QVector3D::crossProduct(axisV, localNormal).normalized();
1059 const bool useDepth = d->videoOverlayUsesDepth(overlay);
1067 float focusAndSize[4];
1068 float axisUAndOpacity[4];
1069 float axisVAndOffset[4];
1070 float axisNAndDepth[4];
1071 float cameraPosAndFacing[4];
1072 float borderColor[4];
1073 float depthParams[4];
1075 memcpy(ub.mvp, data.
mvp.constData(), 64);
1080 ub.axisUAndOpacity[0] = axisU.x();
1081 ub.axisUAndOpacity[1] = axisU.y();
1082 ub.axisUAndOpacity[2] = axisU.z();
1083 ub.axisUAndOpacity[3] = overlay->
opacity();
1084 ub.axisVAndOffset[0] = axisV.x();
1085 ub.axisVAndOffset[1] = axisV.y();
1086 ub.axisVAndOffset[2] = axisV.z();
1087 ub.axisVAndOffset[3] = 0.00045f;
1088 ub.axisNAndDepth[0] = localNormal.x();
1089 ub.axisNAndDepth[1] = localNormal.y();
1090 ub.axisNAndDepth[2] = localNormal.z();
1091 ub.axisNAndDepth[3] = std::max(0.015f, overlay->
sizeMeters() * 0.45f);
1092 ub.cameraPosAndFacing[0] = data.
cameraPos.x();
1093 ub.cameraPosAndFacing[1] = data.
cameraPos.y();
1094 ub.cameraPosAndFacing[2] = data.
cameraPos.z();
1095 const QImage& decalFrame = overlay->
frame();
1096 ub.cameraPosAndFacing[3] = (decalFrame.height() > 0)
1097 ?
static_cast<float>(decalFrame.width()) / decalFrame.height()
1099 ub.borderColor[0] = 0.0f;
1100 ub.borderColor[1] = 0.85f;
1101 ub.borderColor[2] = 1.0f;
1102 ub.borderColor[3] = 1.0f;
1103 ub.depthParams[0] = useDepth ? overlay->
depthScale() : 0.0f;
1104 ub.depthParams[1] = useDepth ?
static_cast<float>(overlay->
depthSteps()) : 0.0f;
1105 ub.depthParams[2] = useDepth ? 1.0f : 0.0f;
1106 ub.depthParams[3] = 0.0f;
1107 u->updateDynamicBuffer(k.uniformBuffer.get(), uniformOffset,
sizeof(ub), &ub);
1108 return uniformOffset;
1147 QRhiResourceUpdateBatch* u,
1151 if (!rhi || !u || d->insidePass(
"prepareSlice"))
1156 auto& k = d->sliceRes;
1157 auto& sliceSlot = k.sliceSlots[slotIndex];
1160 sliceSlot.visible =
false;
1164 const QImage& img = slice->
image();
1166 sliceSlot.visible =
false;
1171 if (!k.initialized) {
1172 QFile vFile(QStringLiteral(
":/slice.vert.qsb"));
1173 QFile fFile(QStringLiteral(
":/slice.frag.qsb"));
1174 if (!vFile.open(QIODevice::ReadOnly) || !fFile.open(QIODevice::ReadOnly)) {
1175 qWarning() <<
"BrainRenderer: failed to open MRI slice shaders";
1178 QShader vShader = QShader::fromSerialized(vFile.readAll());
1179 QShader fShader = QShader::fromSerialized(fFile.readAll());
1180 if (!vShader.isValid() || !fShader.isValid()) {
1181 qWarning() <<
"BrainRenderer: invalid MRI slice shaders";
1185 k.uniformSlotStride = rhi->ubufAligned(kUniformBlockSize);
1186 k.uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1187 QRhiBuffer::UniformBuffer,
1188 64 * k.uniformSlotStride));
1189 k.uniformBuffer->create();
1192 k.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable,
1193 QRhiBuffer::IndexBuffer,
1194 6 *
sizeof(quint32)));
1195 k.indexBuffer->create();
1197 k.sampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear,
1199 QRhiSampler::ClampToEdge,
1200 QRhiSampler::ClampToEdge));
1201 k.sampler->create();
1205 constexpr int kVbSize = 4 * 5 *
sizeof(float);
1206 k.vertexBuffer[i].reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1207 QRhiBuffer::VertexBuffer, kVbSize));
1208 k.vertexBuffer[i]->create();
1211 k.sliceSlots[i].texture.reset(rhi->newTexture(QRhiTexture::R8, QSize(1, 1)));
1212 k.sliceSlots[i].texture->create();
1213 k.sliceSlots[i].textureSize = QSize(1, 1);
1215 k.srb[i].reset(rhi->newShaderResourceBindings());
1216 k.srb[i]->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
1217 QRhiShaderResourceBinding::VertexStage |
1218 QRhiShaderResourceBinding::FragmentStage,
1219 k.uniformBuffer.get(), kUniformBlockSize),
1220 QRhiShaderResourceBinding::sampledTexture(1,
1221 QRhiShaderResourceBinding::FragmentStage,
1222 k.sliceSlots[i].texture.get(), k.sampler.get())});
1227 QRhiGraphicsPipeline::TargetBlend blend;
1228 blend.enable =
true;
1229 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
1230 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1231 blend.srcAlpha = QRhiGraphicsPipeline::One;
1232 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1234 k.pipeline.reset(rhi->newGraphicsPipeline());
1235 k.pipeline->setShaderStages({{QRhiShaderStage::Vertex, vShader},
1236 {QRhiShaderStage::Fragment, fShader}});
1237 QRhiVertexInputLayout il;
1238 il.setBindings({{5 *
sizeof(float)}});
1239 il.setAttributes({{0, 0, QRhiVertexInputAttribute::Float3, 0},
1240 {0, 1, QRhiVertexInputAttribute::Float2, 3 *
sizeof(float)}});
1241 k.pipeline->setVertexInputLayout(il);
1242 k.pipeline->setShaderResourceBindings(k.srb[0].get());
1243 k.pipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
1244 k.pipeline->setSampleCount(d->rtClear->sampleCount());
1245 k.pipeline->setCullMode(QRhiGraphicsPipeline::None);
1246 k.pipeline->setTargetBlends({blend});
1247 k.pipeline->setDepthTest(
true);
1248 k.pipeline->setDepthWrite(
false);
1249 k.pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
1250 k.pipeline->create();
1252 k.initialized =
true;
1256 if (!k.indexUploaded) {
1257 const quint32 idx[6] = {0, 1, 2, 2, 1, 3};
1258 u->uploadStaticBuffer(k.indexBuffer.get(), idx);
1259 k.indexUploaded =
true;
1264 QImage gray = img.convertToFormat(QImage::Format_Grayscale8);
1265 const QSize imgSize = gray.size();
1267 if (imgSize != sliceSlot.textureSize) {
1268 sliceSlot.texture.reset(rhi->newTexture(QRhiTexture::R8, imgSize));
1269 sliceSlot.texture->create();
1270 sliceSlot.textureSize = imgSize;
1272 k.srb[slotIndex]->setBindings({QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
1273 QRhiShaderResourceBinding::VertexStage |
1274 QRhiShaderResourceBinding::FragmentStage,
1275 k.uniformBuffer.get(), kUniformBlockSize),
1276 QRhiShaderResourceBinding::sampledTexture(1,
1277 QRhiShaderResourceBinding::FragmentStage,
1278 sliceSlot.texture.get(), k.sampler.get())});
1279 k.srb[slotIndex]->create();
1284 const qsizetype tightStride = qsizetype(gray.width());
1286 texData.resize(gray.height() * tightStride);
1287 for (
int y = 0; y < gray.height(); ++y) {
1288 memcpy(texData.data() + y * tightStride,
1289 gray.constScanLine(y),
1290 size_t(tightStride));
1292 QRhiTextureSubresourceUploadDescription sub(texData.constData(), texData.size());
1293 sub.setSourceSize(imgSize);
1294 QRhiTextureUploadDescription desc({0, 0, sub});
1295 u->uploadTexture(sliceSlot.texture.get(), desc);
1300 u->updateDynamicBuffer(k.vertexBuffer[slotIndex].get(), 0,
1301 static_cast<quint32
>(sliceSlot.vertices.size() *
sizeof(
float)),
1302 sliceSlot.vertices.constData());
1304 sliceSlot.opacity = slice->
opacity();
1307 sliceSlot.visible =
true;