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