1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qspirvshader_p.h"
5#include "qspirvshaderremap_p.h"
6#include <private/qshaderdescription_p.h>
7#include <private/qshader_p.h>
8
9#include <spirv_cross_c.h>
10
11QT_BEGIN_NAMESPACE
12
13struct QSpirvShaderPrivate
14{
15 ~QSpirvShaderPrivate();
16
17 void createCompiler(spvc_backend backend);
18 void reflect();
19
20 QShaderDescription::InOutVariable inOutVar(const spvc_reflected_resource &r);
21 QShaderDescription::BlockVariable blockVar(spvc_type_id typeId, uint32_t memberIdx);
22
23 QShader::Stage stage;
24 QByteArray ir;
25 QShaderDescription shaderDescription;
26
27 spvc_context ctx = nullptr;
28 spvc_compiler glslGen = nullptr;
29 spvc_compiler hlslGen = nullptr;
30 spvc_compiler mslGen = nullptr;
31
32 QString spirvCrossErrorMsg;
33};
34
35QSpirvShaderPrivate::~QSpirvShaderPrivate()
36{
37 spvc_context_destroy(context: ctx);
38}
39
40void QSpirvShaderPrivate::createCompiler(spvc_backend backend)
41{
42 if (!ctx) {
43 if (spvc_context_create(context: &ctx) != SPVC_SUCCESS) {
44 qWarning(msg: "Failed to create SPIRV-Cross context");
45 return;
46 }
47 }
48
49 const SpvId *spirv = reinterpret_cast<const SpvId *>(ir.constData());
50 size_t wordCount = size_t(ir.size()) / sizeof(SpvId);
51 spvc_parsed_ir parsedIr;
52 if (spvc_context_parse_spirv(context: ctx, spirv, word_count: wordCount, parsed_ir: &parsedIr) != SPVC_SUCCESS) {
53 qWarning(msg: "Failed to parse SPIR-V: %s", spvc_context_get_last_error_string(context: ctx));
54 return;
55 }
56
57 spvc_compiler *outCompiler = nullptr;
58 switch (backend) {
59 case SPVC_BACKEND_GLSL:
60 outCompiler = &glslGen;
61 break;
62 case SPVC_BACKEND_HLSL:
63 outCompiler = &hlslGen;
64 break;
65 case SPVC_BACKEND_MSL:
66 outCompiler = &mslGen;
67 break;
68 default:
69 return;
70 }
71
72 if (spvc_context_create_compiler(context: ctx, backend, parsed_ir: parsedIr,
73 mode: SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, compiler: outCompiler) != SPVC_SUCCESS)
74 {
75 qWarning(msg: "Failed to create SPIRV-Cross compiler: %s", spvc_context_get_last_error_string(context: ctx));
76 return;
77 }
78}
79
80static QShaderDescription::VariableType matVarType(const spvc_type &t, QShaderDescription::VariableType compType)
81{
82 const unsigned vecsize = spvc_type_get_vector_size(type: t);
83 switch (spvc_type_get_columns(type: t)) {
84 case 2:
85 return QShaderDescription::VariableType(compType + 4 + (vecsize == 3 ? 1 : vecsize == 4 ? 2 : 0));
86 case 3:
87 return QShaderDescription::VariableType(compType + 7 + (vecsize == 2 ? 1 : vecsize == 4 ? 2 : 0));
88 case 4:
89 return QShaderDescription::VariableType(compType + 10 + (vecsize == 2 ? 1 : vecsize == 3 ? 2 : 0));
90 default:
91 return QShaderDescription::Unknown;
92 }
93}
94
95static QShaderDescription::VariableType vecVarType(const spvc_type &t, QShaderDescription::VariableType compType)
96{
97 switch (spvc_type_get_vector_size(type: t)) {
98 case 1:
99 return compType;
100 case 2:
101 return QShaderDescription::VariableType(compType + 1);
102 case 3:
103 return QShaderDescription::VariableType(compType + 2);
104 case 4:
105 return QShaderDescription::VariableType(compType + 3);
106 default:
107 return QShaderDescription::Unknown;
108 }
109}
110
111static QShaderDescription::VariableType sampledImageVarType(const spvc_type &t)
112{
113 switch (spvc_type_get_image_dimension(type: t)) {
114 case SpvDim1D:
115 return spvc_type_get_image_arrayed(type: t) ? QShaderDescription::Sampler1DArray
116 : QShaderDescription::Sampler1D;
117 case SpvDim2D:
118 return spvc_type_get_image_arrayed(type: t)
119 ? (spvc_type_get_image_multisampled(type: t) ? QShaderDescription::Sampler2DMSArray
120 : QShaderDescription::Sampler2DArray)
121 : (spvc_type_get_image_multisampled(type: t) ? QShaderDescription::Sampler2DMS
122 : QShaderDescription::Sampler2D);
123 case SpvDim3D:
124 return spvc_type_get_image_arrayed(type: t) ? QShaderDescription::Sampler3DArray
125 : QShaderDescription::Sampler3D;
126 case SpvDimCube:
127 return spvc_type_get_image_arrayed(type: t) ? QShaderDescription::SamplerCubeArray
128 : QShaderDescription::SamplerCube;
129 case SpvDimRect:
130 return QShaderDescription::SamplerRect;
131 case SpvDimBuffer:
132 return QShaderDescription::SamplerBuffer;
133 default:
134 return QShaderDescription::Unknown;
135 }
136}
137
138static QShaderDescription::VariableType imageVarType(const spvc_type &t)
139{
140 switch (spvc_type_get_image_dimension(type: t)) {
141 case SpvDim1D:
142 return spvc_type_get_image_arrayed(type: t) ? QShaderDescription::Image1DArray
143 : QShaderDescription::Image1D;
144 case SpvDim2D:
145 return spvc_type_get_image_arrayed(type: t)
146 ? (spvc_type_get_image_multisampled(type: t) ? QShaderDescription::Image2DMSArray
147 : QShaderDescription::Image2DArray)
148 : (spvc_type_get_image_multisampled(type: t) ? QShaderDescription::Image2DMS
149 : QShaderDescription::Image2D);
150 case SpvDim3D:
151 return spvc_type_get_image_arrayed(type: t) ? QShaderDescription::Image3DArray
152 : QShaderDescription::Image3D;
153 case SpvDimCube:
154 return spvc_type_get_image_arrayed(type: t) ? QShaderDescription::ImageCubeArray
155 : QShaderDescription::ImageCube;
156 case SpvDimRect:
157 return QShaderDescription::ImageRect;
158 case SpvDimBuffer:
159 return QShaderDescription::ImageBuffer;
160 default:
161 return QShaderDescription::Unknown;
162 }
163}
164
165static QShaderDescription::VariableType varType(const spvc_type &t)
166{
167 QShaderDescription::VariableType vt = QShaderDescription::Unknown;
168 const spvc_basetype basetype = spvc_type_get_basetype(type: t);
169 switch (basetype) {
170 case SPVC_BASETYPE_FP32:
171 vt = spvc_type_get_columns(type: t) > 1 ? matVarType(t, compType: QShaderDescription::Float)
172 : vecVarType(t, compType: QShaderDescription::Float);
173 break;
174 case SPVC_BASETYPE_FP64:
175 vt = spvc_type_get_columns(type: t) > 1 ? matVarType(t, compType: QShaderDescription::Double)
176 : vecVarType(t, compType: QShaderDescription::Double);
177 break;
178 case SPVC_BASETYPE_UINT32:
179 vt = vecVarType(t, compType: QShaderDescription::Uint);
180 break;
181 case SPVC_BASETYPE_INT32:
182 vt = vecVarType(t, compType: QShaderDescription::Int);
183 break;
184 case SPVC_BASETYPE_BOOLEAN:
185 vt = vecVarType(t, compType: QShaderDescription::Uint);
186 break;
187 case SPVC_BASETYPE_SAMPLED_IMAGE:
188 vt = sampledImageVarType(t);
189 break;
190 case SPVC_BASETYPE_IMAGE:
191 vt = imageVarType(t);
192 break;
193 case SPVC_BASETYPE_SAMPLER:
194 vt = QShaderDescription::Sampler;
195 break;
196 case SPVC_BASETYPE_STRUCT:
197 vt = QShaderDescription::Struct;
198 break;
199 default:
200 // can encounter types we do not (yet) handle, return Unknown for those
201 qWarning(msg: "Unsupported base type %d", basetype);
202 break;
203 }
204 return vt;
205}
206
207QShaderDescription::InOutVariable QSpirvShaderPrivate::inOutVar(const spvc_reflected_resource &r)
208{
209 QShaderDescription::InOutVariable v;
210 v.name = r.name;
211
212 spvc_type baseTypeHandle = spvc_compiler_get_type_handle(compiler: glslGen, id: r.base_type_id);
213 v.type = varType(t: baseTypeHandle);
214
215 spvc_type typeHandle = spvc_compiler_get_type_handle(compiler: glslGen, id: r.type_id);
216 for (unsigned i = 0, dimCount = spvc_type_get_num_array_dimensions(type: typeHandle); i < dimCount; ++i)
217 v.arrayDims.append(t: int(spvc_type_get_array_dimension(type: typeHandle, dimension: i)));
218
219 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationLocation))
220 v.location = spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationLocation);
221
222 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationBinding))
223 v.binding = spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationBinding);
224
225 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationDescriptorSet))
226 v.descriptorSet = spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationDescriptorSet);
227
228 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationPatch))
229 v.perPatch = spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationPatch);
230
231 if (spvc_type_get_basetype(type: baseTypeHandle) == SPVC_BASETYPE_IMAGE) {
232 v.imageFormat = QShaderDescription::ImageFormat(spvc_type_get_image_storage_format(type: baseTypeHandle));
233
234 v.imageFlags.setFlag(flag: QShaderDescription::ImageFlag::WriteOnlyImage,
235 on: spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationNonReadable));
236
237 v.imageFlags.setFlag(flag: QShaderDescription::ImageFlag::ReadOnlyImage,
238 on: spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationNonWritable));
239 }
240
241 if (v.type == QShaderDescription::Struct) {
242 const unsigned count = spvc_type_get_num_member_types(type: baseTypeHandle);
243 const spvc_type_id id = spvc_type_get_base_type_id(type: baseTypeHandle);
244
245 for (unsigned idx = 0; idx < count; ++idx) {
246 v.structMembers.append(t: blockVar(typeId: id, memberIdx: idx));
247 v.perPatch |= bool(spvc_compiler_has_member_decoration(compiler: glslGen, id, member_index: idx, decoration: SpvDecorationPatch));
248 }
249 }
250
251 return v;
252}
253
254QShaderDescription::BlockVariable QSpirvShaderPrivate::blockVar(spvc_type_id typeId, uint32_t memberIdx)
255{
256 QShaderDescription::BlockVariable v;
257 v.name = spvc_compiler_get_member_name(compiler: glslGen, id: typeId, member_index: memberIdx);
258
259 spvc_type t = spvc_compiler_get_type_handle(compiler: glslGen, id: typeId);
260 spvc_type_id memberTypeId = spvc_type_get_member_type(type: t, index: memberIdx);
261 spvc_type memberType = spvc_compiler_get_type_handle(compiler: glslGen, id: memberTypeId);
262 v.type = varType(t: memberType);
263 v.offset = -1;
264
265 unsigned offset = 0;
266 if (spvc_compiler_type_struct_member_offset(compiler: glslGen, type: t, index: memberIdx, offset: &offset) == SPVC_SUCCESS)
267 v.offset = int(offset);
268
269 size_t size = 0;
270 if (spvc_compiler_get_declared_struct_member_size(compiler: glslGen, type: t, index: memberIdx, size: &size) == SPVC_SUCCESS)
271 v.size = int(size);
272
273 for (unsigned i = 0, dimCount = spvc_type_get_num_array_dimensions(type: memberType); i < dimCount; ++i)
274 v.arrayDims.append(t: int(spvc_type_get_array_dimension(type: memberType, dimension: i)));
275
276 if (spvc_compiler_has_member_decoration(compiler: glslGen, id: typeId, member_index: memberIdx, decoration: SpvDecorationArrayStride)) {
277 unsigned stride = 0;
278 if (spvc_compiler_type_struct_member_array_stride(compiler: glslGen, type: t, index: memberIdx, stride: &stride) == SPVC_SUCCESS)
279 v.arrayStride = int(stride);
280 }
281
282 if (spvc_compiler_has_member_decoration(compiler: glslGen, id: typeId, member_index: memberIdx, decoration: SpvDecorationMatrixStride)) {
283 unsigned stride = 0;
284 if (spvc_compiler_type_struct_member_matrix_stride(compiler: glslGen, type: t, index: memberIdx, stride: &stride) == SPVC_SUCCESS)
285 v.matrixStride = int(stride);
286 }
287
288 if (spvc_compiler_has_member_decoration(compiler: glslGen, id: typeId, member_index: memberIdx, decoration: SpvDecorationRowMajor))
289 v.matrixIsRowMajor = true;
290
291 if (v.type == QShaderDescription::Struct) {
292 unsigned count = spvc_type_get_num_member_types(type: memberType);
293 for (unsigned idx = 0; idx < count; ++idx)
294 v.structMembers.append(t: blockVar(typeId: spvc_type_get_base_type_id(type: memberType), memberIdx: idx));
295 }
296
297 return v;
298}
299
300void QSpirvShaderPrivate::reflect()
301{
302 if (!glslGen)
303 return;
304
305 shaderDescription = QShaderDescription();
306 QShaderDescriptionPrivate *dd = QShaderDescriptionPrivate::get(desc: &shaderDescription);
307
308 dd->localSize[0] = spvc_compiler_get_execution_mode_argument_by_index(compiler: glslGen, mode: SpvExecutionModeLocalSize, index: 0);
309 dd->localSize[1] = spvc_compiler_get_execution_mode_argument_by_index(compiler: glslGen, mode: SpvExecutionModeLocalSize, index: 1);
310 dd->localSize[2] = spvc_compiler_get_execution_mode_argument_by_index(compiler: glslGen, mode: SpvExecutionModeLocalSize, index: 2);
311
312 dd->tessOutVertCount = spvc_compiler_get_execution_mode_argument(compiler: glslGen, mode: SpvExecutionModeOutputVertices);
313 dd->tessMode = QShaderDescription::UnknownTessellationMode;
314 dd->tessWind = QShaderDescription::UnknownTessellationWindingOrder;
315 dd->tessPart = QShaderDescription::UnknownTessellationPartitioning;
316
317 const SpvExecutionMode *execModes = nullptr;
318 size_t execModeCount = 0;
319 if (spvc_compiler_get_execution_modes(compiler: glslGen, modes: &execModes, num_modes: &execModeCount) != SPVC_SUCCESS) {
320 qWarning(msg: "Failed to get shader execution modes: %s", spvc_context_get_last_error_string(context: ctx));
321 return;
322 }
323 for (size_t i = 0; i < execModeCount; ++i) {
324 switch (execModes[i]) {
325 case SpvExecutionModeTriangles:
326 dd->tessMode = QShaderDescription::TrianglesTessellationMode;
327 break;
328 case SpvExecutionModeQuads:
329 dd->tessMode = QShaderDescription::QuadTessellationMode;
330 break;
331 case SpvExecutionModeIsolines:
332 qWarning(msg: "Isoline execution mode used for tessellation,"
333 " this is not portable and may not work as expected.");
334 dd->tessMode = QShaderDescription::IsolineTessellationMode;
335 break;
336 case SpvExecutionModeSpacingEqual:
337 dd->tessPart = QShaderDescription::EqualTessellationPartitioning;
338 break;
339 case SpvExecutionModeSpacingFractionalEven:
340 dd->tessPart = QShaderDescription::FractionalEvenTessellationPartitioning;
341 break;
342 case SpvExecutionModeSpacingFractionalOdd:
343 dd->tessPart = QShaderDescription::FractionalOddTessellationPartitioning;
344 break;
345 case SpvExecutionModeVertexOrderCw:
346 dd->tessWind = QShaderDescription::CwTessellationWindingOrder;
347 break;
348 case SpvExecutionModeVertexOrderCcw :
349 dd->tessWind = QShaderDescription::CcwTessellationWindingOrder;
350 break;
351 default:
352 break;
353 }
354 }
355
356 // For builtin inputs/outputs (think of things like gl_Position (or
357 // gl_out[].gl_Position), gl_InvocationID, gl_TessLevelOuter, etc.) we only
358 // want the ones that are active (really read or written).
359 spvc_compiler_update_active_builtins(compiler: glslGen);
360
361 spvc_resources resources;
362 if (spvc_compiler_create_shader_resources(compiler: glslGen, resources: &resources) != SPVC_SUCCESS) {
363 qWarning(msg: "Failed to get shader resources: %s", spvc_context_get_last_error_string(context: ctx));
364 return;
365 }
366
367 const spvc_reflected_resource *resourceList = nullptr;
368 const spvc_reflected_builtin_resource *builtinResourceList = nullptr;
369 size_t resourceListCount = 0;
370
371 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_STAGE_INPUT,
372 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
373 {
374 for (size_t i = 0; i < resourceListCount; ++i) {
375 const QShaderDescription::InOutVariable v = inOutVar(r: resourceList[i]);
376 if (v.type != QShaderDescription::Unknown)
377 dd->inVars.append(t: v);
378 }
379 }
380
381 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_STAGE_OUTPUT,
382 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
383 {
384 for (size_t i = 0; i < resourceListCount; ++i) {
385 const QShaderDescription::InOutVariable v = inOutVar(r: resourceList[i]);
386 if (v.type != QShaderDescription::Unknown)
387 dd->outVars.append(t: v);
388 }
389 }
390
391 if (spvc_resources_get_builtin_resource_list_for_type(resources, type: SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT,
392 resource_list: &builtinResourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
393 {
394 for (size_t i = 0; i < resourceListCount; ++i) {
395 if (spvc_compiler_has_active_builtin(compiler: glslGen, builtin: builtinResourceList[i].builtin, storage: SpvStorageClassInput)) {
396 QShaderDescription::BuiltinVariable v;
397 v.type = QShaderDescription::BuiltinType(builtinResourceList[i].builtin);
398
399 spvc_type type = spvc_compiler_get_type_handle(
400 compiler: glslGen, id: builtinResourceList[i].value_type_id);
401 v.varType = varType(t: type);
402
403 for (unsigned i = 0, dimCount = spvc_type_get_num_array_dimensions(type);
404 i < dimCount; ++i)
405 v.arrayDims.append(t: int(spvc_type_get_array_dimension(type, dimension: i)));
406
407 dd->inBuiltins.append(t: v);
408 }
409 }
410 std::sort(first: dd->inBuiltins.begin(), last: dd->inBuiltins.end(),
411 comp: [](const QShaderDescription::BuiltinVariable &a, const QShaderDescription::BuiltinVariable &b)
412 {
413 return a.type < b.type;
414 });
415 }
416
417 if (spvc_resources_get_builtin_resource_list_for_type(resources, type: SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT,
418 resource_list: &builtinResourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
419 {
420 for (size_t i = 0; i < resourceListCount; ++i) {
421 if (spvc_compiler_has_active_builtin(compiler: glslGen, builtin: builtinResourceList[i].builtin, storage: SpvStorageClassOutput)) {
422 QShaderDescription::BuiltinVariable v;
423 v.type = QShaderDescription::BuiltinType(builtinResourceList[i].builtin);
424
425 spvc_type type = spvc_compiler_get_type_handle(
426 compiler: glslGen, id: builtinResourceList[i].value_type_id);
427 v.varType = varType(t: type);
428
429 for (unsigned i = 0, dimCount = spvc_type_get_num_array_dimensions(type);
430 i < dimCount; ++i)
431 v.arrayDims.append(t: int(spvc_type_get_array_dimension(type, dimension: i)));
432
433 dd->outBuiltins.append(t: v);
434 }
435 }
436 std::sort(first: dd->outBuiltins.begin(), last: dd->outBuiltins.end(),
437 comp: [](const QShaderDescription::BuiltinVariable &a, const QShaderDescription::BuiltinVariable &b)
438 {
439 return a.type < b.type;
440 });
441 }
442
443 // uniform blocks map to either a uniform buffer or a plain struct
444 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
445 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
446 {
447 for (size_t i = 0; i < resourceListCount; ++i) {
448 const spvc_reflected_resource &r(resourceList[i]);
449 spvc_type t = spvc_compiler_get_type_handle(compiler: glslGen, id: r.base_type_id);
450 QShaderDescription::UniformBlock block;
451 block.blockName = r.name;
452
453 // the simple case:
454 // layout(...) uniform blk { T v; } inst;
455 // gives us blockName "blk" and structName "inst" because
456 // in GLSL without uniform blocks this ends up being
457 // struct blk { T v; }; uniform blk inst;
458 // or, where real UBs are used, for instance Metal:
459 // struct blk { T v; }; constant blk& inst [[buffer(N)]]
460 block.structName = spvc_compiler_get_name(compiler: glslGen, id: r.id);
461
462 // the annoying case:
463 // layout(...) uniform blk { T v; };
464 // here structName is empty and the generated code (when no uniform
465 // blocks) uses _ID as a fallback name:
466 // struct blk { T v; }; uniform blk _ID;
467 // or, with real uniform buffers, f.ex. Metal:
468 // struct blk { T v; }; constant blk& _ID [[buffer(N)]]
469 // Let's make sure the fallback name is filled in correctly.
470 if (block.structName.isEmpty()) {
471 // The catch (matters only when uniform blocks are not used in
472 // the output) is that ID is per-shader and so may differ
473 // between shaders, meaning that having the same uniform block
474 // in a vertex and fragment shader will lead to each stage
475 // having its own set of uniforms corresponding to the ub
476 // members (ofc, inactive uniforms may get optimized out by the
477 // compiler, so the duplication then is only there for members
478 // used in both stages). Not much we can do about that here,
479 // though. The GL backend of QRhi can deal with this.
480 block.structName = QByteArrayLiteral("_") + QByteArray::number(r.id);
481 }
482
483 size_t size = 0;
484 spvc_compiler_get_declared_struct_size(compiler: glslGen, struct_type: t, size: &size);
485 block.size = int(size);
486 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationBinding))
487 block.binding = int(spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationBinding));
488 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationDescriptorSet))
489 block.descriptorSet = int(spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationDescriptorSet));
490
491 unsigned count = spvc_type_get_num_member_types(type: t);
492 for (unsigned idx = 0; idx < count; ++idx) {
493 const QShaderDescription::BlockVariable v = blockVar(typeId: r.base_type_id, memberIdx: idx);
494 if (v.type != QShaderDescription::Unknown)
495 block.members.append(t: v);
496 }
497
498 dd->uniformBlocks.append(t: block);
499 }
500 }
501
502 // push constant blocks map to a plain GLSL struct regardless of version
503 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_PUSH_CONSTANT,
504 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
505 {
506 for (size_t i = 0; i < resourceListCount; ++i) {
507 const spvc_reflected_resource &r(resourceList[i]);
508 spvc_type t = spvc_compiler_get_type_handle(compiler: glslGen, id: r.base_type_id);
509 QShaderDescription::PushConstantBlock block;
510 block.name = spvc_compiler_get_name(compiler: glslGen, id: r.id);
511 size_t size = 0;
512 spvc_compiler_get_declared_struct_size(compiler: glslGen, struct_type: t, size: &size);
513 block.size = int(size);
514 unsigned count = spvc_type_get_num_member_types(type: t);
515 for (unsigned idx = 0; idx < count; ++idx) {
516 const QShaderDescription::BlockVariable v = blockVar(typeId: r.base_type_id, memberIdx: idx);
517 if (v.type != QShaderDescription::Unknown)
518 block.members.append(t: v);
519 }
520 dd->pushConstantBlocks.append(t: block);
521 }
522 }
523
524 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
525 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
526 {
527 for (size_t i = 0; i < resourceListCount; ++i) {
528 const spvc_reflected_resource &r(resourceList[i]);
529 spvc_type t = spvc_compiler_get_type_handle(compiler: glslGen, id: r.base_type_id);
530 QShaderDescription::StorageBlock block;
531 block.blockName = r.name;
532 block.instanceName = spvc_compiler_get_name(compiler: glslGen, id: r.id);
533 size_t size = 0;
534 spvc_compiler_get_declared_struct_size(compiler: glslGen, struct_type: t, size: &size);
535 block.knownSize = int(size);
536 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationBinding))
537 block.binding = int(spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationBinding));
538 if (spvc_compiler_has_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationDescriptorSet))
539 block.descriptorSet = int(spvc_compiler_get_decoration(compiler: glslGen, id: r.id, decoration: SpvDecorationDescriptorSet));
540 size_t offset0 = 0;
541 spvc_compiler_get_declared_struct_size_runtime_array(compiler: glslGen, struct_type: t, array_size: 0, size: &offset0);
542 size_t offset1 = 0;
543 spvc_compiler_get_declared_struct_size_runtime_array(compiler: glslGen, struct_type: t, array_size: 1, size: &offset1);
544 block.runtimeArrayStride = int(offset1 - offset0);
545 const SpvDecoration *decorations;
546 spvc_compiler_get_buffer_block_decorations(compiler: glslGen, id: r.id, decorations: &decorations, num_decorations: &size);
547 for (uint i = 0; i < size; ++i) {
548 switch (decorations[i]) {
549 case SpvDecorationCoherent:
550 block.qualifierFlags.setFlag(flag: QShaderDescription::QualifierCoherent);
551 break;
552 case SpvDecorationVolatile:
553 block.qualifierFlags.setFlag(flag: QShaderDescription::QualifierVolatile);
554 break;
555 case SpvDecorationRestrict:
556 block.qualifierFlags.setFlag(flag: QShaderDescription::QualifierRestrict);
557 break;
558 case SpvDecorationNonWritable:
559 block.qualifierFlags.setFlag(flag: QShaderDescription::QualifierReadOnly);
560 break;
561 case SpvDecorationNonReadable:
562 block.qualifierFlags.setFlag(flag: QShaderDescription::QualifierWriteOnly);
563 break;
564 default:
565 break;
566 }
567 }
568 unsigned count = spvc_type_get_num_member_types(type: t);
569 for (unsigned idx = 0; idx < count; ++idx) {
570 const QShaderDescription::BlockVariable v = blockVar(typeId: r.base_type_id, memberIdx: idx);
571 if (v.type != QShaderDescription::Unknown)
572 block.members.append(t: v);
573 }
574 dd->storageBlocks.append(t: block);
575 }
576 }
577
578 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
579 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
580 {
581 for (size_t i = 0; i < resourceListCount; ++i) {
582 const spvc_reflected_resource &r(resourceList[i]);
583 const QShaderDescription::InOutVariable v = inOutVar(r);
584 if (v.type != QShaderDescription::Unknown)
585 dd->combinedImageSamplers.append(t: v);
586 }
587 }
588
589 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SEPARATE_IMAGE,
590 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
591 {
592 for (size_t i = 0; i < resourceListCount; ++i) {
593 const spvc_reflected_resource &r(resourceList[i]);
594 const QShaderDescription::InOutVariable v = inOutVar(r);
595 if (v.type != QShaderDescription::Unknown)
596 dd->separateImages.append(t: v);
597 }
598 }
599
600 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS,
601 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
602 {
603 for (size_t i = 0; i < resourceListCount; ++i) {
604 const spvc_reflected_resource &r(resourceList[i]);
605 const QShaderDescription::InOutVariable v = inOutVar(r);
606 if (v.type != QShaderDescription::Unknown)
607 dd->separateSamplers.append(t: v);
608 }
609 }
610
611 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
612 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
613 {
614 for (size_t i = 0; i < resourceListCount; ++i) {
615 const spvc_reflected_resource &r(resourceList[i]);
616 const QShaderDescription::InOutVariable v = inOutVar(r);
617 if (v.type != QShaderDescription::Unknown)
618 dd->storageImages.append(t: v);
619 }
620 }
621}
622
623QSpirvShader::QSpirvShader()
624 : d(new QSpirvShaderPrivate)
625{
626}
627
628QSpirvShader::~QSpirvShader()
629{
630 delete d;
631}
632
633void QSpirvShader::setSpirvBinary(const QByteArray &spirv, QShader::Stage stage)
634{
635 d->stage = stage;
636 d->ir = spirv;
637 d->createCompiler(backend: SPVC_BACKEND_GLSL);
638 d->reflect();
639}
640
641QShaderDescription QSpirvShader::shaderDescription() const
642{
643 return d->shaderDescription;
644}
645
646QByteArray QSpirvShader::spirvBinary() const
647{
648 return d->ir;
649}
650
651QByteArray QSpirvShader::remappedSpirvBinary(RemapFlags flags, QString *errorMessage) const
652{
653 QSpirvShaderRemapper remapper;
654 QByteArray result = remapper.remap(ir: d->ir, flags);
655 if (errorMessage)
656 *errorMessage = remapper.errorMessage();
657 return result;
658}
659
660QByteArray QSpirvShader::translateToGLSL(int version,
661 GlslFlags flags,
662 QVector<SeparateToCombinedImageSamplerMapping> *separateToCombinedImageSamplerMappings) const
663{
664 d->spirvCrossErrorMsg.clear();
665
666 d->createCompiler(backend: SPVC_BACKEND_GLSL);
667 if (!d->glslGen)
668 return QByteArray();
669
670 spvc_compiler_options options = nullptr;
671 if (spvc_compiler_create_compiler_options(compiler: d->glslGen, options: &options) != SPVC_SUCCESS)
672 return QByteArray();
673 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_GLSL_VERSION,
674 value: version);
675 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_GLSL_ES,
676 value: flags.testFlag(flag: GlslFlag::GlslEs));
677 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION,
678 value: flags.testFlag(flag: GlslFlag::FixClipSpace));
679 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP,
680 value: !flags.testFlag(flag: GlslFlag::FragDefaultMediump));
681 // The gl backend of QRhi is not prepared for UBOs atm. Have a uniform (heh)
682 // behavior regardless of the GLSL version.
683 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS,
684 value: true);
685 // Do not emit binding qualifiers for samplers (and for uniform blocks, but
686 // those we just disabled above).
687 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION,
688 value: false);
689 spvc_compiler_install_compiler_options(compiler: d->glslGen, options);
690
691 // Let's say the shader has these separate imagers and samplers:
692 // layout(binding = 2) uniform texture2D sepTex;
693 // layout(binding = 3) uniform sampler sepSampler;
694 // layout(binding = 4) uniform sampler sepSampler2;
695 // where sepTex is used with both samplers in the shader.
696 // For OpenGL this is mapped to "combined image samplers", i.e. the
697 // traditional sampler2Ds with arbitrary names:
698 // uniform sampler2D _39;
699 // uniform sampler2D _41;
700 // This mapping (2+3 -> "_39", 2+4 -> "_41") needs to be exposed to the caller.
701 if (spvc_compiler_build_combined_image_samplers(compiler: d->glslGen) != SPVC_SUCCESS) {
702 d->spirvCrossErrorMsg = QString::fromUtf8(utf8: spvc_context_get_last_error_string(context: d->ctx));
703 return QByteArray();
704 }
705 if (separateToCombinedImageSamplerMappings) {
706 const spvc_combined_image_sampler *combinedImageSamplerMappings = nullptr;
707 size_t numCombinedImageSamplerMappings = 0;
708 if (spvc_compiler_get_combined_image_samplers(compiler: d->glslGen,
709 samplers: &combinedImageSamplerMappings,
710 num_samplers: &numCombinedImageSamplerMappings) == SPVC_SUCCESS)
711 {
712 for (size_t i = 0; i < numCombinedImageSamplerMappings; ++i) {
713 const spvc_combined_image_sampler *mapping = &combinedImageSamplerMappings[i];
714 QByteArray combinedName = spvc_compiler_get_name(compiler: d->glslGen, id: mapping->combined_id);
715 if (combinedName.isEmpty())
716 combinedName = QByteArrayLiteral("_") + QByteArray::number(mapping->combined_id);
717 QByteArray textureName = spvc_compiler_get_name(compiler: d->glslGen, id: mapping->image_id);
718 QByteArray samplerName = spvc_compiler_get_name(compiler: d->glslGen, id: mapping->sampler_id);
719 separateToCombinedImageSamplerMappings->append(t: { .textureName: textureName, .samplerName: samplerName, .combinedSamplerName: combinedName });
720 }
721 }
722 }
723
724 const char *result = nullptr;
725 if (spvc_compiler_compile(compiler: d->glslGen, source: &result) != SPVC_SUCCESS) {
726 d->spirvCrossErrorMsg = QString::fromUtf8(utf8: spvc_context_get_last_error_string(context: d->ctx));
727 return QByteArray();
728 }
729
730 // We used to fix up the result to complement GL_ARB_shading_language_420pack
731 // with GL_ARB_separate_shader_objects to make Mesa happy, but the 420pack
732 // is never relied on now so no need to do anything here.
733 return result;
734}
735
736QByteArray QSpirvShader::translateToHLSL(int version, QShader::NativeResourceBindingMap *nativeBindings) const
737{
738 d->spirvCrossErrorMsg.clear();
739
740 d->createCompiler(backend: SPVC_BACKEND_HLSL);
741 if (!d->hlslGen)
742 return QByteArray();
743
744 spvc_compiler_options options = nullptr;
745 if (spvc_compiler_create_compiler_options(compiler: d->hlslGen, options: &options) != SPVC_SUCCESS)
746 return QByteArray();
747 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL,
748 value: version);
749 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT,
750 value: true);
751 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT,
752 value: true);
753 spvc_compiler_install_compiler_options(compiler: d->hlslGen, options);
754
755 // D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT is 16, so we cannot have combined
756 // image/sampler bindings above 15. Problem is, the (SPIR-V) binding
757 // namespace is common with uniform buffers and other resources, and so
758 // having many uniform buffers and samplers can push the sampler binding to
759 // above 15 even when there are less than 16 samplers in total. So cannot
760 // just rely on SPIRV-Cross setting registers t<binding> and s<binding> for
761 // the SRV and sampler for each combined image/sampler. Instead, we want
762 // t<slot> and s<slot> where slot is 0-based, with its own namespace, and
763 // is then exposed in the native resource binding map so QRhi can map
764 // between (SPIR-V) bindings and D3D sampler slots.
765 const SpvExecutionModel stage = spvc_compiler_get_execution_model(compiler: d->hlslGen);
766 int regBinding = 0; // SRVs and samplers
767 for (const QShaderDescription::InOutVariable &var : d->shaderDescription.combinedImageSamplers()) {
768 spvc_hlsl_resource_binding bindingMapping;
769 bindingMapping.stage = stage; // will be per-stage but we have a per-shader NativeResourceBindingMap so it's ok
770 bindingMapping.desc_set = 0;
771 bindingMapping.binding = var.binding;
772 bindingMapping.srv.register_space = 0;
773 bindingMapping.srv.register_binding = regBinding; // t0, t1, ...
774 bindingMapping.sampler.register_space = 0;
775 bindingMapping.sampler.register_binding = regBinding; // s0, s1, ...
776 spvc_compiler_hlsl_add_resource_binding(compiler: d->hlslGen, binding: &bindingMapping);
777 nativeBindings->insert(key: var.binding, value: { regBinding, regBinding });
778 regBinding += var.arrayDims.isEmpty() ? 1 : var.arrayDims.first();
779 }
780 // In fact, with separate images and samplers added to the mix, the manual
781 // mapping specification is essential so that the HLSL nicely uses t0, t1,
782 // t2, ... and s0, s1, s2, ... for all the images and samplers incl. both
783 // combined and separate.
784 int firstSeparateImageReg = regBinding;
785 for (const QShaderDescription::InOutVariable &var : d->shaderDescription.separateImages()) {
786 spvc_hlsl_resource_binding bindingMapping;
787 bindingMapping.stage = stage;
788 bindingMapping.desc_set = 0;
789 bindingMapping.binding = var.binding;
790 bindingMapping.srv.register_space = 0;
791 bindingMapping.srv.register_binding = regBinding; // tN, tN+1, ..., where N is the next reg after the combined image sampler ones
792 spvc_compiler_hlsl_add_resource_binding(compiler: d->hlslGen, binding: &bindingMapping);
793 nativeBindings->insert(key: var.binding, value: { regBinding, -1 });
794 regBinding += var.arrayDims.isEmpty() ? 1 : var.arrayDims.first();
795 }
796 regBinding = firstSeparateImageReg;
797 for (const QShaderDescription::InOutVariable &var : d->shaderDescription.separateSamplers()) {
798 spvc_hlsl_resource_binding bindingMapping;
799 bindingMapping.stage = stage;
800 bindingMapping.desc_set = 0;
801 bindingMapping.binding = var.binding;
802 bindingMapping.sampler.register_space = 0;
803 bindingMapping.sampler.register_binding = regBinding; // sN, sN+1, ..., where N is the next reg after the combined image sampler ones
804 spvc_compiler_hlsl_add_resource_binding(compiler: d->hlslGen, binding: &bindingMapping);
805 nativeBindings->insert(key: var.binding, value: { regBinding, -1 });
806 regBinding += var.arrayDims.isEmpty() ? 1 : var.arrayDims.first();
807 }
808
809 regBinding = 0; // CBVs
810 for (const QShaderDescription::UniformBlock &blk : d->shaderDescription.uniformBlocks()) {
811 spvc_hlsl_resource_binding bindingMapping;
812 bindingMapping.stage = stage;
813 bindingMapping.desc_set = 0;
814 bindingMapping.binding = blk.binding;
815 bindingMapping.cbv.register_space = 0;
816 bindingMapping.cbv.register_binding = regBinding; // b0, b1, ...
817 spvc_compiler_hlsl_add_resource_binding(compiler: d->hlslGen, binding: &bindingMapping);
818 nativeBindings->insert(key: blk.binding, value: { regBinding, -1 });
819 regBinding += 1;
820 }
821
822 regBinding = 0; // UAVs
823 for (const QShaderDescription::StorageBlock &blk : d->shaderDescription.storageBlocks()) {
824 spvc_hlsl_resource_binding bindingMapping;
825 bindingMapping.stage = stage;
826 bindingMapping.desc_set = 0;
827 bindingMapping.binding = blk.binding;
828 bindingMapping.uav.register_space = 0;
829 bindingMapping.uav.register_binding = regBinding; // u0, u1, ...
830 spvc_compiler_hlsl_add_resource_binding(compiler: d->hlslGen, binding: &bindingMapping);
831 nativeBindings->insert(key: blk.binding, value: { regBinding, -1 });
832 regBinding += 1;
833 }
834 for (const QShaderDescription::InOutVariable &var : d->shaderDescription.storageImages()) {
835 spvc_hlsl_resource_binding bindingMapping;
836 bindingMapping.stage = stage;
837 bindingMapping.desc_set = 0;
838 bindingMapping.binding = var.binding;
839 bindingMapping.uav.register_space = 0;
840 bindingMapping.uav.register_binding = regBinding; // u0, u1, ...
841 spvc_compiler_hlsl_add_resource_binding(compiler: d->hlslGen, binding: &bindingMapping);
842 nativeBindings->insert(key: var.binding, value: { regBinding, -1 });
843 regBinding += 1;
844 }
845
846 const char *result = nullptr;
847 if (spvc_compiler_compile(compiler: d->hlslGen, source: &result) != SPVC_SUCCESS) {
848 d->spirvCrossErrorMsg = QString::fromUtf8(utf8: spvc_context_get_last_error_string(context: d->ctx));
849 return QByteArray();
850 }
851
852 return QByteArray(result);
853}
854
855QByteArray QSpirvShader::translateToMSL(int version,
856 MslFlags flags,
857 QShader::Stage stage,
858 QShader::NativeResourceBindingMap *nativeBindings,
859 QShader::NativeShaderInfo *shaderInfo,
860 const TessellationInfo &tessInfo) const
861{
862 d->spirvCrossErrorMsg.clear();
863
864 d->createCompiler(backend: SPVC_BACKEND_MSL);
865 if (!d->mslGen)
866 return QByteArray();
867
868 spvc_compiler_options options = nullptr;
869 if (spvc_compiler_create_compiler_options(compiler: d->mslGen, options: &options) != SPVC_SUCCESS)
870 return QByteArray();
871
872 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_VERSION,
873 SPVC_MAKE_MSL_VERSION(version / 10, version % 10, 0));
874
875 if (flags.testFlag(flag: MslFlag::VertexAsCompute)) {
876 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION, value: 1);
877 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER, value: 1);
878 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION, value: 1);
879
880 spvc_msl_index_type indexType = SPVC_MSL_INDEX_TYPE_NONE;
881 if (flags.testFlag(flag: MslFlag::WithUInt16Index))
882 indexType = SPVC_MSL_INDEX_TYPE_UINT16;
883 else if (flags.testFlag(flag: MslFlag::WithUInt32Index))
884 indexType = SPVC_MSL_INDEX_TYPE_UINT32;
885 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE, value: indexType);
886 }
887
888 // for tessellation; matches the defaults; not sure how to query so just set them
889 uint spvIndicesBufferIndex = 21;
890 uint spvInBufferIndex = 22;
891 uint spvTessLevelBufferIndex = 26;
892 uint spvPatchOutBufferIndex = 27;
893 uint spvOutBufferIndex = 28;
894 uint spvIndirectParamsBufferIndex = 29;
895
896 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX, value: spvIndicesBufferIndex);
897 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX, value: spvInBufferIndex);
898 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX, value: spvTessLevelBufferIndex);
899 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX, value: spvPatchOutBufferIndex);
900 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX, value: spvOutBufferIndex);
901 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX, value: spvIndirectParamsBufferIndex);
902
903 // for buffer size buffer; matches defaults
904 uint spvBufferSizeBufferIndex = 25;
905 spvc_compiler_options_set_uint(options, option: SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX, value: spvBufferSizeBufferIndex);
906
907 if (stage == QShader::TessellationControlStage) {
908 // required to get the kind of tess.control inputs we need
909 spvc_compiler_options_set_bool(options, option: SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP, value: 1);
910
911 // the execution mode needs to be known (e.g. to switch between
912 // MTLTriangleTessellationFactorsHalf and MTLQuadTessellationFactorsHalf)
913 SpvExecutionMode execMode = SpvExecutionModeTriangles;
914 switch (tessInfo.infoForTesc.mode) {
915 case QShaderDescription::QuadTessellationMode:
916 execMode = SpvExecutionModeQuads;
917 break;
918 case QShaderDescription::IsolineTessellationMode:
919 d->spirvCrossErrorMsg = QLatin1String("Isoline tessellation mode is not supported with Metal");
920 return QByteArray();
921 default:
922 break;
923 }
924 spvc_compiler_set_execution_mode(compiler: d->mslGen, mode: execMode);
925 }
926
927 if (stage == QShader::TessellationEvaluationStage)
928 spvc_compiler_set_execution_mode_with_arguments(compiler: d->mslGen, mode: SpvExecutionModeOutputVertices, arg0: uint(tessInfo.infoForTese.vertexCount), arg1: 0, arg2: 0);
929
930 // leave platform set to macOS, it won't matter in practice (hopefully)
931 spvc_compiler_install_compiler_options(compiler: d->mslGen, options);
932
933 const char *result = nullptr;
934 if (spvc_compiler_compile(compiler: d->mslGen, source: &result) != SPVC_SUCCESS) {
935 d->spirvCrossErrorMsg = QString::fromUtf8(utf8: spvc_context_get_last_error_string(context: d->ctx));
936 return QByteArray();
937 }
938
939 if (nativeBindings) {
940 spvc_resources resources;
941 if (spvc_compiler_create_shader_resources(compiler: d->mslGen, resources: &resources) == SPVC_SUCCESS) {
942 const spvc_reflected_resource *resourceList = nullptr;
943 size_t resourceListCount = 0;
944 // A nativeBinding of -1 means unused. This also fits
945 // *_get_automatic_resource_binding() which returns uint32_t(-1)
946 // when there is no binding, which can happen when the uniform
947 // block, sampler, etc. is not actively used in the shader. The map
948 // must always be complete, including a (binding -> -1) mapping for
949 // inactive resources as well. The second value of the pair is only
950 // relevant for combined image samplers, and is -1 otherwise.
951 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
952 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
953 {
954 for (size_t i = 0; i < resourceListCount; ++i) {
955 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
956 unsigned nativeBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
957 nativeBindings->insert(key: int(binding), value: { int(nativeBinding), -1 });
958 }
959 }
960 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
961 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
962 {
963 for (size_t i = 0; i < resourceListCount; ++i) {
964 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
965 unsigned nativeBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
966 nativeBindings->insert(key: int(binding), value: { int(nativeBinding), -1 });
967 }
968 }
969 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
970 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
971 {
972 for (size_t i = 0; i < resourceListCount; ++i) {
973 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
974 unsigned nativeTextureBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
975 unsigned nativeSamplerBinding = spvc_compiler_msl_get_automatic_resource_binding_secondary(compiler: d->mslGen, id: resourceList[i].id);
976 nativeBindings->insert(key: int(binding), value: { int(nativeTextureBinding), int(nativeSamplerBinding) });
977 }
978 }
979 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SEPARATE_IMAGE,
980 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
981 {
982 for (size_t i = 0; i < resourceListCount; ++i) {
983 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
984 unsigned nativeTextureBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
985 nativeBindings->insert(key: int(binding), value: { int(nativeTextureBinding), -1 });
986 }
987 }
988 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS,
989 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
990 {
991 for (size_t i = 0; i < resourceListCount; ++i) {
992 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
993 unsigned nativeSamplerBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
994 nativeBindings->insert(key: int(binding), value: { int(nativeSamplerBinding), -1 });
995 }
996 }
997 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
998 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
999 {
1000 for (size_t i = 0; i < resourceListCount; ++i) {
1001 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
1002 unsigned nativeTextureBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
1003 unsigned nativeSamplerBinding = spvc_compiler_msl_get_automatic_resource_binding_secondary(compiler: d->mslGen, id: resourceList[i].id);
1004 nativeBindings->insert(key: int(binding), value: { int(nativeTextureBinding), int(nativeSamplerBinding) });
1005 }
1006 }
1007 if (spvc_resources_get_resource_list_for_type(resources, type: SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
1008 resource_list: &resourceList, resource_size: &resourceListCount) == SPVC_SUCCESS)
1009 {
1010 for (size_t i = 0; i < resourceListCount; ++i) {
1011 unsigned binding = spvc_compiler_get_decoration(compiler: d->mslGen, id: resourceList[i].id, decoration: SpvDecorationBinding);
1012 unsigned nativeBinding = spvc_compiler_msl_get_automatic_resource_binding(compiler: d->mslGen, id: resourceList[i].id);
1013 nativeBindings->insert(key: int(binding), value: { int(nativeBinding), -1 });
1014 }
1015 }
1016 }
1017 }
1018
1019 if (spvc_compiler_msl_needs_swizzle_buffer(compiler: d->mslGen))
1020 qWarning(msg: "Translated Metal shader needs swizzle buffer, this is unexpected");
1021
1022 if (spvc_compiler_msl_needs_buffer_size_buffer(compiler: d->mslGen))
1023 shaderInfo->extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding] = spvBufferSizeBufferIndex;
1024
1025 // (Aim to) only store extraBufferBindings entries for things that really
1026 // are present, because the presence of a key can already trigger certain
1027 // behavior during rendering. (e.g. generating and exposing the
1028 // corresponding implicit buffer)
1029 switch (spvc_compiler_get_execution_model(compiler: d->mslGen)) {
1030 case SpvExecutionModelVertex:
1031 if (flags.testFlag(flag: MslFlag::VertexAsCompute)) {
1032 if (spvc_compiler_msl_needs_output_buffer(compiler: d->mslGen))
1033 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessVertTescOutputBufferBinding] = spvOutBufferIndex;
1034 if (flags.testFlag(flag: MslFlag::WithUInt16Index) || flags.testFlag(flag: MslFlag::WithUInt32Index)) {
1035 // not given that this buffer is really used in the code,
1036 // depends on e.g. the usage of gl_VertexIndex, but not sure how
1037 // to tell that here
1038 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessVertIndicesBufferBinding] = spvIndicesBufferIndex;
1039 }
1040 }
1041 break;
1042 case SpvExecutionModelTessellationControl:
1043 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessTescInputBufferBinding] = spvInBufferIndex;
1044 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessTescTessLevelBufferBinding] = spvTessLevelBufferIndex;
1045 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessTescParamsBufferBinding] = spvIndirectParamsBufferIndex;
1046 if (spvc_compiler_msl_needs_output_buffer(compiler: d->mslGen))
1047 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessVertTescOutputBufferBinding] = spvOutBufferIndex;
1048 if (spvc_compiler_msl_needs_patch_output_buffer(compiler: d->mslGen))
1049 shaderInfo->extraBufferBindings[QShaderPrivate::MslTessTescPatchOutputBufferBinding] = spvPatchOutBufferIndex;
1050 break;
1051 default:
1052 break;
1053 }
1054
1055 return QByteArray(result);
1056}
1057
1058QString QSpirvShader::translationErrorMessage() const
1059{
1060 return d->spirvCrossErrorMsg;
1061}
1062
1063QT_END_NAMESPACE
1064

source code of qtshadertools/src/shadertools/qspirvshader.cpp