blob: ae34ec65cf663cc32880ebdb4bd9fc98ff2245dc [file] [log] [blame]
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001/*
2 * Copyright 2015-2016 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 "spir2cpp.hpp"
18
19using namespace spv;
20using namespace spir2cross;
21using namespace std;
22
23void CompilerCPP::emit_buffer_block(const SPIRVariable &var)
24{
25 auto &type = get<SPIRType>(var.basetype);
26 auto instance_name = to_name(var.self);
27
28 uint32_t set = meta[var.self].decoration.set;
29 uint32_t binding = meta[var.self].decoration.binding;
30
31 emit_struct(type);
32 statement("internal::Resource<", type_to_glsl(type), type_to_array_glsl(type), "> ", instance_name, "__;");
33 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
34 resource_registrations.push_back(join("s.register_resource(", instance_name, "__", ", ", set, ", ", binding, ");"));
35 statement("");
36}
37
38void CompilerCPP::emit_interface_block(const SPIRVariable &var)
39{
40 auto &type = get<SPIRType>(var.basetype);
41
42 const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput";
43 const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output";
44 auto instance_name = to_name(var.self);
45 uint32_t location = meta[var.self].decoration.location;
46
47 auto flags = meta[type.self].decoration.decoration_flags;
48 if (flags & (1ull << DecorationBlock))
49 emit_struct(type);
50
51 statement("internal::", qual, "<", type_to_glsl(type), type_to_array_glsl(type), "> ", instance_name, "__;");
52 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
53 resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");"));
54 statement("");
55}
56
57void CompilerCPP::emit_shared(const SPIRVariable &var)
58{
59 auto instance_name = to_name(var.self);
60 statement(variable_decl(var), ";");
61 statement_no_indent("#define ", instance_name, " __res->", instance_name);
62}
63
64void CompilerCPP::emit_uniform(const SPIRVariable &var)
65{
66 auto &type = get<SPIRType>(var.basetype);
67 auto instance_name = to_name(var.self);
68
69 uint32_t set = meta[var.self].decoration.set;
70 uint32_t binding = meta[var.self].decoration.binding;
71 uint32_t location = meta[var.self].decoration.location;
72
73 if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::AtomicCounter)
74 {
75 statement("internal::Resource<", type_to_glsl(type), type_to_array_glsl(type), "> ", instance_name, "__;");
76 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
77 resource_registrations.push_back(join("s.register_resource(", instance_name, "__", ", ", set, ", ", binding, ");"));
78 }
79 else
80 {
81 statement("internal::UniformConstant<", type_to_glsl(type), type_to_array_glsl(type), "> ", instance_name, "__;");
82 statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
83 resource_registrations.push_back(join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");"));
84 }
85
86 statement("");
87}
88
89void CompilerCPP::emit_push_constant_block(const SPIRVariable &var)
90{
91 auto &type = get<SPIRType>(var.basetype);
92 auto &flags = meta[var.self].decoration.decoration_flags;
93 if ((flags & (1ull << DecorationBinding)) || (flags & (1ull << DecorationDescriptorSet)))
94 throw CompilerError("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
95 "Remap to location with reflection API first or disable these decorations.");
96
97 emit_struct(type);
98 auto instance_name = to_name(var.self);
99
100 statement("internal::PushConstant<", type_to_glsl(type), type_to_array_glsl(type), "> ", instance_name, ";");
101 statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()");
102 resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");"));
103 statement("");
104}
105
106void CompilerCPP::emit_resources()
107{
108 // Output all basic struct types which are not Block or BufferBlock as these are declared inplace
109 // when such variables are instantiated.
110 for (auto &id : ids)
111 {
112 if (id.get_type() == TypeType)
113 {
114 auto &type = id.get<SPIRType>();
115 if (type.basetype == SPIRType::Struct &&
116 type.array.empty() &&
117 !type.pointer &&
118 (meta[type.self].decoration.decoration_flags & ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) == 0)
119 {
120 emit_struct(type);
121 }
122 }
123 }
124
125 statement("struct Resources : ", resource_type);
126 begin_scope();
127
128 // Output UBOs and SSBOs
129 for (auto &id : ids)
130 {
131 if (id.get_type() == TypeVariable)
132 {
133 auto &var = id.get<SPIRVariable>();
134 auto &type = get<SPIRType>(var.basetype);
135
136 if (type.pointer && type.storage == StorageClassUniform &&
137 !is_builtin_variable(var) &&
138 (meta[type.self].decoration.decoration_flags & ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))))
139 {
140 emit_buffer_block(var);
141 }
142 }
143 }
144
145 // Output push constant blocks
146 for (auto &id : ids)
147 {
148 if (id.get_type() == TypeVariable)
149 {
150 auto &var = id.get<SPIRVariable>();
151 auto &type = get<SPIRType>(var.basetype);
152 if (type.pointer && type.storage == StorageClassPushConstant)
153 emit_push_constant_block(var);
154 }
155 }
156
157 // Output in/out interfaces.
158 for (auto &id : ids)
159 {
160 if (id.get_type() == TypeVariable)
161 {
162 auto &var = id.get<SPIRVariable>();
163 auto &type = get<SPIRType>(var.basetype);
164
165 if (!is_builtin_variable(var) &&
166 !var.remapped_variable &&
167 type.pointer &&
168 (var.storage == StorageClassInput || var.storage == StorageClassOutput))
169 {
170 emit_interface_block(var);
171 }
172 }
173 }
174
175 // Output Uniform Constants (values, samplers, images, etc).
176 for (auto &id : ids)
177 {
178 if (id.get_type() == TypeVariable)
179 {
180 auto &var = id.get<SPIRVariable>();
181 auto &type = get<SPIRType>(var.basetype);
182
183 if (!is_builtin_variable(var) && !var.remapped_variable && type.pointer &&
184 (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
185 {
186 emit_uniform(var);
187 }
188 }
189 }
190
191 // Global variables.
192 bool emitted = false;
193 for (auto global : global_variables)
194 {
195 auto &var = get<SPIRVariable>(global);
196 if (var.storage == StorageClassWorkgroup)
197 {
198 emit_shared(var);
199 emitted = true;
200 }
201 }
202
203 if (emitted)
204 statement("");
205
206 statement("inline void init(spir2cross_shader& s)");
207 begin_scope();
208 statement(resource_type, "::init(s);");
209 for (auto &reg : resource_registrations)
210 statement(reg);
211 end_scope();
212 resource_registrations.clear();
213
214 end_scope_decl();
215
216 statement("");
217 statement("Resources* __res;");
218 if (execution.model == ExecutionModelGLCompute)
219 statement("ComputePrivateResources __priv_res;");
220 statement("");
221
222 // Emit regular globals which are allocated per invocation.
223 emitted = false;
224 for (auto global : global_variables)
225 {
226 auto &var = get<SPIRVariable>(global);
227 if (var.storage == StorageClassPrivate)
228 {
229 if (var.storage == StorageClassWorkgroup)
230 emit_shared(var);
231 else
232 statement(variable_decl(var), ";");
233 emitted = true;
234 }
235 }
236
237 if (emitted)
238 statement("");
239}
240
241string CompilerCPP::compile()
242{
243 // Do not deal with ES-isms like precision, older extensions and such.
244 options.es = false;
245 options.version = 450;
246 backend.float_literal_suffix = true;
247 backend.uint32_t_literal_suffix = true;
248 backend.basic_int_type = "int32_t";
249 backend.basic_uint_type = "uint32_t";
250 backend.swizzle_is_function = true;
251 backend.shared_is_implied = true;
252
253 uint32_t pass_count = 0;
254 do
255 {
256 if (pass_count >= 2)
257 throw CompilerError("Over 2 compilation loops detected. Must be a bug!");
258
259 resource_registrations.clear();
260 reset();
261
262 // Move constructor for this type is broken on GCC 4.9 ...
263 buffer = unique_ptr<ostringstream>(new ostringstream());
264
265 emit_header();
266 emit_resources();
267
268 emit_function(get<SPIRFunction>(execution.entry_point), 0);
269
270 pass_count++;
271 } while (force_recompile);
272
273 // Match opening scope of emit_header().
274 end_scope_decl();
275 // namespace
276 end_scope();
277
278 // Emit C entry points
279 emit_c_linkage();
280
281 return buffer->str();
282}
283
284void CompilerCPP::emit_c_linkage()
285{
286 statement("");
287
288 statement("spir2cross_shader_t* spir2cross_construct(void)");
289 begin_scope();
290 statement("return new ", impl_type, "();");
291 end_scope();
292
293 statement("");
294 statement("void spir2cross_destruct(spir2cross_shader_t *shader)");
295 begin_scope();
296 statement("delete static_cast<", impl_type, "*>(shader);");
297 end_scope();
298
299 statement("");
300 statement("void spir2cross_invoke(spir2cross_shader_t *shader)");
301 begin_scope();
302 statement("static_cast<", impl_type, "*>(shader)->invoke();");
303 end_scope();
304
305 statement("");
306 statement("static const struct spir2cross_interface vtable =");
307 begin_scope();
308 statement("spir2cross_construct,");
309 statement("spir2cross_destruct,");
310 statement("spir2cross_invoke,");
311 end_scope_decl();
312
313 statement("");
314 statement("const struct spir2cross_interface* spir2cross_get_interface(void)");
315 begin_scope();
316 statement("return &vtable;");
317 end_scope();
318}
319
320void CompilerCPP::emit_function_prototype(SPIRFunction &func, uint64_t)
321{
322 local_variables.clear();
323 string decl;
324
325 auto &type = get<SPIRType>(func.return_type);
326 decl += "inline ";
327 decl += type_to_glsl(type);
328 decl += " ";
329
330 if (func.self == execution.entry_point)
331 {
332 decl += "main";
333 processing_entry_point = true;
334 }
335 else
336 decl += to_name(func.self);
337
338 decl += "(";
339 for (auto &arg : func.arguments)
340 {
341 add_local_variable(arg.id);
342
343 decl += argument_decl(arg);
344 if (&arg != &func.arguments.back())
345 decl += ", ";
346
347 // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
348 auto *var = maybe_get<SPIRVariable>(arg.id);
349 if (var)
350 var->parameter = &arg;
351 }
352
353 decl += ")";
354 statement(decl);
355}
356
357string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
358{
359 auto &type = expression_type(arg.id);
360 bool constref = !type.pointer || arg.write_count == 0;
361
362 auto &var = get<SPIRVariable>(arg.id);
363 return join(constref ? "const " : "",
364 type_to_glsl(type), "& ", to_name(var.self), type_to_array_glsl(type));
365}
366
367void CompilerCPP::emit_header()
368{
369 statement("// This C++ shader is autogenerated by spir2cross.");
370 statement("#include \"spir2cross/internal_interface.hpp\"");
371 statement("#include \"spir2cross/external_interface.h\"");
372 statement("#include <stdint.h>");
373 statement("");
374 statement("using namespace spir2cross;");
375 statement("using namespace glm;");
376 statement("");
377
378 statement("namespace Impl");
379 begin_scope();
380
381 switch (execution.model)
382 {
383 case ExecutionModelGeometry:
384 case ExecutionModelTessellationControl:
385 case ExecutionModelTessellationEvaluation:
386 case ExecutionModelGLCompute:
387 case ExecutionModelFragment:
388 case ExecutionModelVertex:
389 statement("struct Shader");
390 begin_scope();
391 break;
392
393 default:
394 throw CompilerError("Unsupported execution model.");
395 }
396
397 switch (execution.model)
398 {
399 case ExecutionModelGeometry:
400 impl_type = "GeometryShader<Impl::Shader, Impl::Shader::Resources>";
401 resource_type = "GeometryResources";
402 break;
403
404 case ExecutionModelVertex:
405 impl_type = "VertexShader<Impl::Shader, Impl::Shader::Resources>";
406 resource_type = "VertexResources";
407 break;
408
409 case ExecutionModelFragment:
410 impl_type = "FragmentShader<Impl::Shader, Impl::Shader::Resources>";
411 resource_type = "FragmentResources";
412 break;
413
414 case ExecutionModelGLCompute:
415 impl_type = join("ComputeShader<Impl::Shader, Impl::Shader::Resources, ",
416 execution.workgroup_size.x, ", ",
417 execution.workgroup_size.y, ", ",
418 execution.workgroup_size.z, ">");
419 resource_type = "ComputeResources";
420 break;
421
422 case ExecutionModelTessellationControl:
423 impl_type = "TessControlShader<Impl::Shader, Impl::Shader::Resources>";
424 resource_type = "TessControlResources";
425 break;
426
427 case ExecutionModelTessellationEvaluation:
428 impl_type = "TessEvaluationShader<Impl::Shader, Impl::Shader::Resources>";
429 resource_type = "TessEvaluationResources";
430 break;
431
432 default:
433 throw CompilerError("Unsupported execution model.");
434 }
435}
436