1 /*
2  * Copyright 2015-2019 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 	// Do not deal with ES-isms like precision, older extensions and such.
310 	options.es = false;
311 	options.version = 450;
312 	backend.float_literal_suffix = true;
313 	backend.double_literal_suffix = false;
314 	backend.long_long_literal_suffix = true;
315 	backend.uint32_t_literal_suffix = true;
316 	backend.basic_int_type = "int32_t";
317 	backend.basic_uint_type = "uint32_t";
318 	backend.swizzle_is_function = true;
319 	backend.shared_is_implied = true;
320 	backend.unsized_array_supported = false;
321 	backend.explicit_struct_type = true;
322 	backend.use_initializer_list = true;
323 
324 	fixup_type_alias();
325 	reorder_type_alias();
326 	build_function_control_flow_graphs_and_analyze();
327 	update_active_builtins();
328 
329 	uint32_t pass_count = 0;
330 	do
331 	{
332 		if (pass_count >= 3)
333 			SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
334 
335 		resource_registrations.clear();
336 		reset();
337 
338 		// Move constructor for this type is broken on GCC 4.9 ...
339 		buffer.reset();
340 
341 		emit_header();
342 		emit_resources();
343 
344 		emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
345 
346 		pass_count++;
347 	} while (is_forcing_recompilation());
348 
349 	// Match opening scope of emit_header().
350 	end_scope_decl();
351 	// namespace
352 	end_scope();
353 
354 	// Emit C entry points
355 	emit_c_linkage();
356 
357 	// Entry point in CPP is always main() for the time being.
358 	get_entry_point().name = "main";
359 
360 	return buffer.str();
361 }
362 
emit_c_linkage()363 void CompilerCPP::emit_c_linkage()
364 {
365 	statement("");
366 
367 	statement("spirv_cross_shader_t *spirv_cross_construct(void)");
368 	begin_scope();
369 	statement("return new ", impl_type, "();");
370 	end_scope();
371 
372 	statement("");
373 	statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)");
374 	begin_scope();
375 	statement("delete static_cast<", impl_type, "*>(shader);");
376 	end_scope();
377 
378 	statement("");
379 	statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)");
380 	begin_scope();
381 	statement("static_cast<", impl_type, "*>(shader)->invoke();");
382 	end_scope();
383 
384 	statement("");
385 	statement("static const struct spirv_cross_interface vtable =");
386 	begin_scope();
387 	statement("spirv_cross_construct,");
388 	statement("spirv_cross_destruct,");
389 	statement("spirv_cross_invoke,");
390 	end_scope_decl();
391 
392 	statement("");
393 	statement("const struct spirv_cross_interface *",
394 	          interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)");
395 	begin_scope();
396 	statement("return &vtable;");
397 	end_scope();
398 }
399 
emit_function_prototype(SPIRFunction & func,const Bitset &)400 void CompilerCPP::emit_function_prototype(SPIRFunction &func, const Bitset &)
401 {
402 	if (func.self != ir.default_entry_point)
403 		add_function_overload(func);
404 
405 	local_variable_names = resource_names;
406 	string decl;
407 
408 	auto &type = get<SPIRType>(func.return_type);
409 	decl += "inline ";
410 	decl += type_to_glsl(type);
411 	decl += " ";
412 
413 	if (func.self == ir.default_entry_point)
414 	{
415 		decl += "main";
416 		processing_entry_point = true;
417 	}
418 	else
419 		decl += to_name(func.self);
420 
421 	decl += "(";
422 	for (auto &arg : func.arguments)
423 	{
424 		add_local_variable_name(arg.id);
425 
426 		decl += argument_decl(arg);
427 		if (&arg != &func.arguments.back())
428 			decl += ", ";
429 
430 		// Hold a pointer to the parameter so we can invalidate the readonly field if needed.
431 		auto *var = maybe_get<SPIRVariable>(arg.id);
432 		if (var)
433 			var->parameter = &arg;
434 	}
435 
436 	decl += ")";
437 	statement(decl);
438 }
439 
argument_decl(const SPIRFunction::Parameter & arg)440 string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
441 {
442 	auto &type = expression_type(arg.id);
443 	bool constref = !type.pointer || arg.write_count == 0;
444 
445 	auto &var = get<SPIRVariable>(arg.id);
446 
447 	string base = type_to_glsl(type);
448 	string variable_name = to_name(var.self);
449 	remap_variable_type_name(type, variable_name, base);
450 
451 	for (uint32_t i = 0; i < type.array.size(); i++)
452 		base = join("std::array<", base, ", ", to_array_size(type, i), ">");
453 
454 	return join(constref ? "const " : "", base, " &", variable_name);
455 }
456 
variable_decl(const SPIRType & type,const string & name,uint32_t)457 string CompilerCPP::variable_decl(const SPIRType &type, const string &name, uint32_t /* id */)
458 {
459 	string base = type_to_glsl(type);
460 	remap_variable_type_name(type, name, base);
461 	bool runtime = false;
462 
463 	for (uint32_t i = 0; i < type.array.size(); i++)
464 	{
465 		auto &array = type.array[i];
466 		if (!array && type.array_size_literal[i])
467 		{
468 			// Avoid using runtime arrays with std::array since this is undefined.
469 			// Runtime arrays cannot be passed around as values, so this is fine.
470 			runtime = true;
471 		}
472 		else
473 			base = join("std::array<", base, ", ", to_array_size(type, i), ">");
474 	}
475 	base += ' ';
476 	return base + name + (runtime ? "[1]" : "");
477 }
478 
emit_header()479 void CompilerCPP::emit_header()
480 {
481 	auto &execution = get_entry_point();
482 
483 	statement("// This C++ shader is autogenerated by spirv-cross.");
484 	statement("#include \"spirv_cross/internal_interface.hpp\"");
485 	statement("#include \"spirv_cross/external_interface.h\"");
486 	// Needed to properly implement GLSL-style arrays.
487 	statement("#include <array>");
488 	statement("#include <stdint.h>");
489 	statement("");
490 	statement("using namespace spirv_cross;");
491 	statement("using namespace glm;");
492 	statement("");
493 
494 	statement("namespace Impl");
495 	begin_scope();
496 
497 	switch (execution.model)
498 	{
499 	case ExecutionModelGeometry:
500 	case ExecutionModelTessellationControl:
501 	case ExecutionModelTessellationEvaluation:
502 	case ExecutionModelGLCompute:
503 	case ExecutionModelFragment:
504 	case ExecutionModelVertex:
505 		statement("struct Shader");
506 		begin_scope();
507 		break;
508 
509 	default:
510 		SPIRV_CROSS_THROW("Unsupported execution model.");
511 	}
512 
513 	switch (execution.model)
514 	{
515 	case ExecutionModelGeometry:
516 		impl_type = "GeometryShader<Impl::Shader, Impl::Shader::Resources>";
517 		resource_type = "GeometryResources";
518 		break;
519 
520 	case ExecutionModelVertex:
521 		impl_type = "VertexShader<Impl::Shader, Impl::Shader::Resources>";
522 		resource_type = "VertexResources";
523 		break;
524 
525 	case ExecutionModelFragment:
526 		impl_type = "FragmentShader<Impl::Shader, Impl::Shader::Resources>";
527 		resource_type = "FragmentResources";
528 		break;
529 
530 	case ExecutionModelGLCompute:
531 		impl_type = join("ComputeShader<Impl::Shader, Impl::Shader::Resources, ", execution.workgroup_size.x, ", ",
532 		                 execution.workgroup_size.y, ", ", execution.workgroup_size.z, ">");
533 		resource_type = "ComputeResources";
534 		break;
535 
536 	case ExecutionModelTessellationControl:
537 		impl_type = "TessControlShader<Impl::Shader, Impl::Shader::Resources>";
538 		resource_type = "TessControlResources";
539 		break;
540 
541 	case ExecutionModelTessellationEvaluation:
542 		impl_type = "TessEvaluationShader<Impl::Shader, Impl::Shader::Resources>";
543 		resource_type = "TessEvaluationResources";
544 		break;
545 
546 	default:
547 		SPIRV_CROSS_THROW("Unsupported execution model.");
548 	}
549 }
550