1 /*
2  * Copyright 2018-2020 Bradley Austin Davis
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_reflect.hpp"
18 #include "spirv_glsl.hpp"
19 #include <iomanip>
20 
21 using namespace spv;
22 using namespace SPIRV_CROSS_NAMESPACE;
23 using namespace std;
24 
25 namespace simple_json
26 {
27 enum class Type
28 {
29 	Object,
30 	Array,
31 };
32 
33 using State = std::pair<Type, bool>;
34 using Stack = std::stack<State>;
35 
36 class Stream
37 {
38 	Stack stack;
39 	StringStream<> buffer;
40 	uint32_t indent{ 0 };
41 	char current_locale_radix_character = '.';
42 
43 public:
set_current_locale_radix_character(char c)44 	void set_current_locale_radix_character(char c)
45 	{
46 		current_locale_radix_character = c;
47 	}
48 
49 	void begin_json_object();
50 	void end_json_object();
51 	void emit_json_key(const std::string &key);
52 	void emit_json_key_value(const std::string &key, const std::string &value);
53 	void emit_json_key_value(const std::string &key, bool value);
54 	void emit_json_key_value(const std::string &key, uint32_t value);
55 	void emit_json_key_value(const std::string &key, int32_t value);
56 	void emit_json_key_value(const std::string &key, float value);
57 	void emit_json_key_object(const std::string &key);
58 	void emit_json_key_array(const std::string &key);
59 
60 	void begin_json_array();
61 	void end_json_array();
62 	void emit_json_array_value(const std::string &value);
63 	void emit_json_array_value(uint32_t value);
64 	void emit_json_array_value(bool value);
65 
str() const66 	std::string str() const
67 	{
68 		return buffer.str();
69 	}
70 
71 private:
statement_indent()72 	inline void statement_indent()
73 	{
74 		for (uint32_t i = 0; i < indent; i++)
75 			buffer << "    ";
76 	}
77 
78 	template <typename T>
statement_inner(T && t)79 	inline void statement_inner(T &&t)
80 	{
81 		buffer << std::forward<T>(t);
82 	}
83 
84 	template <typename T, typename... Ts>
statement_inner(T && t,Ts &&...ts)85 	inline void statement_inner(T &&t, Ts &&... ts)
86 	{
87 		buffer << std::forward<T>(t);
88 		statement_inner(std::forward<Ts>(ts)...);
89 	}
90 
91 	template <typename... Ts>
statement(Ts &&...ts)92 	inline void statement(Ts &&... ts)
93 	{
94 		statement_indent();
95 		statement_inner(std::forward<Ts>(ts)...);
96 		buffer << '\n';
97 	}
98 
99 	template <typename... Ts>
statement_no_return(Ts &&...ts)100 	void statement_no_return(Ts &&... ts)
101 	{
102 		statement_indent();
103 		statement_inner(std::forward<Ts>(ts)...);
104 	}
105 };
106 } // namespace simple_json
107 
108 using namespace simple_json;
109 
110 // Hackery to emit JSON without using nlohmann/json C++ library (which requires a
111 // higher level of compiler compliance than is required by SPIRV-Cross
begin_json_array()112 void Stream::begin_json_array()
113 {
114 	if (!stack.empty() && stack.top().second)
115 	{
116 		statement_inner(",\n");
117 	}
118 	statement("[");
119 	++indent;
120 	stack.emplace(Type::Array, false);
121 }
122 
end_json_array()123 void Stream::end_json_array()
124 {
125 	if (stack.empty() || stack.top().first != Type::Array)
126 		SPIRV_CROSS_THROW("Invalid JSON state");
127 	if (stack.top().second)
128 	{
129 		statement_inner("\n");
130 	}
131 	--indent;
132 	statement_no_return("]");
133 	stack.pop();
134 	if (!stack.empty())
135 	{
136 		stack.top().second = true;
137 	}
138 }
139 
emit_json_array_value(const std::string & value)140 void Stream::emit_json_array_value(const std::string &value)
141 {
142 	if (stack.empty() || stack.top().first != Type::Array)
143 		SPIRV_CROSS_THROW("Invalid JSON state");
144 
145 	if (stack.top().second)
146 		statement_inner(",\n");
147 
148 	statement_no_return("\"", value, "\"");
149 	stack.top().second = true;
150 }
151 
emit_json_array_value(uint32_t value)152 void Stream::emit_json_array_value(uint32_t value)
153 {
154 	if (stack.empty() || stack.top().first != Type::Array)
155 		SPIRV_CROSS_THROW("Invalid JSON state");
156 	if (stack.top().second)
157 		statement_inner(",\n");
158 	statement_no_return(std::to_string(value));
159 	stack.top().second = true;
160 }
161 
emit_json_array_value(bool value)162 void Stream::emit_json_array_value(bool value)
163 {
164 	if (stack.empty() || stack.top().first != Type::Array)
165 		SPIRV_CROSS_THROW("Invalid JSON state");
166 	if (stack.top().second)
167 		statement_inner(",\n");
168 	statement_no_return(value ? "true" : "false");
169 	stack.top().second = true;
170 }
171 
begin_json_object()172 void Stream::begin_json_object()
173 {
174 	if (!stack.empty() && stack.top().second)
175 	{
176 		statement_inner(",\n");
177 	}
178 	statement("{");
179 	++indent;
180 	stack.emplace(Type::Object, false);
181 }
182 
end_json_object()183 void Stream::end_json_object()
184 {
185 	if (stack.empty() || stack.top().first != Type::Object)
186 		SPIRV_CROSS_THROW("Invalid JSON state");
187 	if (stack.top().second)
188 	{
189 		statement_inner("\n");
190 	}
191 	--indent;
192 	statement_no_return("}");
193 	stack.pop();
194 	if (!stack.empty())
195 	{
196 		stack.top().second = true;
197 	}
198 }
199 
emit_json_key(const std::string & key)200 void Stream::emit_json_key(const std::string &key)
201 {
202 	if (stack.empty() || stack.top().first != Type::Object)
203 		SPIRV_CROSS_THROW("Invalid JSON state");
204 
205 	if (stack.top().second)
206 		statement_inner(",\n");
207 	statement_no_return("\"", key, "\" : ");
208 	stack.top().second = true;
209 }
210 
emit_json_key_value(const std::string & key,const std::string & value)211 void Stream::emit_json_key_value(const std::string &key, const std::string &value)
212 {
213 	emit_json_key(key);
214 	statement_inner("\"", value, "\"");
215 }
216 
emit_json_key_value(const std::string & key,uint32_t value)217 void Stream::emit_json_key_value(const std::string &key, uint32_t value)
218 {
219 	emit_json_key(key);
220 	statement_inner(value);
221 }
222 
emit_json_key_value(const std::string & key,int32_t value)223 void Stream::emit_json_key_value(const std::string &key, int32_t value)
224 {
225 	emit_json_key(key);
226 	statement_inner(value);
227 }
228 
emit_json_key_value(const std::string & key,float value)229 void Stream::emit_json_key_value(const std::string &key, float value)
230 {
231 	emit_json_key(key);
232 	statement_inner(convert_to_string(value, current_locale_radix_character));
233 }
234 
emit_json_key_value(const std::string & key,bool value)235 void Stream::emit_json_key_value(const std::string &key, bool value)
236 {
237 	emit_json_key(key);
238 	statement_inner(value ? "true" : "false");
239 }
240 
emit_json_key_object(const std::string & key)241 void Stream::emit_json_key_object(const std::string &key)
242 {
243 	emit_json_key(key);
244 	statement_inner("{\n");
245 	++indent;
246 	stack.emplace(Type::Object, false);
247 }
248 
emit_json_key_array(const std::string & key)249 void Stream::emit_json_key_array(const std::string &key)
250 {
251 	emit_json_key(key);
252 	statement_inner("[\n");
253 	++indent;
254 	stack.emplace(Type::Array, false);
255 }
256 
set_format(const std::string & format)257 void CompilerReflection::set_format(const std::string &format)
258 {
259 	if (format != "json")
260 	{
261 		SPIRV_CROSS_THROW("Unsupported format");
262 	}
263 }
264 
compile()265 string CompilerReflection::compile()
266 {
267 	json_stream = std::make_shared<simple_json::Stream>();
268 	json_stream->set_current_locale_radix_character(current_locale_radix_character);
269 	json_stream->begin_json_object();
270 	reorder_type_alias();
271 	emit_entry_points();
272 	emit_types();
273 	emit_resources();
274 	emit_specialization_constants();
275 	json_stream->end_json_object();
276 	return json_stream->str();
277 }
278 
naturally_emit_type(const SPIRType & type)279 static bool naturally_emit_type(const SPIRType &type)
280 {
281 	return type.basetype == SPIRType::Struct && !type.pointer && type.array.empty();
282 }
283 
type_is_reference(const SPIRType & type) const284 bool CompilerReflection::type_is_reference(const SPIRType &type) const
285 {
286 	// Physical pointers and arrays of physical pointers need to refer to the pointee's type.
287 	return type_is_top_level_physical_pointer(type) ||
288 	       (!type.array.empty() && type_is_top_level_physical_pointer(get<SPIRType>(type.parent_type)));
289 }
290 
emit_types()291 void CompilerReflection::emit_types()
292 {
293 	bool emitted_open_tag = false;
294 
295 	SmallVector<uint32_t> physical_pointee_types;
296 
297 	// If we have physical pointers or arrays of physical pointers, it's also helpful to emit the pointee type
298 	// and chain the type hierarchy. For POD, arrays can emit the entire type in-place.
299 	ir.for_each_typed_id<SPIRType>([&](uint32_t self, SPIRType &type) {
300 		if (naturally_emit_type(type))
301 		{
302 			emit_type(self, emitted_open_tag);
303 		}
304 		else if (type_is_reference(type))
305 		{
306 			if (!naturally_emit_type(this->get<SPIRType>(type.parent_type)) &&
307 			    find(physical_pointee_types.begin(), physical_pointee_types.end(), type.parent_type) ==
308 			        physical_pointee_types.end())
309 			{
310 				physical_pointee_types.push_back(type.parent_type);
311 			}
312 		}
313 	});
314 
315 	for (uint32_t pointee_type : physical_pointee_types)
316 		emit_type(pointee_type, emitted_open_tag);
317 
318 	if (emitted_open_tag)
319 	{
320 		json_stream->end_json_object();
321 	}
322 }
323 
emit_type(uint32_t type_id,bool & emitted_open_tag)324 void CompilerReflection::emit_type(uint32_t type_id, bool &emitted_open_tag)
325 {
326 	auto &type = get<SPIRType>(type_id);
327 	auto name = type_to_glsl(type);
328 
329 	if (!emitted_open_tag)
330 	{
331 		json_stream->emit_json_key_object("types");
332 		emitted_open_tag = true;
333 	}
334 	json_stream->emit_json_key_object("_" + std::to_string(type_id));
335 	json_stream->emit_json_key_value("name", name);
336 
337 	if (type_is_top_level_physical_pointer(type))
338 	{
339 		json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type));
340 		json_stream->emit_json_key_value("physical_pointer", true);
341 	}
342 	else if (!type.array.empty())
343 	{
344 		emit_type_array(type);
345 		json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type));
346 		json_stream->emit_json_key_value("array_stride", get_decoration(type_id, DecorationArrayStride));
347 	}
348 	else
349 	{
350 		json_stream->emit_json_key_array("members");
351 		// FIXME ideally we'd like to emit the size of a structure as a
352 		// convenience to people parsing the reflected JSON.  The problem
353 		// is that there's no implicit size for a type.  It's final size
354 		// will be determined by the top level declaration in which it's
355 		// included.  So there might be one size for the struct if it's
356 		// included in a std140 uniform block and another if it's included
357 		// in a std430 uniform block.
358 		// The solution is to include *all* potential sizes as a map of
359 		// layout type name to integer, but that will probably require
360 		// some additional logic being written in this class, or in the
361 		// parent CompilerGLSL class.
362 		auto size = type.member_types.size();
363 		for (uint32_t i = 0; i < size; ++i)
364 		{
365 			emit_type_member(type, i);
366 		}
367 		json_stream->end_json_array();
368 	}
369 
370 	json_stream->end_json_object();
371 }
372 
emit_type_member(const SPIRType & type,uint32_t index)373 void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index)
374 {
375 	auto &membertype = get<SPIRType>(type.member_types[index]);
376 	json_stream->begin_json_object();
377 	auto name = to_member_name(type, index);
378 	// FIXME we'd like to emit the offset of each member, but such offsets are
379 	// context dependent.  See the comment above regarding structure sizes
380 	json_stream->emit_json_key_value("name", name);
381 
382 	if (type_is_reference(membertype))
383 	{
384 		json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.parent_type));
385 	}
386 	else if (membertype.basetype == SPIRType::Struct)
387 	{
388 		json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self));
389 	}
390 	else
391 	{
392 		json_stream->emit_json_key_value("type", type_to_glsl(membertype));
393 	}
394 	emit_type_member_qualifiers(type, index);
395 	json_stream->end_json_object();
396 }
397 
emit_type_array(const SPIRType & type)398 void CompilerReflection::emit_type_array(const SPIRType &type)
399 {
400 	if (!type_is_top_level_physical_pointer(type) && !type.array.empty())
401 	{
402 		json_stream->emit_json_key_array("array");
403 		// Note that we emit the zeros here as a means of identifying
404 		// unbounded arrays.  This is necessary as otherwise there would
405 		// be no way of differentiating between float[4] and float[4][]
406 		for (const auto &value : type.array)
407 			json_stream->emit_json_array_value(value);
408 		json_stream->end_json_array();
409 
410 		json_stream->emit_json_key_array("array_size_is_literal");
411 		for (const auto &value : type.array_size_literal)
412 			json_stream->emit_json_array_value(value);
413 		json_stream->end_json_array();
414 	}
415 }
416 
emit_type_member_qualifiers(const SPIRType & type,uint32_t index)417 void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index)
418 {
419 	auto &membertype = get<SPIRType>(type.member_types[index]);
420 	emit_type_array(membertype);
421 	auto &memb = ir.meta[type.self].members;
422 	if (index < memb.size())
423 	{
424 		auto &dec = memb[index];
425 		if (dec.decoration_flags.get(DecorationLocation))
426 			json_stream->emit_json_key_value("location", dec.location);
427 		if (dec.decoration_flags.get(DecorationOffset))
428 			json_stream->emit_json_key_value("offset", dec.offset);
429 
430 		// Array stride is a property of the array type, not the struct.
431 		if (has_decoration(type.member_types[index], DecorationArrayStride))
432 			json_stream->emit_json_key_value("array_stride",
433 			                                 get_decoration(type.member_types[index], DecorationArrayStride));
434 
435 		if (dec.decoration_flags.get(DecorationMatrixStride))
436 			json_stream->emit_json_key_value("matrix_stride", dec.matrix_stride);
437 		if (dec.decoration_flags.get(DecorationRowMajor))
438 			json_stream->emit_json_key_value("row_major", true);
439 
440 		if (type_is_top_level_physical_pointer(membertype))
441 			json_stream->emit_json_key_value("physical_pointer", true);
442 	}
443 }
444 
execution_model_to_str(spv::ExecutionModel model)445 string CompilerReflection::execution_model_to_str(spv::ExecutionModel model)
446 {
447 	switch (model)
448 	{
449 	case ExecutionModelVertex:
450 		return "vert";
451 	case ExecutionModelTessellationControl:
452 		return "tesc";
453 	case ExecutionModelTessellationEvaluation:
454 		return "tese";
455 	case ExecutionModelGeometry:
456 		return "geom";
457 	case ExecutionModelFragment:
458 		return "frag";
459 	case ExecutionModelGLCompute:
460 		return "comp";
461 	case ExecutionModelRayGenerationNV:
462 		return "rgen";
463 	case ExecutionModelIntersectionNV:
464 		return "rint";
465 	case ExecutionModelAnyHitNV:
466 		return "rahit";
467 	case ExecutionModelClosestHitNV:
468 		return "rchit";
469 	case ExecutionModelMissNV:
470 		return "rmiss";
471 	case ExecutionModelCallableNV:
472 		return "rcall";
473 	default:
474 		return "???";
475 	}
476 }
477 
478 // FIXME include things like the local_size dimensions, geometry output vertex count, etc
emit_entry_points()479 void CompilerReflection::emit_entry_points()
480 {
481 	auto entries = get_entry_points_and_stages();
482 	if (!entries.empty())
483 	{
484 		// Needed to make output deterministic.
485 		sort(begin(entries), end(entries), [](const EntryPoint &a, const EntryPoint &b) -> bool {
486 			if (a.execution_model < b.execution_model)
487 				return true;
488 			else if (a.execution_model > b.execution_model)
489 				return false;
490 			else
491 				return a.name < b.name;
492 		});
493 
494 		json_stream->emit_json_key_array("entryPoints");
495 		for (auto &e : entries)
496 		{
497 			json_stream->begin_json_object();
498 			json_stream->emit_json_key_value("name", e.name);
499 			json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model));
500 			if (e.execution_model == ExecutionModelGLCompute)
501 			{
502 				const auto &spv_entry = get_entry_point(e.name, e.execution_model);
503 
504 				SpecializationConstant spec_x, spec_y, spec_z;
505 				get_work_group_size_specialization_constants(spec_x, spec_y, spec_z);
506 
507 				json_stream->emit_json_key_array("workgroup_size");
508 				json_stream->emit_json_array_value(spec_x.id != ID(0) ? spec_x.constant_id :
509 				                                                        spv_entry.workgroup_size.x);
510 				json_stream->emit_json_array_value(spec_y.id != ID(0) ? spec_y.constant_id :
511 				                                                        spv_entry.workgroup_size.y);
512 				json_stream->emit_json_array_value(spec_z.id != ID(0) ? spec_z.constant_id :
513 				                                                        spv_entry.workgroup_size.z);
514 				json_stream->end_json_array();
515 
516 				json_stream->emit_json_key_array("workgroup_size_is_spec_constant_id");
517 				json_stream->emit_json_array_value(spec_x.id != ID(0));
518 				json_stream->emit_json_array_value(spec_y.id != ID(0));
519 				json_stream->emit_json_array_value(spec_z.id != ID(0));
520 				json_stream->end_json_array();
521 			}
522 			json_stream->end_json_object();
523 		}
524 		json_stream->end_json_array();
525 	}
526 }
527 
emit_resources()528 void CompilerReflection::emit_resources()
529 {
530 	auto res = get_shader_resources();
531 	emit_resources("subpass_inputs", res.subpass_inputs);
532 	emit_resources("inputs", res.stage_inputs);
533 	emit_resources("outputs", res.stage_outputs);
534 	emit_resources("textures", res.sampled_images);
535 	emit_resources("separate_images", res.separate_images);
536 	emit_resources("separate_samplers", res.separate_samplers);
537 	emit_resources("images", res.storage_images);
538 	emit_resources("ssbos", res.storage_buffers);
539 	emit_resources("ubos", res.uniform_buffers);
540 	emit_resources("push_constants", res.push_constant_buffers);
541 	emit_resources("counters", res.atomic_counters);
542 	emit_resources("acceleration_structures", res.acceleration_structures);
543 }
544 
emit_resources(const char * tag,const SmallVector<Resource> & resources)545 void CompilerReflection::emit_resources(const char *tag, const SmallVector<Resource> &resources)
546 {
547 	if (resources.empty())
548 	{
549 		return;
550 	}
551 
552 	json_stream->emit_json_key_array(tag);
553 	for (auto &res : resources)
554 	{
555 		auto &type = get_type(res.type_id);
556 		auto typeflags = ir.meta[type.self].decoration.decoration_flags;
557 		auto &mask = get_decoration_bitset(res.id);
558 
559 		// If we don't have a name, use the fallback for the type instead of the variable
560 		// for SSBOs and UBOs since those are the only meaningful names to use externally.
561 		// Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
562 		bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant;
563 		bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) ||
564 		                get_decoration_bitset(type.self).get(DecorationBufferBlock);
565 
566 		ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id);
567 
568 		json_stream->begin_json_object();
569 
570 		if (type.basetype == SPIRType::Struct)
571 		{
572 			json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id));
573 		}
574 		else
575 		{
576 			json_stream->emit_json_key_value("type", type_to_glsl(type));
577 		}
578 
579 		json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id));
580 		{
581 			bool ssbo_block = type.storage == StorageClassStorageBuffer ||
582 			                  (type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock));
583 			if (ssbo_block)
584 			{
585 				auto buffer_flags = get_buffer_block_flags(res.id);
586 				if (buffer_flags.get(DecorationNonReadable))
587 					json_stream->emit_json_key_value("writeonly", true);
588 				if (buffer_flags.get(DecorationNonWritable))
589 					json_stream->emit_json_key_value("readonly", true);
590 				if (buffer_flags.get(DecorationRestrict))
591 					json_stream->emit_json_key_value("restrict", true);
592 				if (buffer_flags.get(DecorationCoherent))
593 					json_stream->emit_json_key_value("coherent", true);
594 			}
595 		}
596 
597 		emit_type_array(type);
598 
599 		{
600 			bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform ||
601 			                                   get_storage_class(res.id) == StorageClassUniformConstant ||
602 			                                   get_storage_class(res.id) == StorageClassStorageBuffer);
603 			if (is_sized_block)
604 			{
605 				uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id)));
606 				json_stream->emit_json_key_value("block_size", block_size);
607 			}
608 		}
609 
610 		if (type.storage == StorageClassPushConstant)
611 			json_stream->emit_json_key_value("push_constant", true);
612 		if (mask.get(DecorationLocation))
613 			json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation));
614 		if (mask.get(DecorationRowMajor))
615 			json_stream->emit_json_key_value("row_major", true);
616 		if (mask.get(DecorationColMajor))
617 			json_stream->emit_json_key_value("column_major", true);
618 		if (mask.get(DecorationIndex))
619 			json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex));
620 		if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet))
621 			json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet));
622 		if (mask.get(DecorationBinding))
623 			json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding));
624 		if (mask.get(DecorationInputAttachmentIndex))
625 			json_stream->emit_json_key_value("input_attachment_index",
626 			                                 get_decoration(res.id, DecorationInputAttachmentIndex));
627 		if (mask.get(DecorationOffset))
628 			json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset));
629 
630 		// For images, the type itself adds a layout qualifer.
631 		// Only emit the format for storage images.
632 		if (type.basetype == SPIRType::Image && type.image.sampled == 2)
633 		{
634 			const char *fmt = format_to_glsl(type.image.format);
635 			if (fmt != nullptr)
636 				json_stream->emit_json_key_value("format", std::string(fmt));
637 		}
638 		json_stream->end_json_object();
639 	}
640 	json_stream->end_json_array();
641 }
642 
emit_specialization_constants()643 void CompilerReflection::emit_specialization_constants()
644 {
645 	auto specialization_constants = get_specialization_constants();
646 	if (specialization_constants.empty())
647 		return;
648 
649 	json_stream->emit_json_key_array("specialization_constants");
650 	for (const auto &spec_const : specialization_constants)
651 	{
652 		auto &c = get<SPIRConstant>(spec_const.id);
653 		auto type = get<SPIRType>(c.constant_type);
654 		json_stream->begin_json_object();
655 		json_stream->emit_json_key_value("name", get_name(spec_const.id));
656 		json_stream->emit_json_key_value("id", spec_const.constant_id);
657 		json_stream->emit_json_key_value("type", type_to_glsl(type));
658 		json_stream->emit_json_key_value("variable_id", spec_const.id);
659 		switch (type.basetype)
660 		{
661 		case SPIRType::UInt:
662 			json_stream->emit_json_key_value("default_value", c.scalar());
663 			break;
664 
665 		case SPIRType::Int:
666 			json_stream->emit_json_key_value("default_value", c.scalar_i32());
667 			break;
668 
669 		case SPIRType::Float:
670 			json_stream->emit_json_key_value("default_value", c.scalar_f32());
671 			break;
672 
673 		case SPIRType::Boolean:
674 			json_stream->emit_json_key_value("default_value", c.scalar() != 0);
675 			break;
676 
677 		default:
678 			break;
679 		}
680 		json_stream->end_json_object();
681 	}
682 	json_stream->end_json_array();
683 }
684 
to_member_name(const SPIRType & type,uint32_t index) const685 string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const
686 {
687 	auto *type_meta = ir.find_meta(type.self);
688 
689 	if (type_meta)
690 	{
691 		auto &memb = type_meta->members;
692 		if (index < memb.size() && !memb[index].alias.empty())
693 			return memb[index].alias;
694 		else
695 			return join("_m", index);
696 	}
697 	else
698 		return join("_m", index);
699 }
700