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 &reg : 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