• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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