diff options
Diffstat (limited to 'thirdparty/spirv-reflect/spirv_reflect.c')
-rw-r--r-- | thirdparty/spirv-reflect/spirv_reflect.c | 4442 |
1 files changed, 4442 insertions, 0 deletions
diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c new file mode 100644 index 0000000000..3df963d1ae --- /dev/null +++ b/thirdparty/spirv-reflect/spirv_reflect.c @@ -0,0 +1,4442 @@ +/* + Copyright 2017-2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "spirv_reflect.h" +#include <assert.h> +#include <stdbool.h> +#include <string.h> + +#if defined(WIN32) + #define _CRTDBG_MAP_ALLOC + #include <stdlib.h> + #include <crtdbg.h> +#else + #include <stdlib.h> +#endif + +// Temporary enums until these make it into SPIR-V/Vulkan +// clang-format off +enum { + SpvReflectOpDecorateId = 332, + SpvReflectOpDecorateStringGOOGLE = 5632, + SpvReflectOpMemberDecorateStringGOOGLE = 5633, + SpvReflectDecorationHlslCounterBufferGOOGLE = 5634, + SpvReflectDecorationHlslSemanticGOOGLE = 5635 +}; +// clang-format on + +// clang-format off +enum { + SPIRV_STARTING_WORD_INDEX = 5, + SPIRV_WORD_SIZE = sizeof(uint32_t), + SPIRV_BYTE_WIDTH = 8, + SPIRV_MINIMUM_FILE_SIZE = SPIRV_STARTING_WORD_INDEX * SPIRV_WORD_SIZE, + SPIRV_DATA_ALIGNMENT = 4 * SPIRV_WORD_SIZE, // 16 + SPIRV_ACCESS_CHAIN_INDEX_OFFSET = 4, +}; +// clang-format on + +// clang-format off +enum { + INVALID_VALUE = 0xFFFFFFFF, +}; +// clang-format on + +// clang-format off +enum { + MAX_NODE_NAME_LENGTH = 1024, +}; +// clang-format on + +// clang-format off +enum { + IMAGE_SAMPLED = 1, + IMAGE_STORAGE = 2 +}; +// clang-format on + +// clang-format off +typedef struct ArrayTraits { + uint32_t element_type_id; + uint32_t length_id; +} ArrayTraits; +// clang-format on + +// clang-format off +typedef struct ImageTraits { + uint32_t sampled_type_id; + SpvDim dim; + uint32_t depth; + uint32_t arrayed; + uint32_t ms; + uint32_t sampled; + SpvImageFormat image_format; +} ImageTraits; +// clang-format on + +// clang-format off +typedef struct NumberDecoration { + uint32_t word_offset; + uint32_t value; +} NumberDecoration; +// clang-format on + +// clang-format off +typedef struct StringDecoration { + uint32_t word_offset; + const char* value; +} StringDecoration; +// clang-format on + +// clang-format off +typedef struct Decorations { + bool is_block; + bool is_buffer_block; + bool is_row_major; + bool is_column_major; + bool is_built_in; + bool is_noperspective; + bool is_flat; + bool is_non_writable; + NumberDecoration set; + NumberDecoration binding; + NumberDecoration input_attachment_index; + NumberDecoration location; + NumberDecoration offset; + NumberDecoration uav_counter_buffer; + StringDecoration semantic; + uint32_t array_stride; + uint32_t matrix_stride; + SpvBuiltIn built_in; +} Decorations; +// clang-format on + +// clang-format off +typedef struct Node { + uint32_t result_id; + SpvOp op; + uint32_t result_type_id; + uint32_t type_id; + SpvStorageClass storage_class; + uint32_t word_offset; + uint32_t word_count; + bool is_type; + + ArrayTraits array_traits; + ImageTraits image_traits; + uint32_t image_type_id; + + const char* name; + Decorations decorations; + uint32_t member_count; + const char** member_names; + Decorations* member_decorations; +} Node; +// clang-format on + +// clang-format off +typedef struct String { + uint32_t result_id; + const char* string; +} String; +// clang-format on + +// clang-format off +typedef struct Function { + uint32_t id; + uint32_t callee_count; + uint32_t* callees; + struct Function** callee_ptrs; + uint32_t accessed_ptr_count; + uint32_t* accessed_ptrs; +} Function; +// clang-format on + +// clang-format off +typedef struct AccessChain { + uint32_t result_id; + uint32_t result_type_id; + // + // Pointing to the base of a composite object. + // Generally the id of descriptor block variable + uint32_t base_id; + // + // From spec: + // The first index in Indexes will select the + // top-level member/element/component/element + // of the base composite + uint32_t index_count; + uint32_t* indexes; +} AccessChain; +// clang-format on + +// clang-format off +typedef struct Parser { + size_t spirv_word_count; + uint32_t* spirv_code; + uint32_t string_count; + String* strings; + SpvSourceLanguage source_language; + uint32_t source_language_version; + uint32_t source_file_id; + String source_embedded; + size_t node_count; + Node* nodes; + uint32_t entry_point_count; + uint32_t function_count; + Function* functions; + uint32_t access_chain_count; + AccessChain* access_chains; + + uint32_t type_count; + uint32_t descriptor_count; + uint32_t push_constant_count; +} Parser; +// clang-format on + +static uint32_t Max(uint32_t a, uint32_t b) +{ + return a > b ? a : b; +} + +static uint32_t RoundUp(uint32_t value, uint32_t multiple) +{ + assert(multiple && ((multiple & (multiple - 1)) == 0)); + return (value + multiple - 1) & ~(multiple - 1); +} + +#define IsNull(ptr) \ + (ptr == NULL) + +#define IsNotNull(ptr) \ + (ptr != NULL) + +#define SafeFree(ptr) \ + { \ + if (ptr != NULL) { \ + free((void*)ptr); \ + ptr = NULL; \ + } \ + } + +static int SortCompareUint32(const void* a, const void* b) +{ + const uint32_t* p_a = (const uint32_t*)a; + const uint32_t* p_b = (const uint32_t*)b; + + return (int)*p_a - (int)*p_b; +} + +// +// De-duplicates a sorted array and returns the new size. +// +// Note: The array doesn't actually need to be sorted, just +// arranged into "runs" so that all the entries with one +// value are adjacent. +// +static size_t DedupSortedUint32(uint32_t* arr, size_t size) +{ + if (size == 0) { + return 0; + } + size_t dedup_idx = 0; + for (size_t i = 0; i < size; ++i) { + if (arr[dedup_idx] != arr[i]) { + ++dedup_idx; + arr[dedup_idx] = arr[i]; + } + } + return dedup_idx+1; +} + +static bool SearchSortedUint32(const uint32_t* arr, size_t size, uint32_t target) +{ + size_t lo = 0; + size_t hi = size; + while (lo < hi) { + size_t mid = (hi - lo) / 2 + lo; + if (arr[mid] == target) { + return true; + } else if (arr[mid] < target) { + lo = mid+1; + } else { + hi = mid; + } + } + return false; +} + +static SpvReflectResult IntersectSortedUint32( + const uint32_t* p_arr0, + size_t arr0_size, + const uint32_t* p_arr1, + size_t arr1_size, + uint32_t** pp_res, + size_t* res_size +) +{ + *res_size = 0; + const uint32_t* arr0_end = p_arr0 + arr0_size; + const uint32_t* arr1_end = p_arr1 + arr1_size; + + const uint32_t* idx0 = p_arr0; + const uint32_t* idx1 = p_arr1; + while (idx0 != arr0_end && idx1 != arr1_end) { + if (*idx0 < *idx1) { + ++idx0; + } else if (*idx0 > *idx1) { + ++idx1; + } else { + ++*res_size; + ++idx0; + ++idx1; + } + } + + *pp_res = NULL; + if (*res_size > 0) { + *pp_res = (uint32_t*)calloc(*res_size, sizeof(**pp_res)); + if (IsNull(*pp_res)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + uint32_t* idxr = *pp_res; + idx0 = p_arr0; + idx1 = p_arr1; + while (idx0 != arr0_end && idx1 != arr1_end) { + if (*idx0 < *idx1) { + ++idx0; + } else if (*idx0 > *idx1) { + ++idx1; + } else { + *(idxr++) = *idx0; + ++idx0; + ++idx1; + } + } + } + return SPV_REFLECT_RESULT_SUCCESS; +} + + +static bool InRange(const Parser* p_parser, uint32_t index) +{ + bool in_range = false; + if (IsNotNull(p_parser)) { + in_range = (index < p_parser->spirv_word_count); + } + return in_range; +} + +static SpvReflectResult ReadU32(Parser* p_parser, uint32_t word_offset, uint32_t* p_value) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + assert(InRange(p_parser, word_offset)); + SpvReflectResult result = SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF; + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && InRange(p_parser, word_offset)) { + *p_value = *(p_parser->spirv_code + word_offset); + result = SPV_REFLECT_RESULT_SUCCESS; + } + return result; +} + +#define CHECKED_READU32(parser, word_offset, value) \ + { \ + SpvReflectResult checked_readu32_result = ReadU32(parser, \ + word_offset, (uint32_t*)&(value)); \ + if (checked_readu32_result != SPV_REFLECT_RESULT_SUCCESS) { \ + return checked_readu32_result; \ + } \ + } + +#define CHECKED_READU32_CAST(parser, word_offset, cast_to_type, value) \ + { \ + uint32_t checked_readu32_cast_u32 = UINT32_MAX; \ + SpvReflectResult checked_readu32_cast_result = ReadU32(parser, \ + word_offset, \ + (uint32_t*)&(checked_readu32_cast_u32)); \ + if (checked_readu32_cast_result != SPV_REFLECT_RESULT_SUCCESS) { \ + return checked_readu32_cast_result; \ + } \ + value = (cast_to_type)checked_readu32_cast_u32; \ + } + +#define IF_READU32(result, parser, word_offset, value) \ + if ((result) == SPV_REFLECT_RESULT_SUCCESS) { \ + result = ReadU32(parser, word_offset, (uint32_t*)&(value)); \ + } + +#define IF_READU32_CAST(result, parser, word_offset, cast_to_type, value) \ + if ((result) == SPV_REFLECT_RESULT_SUCCESS) { \ + uint32_t if_readu32_cast_u32 = UINT32_MAX; \ + result = ReadU32(parser, word_offset, &if_readu32_cast_u32); \ + if ((result) == SPV_REFLECT_RESULT_SUCCESS) { \ + value = (cast_to_type)if_readu32_cast_u32; \ + } \ + } + +static SpvReflectResult ReadStr( + Parser* p_parser, + uint32_t word_offset, + uint32_t word_index, + uint32_t word_count, + uint32_t* p_buf_size, + char* p_buf +) +{ + uint32_t limit = (word_offset + word_count); + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + assert(InRange(p_parser, limit)); + SpvReflectResult result = SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF; + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && InRange(p_parser, limit)) { + const char* c_str = (const char*)(p_parser->spirv_code + word_offset + word_index); + uint32_t n = word_count * SPIRV_WORD_SIZE; + uint32_t length_with_terminator = 0; + for (uint32_t i = 0; i < n; ++i) { + char c = *(c_str + i); + if (c == 0) { + length_with_terminator = i + 1; + break; + } + } + + if (length_with_terminator > 0) { + result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + if (IsNotNull(p_buf_size) && IsNotNull(p_buf)) { + result = SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED; + if (length_with_terminator <= *p_buf_size) { + memset(p_buf, 0, *p_buf_size); + memcpy(p_buf, c_str, length_with_terminator); + result = SPV_REFLECT_RESULT_SUCCESS; + } + } + else { + if (IsNotNull(p_buf_size)) { + *p_buf_size = length_with_terminator; + result = SPV_REFLECT_RESULT_SUCCESS; + } + } + } + } + return result; +} + +static SpvReflectDecorationFlags ApplyDecorations(const Decorations* p_decoration_fields) +{ + SpvReflectDecorationFlags decorations = SPV_REFLECT_DECORATION_NONE; + if (p_decoration_fields->is_block) { + decorations |= SPV_REFLECT_DECORATION_BLOCK; + } + if (p_decoration_fields->is_buffer_block) { + decorations |= SPV_REFLECT_DECORATION_BUFFER_BLOCK; + } + if (p_decoration_fields->is_row_major) { + decorations |= SPV_REFLECT_DECORATION_ROW_MAJOR; + } + if (p_decoration_fields->is_column_major) { + decorations |= SPV_REFLECT_DECORATION_COLUMN_MAJOR; + } + if (p_decoration_fields->is_built_in) { + decorations |= SPV_REFLECT_DECORATION_BUILT_IN; + } + if (p_decoration_fields->is_noperspective) { + decorations |= SPV_REFLECT_DECORATION_NOPERSPECTIVE; + } + if (p_decoration_fields->is_flat) { + decorations |= SPV_REFLECT_DECORATION_FLAT; + } + if (p_decoration_fields->is_non_writable) { + decorations |= SPV_REFLECT_DECORATION_NON_WRITABLE; + } + return decorations; +} + +static void ApplyNumericTraits(const SpvReflectTypeDescription* p_type, SpvReflectNumericTraits* p_numeric_traits) +{ + memcpy(p_numeric_traits, &p_type->traits.numeric, sizeof(p_type->traits.numeric)); +} + +static void ApplyArrayTraits(const SpvReflectTypeDescription* p_type, SpvReflectArrayTraits* p_array_traits) +{ + memcpy(p_array_traits, &p_type->traits.array, sizeof(p_type->traits.array)); +} + +static Node* FindNode(Parser* p_parser, uint32_t result_id) +{ + Node* p_node = NULL; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_elem = &(p_parser->nodes[i]); + if (p_elem->result_id == result_id) { + p_node = p_elem; + break; + } + } + return p_node; +} + +static SpvReflectTypeDescription* FindType(SpvReflectShaderModule* p_module, uint32_t type_id) +{ + SpvReflectTypeDescription* p_type = NULL; + for (size_t i = 0; i < p_module->_internal->type_description_count; ++i) { + SpvReflectTypeDescription* p_elem = &(p_module->_internal->type_descriptions[i]); + if (p_elem->id == type_id) { + p_type = p_elem; + break; + } + } + return p_type; +} + +static SpvReflectResult CreateParser(size_t size, void* p_code, Parser* p_parser) +{ + if (p_code == NULL) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (size < SPIRV_MINIMUM_FILE_SIZE) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_CODE_SIZE; + } + if ((size % 4) != 0) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_CODE_SIZE; + } + + p_parser->spirv_word_count = size / SPIRV_WORD_SIZE; + p_parser->spirv_code = (uint32_t*)p_code; + + if (p_parser->spirv_code[0] != SpvMagicNumber) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_MAGIC_NUMBER; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static void DestroyParser(Parser* p_parser) +{ + if (!IsNull(p_parser->nodes)) { + // Free nodes + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (IsNotNull(p_node->member_names)) { + SafeFree(p_node->member_names); + } + if (IsNotNull(p_node->member_decorations)) { + SafeFree(p_node->member_decorations); + } + } + + // Free functions + for (size_t i = 0; i < p_parser->function_count; ++i) { + SafeFree(p_parser->functions[i].callees); + SafeFree(p_parser->functions[i].callee_ptrs); + SafeFree(p_parser->functions[i].accessed_ptrs); + } + + // Free access chains + for (uint32_t i = 0; i < p_parser->access_chain_count; ++i) { + SafeFree(p_parser->access_chains[i].indexes); + } + + SafeFree(p_parser->nodes); + SafeFree(p_parser->strings); + SafeFree(p_parser->functions); + SafeFree(p_parser->access_chains); + p_parser->node_count = 0; + } +} + +static SpvReflectResult ParseNodes(Parser* p_parser) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + + uint32_t* p_spirv = p_parser->spirv_code; + uint32_t spirv_word_index = SPIRV_STARTING_WORD_INDEX; + + // Count nodes + uint32_t node_count = 0; + while (spirv_word_index < p_parser->spirv_word_count) { + uint32_t word = p_spirv[spirv_word_index]; + SpvOp op = (SpvOp)(word & 0xFFFF); + uint32_t node_word_count = (word >> 16) & 0xFFFF; + if (node_word_count == 0) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_INSTRUCTION; + } + if (op == SpvOpAccessChain) { + ++(p_parser->access_chain_count); + } + spirv_word_index += node_word_count; + ++node_count; + } + + if (node_count == 0) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF; + } + + // Allocate nodes + p_parser->node_count = node_count; + p_parser->nodes = (Node*)calloc(p_parser->node_count, sizeof(*(p_parser->nodes))); + if (IsNull(p_parser->nodes)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + // Mark all nodes with an invalid state + for (uint32_t i = 0; i < node_count; ++i) { + p_parser->nodes[i].op = (SpvOp)INVALID_VALUE; + p_parser->nodes[i].storage_class = (SpvStorageClass)INVALID_VALUE; + p_parser->nodes[i].decorations.set.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.binding.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.location.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.offset.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.uav_counter_buffer.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.built_in = (SpvBuiltIn)INVALID_VALUE; + } + // Mark source file id node + p_parser->source_file_id = (uint32_t)INVALID_VALUE; + + // Function node + uint32_t function_node = (uint32_t)INVALID_VALUE; + + // Allocate access chain + if (p_parser->access_chain_count > 0) { + p_parser->access_chains = (AccessChain*)calloc(p_parser->access_chain_count, sizeof(*(p_parser->access_chains))); + if (IsNull(p_parser->access_chains)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + // Parse nodes + uint32_t node_index = 0; + uint32_t access_chain_index = 0; + spirv_word_index = SPIRV_STARTING_WORD_INDEX; + while (spirv_word_index < p_parser->spirv_word_count) { + uint32_t word = p_spirv[spirv_word_index]; + SpvOp op = (SpvOp)(word & 0xFFFF); + uint32_t node_word_count = (word >> 16) & 0xFFFF; + + Node* p_node = &(p_parser->nodes[node_index]); + p_node->op = op; + p_node->word_offset = spirv_word_index; + p_node->word_count = node_word_count; + + switch (p_node->op) { + default: break; + + case SpvOpString: { + ++(p_parser->string_count); + } + break; + + case SpvOpSource: { + CHECKED_READU32_CAST(p_parser, p_node->word_offset + 1, SpvSourceLanguage, p_parser->source_language); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_parser->source_language_version); + if (p_node->word_count >= 4) { + CHECKED_READU32(p_parser, p_node->word_offset + 3, p_parser->source_file_id); + } + } + break; + + case SpvOpEntryPoint: { + ++(p_parser->entry_point_count); + } + break; + + case SpvOpName: + case SpvOpMemberName: + { + uint32_t member_offset = (p_node->op == SpvOpMemberName) ? 1 : 0; + uint32_t name_start = p_node->word_offset + member_offset + 2; + p_node->name = (const char*)(p_parser->spirv_code + name_start); + } + break; + + case SpvOpTypeStruct: + { + p_node->member_count = p_node->word_count - 2; + } // Fall through + case SpvOpTypeVoid: + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeSampler: + case SpvOpTypeOpaque: + case SpvOpTypeFunction: + case SpvOpTypeEvent: + case SpvOpTypeDeviceEvent: + case SpvOpTypeReserveId: + case SpvOpTypeQueue: + case SpvOpTypePipe: + { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + p_node->is_type = true; + } + break; + + case SpvOpTypeImage: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->image_traits.sampled_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->image_traits.dim); + CHECKED_READU32(p_parser, p_node->word_offset + 4, p_node->image_traits.depth); + CHECKED_READU32(p_parser, p_node->word_offset + 5, p_node->image_traits.arrayed); + CHECKED_READU32(p_parser, p_node->word_offset + 6, p_node->image_traits.ms); + CHECKED_READU32(p_parser, p_node->word_offset + 7, p_node->image_traits.sampled); + CHECKED_READU32(p_parser, p_node->word_offset + 8, p_node->image_traits.image_format); + p_node->is_type = true; + } + break; + + case SpvOpTypeSampledImage: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->image_type_id); + p_node->is_type = true; + } + break; + + case SpvOpTypeArray: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->array_traits.element_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->array_traits.length_id); + p_node->is_type = true; + } + break; + + case SpvOpTypeRuntimeArray: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->array_traits.element_type_id); + p_node->is_type = true; + } + break; + + case SpvOpTypePointer: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->storage_class); + CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->type_id); + p_node->is_type = true; + } + break; + + case SpvOpTypeForwardPointer: + { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->storage_class); + p_node->is_type = true; + } + break; + + case SpvOpConstantTrue: + case SpvOpConstantFalse: + case SpvOpConstant: + case SpvOpConstantComposite: + case SpvOpConstantSampler: + case SpvOpConstantNull: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + } + break; + + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: + case SpvOpSpecConstantComposite: + case SpvOpSpecConstantOp: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + } + break; + + case SpvOpVariable: + { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->storage_class); + } + break; + + case SpvOpLoad: + { + // Only load enough so OpDecorate can reference the node, skip the remaining operands. + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + } + break; + + case SpvOpAccessChain: + { + AccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]); + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_access_chain->result_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_access_chain->result_id); + CHECKED_READU32(p_parser, p_node->word_offset + 3, p_access_chain->base_id); + // + // SPIRV_ACCESS_CHAIN_INDEX_OFFSET (4) is the number of words up until the first index: + // [Node, Result Type Id, Result Id, Base Id, <Indexes>] + // + p_access_chain->index_count = (node_word_count - SPIRV_ACCESS_CHAIN_INDEX_OFFSET); + if (p_access_chain->index_count > 0) { + p_access_chain->indexes = (uint32_t*)calloc(p_access_chain->index_count, sizeof(*(p_access_chain->indexes))); + if (IsNull( p_access_chain->indexes)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + // Parse any index values for access chain + for (uint32_t index_index = 0; index_index < p_access_chain->index_count; ++index_index) { + // Read index id + uint32_t index_id = 0; + CHECKED_READU32(p_parser, p_node->word_offset + SPIRV_ACCESS_CHAIN_INDEX_OFFSET + index_index, index_id); + // Find OpConstant node that contains index value + Node* p_index_value_node = FindNode(p_parser, index_id); + if ((p_index_value_node != NULL) && (p_index_value_node->op == SpvOpConstant)) { + // Read index value + uint32_t index_value = UINT32_MAX; + CHECKED_READU32(p_parser, p_index_value_node->word_offset + 3, index_value); + assert(index_value != UINT32_MAX); + // Write index value to array + p_access_chain->indexes[index_index] = index_value; + } + } + } + ++access_chain_index; + } + break; + + case SpvOpFunction: + { + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + // Count function definitions, not function declarations. To determine + // the difference, set an in-function variable, and then if an OpLabel + // is reached before the end of the function increment the function + // count. + function_node = node_index; + } + break; + + case SpvOpLabel: + { + if (function_node != (uint32_t)INVALID_VALUE) { + Node* p_func_node = &(p_parser->nodes[function_node]); + CHECKED_READU32(p_parser, p_func_node->word_offset + 2, p_func_node->result_id); + ++(p_parser->function_count); + } + } // Fall through + + case SpvOpFunctionEnd: + { + function_node = (uint32_t)INVALID_VALUE; + } + break; + } + + if (p_node->is_type) { + ++(p_parser->type_count); + } + + spirv_word_index += node_word_count; + ++node_index; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseStrings(Parser* p_parser) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + assert(IsNotNull(p_parser->nodes)); + + // Early out + if (p_parser->string_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) { + // Allocate string storage + p_parser->strings = (String*)calloc(p_parser->string_count, sizeof(*(p_parser->strings))); + + uint32_t string_index = 0; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->op != SpvOpString) { + continue; + } + + // Paranoid check against string count + assert(string_index < p_parser->string_count); + if (string_index >= p_parser->string_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + // Result id + String* p_string = &(p_parser->strings[string_index]); + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_string->result_id); + + // String + uint32_t string_start = p_node->word_offset + 2; + p_string->string = (const char*)(p_parser->spirv_code + string_start); + + // Increment string index + ++string_index; + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseSource(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code)) { + // Source file + if (IsNotNull(p_parser->strings)) { + for (uint32_t i = 0; i < p_parser->string_count; ++i) { + String* p_string = &(p_parser->strings[i]); + if (p_string->result_id == p_parser->source_file_id) { + p_module->source_file = p_string->string; + break; + } + } + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseFunction(Parser* p_parser, Node* p_func_node, Function* p_func, size_t first_label_index) +{ + p_func->id = p_func_node->result_id; + + p_func->callee_count = 0; + p_func->accessed_ptr_count = 0; + + for (size_t i = first_label_index; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->op == SpvOpFunctionEnd) { + break; + } + switch (p_node->op) { + case SpvOpFunctionCall: { + ++(p_func->callee_count); + } + break; + case SpvOpLoad: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpArrayLength: + case SpvOpGenericPtrMemSemantics: + case SpvOpInBoundsPtrAccessChain: + case SpvOpStore: + { + ++(p_func->accessed_ptr_count); + } + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + { + p_func->accessed_ptr_count += 2; + } + break; + default: break; + } + } + + if (p_func->callee_count > 0) { + p_func->callees = (uint32_t*)calloc(p_func->callee_count, + sizeof(*(p_func->callees))); + if (IsNull(p_func->callees)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + if (p_func->accessed_ptr_count > 0) { + p_func->accessed_ptrs = (uint32_t*)calloc(p_func->accessed_ptr_count, + sizeof(*(p_func->accessed_ptrs))); + if (IsNull(p_func->accessed_ptrs)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + p_func->callee_count = 0; + p_func->accessed_ptr_count = 0; + for (size_t i = first_label_index; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->op == SpvOpFunctionEnd) { + break; + } + switch (p_node->op) { + case SpvOpFunctionCall: { + CHECKED_READU32(p_parser, p_node->word_offset + 3, + p_func->callees[p_func->callee_count]); + (++p_func->callee_count); + } + break; + case SpvOpLoad: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpArrayLength: + case SpvOpGenericPtrMemSemantics: + case SpvOpInBoundsPtrAccessChain: + { + CHECKED_READU32(p_parser, p_node->word_offset + 3, + p_func->accessed_ptrs[p_func->accessed_ptr_count]); + (++p_func->accessed_ptr_count); + } + break; + case SpvOpStore: + { + CHECKED_READU32(p_parser, p_node->word_offset + 2, + p_func->accessed_ptrs[p_func->accessed_ptr_count]); + (++p_func->accessed_ptr_count); + } + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + { + CHECKED_READU32(p_parser, p_node->word_offset + 2, + p_func->accessed_ptrs[p_func->accessed_ptr_count]); + (++p_func->accessed_ptr_count); + CHECKED_READU32(p_parser, p_node->word_offset + 3, + p_func->accessed_ptrs[p_func->accessed_ptr_count]); + (++p_func->accessed_ptr_count); + } + break; + default: break; + } + } + + if (p_func->callee_count > 0) { + qsort(p_func->callees, p_func->callee_count, + sizeof(*(p_func->callees)), SortCompareUint32); + } + p_func->callee_count = (uint32_t)DedupSortedUint32(p_func->callees, + p_func->callee_count); + + if (p_func->accessed_ptr_count > 0) { + qsort(p_func->accessed_ptrs, p_func->accessed_ptr_count, + sizeof(*(p_func->accessed_ptrs)), SortCompareUint32); + } + p_func->accessed_ptr_count = (uint32_t)DedupSortedUint32(p_func->accessed_ptrs, + p_func->accessed_ptr_count); + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static int SortCompareFunctions(const void* a, const void* b) +{ + const Function* af = (const Function*)a; + const Function* bf = (const Function*)b; + return (int)af->id - (int)bf->id; +} + +static SpvReflectResult ParseFunctions(Parser* p_parser) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + assert(IsNotNull(p_parser->nodes)); + + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) { + if (p_parser->function_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_parser->functions = (Function*)calloc(p_parser->function_count, + sizeof(*(p_parser->functions))); + if (IsNull(p_parser->functions)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + size_t function_index = 0; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->op != SpvOpFunction) { + continue; + } + + // Skip over function declarations that aren't definitions + bool func_definition = false; + // Intentionally reuse i to avoid iterating over these nodes more than + // once + for (; i < p_parser->node_count; ++i) { + if (p_parser->nodes[i].op == SpvOpLabel) { + func_definition = true; + break; + } + if (p_parser->nodes[i].op == SpvOpFunctionEnd) { + break; + } + } + if (!func_definition) { + continue; + } + + Function* p_function = &(p_parser->functions[function_index]); + + SpvReflectResult result = ParseFunction(p_parser, p_node, p_function, i); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + ++function_index; + } + + qsort(p_parser->functions, p_parser->function_count, + sizeof(*(p_parser->functions)), SortCompareFunctions); + + // Once they're sorted, link the functions with pointers to improve graph + // traversal efficiency + for (size_t i = 0; i < p_parser->function_count; ++i) { + Function* p_func = &(p_parser->functions[i]); + if (p_func->callee_count == 0) { + continue; + } + p_func->callee_ptrs = (Function**)calloc(p_func->callee_count, + sizeof(*(p_func->callee_ptrs))); + for (size_t j = 0, k = 0; j < p_func->callee_count; ++j) { + while (p_parser->functions[k].id != p_func->callees[j]) { + ++k; + if (k >= p_parser->function_count) { + // Invalid called function ID somewhere + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + p_func->callee_ptrs[j] = &(p_parser->functions[k]); + } + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseMemberCounts(Parser* p_parser) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + assert(IsNotNull(p_parser->nodes)); + + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) { + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if ((p_node->op != SpvOpMemberName) && (p_node->op != SpvOpMemberDecorate)) { + continue; + } + + uint32_t target_id = 0; + uint32_t member_index = (uint32_t)INVALID_VALUE; + CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index); + Node* p_target_node = FindNode(p_parser, target_id); + // Not all nodes get parsed, so FindNode returning NULL is expected. + if (IsNull(p_target_node)) { + continue; + } + + if (member_index == INVALID_VALUE) { + return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED; + } + + p_target_node->member_count = Max(p_target_node->member_count, member_index + 1); + } + + for (uint32_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->member_count == 0) { + continue; + } + + p_node->member_names = (const char **)calloc(p_node->member_count, sizeof(*(p_node->member_names))); + if (IsNull(p_node->member_names)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + p_node->member_decorations = (Decorations*)calloc(p_node->member_count, sizeof(*(p_node->member_decorations))); + if (IsNull(p_node->member_decorations)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseNames(Parser* p_parser) +{ + assert(IsNotNull(p_parser)); + assert(IsNotNull(p_parser->spirv_code)); + assert(IsNotNull(p_parser->nodes)); + + if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) { + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if ((p_node->op != SpvOpName) && (p_node->op != SpvOpMemberName)) { + continue; + } + + uint32_t target_id = 0; + CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id); + Node* p_target_node = FindNode(p_parser, target_id); + // Not all nodes get parsed, so FindNode returning NULL is expected. + if (IsNull(p_target_node)) { + continue; + } + + const char** pp_target_name = &(p_target_node->name); + if (p_node->op == SpvOpMemberName) { + uint32_t member_index = UINT32_MAX; + CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index); + pp_target_name = &(p_target_node->member_names[member_index]); + } + + *pp_target_name = p_node->name; + } + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDecorations(Parser* p_parser) +{ + for (uint32_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + + if (((uint32_t)p_node->op != (uint32_t)SpvOpDecorate) && + ((uint32_t)p_node->op != (uint32_t)SpvOpMemberDecorate) && + ((uint32_t)p_node->op != (uint32_t)SpvReflectOpDecorateId) && + ((uint32_t)p_node->op != (uint32_t)SpvReflectOpDecorateStringGOOGLE) && + ((uint32_t)p_node->op != (uint32_t)SpvReflectOpMemberDecorateStringGOOGLE)) + { + continue; + } + + // Need to adjust the read offset if this is a member decoration + uint32_t member_offset = 0; + if (p_node->op == SpvOpMemberDecorate) { + member_offset = 1; + } + + // Get decoration + uint32_t decoration = (uint32_t)INVALID_VALUE; + CHECKED_READU32(p_parser, p_node->word_offset + member_offset + 2, decoration); + + // Filter out the decoration that do not affect reflection, otherwise + // there will be random crashes because the nodes aren't found. + bool skip = false; + switch (decoration) { + default: { + skip = true; + } + break; + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationColMajor: + case SpvDecorationRowMajor: + case SpvDecorationArrayStride: + case SpvDecorationMatrixStride: + case SpvDecorationBuiltIn: + case SpvDecorationNoPerspective: + case SpvDecorationFlat: + case SpvDecorationNonWritable: + case SpvDecorationLocation: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationOffset: + case SpvDecorationInputAttachmentIndex: + case SpvReflectDecorationHlslCounterBufferGOOGLE: + case SpvReflectDecorationHlslSemanticGOOGLE: { + skip = false; + } + break; + } + if (skip) { + continue; + } + + // Find target target node + uint32_t target_id = 0; + CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id); + Node* p_target_node = FindNode(p_parser, target_id); + if (IsNull(p_target_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Get decorations + Decorations* p_target_decorations = &(p_target_node->decorations); + // Update pointer if this is a member member decoration + if (p_node->op == SpvOpMemberDecorate) { + uint32_t member_index = (uint32_t)INVALID_VALUE; + CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index); + p_target_decorations = &(p_target_node->member_decorations[member_index]); + } + + switch (decoration) { + default: break; + + case SpvDecorationBlock: { + p_target_decorations->is_block = true; + } + break; + + case SpvDecorationBufferBlock: { + p_target_decorations->is_buffer_block = true; + } + break; + + case SpvDecorationColMajor: { + p_target_decorations->is_column_major = true; + } + break; + + case SpvDecorationRowMajor: { + p_target_decorations->is_row_major = true; + } + break; + + case SpvDecorationArrayStride: { + uint32_t word_offset = p_node->word_offset + member_offset + 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->array_stride); + } + break; + + case SpvDecorationMatrixStride: { + uint32_t word_offset = p_node->word_offset + member_offset + 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->matrix_stride); + } + break; + + case SpvDecorationBuiltIn: { + p_target_decorations->is_built_in = true; + uint32_t word_offset = p_node->word_offset + member_offset + 3; + CHECKED_READU32_CAST(p_parser, word_offset, SpvBuiltIn, p_target_decorations->built_in); + } + break; + + case SpvDecorationNoPerspective: { + p_target_decorations->is_noperspective = true; + } + break; + + case SpvDecorationFlat: { + p_target_decorations->is_flat = true; + } + break; + + case SpvDecorationNonWritable: { + p_target_decorations->is_non_writable = true; + } + break; + + case SpvDecorationLocation: { + uint32_t word_offset = p_node->word_offset + member_offset + 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->location.value); + p_target_decorations->location.word_offset = word_offset; + } + break; + + case SpvDecorationBinding: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->binding.value); + p_target_decorations->binding.word_offset = word_offset; + } + break; + + case SpvDecorationDescriptorSet: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->set.value); + p_target_decorations->set.word_offset = word_offset; + } + break; + + case SpvDecorationOffset: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->offset.value); + p_target_decorations->offset.word_offset = word_offset; + } + break; + + case SpvDecorationInputAttachmentIndex: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->input_attachment_index.value); + p_target_decorations->input_attachment_index.word_offset = word_offset; + } + break; + + case SpvReflectDecorationHlslCounterBufferGOOGLE: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->uav_counter_buffer.value); + p_target_decorations->uav_counter_buffer.word_offset = word_offset; + } + break; + + case SpvReflectDecorationHlslSemanticGOOGLE: { + uint32_t word_offset = p_node->word_offset + member_offset + 3; + p_target_decorations->semantic.value = (const char*)(p_parser->spirv_code + word_offset); + p_target_decorations->semantic.word_offset = word_offset; + } + break; + } + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult EnumerateAllUniforms( + SpvReflectShaderModule* p_module, + size_t* p_uniform_count, + uint32_t** pp_uniforms +) +{ + *p_uniform_count = p_module->descriptor_binding_count; + if (*p_uniform_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + *pp_uniforms = (uint32_t*)calloc(*p_uniform_count, sizeof(**pp_uniforms)); + + if (IsNull(*pp_uniforms)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + for (size_t i = 0; i < *p_uniform_count; ++i) { + (*pp_uniforms)[i] = p_module->descriptor_bindings[i].spirv_id; + } + qsort(*pp_uniforms, *p_uniform_count, sizeof(**pp_uniforms), + SortCompareUint32); + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseType( + Parser* p_parser, + Node* p_node, + Decorations* p_struct_member_decorations, + SpvReflectShaderModule* p_module, + SpvReflectTypeDescription* p_type +) +{ + SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS; + + if (p_node->member_count > 0) { + p_type->member_count = p_node->member_count; + p_type->members = (SpvReflectTypeDescription*)calloc(p_type->member_count, sizeof(*(p_type->members))); + if (IsNotNull(p_type->members)) { + // Mark all members types with an invalid state + for (size_t i = 0; i < p_type->members->member_count; ++i) { + SpvReflectTypeDescription* p_member_type = &(p_type->members[i]); + p_member_type->id = (uint32_t)INVALID_VALUE; + p_member_type->op = (SpvOp)INVALID_VALUE; + p_member_type->storage_class = (SpvStorageClass)INVALID_VALUE; + } + } + else { + result = SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + if (result == SPV_REFLECT_RESULT_SUCCESS) { + // Since the parse descends on type information, these will get overwritten + // if not guarded against assignment. Only assign if the id is invalid. + if (p_type->id == INVALID_VALUE) { + p_type->id = p_node->result_id; + p_type->op = p_node->op; + p_type->decoration_flags = 0; + } + // Top level types need to pick up decorations from all types below it. + // Issue and fix here: https://github.com/chaoticbob/SPIRV-Reflect/issues/64 + p_type->decoration_flags = ApplyDecorations(&p_node->decorations); + + switch (p_node->op) { + default: break; + case SpvOpTypeVoid: + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_VOID; + break; + + case SpvOpTypeBool: + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_BOOL; + break; + + case SpvOpTypeInt: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_INT; + IF_READU32(result, p_parser, p_node->word_offset + 2, p_type->traits.numeric.scalar.width); + IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.scalar.signedness); + } + break; + + case SpvOpTypeFloat: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_FLOAT; + IF_READU32(result, p_parser, p_node->word_offset + 2, p_type->traits.numeric.scalar.width); + } + break; + + case SpvOpTypeVector: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_VECTOR; + uint32_t component_type_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + 2, component_type_id); + IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.vector.component_count); + // Parse component type + Node* p_next_node = FindNode(p_parser, component_type_id); + if (IsNotNull(p_next_node)) { + result = ParseType(p_parser, p_next_node, NULL, p_module, p_type); + } + else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + break; + + case SpvOpTypeMatrix: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_MATRIX; + uint32_t column_type_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + 2, column_type_id); + IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.matrix.column_count); + Node* p_next_node = FindNode(p_parser, column_type_id); + if (IsNotNull(p_next_node)) { + result = ParseType(p_parser, p_next_node, NULL, p_module, p_type); + } + else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + p_type->traits.numeric.matrix.row_count = p_type->traits.numeric.vector.component_count; + p_type->traits.numeric.matrix.stride = p_node->decorations.matrix_stride; + // NOTE: Matrix stride is decorated using OpMemberDecoreate - not OpDecoreate. + if (IsNotNull(p_struct_member_decorations)) { + p_type->traits.numeric.matrix.stride = p_struct_member_decorations->matrix_stride; + } + } + break; + + case SpvOpTypeImage: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE; + IF_READU32_CAST(result, p_parser, p_node->word_offset + 3, SpvDim, p_type->traits.image.dim); + IF_READU32(result, p_parser, p_node->word_offset + 4, p_type->traits.image.depth); + IF_READU32(result, p_parser, p_node->word_offset + 5, p_type->traits.image.arrayed); + IF_READU32(result, p_parser, p_node->word_offset + 6, p_type->traits.image.ms); + IF_READU32(result, p_parser, p_node->word_offset + 7, p_type->traits.image.sampled); + IF_READU32_CAST(result, p_parser, p_node->word_offset + 8, SpvImageFormat, p_type->traits.image.image_format); + } + break; + + case SpvOpTypeSampler: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLER; + } + break; + + case SpvOpTypeSampledImage: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE; + uint32_t image_type_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + 2, image_type_id); + Node* p_next_node = FindNode(p_parser, image_type_id); + if (IsNotNull(p_next_node)) { + result = ParseType(p_parser, p_next_node, NULL, p_module, p_type); + } + else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + break; + + case SpvOpTypeArray: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_ARRAY; + if (result == SPV_REFLECT_RESULT_SUCCESS) { + uint32_t element_type_id = (uint32_t)INVALID_VALUE; + uint32_t length_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + 2, element_type_id); + IF_READU32(result, p_parser, p_node->word_offset + 3, length_id); + // NOTE: Array stride is decorated using OpDecorate instead of + // OpMemberDecorate, even if the array is apart of a struct. + p_type->traits.array.stride = p_node->decorations.array_stride; + // Get length for current dimension + Node* p_length_node = FindNode(p_parser, length_id); + if (IsNotNull(p_length_node)) { + if (p_length_node->op == SpvOpSpecConstant || + p_length_node->op == SpvOpSpecConstantOp) { + p_type->traits.array.dims[p_type->traits.array.dims_count] = 0xFFFFFFFF; + p_type->traits.array.dims_count += 1; + } else { + uint32_t length = 0; + IF_READU32(result, p_parser, p_length_node->word_offset + 3, length); + if (result == SPV_REFLECT_RESULT_SUCCESS) { + // Write the array dim and increment the count and offset + p_type->traits.array.dims[p_type->traits.array.dims_count] = length; + p_type->traits.array.dims_count += 1; + } else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + // Parse next dimension or element type + Node* p_next_node = FindNode(p_parser, element_type_id); + if (IsNotNull(p_next_node)) { + result = ParseType(p_parser, p_next_node, NULL, p_module, p_type); + } + } + else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + } + break; + + case SpvOpTypeRuntimeArray: { + uint32_t element_type_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + 2, element_type_id); + // Parse next dimension or element type + Node* p_next_node = FindNode(p_parser, element_type_id); + if (IsNotNull(p_next_node)) { + result = ParseType(p_parser, p_next_node, NULL, p_module, p_type); + } + else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + break; + + case SpvOpTypeStruct: { + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_STRUCT; + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_BLOCK; + uint32_t word_index = 2; + uint32_t member_index = 0; + for (; word_index < p_node->word_count; ++word_index, ++member_index) { + uint32_t member_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + word_index, member_id); + // Find member node + Node* p_member_node = FindNode(p_parser, member_id); + if (IsNull(p_member_node)) { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + break; + } + + // Member decorations + Decorations* p_member_decorations = &p_node->member_decorations[member_index]; + + assert(member_index < p_type->member_count); + // Parse member type + SpvReflectTypeDescription* p_member_type = &(p_type->members[member_index]); + p_member_type->id = member_id; + p_member_type->op = p_member_node->op; + result = ParseType(p_parser, p_member_node, p_member_decorations, p_module, p_member_type); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + break; + } + // This looks wrong + //p_member_type->type_name = p_member_node->name; + p_member_type->struct_member_name = p_node->member_names[member_index]; + } + } + break; + + case SpvOpTypeOpaque: break; + + case SpvOpTypePointer: { + IF_READU32_CAST(result, p_parser, p_node->word_offset + 2, SpvStorageClass, p_type->storage_class); + uint32_t type_id = (uint32_t)INVALID_VALUE; + IF_READU32(result, p_parser, p_node->word_offset + 3, type_id); + // Parse type + Node* p_next_node = FindNode(p_parser, type_id); + if (IsNotNull(p_next_node)) { + result = ParseType(p_parser, p_next_node, NULL, p_module, p_type); + } + else { + result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + break; + } + + if (result == SPV_REFLECT_RESULT_SUCCESS) { + // Names get assigned on the way down. Guard against names + // get overwritten on the way up. + if (IsNull(p_type->type_name)) { + p_type->type_name = p_node->name; + } + } + } + + return result; +} + +static SpvReflectResult ParseTypes(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + if (p_parser->type_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_module->_internal->type_description_count = p_parser->type_count; + p_module->_internal->type_descriptions = (SpvReflectTypeDescription*)calloc(p_module->_internal->type_description_count, + sizeof(*(p_module->_internal->type_descriptions))); + if (IsNull(p_module->_internal->type_descriptions)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + // Mark all types with an invalid state + for (size_t i = 0; i < p_module->_internal->type_description_count; ++i) { + SpvReflectTypeDescription* p_type = &(p_module->_internal->type_descriptions[i]); + p_type->id = (uint32_t)INVALID_VALUE; + p_type->op = (SpvOp)INVALID_VALUE; + p_type->storage_class = (SpvStorageClass)INVALID_VALUE; + } + + size_t type_index = 0; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (! p_node->is_type) { + continue; + } + + SpvReflectTypeDescription* p_type = &(p_module->_internal->type_descriptions[type_index]); + SpvReflectResult result = ParseType(p_parser, p_node, NULL, p_module, p_type); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + ++type_index; + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +static int SortCompareDescriptorBinding(const void* a, const void* b) +{ + const SpvReflectDescriptorBinding* p_elem_a = (const SpvReflectDescriptorBinding*)a; + const SpvReflectDescriptorBinding* p_elem_b = (const SpvReflectDescriptorBinding*)b; + int value = (int)(p_elem_a->binding) - (int)(p_elem_b->binding); + if (value == 0) { + // use spirv-id as a tiebreaker to ensure a stable ordering, as they're guaranteed + // unique. + assert(p_elem_a->spirv_id != p_elem_b->spirv_id); + value = (int)(p_elem_a->spirv_id) - (int)(p_elem_b->spirv_id); + } + return value; +} + +static SpvReflectResult ParseDescriptorBindings(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + p_module->descriptor_binding_count = 0; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if ((p_node->op != SpvOpVariable) || + ((p_node->storage_class != SpvStorageClassUniform) && (p_node->storage_class != SpvStorageClassUniformConstant))) + { + continue; + } + if ((p_node->decorations.set.value == INVALID_VALUE) || (p_node->decorations.binding.value == INVALID_VALUE)) { + continue; + } + + p_module->descriptor_binding_count += 1; + } + + if (p_module->descriptor_binding_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_module->descriptor_bindings = (SpvReflectDescriptorBinding*)calloc(p_module->descriptor_binding_count, sizeof(*(p_module->descriptor_bindings))); + if (IsNull(p_module->descriptor_bindings)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + // Mark all types with an invalid state + for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]); + p_descriptor->binding = (uint32_t)INVALID_VALUE; + p_descriptor->input_attachment_index = (uint32_t)INVALID_VALUE; + p_descriptor->set = (uint32_t)INVALID_VALUE; + p_descriptor->descriptor_type = (SpvReflectDescriptorType)INVALID_VALUE; + p_descriptor->uav_counter_id = (uint32_t)INVALID_VALUE; + } + + size_t descriptor_index = 0; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if ((p_node->op != SpvOpVariable) || + ((p_node->storage_class != SpvStorageClassUniform) && (p_node->storage_class != SpvStorageClassUniformConstant)))\ + { + continue; + } + if ((p_node->decorations.set.value == INVALID_VALUE) || (p_node->decorations.binding.value == INVALID_VALUE)) { + continue; + } + + SpvReflectTypeDescription* p_type = FindType(p_module, p_node->type_id); + if (IsNull(p_type)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // If the type is a pointer, resolve it + if (p_type->op == SpvOpTypePointer) { + // Find the type's node + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Should be the resolved type + p_type = FindType(p_module, p_type_node->type_id); + if (IsNull(p_type)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + + SpvReflectDescriptorBinding* p_descriptor = &p_module->descriptor_bindings[descriptor_index]; + p_descriptor->spirv_id = p_node->result_id; + p_descriptor->name = p_node->name; + p_descriptor->binding = p_node->decorations.binding.value; + p_descriptor->input_attachment_index = p_node->decorations.input_attachment_index.value; + p_descriptor->set = p_node->decorations.set.value; + p_descriptor->count = 1; + p_descriptor->uav_counter_id = p_node->decorations.uav_counter_buffer.value; + p_descriptor->type_description = p_type; + + // Copy image traits + if ((p_type->type_flags & SPV_REFLECT_TYPE_FLAG_EXTERNAL_MASK) == SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE) { + memcpy(&p_descriptor->image, &p_type->traits.image, sizeof(p_descriptor->image)); + } + + // This is a workaround for: https://github.com/KhronosGroup/glslang/issues/1096 + { + const uint32_t resource_mask = SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE | SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE; + if ((p_type->type_flags & resource_mask) == resource_mask) { + memcpy(&p_descriptor->image, &p_type->traits.image, sizeof(p_descriptor->image)); + } + } + + // Copy array traits + if (p_type->traits.array.dims_count > 0) { + p_descriptor->array.dims_count = p_type->traits.array.dims_count; + for (uint32_t dim_index = 0; dim_index < p_type->traits.array.dims_count; ++dim_index) { + uint32_t dim_value = p_type->traits.array.dims[dim_index]; + p_descriptor->array.dims[dim_index] = dim_value; + p_descriptor->count *= dim_value; + } + } + + // Count + + + p_descriptor->word_offset.binding = p_node->decorations.binding.word_offset; + p_descriptor->word_offset.set = p_node->decorations.set.word_offset; + + ++descriptor_index; + } + + if (p_module->descriptor_binding_count > 0) { + qsort(p_module->descriptor_bindings, + p_module->descriptor_binding_count, + sizeof(*(p_module->descriptor_bindings)), + SortCompareDescriptorBinding); + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDescriptorType(SpvReflectShaderModule* p_module) +{ + if (p_module->descriptor_binding_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]); + SpvReflectTypeDescription* p_type = p_descriptor->type_description; + + switch (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_EXTERNAL_MASK) { + default: assert(false && "unknown type flag"); break; + + case SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE: { + if (p_descriptor->image.dim == SpvDimBuffer) { + switch (p_descriptor->image.sampled) { + default: assert(false && "unknown texel buffer sampled value"); break; + case IMAGE_SAMPLED: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; break; + case IMAGE_STORAGE: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; break; + } + } + else if(p_descriptor->image.dim == SpvDimSubpassData) { + p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + } + else { + switch (p_descriptor->image.sampled) { + default: assert(false && "unknown image sampled value"); break; + case IMAGE_SAMPLED: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE; break; + case IMAGE_STORAGE: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE; break; + } + } + } + break; + + case SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLER: { + p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER; + } + break; + + case (SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE | SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE): { + // This is a workaround for: https://github.com/KhronosGroup/glslang/issues/1096 + if (p_descriptor->image.dim == SpvDimBuffer) { + switch (p_descriptor->image.sampled) { + default: assert(false && "unknown texel buffer sampled value"); break; + case IMAGE_SAMPLED: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; break; + case IMAGE_STORAGE: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; break; + } + } + else { + p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + } + } + break; + + case SPV_REFLECT_TYPE_FLAG_EXTERNAL_BLOCK: { + if (p_type->decoration_flags & SPV_REFLECT_DECORATION_BLOCK) { + p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + } + else if (p_type->decoration_flags & SPV_REFLECT_DECORATION_BUFFER_BLOCK) { + p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER; + } + else { + assert(false && "unknown struct"); + } + } + break; + } + + switch (p_descriptor->descriptor_type) { + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SAMPLER; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : p_descriptor->resource_type = (SpvReflectResourceType)(SPV_REFLECT_RESOURCE_FLAG_SAMPLER | SPV_REFLECT_RESOURCE_FLAG_SRV); break; + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_CBV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_CBV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break; + + case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + break; + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseUAVCounterBindings(SpvReflectShaderModule* p_module) +{ + char name[MAX_NODE_NAME_LENGTH]; + const char* k_count_tag = "@count"; + + for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]); + + if (p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) { + continue; + } + + SpvReflectDescriptorBinding* p_counter_descriptor = NULL; + // Use UAV counter buffer id if present... + if (p_descriptor->uav_counter_id != UINT32_MAX) { + for (uint32_t counter_descriptor_index = 0; counter_descriptor_index < p_module->descriptor_binding_count; ++counter_descriptor_index) { + SpvReflectDescriptorBinding* p_test_counter_descriptor = &(p_module->descriptor_bindings[counter_descriptor_index]); + if (p_test_counter_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) { + continue; + } + if (p_descriptor->uav_counter_id == p_test_counter_descriptor->spirv_id) { + p_counter_descriptor = p_test_counter_descriptor; + break; + } + } + } + // ...otherwise use old @count convention. + else { + const size_t descriptor_name_length = p_descriptor->name? strlen(p_descriptor->name): 0; + + memset(name, 0, MAX_NODE_NAME_LENGTH); + memcpy(name, p_descriptor->name, descriptor_name_length); +#if defined(WIN32) + strcat_s(name, MAX_NODE_NAME_LENGTH, k_count_tag); +#else + strcat(name, k_count_tag); +#endif + + for (uint32_t counter_descriptor_index = 0; counter_descriptor_index < p_module->descriptor_binding_count; ++counter_descriptor_index) { + SpvReflectDescriptorBinding* p_test_counter_descriptor = &(p_module->descriptor_bindings[counter_descriptor_index]); + if (p_test_counter_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) { + continue; + } + if (p_test_counter_descriptor->name && strcmp(name, p_test_counter_descriptor->name) == 0) { + p_counter_descriptor = p_test_counter_descriptor; + break; + } + } + } + + if (p_counter_descriptor != NULL) { + p_descriptor->uav_counter_binding = p_counter_descriptor; + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDescriptorBlockVariable( + Parser* p_parser, + SpvReflectShaderModule* p_module, + SpvReflectTypeDescription* p_type, + SpvReflectBlockVariable* p_var +) +{ + bool has_non_writable = false; + + if (IsNotNull(p_type->members) && (p_type->member_count > 0)) { + p_var->member_count = p_type->member_count; + p_var->members = (SpvReflectBlockVariable*)calloc(p_var->member_count, sizeof(*p_var->members)); + if (IsNull(p_var->members)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Resolve to element type if current type is array or run time array + if (p_type_node->op == SpvOpTypeArray) { + while (p_type_node->op == SpvOpTypeArray) { + p_type_node = FindNode(p_parser, p_type_node->array_traits.element_type_id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + } + else if(p_type_node->op == SpvOpTypeRuntimeArray) { + // Element type description + p_type = FindType(p_module, p_type_node->array_traits.element_type_id); + if (IsNull(p_type)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Element type node + p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + + // Parse members + for (uint32_t member_index = 0; member_index < p_type->member_count; ++member_index) { + SpvReflectTypeDescription* p_member_type = &p_type->members[member_index]; + SpvReflectBlockVariable* p_member_var = &p_var->members[member_index]; + bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT; + if (is_struct) { + SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_member_type, p_member_var); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + + p_member_var->name = p_type_node->member_names[member_index]; + p_member_var->offset = p_type_node->member_decorations[member_index].offset.value; + p_member_var->decoration_flags = ApplyDecorations(&p_type_node->member_decorations[member_index]); + p_member_var->flags |= SPV_REFLECT_VARIABLE_FLAGS_UNUSED; + if (!has_non_writable && (p_member_var->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE)) { + has_non_writable = true; + } + ApplyNumericTraits(p_member_type, &p_member_var->numeric); + if (p_member_type->op == SpvOpTypeArray) { + ApplyArrayTraits(p_member_type, &p_member_var->array); + } + + p_member_var->type_description = p_member_type; + } + } + + p_var->name = p_type->type_name; + p_var->type_description = p_type; + if (has_non_writable) { + p_var->decoration_flags |= SPV_REFLECT_DECORATION_NON_WRITABLE; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDescriptorBlockVariableSizes( + Parser* p_parser, + SpvReflectShaderModule* p_module, + bool is_parent_root, + bool is_parent_aos, + bool is_parent_rta, + SpvReflectBlockVariable* p_var +) +{ + if (p_var->member_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + // Absolute offsets + for (uint32_t member_index = 0; member_index < p_var->member_count; ++member_index) { + SpvReflectBlockVariable* p_member_var = &p_var->members[member_index]; + if (is_parent_root) { + p_member_var->absolute_offset = p_member_var->offset; + } + else { + p_member_var->absolute_offset = is_parent_aos ? 0 : p_member_var->offset + p_var->absolute_offset; + } + } + + // Size + for (uint32_t member_index = 0; member_index < p_var->member_count; ++member_index) { + SpvReflectBlockVariable* p_member_var = &p_var->members[member_index]; + SpvReflectTypeDescription* p_member_type = p_member_var->type_description; + + switch (p_member_type->op) { + case SpvOpTypeBool: { + p_member_var->size = SPIRV_WORD_SIZE; + } + break; + + case SpvOpTypeInt: + case SpvOpTypeFloat: { + p_member_var->size = p_member_type->traits.numeric.scalar.width / SPIRV_BYTE_WIDTH; + } + break; + + case SpvOpTypeVector: { + uint32_t size = p_member_type->traits.numeric.vector.component_count * + (p_member_type->traits.numeric.scalar.width / SPIRV_BYTE_WIDTH); + p_member_var->size = size; + } + break; + + case SpvOpTypeMatrix: { + if (p_member_var->decoration_flags & SPV_REFLECT_DECORATION_COLUMN_MAJOR) { + p_member_var->size = p_member_var->numeric.matrix.column_count * p_member_var->numeric.matrix.stride; + } + else if (p_member_var->decoration_flags & SPV_REFLECT_DECORATION_ROW_MAJOR) { + p_member_var->size = p_member_var->numeric.matrix.row_count * p_member_var->numeric.matrix.stride; + } + } + break; + + case SpvOpTypeArray: { + // If array of structs, parse members first... + bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT; + if (is_struct) { + SpvReflectResult result = ParseDescriptorBlockVariableSizes(p_parser, p_module, false, true, is_parent_rta, p_member_var); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + // ...then array + uint32_t element_count = (p_member_var->array.dims_count > 0 ? 1 : 0); + for (uint32_t i = 0; i < p_member_var->array.dims_count; ++i) { + element_count *= p_member_var->array.dims[i]; + } + p_member_var->size = element_count * p_member_var->array.stride; + } + break; + + case SpvOpTypeRuntimeArray: { + bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT; + if (is_struct) { + SpvReflectResult result = ParseDescriptorBlockVariableSizes(p_parser, p_module, false, true, true, p_member_var); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + } + break; + + case SpvOpTypeStruct: { + SpvReflectResult result = ParseDescriptorBlockVariableSizes(p_parser, p_module, false, is_parent_aos, is_parent_rta, p_member_var); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + break; + + default: + break; + } + } + + // Parse padded size using offset difference for all member except for the last entry... + for (uint32_t member_index = 0; member_index < (p_var->member_count - 1); ++member_index) { + SpvReflectBlockVariable* p_member_var = &p_var->members[member_index]; + SpvReflectBlockVariable* p_next_member_var = &p_var->members[member_index + 1]; + p_member_var->padded_size = p_next_member_var->offset - p_member_var->offset; + if (p_member_var->size > p_member_var->padded_size) { + p_member_var->size = p_member_var->padded_size; + } + if (is_parent_rta) { + p_member_var->padded_size = p_member_var->size; + } + } + // ...last entry just gets rounded up to near multiple of SPIRV_DATA_ALIGNMENT, which is 16 and + // subtract the offset. + if (p_var->member_count > 0) { + SpvReflectBlockVariable* p_member_var = &p_var->members[p_var->member_count - 1]; + p_member_var->padded_size = RoundUp(p_member_var->offset + p_member_var->size, SPIRV_DATA_ALIGNMENT) - p_member_var->offset; + if (p_member_var->size > p_member_var->padded_size) { + p_member_var->size = p_member_var->padded_size; + } + if (is_parent_rta) { + p_member_var->padded_size = p_member_var->size; + } + } + + // @TODO validate this with assertion + p_var->size = p_var->members[p_var->member_count - 1].offset + + p_var->members[p_var->member_count - 1].padded_size; + p_var->padded_size = p_var->size; + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDescriptorBlockVariableUsage( + Parser* p_parser, + SpvReflectShaderModule* p_module, + AccessChain* p_access_chain, + uint32_t index_index, + SpvOp override_op_type, + SpvReflectBlockVariable* p_var +) +{ + (void)p_parser; + (void)p_access_chain; + (void)p_var; + + // Clear the current variable's USED flag + p_var->flags &= ~SPV_REFLECT_VARIABLE_FLAGS_UNUSED; + + // Parsing arrays requires overriding the op type for + // for the lowest dim's element type. + SpvOp op_type = p_var->type_description->op; + if (override_op_type != (SpvOp)INVALID_VALUE) { + op_type = override_op_type; + } + + switch (op_type) { + default: break; + + case SpvOpTypeArray: { + // Parse through array's type hierarchy to find the actual/non-array element type + SpvReflectTypeDescription* p_type = p_var->type_description; + while ((p_type->op == SpvOpTypeArray) && (index_index < p_access_chain->index_count)) { + // Find the array element type id + Node* p_node = FindNode(p_parser, p_type->id); + if (p_node == NULL) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + uint32_t element_type_id = p_node->array_traits.element_type_id; + // Get the array element type + p_type = FindType(p_module, element_type_id); + if (p_type == NULL) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Next access index + index_index += 1; + } + // Parse current var again with a type override and advanced index index + SpvReflectResult result = ParseDescriptorBlockVariableUsage( + p_parser, + p_module, + p_access_chain, + index_index, + p_type->op, + p_var); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + break; + + case SpvOpTypeStruct: { + assert(p_var->member_count > 0); + if (p_var->member_count == 0) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_BLOCK_DATA; + } + + uint32_t index = p_access_chain->indexes[index_index]; + + if (index >= p_var->member_count) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_BLOCK_MEMBER_REFERENCE; + } + + SpvReflectBlockVariable* p_member_var = &p_var->members[index]; + if (index_index < p_access_chain->index_count) { + SpvReflectResult result = ParseDescriptorBlockVariableUsage( + p_parser, + p_module, + p_access_chain, + index_index + 1, + (SpvOp)INVALID_VALUE, + p_member_var); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + } + break; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDescriptorBlocks(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + if (p_module->descriptor_binding_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]); + SpvReflectTypeDescription* p_type = p_descriptor->type_description; + if ((p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) && + (p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) ) + { + continue; + } + + // Mark UNUSED + p_descriptor->block.flags |= SPV_REFLECT_VARIABLE_FLAGS_UNUSED; + // Parse descriptor block + SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_type, &p_descriptor->block); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + for (uint32_t access_chain_index = 0; access_chain_index < p_parser->access_chain_count; ++access_chain_index) { + AccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]); + // Skip any access chains that aren't touching this descriptor block + if (p_descriptor->spirv_id != p_access_chain->base_id) { + continue; + } + result = ParseDescriptorBlockVariableUsage( + p_parser, + p_module, + p_access_chain, + 0, + (SpvOp)INVALID_VALUE, + &p_descriptor->block); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + + p_descriptor->block.name = p_descriptor->name; + + bool is_parent_rta = (p_descriptor->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER); + result = ParseDescriptorBlockVariableSizes(p_parser, p_module, true, false, is_parent_rta, &p_descriptor->block); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + if (is_parent_rta) { + p_descriptor->block.size = 0; + p_descriptor->block.padded_size = 0; + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseFormat( + const SpvReflectTypeDescription* p_type, + SpvReflectFormat* p_format +) +{ + SpvReflectResult result = SPV_REFLECT_RESULT_ERROR_INTERNAL_ERROR; + bool signedness = p_type->traits.numeric.scalar.signedness; + if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_VECTOR) { + uint32_t component_count = p_type->traits.numeric.vector.component_count; + if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_FLOAT) { + switch (component_count) { + case 2: *p_format = SPV_REFLECT_FORMAT_R32G32_SFLOAT; break; + case 3: *p_format = SPV_REFLECT_FORMAT_R32G32B32_SFLOAT; break; + case 4: *p_format = SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT; break; + } + result = SPV_REFLECT_RESULT_SUCCESS; + } + else if (p_type->type_flags & (SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_BOOL)) { + switch (component_count) { + case 2: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32_SINT : SPV_REFLECT_FORMAT_R32G32_UINT; break; + case 3: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32B32_SINT : SPV_REFLECT_FORMAT_R32G32B32_UINT; break; + case 4: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32B32A32_SINT : SPV_REFLECT_FORMAT_R32G32B32A32_UINT; break; + } + result = SPV_REFLECT_RESULT_SUCCESS; + } + } + else if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_FLOAT) { + *p_format = SPV_REFLECT_FORMAT_R32_SFLOAT; + result = SPV_REFLECT_RESULT_SUCCESS; + } + else if (p_type->type_flags & (SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_BOOL)) { + if (signedness) { + *p_format = SPV_REFLECT_FORMAT_R32_SINT; + result = SPV_REFLECT_RESULT_SUCCESS; + } + else { + *p_format = SPV_REFLECT_FORMAT_R32_UINT; + result = SPV_REFLECT_RESULT_SUCCESS; + } + } + else if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) { + *p_format = SPV_REFLECT_FORMAT_UNDEFINED; + result = SPV_REFLECT_RESULT_SUCCESS; + } + return result; +} + +static SpvReflectResult ParseInterfaceVariable( + Parser* p_parser, + const Decorations* p_type_node_decorations, + SpvReflectShaderModule* p_module, + SpvReflectTypeDescription* p_type, + SpvReflectInterfaceVariable* p_var, + bool* p_has_built_in +) +{ + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + + if (p_type->member_count > 0) { + p_var->member_count = p_type->member_count; + p_var->members = (SpvReflectInterfaceVariable*)calloc(p_var->member_count, sizeof(*p_var->members)); + if (IsNull(p_var->members)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + for (uint32_t member_index = 0; member_index < p_type_node->member_count; ++member_index) { + Decorations* p_member_decorations = &p_type_node->member_decorations[member_index]; + SpvReflectTypeDescription* p_member_type = &p_type->members[member_index]; + SpvReflectInterfaceVariable* p_member_var = &p_var->members[member_index]; + SpvReflectResult result = ParseInterfaceVariable(p_parser, p_member_decorations, p_module, p_member_type, p_member_var, p_has_built_in); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + } + + p_var->name = p_type_node->name; + p_var->decoration_flags = ApplyDecorations(p_type_node_decorations); + p_var->built_in = p_type_node_decorations->built_in; + ApplyNumericTraits(p_type, &p_var->numeric); + if (p_type->op == SpvOpTypeArray) { + ApplyArrayTraits(p_type, &p_var->array); + } + + p_var->type_description = p_type; + + *p_has_built_in |= p_type_node_decorations->is_built_in; + + SpvReflectResult result = ParseFormat(p_var->type_description, &p_var->format); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseInterfaceVariables( + Parser* p_parser, + SpvReflectShaderModule* p_module, + SpvReflectEntryPoint* p_entry, + size_t io_var_count, + uint32_t* io_vars +) +{ + if (io_var_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_entry->input_variable_count = 0; + p_entry->output_variable_count = 0; + for (size_t i = 0; i < io_var_count; ++i) { + uint32_t var_result_id = *(io_vars + i); + Node* p_node = FindNode(p_parser, var_result_id); + if (IsNull(p_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + + if (p_node->storage_class == SpvStorageClassInput) { + p_entry->input_variable_count += 1; + } + else if (p_node->storage_class == SpvStorageClassOutput) { + p_entry->output_variable_count += 1; + } + } + + if (p_entry->input_variable_count > 0) { + p_entry->input_variables = (SpvReflectInterfaceVariable*)calloc(p_entry->input_variable_count, sizeof(*(p_entry->input_variables))); + if (IsNull(p_entry->input_variables)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + + if (p_entry->output_variable_count > 0) { + p_entry->output_variables = (SpvReflectInterfaceVariable*)calloc(p_entry->output_variable_count, sizeof(*(p_entry->output_variables))); + if (IsNull(p_entry->output_variables)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + size_t input_index = 0; + size_t output_index = 0; + for (size_t i = 0; i < io_var_count; ++i) { + uint32_t var_result_id = *(io_vars + i); + Node* p_node = FindNode(p_parser, var_result_id); + if (IsNull(p_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + + SpvReflectTypeDescription* p_type = FindType(p_module, p_node->type_id); + if (IsNull(p_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // If the type is a pointer, resolve it + if (p_type->op == SpvOpTypePointer) { + // Find the type's node + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Should be the resolved type + p_type = FindType(p_module, p_type_node->type_id); + if (IsNull(p_type)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + + SpvReflectInterfaceVariable* p_var = NULL; + if (p_node->storage_class == SpvStorageClassInput) { + p_var = &(p_entry->input_variables[input_index]); + p_var->storage_class = SpvStorageClassInput; + ++input_index; + } + else if (p_node->storage_class == SpvStorageClassOutput) { + p_var = &(p_entry->output_variables[output_index]); + p_var->storage_class = SpvStorageClassOutput; + ++output_index; + } else { + // interface variables can only have input or output storage classes; + // anything else is either a new addition or an error. + assert(false && "Unsupported storage class for interface variable"); + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_STORAGE_CLASS; + } + + bool has_built_in = p_node->decorations.is_built_in; + SpvReflectResult result = ParseInterfaceVariable( + p_parser, + &p_type_node->decorations, + p_module, + p_type, + p_var, + &has_built_in); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + // SPIR-V result id + p_var->spirv_id = p_node->result_id; + // Name + p_var->name = p_node->name; + // Semantic + p_var->semantic = p_node->decorations.semantic.value; + + // Decorate with built-in if any member is built-in + if (has_built_in) { + p_var->decoration_flags |= SPV_REFLECT_DECORATION_BUILT_IN; + } + + // Location is decorated on OpVariable node, not the type node. + p_var->location = p_node->decorations.location.value; + p_var->word_offset.location = p_node->decorations.location.word_offset; + + // Built in + if (p_node->decorations.is_built_in) { + p_var->built_in = p_node->decorations.built_in; + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult EnumerateAllPushConstants( + SpvReflectShaderModule* p_module, + size_t* p_push_constant_count, + uint32_t** p_push_constants +) +{ + *p_push_constant_count = p_module->push_constant_block_count; + if (*p_push_constant_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + *p_push_constants = (uint32_t*)calloc(*p_push_constant_count, sizeof(**p_push_constants)); + + if (IsNull(*p_push_constants)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + for (size_t i = 0; i < *p_push_constant_count; ++i) { + (*p_push_constants)[i] = p_module->push_constant_blocks[i].spirv_id; + } + qsort(*p_push_constants, *p_push_constant_count, sizeof(**p_push_constants), + SortCompareUint32); + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult TraverseCallGraph( + Parser* p_parser, + Function* p_func, + size_t* p_func_count, + uint32_t* p_func_ids, + uint32_t depth +) +{ + if (depth > p_parser->function_count) { + // Vulkan does not permit recursion (Vulkan spec Appendix A): + // "Recursion: The static function-call graph for an entry point must not + // contain cycles." + return SPV_REFLECT_RESULT_ERROR_SPIRV_RECURSION; + } + if (IsNotNull(p_func_ids)) { + p_func_ids[(*p_func_count)++] = p_func->id; + } else { + ++*p_func_count; + } + for (size_t i = 0; i < p_func->callee_count; ++i) { + SpvReflectResult result = TraverseCallGraph( + p_parser, p_func->callee_ptrs[i], p_func_count, p_func_ids, depth + 1); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseStaticallyUsedResources( + Parser* p_parser, + SpvReflectShaderModule* p_module, + SpvReflectEntryPoint* p_entry, + size_t uniform_count, + uint32_t* uniforms, + size_t push_constant_count, + uint32_t* push_constants +) +{ + // Find function with the right id + Function* p_func = NULL; + for (size_t i = 0; i < p_parser->function_count; ++i) { + if (p_parser->functions[i].id == p_entry->id) { + p_func = &(p_parser->functions[i]); + break; + } + } + if (p_func == NULL) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + + size_t called_function_count = 0; + SpvReflectResult result = TraverseCallGraph( + p_parser, + p_func, + &called_function_count, + NULL, + 0); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + uint32_t* p_called_functions = NULL; + if (called_function_count > 0) { + p_called_functions = (uint32_t*)calloc(called_function_count, sizeof(*p_called_functions)); + if (IsNull(p_called_functions)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + called_function_count = 0; + result = TraverseCallGraph( + p_parser, + p_func, + &called_function_count, + p_called_functions, + 0); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + if (called_function_count > 0) { + qsort( + p_called_functions, + called_function_count, + sizeof(*p_called_functions), + SortCompareUint32); + } + called_function_count = DedupSortedUint32(p_called_functions, called_function_count); + + uint32_t used_variable_count = 0; + for (size_t i = 0, j = 0; i < called_function_count; ++i) { + // No need to bounds check j because a missing ID issue would have been + // found during TraverseCallGraph + while (p_parser->functions[j].id != p_called_functions[i]) { + ++j; + } + used_variable_count += p_parser->functions[j].accessed_ptr_count; + } + uint32_t* used_variables = NULL; + if (used_variable_count > 0) { + used_variables = (uint32_t*)calloc(used_variable_count, + sizeof(*used_variables)); + if (IsNull(used_variables)) { + SafeFree(p_called_functions); + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + used_variable_count = 0; + for (size_t i = 0, j = 0; i < called_function_count; ++i) { + while (p_parser->functions[j].id != p_called_functions[i]) { + ++j; + } + + memcpy(&used_variables[used_variable_count], + p_parser->functions[j].accessed_ptrs, + p_parser->functions[j].accessed_ptr_count * sizeof(*used_variables)); + used_variable_count += p_parser->functions[j].accessed_ptr_count; + } + SafeFree(p_called_functions); + + if (used_variable_count > 0) { + qsort(used_variables, used_variable_count, sizeof(*used_variables), + SortCompareUint32); + } + used_variable_count = (uint32_t)DedupSortedUint32(used_variables, + used_variable_count); + + // Do set intersection to find the used uniform and push constants + size_t used_uniform_count = 0; + // + SpvReflectResult result0 = IntersectSortedUint32( + used_variables, + used_variable_count, + uniforms, + uniform_count, + &p_entry->used_uniforms, + &used_uniform_count); + + size_t used_push_constant_count = 0; + // + SpvReflectResult result1 = IntersectSortedUint32( + used_variables, + used_variable_count, + push_constants, + push_constant_count, + &p_entry->used_push_constants, + &used_push_constant_count); + + for (uint32_t j = 0; j < p_module->descriptor_binding_count; ++j) { + SpvReflectDescriptorBinding* p_binding = &p_module->descriptor_bindings[j]; + bool found = SearchSortedUint32( + used_variables, + used_variable_count, + p_binding->spirv_id); + if (found) { + p_binding->accessed = 1; + } + } + + SafeFree(used_variables); + if (result0 != SPV_REFLECT_RESULT_SUCCESS) { + return result0; + } + if (result1 != SPV_REFLECT_RESULT_SUCCESS) { + return result1; + } + + p_entry->used_uniform_count = (uint32_t)used_uniform_count; + p_entry->used_push_constant_count = (uint32_t)used_push_constant_count; + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseEntryPoints(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + if (p_parser->entry_point_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_module->entry_point_count = p_parser->entry_point_count; + p_module->entry_points = (SpvReflectEntryPoint*)calloc(p_module->entry_point_count, + sizeof(*(p_module->entry_points))); + if (IsNull(p_module->entry_points)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + SpvReflectResult result; + size_t uniform_count = 0; + uint32_t* uniforms = NULL; + if ((result = EnumerateAllUniforms(p_module, &uniform_count, &uniforms)) != + SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + size_t push_constant_count = 0; + uint32_t* push_constants = NULL; + if ((result = EnumerateAllPushConstants(p_module, &push_constant_count, &push_constants)) != + SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + size_t entry_point_index = 0; + for (size_t i = 0; entry_point_index < p_parser->entry_point_count && i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->op != SpvOpEntryPoint) { + continue; + } + + SpvReflectEntryPoint* p_entry_point = &(p_module->entry_points[entry_point_index]); + CHECKED_READU32_CAST(p_parser, p_node->word_offset + 1, SpvExecutionModel, p_entry_point->spirv_execution_model); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_entry_point->id); + + switch (p_entry_point->spirv_execution_model) { + default: break; + case SpvExecutionModelVertex : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_VERTEX_BIT; break; + case SpvExecutionModelTessellationControl : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break; + case SpvExecutionModelTessellationEvaluation : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break; + case SpvExecutionModelGeometry : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT; break; + case SpvExecutionModelFragment : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT; break; + case SpvExecutionModelGLCompute : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT; break; + } + + ++entry_point_index; + + // Name length is required to calculate next operand + uint32_t name_start_word_offset = 3; + uint32_t name_length_with_terminator = 0; + result = ReadStr(p_parser, p_node->word_offset + name_start_word_offset, 0, p_node->word_count, &name_length_with_terminator, NULL); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + p_entry_point->name = (const char*)(p_parser->spirv_code + p_node->word_offset + name_start_word_offset); + + uint32_t name_word_count = RoundUp(name_length_with_terminator, SPIRV_WORD_SIZE) / SPIRV_WORD_SIZE; + size_t interface_variable_count = (p_node->word_count - (name_start_word_offset + name_word_count)); + uint32_t* interface_variables = NULL; + if (interface_variable_count > 0) { + interface_variables = (uint32_t*)calloc(interface_variable_count, sizeof(*(interface_variables))); + if (IsNull(interface_variables)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + + for (uint32_t var_index = 0; var_index < interface_variable_count; ++var_index) { + uint32_t var_result_id = (uint32_t)INVALID_VALUE; + uint32_t offset = name_start_word_offset + name_word_count + var_index; + CHECKED_READU32(p_parser, p_node->word_offset + offset, var_result_id); + interface_variables[var_index] = var_result_id; + } + + result = ParseInterfaceVariables( + p_parser, + p_module, + p_entry_point, + interface_variable_count, + interface_variables); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + SafeFree(interface_variables); + + result = ParseStaticallyUsedResources( + p_parser, + p_module, + p_entry_point, + uniform_count, + uniforms, + push_constant_count, + push_constants); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + } + + SafeFree(uniforms); + SafeFree(push_constants); + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if ((p_node->op != SpvOpVariable) || (p_node->storage_class != SpvStorageClassPushConstant)) { + continue; + } + + p_module->push_constant_block_count += 1; + } + + if (p_module->push_constant_block_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_module->push_constant_blocks = (SpvReflectBlockVariable*)calloc(p_module->push_constant_block_count, sizeof(*p_module->push_constant_blocks)); + if (IsNull(p_module->push_constant_blocks)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + + uint32_t push_constant_index = 0; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if ((p_node->op != SpvOpVariable) || (p_node->storage_class != SpvStorageClassPushConstant)) { + continue; + } + + SpvReflectTypeDescription* p_type = FindType(p_module, p_node->type_id); + if (IsNull(p_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // If the type is a pointer, resolve it + if (p_type->op == SpvOpTypePointer) { + // Find the type's node + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + // Should be the resolved type + p_type = FindType(p_module, p_type_node->type_id); + if (IsNull(p_type)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + } + + Node* p_type_node = FindNode(p_parser, p_type->id); + if (IsNull(p_type_node)) { + return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE; + } + + SpvReflectBlockVariable* p_push_constant = &p_module->push_constant_blocks[push_constant_index]; + p_push_constant->spirv_id = p_node->result_id; + SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_type, p_push_constant); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + result = ParseDescriptorBlockVariableSizes(p_parser, p_module, true, false, false, p_push_constant); + if (result != SPV_REFLECT_RESULT_SUCCESS) { + return result; + } + + ++push_constant_index; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static int SortCompareDescriptorSet(const void* a, const void* b) +{ + const SpvReflectDescriptorSet* p_elem_a = (const SpvReflectDescriptorSet*)a; + const SpvReflectDescriptorSet* p_elem_b = (const SpvReflectDescriptorSet*)b; + int value = (int)(p_elem_a->set) - (int)(p_elem_b->set); + // We should never see duplicate descriptor set numbers in a shader; if so, a tiebreaker + // would be needed here. + assert(value != 0); + return value; +} + +static SpvReflectResult ParseEntrypointDescriptorSets(SpvReflectShaderModule* p_module) { + // Update the entry point's sets + for (uint32_t i = 0; i < p_module->entry_point_count; ++i) { + SpvReflectEntryPoint* p_entry = &p_module->entry_points[i]; + for (uint32_t j = 0; j < p_entry->descriptor_set_count; ++j) { + SafeFree(p_entry->descriptor_sets[j].bindings); + } + SafeFree(p_entry->descriptor_sets); + p_entry->descriptor_set_count = 0; + for (uint32_t j = 0; j < p_module->descriptor_set_count; ++j) { + const SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j]; + for (uint32_t k = 0; k < p_set->binding_count; ++k) { + bool found = SearchSortedUint32( + p_entry->used_uniforms, + p_entry->used_uniform_count, + p_set->bindings[k]->spirv_id); + if (found) { + ++p_entry->descriptor_set_count; + break; + } + } + } + + p_entry->descriptor_sets = NULL; + if (p_entry->descriptor_set_count > 0) { + p_entry->descriptor_sets = (SpvReflectDescriptorSet*)calloc(p_entry->descriptor_set_count, + sizeof(*p_entry->descriptor_sets)); + if (IsNull(p_entry->descriptor_sets)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + } + p_entry->descriptor_set_count = 0; + for (uint32_t j = 0; j < p_module->descriptor_set_count; ++j) { + const SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j]; + uint32_t count = 0; + for (uint32_t k = 0; k < p_set->binding_count; ++k) { + bool found = SearchSortedUint32( + p_entry->used_uniforms, + p_entry->used_uniform_count, + p_set->bindings[k]->spirv_id); + if (found) { + ++count; + } + } + if (count == 0) { + continue; + } + SpvReflectDescriptorSet* p_entry_set = &p_entry->descriptor_sets[ + p_entry->descriptor_set_count++]; + p_entry_set->set = p_set->set; + p_entry_set->bindings = (SpvReflectDescriptorBinding**)calloc(count, + sizeof(*p_entry_set->bindings)); + if (IsNull(p_entry_set->bindings)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + for (uint32_t k = 0; k < p_set->binding_count; ++k) { + bool found = SearchSortedUint32( + p_entry->used_uniforms, + p_entry->used_uniform_count, + p_set->bindings[k]->spirv_id); + if (found) { + p_entry_set->bindings[p_entry_set->binding_count++] = p_set->bindings[k]; + } + } + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult ParseDescriptorSets(SpvReflectShaderModule* p_module) +{ + // Count the descriptors in each set + for (uint32_t i = 0; i < p_module->descriptor_binding_count; ++i) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[i]); + + // Look for a target set using the descriptor's set number + SpvReflectDescriptorSet* p_target_set = NULL; + for (uint32_t j = 0; j < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++j) { + SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j]; + if (p_set->set == p_descriptor->set) { + p_target_set = p_set; + break; + } + } + + // If a target set isn't found, find the first available one. + if (IsNull(p_target_set)) { + for (uint32_t j = 0; j < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++j) { + SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j]; + if (p_set->set == (uint32_t)INVALID_VALUE) { + p_target_set = p_set; + p_target_set->set = p_descriptor->set; + break; + } + } + } + + if (IsNull(p_target_set)) { + return SPV_REFLECT_RESULT_ERROR_INTERNAL_ERROR; + } + + p_target_set->binding_count += 1; + } + + // Count the descriptor sets + for (uint32_t i = 0; i < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++i) { + const SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[i]; + if (p_set->set != (uint32_t)INVALID_VALUE) { + p_module->descriptor_set_count += 1; + } + } + + // Sort the descriptor sets based on numbers + if (p_module->descriptor_set_count > 0) { + qsort(p_module->descriptor_sets, + p_module->descriptor_set_count, + sizeof(*(p_module->descriptor_sets)), + SortCompareDescriptorSet); + } + + // Build descriptor pointer array + for (uint32_t i = 0; i <p_module->descriptor_set_count; ++i) { + SpvReflectDescriptorSet* p_set = &(p_module->descriptor_sets[i]); + p_set->bindings = (SpvReflectDescriptorBinding **)calloc(p_set->binding_count, sizeof(*(p_set->bindings))); + + uint32_t descriptor_index = 0; + for (uint32_t j = 0; j < p_module->descriptor_binding_count; ++j) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[j]); + if (p_descriptor->set == p_set->set) { + assert(descriptor_index < p_set->binding_count); + p_set->bindings[descriptor_index] = p_descriptor; + ++descriptor_index; + } + } + } + + return ParseEntrypointDescriptorSets(p_module); +} + +static SpvReflectResult DisambiguateStorageBufferSrvUav(SpvReflectShaderModule* p_module) +{ + if (p_module->descriptor_binding_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) { + SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]); + // Skip everything that isn't a STORAGE_BUFFER descriptor + if (p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) { + continue; + } + + // + // Vulkan doesn't disambiguate between SRVs and UAVs so they + // come back as STORAGE_BUFFER. The block parsing process will + // mark a block as non-writable should any member of the block + // or its descendants are non-writable. + // + if (p_descriptor->block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) { + p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV; + } + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +static SpvReflectResult SynchronizeDescriptorSets(SpvReflectShaderModule* p_module) +{ + // Free and reset all descriptor set numbers + for (uint32_t i = 0; i < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++i) { + SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[i]; + SafeFree(p_set->bindings); + p_set->binding_count = 0; + p_set->set = (uint32_t)INVALID_VALUE; + } + // Set descriptor set count to zero + p_module->descriptor_set_count = 0; + + SpvReflectResult result = ParseDescriptorSets(p_module); + return result; +} + +SpvReflectResult spvReflectGetShaderModule( + size_t size, + const void* p_code, + SpvReflectShaderModule* p_module +) +{ + return spvReflectCreateShaderModule(size, p_code, p_module); +} + +SpvReflectResult spvReflectCreateShaderModule( + size_t size, + const void* p_code, + SpvReflectShaderModule* p_module +) +{ + // Initialize all module fields to zero + memset(p_module, 0, sizeof(*p_module)); + + // Allocate module internals +#ifdef __cplusplus + p_module->_internal = (SpvReflectShaderModule::Internal*)calloc(1, sizeof(*(p_module->_internal))); +#else + p_module->_internal = calloc(1, sizeof(*(p_module->_internal))); +#endif + if (IsNull(p_module->_internal)) { + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + // Allocate SPIR-V code storage + p_module->_internal->spirv_size = size; + p_module->_internal->spirv_code = (uint32_t*)calloc(1, p_module->_internal->spirv_size); + p_module->_internal->spirv_word_count = (uint32_t)(size / SPIRV_WORD_SIZE); + if (IsNull(p_module->_internal->spirv_code)) { + SafeFree(p_module->_internal); + return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; + } + memcpy(p_module->_internal->spirv_code, p_code, size); + + Parser parser = { 0 }; + SpvReflectResult result = CreateParser(p_module->_internal->spirv_size, + p_module->_internal->spirv_code, + &parser); + + // Generator + { + const uint32_t* p_ptr = (const uint32_t*)p_module->_internal->spirv_code; + p_module->generator = (SpvReflectGenerator)((*(p_ptr + 2) & 0xFFFF0000) >> 16); + } + + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseNodes(&parser); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseStrings(&parser); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseSource(&parser, p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseFunctions(&parser); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseMemberCounts(&parser); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseNames(&parser); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseDecorations(&parser); + } + + // Start of reflection data parsing + if (result == SPV_REFLECT_RESULT_SUCCESS) { + p_module->source_language = parser.source_language; + p_module->source_language_version = parser.source_language_version; + + // Zero out descriptor set data + p_module->descriptor_set_count = 0; + memset(p_module->descriptor_sets, 0, SPV_REFLECT_MAX_DESCRIPTOR_SETS * sizeof(*p_module->descriptor_sets)); + // Initialize descriptor set numbers + for (uint32_t set_number = 0; set_number < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++set_number) { + p_module->descriptor_sets[set_number].set = (uint32_t)INVALID_VALUE; + } + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseTypes(&parser, p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseDescriptorBindings(&parser, p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseDescriptorType(p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseUAVCounterBindings(p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseDescriptorBlocks(&parser, p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParsePushConstantBlocks(&parser, p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseEntryPoints(&parser, p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS && p_module->entry_point_count > 0) { + SpvReflectEntryPoint* p_entry = &(p_module->entry_points[0]); + p_module->entry_point_name = p_entry->name; + p_module->entry_point_id = p_entry->id; + p_module->spirv_execution_model = p_entry->spirv_execution_model; + p_module->shader_stage = p_entry->shader_stage; + p_module->input_variable_count = p_entry->input_variable_count; + p_module->input_variables = p_entry->input_variables; + p_module->output_variable_count = p_entry->output_variable_count; + p_module->output_variables = p_entry->output_variables; + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = DisambiguateStorageBufferSrvUav(p_module); + } + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = SynchronizeDescriptorSets(p_module); + } + + // Destroy module if parse was not successful + if (result != SPV_REFLECT_RESULT_SUCCESS) { + spvReflectDestroyShaderModule(p_module); + } + + DestroyParser(&parser); + + return result; +} + +static void SafeFreeTypes(SpvReflectTypeDescription* p_type) +{ + if (IsNull(p_type)) { + return; + } + + if (IsNotNull(p_type->members)) { + for (size_t i = 0; i < p_type->member_count; ++i) { + SpvReflectTypeDescription* p_member = &p_type->members[i]; + SafeFreeTypes(p_member); + } + + SafeFree(p_type->members); + p_type->members = NULL; + } +} + +static void SafeFreeBlockVariables(SpvReflectBlockVariable* p_block) +{ + if (IsNull(p_block)) { + return; + } + + if (IsNotNull(p_block->members)) { + for (size_t i = 0; i < p_block->member_count; ++i) { + SpvReflectBlockVariable* p_member = &p_block->members[i]; + SafeFreeBlockVariables(p_member); + } + + SafeFree(p_block->members); + p_block->members = NULL; + } +} + +static void SafeFreeInterfaceVariable(SpvReflectInterfaceVariable* p_interface) +{ + if (IsNull(p_interface)) { + return; + } + + if (IsNotNull(p_interface->members)) { + for (size_t i = 0; i < p_interface->member_count; ++i) { + SpvReflectInterfaceVariable* p_member = &p_interface->members[i]; + SafeFreeInterfaceVariable(p_member); + } + + SafeFree(p_interface->members); + p_interface->members = NULL; + } +} + +void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module) +{ + if (IsNull(p_module->_internal)) { + return; + } + + // Descriptor set bindings + for (size_t i = 0; i < p_module->descriptor_set_count; ++i) { + SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[i]; + free(p_set->bindings); + } + + // Descriptor binding blocks + for (size_t i = 0; i < p_module->descriptor_binding_count; ++i) { + SpvReflectDescriptorBinding* p_descriptor = &p_module->descriptor_bindings[i]; + SafeFreeBlockVariables(&p_descriptor->block); + } + SafeFree(p_module->descriptor_bindings); + + // Entry points + for (size_t i = 0; i < p_module->entry_point_count; ++i) { + SpvReflectEntryPoint* p_entry = &p_module->entry_points[i]; + for (size_t j = 0; j < p_entry->input_variable_count; j++) { + SafeFreeInterfaceVariable(&p_entry->input_variables[j]); + } + for (size_t j = 0; j < p_entry->output_variable_count; j++) { + SafeFreeInterfaceVariable(&p_entry->output_variables[j]); + } + for (uint32_t j = 0; j < p_entry->descriptor_set_count; ++j) { + SafeFree(p_entry->descriptor_sets[j].bindings); + } + SafeFree(p_entry->descriptor_sets); + SafeFree(p_entry->input_variables); + SafeFree(p_entry->output_variables); + SafeFree(p_entry->used_uniforms); + SafeFree(p_entry->used_push_constants); + } + SafeFree(p_module->entry_points); + + // Push constants + for (size_t i = 0; i < p_module->push_constant_block_count; ++i) { + SafeFreeBlockVariables(&p_module->push_constant_blocks[i]); + } + SafeFree(p_module->push_constant_blocks); + + // Type infos + for (size_t i = 0; i < p_module->_internal->type_description_count; ++i) { + SpvReflectTypeDescription* p_type = &p_module->_internal->type_descriptions[i]; + if (IsNotNull(p_type->members)) { + SafeFreeTypes(p_type); + } + SafeFree(p_type->members); + } + SafeFree(p_module->_internal->type_descriptions); + + // Free SPIR-V code + SafeFree(p_module->_internal->spirv_code); + // Free internal + SafeFree(p_module->_internal); +} + +uint32_t spvReflectGetCodeSize(const SpvReflectShaderModule* p_module) +{ + if (IsNull(p_module)) { + return 0; + } + + return (uint32_t)(p_module->_internal->spirv_size); +} + +const uint32_t* spvReflectGetCode(const SpvReflectShaderModule* p_module) +{ + if (IsNull(p_module)) { + return NULL; + } + + return p_module->_internal->spirv_code; +} + +const SpvReflectEntryPoint* spvReflectGetEntryPoint( + const SpvReflectShaderModule* p_module, + const char* entry_point +) { + if (IsNull(p_module) || IsNull(entry_point)) { + return NULL; + } + + for (uint32_t i = 0; i < p_module->entry_point_count; ++i) { + if (strcmp(p_module->entry_points[i].name, entry_point) == 0) { + return &p_module->entry_points[i]; + } + } + return NULL; +} + +SpvReflectResult spvReflectEnumerateDescriptorBindings( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectDescriptorBinding** pp_bindings +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (IsNotNull(pp_bindings)) { + if (*p_count != p_module->descriptor_binding_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectDescriptorBinding* p_bindings = (SpvReflectDescriptorBinding*)&p_module->descriptor_bindings[index]; + pp_bindings[index] = p_bindings; + } + } + else { + *p_count = p_module->descriptor_binding_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateEntryPointDescriptorBindings( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t* p_count, + SpvReflectDescriptorBinding** pp_bindings +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + + uint32_t count = 0; + for (uint32_t i = 0; i < p_module->descriptor_binding_count; ++i) { + bool found = SearchSortedUint32( + p_entry->used_uniforms, + p_entry->used_uniform_count, + p_module->descriptor_bindings[i].spirv_id); + if (found) { + if (IsNotNull(pp_bindings)) { + if (count >= *p_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + pp_bindings[count++] = (SpvReflectDescriptorBinding*)&p_module->descriptor_bindings[i]; + } else { + ++count; + } + } + } + if (IsNotNull(pp_bindings)) { + if (count != *p_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + } else { + *p_count = count; + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateDescriptorSets( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectDescriptorSet** pp_sets +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (IsNotNull(pp_sets)) { + if (*p_count != p_module->descriptor_set_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectDescriptorSet* p_set = (SpvReflectDescriptorSet*)&p_module->descriptor_sets[index]; + pp_sets[index] = p_set; + } + } + else { + *p_count = p_module->descriptor_set_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateEntryPointDescriptorSets( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t* p_count, + SpvReflectDescriptorSet** pp_sets +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + + if (IsNotNull(pp_sets)) { + if (*p_count != p_entry->descriptor_set_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectDescriptorSet* p_set = (SpvReflectDescriptorSet*)&p_entry->descriptor_sets[index]; + pp_sets[index] = p_set; + } + } + else { + *p_count = p_entry->descriptor_set_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateInputVariables( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectInterfaceVariable** pp_variables +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (IsNotNull(pp_variables)) { + if (*p_count != p_module->input_variable_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectInterfaceVariable* p_var = (SpvReflectInterfaceVariable*)&p_module->input_variables[index]; + pp_variables[index] = p_var; + } + } + else { + *p_count = p_module->input_variable_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateEntryPointInputVariables( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t* p_count, + SpvReflectInterfaceVariable** pp_variables +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + + if (IsNotNull(pp_variables)) { + if (*p_count != p_entry->input_variable_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectInterfaceVariable* p_var = (SpvReflectInterfaceVariable*)&p_entry->input_variables[index]; + pp_variables[index] = p_var; + } + } + else { + *p_count = p_entry->input_variable_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateOutputVariables( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectInterfaceVariable** pp_variables +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (IsNotNull(pp_variables)) { + if (*p_count != p_module->output_variable_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectInterfaceVariable* p_var = (SpvReflectInterfaceVariable*)&p_module->output_variables[index]; + pp_variables[index] = p_var; + } + } + else { + *p_count = p_module->output_variable_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumerateEntryPointOutputVariables( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t* p_count, + SpvReflectInterfaceVariable** pp_variables +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + + if (IsNotNull(pp_variables)) { + if (*p_count != p_entry->output_variable_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectInterfaceVariable* p_var = (SpvReflectInterfaceVariable*)&p_entry->output_variables[index]; + pp_variables[index] = p_var; + } + } + else { + *p_count = p_entry->output_variable_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectEnumeratePushConstantBlocks( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectBlockVariable** pp_blocks +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (pp_blocks != NULL) { + if (*p_count != p_module->push_constant_block_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectBlockVariable* p_push_constant_blocks = (SpvReflectBlockVariable*)&p_module->push_constant_blocks[index]; + pp_blocks[index] = p_push_constant_blocks; + } + } + else { + *p_count = p_module->push_constant_block_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} +SpvReflectResult spvReflectEnumeratePushConstants( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectBlockVariable** pp_blocks +) +{ + return spvReflectEnumeratePushConstantBlocks(p_module, p_count, pp_blocks); +} + +SpvReflectResult spvReflectEnumerateEntryPointPushConstantBlocks( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t* p_count, + SpvReflectBlockVariable** pp_blocks +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + + uint32_t count = 0; + for (uint32_t i = 0; i < p_module->push_constant_block_count; ++i) { + bool found = SearchSortedUint32(p_entry->used_push_constants, + p_entry->used_push_constant_count, + p_module->push_constant_blocks[i].spirv_id); + if (found) { + if (IsNotNull(pp_blocks)) { + if (count >= *p_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + pp_blocks[count++] = (SpvReflectBlockVariable*)&p_module->push_constant_blocks[i]; + } else { + ++count; + } + } + } + if (IsNotNull(pp_blocks)) { + if (count != *p_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + } else { + *p_count = count; + } + return SPV_REFLECT_RESULT_SUCCESS; +} + +const SpvReflectDescriptorBinding* spvReflectGetDescriptorBinding( + const SpvReflectShaderModule* p_module, + uint32_t binding_number, + uint32_t set_number, + SpvReflectResult* p_result +) +{ + const SpvReflectDescriptorBinding* p_descriptor = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->descriptor_binding_count; ++index) { + const SpvReflectDescriptorBinding* p_potential = &p_module->descriptor_bindings[index]; + if ((p_potential->binding == binding_number) && (p_potential->set == set_number)) { + p_descriptor = p_potential; + break; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_descriptor) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_descriptor; +} + +const SpvReflectDescriptorBinding* spvReflectGetEntryPointDescriptorBinding( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t binding_number, + uint32_t set_number, + SpvReflectResult* p_result +) +{ + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectDescriptorBinding* p_descriptor = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->descriptor_binding_count; ++index) { + const SpvReflectDescriptorBinding* p_potential = &p_module->descriptor_bindings[index]; + bool found = SearchSortedUint32( + p_entry->used_uniforms, + p_entry->used_uniform_count, + p_potential->spirv_id); + if ((p_potential->binding == binding_number) && (p_potential->set == set_number) && found) { + p_descriptor = p_potential; + break; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_descriptor) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_descriptor; +} + +const SpvReflectDescriptorSet* spvReflectGetDescriptorSet( + const SpvReflectShaderModule* p_module, + uint32_t set_number, + SpvReflectResult* p_result +) +{ + const SpvReflectDescriptorSet* p_set = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->descriptor_set_count; ++index) { + const SpvReflectDescriptorSet* p_potential = &p_module->descriptor_sets[index]; + if (p_potential->set == set_number) { + p_set = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_set) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_set; +} + +const SpvReflectDescriptorSet* spvReflectGetEntryPointDescriptorSet( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t set_number, + SpvReflectResult* p_result) +{ + const SpvReflectDescriptorSet* p_set = NULL; + if (IsNotNull(p_module)) { + const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + for (uint32_t index = 0; index < p_entry->descriptor_set_count; ++index) { + const SpvReflectDescriptorSet* p_potential = &p_entry->descriptor_sets[index]; + if (p_potential->set == set_number) { + p_set = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_set) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_set; +} + + +const SpvReflectInterfaceVariable* spvReflectGetInputVariableByLocation( + const SpvReflectShaderModule* p_module, + uint32_t location, + SpvReflectResult* p_result +) +{ + if (location == INVALID_VALUE) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->input_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_module->input_variables[index]; + if (p_potential->location == location) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} +const SpvReflectInterfaceVariable* spvReflectGetInputVariable( + const SpvReflectShaderModule* p_module, + uint32_t location, + SpvReflectResult* p_result +) +{ + return spvReflectGetInputVariableByLocation(p_module, location, p_result); +} + +const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableByLocation( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t location, + SpvReflectResult* p_result +) +{ + if (location == INVALID_VALUE) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + for (uint32_t index = 0; index < p_entry->input_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_entry->input_variables[index]; + if (p_potential->location == location) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} + +const SpvReflectInterfaceVariable* spvReflectGetInputVariableBySemantic( + const SpvReflectShaderModule* p_module, + const char* semantic, + SpvReflectResult* p_result +) +{ + if (IsNull(semantic)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + return NULL; + } + if (semantic[0] == '\0') { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->input_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_module->input_variables[index]; + if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} + +const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableBySemantic( + const SpvReflectShaderModule* p_module, + const char* entry_point, + const char* semantic, + SpvReflectResult* p_result +) +{ + if (IsNull(semantic)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + return NULL; + } + if (semantic[0] == '\0') { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + for (uint32_t index = 0; index < p_entry->input_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_entry->input_variables[index]; + if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} + +const SpvReflectInterfaceVariable* spvReflectGetOutputVariableByLocation( + const SpvReflectShaderModule* p_module, + uint32_t location, + SpvReflectResult* p_result +) +{ + if (location == INVALID_VALUE) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->output_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_module->output_variables[index]; + if (p_potential->location == location) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} +const SpvReflectInterfaceVariable* spvReflectGetOutputVariable( + const SpvReflectShaderModule* p_module, + uint32_t location, + SpvReflectResult* p_result +) +{ + return spvReflectGetOutputVariableByLocation(p_module, location, p_result); +} + +const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableByLocation( + const SpvReflectShaderModule* p_module, + const char* entry_point, + uint32_t location, + SpvReflectResult* p_result +) +{ + if (location == INVALID_VALUE) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + for (uint32_t index = 0; index < p_entry->output_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_entry->output_variables[index]; + if (p_potential->location == location) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} + +const SpvReflectInterfaceVariable* spvReflectGetOutputVariableBySemantic( + const SpvReflectShaderModule* p_module, + const char* semantic, + SpvReflectResult* p_result +) +{ + if (IsNull(semantic)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + return NULL; + } + if (semantic[0] == '\0') { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + for (uint32_t index = 0; index < p_module->output_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_module->output_variables[index]; + if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} + +const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableBySemantic( + const SpvReflectShaderModule* p_module, + const char* entry_point, + const char* semantic, + SpvReflectResult* p_result) +{ + if (IsNull(semantic)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + return NULL; + } + if (semantic[0] == '\0') { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + const SpvReflectInterfaceVariable* p_var = NULL; + if (IsNotNull(p_module)) { + const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + for (uint32_t index = 0; index < p_entry->output_variable_count; ++index) { + const SpvReflectInterfaceVariable* p_potential = &p_entry->output_variables[index]; + if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) { + p_var = p_potential; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_var) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_var; +} + +const SpvReflectBlockVariable* spvReflectGetPushConstantBlock( + const SpvReflectShaderModule* p_module, + uint32_t index, + SpvReflectResult* p_result +) +{ + const SpvReflectBlockVariable* p_push_constant = NULL; + if (IsNotNull(p_module)) { + if (index < p_module->push_constant_block_count) { + p_push_constant = &p_module->push_constant_blocks[index]; + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_push_constant) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_push_constant; +} +const SpvReflectBlockVariable* spvReflectGetPushConstant( + const SpvReflectShaderModule* p_module, + uint32_t index, + SpvReflectResult* p_result +) +{ + return spvReflectGetPushConstantBlock(p_module, index, p_result); +} + +const SpvReflectBlockVariable* spvReflectGetEntryPointPushConstantBlock( + const SpvReflectShaderModule* p_module, + const char* entry_point, + SpvReflectResult* p_result) +{ + const SpvReflectBlockVariable* p_push_constant = NULL; + if (IsNotNull(p_module)) { + const SpvReflectEntryPoint* p_entry = + spvReflectGetEntryPoint(p_module, entry_point); + if (IsNull(p_entry)) { + if (IsNotNull(p_result)) { + *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; + } + return NULL; + } + for (uint32_t i = 0; i < p_module->push_constant_block_count; ++i) { + bool found = SearchSortedUint32( + p_entry->used_push_constants, + p_entry->used_push_constant_count, + p_module->push_constant_blocks[i].spirv_id); + if (found) { + p_push_constant = &p_module->push_constant_blocks[i]; + break; + } + } + } + if (IsNotNull(p_result)) { + *p_result = IsNotNull(p_push_constant) + ? SPV_REFLECT_RESULT_SUCCESS + : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER + : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); + } + return p_push_constant; +} + +SpvReflectResult spvReflectChangeDescriptorBindingNumbers( + SpvReflectShaderModule* p_module, + const SpvReflectDescriptorBinding* p_binding, + uint32_t new_binding_number, + uint32_t new_set_binding +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_binding)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + SpvReflectDescriptorBinding* p_target_descriptor = NULL; + for (uint32_t index = 0; index < p_module->descriptor_binding_count; ++index) { + if(&p_module->descriptor_bindings[index] == p_binding) { + p_target_descriptor = &p_module->descriptor_bindings[index]; + break; + } + } + + if (IsNotNull(p_target_descriptor)) { + if (p_target_descriptor->word_offset.binding > (p_module->_internal->spirv_word_count - 1)) { + return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED; + } + // Binding number + if (new_binding_number != SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE) { + uint32_t* p_code = p_module->_internal->spirv_code + p_target_descriptor->word_offset.binding; + *p_code = new_binding_number; + p_target_descriptor->binding = new_binding_number; + } + // Set number + if (new_set_binding != SPV_REFLECT_SET_NUMBER_DONT_CHANGE) { + uint32_t* p_code = p_module->_internal->spirv_code + p_target_descriptor->word_offset.set; + *p_code = new_set_binding; + p_target_descriptor->set = new_set_binding; + } + } + + SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS; + if (new_set_binding != SPV_REFLECT_SET_NUMBER_DONT_CHANGE) { + result = SynchronizeDescriptorSets(p_module); + } + return result; +} +SpvReflectResult spvReflectChangeDescriptorBindingNumber( + SpvReflectShaderModule* p_module, + const SpvReflectDescriptorBinding* p_descriptor_binding, + uint32_t new_binding_number, + uint32_t optional_new_set_number +) +{ + return spvReflectChangeDescriptorBindingNumbers( + p_module,p_descriptor_binding, + new_binding_number, + optional_new_set_number); +} + +SpvReflectResult spvReflectChangeDescriptorSetNumber( + SpvReflectShaderModule* p_module, + const SpvReflectDescriptorSet* p_set, + uint32_t new_set_number +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_set)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + SpvReflectDescriptorSet* p_target_set = NULL; + for (uint32_t index = 0; index < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++index) { + // The descriptor sets for specific entry points might not be in this set, + // so just match on set index. + if (p_module->descriptor_sets[index].set == p_set->set) { + p_target_set = (SpvReflectDescriptorSet*)p_set; + break; + } + } + + SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS; + if (IsNotNull(p_target_set) && new_set_number != SPV_REFLECT_SET_NUMBER_DONT_CHANGE) { + for (uint32_t index = 0; index < p_target_set->binding_count; ++index) { + SpvReflectDescriptorBinding* p_descriptor = p_target_set->bindings[index]; + if (p_descriptor->word_offset.set > (p_module->_internal->spirv_word_count - 1)) { + return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED; + } + + uint32_t* p_code = p_module->_internal->spirv_code + p_descriptor->word_offset.set; + *p_code = new_set_number; + p_descriptor->set = new_set_number; + } + + result = SynchronizeDescriptorSets(p_module); + } + + return result; +} + +static SpvReflectResult ChangeVariableLocation( + SpvReflectShaderModule* p_module, + SpvReflectInterfaceVariable* p_variable, + uint32_t new_location +) +{ + if (p_variable->word_offset.location > (p_module->_internal->spirv_word_count - 1)) { + return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED; + } + uint32_t* p_code = p_module->_internal->spirv_code + p_variable->word_offset.location; + *p_code = new_location; + p_variable->location = new_location; + return SPV_REFLECT_RESULT_SUCCESS; +} + +SpvReflectResult spvReflectChangeInputVariableLocation( + SpvReflectShaderModule* p_module, + const SpvReflectInterfaceVariable* p_input_variable, + uint32_t new_location +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_input_variable)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + for (uint32_t index = 0; index < p_module->input_variable_count; ++index) { + if(&p_module->input_variables[index] == p_input_variable) { + return ChangeVariableLocation(p_module, &p_module->input_variables[index], new_location); + } + } + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; +} + +SpvReflectResult spvReflectChangeOutputVariableLocation( + SpvReflectShaderModule* p_module, + const SpvReflectInterfaceVariable* p_output_variable, + uint32_t new_location +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_output_variable)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + for (uint32_t index = 0; index < p_module->output_variable_count; ++index) { + if(&p_module->output_variables[index] == p_output_variable) { + return ChangeVariableLocation(p_module, &p_module->output_variables[index], new_location); + } + } + return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND; +} + +const char* spvReflectSourceLanguage(SpvSourceLanguage source_lang) +{ + switch (source_lang) { + case SpvSourceLanguageUnknown : return "Unknown"; + case SpvSourceLanguageESSL : return "ESSL"; + case SpvSourceLanguageGLSL : return "GLSL"; + case SpvSourceLanguageOpenCL_C : return "OpenCL_C"; + case SpvSourceLanguageOpenCL_CPP : return "OpenCL_CPP"; + case SpvSourceLanguageHLSL : return "HLSL"; + + case SpvSourceLanguageMax: + break; + } + return ""; +} |