summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhi.cpp9
-rw-r--r--src/gui/rhi/qrhimetal.mm448
-rw-r--r--src/gui/rhi/qshader.cpp1
-rw-r--r--src/gui/rhi/qshader_p_p.h3
-rw-r--r--src/gui/rhi/qshaderdescription.cpp165
-rw-r--r--src/gui/rhi/qshaderdescription_p.h33
6 files changed, 470 insertions, 189 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 801a8984f1..3b9c150166 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -1405,6 +1405,15 @@ QRhiVertexInputAttribute::Format QRhiImplementation::shaderDescVariableFormatToV
case QShaderDescription::Uint:
return QRhiVertexInputAttribute::UInt;
+ case QShaderDescription::Half4:
+ return QRhiVertexInputAttribute::Half4;
+ case QShaderDescription::Half3:
+ return QRhiVertexInputAttribute::Half3;
+ case QShaderDescription::Half2:
+ return QRhiVertexInputAttribute::Half2;
+ case QShaderDescription::Half:
+ return QRhiVertexInputAttribute::Half;
+
default:
Q_UNREACHABLE_RETURN(QRhiVertexInputAttribute::Float);
}
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 5cb9ae0a05..2da7d24daf 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -5013,19 +5013,176 @@ id<MTLComputePipelineState> QMetalGraphicsPipelineData::Tessellation::tescCompPi
return ps;
}
-static inline bool hasBuiltin(const QVector<QShaderDescription::BuiltinVariable> &builtinList, QShaderDescription::BuiltinType builtin)
+static inline bool indexTaken(quint32 index, quint64 indices)
{
- return std::find_if(builtinList.cbegin(), builtinList.cend(),
- [builtin](const QShaderDescription::BuiltinVariable &b) { return b.type == builtin; }) != builtinList.cend();
+ return (indices >> index) & 0x1;
+}
+
+static inline void takeIndex(quint32 index, quint64 &indices)
+{
+ indices |= 1 << index;
+}
+
+static inline int nextAttributeIndex(quint64 indices)
+{
+ // Maximum number of vertex attributes per vertex descriptor. There does
+ // not appear to be a way to query this from the implementation.
+ // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf indicates
+ // that all GPU families have a value of 31.
+ static const int maxVertexAttributes = 31;
+
+ for (int index = 0; index < maxVertexAttributes; ++index) {
+ if (!indexTaken(index, indices))
+ return index;
+ }
+
+ Q_UNREACHABLE_RETURN(-1);
+}
+
+static inline int aligned(quint32 offset, quint32 alignment)
+{
+ return ((offset + alignment - 1) / alignment) * alignment;
+}
+
+template<typename T>
+static void addUnusedVertexAttribute(const T &variable, QRhiMetal *rhiD, quint32 &offset, quint32 &vertexAlignment)
+{
+
+ int elements = 1;
+ for (const int dim : variable.arrayDims)
+ elements *= dim;
+
+ if (variable.type == QShaderDescription::VariableType::Struct) {
+ for (int element = 0; element < elements; ++element) {
+ for (const auto &member : variable.structMembers) {
+ addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
+ }
+ }
+ } else {
+ const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
+ const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
+
+ // MSL specification 3.0 says alignment = size for non packed scalars and vectors
+ const quint32 alignment = size;
+ vertexAlignment = std::max(vertexAlignment, alignment);
+
+ for (int element = 0; element < elements; ++element) {
+ // adjust alignment
+ offset = aligned(offset, alignment);
+ offset += size;
+ }
+ }
+}
+
+template<typename T>
+static void addVertexAttribute(const T &variable, int binding, QRhiMetal *rhiD, int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
+{
+
+ int elements = 1;
+ for (const int dim : variable.arrayDims)
+ elements *= dim;
+
+ if (variable.type == QShaderDescription::VariableType::Struct) {
+ for (int element = 0; element < elements; ++element) {
+ for (const auto &member : variable.structMembers) {
+ addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
+ }
+ }
+ } else {
+ const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
+ const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
+
+ // MSL specification 3.0 says alignment = size for non packed scalars and vectors
+ const quint32 alignment = size;
+ vertexAlignment = std::max(vertexAlignment, alignment);
+
+ for (int element = 0; element < elements; ++element) {
+ Q_ASSERT(!indexTaken(index, indices));
+
+ // adjust alignment
+ offset = aligned(offset, alignment);
+
+ attributes[index].bufferIndex = binding;
+ attributes[index].format = toMetalAttributeFormat(format);
+ attributes[index].offset = offset;
+
+ takeIndex(index, indices);
+ index++;
+ if (indexTaken(index, indices))
+ index = nextAttributeIndex(indices);
+
+ offset += size;
+ }
+ }
+}
+
+static inline bool matches(const QList<QShaderDescription::BlockVariable> &a, const QList<QShaderDescription::BlockVariable> &b)
+{
+ if (a.size() == b.size()) {
+ bool match = true;
+ for (int i = 0; i < a.size() && match; ++i) {
+ match &= a[i].type == b[i].type
+ && a[i].arrayDims == b[i].arrayDims
+ && matches(a[i].structMembers, b[i].structMembers);
+ }
+ return match;
+ }
+
+ return false;
}
static inline bool matches(const QShaderDescription::InOutVariable &a, const QShaderDescription::InOutVariable &b)
{
return a.location == b.location
&& a.type == b.type
- && a.perPatch == b.perPatch;
-}
-
+ && a.perPatch == b.perPatch
+ && matches(a.structMembers, b.structMembers);
+}
+
+//
+// Create the tessellation evaluation render pipeline state
+//
+// The tesc runs as a compute shader in a compute pipeline and writes per patch and per patch
+// control point data into separate storage buffers. The tese runs as a vertex shader in a render
+// pipeline. Our task is to generate a render pipeline descriptor for the tese that pulls vertices
+// from these buffers.
+//
+// As the buffers we are pulling vertices from are written by a compute pipeline, they follow the
+// MSL alignment conventions which we must take into account when generating our
+// MTLVertexDescriptor. We must include the user defined tese input attributes, and any builtins
+// that were used.
+//
+// SPIRV-Cross generates the MSL tese shader code with input attribute indices that reflect the
+// specified GLSL locations. Interface blocks are flattened with each member having an incremented
+// attribute index. SPIRV-Cross reports an error on compilation if there are clashes in the index
+// address space.
+//
+// After the user specified attributes are processed, SPIRV-Cross places the in-use builtins at the
+// next available (lowest value) attribute index. Tese builtins are processed in the following
+// order:
+//
+// in gl_PerVertex
+// {
+// vec4 gl_Position;
+// float gl_PointSize;
+// float gl_ClipDistance[];
+// };
+//
+// patch in float gl_TessLevelOuter[4];
+// patch in float gl_TessLevelInner[2];
+//
+// Enumerations in QShaderDescription::BuiltinType are defined in this order.
+//
+// For quads, SPIRV-Cross places MTLQuadTessellationFactorsHalf per patch in the tessellation
+// factor buffer. For triangles it uses MTLTriangleTessellationFactorsHalf.
+//
+// It should be noted that SPIRV-Cross handles the following builtin inputs internally, with no
+// host side support required.
+//
+// in vec3 gl_TessCoord;
+// in int gl_PatchVerticesIn;
+// in int gl_PrimitiveID;
+//
id<MTLRenderPipelineState> QMetalGraphicsPipelineData::Tessellation::teseFragRenderPipeline(QRhiMetal *rhiD, QMetalGraphicsPipeline *pipeline)
{
if (pipeline->d->ps)
@@ -5034,154 +5191,191 @@ id<MTLRenderPipelineState> QMetalGraphicsPipelineData::Tessellation::teseFragRen
MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
- // Going to use the same buffer indices for the extra buffers as the tess.control compute shader did.
+ // tesc output buffers
const QMap<int, int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
-
- QMap<int, QShaderDescription::InOutVariable> teseInVars;
- for (const QShaderDescription::InOutVariable &teseInVar : vertTese.desc.inputVariables())
- teseInVars[teseInVar.location] = teseInVar;
-
quint32 offsetInTescOutput = 0;
quint32 offsetInTescPatchOutput = 0;
- int lastLocation = -1;
+ quint32 offsetInTessFactorBuffer = 0;
+ quint32 tescOutputAlignment = 0;
+ quint32 tescPatchOutputAlignment = 0;
+ quint32 tessFactorAlignment = 0;
+ QSet<int> usedBuffers;
- // these need to be sorted in location order so that lastLocation is calculated correctly - use QMap.
+ // tesc output variables in ascending location order
QMap<int, QShaderDescription::InOutVariable> tescOutVars;
- for (const QShaderDescription::InOutVariable &tescOutVar : compTesc.desc.outputVariables())
+ for (const auto &tescOutVar : compTesc.desc.outputVariables())
tescOutVars[tescOutVar.location] = tescOutVar;
- for (const QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
- const int location = tescOutVar.location;
- lastLocation = location;
- const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(tescOutVar.type);
- if (teseInVars.contains(location)) {
- if (!matches(teseInVars[location], tescOutVar)) {
- qWarning() << "mismatched tessellation control output -> tesssellation evaluation input at location" << location;
- qWarning() << "tesc out:" << tescOutVar << "tese in:" << teseInVars[location];
+ // tese input variables in ascending location order
+ QMap<int, QShaderDescription::InOutVariable> teseInVars;
+ for (const auto &teseInVar : vertTese.desc.inputVariables())
+ teseInVars[teseInVar.location] = teseInVar;
+
+ // bit mask tracking usage of vertex attribute indices
+ quint64 indices = 0;
+
+ for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
+
+ int index = tescOutVar.location;
+ int binding = -1;
+ quint32 *offset = nullptr;
+ quint32 *alignment = nullptr;
+
+ if (tescOutVar.perPatch) {
+ binding = tescPatchOutputBufferBinding;
+ offset = &offsetInTescPatchOutput;
+ alignment = &tescPatchOutputAlignment;
+ } else {
+ tescOutVar.arrayDims.removeLast();
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ }
+
+ if (teseInVars.contains(index)) {
+
+ if (!matches(teseInVars[index], tescOutVar)) {
+ qWarning() << "mismatched tessellation control output -> tesssellation evaluation input at location" << index;
+ qWarning() << " tesc out:" << tescOutVar;
+ qWarning() << " tese in:" << teseInVars[index];
}
- if (tescOutVar.perPatch) {
- if (tescPatchOutputBufferBinding >= 0) {
- vertexDesc.attributes[location].bufferIndex = tescPatchOutputBufferBinding;
- vertexDesc.attributes[location].format = toMetalAttributeFormat(format);
- vertexDesc.attributes[location].offset = offsetInTescPatchOutput;
- }
+
+ if (binding != -1) {
+ addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
+ usedBuffers << binding;
} else {
- if (tescOutputBufferBinding >= 0) {
- vertexDesc.attributes[location].bufferIndex = tescOutputBufferBinding;
- vertexDesc.attributes[location].format = toMetalAttributeFormat(format);
- vertexDesc.attributes[location].offset = offsetInTescOutput;
- }
+ qWarning() << "baked tessellation control shader missing output buffer binding information";
+ addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
}
+
} else {
qWarning() << "missing tessellation evaluation input for tessellation control output:" << tescOutVar;
+ addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
}
- if (tescOutVar.perPatch)
- offsetInTescPatchOutput += rhiD->byteSizePerVertexForVertexInputFormat(format);
- else
- offsetInTescOutput += rhiD->byteSizePerVertexForVertexInputFormat(format);
- }
-
- const QVector<QShaderDescription::BuiltinVariable> tescOutBuiltins = compTesc.desc.outputBuiltinVariables();
- const QVector<QShaderDescription::BuiltinVariable> teseInBuiltins = vertTese.desc.inputBuiltinVariables();
-
- // Take a tess.control shader with an output variable layout(location = 0) out vec3 outColor[].
- // Assume it also writes to glPosition, e.g. gl_out[gl_InvocationID].gl_Position = ...
- // The tess.eval. shader translated to a Metal vertex function will then contain:
- //
- // struct main0_in {
- // float3 inColor [[attribute(0)]];
- // float4 gl_Position [[attribute(1)]]; }
- //
- // The vertex description has to be set up accordingly. The color is
- // simple because that will be in the input/output variable list with
- // location 0. The position is a builtin however. So for now just
- // assume that builtins such as that come after the other variables,
- // with increasing location values.
-
- if (hasBuiltin(tescOutBuiltins, QShaderDescription::PositionBuiltin)
- && hasBuiltin(teseInBuiltins, QShaderDescription::PositionBuiltin)
- && tescOutputBufferBinding >= 0)
- {
- const int location = ++lastLocation;
- vertexDesc.attributes[location].bufferIndex = tescOutputBufferBinding;
- vertexDesc.attributes[location].format = toMetalAttributeFormat(QRhiVertexInputAttribute::Float4);
- vertexDesc.attributes[location].offset = offsetInTescOutput;
- offsetInTescOutput += 4 * sizeof(float);
- }
-
- // Per-patch outputs from the tess.control stage. are mostly handled above.
- // Consider:
- // layout(location = 1) patch in vec3 stuff;
- // layout(location = 2) patch in float more_stuff;
- //
- // This maps to:
- //
- // struct main0_patchIn {
- // float3 stuff [[attribute(1)]];
- // float more_stuff [[attribute(2)]];
- // patch_control_point<main0_in> gl_in; };
- //
- // These are already in place (location 1 and 2, referencing the per-patch
- // output buffer of tesc) at this point. But now if the tess.eval.shader
- // reads gl_TessLevelInner and gl_TessLevelOuter, which are also per-patch,
- // that adds, if the mode is triangles:
- // (assuming gl_Position got location 3, sorted based on the builtin type
- // (Position < Outer < Inner))
- //
- // float4 gl_TessLevel [[attribute(4)]];
- //
- // or if the mode is quads:
- //
- // float4 gl_TessLevelOuter [[attribute(4)]];
- // float2 gl_TessLevelInner [[attribute(5)]];
- //
- // Like gl_Position, these built-ins needs to be handled specially.
- // Note that the data is in a dedicated buffer, not in the patch buffer.
-
- const bool hasTessLevelOuter = hasBuiltin(tescOutBuiltins, QShaderDescription::TessLevelOuterBuiltin)
- && hasBuiltin(teseInBuiltins, QShaderDescription::TessLevelOuterBuiltin);
- const bool hasTessLevelInner = hasBuiltin(tescOutBuiltins, QShaderDescription::TessLevelInnerBuiltin)
- && hasBuiltin(teseInBuiltins, QShaderDescription::TessLevelInnerBuiltin);
- if (vertTese.desc.tessellationMode() != QShaderDescription::TrianglesTessellationMode
- && vertTese.desc.tessellationMode() != QShaderDescription::QuadTessellationMode)
- {
- qWarning("Tessellation evaluation stage mode is neither 'triangles' nor 'quads', this should not happen");
+
+ teseInVars.remove(tescOutVar.location);
}
+
+ for (const QShaderDescription::InOutVariable &teseInVar : teseInVars)
+ qWarning() << "missing tessellation control output for tessellation evaluation input:" << teseInVar;
+
+ // tesc output builtins in ascending location order
+ QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
+ for (const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
+ tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
+
+ // tese input builtins in ascending location order
+ QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
+ for (const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
+ teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
+
const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
- if ((hasTessLevelOuter || hasTessLevelInner) && tessFactorBufferBinding >= 0) {
- int loc0 = -1;
- int loc1 = -1;
- if (trianglesMode) {
- loc0 = ++lastLocation; // float4 gl_TessLevel
- } else {
- loc0 = ++lastLocation; // float4 gl_TessLevelOuter
- loc1 = ++lastLocation; // float2 gl_TessLevelInner
+ bool tessLevelAdded = false;
+
+ for (const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
+
+ QShaderDescription::InOutVariable variable;
+ int binding = -1;
+ quint32 *offset = nullptr;
+ quint32 *alignment = nullptr;
+
+ switch (builtin.type) {
+ case QShaderDescription::BuiltinType::PositionBuiltin:
+ variable.type = QShaderDescription::VariableType::Vec4;
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ break;
+ case QShaderDescription::BuiltinType::PointSizeBuiltin:
+ variable.type = QShaderDescription::VariableType::Float;
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ break;
+ case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
+ variable.type = QShaderDescription::VariableType::Float;
+ variable.arrayDims = builtin.arrayDims;
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ break;
+ case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
+ variable.type = QShaderDescription::VariableType::Half4;
+ binding = tessFactorBufferBinding;
+ offset = &offsetInTessFactorBuffer;
+ tessLevelAdded = trianglesMode;
+ alignment = &tessFactorAlignment;
+ break;
+ case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
+ if (trianglesMode) {
+ if (!tessLevelAdded) {
+ variable.type = QShaderDescription::VariableType::Half4;
+ binding = tessFactorBufferBinding;
+ offsetInTessFactorBuffer = 0;
+ offset = &offsetInTessFactorBuffer;
+ alignment = &tessFactorAlignment;
+ tessLevelAdded = true;
+ } else {
+ teseInBuiltins.remove(builtin.type);
+ continue;
+ }
+ } else {
+ variable.type = QShaderDescription::VariableType::Half2;
+ binding = tessFactorBufferBinding;
+ offsetInTessFactorBuffer = 8;
+ offset = &offsetInTessFactorBuffer;
+ alignment = &tessFactorAlignment;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
}
- if (loc0 >= 0) {
- vertexDesc.attributes[loc0].bufferIndex = tessFactorBufferBinding;
- vertexDesc.attributes[loc0].format = MTLVertexFormatHalf4;
- vertexDesc.attributes[loc0].offset = 0;
+
+ if (teseInBuiltins.contains(builtin.type)) {
+ if (binding != -1) {
+ int index = nextAttributeIndex(indices);
+ addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
+ usedBuffers << binding;
+ } else {
+ qWarning() << "baked tessellation control shader missing output buffer binding information";
+ addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
+ }
+ } else {
+ addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
}
- if (loc1 >= 0) {
- vertexDesc.attributes[loc1].bufferIndex = tessFactorBufferBinding;
- vertexDesc.attributes[loc1].format = MTLVertexFormatHalf2;
- vertexDesc.attributes[loc1].offset = 8;
+
+ teseInBuiltins.remove(builtin.type);
+ }
+
+ for (const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
+ switch (builtin.type) {
+ case QShaderDescription::BuiltinType::PositionBuiltin:
+ case QShaderDescription::BuiltinType::PointSizeBuiltin:
+ case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
+ qWarning() << "missing tessellation control output for tessellation evaluation builtin input:" << builtin;
+ break;
+ default:
+ break;
}
- vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
- vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ? 8 : 12;
}
- if (offsetInTescOutput > 0) {
+ if (usedBuffers.contains(tescOutputBufferBinding)) {
vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
- vertexDesc.layouts[tescOutputBufferBinding].stride = offsetInTescOutput;
+ vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
}
- if (offsetInTescPatchOutput > 0) {
+ if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
- vertexDesc.layouts[tescPatchOutputBufferBinding].stride = offsetInTescPatchOutput;
+ vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
+ }
+
+ if (usedBuffers.contains(tessFactorBufferBinding)) {
+ vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
+ vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ? sizeof(MTLTriangleTessellationFactorsHalf) : sizeof(MTLQuadTessellationFactorsHalf);
}
rpDesc.vertexDescriptor = vertexDesc;
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index c35445f7b8..52cb503521 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -474,6 +474,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> intVal;
d->qsbVersion = intVal;
if (d->qsbVersion != QShaderPrivate::QSB_VERSION
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS
diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h
index f0f990935a..9cc9207f69 100644
--- a/src/gui/rhi/qshader_p_p.h
+++ b/src/gui/rhi/qshader_p_p.h
@@ -24,7 +24,8 @@ QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
- static const int QSB_VERSION = 8;
+ static const int QSB_VERSION = 9;
+ static const int QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS = 8;
static const int QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO = 7;
static const int QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO = 6;
static const int QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS = 5;
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index ca697d8edb..44fcd936fd 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -724,8 +724,12 @@ static const struct TypeTab {
{ "image3DArray", QShaderDescription::Image3DArray },
{ "imageCubeArray", QShaderDescription::ImageCubeArray },
{ "imageRect", QShaderDescription::ImageRect },
- { "imageBuffer", QShaderDescription::ImageBuffer }
-};
+ { "imageBuffer", QShaderDescription::ImageBuffer },
+
+ { "half", QShaderDescription::Half },
+ { "half2", QShaderDescription::Half2 },
+ { "half3", QShaderDescription::Half3 },
+ { "half4", QShaderDescription::Half4 } };
static QLatin1StringView typeStr(QShaderDescription::VariableType t)
{
@@ -936,6 +940,8 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
dbg.nospace() << " imageFlags=" << var.imageFlags;
if (!var.arrayDims.isEmpty())
dbg.nospace() << " array=" << var.arrayDims;
+ if (!var.structMembers.isEmpty())
+ dbg.nospace() << " structMembers=" << var.structMembers;
dbg.nospace() << ')';
return dbg;
}
@@ -943,8 +949,10 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
QDebug operator<<(QDebug dbg, const QShaderDescription::BlockVariable &var)
{
QDebugStateSaver saver(dbg);
- dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name
- << " offset=" << var.offset << " size=" << var.size;
+ dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name;
+ if (var.offset != -1)
+ dbg.nospace() << " offset=" << var.offset;
+ dbg.nospace() << " size=" << var.size;
if (!var.arrayDims.isEmpty())
dbg.nospace() << " array=" << var.arrayDims;
if (var.arrayStride)
@@ -1000,7 +1008,11 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk)
QDebug operator<<(QDebug dbg, const QShaderDescription::BuiltinVariable &builtin)
{
QDebugStateSaver saver(dbg);
- dbg.nospace() << "BuiltinVariable(type=" << builtinTypeStr(builtin.type) << ")";
+ dbg.nospace() << "BuiltinVariable(type=" << builtinTypeStr(builtin.type);
+ dbg.nospace() << " varType=" << typeStr(builtin.varType);
+ if (!builtin.arrayDims.isEmpty())
+ dbg.nospace() << " array=" << builtin.arrayDims;
+ dbg.nospace() << ")";
return dbg;
}
#endif
@@ -1082,20 +1094,15 @@ static void serializeDecorations(QDataStream *stream, const QShaderDescription::
(*stream) << quint8(v.perPatch);
}
-static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
-{
- QJsonObject obj;
- obj[nameKey()] = QString::fromUtf8(v.name);
- obj[typeKey()] = typeStr(v.type);
- addDeco(&obj, v);
- return obj;
-}
-
-static void serializeInOutVar(QDataStream *stream, const QShaderDescription::InOutVariable &v, int version)
+static void serializeBuiltinVar(QDataStream *stream, const QShaderDescription::BuiltinVariable &v, int version)
{
- (*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
- serializeDecorations(stream, v, version);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ (*stream) << int(v.varType);
+ (*stream) << int(v.arrayDims.size());
+ for (int dim : v.arrayDims)
+ (*stream) << dim;
+ }
}
static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
@@ -1103,7 +1110,8 @@ static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
QJsonObject obj;
obj[nameKey()] = QString::fromUtf8(v.name);
obj[typeKey()] = typeStr(v.type);
- obj[offsetKey()] = v.offset;
+ if (v.offset != -1)
+ obj[offsetKey()] = v.offset;
obj[sizeKey()] = v.size;
if (!v.arrayDims.isEmpty()) {
QJsonArray dimArr;
@@ -1126,6 +1134,36 @@ static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
return obj;
}
+static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
+{
+ QJsonObject obj;
+ obj[nameKey()] = QString::fromUtf8(v.name);
+ obj[typeKey()] = typeStr(v.type);
+ addDeco(&obj, v);
+ if (!v.structMembers.isEmpty()) {
+ QJsonArray arr;
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ arr.append(blockMemberObject(sv));
+ obj[structMembersKey()] = arr;
+ }
+ return obj;
+}
+
+static QJsonObject builtinObject(const QShaderDescription::BuiltinVariable &v)
+{
+ QJsonObject obj;
+
+ obj[nameKey()] = builtinTypeStr(v.type);
+ obj[typeKey()] = typeStr(v.varType);
+ if (!v.arrayDims.isEmpty()) {
+ QJsonArray dimArr;
+ for (int dim : v.arrayDims)
+ dimArr.append(dim);
+ obj[arrayDimsKey()] = dimArr;
+ }
+ return obj;
+}
+
static void serializeBlockMemberVar(QDataStream *stream, const QShaderDescription::BlockVariable &v)
{
(*stream) << QString::fromUtf8(v.name);
@@ -1143,6 +1181,19 @@ static void serializeBlockMemberVar(QDataStream *stream, const QShaderDescriptio
serializeBlockMemberVar(stream, sv);
}
+static void serializeInOutVar(QDataStream *stream, const QShaderDescription::InOutVariable &v,
+ int version)
+{
+ (*stream) << QString::fromUtf8(v.name);
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v, version);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ (*stream) << int(v.structMembers.size());
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ serializeBlockMemberVar(stream, sv);
+ }
+}
+
QJsonDocument QShaderDescriptionPrivate::makeDoc()
{
QJsonObject root;
@@ -1238,20 +1289,14 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
root[storageImagesKey()] = jstorageImages;
QJsonArray jinBuiltins;
- for (const QShaderDescription::BuiltinVariable &v : std::as_const(inBuiltins)) {
- QJsonObject builtin;
- builtin[typeKey()] = builtinTypeStr(v.type);
- jinBuiltins.append(builtin);
- }
+ for (const QShaderDescription::BuiltinVariable &v : std::as_const(inBuiltins))
+ jinBuiltins.append(builtinObject(v));
if (!jinBuiltins.isEmpty())
root[inBuiltinsKey()] = jinBuiltins;
QJsonArray joutBuiltins;
- for (const QShaderDescription::BuiltinVariable &v : std::as_const(outBuiltins)) {
- QJsonObject builtin;
- builtin[typeKey()] = builtinTypeStr(v.type);
- joutBuiltins.append(builtin);
- }
+ for (const QShaderDescription::BuiltinVariable &v : std::as_const(outBuiltins))
+ joutBuiltins.append(builtinObject(v));
if (!joutBuiltins.isEmpty())
root[outBuiltinsKey()] = joutBuiltins;
@@ -1385,11 +1430,11 @@ void QShaderDescriptionPrivate::writeToStream(QDataStream *stream, int version)
(*stream) << int(inBuiltins.size());
for (const QShaderDescription::BuiltinVariable &v : std::as_const(inBuiltins))
- (*stream) << int(v.type);
+ serializeBuiltinVar(stream, v, version);
(*stream) << int(outBuiltins.size());
for (const QShaderDescription::BuiltinVariable &v : std::as_const(outBuiltins))
- (*stream) << int(v.type);
+ serializeBuiltinVar(stream, v, version);
}
}
@@ -1418,16 +1463,21 @@ static void deserializeDecorations(QDataStream *stream, int version, QShaderDesc
}
}
-static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream, int version)
+static QShaderDescription::BuiltinVariable deserializeBuiltinVar(QDataStream *stream, int version)
{
- QShaderDescription::InOutVariable var;
- QString tmp;
- (*stream) >> tmp;
- var.name = tmp.toUtf8();
+ QShaderDescription::BuiltinVariable var;
int t;
(*stream) >> t;
- var.type = QShaderDescription::VariableType(t);
- deserializeDecorations(stream, version, &var);
+ var.type = QShaderDescription::BuiltinType(t);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ (*stream) >> t;
+ var.varType = QShaderDescription::VariableType(t);
+ int count;
+ (*stream) >> count;
+ var.arrayDims.resize(count);
+ for (int i = 0; i < count; ++i)
+ (*stream) >> var.arrayDims[i];
+ }
return var;
}
@@ -1457,6 +1507,26 @@ static QShaderDescription::BlockVariable deserializeBlockMemberVar(QDataStream *
return var;
}
+static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream, int version)
+{
+ QShaderDescription::InOutVariable var;
+ QString tmp;
+ (*stream) >> tmp;
+ var.name = tmp.toUtf8();
+ int t;
+ (*stream) >> t;
+ var.type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, version, &var);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ int count;
+ (*stream) >> count;
+ var.structMembers.resize(count);
+ for (int i = 0; i < count; ++i)
+ var.structMembers[i] = deserializeBlockMemberVar(stream, version);
+ }
+ return var;
+}
+
void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
{
Q_ASSERT(ref.loadRelaxed() == 1); // must be detached
@@ -1596,19 +1666,13 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
(*stream) >> count;
inBuiltins.resize(count);
- for (int i = 0; i < count; ++i) {
- int t;
- (*stream) >> t;
- inBuiltins[i].type = QShaderDescription::BuiltinType(t);
- }
+ for (int i = 0; i < count; ++i)
+ inBuiltins[i] = deserializeBuiltinVar(stream, version);
(*stream) >> count;
outBuiltins.resize(count);
- for (int i = 0; i < count; ++i) {
- int t;
- (*stream) >> t;
- outBuiltins[i].type = QShaderDescription::BuiltinType(t);
- }
+ for (int i = 0; i < count; ++i)
+ outBuiltins[i] = deserializeBuiltinVar(stream, version);
}
}
@@ -1657,7 +1721,8 @@ bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescr
&& lhs.imageFormat == rhs.imageFormat
&& lhs.imageFlags == rhs.imageFlags
&& lhs.arrayDims == rhs.arrayDims
- && lhs.perPatch == rhs.perPatch;
+ && lhs.perPatch == rhs.perPatch
+ && lhs.structMembers == rhs.structMembers;
}
/*!
@@ -1734,7 +1799,9 @@ bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescri
*/
bool operator==(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept
{
- return lhs.type == rhs.type;
+ return lhs.type == rhs.type
+ && lhs.varType == rhs.varType
+ && lhs.arrayDims == rhs.arrayDims;
}
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
index 13e72ad134..990cb12003 100644
--- a/src/gui/rhi/qshaderdescription_p.h
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -117,7 +117,12 @@ public:
ImageRect,
ImageBuffer,
- Struct
+ Struct,
+
+ Half,
+ Half2,
+ Half3,
+ Half4
};
enum ImageFormat {
@@ -181,6 +186,19 @@ public:
// Optional data (like decorations) usually default to an otherwise invalid value (-1 or 0). This is intentional.
+ struct BlockVariable
+ {
+ QByteArray name;
+ VariableType type = Unknown;
+ int offset = 0;
+ int size = 0;
+ QList<int> arrayDims;
+ int arrayStride = 0;
+ int matrixStride = 0;
+ bool matrixIsRowMajor = false;
+ QList<BlockVariable> structMembers;
+ };
+
struct InOutVariable {
QByteArray name;
VariableType type = Unknown;
@@ -191,17 +209,6 @@ public:
ImageFlags imageFlags;
QList<int> arrayDims;
bool perPatch = false;
- };
-
- struct BlockVariable {
- QByteArray name;
- VariableType type = Unknown;
- int offset = 0;
- int size = 0;
- QList<int> arrayDims;
- int arrayStride = 0;
- int matrixStride = 0;
- bool matrixIsRowMajor = false;
QList<BlockVariable> structMembers;
};
@@ -276,6 +283,8 @@ public:
struct BuiltinVariable {
BuiltinType type;
+ VariableType varType;
+ QList<int> arrayDims;
};
QList<BuiltinVariable> inputBuiltinVariables() const;