1 /*
2 * Copyright 2015-2017 ARM Limited
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #ifndef SPIRV_CROSS_INTERNAL_INTERFACE_HPP
19 #define SPIRV_CROSS_INTERNAL_INTERFACE_HPP
20
21 // This file must only be included by the shader generated by spirv-cross!
22
23 #ifndef GLM_FORCE_SWIZZLE
24 #define GLM_FORCE_SWIZZLE
25 #endif
26
27 #ifndef GLM_FORCE_RADIANS
28 #define GLM_FORCE_RADIANS
29 #endif
30
31 #include <glm/glm.hpp>
32
33 #include "barrier.hpp"
34 #include "external_interface.h"
35 #include "image.hpp"
36 #include "sampler.hpp"
37 #include "thread_group.hpp"
38 #include <assert.h>
39 #include <stdint.h>
40
41 namespace internal
42 {
43 // Adaptor helpers to adapt GLSL access chain syntax to C++.
44 // Don't bother with arrays of arrays on uniforms ...
45 // Would likely need horribly complex variadic template munging.
46
47 template <typename T>
48 struct Interface
49 {
50 enum
51 {
52 ArraySize = 1,
53 Size = sizeof(T)
54 };
55
Interfaceinternal::Interface56 Interface()
57 : ptr(0)
58 {
59 }
getinternal::Interface60 T &get()
61 {
62 assert(ptr);
63 return *ptr;
64 }
65
66 T *ptr;
67 };
68
69 // For array types, return a pointer instead.
70 template <typename T, unsigned U>
71 struct Interface<T[U]>
72 {
73 enum
74 {
75 ArraySize = U,
76 Size = U * sizeof(T)
77 };
78
Interfaceinternal::Interface79 Interface()
80 : ptr(0)
81 {
82 }
getinternal::Interface83 T *get()
84 {
85 assert(ptr);
86 return ptr;
87 }
88
89 T *ptr;
90 };
91
92 // For case when array size is 1, avoid double dereference.
93 template <typename T>
94 struct PointerInterface
95 {
96 enum
97 {
98 ArraySize = 1,
99 Size = sizeof(T *)
100 };
101 enum
102 {
103 PreDereference = true
104 };
105
PointerInterfaceinternal::PointerInterface106 PointerInterface()
107 : ptr(0)
108 {
109 }
110
getinternal::PointerInterface111 T &get()
112 {
113 assert(ptr);
114 return *ptr;
115 }
116
117 T *ptr;
118 };
119
120 // Automatically converts a pointer down to reference to match GLSL syntax.
121 template <typename T>
122 struct DereferenceAdaptor
123 {
DereferenceAdaptorinternal::DereferenceAdaptor124 DereferenceAdaptor(T **ptr)
125 : ptr(ptr)
126 {
127 }
operator []internal::DereferenceAdaptor128 T &operator[](unsigned index) const
129 {
130 return *(ptr[index]);
131 }
132 T **ptr;
133 };
134
135 // We can't have a linear array of T* since T* can be an abstract type in case of samplers.
136 // We also need a list of pointers since we can have run-time length SSBOs.
137 template <typename T, unsigned U>
138 struct PointerInterface<T[U]>
139 {
140 enum
141 {
142 ArraySize = U,
143 Size = sizeof(T *) * U
144 };
145 enum
146 {
147 PreDereference = false
148 };
PointerInterfaceinternal::PointerInterface149 PointerInterface()
150 : ptr(0)
151 {
152 }
153
getinternal::PointerInterface154 DereferenceAdaptor<T> get()
155 {
156 assert(ptr);
157 return DereferenceAdaptor<T>(ptr);
158 }
159
160 T **ptr;
161 };
162
163 // Resources can be more abstract and be unsized,
164 // so we need to have an array of pointers for those cases.
165 template <typename T>
166 struct Resource : PointerInterface<T>
167 {
168 };
169
170 // POD with no unknown sizes, so we can express these as flat arrays.
171 template <typename T>
172 struct UniformConstant : Interface<T>
173 {
174 };
175 template <typename T>
176 struct StageInput : Interface<T>
177 {
178 };
179 template <typename T>
180 struct StageOutput : Interface<T>
181 {
182 };
183 template <typename T>
184 struct PushConstant : Interface<T>
185 {
186 };
187 }
188
189 struct spirv_cross_shader
190 {
191 struct PPSize
192 {
PPSizespirv_cross_shader::PPSize193 PPSize()
194 : ptr(0)
195 , size(0)
196 {
197 }
198 void **ptr;
199 size_t size;
200 };
201
202 struct PPSizeResource
203 {
PPSizeResourcespirv_cross_shader::PPSizeResource204 PPSizeResource()
205 : ptr(0)
206 , size(0)
207 , pre_dereference(false)
208 {
209 }
210 void **ptr;
211 size_t size;
212 bool pre_dereference;
213 };
214
215 PPSizeResource resources[SPIRV_CROSS_NUM_DESCRIPTOR_SETS][SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS];
216 PPSize stage_inputs[SPIRV_CROSS_NUM_STAGE_INPUTS];
217 PPSize stage_outputs[SPIRV_CROSS_NUM_STAGE_OUTPUTS];
218 PPSize uniform_constants[SPIRV_CROSS_NUM_UNIFORM_CONSTANTS];
219 PPSize push_constant;
220 PPSize builtins[SPIRV_CROSS_NUM_BUILTINS];
221
222 template <typename U>
register_builtinspirv_cross_shader223 void register_builtin(spirv_cross_builtin builtin, const U &value)
224 {
225 assert(!builtins[builtin].ptr);
226
227 builtins[builtin].ptr = (void **)&value.ptr;
228 builtins[builtin].size = sizeof(*value.ptr) * U::ArraySize;
229 }
230
set_builtinspirv_cross_shader231 void set_builtin(spirv_cross_builtin builtin, void *data, size_t size)
232 {
233 assert(builtins[builtin].ptr);
234 assert(size >= builtins[builtin].size);
235
236 *builtins[builtin].ptr = data;
237 }
238
239 template <typename U>
register_resourcespirv_cross_shader240 void register_resource(const internal::Resource<U> &value, unsigned set, unsigned binding)
241 {
242 assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS);
243 assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS);
244 assert(!resources[set][binding].ptr);
245
246 resources[set][binding].ptr = (void **)&value.ptr;
247 resources[set][binding].size = internal::Resource<U>::Size;
248 resources[set][binding].pre_dereference = internal::Resource<U>::PreDereference;
249 }
250
251 template <typename U>
register_stage_inputspirv_cross_shader252 void register_stage_input(const internal::StageInput<U> &value, unsigned location)
253 {
254 assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS);
255 assert(!stage_inputs[location].ptr);
256
257 stage_inputs[location].ptr = (void **)&value.ptr;
258 stage_inputs[location].size = internal::StageInput<U>::Size;
259 }
260
261 template <typename U>
register_stage_outputspirv_cross_shader262 void register_stage_output(const internal::StageOutput<U> &value, unsigned location)
263 {
264 assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS);
265 assert(!stage_outputs[location].ptr);
266
267 stage_outputs[location].ptr = (void **)&value.ptr;
268 stage_outputs[location].size = internal::StageOutput<U>::Size;
269 }
270
271 template <typename U>
register_uniform_constantspirv_cross_shader272 void register_uniform_constant(const internal::UniformConstant<U> &value, unsigned location)
273 {
274 assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS);
275 assert(!uniform_constants[location].ptr);
276
277 uniform_constants[location].ptr = (void **)&value.ptr;
278 uniform_constants[location].size = internal::UniformConstant<U>::Size;
279 }
280
281 template <typename U>
register_push_constantspirv_cross_shader282 void register_push_constant(const internal::PushConstant<U> &value)
283 {
284 assert(!push_constant.ptr);
285
286 push_constant.ptr = (void **)&value.ptr;
287 push_constant.size = internal::PushConstant<U>::Size;
288 }
289
set_stage_inputspirv_cross_shader290 void set_stage_input(unsigned location, void *data, size_t size)
291 {
292 assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS);
293 assert(stage_inputs[location].ptr);
294 assert(size >= stage_inputs[location].size);
295
296 *stage_inputs[location].ptr = data;
297 }
298
set_stage_outputspirv_cross_shader299 void set_stage_output(unsigned location, void *data, size_t size)
300 {
301 assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS);
302 assert(stage_outputs[location].ptr);
303 assert(size >= stage_outputs[location].size);
304
305 *stage_outputs[location].ptr = data;
306 }
307
set_uniform_constantspirv_cross_shader308 void set_uniform_constant(unsigned location, void *data, size_t size)
309 {
310 assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS);
311 assert(uniform_constants[location].ptr);
312 assert(size >= uniform_constants[location].size);
313
314 *uniform_constants[location].ptr = data;
315 }
316
set_push_constantspirv_cross_shader317 void set_push_constant(void *data, size_t size)
318 {
319 assert(push_constant.ptr);
320 assert(size >= push_constant.size);
321
322 *push_constant.ptr = data;
323 }
324
set_resourcespirv_cross_shader325 void set_resource(unsigned set, unsigned binding, void **data, size_t size)
326 {
327 assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS);
328 assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS);
329 assert(resources[set][binding].ptr);
330 assert(size >= resources[set][binding].size);
331
332 // We're using the regular PointerInterface, dereference ahead of time.
333 if (resources[set][binding].pre_dereference)
334 *resources[set][binding].ptr = *data;
335 else
336 *resources[set][binding].ptr = data;
337 }
338 };
339
340 namespace spirv_cross
341 {
342 template <typename T>
343 struct BaseShader : spirv_cross_shader
344 {
invokespirv_cross::BaseShader345 void invoke()
346 {
347 static_cast<T *>(this)->main();
348 }
349 };
350
351 struct FragmentResources
352 {
353 internal::StageOutput<glm::vec4> gl_FragCoord;
initspirv_cross::FragmentResources354 void init(spirv_cross_shader &s)
355 {
356 s.register_builtin(SPIRV_CROSS_BUILTIN_FRAG_COORD, gl_FragCoord);
357 }
358 #define gl_FragCoord __res->gl_FragCoord.get()
359 };
360
361 template <typename T, typename Res>
362 struct FragmentShader : BaseShader<FragmentShader<T, Res>>
363 {
mainspirv_cross::FragmentShader364 inline void main()
365 {
366 impl.main();
367 }
368
FragmentShaderspirv_cross::FragmentShader369 FragmentShader()
370 {
371 resources.init(*this);
372 impl.__res = &resources;
373 }
374
375 T impl;
376 Res resources;
377 };
378
379 struct VertexResources
380 {
381 internal::StageOutput<glm::vec4> gl_Position;
initspirv_cross::VertexResources382 void init(spirv_cross_shader &s)
383 {
384 s.register_builtin(SPIRV_CROSS_BUILTIN_POSITION, gl_Position);
385 }
386 #define gl_Position __res->gl_Position.get()
387 };
388
389 template <typename T, typename Res>
390 struct VertexShader : BaseShader<VertexShader<T, Res>>
391 {
mainspirv_cross::VertexShader392 inline void main()
393 {
394 impl.main();
395 }
396
VertexShaderspirv_cross::VertexShader397 VertexShader()
398 {
399 resources.init(*this);
400 impl.__res = &resources;
401 }
402
403 T impl;
404 Res resources;
405 };
406
407 struct TessEvaluationResources
408 {
initspirv_cross::TessEvaluationResources409 inline void init(spirv_cross_shader &)
410 {
411 }
412 };
413
414 template <typename T, typename Res>
415 struct TessEvaluationShader : BaseShader<TessEvaluationShader<T, Res>>
416 {
mainspirv_cross::TessEvaluationShader417 inline void main()
418 {
419 impl.main();
420 }
421
TessEvaluationShaderspirv_cross::TessEvaluationShader422 TessEvaluationShader()
423 {
424 resources.init(*this);
425 impl.__res = &resources;
426 }
427
428 T impl;
429 Res resources;
430 };
431
432 struct TessControlResources
433 {
initspirv_cross::TessControlResources434 inline void init(spirv_cross_shader &)
435 {
436 }
437 };
438
439 template <typename T, typename Res>
440 struct TessControlShader : BaseShader<TessControlShader<T, Res>>
441 {
mainspirv_cross::TessControlShader442 inline void main()
443 {
444 impl.main();
445 }
446
TessControlShaderspirv_cross::TessControlShader447 TessControlShader()
448 {
449 resources.init(*this);
450 impl.__res = &resources;
451 }
452
453 T impl;
454 Res resources;
455 };
456
457 struct GeometryResources
458 {
initspirv_cross::GeometryResources459 inline void init(spirv_cross_shader &)
460 {
461 }
462 };
463
464 template <typename T, typename Res>
465 struct GeometryShader : BaseShader<GeometryShader<T, Res>>
466 {
mainspirv_cross::GeometryShader467 inline void main()
468 {
469 impl.main();
470 }
471
GeometryShaderspirv_cross::GeometryShader472 GeometryShader()
473 {
474 resources.init(*this);
475 impl.__res = &resources;
476 }
477
478 T impl;
479 Res resources;
480 };
481
482 struct ComputeResources
483 {
484 internal::StageInput<glm::uvec3> gl_WorkGroupID__;
485 internal::StageInput<glm::uvec3> gl_NumWorkGroups__;
initspirv_cross::ComputeResources486 void init(spirv_cross_shader &s)
487 {
488 s.register_builtin(SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, gl_WorkGroupID__);
489 s.register_builtin(SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, gl_NumWorkGroups__);
490 }
491 #define gl_WorkGroupID __res->gl_WorkGroupID__.get()
492 #define gl_NumWorkGroups __res->gl_NumWorkGroups__.get()
493
494 Barrier barrier__;
495 #define barrier() __res->barrier__.wait()
496 };
497
498 struct ComputePrivateResources
499 {
500 uint32_t gl_LocalInvocationIndex__;
501 #define gl_LocalInvocationIndex __priv_res.gl_LocalInvocationIndex__
502 glm::uvec3 gl_LocalInvocationID__;
503 #define gl_LocalInvocationID __priv_res.gl_LocalInvocationID__
504 glm::uvec3 gl_GlobalInvocationID__;
505 #define gl_GlobalInvocationID __priv_res.gl_GlobalInvocationID__
506 };
507
508 template <typename T, typename Res, unsigned WorkGroupX, unsigned WorkGroupY, unsigned WorkGroupZ>
509 struct ComputeShader : BaseShader<ComputeShader<T, Res, WorkGroupX, WorkGroupY, WorkGroupZ>>
510 {
mainspirv_cross::ComputeShader511 inline void main()
512 {
513 resources.barrier__.reset_counter();
514
515 for (unsigned z = 0; z < WorkGroupZ; z++)
516 for (unsigned y = 0; y < WorkGroupY; y++)
517 for (unsigned x = 0; x < WorkGroupX; x++)
518 impl[z][y][x].__priv_res.gl_GlobalInvocationID__ =
519 glm::uvec3(WorkGroupX, WorkGroupY, WorkGroupZ) * resources.gl_WorkGroupID__.get() +
520 glm::uvec3(x, y, z);
521
522 group.run();
523 group.wait();
524 }
525
ComputeShaderspirv_cross::ComputeShader526 ComputeShader()
527 : group(&impl[0][0][0])
528 {
529 resources.init(*this);
530 resources.barrier__.set_release_divisor(WorkGroupX * WorkGroupY * WorkGroupZ);
531
532 unsigned i = 0;
533 for (unsigned z = 0; z < WorkGroupZ; z++)
534 {
535 for (unsigned y = 0; y < WorkGroupY; y++)
536 {
537 for (unsigned x = 0; x < WorkGroupX; x++)
538 {
539 impl[z][y][x].__priv_res.gl_LocalInvocationID__ = glm::uvec3(x, y, z);
540 impl[z][y][x].__priv_res.gl_LocalInvocationIndex__ = i++;
541 impl[z][y][x].__res = &resources;
542 }
543 }
544 }
545 }
546
547 T impl[WorkGroupZ][WorkGroupY][WorkGroupX];
548 ThreadGroup<T, WorkGroupX * WorkGroupY * WorkGroupZ> group;
549 Res resources;
550 };
551
memoryBarrierShared()552 inline void memoryBarrierShared()
553 {
554 Barrier::memoryBarrier();
555 }
memoryBarrier()556 inline void memoryBarrier()
557 {
558 Barrier::memoryBarrier();
559 }
560 // TODO: Rest of the barriers.
561
562 // Atomics
563 template <typename T>
atomicAdd(T & v,T a)564 inline T atomicAdd(T &v, T a)
565 {
566 static_assert(sizeof(std::atomic<T>) == sizeof(T), "Cannot cast properly to std::atomic<T>.");
567
568 // We need explicit memory barriers in GLSL to enfore any ordering.
569 // FIXME: Can we really cast this? There is no other way I think ...
570 return std::atomic_fetch_add_explicit(reinterpret_cast<std::atomic<T> *>(&v), a, std::memory_order_relaxed);
571 }
572 }
573
spirv_cross_set_stage_input(spirv_cross_shader_t * shader,unsigned location,void * data,size_t size)574 void spirv_cross_set_stage_input(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size)
575 {
576 shader->set_stage_input(location, data, size);
577 }
578
spirv_cross_set_stage_output(spirv_cross_shader_t * shader,unsigned location,void * data,size_t size)579 void spirv_cross_set_stage_output(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size)
580 {
581 shader->set_stage_output(location, data, size);
582 }
583
spirv_cross_set_uniform_constant(spirv_cross_shader_t * shader,unsigned location,void * data,size_t size)584 void spirv_cross_set_uniform_constant(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size)
585 {
586 shader->set_uniform_constant(location, data, size);
587 }
588
spirv_cross_set_resource(spirv_cross_shader_t * shader,unsigned set,unsigned binding,void ** data,size_t size)589 void spirv_cross_set_resource(spirv_cross_shader_t *shader, unsigned set, unsigned binding, void **data, size_t size)
590 {
591 shader->set_resource(set, binding, data, size);
592 }
593
spirv_cross_set_push_constant(spirv_cross_shader_t * shader,void * data,size_t size)594 void spirv_cross_set_push_constant(spirv_cross_shader_t *shader, void *data, size_t size)
595 {
596 shader->set_push_constant(data, size);
597 }
598
spirv_cross_set_builtin(spirv_cross_shader_t * shader,spirv_cross_builtin builtin,void * data,size_t size)599 void spirv_cross_set_builtin(spirv_cross_shader_t *shader, spirv_cross_builtin builtin, void *data, size_t size)
600 {
601 shader->set_builtin(builtin, data, size);
602 }
603
604 #endif
605