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