2
0

spirv_reflect.cpp 18 KB

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