1 /*
2 * Copyright 2015-2020 Arm Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "spirv_cpp.hpp"
18
19 using namespace spv;
20 using namespace SPIRV_CROSS_NAMESPACE;
21 using namespace std;
22
emit_buffer_block(const SPIRVariable & var)23 void CompilerCPP::emit_buffer_block(const SPIRVariable &var)
24 {
25 add_resource_name(var.self);
26
27 auto &type = get<SPIRType>(var.basetype);
28 auto instance_name = to_name(var.self);
29
30 uint32_t descriptor_set = ir.meta[var.self].decoration.set;
31 uint32_t binding = ir.meta[var.self].decoration.binding;
32
33 emit_block_struct(type);
34 auto buffer_name = to_name(type.self);
35
36 statement("internal::Resource<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
37 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
38 resource_registrations.push_back(
39 join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
40 statement("");
41 }
42
emit_interface_block(const SPIRVariable & var)43 void CompilerCPP::emit_interface_block(const SPIRVariable &var)
44 {
45 add_resource_name(var.self);
46
47 auto &type = get<SPIRType>(var.basetype);
48
49 const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput";
50 const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output";
51 auto instance_name = to_name(var.self);
52 uint32_t location = ir.meta[var.self].decoration.location;
53
54 string buffer_name;
55 auto flags = ir.meta[type.self].decoration.decoration_flags;
56 if (flags.get(DecorationBlock))
57 {
58 emit_block_struct(type);
59 buffer_name = to_name(type.self);
60 }
61 else
62 buffer_name = type_to_glsl(type);
63
64 statement("internal::", qual, "<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
65 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
66 resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");"));
67 statement("");
68 }
69
emit_shared(const SPIRVariable & var)70 void CompilerCPP::emit_shared(const SPIRVariable &var)
71 {
72 add_resource_name(var.self);
73
74 auto instance_name = to_name(var.self);
75 statement(CompilerGLSL::variable_decl(var), ";");
76 statement_no_indent("#define ", instance_name, " __res->", instance_name);
77 }
78
emit_uniform(const SPIRVariable & var)79 void CompilerCPP::emit_uniform(const SPIRVariable &var)
80 {
81 add_resource_name(var.self);
82
83 auto &type = get<SPIRType>(var.basetype);
84 auto instance_name = to_name(var.self);
85
86 uint32_t descriptor_set = ir.meta[var.self].decoration.set;
87 uint32_t binding = ir.meta[var.self].decoration.binding;
88 uint32_t location = ir.meta[var.self].decoration.location;
89
90 string type_name = type_to_glsl(type);
91 remap_variable_type_name(type, instance_name, type_name);
92
93 if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage ||
94 type.basetype == SPIRType::AtomicCounter)
95 {
96 statement("internal::Resource<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
97 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
98 resource_registrations.push_back(
99 join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
100 }
101 else
102 {
103 statement("internal::UniformConstant<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
104 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
105 resource_registrations.push_back(
106 join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");"));
107 }
108
109 statement("");
110 }
111
emit_push_constant_block(const SPIRVariable & var)112 void CompilerCPP::emit_push_constant_block(const SPIRVariable &var)
113 {
114 add_resource_name(var.self);
115
116 auto &type = get<SPIRType>(var.basetype);
117 auto &flags = ir.meta[var.self].decoration.decoration_flags;
118 if (flags.get(DecorationBinding) || flags.get(DecorationDescriptorSet))
119 SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
120 "Remap to location with reflection API first or disable these decorations.");
121
122 emit_block_struct(type);
123 auto buffer_name = to_name(type.self);
124 auto instance_name = to_name(var.self);
125
126 statement("internal::PushConstant<", buffer_name, type_to_array_glsl(type), "> ", instance_name, ";");
127 statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()");
128 resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");"));
129 statement("");
130 }
131
emit_block_struct(SPIRType & type)132 void CompilerCPP::emit_block_struct(SPIRType &type)
133 {
134 // C++ can't do interface blocks, so we fake it by emitting a separate struct.
135 // However, these structs are not allowed to alias anything, so remove it before
136 // emitting the struct.
137 //
138 // The type we have here needs to be resolved to the non-pointer type so we can remove aliases.
139 auto &self = get<SPIRType>(type.self);
140 self.type_alias = 0;
141 emit_struct(self);
142 }
143
emit_resources()144 void CompilerCPP::emit_resources()
145 {
146 for (auto &id : ir.ids)
147 {
148 if (id.get_type() == TypeConstant)
149 {
150 auto &c = id.get<SPIRConstant>();
151
152 bool needs_declaration = c.specialization || c.is_used_as_lut;
153
154 if (needs_declaration)
155 {
156 if (!options.vulkan_semantics && c.specialization)
157 {
158 c.specialization_constant_macro_name =
159 constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
160 }
161 emit_constant(c);
162 }
163 }
164 else if (id.get_type() == TypeConstantOp)
165 {
166 emit_specialization_constant_op(id.get<SPIRConstantOp>());
167 }
168 }
169
170 // Output all basic struct types which are not Block or BufferBlock as these are declared inplace
171 // when such variables are instantiated.
172 for (auto &id : ir.ids)
173 {
174 if (id.get_type() == TypeType)
175 {
176 auto &type = id.get<SPIRType>();
177 if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
178 (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
179 !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
180 {
181 emit_struct(type);
182 }
183 }
184 }
185
186 statement("struct Resources : ", resource_type);
187 begin_scope();
188
189 // Output UBOs and SSBOs
190 for (auto &id : ir.ids)
191 {
192 if (id.get_type() == TypeVariable)
193 {
194 auto &var = id.get<SPIRVariable>();
195 auto &type = get<SPIRType>(var.basetype);
196
197 if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassUniform &&
198 !is_hidden_variable(var) &&
199 (ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
200 ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
201 {
202 emit_buffer_block(var);
203 }
204 }
205 }
206
207 // Output push constant blocks
208 for (auto &id : ir.ids)
209 {
210 if (id.get_type() == TypeVariable)
211 {
212 auto &var = id.get<SPIRVariable>();
213 auto &type = get<SPIRType>(var.basetype);
214 if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer &&
215 type.storage == StorageClassPushConstant)
216 {
217 emit_push_constant_block(var);
218 }
219 }
220 }
221
222 // Output in/out interfaces.
223 for (auto &id : ir.ids)
224 {
225 if (id.get_type() == TypeVariable)
226 {
227 auto &var = id.get<SPIRVariable>();
228 auto &type = get<SPIRType>(var.basetype);
229
230 if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
231 (var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
232 interface_variable_exists_in_entry_point(var.self))
233 {
234 emit_interface_block(var);
235 }
236 }
237 }
238
239 // Output Uniform Constants (values, samplers, images, etc).
240 for (auto &id : ir.ids)
241 {
242 if (id.get_type() == TypeVariable)
243 {
244 auto &var = id.get<SPIRVariable>();
245 auto &type = get<SPIRType>(var.basetype);
246
247 if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
248 (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
249 {
250 emit_uniform(var);
251 }
252 }
253 }
254
255 // Global variables.
256 bool emitted = false;
257 for (auto global : global_variables)
258 {
259 auto &var = get<SPIRVariable>(global);
260 if (var.storage == StorageClassWorkgroup)
261 {
262 emit_shared(var);
263 emitted = true;
264 }
265 }
266
267 if (emitted)
268 statement("");
269
270 declare_undefined_values();
271
272 statement("inline void init(spirv_cross_shader& s)");
273 begin_scope();
274 statement(resource_type, "::init(s);");
275 for (auto ® : resource_registrations)
276 statement(reg);
277 end_scope();
278 resource_registrations.clear();
279
280 end_scope_decl();
281
282 statement("");
283 statement("Resources* __res;");
284 if (get_entry_point().model == ExecutionModelGLCompute)
285 statement("ComputePrivateResources __priv_res;");
286 statement("");
287
288 // Emit regular globals which are allocated per invocation.
289 emitted = false;
290 for (auto global : global_variables)
291 {
292 auto &var = get<SPIRVariable>(global);
293 if (var.storage == StorageClassPrivate)
294 {
295 if (var.storage == StorageClassWorkgroup)
296 emit_shared(var);
297 else
298 statement(CompilerGLSL::variable_decl(var), ";");
299 emitted = true;
300 }
301 }
302
303 if (emitted)
304 statement("");
305 }
306
compile()307 string CompilerCPP::compile()
308 {
309 ir.fixup_reserved_names();
310
311 // Do not deal with ES-isms like precision, older extensions and such.
312 options.es = false;
313 options.version = 450;
314 backend.float_literal_suffix = true;
315 backend.double_literal_suffix = false;
316 backend.long_long_literal_suffix = true;
317 backend.uint32_t_literal_suffix = true;
318 backend.basic_int_type = "int32_t";
319 backend.basic_uint_type = "uint32_t";
320 backend.swizzle_is_function = true;
321 backend.shared_is_implied = true;
322 backend.unsized_array_supported = false;
323 backend.explicit_struct_type = true;
324 backend.use_initializer_list = true;
325
326 fixup_type_alias();
327 reorder_type_alias();
328 build_function_control_flow_graphs_and_analyze();
329 update_active_builtins();
330
331 uint32_t pass_count = 0;
332 do
333 {
334 if (pass_count >= 3)
335 SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
336
337 resource_registrations.clear();
338 reset();
339
340 // Move constructor for this type is broken on GCC 4.9 ...
341 buffer.reset();
342
343 emit_header();
344 emit_resources();
345
346 emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
347
348 pass_count++;
349 } while (is_forcing_recompilation());
350
351 // Match opening scope of emit_header().
352 end_scope_decl();
353 // namespace
354 end_scope();
355
356 // Emit C entry points
357 emit_c_linkage();
358
359 // Entry point in CPP is always main() for the time being.
360 get_entry_point().name = "main";
361
362 return buffer.str();
363 }
364
emit_c_linkage()365 void CompilerCPP::emit_c_linkage()
366 {
367 statement("");
368
369 statement("spirv_cross_shader_t *spirv_cross_construct(void)");
370 begin_scope();
371 statement("return new ", impl_type, "();");
372 end_scope();
373
374 statement("");
375 statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)");
376 begin_scope();
377 statement("delete static_cast<", impl_type, "*>(shader);");
378 end_scope();
379
380 statement("");
381 statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)");
382 begin_scope();
383 statement("static_cast<", impl_type, "*>(shader)->invoke();");
384 end_scope();
385
386 statement("");
387 statement("static const struct spirv_cross_interface vtable =");
388 begin_scope();
389 statement("spirv_cross_construct,");
390 statement("spirv_cross_destruct,");
391 statement("spirv_cross_invoke,");
392 end_scope_decl();
393
394 statement("");
395 statement("const struct spirv_cross_interface *",
396 interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)");
397 begin_scope();
398 statement("return &vtable;");
399 end_scope();
400 }
401
emit_function_prototype(SPIRFunction & func,const Bitset &)402 void CompilerCPP::emit_function_prototype(SPIRFunction &func, const Bitset &)
403 {
404 if (func.self != ir.default_entry_point)
405 add_function_overload(func);
406
407 local_variable_names = resource_names;
408 string decl;
409
410 auto &type = get<SPIRType>(func.return_type);
411 decl += "inline ";
412 decl += type_to_glsl(type);
413 decl += " ";
414
415 if (func.self == ir.default_entry_point)
416 {
417 decl += "main";
418 processing_entry_point = true;
419 }
420 else
421 decl += to_name(func.self);
422
423 decl += "(";
424 for (auto &arg : func.arguments)
425 {
426 add_local_variable_name(arg.id);
427
428 decl += argument_decl(arg);
429 if (&arg != &func.arguments.back())
430 decl += ", ";
431
432 // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
433 auto *var = maybe_get<SPIRVariable>(arg.id);
434 if (var)
435 var->parameter = &arg;
436 }
437
438 decl += ")";
439 statement(decl);
440 }
441
argument_decl(const SPIRFunction::Parameter & arg)442 string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
443 {
444 auto &type = expression_type(arg.id);
445 bool constref = !type.pointer || arg.write_count == 0;
446
447 auto &var = get<SPIRVariable>(arg.id);
448
449 string base = type_to_glsl(type);
450 string variable_name = to_name(var.self);
451 remap_variable_type_name(type, variable_name, base);
452
453 for (uint32_t i = 0; i < type.array.size(); i++)
454 base = join("std::array<", base, ", ", to_array_size(type, i), ">");
455
456 return join(constref ? "const " : "", base, " &", variable_name);
457 }
458
variable_decl(const SPIRType & type,const string & name,uint32_t)459 string CompilerCPP::variable_decl(const SPIRType &type, const string &name, uint32_t /* id */)
460 {
461 string base = type_to_glsl(type);
462 remap_variable_type_name(type, name, base);
463 bool runtime = false;
464
465 for (uint32_t i = 0; i < type.array.size(); i++)
466 {
467 auto &array = type.array[i];
468 if (!array && type.array_size_literal[i])
469 {
470 // Avoid using runtime arrays with std::array since this is undefined.
471 // Runtime arrays cannot be passed around as values, so this is fine.
472 runtime = true;
473 }
474 else
475 base = join("std::array<", base, ", ", to_array_size(type, i), ">");
476 }
477 base += ' ';
478 return base + name + (runtime ? "[1]" : "");
479 }
480
emit_header()481 void CompilerCPP::emit_header()
482 {
483 auto &execution = get_entry_point();
484
485 statement("// This C++ shader is autogenerated by spirv-cross.");
486 statement("#include \"spirv_cross/internal_interface.hpp\"");
487 statement("#include \"spirv_cross/external_interface.h\"");
488 // Needed to properly implement GLSL-style arrays.
489 statement("#include <array>");
490 statement("#include <stdint.h>");
491 statement("");
492 statement("using namespace spirv_cross;");
493 statement("using namespace glm;");
494 statement("");
495
496 statement("namespace Impl");
497 begin_scope();
498
499 switch (execution.model)
500 {
501 case ExecutionModelGeometry:
502 case ExecutionModelTessellationControl:
503 case ExecutionModelTessellationEvaluation:
504 case ExecutionModelGLCompute:
505 case ExecutionModelFragment:
506 case ExecutionModelVertex:
507 statement("struct Shader");
508 begin_scope();
509 break;
510
511 default:
512 SPIRV_CROSS_THROW("Unsupported execution model.");
513 }
514
515 switch (execution.model)
516 {
517 case ExecutionModelGeometry:
518 impl_type = "GeometryShader<Impl::Shader, Impl::Shader::Resources>";
519 resource_type = "GeometryResources";
520 break;
521
522 case ExecutionModelVertex:
523 impl_type = "VertexShader<Impl::Shader, Impl::Shader::Resources>";
524 resource_type = "VertexResources";
525 break;
526
527 case ExecutionModelFragment:
528 impl_type = "FragmentShader<Impl::Shader, Impl::Shader::Resources>";
529 resource_type = "FragmentResources";
530 break;
531
532 case ExecutionModelGLCompute:
533 impl_type = join("ComputeShader<Impl::Shader, Impl::Shader::Resources, ", execution.workgroup_size.x, ", ",
534 execution.workgroup_size.y, ", ", execution.workgroup_size.z, ">");
535 resource_type = "ComputeResources";
536 break;
537
538 case ExecutionModelTessellationControl:
539 impl_type = "TessControlShader<Impl::Shader, Impl::Shader::Resources>";
540 resource_type = "TessControlResources";
541 break;
542
543 case ExecutionModelTessellationEvaluation:
544 impl_type = "TessEvaluationShader<Impl::Shader, Impl::Shader::Resources>";
545 resource_type = "TessEvaluationResources";
546 break;
547
548 default:
549 SPIRV_CROSS_THROW("Unsupported execution model.");
550 }
551 }
552