381void QSSGRhiShaderPipeline::addStage(
const QRhiShaderStage &stage, StageFlags flags)
383 m_stages.append(stage);
387 if (stage.type() == QRhiShaderStage::Vertex) {
392 const QVector<QShaderDescription::UniformBlock> uniformBlocks = stage.shader().description().uniformBlocks();
393 for (
const QShaderDescription::UniformBlock &blk : uniformBlocks) {
394 if (blk.binding == 0) {
395 m_ub0Size = blk.size;
396 m_ub0NextUBufOffset = m_context.rhi()->ubufAligned(m_ub0Size);
397 for (
const QShaderDescription::BlockVariable &var : blk.members)
398 m_ub0[var.name] = var;
403 if (!flags.testFlag(UsedWithoutIa)) {
404 const QVector<QShaderDescription::InOutVariable> inputs = stage.shader().description().inputVariables();
405 for (
const QShaderDescription::InOutVariable &var : inputs) {
406 if (var.name == QSSGMesh::MeshInternal::getPositionAttrName()) {
407 m_vertexInputs[QSSGRhiInputAssemblerState::PositionSemantic] = var;
408 }
else if (var.name == QSSGMesh::MeshInternal::getNormalAttrName()) {
409 m_vertexInputs[QSSGRhiInputAssemblerState::NormalSemantic] = var;
410 }
else if (var.name == QSSGMesh::MeshInternal::getUV0AttrName()) {
411 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoord0Semantic] = var;
412 }
else if (var.name == QSSGMesh::MeshInternal::getUV1AttrName()) {
413 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoord1Semantic] = var;
414 }
else if (var.name == QSSGMesh::MeshInternal::getLightmapUVAttrName()) {
415 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoordLightmapSemantic] = var;
416 }
else if (var.name == QSSGMesh::MeshInternal::getTexTanAttrName()) {
417 m_vertexInputs[QSSGRhiInputAssemblerState::TangentSemantic] = var;
418 }
else if (var.name == QSSGMesh::MeshInternal::getTexBinormalAttrName()) {
419 m_vertexInputs[QSSGRhiInputAssemblerState::BinormalSemantic] = var;
420 }
else if (var.name == QSSGMesh::MeshInternal::getColorAttrName()) {
421 m_vertexInputs[QSSGRhiInputAssemblerState::ColorSemantic] = var;
422 }
else if (var.name == QSSGMesh::MeshInternal::getJointAttrName()) {
423 m_vertexInputs[QSSGRhiInputAssemblerState::JointSemantic] = var;
424 }
else if (var.name == QSSGMesh::MeshInternal::getWeightAttrName()) {
425 m_vertexInputs[QSSGRhiInputAssemblerState::WeightSemantic] = var;
426 }
else if (var.name ==
"qt_instanceTransform0") {
427 instanceLocations.transform0 = var.location;
428 }
else if (var.name ==
"qt_instanceTransform1") {
429 instanceLocations.transform1 = var.location;
430 }
else if (var.name ==
"qt_instanceTransform2") {
431 instanceLocations.transform2 = var.location;
432 }
else if (var.name ==
"qt_instanceColor") {
433 instanceLocations.color = var.location;
434 }
else if (var.name ==
"qt_instanceData") {
435 instanceLocations.data = var.location;
437 qWarning(
"Ignoring vertex input %s in shader", var.name.constData());
443 const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = stage.shader().description().combinedImageSamplers();
444 for (
const QShaderDescription::InOutVariable &var : combinedImageSamplers)
445 m_combinedImageSamplers[var.name] = var;
447 std::fill(m_materialImageSamplerBindings,
448 m_materialImageSamplerBindings + size_t(QSSGRhiSamplerBindingHints::BindingMapSize),
457void QSSGRhiShaderPipeline::setUniformValue(
char *ubufData,
const char *name,
const QVariant &inValue, QSSGRenderShaderValue::Type inType)
459 using namespace QSSGRenderShaderValue;
461 case QSSGRenderShaderValue::Integer:
463 const qint32 v = inValue.toInt();
464 setUniform(ubufData, name, &v,
sizeof(qint32));
467 case QSSGRenderShaderValue::IntegerVec2:
469 const ivec2 v = inValue.value<ivec2>();
470 setUniform(ubufData, name, &v, 2 *
sizeof(qint32));
473 case QSSGRenderShaderValue::IntegerVec3:
475 const ivec3 v = inValue.value<ivec3>();
476 setUniform(ubufData, name, &v, 3 *
sizeof(qint32));
479 case QSSGRenderShaderValue::IntegerVec4:
481 const ivec4 v = inValue.value<ivec4>();
482 setUniform(ubufData, name, &v, 4 *
sizeof(qint32));
485 case QSSGRenderShaderValue::Boolean:
488 const qint32 v = inValue.value<
bool>();
489 setUniform(ubufData, name, &v,
sizeof(qint32));
492 case QSSGRenderShaderValue::BooleanVec2:
494 const bvec2 b = inValue.value<bvec2>();
495 const ivec2 v(b.x, b.y);
496 setUniform(ubufData, name, &v, 2 *
sizeof(qint32));
499 case QSSGRenderShaderValue::BooleanVec3:
501 const bvec3 b = inValue.value<bvec3>();
502 const ivec3 v(b.x, b.y, b.z);
503 setUniform(ubufData, name, &v, 3 *
sizeof(qint32));
506 case QSSGRenderShaderValue::BooleanVec4:
508 const bvec4 b = inValue.value<bvec4>();
509 const ivec4 v(b.x, b.y, b.z, b.w);
510 setUniform(ubufData, name, &v, 4 *
sizeof(qint32));
513 case QSSGRenderShaderValue::Float:
515 const float v = inValue.value<
float>();
516 setUniform(ubufData, name, &v,
sizeof(
float));
519 case QSSGRenderShaderValue::Vec2:
521 const QVector2D v = inValue.value<QVector2D>();
522 setUniform(ubufData, name, &v, 2 *
sizeof(
float));
525 case QSSGRenderShaderValue::Vec3:
527 const QVector3D v = inValue.value<QVector3D>();
528 setUniform(ubufData, name, &v, 3 *
sizeof(
float));
531 case QSSGRenderShaderValue::Vec4:
533 const QVector4D v = inValue.value<QVector4D>();
534 setUniform(ubufData, name, &v, 4 *
sizeof(
float));
537 case QSSGRenderShaderValue::Rgba:
539 const QVector4D v = QSSGUtils::color::sRGBToLinear(inValue.value<QColor>());
540 setUniform(ubufData, name, &v, 4 *
sizeof(
float));
543 case QSSGRenderShaderValue::UnsignedInteger:
545 const quint32 v = inValue.value<quint32>();
546 setUniform(ubufData, name, &v,
sizeof(quint32));
549 case QSSGRenderShaderValue::UnsignedIntegerVec2:
551 const uvec2 v = inValue.value<uvec2>();
552 setUniform(ubufData, name, &v, 2 *
sizeof(quint32));
555 case QSSGRenderShaderValue::UnsignedIntegerVec3:
557 const uvec3 v = inValue.value<uvec3>();
558 setUniform(ubufData, name, &v, 3 *
sizeof(quint32));
561 case QSSGRenderShaderValue::UnsignedIntegerVec4:
563 const uvec4 v = inValue.value<uvec4>();
564 setUniform(ubufData, name, &v, 4 *
sizeof(quint32));
567 case QSSGRenderShaderValue::Matrix3x3:
569 const QMatrix3x3 m = inValue.value<QMatrix3x3>();
570 setUniform(ubufData, name, m.constData(), 12 *
sizeof(
float),
nullptr, QSSGRhiShaderPipeline::UniformFlag::Mat3);
573 case QSSGRenderShaderValue::Matrix4x4:
575 const QMatrix4x4 v = inValue.value<QMatrix4x4>();
576 setUniform(ubufData, name, v.constData(), 16 *
sizeof(
float));
579 case QSSGRenderShaderValue::Size:
581 const QSize s = inValue.value<QSize>();
582 float v[2] = {
float(s.width()),
float(s.height()) };
583 setUniform(ubufData, name, v, 2 *
sizeof(
float));
586 case QSSGRenderShaderValue::SizeF:
588 const QSizeF s = inValue.value<QSizeF>();
589 float v[2] = {
float(s.width()),
float(s.height()) };
590 setUniform(ubufData, name, v, 2 *
sizeof(
float));
593 case QSSGRenderShaderValue::Point:
595 const QPoint p = inValue.value<QPoint>();
596 float v[2] = {
float(p.x()),
float(p.y()) };
597 setUniform(ubufData, name, v, 2 *
sizeof(
float));
600 case QSSGRenderShaderValue::PointF:
602 const QPointF p = inValue.value<QPointF>();
603 float v[2] = {
float(p.x()),
float(p.y()) };
604 setUniform(ubufData, name, v, 2 *
sizeof(
float));
607 case QSSGRenderShaderValue::Rect:
609 const QRect r = inValue.value<QRect>();
610 float v[4] = {
float(r.x()),
float(r.y()),
float(r.width()),
float(r.height()) };
611 setUniform(ubufData, name, v, 4 *
sizeof(
float));
614 case QSSGRenderShaderValue::RectF:
616 const QRectF r = inValue.value<QRectF>();
617 float v[4] = {
float(r.x()),
float(r.y()),
float(r.width()),
float(r.height()) };
618 setUniform(ubufData, name, v, 4 *
sizeof(
float));
621 case QSSGRenderShaderValue::Quaternion:
623 const QQuaternion q = inValue.value<QQuaternion>();
624 float v[4] = {
float(q.x()),
float(q.y()),
float(q.z()),
float(q.scalar()) };
625 setUniform(ubufData, name, v, 4 *
sizeof(
float));
629 qWarning(
"Attempted to set uniform %s value with unsupported data type %i",
710void QSSGRhiShaderPipeline::setUniformArray(
char *ubufData,
const char *name,
const void *data, size_t itemCount, QSSGRenderShaderValue::Type type,
int *storeIndex)
712 using namespace QSSGRenderShaderValue;
714 QSSGRhiShaderUniformArray *ua =
nullptr;
715 constexpr size_t std140BaseTypeSize = 4 *
sizeof(
float);
717 static const auto checkSize = [std140BaseTypeSize](QSSGRhiShaderUniformArray *ua) ->
bool {
718 Q_UNUSED(std140BaseTypeSize);
719 const size_t uniformSize = std140BaseTypeSize < ua->typeSize ? ua->typeSize * ua->itemCount : std140BaseTypeSize * ua->itemCount;
720 QSSG_ASSERT_X(uniformSize == ua->size, qPrintable(getUBMemberSizeWarning(QLatin1StringView(ua->name), uniformSize, ua->size)),
return false);
724 if (!storeIndex || *storeIndex == -1) {
726 const QByteArray ba = QByteArray::fromRawData(name, strlen(name));
727 auto it = m_uniformIndex.constFind(ba);
728 if (it != m_uniformIndex.cend()) {
730 ua = &m_uniformArrays[index];
731 }
else if (ba.size() < qsizetype(
sizeof(QSSGRhiShaderUniformArray::name))) {
732 index = m_uniformArrays.size();
733 m_uniformArrays.push_back(QSSGRhiShaderUniformArray());
734 m_uniformIndex[name] = index;
735 ua = &m_uniformArrays.last();
736 memcpy(ua->name, name, ba.size() + 1);
738 qWarning(
"Attempted to set uniform array with too long name: %s", name);
744 ua = &m_uniformArrays[*storeIndex];
750 if (ua->offset == SIZE_MAX && ua->maybeExists) {
751 auto it = m_ub0.constFind(QByteArray::fromRawData(ua->name, strlen(ua->name)));
752 if (it != m_ub0.constEnd()) {
753 ua->offset = it->offset;
757 if (ua->offset == SIZE_MAX) {
759 ua->maybeExists =
false;
763 char *p = ubufData + ua->offset;
766 case QSSGRenderShaderValue::Integer:
768 const qint32 *v =
static_cast<
const qint32 *>(data);
769 if (
sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
770 ua->typeSize =
sizeof(qint32);
771 ua->itemCount = itemCount;
774 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
776 for (size_t i = 0; i < itemCount; ++i)
777 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
780 case QSSGRenderShaderValue::IntegerVec2:
782 const ivec2 *v =
static_cast<
const ivec2 *>(data);
783 if (2 *
sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
784 ua->typeSize = 2 *
sizeof(qint32);
785 ua->itemCount = itemCount;
788 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
790 for (size_t i = 0; i < itemCount; ++i)
791 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
794 case QSSGRenderShaderValue::IntegerVec3:
796 const QSSGRenderShaderValue::ivec3 *v =
static_cast<
const QSSGRenderShaderValue::ivec3 *>(data);
797 if (3 *
sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
798 ua->typeSize = 3 *
sizeof(qint32);
799 ua->itemCount = itemCount;
802 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
804 for (size_t i = 0; i < itemCount; ++i)
805 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
808 case QSSGRenderShaderValue::IntegerVec4:
810 const ivec4 *v =
static_cast<
const ivec4 *>(data);
811 if (4 *
sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
812 ua->typeSize = 4 *
sizeof(qint32);
813 ua->itemCount = itemCount;
816 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
818 memcpy(p, v, ua->typeSize * ua->itemCount);
821 case QSSGRenderShaderValue::Float:
823 const float *v =
static_cast<
const float *>(data);
824 if (
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
825 ua->typeSize =
sizeof(
float);
826 ua->itemCount = itemCount;
829 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
831 for (size_t i = 0; i < itemCount; ++i)
832 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
835 case QSSGRenderShaderValue::Vec2:
837 const QVector2D *v =
static_cast<
const QVector2D *>(data);
838 if (2 *
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
839 ua->typeSize = 2 *
sizeof(
float);
840 ua->itemCount = itemCount;
843 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
845 for (size_t i = 0; i < itemCount; ++i)
846 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
849 case QSSGRenderShaderValue::Vec3:
851 const QVector3D *v =
static_cast<
const QVector3D *>(data);
852 if (3 *
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
853 ua->typeSize = 3 *
sizeof(
float);
854 ua->itemCount = itemCount;
857 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
859 for (size_t i = 0; i < itemCount; ++i)
860 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
863 case QSSGRenderShaderValue::Vec4:
865 const QVector4D *v =
static_cast<
const QVector4D *>(data);
866 if (4 *
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
867 ua->typeSize = 4 *
sizeof(
float);
868 ua->itemCount = itemCount;
871 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
873 memcpy(p, v, ua->typeSize * ua->itemCount);
876 case QSSGRenderShaderValue::Rgba:
878 const QColor *v =
static_cast<
const QColor *>(data);
879 if (4 *
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
880 ua->typeSize = 4 *
sizeof(
float);
881 ua->itemCount = itemCount;
884 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
886 for (size_t i = 0; i < itemCount; ++i) {
887 const QVector4D vi = QSSGUtils::color::sRGBToLinear(v[i]);
888 memcpy(p + i * std140BaseTypeSize, &vi, ua->typeSize);
892 case QSSGRenderShaderValue::UnsignedInteger:
894 const quint32 *v =
static_cast<
const quint32 *>(data);
895 if (
sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
896 ua->typeSize =
sizeof(quint32);
897 ua->itemCount = itemCount;
900 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
902 for (size_t i = 0; i < itemCount; ++i)
903 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
906 case QSSGRenderShaderValue::UnsignedIntegerVec2:
908 const uvec2 *v =
static_cast<
const uvec2 *>(data);
909 if (2 *
sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
910 ua->typeSize = 2 *
sizeof(quint32);
911 ua->itemCount = itemCount;
914 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
916 for (size_t i = 0; i < itemCount; ++i)
917 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
920 case QSSGRenderShaderValue::UnsignedIntegerVec3:
922 const uvec3 *v =
static_cast<
const uvec3 *>(data);
923 if (3 *
sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
924 ua->typeSize = 3 *
sizeof(quint32);
925 ua->itemCount = itemCount;
928 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
930 for (size_t i = 0; i < itemCount; ++i)
931 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
934 case QSSGRenderShaderValue::UnsignedIntegerVec4:
936 const uvec4 *v =
static_cast<
const uvec4 *>(data);
937 if (4 *
sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
938 ua->typeSize = 4 *
sizeof(quint32);
939 ua->itemCount = itemCount;
942 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
944 memcpy(p, v, ua->typeSize * ua->itemCount);
947 case QSSGRenderShaderValue::Matrix3x3:
949 const QMatrix3x3 *v =
static_cast<
const QMatrix3x3 *>(data);
950 if (12 *
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
951 ua->typeSize = 12 *
sizeof(
float);
952 ua->itemCount = itemCount;
955 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
957 for (uint i = 0; i < ua->itemCount; ++i) {
958 memcpy(p + i * ua->typeSize, v[i].constData(), 3 *
sizeof(
float));
959 memcpy(p + i * ua->typeSize + 4 *
sizeof(
float), v[i].constData() + 3, 3 *
sizeof(
float));
960 memcpy(p + i * ua->typeSize + 8 *
sizeof(
float), v[i].constData() + 6, 3 *
sizeof(
float));
964 case QSSGRenderShaderValue::Matrix4x4:
966 const QMatrix4x4 *v =
static_cast<
const QMatrix4x4 *>(data);
967 if (16 *
sizeof(
float) != ua->typeSize || itemCount != ua->itemCount) {
968 ua->typeSize = 16 *
sizeof(
float);
969 ua->itemCount = itemCount;
972 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)),
return);
974 for (uint i = 0; i < ua->itemCount; ++i)
975 memcpy(p + i * ua->typeSize, &v[i] , ua->typeSize);
978 case QSSGRenderShaderValue::Boolean:
979 case QSSGRenderShaderValue::BooleanVec2:
980 case QSSGRenderShaderValue::BooleanVec3:
981 case QSSGRenderShaderValue::BooleanVec4:
982 case QSSGRenderShaderValue::Size:
983 case QSSGRenderShaderValue::SizeF:
984 case QSSGRenderShaderValue::Point:
985 case QSSGRenderShaderValue::PointF:
986 case QSSGRenderShaderValue::Rect:
987 case QSSGRenderShaderValue::RectF:
988 case QSSGRenderShaderValue::Quaternion:
990 qWarning(
"Attempted to set uniform %s value with type %d that is unsupported for uniform arrays",
1629QRhiGraphicsPipeline *QSSGRhiContextPrivate::pipeline(
const QSSGGraphicsPipelineStateKey &key,
1630 QRhiRenderPassDescriptor *rpDesc,
1631 QRhiShaderResourceBindings *srb)
1633 auto it = m_pipelines.constFind(key);
1634 if (it != m_pipelines.constEnd())
1638 QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
1639 const auto &ia = QSSGRhiInputAssemblerStatePrivate::get(key.state);
1641 const auto *shaderPipeline = QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(key.state);
1642 ps->setShaderStages(shaderPipeline->cbeginStages(), shaderPipeline->cendStages());
1643 ps->setVertexInputLayout(ia.inputLayout);
1644 ps->setShaderResourceBindings(srb);
1645 ps->setRenderPassDescriptor(rpDesc);
1647 QRhiGraphicsPipeline::Flags flags;
1648 if (key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::UsesScissor))
1649 flags |= QRhiGraphicsPipeline::UsesScissor;
1651 static const bool shaderDebugInfo = qEnvironmentVariableIntValue(
"QT_QUICK3D_SHADER_DEBUG_INFO");
1652 if (shaderDebugInfo)
1653 flags |= QRhiGraphicsPipeline::CompileShadersWithDebugInfo;
1654 ps->setFlags(flags);
1656 ps->setTopology(ia.topology);
1657 ps->setCullMode(key.state.cullMode);
1658 if (ia.topology == QRhiGraphicsPipeline::Lines || ia.topology == QRhiGraphicsPipeline::LineStrip)
1659 ps->setLineWidth(key.state.lineWidth);
1661 const bool blendEnabled = key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled);
1662 QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 8> targetBlends(key.state.colorAttachmentCount);
1663 for (
int i = 0; i < key.state.colorAttachmentCount; ++i) {
1664 targetBlends[i] = key.state.targetBlend[i];
1665 targetBlends[i].enable = blendEnabled;
1667 ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend());
1669 ps->setSampleCount(key.state.samples);
1670 ps->setMultiViewCount(key.state.viewCount);
1672 ps->setDepthTest(key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled));
1673 ps->setDepthWrite(key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled));
1674 ps->setDepthOp(key.state.depthFunc);
1676 ps->setDepthBias(key.state.depthBias);
1677 ps->setSlopeScaledDepthBias(key.state.slopeScaledDepthBias);
1678 ps->setPolygonMode(key.state.polygonMode);
1680 const bool usesStencilRef = (key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::UsesStencilRef));
1682 flags |= QRhiGraphicsPipeline::UsesStencilRef;
1683 ps->setFlags(flags);
1684 ps->setStencilFront(key.state.stencilOpFrontState);
1685 ps->setStencilTest(usesStencilRef);
1686 ps->setStencilWriteMask(key.state.stencilWriteMask);
1688 if (!ps->create()) {
1689 qWarning(
"Failed to build graphics pipeline state");
1694 m_pipelines.insert(key, ps);