Graphics reflection for SPIRV 03/316003/17
authorAdam Bialogonski <adam.b@samsung.com>
Tue, 20 Aug 2024 15:29:00 +0000 (16:29 +0100)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 23 Aug 2024 13:13:59 +0000 (14:13 +0100)
Current state:
- Using SPIRV Reflect
- All Graphics::Reflection APIs supported as in GLES
- Preparation done for creating Vulkan descriptor set layouts

Change-Id: I7c4869ff6584759868d360df990b060148912e3c

14 files changed:
build/tizen/profiles/ubuntu-profile.cmake
dali/internal/graphics/file.list
dali/internal/graphics/vulkan-impl/vulkan-program-impl.cpp
dali/internal/graphics/vulkan-impl/vulkan-program-impl.h
dali/internal/graphics/vulkan-impl/vulkan-program.cpp
dali/internal/graphics/vulkan-impl/vulkan-reflection.cpp [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-reflection.h [new file with mode: 0644]
third-party/SPIRV-Reflect/common/output_stream.cpp [new file with mode: 0644]
third-party/SPIRV-Reflect/common/output_stream.h [new file with mode: 0644]
third-party/SPIRV-Reflect/include/spirv/unified1/spirv.h [new file with mode: 0644]
third-party/SPIRV-Reflect/spirv_reflect.c [new file with mode: 0644]
third-party/SPIRV-Reflect/spirv_reflect.cpp [new file with mode: 0644]
third-party/SPIRV-Reflect/spirv_reflect.h [new file with mode: 0644]
third-party/file.list

index 55b0934d90fa9113c5c81141637016a8ae9fa3b1..fec48a4afd2413fe0f2ef630a01b39086cf90993 100644 (file)
@@ -53,6 +53,7 @@ IF( ENABLE_VULKAN )
     ${adaptor_graphics_vulkan_src_files}
     ${adaptor_graphics_vulkan_x11_src_files}
     ${adaptor_imaging_ubuntu_x11_vulkan_src_files}
+    ${adaptor_libraries_spirv_reflect_src_files}
     )
 ELSE()
   SET(SOURCES ${SOURCES}
index 17ee6716cd432d1a70f2df6fc0087ded95eed920..0d4fcf67464b947751fbe1c4aae7a107556d13c5 100644 (file)
@@ -35,6 +35,7 @@ SET( adaptor_graphics_vulkan_src_files
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-program.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-program-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-queue-impl.cpp
+    ${adaptor_graphics_dir}/vulkan-impl/vulkan-reflection.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-render-pass.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-render-pass-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-render-target.cpp
index 87f109f1b8eb5ce63ead9047bc7a3be4342b226f..7d09463b6dbaad2a3cf4d7f97ac34110f32cbad1 100644 (file)
@@ -21,6 +21,7 @@
 #include <dali/integration-api/debug.h>
 #include <dali/internal/graphics/common/shader-parser.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-reflection.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-shader.h>
 
@@ -58,6 +59,8 @@ struct ProgramImpl::Impl
   ProgramCreateInfo         createInfo;
   std::string               name;
   uint32_t                  refCount{0u};
+
+  std::unique_ptr<Vulkan::Reflection> reflection{nullptr};
 };
 
 ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller)
@@ -76,9 +79,16 @@ ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, VulkanGr
       if(!shaderImpl->Compile())
       {
         DALI_LOG_ERROR("SPIRV Compilation failed!\n");
+        success = false;
       }
     }
   }
+
+  if(success)
+  {
+    // Build reflection
+    mImpl->reflection = std::make_unique<Vulkan::Reflection>(*this, controller);
+  }
 }
 
 ProgramImpl::~ProgramImpl() = default;
@@ -190,6 +200,11 @@ uint32_t ProgramImpl::GetRefCount() const
   return mImpl->refCount;
 }
 
+const Vulkan::Reflection& ProgramImpl::GetReflection() const
+{
+  return *mImpl->reflection;
+}
+
 bool ProgramImpl::GetParameter(uint32_t parameterId, void* out)
 {
   return false;
index a651f73faf7f12f43a332342db61fc9fd39c9f9b..6d1bef7797ba3239e8b722c6b562871dacdbb876 100644 (file)
@@ -26,6 +26,8 @@
 
 namespace Dali::Graphics::Vulkan
 {
+class Reflection;
+
 /**
  * @brief Program implementation
  *
@@ -98,6 +100,13 @@ public:
    */
   [[nodiscard]] uint32_t GetRefCount() const;
 
+  /**
+   * @brief Returns reflection
+   *
+   * @return Valid reflection associated with the Program
+   */
+  [[nodiscard]] const Vulkan::Reflection& GetReflection() const;
+
   /**
    * @brief Returns controller
    *
index 7a6e1b43dd848c3af6f70ac528da79dd9782862f..0f50162c774c795d0bf3274aae254f9ee7afd140 100644 (file)
@@ -51,8 +51,7 @@ Program::~Program()
 
 const Vulkan::Reflection& Program::GetReflection() const
 {
-  // TODO: Implement reflection
-  return *reinterpret_cast<Vulkan::Reflection*>(0u);
+  return mProgram->GetReflection();
 }
 
 VulkanGraphicsController& Program::GetController() const
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-reflection.cpp b/dali/internal/graphics/vulkan-impl/vulkan-reflection.cpp
new file mode 100644 (file)
index 0000000..d3a0761
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/graphics/vulkan-impl/vulkan-reflection.h>
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-program-impl.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-shader.h>
+#include <dali/public-api/common/vector-wrapper.h>
+
+// EXTERNAL INCLUDES
+#include <third-party/SPIRV-Reflect/spirv_reflect.h>
+#include <vulkan/vulkan.hpp>
+
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gGraphicsReflectionLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GRAPHICS_REFLECTION");
+#endif
+
+} // namespace
+namespace Dali::Graphics::Vulkan
+{
+inline VertexInputAttributeFormat ToVertexInputAttributeFormat(SpvReflectFormat spvFormat)
+{
+  // only supported types (we need to verify)
+  switch(spvFormat)
+  {
+    case SPV_REFLECT_FORMAT_R32_SINT:
+    {
+      return VertexInputAttributeFormat::INTEGER;
+    }
+    case SPV_REFLECT_FORMAT_R32_SFLOAT:
+    {
+      return VertexInputAttributeFormat::FLOAT;
+    }
+    //case SPV_REFLECT_FORMAT_R32G32_SINT:{
+    //  return VertexInputAttributeFormat::VEC2
+    //}
+    case SPV_REFLECT_FORMAT_R32G32_SFLOAT:
+    {
+      return VertexInputAttributeFormat::VEC2;
+    }
+    //case SPV_REFLECT_FORMAT_R32G32B32_SINT:{}
+    case SPV_REFLECT_FORMAT_R32G32B32_SFLOAT:
+    {
+      return VertexInputAttributeFormat::VEC3;
+    }
+    //case SPV_REFLECT_FORMAT_R32G32B32A32_SINT:{};
+    case SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT:
+    {
+      return VertexInputAttributeFormat::VEC4;
+    }
+    default:
+    {
+      return VertexInputAttributeFormat::UNDEFINED;
+    }
+  }
+}
+
+Reflection::Reflection(Vulkan::ProgramImpl& program, VulkanGraphicsController& controller)
+: Graphics::Reflection(),
+  mController(controller),
+  mProgram(program)
+{
+  BuildReflection();
+}
+
+Reflection::~Reflection() = default;
+
+template<typename FN, typename OUT>
+void SPIRVEnumerate(FN& proc, SpvReflectShaderModule* module, std::vector<OUT*>& out)
+{
+  uint32_t var_count = 0;
+  proc(module, &var_count, nullptr);
+  out.resize(var_count);
+  auto ptr = out.data();
+  proc(module, &var_count, reinterpret_cast<OUT**>(ptr));
+}
+
+void Reflection::BuildReflection()
+{
+  // LIMITATION: only one shader module per stage is allowed. Vulkan supports linking multiple
+  // modules per stage but at this moment we won't support it (because we don't have a use case)
+  bool vertexShaderDone   = false;
+  bool fragmentShaderDone = false;
+  enum class StageCheckResult
+  {
+    INVALID      = -1,
+    ALREADY_DONE = 1,
+    OK           = 0
+  };
+
+  // initialize uniform block data
+  mUniformBlocks.clear();
+  mUniformBlocks.resize(1); // index 0 is standalone block which isn't in use by Vulkan but must be there
+
+  // initialize list of samplers
+  // NOTE: We support only COMBINED_IMAGE_SAMPLER type currently (regular sampler on the GLES side)
+  std::vector<UniformInfo> samplers;
+
+  // build descriptor set layout (currently, we support only one set!)
+  std::vector<vk::DescriptorSetLayoutCreateInfo> dsLayoutInfos;
+
+  for(auto& state : (*mProgram.GetCreateInfo().shaderState))
+  {
+    auto                   impl        = static_cast<const Vulkan::Shader*>(state.shader)->GetImplementation();
+    auto                   spirvBinary = impl->GetCreateInfo().sourceData;
+    auto                   spirvLength = impl->GetCreateInfo().sourceSize;
+    auto                   stage       = state.pipelineStage;
+    SpvReflectShaderModule module;
+    SpvReflectResult       result = spvReflectCreateShaderModule(spirvLength, spirvBinary, &module);
+
+    if(result != SPV_REFLECT_RESULT_SUCCESS)
+    {
+      DALI_LOG_ERROR("Can't reflect SPIRV module! err = %d\n", int(result));
+      continue;
+    }
+
+    // helper lambda if we need to check more types of pipeline stages in the future
+    auto CheckStageIfDone = [stage](auto expectedStage, auto& variable, const char* stageName) -> StageCheckResult {
+      if(stage == expectedStage)
+      {
+        if(!variable)
+        {
+          variable = true;
+        }
+        else
+        {
+          DALI_LOG_ERROR("Can't reflect SPIRV module! Only one module per %s is allowed!\n", stageName);
+          return StageCheckResult::ALREADY_DONE; // return true if stage already done
+        }
+      }
+      else
+      {
+        return StageCheckResult::INVALID; // not stage
+      }
+      return StageCheckResult::OK;
+    };
+
+    // Process only one module per stage
+    if(CheckStageIfDone(PipelineStage::VERTEX_SHADER, vertexShaderDone, "VERTEX_STAGE") == StageCheckResult::ALREADY_DONE)
+    {
+      continue;
+    }
+    if(CheckStageIfDone(PipelineStage::FRAGMENT_SHADER, fragmentShaderDone, "FRAGMENT_STAGE") == StageCheckResult::ALREADY_DONE)
+    {
+      continue;
+    }
+
+    // Process vertex shader attributes
+    if(stage == PipelineStage::VERTEX_SHADER)
+    {
+      BuildVertexAttributeReflection(&module);
+
+      // build vertex input layouts for Vulkan
+    }
+
+    // Prepare descriptor set layout allocation
+    std::vector<SpvReflectDescriptorSet*> dsSets;
+    SPIRVEnumerate(spvReflectEnumerateDescriptorSets, &module, dsSets);
+
+    // Per each descriptor set
+    for(auto& dsSet : dsSets)
+    {
+      if(dsSet->set >= mVkDescriptorSetLayoutCreateInfoList.size())
+      {
+        mVkDescriptorSetLayoutCreateInfoList.resize(dsSet->set + 1);
+        mVkDescriptorSetLayoutBindingList.resize(dsSet->set + 1);
+      }
+
+      // per each set preallocate bindings
+      std::vector<vk::DescriptorSetLayoutBinding> bindings;
+      bindings.resize(dsSet->binding_count);
+      for(auto i = 0u; i < dsSet->binding_count; ++i)
+      {
+        auto& bind = bindings[i];
+        auto& ref  = *dsSet->bindings[i];
+        bind.setBinding(ref.binding);
+        bind.setDescriptorCount(ref.count);
+        bind.setDescriptorType(vk::DescriptorType(ref.descriptor_type));
+        bind.setStageFlags(
+          stage == PipelineStage::VERTEX_SHADER ? vk::ShaderStageFlagBits::eVertex : vk::ShaderStageFlagBits::eFragment);
+      }
+
+      auto& dsSetCreateInfo = mVkDescriptorSetLayoutCreateInfoList[dsSet->set];
+
+      auto& bindingList = mVkDescriptorSetLayoutBindingList[dsSet->set];
+      bindingList.insert(bindingList.end(), bindings.begin(), bindings.end());
+      dsSetCreateInfo.setBindings(bindingList);
+      dsSetCreateInfo.setBindingCount(bindingList.size());
+    }
+
+    // process uniform buffer bindings and samplers (opaque uniforms)
+    std::vector<SpvReflectDescriptorBinding*> dsBindings;
+    SPIRVEnumerate(spvReflectEnumerateDescriptorBindings, &module, dsBindings);
+
+    uint32_t blockIndex = 1u;
+    for([[maybe_unused]] auto& binding : dsBindings)
+    {
+      // process uniform buffer
+      if(binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
+      {
+        auto memberCount = binding->block.member_count;
+        auto members     = binding->block.members;
+        mUniformBlocks.emplace_back();
+        auto& block         = mUniformBlocks.back();
+        block.binding       = binding->binding;
+        block.name          = binding->name;
+        block.descriptorSet = binding->set;
+        block.size          = 0; // to be updated with members
+
+        block.members.resize(memberCount);
+        for(auto i = 0u; i < memberCount; ++i)
+        {
+          auto& out         = block.members[i];
+          auto& memb        = members[i];
+          out.name          = memb.name;
+          out.location      = 0;
+          out.offset        = memb.offset;
+          out.elementStride = memb.array.dims_count ? memb.array.stride : 0;
+          out.elementCount  = memb.array.dims[0]; // will be zero for non-array
+          out.uniformClass  = UniformClass::UNIFORM_BUFFER;
+          out.bufferIndex   = blockIndex++; // TODO: do we need this for Vulkan?
+          block.size += memb.padded_size;
+        }
+      }
+      else if(binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+      {
+        samplers.emplace_back();
+        auto uniformInfo          = &samplers.back();
+        uniformInfo->uniformClass = UniformClass::COMBINED_IMAGE_SAMPLER;
+        uniformInfo->name         = binding->name;
+        uniformInfo->offset       = 0;
+        uniformInfo->location     = 0; // location is in the order of bidings
+        uniformInfo->binding      = binding->binding;
+      }
+    }
+
+    if(!samplers.empty())
+    {
+      mUniformOpaques.insert(samplers.begin(), samplers.end(), mUniformOpaques.end());
+      // sort samplers by bindings
+      std::sort(mUniformOpaques.begin(), mUniformOpaques.end(), [](auto& lhs, auto& rhs) { return lhs.binding < rhs.binding; });
+      for(auto i = 0u; i < mUniformOpaques.size(); ++i)
+      {
+        mUniformOpaques[i].location = i;
+      }
+    }
+
+    spvReflectDestroyShaderModule(&module);
+  }
+}
+
+void Reflection::BuildVertexAttributeReflection(SpvReflectShaderModule* spvModule)
+{
+  std::vector<SpvReflectInterfaceVariable*> attrs;
+  SPIRVEnumerate(spvReflectEnumerateInputVariables, &*spvModule, attrs);
+  mVertexInputAttributes.clear();
+  mVertexInputAttributes.resize(attrs.size());
+  int maximumLocation = int(attrs.size()) - 1;
+  for(auto& attr : attrs)
+  {
+    // SPIRV contains builtin attributes that are added with locations
+    // at the end of 32bit range. To skip it we assume some 'healthy' top range
+    // for locations that we are unlikely to exceed. max of unsigned 16bit number (65535)
+    // should be more than enough.
+    // TODO: consider an usecase we may want to use built-in inputs?
+    if(attr->location > std::numeric_limits<uint16_t>::max())
+    {
+      continue;
+    }
+
+    AttributeInfo attributeInfo;
+    attributeInfo.location = attr->location;
+    attributeInfo.name     = attr->name;
+    attributeInfo.format   = ToVertexInputAttributeFormat(attr->format);
+
+    if(attributeInfo.location >= 0)
+    {
+      if(maximumLocation < int(attributeInfo.location))
+      {
+        maximumLocation = int(attributeInfo.location);
+        // Increase container size s.t. we can use maximumLocation as index.
+        mVertexInputAttributes.resize(maximumLocation + 1u);
+      }
+
+      mVertexInputAttributes[attributeInfo.location] = std::move(attributeInfo);
+    }
+  }
+}
+
+uint32_t Reflection::GetVertexAttributeLocation(const std::string& name) const
+{
+  DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "name : %s\n", name.c_str());
+  for(auto&& attr : mVertexInputAttributes)
+  {
+    if(attr.name == name)
+    {
+      return attr.location;
+    }
+  }
+  return ERROR_ATTRIBUTE_NOT_FOUND;
+}
+
+Dali::Graphics::VertexInputAttributeFormat Reflection::GetVertexAttributeFormat(uint32_t location) const
+{
+  DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
+  if(location >= mVertexInputAttributes.size())
+  {
+    return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
+  }
+
+  return mVertexInputAttributes[location].format;
+}
+
+std::string Reflection::GetVertexAttributeName(uint32_t location) const
+{
+  DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
+  if(location >= mVertexInputAttributes.size())
+  {
+    return {};
+  }
+
+  return mVertexInputAttributes[location].name;
+}
+
+std::vector<uint32_t> Reflection::GetVertexAttributeLocations() const
+{
+  std::vector<uint32_t> locations;
+  for(auto&& attr : mVertexInputAttributes)
+  {
+    if(attr.format != Dali::Graphics::VertexInputAttributeFormat::UNDEFINED)
+    {
+      locations.push_back(attr.location);
+    }
+  }
+
+  return locations;
+}
+
+uint32_t Reflection::GetUniformBlockCount() const
+{
+  return mUniformBlocks.size();
+}
+
+uint32_t Reflection::GetUniformBlockBinding(uint32_t index) const
+{
+  return index < mUniformBlocks.size() ? mUniformBlocks[index].binding : 0u;
+}
+
+uint32_t Reflection::GetUniformBlockSize(uint32_t index) const
+{
+  return index < mUniformBlocks.size() ? mUniformBlocks[index].size : 0u;
+}
+
+bool Reflection::GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const
+{
+  if(index >= mUniformBlocks.size())
+  {
+    return false;
+  }
+
+  const auto& block = mUniformBlocks[index];
+
+  out.name          = block.name;
+  out.binding       = block.binding;
+  out.descriptorSet = block.descriptorSet;
+  auto membersSize  = block.members.size();
+  out.members.resize(membersSize);
+  out.size = block.size;
+  for(auto i = 0u; i < out.members.size(); ++i)
+  {
+    const auto& memberUniform    = block.members[i];
+    out.members[i].name          = memberUniform.name;
+    out.members[i].binding       = block.binding;
+    out.members[i].uniformClass  = Graphics::UniformClass::UNIFORM;
+    out.members[i].offset        = memberUniform.offset;
+    out.members[i].location      = memberUniform.location;
+    out.members[i].elementCount  = memberUniform.elementCount;
+    out.members[i].elementStride = memberUniform.elementStride;
+  }
+
+  return true;
+}
+
+std::vector<uint32_t> Reflection::GetUniformBlockLocations() const
+{
+  std::vector<uint32_t> retval{};
+  for(auto&& ubo : mUniformBlocks)
+  {
+    retval.emplace_back(ubo.binding);
+  }
+  return retval;
+}
+
+std::string Reflection::GetUniformBlockName(uint32_t blockIndex) const
+{
+  if(blockIndex < mUniformBlocks.size())
+  {
+    return mUniformBlocks[blockIndex].name;
+  }
+  else
+  {
+    return {};
+  }
+}
+
+uint32_t Reflection::GetUniformBlockMemberCount(uint32_t blockIndex) const
+{
+  if(blockIndex < mUniformBlocks.size())
+  {
+    return static_cast<uint32_t>(mUniformBlocks[blockIndex].members.size());
+  }
+  else
+  {
+    return 0u;
+  }
+}
+
+std::string Reflection::GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const
+{
+  if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
+  {
+    return mUniformBlocks[blockIndex].members[memberLocation].name;
+  }
+  else
+  {
+    return {};
+  }
+}
+
+uint32_t Reflection::GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const
+{
+  if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
+  {
+    return mUniformBlocks[blockIndex].members[memberLocation].offset;
+  }
+  else
+  {
+    return 0u;
+  }
+}
+
+bool Reflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
+{
+  auto index = 0u;
+  for(auto&& ubo : mUniformBlocks)
+  {
+    for(auto&& member : ubo.members)
+    {
+      if(name == member.name || name == (ubo.name + "." + member.name))
+      {
+        out.name         = name;
+        out.location     = member.location;
+        out.binding      = ubo.binding;
+        out.bufferIndex  = index;
+        out.offset       = member.offset;
+        out.uniformClass = Graphics::UniformClass::UNIFORM;
+        return true;
+      }
+    }
+    ++index;
+  }
+
+  // check samplers
+  index = 0u;
+  for(auto&& uniform : mUniformOpaques)
+  {
+    if(uniform.name == name)
+    {
+      out.uniformClass = Graphics::UniformClass::COMBINED_IMAGE_SAMPLER;
+      out.binding      = 0;
+      out.name         = name;
+      out.offset       = index;            // lexical location in shader
+      out.location     = uniform.location; // uniform location mapping
+      return true;
+    }
+    ++index;
+  }
+
+  return false;
+}
+
+const std::vector<Dali::Graphics::UniformInfo>& Reflection::GetSamplers() const
+{
+  return mUniformOpaques;
+}
+
+Graphics::ShaderLanguage Reflection::GetLanguage() const
+{
+  return ShaderLanguage::SPIRV_1_0;
+}
+
+} // namespace Dali::Graphics::Vulkan
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-reflection.h b/dali/internal/graphics/vulkan-impl/vulkan-reflection.h
new file mode 100644 (file)
index 0000000..a562695
--- /dev/null
@@ -0,0 +1,234 @@
+#ifndef DALI_GRAPHICS_VULKAN_REFLECTION_H
+#define DALI_GRAPHICS_VULKAN_REFLECTION_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/graphics-api/graphics-reflection.h>
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-types.h>
+
+// EXTERNAL_INCLUDES
+#include <unordered_set>
+#include <vulkan/vulkan.hpp>
+
+struct SpvReflectShaderModule;
+namespace Dali::Graphics
+{
+namespace Vulkan
+{
+class ProgramImpl;
+constexpr uint32_t ERROR_ATTRIBUTE_NOT_FOUND(-1u);
+class VulkanGraphicsController;
+
+/**
+ * Reflection object represents a single full graphics reflection state.
+ *
+ * The state involves compiled and linked shaders as well as state parameters
+ * like blending, stencil, scissors, viewport etc.
+ *
+ * Some of the parameters can be modified by issuing commands but
+ * the Reflection must mark those states
+ * as dynamic.
+ *
+ */
+class Reflection : public Dali::Graphics::Reflection
+{
+public:
+  explicit Reflection(Vulkan::ProgramImpl& program, VulkanGraphicsController& controller);
+
+  ~Reflection() override;
+
+  // not copyable
+  Reflection(const Reflection&) = delete;
+  Reflection& operator=(const Reflection&) = delete;
+
+  /**
+   * @brief Gets the location of a vertex attribute.
+   *
+   * @param [in] name The name of vertex attribute
+   * @return The index of the vertex attribute in the shader
+   */
+  [[nodiscard]] uint32_t GetVertexAttributeLocation(const std::string& name) const override;
+
+  /**
+   * @brief Gets the format of a vertex attribute.
+   *
+   * @param [in] location The location of vertex attribute
+   * @return The format of a vertex attribute
+   */
+  [[nodiscard]] Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override;
+
+  /**
+   * @brief Gets the name of a vertex attribute.
+   *
+   * @param [in] location The location of vertex attribute
+   * @return The name of the vertex attribute
+   */
+  [[nodiscard]] std::string GetVertexAttributeName(uint32_t location) const override;
+
+  /**
+   * @brief Gets the locations of all the vertex attribute in the shader.
+   *
+   * @return A vector of the locations of all the vertex attributes in the shader
+   */
+  [[nodiscard]] std::vector<uint32_t> GetVertexAttributeLocations() const override;
+
+  // Uniform blocks
+
+  /**
+   * @brief Gets the number of uniform blocks in the shader
+   *
+   * @return The number of uniform blocks
+   */
+  [[nodiscard]] uint32_t GetUniformBlockCount() const override;
+
+  /**
+   * @brief Gets the binding point to which the uniform block with the given index is binded.
+   *
+   * @param [in] index The index of the uniform block
+   * @return The binding point
+   */
+  [[nodiscard]] uint32_t GetUniformBlockBinding(uint32_t index) const override;
+
+  /**
+   * @brief Gets the size of the uniform block with the given index.
+   *
+   * @param [in] index The index of the uniform block
+   * @return The size of the uniform block
+   */
+  [[nodiscard]] uint32_t GetUniformBlockSize(uint32_t index) const override;
+
+  /**
+   * @brief Retrieves the information of the uniform block with the given index.
+   *
+   * The information includes the name, binding point, size, uniforms inside the uniform block, etc.
+   *
+   * @param [in] index The index of the uniform block
+   * @param [out] out A structure that contains the information of the uniform block
+   * @return Whether the uniform block exists or not
+   */
+  bool GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const override;
+
+  /**
+   * @brief Gets the binding points of all the uniform blocks in the shader.
+   *
+   * @return A vector of binding points
+   */
+  [[nodiscard]] std::vector<uint32_t> GetUniformBlockLocations() const override;
+
+  /**
+   * @brief Gets the name of uniform block with the given index.
+   *
+   * @param [in] blockIndex The index of the uniform block
+   * @return The name of the uniform block
+   */
+  [[nodiscard]] std::string GetUniformBlockName(uint32_t blockIndex) const override;
+
+  /**
+   * @brief Gets the number of uniforms in the uniform block with the given index.
+   *
+   * @param [in] blockIndex The index of the uniform block
+   * @return The number of uniforms in the uniform block
+   */
+  [[nodiscard]] uint32_t GetUniformBlockMemberCount(uint32_t blockIndex) const override;
+
+  /**
+   * @brief Gets the name of the uniform in the given location within the uniform block.
+   *
+   * @param [in] blockIndex The index of the uniform block
+   * @param [in] memberLocation The location of the uniform within the uniform block
+   * @return The name of the uniform
+   */
+  [[nodiscard]] std::string GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const override;
+
+  /**
+   * @brief Gets the byte offset of the uniform in the given location within the uniform block.
+   *
+   * @param [in] blockIndex The index of the uniform block
+   * @param [in] memberLocation The location of the uniform within the uniform block
+   * @return The byte offset of the uniform
+   */
+  [[nodiscard]] uint32_t GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const override;
+
+  // Named uniforms
+
+  /**
+   * @brief Gets the information of the uniform by its name.
+   *
+   * @param [in] name The name of the uniform
+   * @param [out] out The information of the uniform
+   * @return Whether the uniform exists or not
+   */
+  [[nodiscard]] bool GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const override;
+
+  // Sampler
+
+  /**
+   * @brief Gets all the sampler uniforms
+   *
+   * @return A vector of the sampler uniforms
+   */
+  [[nodiscard]] const std::vector<Dali::Graphics::UniformInfo>& GetSamplers() const override;
+
+  // Language
+
+  /**
+   * @brief Retrieves the language of the shader
+   *
+   * @return The language of the shader
+   */
+  [[nodiscard]] Graphics::ShaderLanguage GetLanguage() const override;
+
+public:
+  /**
+   * @brief Build the reflection of vertex attributes
+   */
+  void BuildVertexAttributeReflection(SpvReflectShaderModule* spvModule);
+
+  void BuildReflection();
+
+protected:
+  Reflection(Reflection&&) = default;
+  Reflection& operator=(Reflection&&) = default;
+
+private:
+  VulkanGraphicsController& mController; ///< The Graphics controller
+  Vulkan::ProgramImpl&      mProgram;    ///< The Program object
+
+  struct AttributeInfo
+  {
+    uint32_t                                   location{ERROR_ATTRIBUTE_NOT_FOUND};
+    std::string                                name{};
+    Dali::Graphics::VertexInputAttributeFormat format{};
+  };
+
+  std::vector<AttributeInfo>              mVertexInputAttributes; ///< List of vertex attributes
+  std::vector<Graphics::UniformInfo>      mUniformOpaques{};      ///< List of opaque uniforms (i.e. samplers)
+  std::vector<Graphics::UniformBlockInfo> mUniformBlocks{};       ///< List of uniform blocks (First is for standalone uniforms)
+
+  // Vulkan data
+  std::vector<vk::DescriptorSetLayoutCreateInfo>           mVkDescriptorSetLayoutCreateInfoList; ///< List of DSlayout create structures
+  std::vector<std::vector<vk::DescriptorSetLayoutBinding>> mVkDescriptorSetLayoutBindingList;
+};
+
+} // namespace Vulkan
+} // namespace Dali::Graphics
+
+#endif // DALI_GRAPHICS_VULKAN_REFLECTION_H
diff --git a/third-party/SPIRV-Reflect/common/output_stream.cpp b/third-party/SPIRV-Reflect/common/output_stream.cpp
new file mode 100644 (file)
index 0000000..f486955
--- /dev/null
@@ -0,0 +1,2321 @@
+#include "output_stream.h"
+
+#include <algorithm>
+#include <cassert>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+enum TextLineType {
+  TEXT_LINE_TYPE_BLOCK_BEGIN = 0x01,
+  TEXT_LINE_TYPE_BLOCK_END = 0x02,
+  TEXT_LINE_TYPE_STRUCT_BEGIN = 0x04,
+  TEXT_LINE_TYPE_STRUCT_END = 0x08,
+  TEXT_LINE_TYPE_REF_BEGIN = 0x10,
+  TEXT_LINE_TYPE_REF_END = 0x20,
+  TEXT_LINE_TYPE_LINES = 0x40,
+};
+
+struct TextLine {
+  std::string indent;
+  std::string modifier;
+  std::string type_name;
+  std::string name;
+  uint32_t absolute_offset;
+  uint32_t relative_offset;
+  uint32_t size;
+  uint32_t padded_size;
+  uint32_t array_stride;
+  uint32_t block_variable_flags;
+  std::vector<uint32_t> array_dims;
+  // Text Data
+  uint32_t text_line_flags;
+  std::vector<TextLine> lines;
+  std::string formatted_line;
+  std::string formatted_absolute_offset;
+  std::string formatted_relative_offset;
+  std::string formatted_size;
+  std::string formatted_padded_size;
+  std::string formatted_array_stride;
+  std::string formatted_block_variable_flags;
+};
+
+static std::string AsHexString(uint32_t n) {
+  // std::iomanip can die in a fire.
+  char out_word[11];
+  int len = snprintf(out_word, 11, "0x%08X", n);
+  assert(len == 10);
+  (void)len;
+  return std::string(out_word);
+}
+
+std::string ToStringGenerator(SpvReflectGenerator generator) {
+  switch (generator) {
+    case SPV_REFLECT_GENERATOR_KHRONOS_LLVM_SPIRV_TRANSLATOR:
+      return "Khronos LLVM/SPIR-V Translator";
+      break;
+    case SPV_REFLECT_GENERATOR_KHRONOS_SPIRV_TOOLS_ASSEMBLER:
+      return "Khronos SPIR-V Tools Assembler";
+      break;
+    case SPV_REFLECT_GENERATOR_KHRONOS_GLSLANG_REFERENCE_FRONT_END:
+      return "Khronos Glslang Reference Front End";
+      break;
+    case SPV_REFLECT_GENERATOR_GOOGLE_SHADERC_OVER_GLSLANG:
+      return "Google Shaderc over Glslang";
+      break;
+    case SPV_REFLECT_GENERATOR_GOOGLE_SPIREGG:
+      return "Google spiregg";
+      break;
+    case SPV_REFLECT_GENERATOR_GOOGLE_RSPIRV:
+      return "Google rspirv";
+      break;
+    case SPV_REFLECT_GENERATOR_X_LEGEND_MESA_MESAIR_SPIRV_TRANSLATOR:
+      return "X-LEGEND Mesa-IR/SPIR-V Translator";
+      break;
+    case SPV_REFLECT_GENERATOR_KHRONOS_SPIRV_TOOLS_LINKER:
+      return "Khronos SPIR-V Tools Linker";
+      break;
+    case SPV_REFLECT_GENERATOR_WINE_VKD3D_SHADER_COMPILER:
+      return "Wine VKD3D Shader Compiler";
+      break;
+    case SPV_REFLECT_GENERATOR_CLAY_CLAY_SHADER_COMPILER:
+      return "Clay Clay Shader Compiler";
+      break;
+  }
+  // unhandled SpvReflectGenerator enum value
+  return "???";
+}
+
+std::string ToStringSpvSourceLanguage(SpvSourceLanguage lang) {
+  switch (lang) {
+    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 SpvSourceLanguageCPP_for_OpenCL:
+      return "CPP_for_OpenCL";
+    case SpvSourceLanguageSYCL:
+      return "SYCL";
+    case SpvSourceLanguageHERO_C:
+      return "Hero C";
+    case SpvSourceLanguageNZSL:
+      return "NZSL";
+
+    default:
+      break;
+  }
+  // SpvSourceLanguageUnknown, SpvSourceLanguageMax, or another value that does
+  // not correspond to a known language.
+  return "Unknown";
+}
+
+std::string ToStringSpvExecutionModel(SpvExecutionModel model) {
+  switch (model) {
+    case SpvExecutionModelVertex:
+      return "Vertex";
+    case SpvExecutionModelTessellationControl:
+      return "TessellationControl";
+    case SpvExecutionModelTessellationEvaluation:
+      return "TessellationEvaluation";
+    case SpvExecutionModelGeometry:
+      return "Geometry";
+    case SpvExecutionModelFragment:
+      return "Fragment";
+    case SpvExecutionModelGLCompute:
+      return "GLCompute";
+    case SpvExecutionModelKernel:
+      return "Kernel";
+    case SpvExecutionModelTaskNV:
+      return "TaskNV";
+    case SpvExecutionModelMeshNV:
+      return "MeshNV";
+    case SpvExecutionModelTaskEXT:
+      return "TaskEXT";
+    case SpvExecutionModelMeshEXT:
+      return "MeshEXT";
+    case SpvExecutionModelRayGenerationKHR:
+      return "RayGenerationKHR";
+    case SpvExecutionModelIntersectionKHR:
+      return "IntersectionKHR";
+    case SpvExecutionModelAnyHitKHR:
+      return "AnyHitKHR";
+    case SpvExecutionModelClosestHitKHR:
+      return "ClosestHitKHR";
+    case SpvExecutionModelMissKHR:
+      return "MissKHR";
+    case SpvExecutionModelCallableKHR:
+      return "CallableKHR";
+
+    case SpvExecutionModelMax:
+      break;
+
+    default:
+      break;
+  }
+
+  // unhandled SpvExecutionModel enum value
+  return "???";
+}
+
+std::string ToStringShaderStage(SpvReflectShaderStageFlagBits stage) {
+  switch (stage) {
+    case SPV_REFLECT_SHADER_STAGE_VERTEX_BIT:
+      return "VS";
+    case SPV_REFLECT_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
+      return "HS";
+    case SPV_REFLECT_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
+      return "DS";
+    case SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT:
+      return "GS";
+    case SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT:
+      return "PS";
+    case SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT:
+      return "CS";
+    case SPV_REFLECT_SHADER_STAGE_TASK_BIT_NV:
+      return "TASK";
+    case SPV_REFLECT_SHADER_STAGE_MESH_BIT_NV:
+      return "MESH";
+    case SPV_REFLECT_SHADER_STAGE_RAYGEN_BIT_KHR:
+      return "RAYGEN";
+    case SPV_REFLECT_SHADER_STAGE_ANY_HIT_BIT_KHR:
+      return "ANY_HIT";
+    case SPV_REFLECT_SHADER_STAGE_CLOSEST_HIT_BIT_KHR:
+      return "CLOSEST_HIT";
+    case SPV_REFLECT_SHADER_STAGE_MISS_BIT_KHR:
+      return "MISS";
+    case SPV_REFLECT_SHADER_STAGE_INTERSECTION_BIT_KHR:
+      return "INTERSECTION";
+    case SPV_REFLECT_SHADER_STAGE_CALLABLE_BIT_KHR:
+      return "CALLABLE";
+
+    default:
+      break;
+  }
+
+  // Unhandled SpvReflectShaderStageFlagBits enum value
+  return "???";
+}
+
+std::string ToStringSpvStorageClass(SpvStorageClass storage_class) {
+  switch (storage_class) {
+    case SpvStorageClassUniformConstant:
+      return "UniformConstant";
+    case SpvStorageClassInput:
+      return "Input";
+    case SpvStorageClassUniform:
+      return "Uniform";
+    case SpvStorageClassOutput:
+      return "Output";
+    case SpvStorageClassWorkgroup:
+      return "Workgroup";
+    case SpvStorageClassCrossWorkgroup:
+      return "CrossWorkgroup";
+    case SpvStorageClassPrivate:
+      return "Private";
+    case SpvStorageClassFunction:
+      return "Function";
+    case SpvStorageClassGeneric:
+      return "Generic";
+    case SpvStorageClassPushConstant:
+      return "PushConstant";
+    case SpvStorageClassAtomicCounter:
+      return "AtomicCounter";
+    case SpvStorageClassImage:
+      return "Image";
+    case SpvStorageClassStorageBuffer:
+      return "StorageBuffer";
+    case SpvStorageClassCallableDataKHR:
+      return "CallableDataKHR";
+    case SpvStorageClassIncomingCallableDataKHR:
+      return "IncomingCallableDataKHR";
+    case SpvStorageClassRayPayloadKHR:
+      return "RayPayloadKHR";
+    case SpvStorageClassHitAttributeKHR:
+      return "HitAttributeKHR";
+    case SpvStorageClassIncomingRayPayloadKHR:
+      return "IncomingRayPayloadKHR";
+    case SpvStorageClassShaderRecordBufferKHR:
+      return "ShaderRecordBufferKHR";
+    case SpvStorageClassPhysicalStorageBuffer:
+      return "PhysicalStorageBuffer";
+    case SpvStorageClassCodeSectionINTEL:
+      return "CodeSectionINTEL";
+    case SpvStorageClassDeviceOnlyINTEL:
+      return "DeviceOnlyINTEL";
+    case SpvStorageClassHostOnlyINTEL:
+      return "HostOnlyINTEL";
+    case SpvStorageClassMax:
+      break;
+
+    default:
+      break;
+  }
+
+  // Special case: this specific "unhandled" value does actually seem to show
+  // up.
+  if (storage_class == (SpvStorageClass)-1) {
+    return "NOT APPLICABLE";
+  }
+
+  // unhandled SpvStorageClass enum value
+  return "???";
+}
+
+std::string ToStringSpvDim(SpvDim dim) {
+  switch (dim) {
+    case SpvDim1D:
+      return "1D";
+    case SpvDim2D:
+      return "2D";
+    case SpvDim3D:
+      return "3D";
+    case SpvDimCube:
+      return "Cube";
+    case SpvDimRect:
+      return "Rect";
+    case SpvDimBuffer:
+      return "Buffer";
+    case SpvDimSubpassData:
+      return "SubpassData";
+    case SpvDimTileImageDataEXT:
+      return "DimTileImageDataEXT";
+
+    case SpvDimMax:
+      break;
+  }
+  // unhandled SpvDim enum value
+  return "???";
+}
+
+std::string ToStringResourceType(SpvReflectResourceType res_type) {
+  switch (res_type) {
+    case SPV_REFLECT_RESOURCE_FLAG_UNDEFINED:
+      return "UNDEFINED";
+    case SPV_REFLECT_RESOURCE_FLAG_SAMPLER:
+      return "SAMPLER";
+    case SPV_REFLECT_RESOURCE_FLAG_CBV:
+      return "CBV";
+    case SPV_REFLECT_RESOURCE_FLAG_SRV:
+      return "SRV";
+    case SPV_REFLECT_RESOURCE_FLAG_UAV:
+      return "UAV";
+  }
+  // unhandled SpvReflectResourceType enum value
+  return "???";
+}
+
+std::string ToStringDescriptorType(SpvReflectDescriptorType value) {
+  switch (value) {
+    case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER:
+      return "VK_DESCRIPTOR_TYPE_SAMPLER";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+      return "VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+      return "VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+      return "VK_DESCRIPTOR_TYPE_STORAGE_IMAGE";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+      return "VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+      return "VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+      return "VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+      return "VK_DESCRIPTOR_TYPE_STORAGE_BUFFER";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+      return "VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+      return "VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+      return "VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT";
+    case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
+      return "VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR";
+  }
+  // unhandled SpvReflectDescriptorType enum value
+  return "VK_DESCRIPTOR_TYPE_???";
+}
+
+static std::string ToStringSpvBuiltIn(SpvBuiltIn built_in) {
+  switch (built_in) {
+    case SpvBuiltInPosition:
+      return "Position";
+    case SpvBuiltInPointSize:
+      return "PointSize";
+    case SpvBuiltInClipDistance:
+      return "ClipDistance";
+    case SpvBuiltInCullDistance:
+      return "CullDistance";
+    case SpvBuiltInVertexId:
+      return "VertexId";
+    case SpvBuiltInInstanceId:
+      return "InstanceId";
+    case SpvBuiltInPrimitiveId:
+      return "PrimitiveId";
+    case SpvBuiltInInvocationId:
+      return "InvocationId";
+    case SpvBuiltInLayer:
+      return "Layer";
+    case SpvBuiltInViewportIndex:
+      return "ViewportIndex";
+    case SpvBuiltInTessLevelOuter:
+      return "TessLevelOuter";
+    case SpvBuiltInTessLevelInner:
+      return "TessLevelInner";
+    case SpvBuiltInTessCoord:
+      return "TessCoord";
+    case SpvBuiltInPatchVertices:
+      return "PatchVertices";
+    case SpvBuiltInFragCoord:
+      return "FragCoord";
+    case SpvBuiltInPointCoord:
+      return "PointCoord";
+    case SpvBuiltInFrontFacing:
+      return "FrontFacing";
+    case SpvBuiltInSampleId:
+      return "SampleId";
+    case SpvBuiltInSamplePosition:
+      return "SamplePosition";
+    case SpvBuiltInSampleMask:
+      return "SampleMask";
+    case SpvBuiltInFragDepth:
+      return "FragDepth";
+    case SpvBuiltInHelperInvocation:
+      return "HelperInvocation";
+    case SpvBuiltInNumWorkgroups:
+      return "NumWorkgroups";
+    case SpvBuiltInWorkgroupSize:
+      return "WorkgroupSize";
+    case SpvBuiltInWorkgroupId:
+      return "WorkgroupId";
+    case SpvBuiltInLocalInvocationId:
+      return "LocalInvocationId";
+    case SpvBuiltInGlobalInvocationId:
+      return "GlobalInvocationId";
+    case SpvBuiltInLocalInvocationIndex:
+      return "LocalInvocationIndex";
+    case SpvBuiltInWorkDim:
+      return "WorkDim";
+    case SpvBuiltInGlobalSize:
+      return "GlobalSize";
+    case SpvBuiltInEnqueuedWorkgroupSize:
+      return "EnqueuedWorkgroupSize";
+    case SpvBuiltInGlobalOffset:
+      return "GlobalOffset";
+    case SpvBuiltInGlobalLinearId:
+      return "GlobalLinearId";
+    case SpvBuiltInSubgroupSize:
+      return "SubgroupSize";
+    case SpvBuiltInSubgroupMaxSize:
+      return "SubgroupMaxSize";
+    case SpvBuiltInNumSubgroups:
+      return "NumSubgroups";
+    case SpvBuiltInNumEnqueuedSubgroups:
+      return "NumEnqueuedSubgroups";
+    case SpvBuiltInSubgroupId:
+      return "SubgroupId";
+    case SpvBuiltInSubgroupLocalInvocationId:
+      return "SubgroupLocalInvocationId";
+    case SpvBuiltInVertexIndex:
+      return "VertexIndex";
+    case SpvBuiltInInstanceIndex:
+      return "InstanceIndex";
+    case SpvBuiltInSubgroupEqMaskKHR:
+      return "SubgroupEqMaskKHR";
+    case SpvBuiltInSubgroupGeMaskKHR:
+      return "SubgroupGeMaskKHR";
+    case SpvBuiltInSubgroupGtMaskKHR:
+      return "SubgroupGtMaskKHR";
+    case SpvBuiltInSubgroupLeMaskKHR:
+      return "SubgroupLeMaskKHR";
+    case SpvBuiltInSubgroupLtMaskKHR:
+      return "SubgroupLtMaskKHR";
+    case SpvBuiltInBaseVertex:
+      return "BaseVertex";
+    case SpvBuiltInBaseInstance:
+      return "BaseInstance";
+    case SpvBuiltInDrawIndex:
+      return "DrawIndex";
+    case SpvBuiltInDeviceIndex:
+      return "DeviceIndex";
+    case SpvBuiltInViewIndex:
+      return "ViewIndex";
+    case SpvBuiltInBaryCoordNoPerspAMD:
+      return "BaryCoordNoPerspAMD";
+    case SpvBuiltInBaryCoordNoPerspCentroidAMD:
+      return "BaryCoordNoPerspCentroidAMD";
+    case SpvBuiltInBaryCoordNoPerspSampleAMD:
+      return "BaryCoordNoPerspSampleAMD";
+    case SpvBuiltInBaryCoordSmoothAMD:
+      return "BaryCoordSmoothAMD";
+    case SpvBuiltInBaryCoordSmoothCentroidAMD:
+      return "BaryCoordSmoothCentroidAMD";
+    case SpvBuiltInBaryCoordSmoothSampleAMD:
+      return "BaryCoordSmoothSampleAMD";
+    case SpvBuiltInBaryCoordPullModelAMD:
+      return "BaryCoordPullModelAMD";
+    case SpvBuiltInFragStencilRefEXT:
+      return "FragStencilRefEXT";
+    case SpvBuiltInViewportMaskNV:
+      return "ViewportMaskNV";
+    case SpvBuiltInSecondaryPositionNV:
+      return "SecondaryPositionNV";
+    case SpvBuiltInSecondaryViewportMaskNV:
+      return "SecondaryViewportMaskNV";
+    case SpvBuiltInPositionPerViewNV:
+      return "PositionPerViewNV";
+    case SpvBuiltInViewportMaskPerViewNV:
+      return "ViewportMaskPerViewNV";
+    case SpvBuiltInLaunchIdKHR:
+      return "InLaunchIdKHR";
+    case SpvBuiltInLaunchSizeKHR:
+      return "InLaunchSizeKHR";
+    case SpvBuiltInWorldRayOriginKHR:
+      return "InWorldRayOriginKHR";
+    case SpvBuiltInWorldRayDirectionKHR:
+      return "InWorldRayDirectionKHR";
+    case SpvBuiltInObjectRayOriginKHR:
+      return "InObjectRayOriginKHR";
+    case SpvBuiltInObjectRayDirectionKHR:
+      return "InObjectRayDirectionKHR";
+    case SpvBuiltInRayTminKHR:
+      return "InRayTminKHR";
+    case SpvBuiltInRayTmaxKHR:
+      return "InRayTmaxKHR";
+    case SpvBuiltInInstanceCustomIndexKHR:
+      return "InInstanceCustomIndexKHR";
+    case SpvBuiltInObjectToWorldKHR:
+      return "InObjectToWorldKHR";
+    case SpvBuiltInWorldToObjectKHR:
+      return "InWorldToObjectKHR";
+    case SpvBuiltInHitTNV:
+      return "InHitTNV";
+    case SpvBuiltInHitKindKHR:
+      return "InHitKindKHR";
+    case SpvBuiltInIncomingRayFlagsKHR:
+      return "InIncomingRayFlagsKHR";
+    case SpvBuiltInRayGeometryIndexKHR:
+      return "InRayGeometryIndexKHR";
+
+    case SpvBuiltInMax:
+    default:
+      break;
+  }
+  // unhandled SpvBuiltIn enum value
+  std::stringstream ss;
+  ss << "??? (" << built_in << ")";
+  return ss.str();
+}
+
+std::string ToStringSpvBuiltIn(const SpvReflectInterfaceVariable& variable, bool preface) {
+  std::stringstream ss;
+  if (variable.decoration_flags & SPV_REFLECT_DECORATION_BLOCK) {
+    if (preface) {
+      ss << "(built-in block) ";
+    }
+    ss << "[";
+    for (uint32_t i = 0; i < variable.member_count; i++) {
+      ss << ToStringSpvBuiltIn(variable.members[i].built_in);
+      if (i < (variable.member_count - 1)) {
+        ss << ", ";
+      }
+    }
+    ss << "]";
+  } else {
+    if (preface) {
+      ss << "(built-in) ";
+    }
+    ss << ToStringSpvBuiltIn(variable.built_in);
+  }
+  return ss.str();
+}
+
+std::string ToStringSpvImageFormat(SpvImageFormat fmt) {
+  switch (fmt) {
+    case SpvImageFormatUnknown:
+      return "Unknown";
+    case SpvImageFormatRgba32f:
+      return "Rgba32f";
+    case SpvImageFormatRgba16f:
+      return "Rgba16f";
+    case SpvImageFormatR32f:
+      return "R32f";
+    case SpvImageFormatRgba8:
+      return "Rgba8";
+    case SpvImageFormatRgba8Snorm:
+      return "Rgba8Snorm";
+    case SpvImageFormatRg32f:
+      return "Rg32f";
+    case SpvImageFormatRg16f:
+      return "Rg16f";
+    case SpvImageFormatR11fG11fB10f:
+      return "R11fG11fB10f";
+    case SpvImageFormatR16f:
+      return "R16f";
+    case SpvImageFormatRgba16:
+      return "Rgba16";
+    case SpvImageFormatRgb10A2:
+      return "Rgb10A2";
+    case SpvImageFormatRg16:
+      return "Rg16";
+    case SpvImageFormatRg8:
+      return "Rg8";
+    case SpvImageFormatR16:
+      return "R16";
+    case SpvImageFormatR8:
+      return "R8";
+    case SpvImageFormatRgba16Snorm:
+      return "Rgba16Snorm";
+    case SpvImageFormatRg16Snorm:
+      return "Rg16Snorm";
+    case SpvImageFormatRg8Snorm:
+      return "Rg8Snorm";
+    case SpvImageFormatR16Snorm:
+      return "R16Snorm";
+    case SpvImageFormatR8Snorm:
+      return "R8Snorm";
+    case SpvImageFormatRgba32i:
+      return "Rgba32i";
+    case SpvImageFormatRgba16i:
+      return "Rgba16i";
+    case SpvImageFormatRgba8i:
+      return "Rgba8i";
+    case SpvImageFormatR32i:
+      return "R32i";
+    case SpvImageFormatRg32i:
+      return "Rg32i";
+    case SpvImageFormatRg16i:
+      return "Rg16i";
+    case SpvImageFormatRg8i:
+      return "Rg8i";
+    case SpvImageFormatR16i:
+      return "R16i";
+    case SpvImageFormatR8i:
+      return "R8i";
+    case SpvImageFormatRgba32ui:
+      return "Rgba32ui";
+    case SpvImageFormatRgba16ui:
+      return "Rgba16ui";
+    case SpvImageFormatRgba8ui:
+      return "Rgba8ui";
+    case SpvImageFormatR32ui:
+      return "R32ui";
+    case SpvImageFormatRgb10a2ui:
+      return "Rgb10a2ui";
+    case SpvImageFormatRg32ui:
+      return "Rg32ui";
+    case SpvImageFormatRg16ui:
+      return "Rg16ui";
+    case SpvImageFormatRg8ui:
+      return "Rg8ui";
+    case SpvImageFormatR16ui:
+      return "R16ui";
+    case SpvImageFormatR8ui:
+      return "R8ui";
+    case SpvImageFormatR64ui:
+      return "R64ui";
+    case SpvImageFormatR64i:
+      return "R64i";
+
+    case SpvImageFormatMax:
+      break;
+  }
+  // unhandled SpvImageFormat enum value
+  return "???";
+}
+
+std::string ToStringUserType(SpvReflectUserType user_type) {
+  switch (user_type) {
+    case SPV_REFLECT_USER_TYPE_CBUFFER:
+      return "cbuffer";
+    case SPV_REFLECT_USER_TYPE_TBUFFER:
+      return "tbuffer";
+    case SPV_REFLECT_USER_TYPE_APPEND_STRUCTURED_BUFFER:
+      return "AppendStructuredBuffer";
+    case SPV_REFLECT_USER_TYPE_BUFFER:
+      return "Buffer";
+    case SPV_REFLECT_USER_TYPE_BYTE_ADDRESS_BUFFER:
+      return "ByteAddressBuffer";
+    case SPV_REFLECT_USER_TYPE_CONSTANT_BUFFER:
+      return "ConstantBuffer";
+    case SPV_REFLECT_USER_TYPE_CONSUME_STRUCTURED_BUFFER:
+      return "ConsumeStructuredBuffer";
+    case SPV_REFLECT_USER_TYPE_INPUT_PATCH:
+      return "InputPatch";
+    case SPV_REFLECT_USER_TYPE_OUTPUT_PATCH:
+      return "OutputPatch";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_BUFFER:
+      return "RasterizerOrderedBuffer";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_BYTE_ADDRESS_BUFFER:
+      return "RasterizerOrderedByteAddressBuffer";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_STRUCTURED_BUFFER:
+      return "RasterizerOrderedStructuredBuffer";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_1D:
+      return "RasterizerOrderedTexture1D";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_1D_ARRAY:
+      return "RasterizerOrderedTexture1DArray";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_2D:
+      return "RasterizerOrderedTexture2D";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_2D_ARRAY:
+      return "RasterizerOrderedTexture2DArray";
+    case SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_3D:
+      return "RasterizerOrderedTexture3D";
+    case SPV_REFLECT_USER_TYPE_RAYTRACING_ACCELERATION_STRUCTURE:
+      return "RaytracingAccelerationStructure";
+    case SPV_REFLECT_USER_TYPE_RW_BUFFER:
+      return "RWBuffer";
+    case SPV_REFLECT_USER_TYPE_RW_BYTE_ADDRESS_BUFFER:
+      return "RWByteAddressBuffer";
+    case SPV_REFLECT_USER_TYPE_RW_STRUCTURED_BUFFER:
+      return "RWStructuredBuffer";
+    case SPV_REFLECT_USER_TYPE_RW_TEXTURE_1D:
+      return "RWTexture1D";
+    case SPV_REFLECT_USER_TYPE_RW_TEXTURE_1D_ARRAY:
+      return "RWTexture1DArray";
+    case SPV_REFLECT_USER_TYPE_RW_TEXTURE_2D:
+      return "RWTexture2D";
+    case SPV_REFLECT_USER_TYPE_RW_TEXTURE_2D_ARRAY:
+      return "RWTexture2DArray";
+    case SPV_REFLECT_USER_TYPE_RW_TEXTURE_3D:
+      return "RWTexture3D";
+    case SPV_REFLECT_USER_TYPE_STRUCTURED_BUFFER:
+      return "StructuredBuffer";
+    case SPV_REFLECT_USER_TYPE_SUBPASS_INPUT:
+      return "SubpassInput";
+    case SPV_REFLECT_USER_TYPE_SUBPASS_INPUT_MS:
+      return "SubpassInputMS";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_1D:
+      return "Texture1D";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_1D_ARRAY:
+      return "Texture1DArray";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_2D:
+      return "Texture2D";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_2D_ARRAY:
+      return "Texture2DArray";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_2DMS:
+      return "Texture2DMS";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_2DMS_ARRAY:
+      return "Texture2DMSArray";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_3D:
+      return "Texture3D";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_BUFFER:
+      return "TextureBuffer";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_CUBE:
+      return "TextureCube";
+    case SPV_REFLECT_USER_TYPE_TEXTURE_CUBE_ARRAY:
+      return "TextureCubeArray";
+    default:
+      return "???";
+  }
+}
+
+std::string ToStringTypeFlags(SpvReflectTypeFlags type_flags) {
+  if (type_flags == SPV_REFLECT_TYPE_FLAG_UNDEFINED) {
+    return "UNDEFINED";
+  }
+
+#define PRINT_AND_CLEAR_TYPE_FLAG(stream, flags, bit)                               \
+  if (((flags) & (SPV_REFLECT_TYPE_FLAG_##bit)) == (SPV_REFLECT_TYPE_FLAG_##bit)) { \
+    stream << #bit << " ";                                                          \
+    flags ^= SPV_REFLECT_TYPE_FLAG_##bit;                                           \
+  }
+  std::stringstream sstream;
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, ARRAY);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, STRUCT);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, REF);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, EXTERNAL_MASK);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, EXTERNAL_BLOCK);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, EXTERNAL_SAMPLED_IMAGE);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, EXTERNAL_SAMPLER);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, EXTERNAL_IMAGE);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, MATRIX);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, VECTOR);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, FLOAT);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, INT);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, BOOL);
+  PRINT_AND_CLEAR_TYPE_FLAG(sstream, type_flags, VOID);
+#undef PRINT_AND_CLEAR_TYPE_FLAG
+  if (type_flags != 0) {
+    // Unhandled SpvReflectTypeFlags bit
+    sstream << "???";
+  }
+  return sstream.str();
+}
+
+std::string ToStringDecorationFlags(SpvReflectDecorationFlags decoration_flags) {
+  if (decoration_flags == SPV_REFLECT_DECORATION_NONE) {
+    return "NONE";
+  }
+
+#define PRINT_AND_CLEAR_DECORATION_FLAG(stream, flags, bit)                           \
+  if (((flags) & (SPV_REFLECT_DECORATION_##bit)) == (SPV_REFLECT_DECORATION_##bit)) { \
+    stream << #bit << " ";                                                            \
+    flags ^= SPV_REFLECT_DECORATION_##bit;                                            \
+  }
+  std::stringstream sstream;
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, NON_WRITABLE);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, NON_READABLE);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, FLAT);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, NOPERSPECTIVE);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, BUILT_IN);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, COLUMN_MAJOR);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, ROW_MAJOR);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, BUFFER_BLOCK);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, BLOCK);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, PATCH);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, PER_VERTEX);
+  PRINT_AND_CLEAR_DECORATION_FLAG(sstream, decoration_flags, PER_TASK);
+#undef PRINT_AND_CLEAR_DECORATION_FLAG
+  if (decoration_flags != 0) {
+    // Unhandled SpvReflectDecorationFlags bit
+    sstream << "???";
+  }
+  return sstream.str();
+}
+
+std::string ToStringFormat(SpvReflectFormat fmt) {
+  switch (fmt) {
+    case SPV_REFLECT_FORMAT_UNDEFINED:
+      return "VK_FORMAT_UNDEFINED";
+    case SPV_REFLECT_FORMAT_R16_UINT:
+      return "VK_FORMAT_R16_UINT";
+    case SPV_REFLECT_FORMAT_R16_SINT:
+      return "VK_FORMAT_R16_SINT";
+    case SPV_REFLECT_FORMAT_R16_SFLOAT:
+      return "VK_FORMAT_R16_SFLOAT";
+    case SPV_REFLECT_FORMAT_R16G16_UINT:
+      return "VK_FORMAT_R16G16_UINT";
+    case SPV_REFLECT_FORMAT_R16G16_SINT:
+      return "VK_FORMAT_R16G16_SINT";
+    case SPV_REFLECT_FORMAT_R16G16_SFLOAT:
+      return "VK_FORMAT_R16G16_SFLOAT";
+    case SPV_REFLECT_FORMAT_R16G16B16_UINT:
+      return "VK_FORMAT_R16G16B16_UINT";
+    case SPV_REFLECT_FORMAT_R16G16B16_SINT:
+      return "VK_FORMAT_R16G16B16_SINT";
+    case SPV_REFLECT_FORMAT_R16G16B16_SFLOAT:
+      return "VK_FORMAT_R16G16B16_SFLOAT";
+    case SPV_REFLECT_FORMAT_R16G16B16A16_UINT:
+      return "VK_FORMAT_R16G16B16A16_UINT";
+    case SPV_REFLECT_FORMAT_R16G16B16A16_SINT:
+      return "VK_FORMAT_R16G16B16A16_SINT";
+    case SPV_REFLECT_FORMAT_R16G16B16A16_SFLOAT:
+      return "VK_FORMAT_R16G16B16A16_SFLOAT";
+    case SPV_REFLECT_FORMAT_R32_UINT:
+      return "VK_FORMAT_R32_UINT";
+    case SPV_REFLECT_FORMAT_R32_SINT:
+      return "VK_FORMAT_R32_SINT";
+    case SPV_REFLECT_FORMAT_R32_SFLOAT:
+      return "VK_FORMAT_R32_SFLOAT";
+    case SPV_REFLECT_FORMAT_R32G32_UINT:
+      return "VK_FORMAT_R32G32_UINT";
+    case SPV_REFLECT_FORMAT_R32G32_SINT:
+      return "VK_FORMAT_R32G32_SINT";
+    case SPV_REFLECT_FORMAT_R32G32_SFLOAT:
+      return "VK_FORMAT_R32G32_SFLOAT";
+    case SPV_REFLECT_FORMAT_R32G32B32_UINT:
+      return "VK_FORMAT_R32G32B32_UINT";
+    case SPV_REFLECT_FORMAT_R32G32B32_SINT:
+      return "VK_FORMAT_R32G32B32_SINT";
+    case SPV_REFLECT_FORMAT_R32G32B32_SFLOAT:
+      return "VK_FORMAT_R32G32B32_SFLOAT";
+    case SPV_REFLECT_FORMAT_R32G32B32A32_UINT:
+      return "VK_FORMAT_R32G32B32A32_UINT";
+    case SPV_REFLECT_FORMAT_R32G32B32A32_SINT:
+      return "VK_FORMAT_R32G32B32A32_SINT";
+    case SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT:
+      return "VK_FORMAT_R32G32B32A32_SFLOAT";
+    case SPV_REFLECT_FORMAT_R64_UINT:
+      return "VK_FORMAT_R64_UINT";
+    case SPV_REFLECT_FORMAT_R64_SINT:
+      return "VK_FORMAT_R64_SINT";
+    case SPV_REFLECT_FORMAT_R64_SFLOAT:
+      return "VK_FORMAT_R64_SFLOAT";
+    case SPV_REFLECT_FORMAT_R64G64_UINT:
+      return "VK_FORMAT_R64G64_UINT";
+    case SPV_REFLECT_FORMAT_R64G64_SINT:
+      return "VK_FORMAT_R64G64_SINT";
+    case SPV_REFLECT_FORMAT_R64G64_SFLOAT:
+      return "VK_FORMAT_R64G64_SFLOAT";
+    case SPV_REFLECT_FORMAT_R64G64B64_UINT:
+      return "VK_FORMAT_R64G64B64_UINT";
+    case SPV_REFLECT_FORMAT_R64G64B64_SINT:
+      return "VK_FORMAT_R64G64B64_SINT";
+    case SPV_REFLECT_FORMAT_R64G64B64_SFLOAT:
+      return "VK_FORMAT_R64G64B64_SFLOAT";
+    case SPV_REFLECT_FORMAT_R64G64B64A64_UINT:
+      return "VK_FORMAT_R64G64B64A64_UINT";
+    case SPV_REFLECT_FORMAT_R64G64B64A64_SINT:
+      return "VK_FORMAT_R64G64B64A64_SINT";
+    case SPV_REFLECT_FORMAT_R64G64B64A64_SFLOAT:
+      return "VK_FORMAT_R64G64B64A64_SFLOAT";
+  }
+  // unhandled SpvReflectFormat enum value
+  return "VK_FORMAT_???";
+}
+
+static std::string ToStringScalarType(const SpvReflectTypeDescription& type) {
+  switch (type.op) {
+    case SpvOpTypeVoid: {
+      return "void";
+      break;
+    }
+    case SpvOpTypeBool: {
+      return "bool";
+      break;
+    }
+    case SpvOpTypeInt: {
+      if (type.traits.numeric.scalar.signedness)
+        return "int";
+      else
+        return "uint";
+    }
+    case SpvOpTypeFloat: {
+      switch (type.traits.numeric.scalar.width) {
+        case 32:
+          return "float";
+        case 64:
+          return "double";
+        default:
+          break;
+      }
+      break;
+    }
+    case SpvOpTypeStruct: {
+      return "struct";
+    }
+    case SpvOpTypePointer: {
+      return "ptr";
+    }
+    default: {
+      break;
+    }
+  }
+  return "";
+}
+
+static std::string ToStringGlslType(const SpvReflectTypeDescription& type) {
+  switch (type.op) {
+    case SpvOpTypeVector: {
+      switch (type.traits.numeric.scalar.width) {
+        case 32: {
+          switch (type.traits.numeric.vector.component_count) {
+            case 2:
+              return "vec2";
+            case 3:
+              return "vec3";
+            case 4:
+              return "vec4";
+          }
+        } break;
+
+        case 64: {
+          switch (type.traits.numeric.vector.component_count) {
+            case 2:
+              return "dvec2";
+            case 3:
+              return "dvec3";
+            case 4:
+              return "dvec4";
+          }
+        } break;
+      }
+    } break;
+    default:
+      break;
+  }
+  return ToStringScalarType(type);
+}
+
+static std::string ToStringHlslType(const SpvReflectTypeDescription& type) {
+  switch (type.op) {
+    case SpvOpTypeVector: {
+      switch (type.traits.numeric.scalar.width) {
+        case 32: {
+          switch (type.traits.numeric.vector.component_count) {
+            case 2:
+              return "float2";
+            case 3:
+              return "float3";
+            case 4:
+              return "float4";
+          }
+        } break;
+
+        case 64: {
+          switch (type.traits.numeric.vector.component_count) {
+            case 2:
+              return "double2";
+            case 3:
+              return "double3";
+            case 4:
+              return "double4";
+          }
+        } break;
+      }
+    } break;
+
+    default:
+      break;
+  }
+  return ToStringScalarType(type);
+}
+
+std::string ToStringType(SpvSourceLanguage src_lang, const SpvReflectTypeDescription& type) {
+  if (src_lang == SpvSourceLanguageHLSL) {
+    return ToStringHlslType(type);
+  }
+
+  return ToStringGlslType(type);
+}
+
+std::string ToStringComponentType(const SpvReflectTypeDescription& type, uint32_t member_decoration_flags) {
+  uint32_t masked_type = type.type_flags & 0xF;
+  if (masked_type == 0) {
+    return "";
+  }
+
+  std::stringstream ss;
+
+  if (type.type_flags & SPV_REFLECT_TYPE_FLAG_MATRIX) {
+    if (member_decoration_flags & SPV_REFLECT_DECORATION_COLUMN_MAJOR) {
+      ss << "column_major"
+         << " ";
+    } else if (member_decoration_flags & SPV_REFLECT_DECORATION_ROW_MAJOR) {
+      ss << "row_major"
+         << " ";
+    }
+  }
+
+  switch (masked_type) {
+    default:
+      assert(false && "unsupported component type");
+      break;
+    case SPV_REFLECT_TYPE_FLAG_BOOL:
+      ss << "bool";
+      break;
+    case SPV_REFLECT_TYPE_FLAG_INT:
+      ss << (type.traits.numeric.scalar.signedness ? "int" : "uint");
+      break;
+    case SPV_REFLECT_TYPE_FLAG_FLOAT:
+      ss << "float";
+      break;
+  }
+
+  if (type.type_flags & SPV_REFLECT_TYPE_FLAG_MATRIX) {
+    ss << type.traits.numeric.matrix.row_count;
+    ss << "x";
+    ss << type.traits.numeric.matrix.column_count;
+  } else if (type.type_flags & SPV_REFLECT_TYPE_FLAG_VECTOR) {
+    ss << type.traits.numeric.vector.component_count;
+  }
+
+  return ss.str();
+}
+
+void ParseBlockMembersToTextLines(const char* indent, int indent_depth, bool flatten_cbuffers, const std::string& parent_name,
+                                  uint32_t member_count, const SpvReflectBlockVariable* p_members,
+                                  std::vector<TextLine>* p_text_lines, std::unordered_set<uint32_t>& physical_pointer_spirv_id) {
+  const char* t = indent;
+  for (uint32_t member_index = 0; member_index < member_count; ++member_index) {
+    indent_depth = flatten_cbuffers ? 2 : indent_depth;
+    std::stringstream ss_indent;
+    for (int indent_count = 0; indent_count < indent_depth; ++indent_count) {
+      ss_indent << t;
+    }
+    std::string expanded_indent = ss_indent.str();
+
+    const auto& member = p_members[member_index];
+    if (!member.type_description) {
+      // TODO 212 - If a buffer ref has an array of itself, all members are null
+      continue;
+    }
+
+    bool is_struct = ((member.type_description->type_flags & static_cast<SpvReflectTypeFlags>(SPV_REFLECT_TYPE_FLAG_STRUCT)) != 0);
+    bool is_ref = ((member.type_description->type_flags & static_cast<SpvReflectTypeFlags>(SPV_REFLECT_TYPE_FLAG_REF)) != 0);
+    bool is_array = ((member.type_description->type_flags & static_cast<SpvReflectTypeFlags>(SPV_REFLECT_TYPE_FLAG_ARRAY)) != 0);
+    if (is_struct) {
+      const std::string name = (member.name == nullptr ? "" : member.name);
+
+      // Begin struct
+      TextLine tl = {};
+      tl.indent = expanded_indent;
+      tl.type_name = (member.type_description->type_name == nullptr ? "" : member.type_description->type_name);
+      tl.absolute_offset = member.absolute_offset;
+      tl.relative_offset = member.offset;
+      tl.size = member.size;
+      tl.padded_size = member.padded_size;
+      tl.array_stride = member.array.stride;
+      tl.block_variable_flags = member.flags;
+      tl.text_line_flags = is_ref ? TEXT_LINE_TYPE_REF_BEGIN : TEXT_LINE_TYPE_STRUCT_BEGIN;
+      if (!flatten_cbuffers) {
+        p_text_lines->push_back(tl);
+      }
+
+      const bool array_of_structs = is_array && member.type_description->struct_type_description;
+      const uint32_t struct_id =
+          array_of_structs ? member.type_description->struct_type_description->id : member.type_description->id;
+
+      if (physical_pointer_spirv_id.count(struct_id) == 0) {
+        physical_pointer_spirv_id.insert(member.type_description->id);
+        if (array_of_structs) {
+          physical_pointer_spirv_id.insert(member.type_description->struct_type_description->id);
+        }
+
+        // Members
+        tl = {};
+        std::string current_parent_name;
+        if (flatten_cbuffers) {
+          current_parent_name = parent_name.empty() ? name : (parent_name + "." + name);
+        }
+        std::vector<TextLine>* p_target_text_line = flatten_cbuffers ? p_text_lines : &tl.lines;
+        ParseBlockMembersToTextLines(t, indent_depth + 1, flatten_cbuffers, current_parent_name, member.member_count,
+                                     member.members, p_target_text_line, physical_pointer_spirv_id);
+        tl.text_line_flags = TEXT_LINE_TYPE_LINES;
+        p_text_lines->push_back(tl);
+      }
+      physical_pointer_spirv_id.erase(member.type_description->id);
+
+      // End struct
+      tl = {};
+      tl.indent = expanded_indent;
+      tl.name = name;
+      if ((member.array.dims_count > 0) || (member.type_description->traits.array.dims[0] > 0)) {
+        const SpvReflectArrayTraits* p_array_info = (member.array.dims_count > 0) ? &member.array : nullptr;
+        if (p_array_info == nullptr) {
+          //
+          // glslang based compilers stores array information in the type and
+          // not the variable
+          //
+          p_array_info = (member.type_description->traits.array.dims[0] > 0) ? &member.type_description->traits.array : nullptr;
+        }
+        if (p_array_info != nullptr) {
+          std::stringstream ss_array;
+          for (uint32_t array_dim_index = 0; array_dim_index < p_array_info->dims_count; ++array_dim_index) {
+            uint32_t dim = p_array_info->dims[array_dim_index];
+            //
+            // dim = 0 means it's an unbounded array
+            //
+            if (dim > 0) {
+              ss_array << "[" << dim << "]";
+            } else {
+              ss_array << "[]";
+            }
+          }
+          tl.name += ss_array.str();
+        }
+      }
+      tl.absolute_offset = member.absolute_offset;
+      tl.relative_offset = member.offset;
+      tl.size = member.size;
+      tl.padded_size = member.padded_size;
+      tl.array_stride = member.array.stride;
+      tl.block_variable_flags = member.flags;
+      tl.text_line_flags = is_ref ? TEXT_LINE_TYPE_REF_END : TEXT_LINE_TYPE_STRUCT_END;
+      if (!flatten_cbuffers) {
+        p_text_lines->push_back(tl);
+      }
+    } else {
+      std::string name = (member.name == nullptr ? "" : member.name);
+      if (flatten_cbuffers) {
+        if (!parent_name.empty()) {
+          name = parent_name + "." + name;
+        }
+      }
+
+      TextLine tl = {};
+      tl.indent = expanded_indent;
+      tl.type_name = ToStringComponentType(*member.type_description, member.decoration_flags);
+      tl.name = name;
+      if (member.array.dims_count > 0) {
+        std::stringstream ss_array;
+        for (uint32_t array_dim_index = 0; array_dim_index < member.array.dims_count; ++array_dim_index) {
+          uint32_t dim = member.array.dims[array_dim_index];
+          ss_array << "[" << dim << "]";
+        }
+        tl.name += ss_array.str();
+      }
+      tl.absolute_offset = member.absolute_offset;
+      tl.relative_offset = member.offset;
+      tl.size = member.size;
+      tl.padded_size = member.padded_size;
+      tl.array_stride = member.array.stride;
+      tl.block_variable_flags = member.flags;
+      p_text_lines->push_back(tl);
+    }
+  }
+}
+
+void ParseBlockVariableToTextLines(const char* indent, bool flatten_cbuffers, const SpvReflectBlockVariable& block_var,
+                                   std::vector<TextLine>* p_text_lines) {
+  // Begin block
+  TextLine tl = {};
+  tl.indent = indent;
+  tl.type_name = (block_var.type_description->type_name != nullptr) ? block_var.type_description->type_name : "<unnamed>";
+  tl.size = block_var.size;
+  tl.padded_size = block_var.padded_size;
+  tl.text_line_flags = TEXT_LINE_TYPE_BLOCK_BEGIN;
+  p_text_lines->push_back(tl);
+
+  // Members
+  tl = {};
+  std::unordered_set<uint32_t> physical_pointer_spirv_id;
+  ParseBlockMembersToTextLines(indent, 2, flatten_cbuffers, "", block_var.member_count, block_var.members, &tl.lines,
+                               physical_pointer_spirv_id);
+  tl.text_line_flags = TEXT_LINE_TYPE_LINES;
+  p_text_lines->push_back(tl);
+
+  // End block
+  tl = {};
+  tl.indent = indent;
+  tl.name = (block_var.name != nullptr) ? block_var.name : "<unnamed>";
+  tl.absolute_offset = 0;
+  tl.relative_offset = 0;
+  tl.size = block_var.size;
+  tl.padded_size = block_var.padded_size;
+  tl.text_line_flags = TEXT_LINE_TYPE_BLOCK_END;
+  p_text_lines->push_back(tl);
+}
+
+void FormatTextLines(const std::vector<TextLine>& text_lines, const char* indent, std::vector<TextLine>* p_formatted_lines) {
+  size_t modifier_width = 0;
+  size_t type_name_width = 0;
+  size_t name_width = 0;
+
+  // Widths
+  for (auto& tl : text_lines) {
+    if (tl.text_line_flags != 0) {
+      continue;
+    }
+    modifier_width = std::max<size_t>(modifier_width, tl.modifier.length());
+    type_name_width = std::max<size_t>(type_name_width, tl.type_name.length());
+    name_width = std::max<size_t>(name_width, tl.name.length());
+  }
+
+  // Output
+  size_t n = text_lines.size();
+  for (size_t i = 0; i < n; ++i) {
+    auto& tl = text_lines[i];
+
+    std::stringstream ss;
+    if ((tl.text_line_flags == TEXT_LINE_TYPE_BLOCK_BEGIN) || (tl.text_line_flags == TEXT_LINE_TYPE_STRUCT_BEGIN) ||
+        (tl.text_line_flags == TEXT_LINE_TYPE_REF_BEGIN)) {
+      ss << indent;
+      ss << tl.indent;
+      if (tl.text_line_flags == TEXT_LINE_TYPE_REF_BEGIN) ss << "ref ";
+      ss << "struct ";
+      ss << tl.type_name;
+      ss << " {";
+    } else if ((tl.text_line_flags == TEXT_LINE_TYPE_BLOCK_END) || (tl.text_line_flags == TEXT_LINE_TYPE_STRUCT_END) ||
+               (tl.text_line_flags == TEXT_LINE_TYPE_REF_END)) {
+      ss << indent;
+      ss << tl.indent;
+      ss << "} ";
+      ss << tl.name;
+      ss << ";";
+    } else if (tl.text_line_flags == TEXT_LINE_TYPE_LINES) {
+      FormatTextLines(tl.lines, indent, p_formatted_lines);
+    } else {
+      ss << indent;
+      ss << tl.indent;
+      if (modifier_width > 0) {
+        ss << std::setw(modifier_width) << std::left << tl.modifier;
+        ss << " ";
+      }
+      ss << std::setw(type_name_width) << std::left << tl.type_name;
+      ss << " ";
+      ss << std::setw(name_width) << (tl.name + ";");
+    }
+
+    // Reuse the various strings to store the formatted texts
+    TextLine out_tl = {};
+    out_tl.formatted_line = ss.str();
+    if (out_tl.formatted_line.length() > 0) {
+      out_tl.array_stride = tl.array_stride;
+      out_tl.text_line_flags = tl.text_line_flags;
+      out_tl.formatted_absolute_offset = std::to_string(tl.absolute_offset);
+      out_tl.formatted_relative_offset = std::to_string(tl.relative_offset);
+      out_tl.formatted_size = std::to_string(tl.size);
+      out_tl.formatted_padded_size = std::to_string(tl.padded_size);
+      out_tl.formatted_array_stride = std::to_string(tl.array_stride);
+      // Block variable flags
+      if (tl.block_variable_flags != 0) {
+        std::stringstream ss_flags;
+        if (tl.block_variable_flags & SPV_REFLECT_VARIABLE_FLAGS_UNUSED) {
+          ss_flags << "UNUSED";
+        }
+        out_tl.formatted_block_variable_flags = ss_flags.str();
+      }
+      p_formatted_lines->push_back(out_tl);
+    }
+  }
+}
+
+void StreamWriteTextLines(std::ostream& os, const char* indent, bool flatten_cbuffers, const std::vector<TextLine>& text_lines) {
+  std::vector<TextLine> formatted_lines;
+  FormatTextLines(text_lines, indent, &formatted_lines);
+
+  size_t line_width = 0;
+  size_t offset_width = 0;
+  size_t absolute_offset_width = 0;
+  size_t size_width = 0;
+  size_t padded_size_width = 0;
+  size_t array_stride_width = 0;
+
+  // Width
+  for (auto& tl : formatted_lines) {
+    if (tl.text_line_flags != 0) {
+      continue;
+    }
+    line_width = std::max<size_t>(line_width, tl.formatted_line.length());
+    absolute_offset_width = std::max<size_t>(absolute_offset_width, tl.formatted_absolute_offset.length());
+    offset_width = std::max<size_t>(offset_width, tl.formatted_relative_offset.length());
+    size_width = std::max<size_t>(size_width, tl.formatted_size.length());
+    padded_size_width = std::max<size_t>(padded_size_width, tl.formatted_padded_size.length());
+    array_stride_width = std::max<size_t>(array_stride_width, tl.formatted_array_stride.length());
+  }
+
+  size_t n = formatted_lines.size();
+  for (size_t i = 0; i < n; ++i) {
+    auto& tl = formatted_lines[i];
+
+    if (tl.text_line_flags == TEXT_LINE_TYPE_BLOCK_BEGIN) {
+      if (i > 0) {
+        os << "\n";
+      }
+
+      size_t pos = tl.formatted_line.find_first_not_of(' ');
+      if (pos != std::string::npos) {
+        std::string s(pos, ' ');
+        os << s << "//"
+           << " ";
+        os << "size = " << tl.formatted_size << ", ";
+        os << "padded size = " << tl.formatted_padded_size;
+        os << "\n";
+      }
+
+      os << std::setw(line_width) << std::left << tl.formatted_line;
+    } else if (tl.text_line_flags == TEXT_LINE_TYPE_BLOCK_END) {
+      os << std::setw(line_width) << std::left << tl.formatted_line;
+      if (i < (n - 1)) {
+        os << "\n";
+      }
+    } else if (tl.text_line_flags == TEXT_LINE_TYPE_STRUCT_BEGIN || tl.text_line_flags == TEXT_LINE_TYPE_REF_BEGIN) {
+      if (!flatten_cbuffers) {
+        if (i > 0) {
+          os << "\n";
+        }
+
+        size_t pos = tl.formatted_line.find_first_not_of(' ');
+        if (pos != std::string::npos) {
+          std::string s(pos, ' ');
+          os << s << "//"
+             << " ";
+          os << "abs offset = " << tl.formatted_absolute_offset << ", ";
+          os << "rel offset = " << tl.formatted_relative_offset << ", ";
+          os << "size = " << tl.formatted_size << ", ";
+          os << "padded size = " << tl.formatted_padded_size;
+          if (tl.array_stride > 0) {
+            os << ", ";
+            os << "array stride = " << tl.formatted_array_stride;
+          }
+          if (!tl.formatted_block_variable_flags.empty()) {
+            os << " ";
+            os << tl.formatted_block_variable_flags;
+          }
+          os << "\n";
+        }
+
+        os << std::setw(line_width) << std::left << tl.formatted_line;
+      }
+    } else if (tl.text_line_flags == TEXT_LINE_TYPE_STRUCT_END || tl.text_line_flags == TEXT_LINE_TYPE_REF_END) {
+      if (!flatten_cbuffers) {
+        os << std::setw(line_width) << std::left << tl.formatted_line;
+        if (i < (n - 1)) {
+          os << "\n";
+        }
+      }
+    } else {
+      os << std::setw(line_width) << std::left << tl.formatted_line;
+      os << " "
+         << "//"
+         << " ";
+      os << "abs offset = " << std::setw(absolute_offset_width) << std::right << tl.formatted_absolute_offset << ", ";
+      if (!flatten_cbuffers) {
+        os << "rel offset = " << std::setw(offset_width) << std::right << tl.formatted_relative_offset << ", ";
+      }
+      os << "size = " << std::setw(size_width) << std::right << tl.formatted_size << ", ";
+      os << "padded size = " << std::setw(padded_size_width) << std::right << tl.formatted_padded_size;
+      if (tl.array_stride > 0) {
+        os << ", ";
+        os << "array stride = " << std::setw(array_stride_width) << tl.formatted_array_stride;
+      }
+      if (!tl.formatted_block_variable_flags.empty()) {
+        os << " ";
+        os << tl.formatted_block_variable_flags;
+      }
+    }
+
+    if (i < (n - 1)) {
+      os << "\n";
+    }
+  }
+}
+
+void StreamWritePushConstantsBlock(std::ostream& os, const SpvReflectBlockVariable& obj, bool flatten_cbuffers,
+                                   const char* indent) {
+  const char* t = indent;
+  os << t << "spirv id : " << obj.spirv_id << "\n";
+
+  os << t << "name     : " << ((obj.name != nullptr) ? obj.name : "<unnamed>");
+  if ((obj.type_description->type_name != nullptr) && (strlen(obj.type_description->type_name) > 0)) {
+    os << " "
+       << "(" << obj.type_description->type_name << ")";
+  }
+
+  std::vector<TextLine> text_lines;
+  ParseBlockVariableToTextLines("    ", flatten_cbuffers, obj, &text_lines);
+  if (!text_lines.empty()) {
+    os << "\n";
+    StreamWriteTextLines(os, t, flatten_cbuffers, text_lines);
+    os << "\n";
+  }
+}
+
+void StreamWriteDescriptorBinding(std::ostream& os, const SpvReflectDescriptorBinding& obj, bool write_set, bool flatten_cbuffers,
+                                  const char* indent) {
+  const char* t = indent;
+  os << t << "spirv id : " << obj.spirv_id << "\n";
+  if (write_set) {
+    os << t << "set      : " << obj.set << "\n";
+  }
+  os << t << "binding  : " << obj.binding << "\n";
+  os << t << "type     : " << ToStringDescriptorType(obj.descriptor_type);
+  os << " "
+     << "(" << ToStringResourceType(obj.resource_type) << ")"
+     << "\n";
+
+  // count
+  os << t << "count    : " << obj.count << "\n";
+
+  // array
+  if (obj.array.dims_count > 0) {
+    os << t << "array    : ";
+    for (uint32_t dim_index = 0; dim_index < obj.array.dims_count; ++dim_index) {
+      os << "[" << obj.array.dims[dim_index] << "]";
+    }
+    os << "\n";
+  }
+
+  // counter
+  if (obj.uav_counter_binding != nullptr) {
+    os << t << "counter  : ";
+    os << "(";
+    os << "set=" << obj.uav_counter_binding->set << ", ";
+    os << "binding=" << obj.uav_counter_binding->binding << ", ";
+    os << "name=" << obj.uav_counter_binding->name;
+    os << ");";
+    os << "\n";
+  }
+
+  // accessed
+  os << t << "accessed : " << (obj.accessed ? "true" : "false") << "\n";
+
+  os << t << "name     : " << ((obj.name != nullptr) ? obj.name : "<unnamed>");
+  if ((obj.type_description->type_name != nullptr) && (strlen(obj.type_description->type_name) > 0)) {
+    os << " "
+       << "(" << obj.type_description->type_name << ")";
+  }
+
+  if (obj.descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
+      obj.descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) {
+    std::vector<TextLine> text_lines;
+    ParseBlockVariableToTextLines("    ", flatten_cbuffers, obj.block, &text_lines);
+    if (!text_lines.empty()) {
+      os << "\n";
+      StreamWriteTextLines(os, t, flatten_cbuffers, text_lines);
+      os << "\n";
+    }
+  }
+}
+
+void StreamWriteInterfaceVariable(std::ostream& os, const SpvReflectInterfaceVariable& obj, const char* indent) {
+  const char* t = indent;
+  os << t << "spirv id  : " << obj.spirv_id << "\n";
+  os << t << "location  : ";
+  if (obj.decoration_flags & SPV_REFLECT_DECORATION_BUILT_IN) {
+    os << ToStringSpvBuiltIn(obj, true);
+  } else {
+    os << obj.location;
+  }
+  os << "\n";
+  os << t << "type      : " << ToStringComponentType(*obj.type_description, 0) << "\n";
+
+  // array
+  if (obj.array.dims_count > 0) {
+    os << t << "array     : ";
+    for (uint32_t dim_index = 0; dim_index < obj.array.dims_count; ++dim_index) {
+      os << "[" << obj.array.dims[dim_index] << "]";
+    }
+    os << "\n";
+  }
+
+  os << t << "semantic  : " << (obj.semantic != NULL ? obj.semantic : "") << "\n";
+  os << t << "name      : " << (obj.name != NULL ? obj.name : "");
+  if ((obj.type_description->type_name != nullptr) && (strlen(obj.type_description->type_name) > 0)) {
+    os << " "
+       << "(" << obj.type_description->type_name << ")";
+  }
+  os << "\n";
+  os << t << "qualifier : ";
+  if (obj.decoration_flags & SPV_REFLECT_DECORATION_FLAT) {
+    os << "flat";
+  } else if (obj.decoration_flags & SPV_REFLECT_DECORATION_NOPERSPECTIVE) {
+    os << "noperspective";
+  }
+}
+
+void StreamWriteEntryPoint(std::ostream& os, const SpvReflectEntryPoint& obj, const char* indent) {
+  os << indent << "entry point     : " << obj.name;
+  os << " (stage=" << ToStringShaderStage(obj.shader_stage) << ")";
+  if (obj.shader_stage == SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT) {
+    os << "\n";
+    os << "local size      : "
+       << "(" << (obj.local_size.x == SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT ? "Spec Constant" : std::to_string(obj.local_size.x))
+       << ", "
+       << (obj.local_size.y == SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT ? "Spec Constant" : std::to_string(obj.local_size.y))
+       << ", "
+       << (obj.local_size.z == SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT ? "Spec Constant" : std::to_string(obj.local_size.z))
+       << ")";
+  }
+}
+
+void StreamWriteShaderModule(std::ostream& os, const SpvReflectShaderModule& obj, const char* indent) {
+  (void)indent;
+  os << "generator       : " << ToStringGenerator(obj.generator) << "\n";
+  os << "source lang     : " << spvReflectSourceLanguage(obj.source_language) << "\n";
+  os << "source lang ver : " << obj.source_language_version << "\n";
+  os << "source file     : " << (obj.source_file != NULL ? obj.source_file : "") << "\n";
+  // os << "shader stage    : " << ToStringShaderStage(obj.shader_stage) <<
+  // "\n";
+
+  for (uint32_t i = 0; i < obj.entry_point_count; ++i) {
+    StreamWriteEntryPoint(os, obj.entry_points[i], "");
+    if (i < (obj.entry_point_count - 1)) {
+      os << "\n";
+    }
+  }
+}
+
+// Avoid unused variable warning/error on Linux
+#ifndef NDEBUG
+#define USE_ASSERT(x) assert(x)
+#else
+#define USE_ASSERT(x) ((void)(x))
+#endif
+
+void WriteReflection(const spv_reflect::ShaderModule& obj, bool flatten_cbuffers, std::ostream& os) {
+  const char* t = "  ";
+  const char* tt = "    ";
+  const char* ttt = "      ";
+
+  StreamWriteShaderModule(os, obj.GetShaderModule(), "");
+
+  uint32_t count = 0;
+  std::vector<SpvReflectInterfaceVariable*> variables;
+  std::vector<SpvReflectDescriptorBinding*> bindings;
+  std::vector<SpvReflectDescriptorSet*> sets;
+  std::vector<SpvReflectBlockVariable*> push_constant_bocks;
+
+  count = 0;
+  SpvReflectResult result = obj.EnumerateInputVariables(&count, nullptr);
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  variables.resize(count);
+  result = obj.EnumerateInputVariables(&count, variables.data());
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  if (count > 0) {
+    os << "\n";
+    os << "\n";
+    os << "\n";
+    os << t << "Input variables: " << count << "\n\n";
+    for (size_t i = 0; i < variables.size(); ++i) {
+      auto p_var = variables[i];
+      USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+      os << tt << i << ":"
+         << "\n";
+      StreamWriteInterfaceVariable(os, *p_var, ttt);
+      if (i < (count - 1)) {
+        os << "\n";
+      }
+    }
+  }
+
+  count = 0;
+  result = obj.EnumerateOutputVariables(&count, nullptr);
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  variables.resize(count);
+  result = obj.EnumerateOutputVariables(&count, variables.data());
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  if (count > 0) {
+    os << "\n";
+    os << "\n";
+    os << "\n";
+    os << t << "Output variables: " << count << "\n\n";
+    for (size_t i = 0; i < variables.size(); ++i) {
+      auto p_var = variables[i];
+      USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+      os << tt << i << ":"
+         << "\n";
+      StreamWriteInterfaceVariable(os, *p_var, ttt);
+      if (i < (count - 1)) {
+        os << "\n";
+      }
+    }
+  }
+
+  count = 0;
+  result = obj.EnumeratePushConstantBlocks(&count, nullptr);
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  push_constant_bocks.resize(count);
+  result = obj.EnumeratePushConstantBlocks(&count, push_constant_bocks.data());
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  if (count > 0) {
+    os << "\n";
+    os << "\n";
+    os << "\n";
+    os << t << "Push constant blocks: " << count << "\n\n";
+    for (size_t i = 0; i < push_constant_bocks.size(); ++i) {
+      auto p_block = push_constant_bocks[i];
+      os << tt << i << ":"
+         << "\n";
+      StreamWritePushConstantsBlock(os, *p_block, flatten_cbuffers, ttt);
+    }
+  }
+
+  count = 0;
+  result = obj.EnumerateDescriptorBindings(&count, nullptr);
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  bindings.resize(count);
+  result = obj.EnumerateDescriptorBindings(&count, bindings.data());
+  USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  std::sort(std::begin(bindings), std::end(bindings), [](SpvReflectDescriptorBinding* a, SpvReflectDescriptorBinding* b) -> bool {
+    if (a->set != b->set) {
+      return a->set < b->set;
+    }
+    return a->binding < b->binding;
+  });
+  if (count > 0) {
+    os << "\n";
+    os << "\n";
+    os << "\n";
+    os << t << "Descriptor bindings: " << count << "\n\n";
+    for (size_t i = 0; i < bindings.size(); ++i) {
+      auto p_binding = bindings[i];
+      USE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+      os << tt << "Binding"
+         << " " << p_binding->set << "." << p_binding->binding << ""
+         << "\n";
+      StreamWriteDescriptorBinding(os, *p_binding, true, flatten_cbuffers, ttt);
+      if (i < (count - 1)) {
+        os << "\n\n";
+      }
+    }
+  }
+}
+
+//////////////////////////////////
+
+SpvReflectToYaml::SpvReflectToYaml(const SpvReflectShaderModule& shader_module, uint32_t verbosity)
+    : sm_(shader_module), verbosity_(verbosity) {}
+
+void SpvReflectToYaml::WriteTypeDescription(std::ostream& os, const SpvReflectTypeDescription& td, uint32_t indent_level) {
+  // YAML anchors can only refer to points earlier in the doc, so child type
+  // descriptions must be processed before the parent.
+  if (!td.copied) {
+    for (uint32_t i = 0; i < td.member_count; ++i) {
+      WriteTypeDescription(os, td.members[i], indent_level);
+    }
+  }
+  const std::string t0 = Indent(indent_level);
+  const std::string t1 = Indent(indent_level + 1);
+  const std::string t2 = Indent(indent_level + 2);
+  const std::string t3 = Indent(indent_level + 3);
+  const std::string t4 = Indent(indent_level + 4);
+
+  // Determine the index of this type within the shader module's list.
+  uint32_t type_description_index = static_cast<uint32_t>(type_description_to_index_.size());
+  type_description_to_index_[&td] = type_description_index;
+
+  os << t0 << "- &td" << type_description_index << std::endl;
+  // typedef struct SpvReflectTypeDescription {
+  //   uint32_t                          id;
+  os << t1 << "id: " << td.id << std::endl;
+  //   SpvOp                             op;
+  os << t1 << "op: " << td.op << std::endl;
+  //   const char*                       type_name;
+  os << t1 << "type_name: " << SafeString(td.type_name) << std::endl;
+  //   const char*                       struct_member_name;
+  os << t1 << "struct_member_name: " << SafeString(td.struct_member_name) << std::endl;
+  //   SpvStorageClass                   storage_class;
+  os << t1 << "storage_class: " << td.storage_class << " # " << ToStringSpvStorageClass(td.storage_class) << std::endl;
+  //   SpvReflectTypeFlags               type_flags;
+  os << t1 << "type_flags: " << AsHexString(td.type_flags) << " # " << ToStringTypeFlags(td.type_flags) << std::endl;
+  //   SpvReflectDecorationFlags         decoration_flags;
+  os << t1 << "decoration_flags: " << AsHexString(td.decoration_flags) << " # " << ToStringDecorationFlags(td.decoration_flags)
+     << std::endl;
+  //   struct Traits {
+  os << t1 << "traits:" << std::endl;
+  //     SpvReflectNumericTraits         numeric;
+  // typedef struct SpvReflectNumericTraits {
+  os << t2 << "numeric:" << std::endl;
+  //   struct Scalar {
+  //     uint32_t                        width;
+  //     uint32_t                        signedness;
+  //   } scalar;
+  os << t3 << "scalar: { ";
+  os << "width: " << td.traits.numeric.scalar.width << ", ";
+  os << "signedness: " << td.traits.numeric.scalar.signedness;
+  os << " }" << std::endl;
+  //   struct Vector {
+  //     uint32_t                        component_count;
+  //   } vector;
+  os << t3 << "vector: { ";
+  os << "component_count: " << td.traits.numeric.vector.component_count;
+  os << " }" << std::endl;
+  //   struct Matrix {
+  //     uint32_t                        column_count;
+  //     uint32_t                        row_count;
+  //     uint32_t                        stride; // Measured in bytes
+  //   } matrix;
+  os << t3 << "matrix: { ";
+  os << "column_count: " << td.traits.numeric.matrix.column_count << ", ";
+  os << "row_count: " << td.traits.numeric.matrix.row_count << ", ";
+  ;
+  os << "stride: " << td.traits.numeric.matrix.stride;
+  os << " }" << std::endl;
+  // } SpvReflectNumericTraits;
+
+  //     SpvReflectImageTraits           image;
+  os << t2 << "image: { ";
+  // typedef struct SpvReflectImageTraits {
+  //   SpvDim                            dim;
+  os << "dim: " << td.traits.image.dim << ", ";
+  //   uint32_t                          depth;
+  os << "depth: " << td.traits.image.depth << ", ";
+  //   uint32_t                          arrayed;
+  os << "arrayed: " << td.traits.image.arrayed << ", ";
+  //   uint32_t                          ms;
+  os << "ms: " << td.traits.image.ms << ", ";
+  //   uint32_t                          sampled;
+  os << "sampled: " << td.traits.image.sampled << ", ";
+  //   SpvImageFormat                    image_format;
+  os << "image_format: " << td.traits.image.image_format;
+  // } SpvReflectImageTraits;
+  os << " }"
+     << " # dim=" << ToStringSpvDim(td.traits.image.dim) << " image_format=" << ToStringSpvImageFormat(td.traits.image.image_format)
+     << std::endl;
+
+  //     SpvReflectArrayTraits           array;
+  os << t2 << "array: { ";
+  // typedef struct SpvReflectArrayTraits {
+  //   uint32_t                          dims_count;
+  os << "dims_count: " << td.traits.array.dims_count << ", ";
+  //   uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+  os << "dims: [";
+  for (uint32_t i_dim = 0; i_dim < td.traits.array.dims_count; ++i_dim) {
+    os << td.traits.array.dims[i_dim] << ",";
+  }
+  os << "], ";
+  //   uint32_t                          stride; // Measured in bytes
+  os << "stride: " << td.traits.array.stride;
+  // } SpvReflectArrayTraits;
+  os << " }" << std::endl;
+  //   } traits;
+
+  //   uint32_t                          member_count;
+  os << t1 << "member_count: " << td.member_count << std::endl;
+  //   struct SpvReflectTypeDescription* members;
+  os << t1 << "members:" << std::endl;
+  if (td.copied) {
+    os << t1 << "- [forward pointer]" << std::endl;
+  } else {
+    for (uint32_t i_member = 0; i_member < td.member_count; ++i_member) {
+      os << t2 << "- *td" << type_description_to_index_[&(td.members[i_member])] << std::endl;
+    }
+  }
+  // } SpvReflectTypeDescription;
+}
+
+void SpvReflectToYaml::WriteBlockVariable(std::ostream& os, const SpvReflectBlockVariable& bv, uint32_t indent_level) {
+  if ((bv.flags & SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY)) {
+    return;  // catches recursive buffer references
+  }
+
+  for (uint32_t i = 0; i < bv.member_count; ++i) {
+    WriteBlockVariable(os, bv.members[i], indent_level);
+  }
+
+  const std::string t0 = Indent(indent_level);
+  const std::string t1 = Indent(indent_level + 1);
+  const std::string t2 = Indent(indent_level + 2);
+  const std::string t3 = Indent(indent_level + 3);
+
+  assert(block_variable_to_index_.find(&bv) == block_variable_to_index_.end());
+  uint32_t block_variable_index = static_cast<uint32_t>(block_variable_to_index_.size());
+  block_variable_to_index_[&bv] = block_variable_index;
+
+  os << t0 << "- &bv" << block_variable_index << std::endl;
+  // typedef struct SpvReflectBlockVariable {
+  //   const char*                       name;
+  os << t1 << "name: " << SafeString(bv.name) << std::endl;
+  //   uint32_t                          offset;           // Measured in bytes
+  os << t1 << "offset: " << bv.offset << std::endl;
+  //   uint32_t                          absolute_offset;  // Measured in bytes
+  os << t1 << "absolute_offset: " << bv.absolute_offset << std::endl;
+  //   uint32_t                          size;             // Measured in bytes
+  os << t1 << "size: " << bv.size << std::endl;
+  //   uint32_t                          padded_size;      // Measured in bytes
+  os << t1 << "padded_size: " << bv.padded_size << std::endl;
+  //   SpvReflectDecorationFlags         decoration_flags;
+  os << t1 << "decorations: " << AsHexString(bv.decoration_flags) << " # " << ToStringDecorationFlags(bv.decoration_flags)
+     << std::endl;
+  //   SpvReflectNumericTraits           numeric;
+  // typedef struct SpvReflectNumericTraits {
+  os << t1 << "numeric:" << std::endl;
+  //   struct Scalar {
+  //     uint32_t                        width;
+  //     uint32_t                        signedness;
+  //   } scalar;
+  os << t2 << "scalar: { ";
+  os << "width: " << bv.numeric.scalar.width << ", ";
+  os << "signedness: " << bv.numeric.scalar.signedness << " }" << std::endl;
+  //   struct Vector {
+  //     uint32_t                        component_count;
+  //   } vector;
+  os << t2 << "vector: { ";
+  os << "component_count: " << bv.numeric.vector.component_count << " }" << std::endl;
+  //   struct Matrix {
+  //     uint32_t                        column_count;
+  //     uint32_t                        row_count;
+  //     uint32_t                        stride; // Measured in bytes
+  //   } matrix;
+  os << t2 << "matrix: { ";
+  os << "column_count: " << bv.numeric.matrix.column_count << ", ";
+  os << "row_count: " << bv.numeric.matrix.row_count << ", ";
+  os << "stride: " << bv.numeric.matrix.stride << " }" << std::endl;
+  // } SpvReflectNumericTraits;
+
+  //     SpvReflectArrayTraits           array;
+  os << t1 << "array: { ";
+  // typedef struct SpvReflectArrayTraits {
+  //   uint32_t                          dims_count;
+  os << "dims_count: " << bv.array.dims_count << ", ";
+  //   uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+  os << "dims: [";
+  for (uint32_t i_dim = 0; i_dim < bv.array.dims_count; ++i_dim) {
+    os << bv.array.dims[i_dim] << ",";
+  }
+  os << "], ";
+  //   uint32_t                          stride; // Measured in bytes
+  os << "stride: " << bv.array.stride;
+  // } SpvReflectArrayTraits;
+  os << " }" << std::endl;
+
+  //   uint32_t                          member_count;
+  os << t1 << "member_count: " << bv.member_count << std::endl;
+  //   struct SpvReflectBlockVariable*   members;
+  os << t1 << "members:" << std::endl;
+  for (uint32_t i = 0; i < bv.member_count; ++i) {
+    auto itor = block_variable_to_index_.find(&bv.members[i]);
+    if (itor != block_variable_to_index_.end()) {
+      os << t2 << "- *bv" << itor->second << std::endl;
+    } else {
+      os << t2 << "- [recursive]" << std::endl;
+    }
+  }
+  if (verbosity_ >= 1) {
+    //   SpvReflectTypeDescription*        type_description;
+    if (bv.type_description == nullptr) {
+      os << t1 << "type_description:" << std::endl;
+    } else {
+      auto itor = type_description_to_index_.find(bv.type_description);
+      assert(itor != type_description_to_index_.end());
+      os << t1 << "type_description: *td" << itor->second << std::endl;
+    }
+  }
+  // } SpvReflectBlockVariable;
+}
+
+void SpvReflectToYaml::WriteDescriptorBinding(std::ostream& os, const SpvReflectDescriptorBinding& db, uint32_t indent_level) {
+  if (db.uav_counter_binding != nullptr) {
+    auto itor = descriptor_binding_to_index_.find(db.uav_counter_binding);
+    if (itor == descriptor_binding_to_index_.end()) {
+      WriteDescriptorBinding(os, *(db.uav_counter_binding), indent_level);
+    }
+  }
+
+  const std::string t0 = Indent(indent_level);
+  const std::string t1 = Indent(indent_level + 1);
+  const std::string t2 = Indent(indent_level + 2);
+  const std::string t3 = Indent(indent_level + 3);
+
+  // A binding's UAV binding later may appear later in the table than the
+  // binding itself, in which case we've already output entries for both
+  // bindings, and can just write another reference here.
+  {
+    auto itor = descriptor_binding_to_index_.find(&db);
+    if (itor != descriptor_binding_to_index_.end()) {
+      os << t0 << "- *db" << itor->second << std::endl;
+      return;
+    }
+  }
+
+  uint32_t descriptor_binding_index = static_cast<uint32_t>(descriptor_binding_to_index_.size());
+  descriptor_binding_to_index_[&db] = descriptor_binding_index;
+
+  os << t0 << "- &db" << descriptor_binding_index << std::endl;
+  // typedef struct SpvReflectDescriptorBinding {
+  //   uint32_t                            spirv_id;
+  os << t1 << "spirv_id: " << db.spirv_id << std::endl;
+  //   const char*                         name;
+  os << t1 << "name: " << SafeString(db.name) << std::endl;
+  //   uint32_t                            binding;
+  os << t1 << "binding: " << db.binding << std::endl;
+  //   uint32_t                            input_attachment_index;
+  os << t1 << "input_attachment_index: " << db.input_attachment_index << std::endl;
+  //   uint32_t                            set;
+  os << t1 << "set: " << db.set << std::endl;
+  //   SpvReflectDecorationFlags           decoration_flags;
+  os << t1 << "decoration_flags: " << AsHexString(db.decoration_flags) << " # " << ToStringDecorationFlags(db.decoration_flags)
+     << std::endl;
+  //   SpvReflectDescriptorType            descriptor_type;
+  os << t1 << "descriptor_type: " << db.descriptor_type << " # " << ToStringDescriptorType(db.descriptor_type) << std::endl;
+  //   SpvReflectResourceType              resource_type;
+  os << t1 << "resource_type: " << db.resource_type << " # " << ToStringResourceType(db.resource_type) << std::endl;
+  //   SpvReflectImageTraits           image;
+  os << t1 << "image: { ";
+  // typedef struct SpvReflectImageTraits {
+  //   SpvDim                            dim;
+  os << "dim: " << db.image.dim << ", ";
+  //   uint32_t                          depth;
+  os << "depth: " << db.image.depth << ", ";
+  //   uint32_t                          arrayed;
+  os << "arrayed: " << db.image.arrayed << ", ";
+  //   uint32_t                          ms;
+  os << "ms: " << db.image.ms << ", ";
+  //   uint32_t                          sampled;
+  os << "sampled: " << db.image.sampled << ", ";
+  //   SpvImageFormat                    image_format;
+  os << "image_format: " << db.image.image_format;
+  // } SpvReflectImageTraits;
+  os << " }"
+     << " # dim=" << ToStringSpvDim(db.image.dim) << " image_format=" << ToStringSpvImageFormat(db.image.image_format) << std::endl;
+
+  //   SpvReflectBlockVariable             block;
+  {
+    auto itor = block_variable_to_index_.find(&db.block);
+    assert(itor != block_variable_to_index_.end());
+    os << t1 << "block: *bv" << itor->second << " # " << SafeString(db.block.name) << std::endl;
+  }
+  //   SpvReflectBindingArrayTraits        array;
+  os << t1 << "array: { ";
+  // typedef struct SpvReflectBindingArrayTraits {
+  //   uint32_t                          dims_count;
+  os << "dims_count: " << db.array.dims_count << ", ";
+  //   uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+  os << "dims: [";
+  for (uint32_t i_dim = 0; i_dim < db.array.dims_count; ++i_dim) {
+    os << db.array.dims[i_dim] << ",";
+  }
+  // } SpvReflectBindingArrayTraits;
+  os << "] }" << std::endl;
+
+  //   uint32_t                            accessed;
+  os << t1 << "accessed: " << db.accessed << std::endl;
+
+  //   uint32_t                            uav_counter_id;
+  os << t1 << "uav_counter_id: " << db.uav_counter_id << std::endl;
+  //   struct SpvReflectDescriptorBinding* uav_counter_binding;
+  if (db.uav_counter_binding == nullptr) {
+    os << t1 << "uav_counter_binding:" << std::endl;
+  } else {
+    auto itor = descriptor_binding_to_index_.find(db.uav_counter_binding);
+    assert(itor != descriptor_binding_to_index_.end());
+    os << t1 << "uav_counter_binding: *db" << itor->second << " # " << SafeString(db.uav_counter_binding->name) << std::endl;
+  }
+
+  if (db.byte_address_buffer_offset_count > 0) {
+    os << t1 << "ByteAddressBuffer offsets: [";
+    for (uint32_t i = 0; i < db.byte_address_buffer_offset_count; i++) {
+      os << db.byte_address_buffer_offsets[i];
+      if (i < (db.byte_address_buffer_offset_count - 1)) {
+        os << ", ";
+      }
+    }
+    os << "]\n";
+  }
+
+  if (verbosity_ >= 1) {
+    //   SpvReflectTypeDescription*        type_description;
+    if (db.type_description == nullptr) {
+      os << t1 << "type_description:" << std::endl;
+    } else {
+      auto itor = type_description_to_index_.find(db.type_description);
+      assert(itor != type_description_to_index_.end());
+      os << t1 << "type_description: *td" << itor->second << std::endl;
+    }
+  }
+  //   struct {
+  //     uint32_t                        binding;
+  //     uint32_t                        set;
+  //   } word_offset;
+  os << t1 << "word_offset: { binding: " << db.word_offset.binding;
+  os << ", set: " << db.word_offset.set << " }" << std::endl;
+
+  if (db.user_type != SPV_REFLECT_USER_TYPE_INVALID) {
+    os << t1 << "user_type: " << ToStringUserType(db.user_type) << std::endl;
+  }
+  // } SpvReflectDescriptorBinding;
+}
+
+void SpvReflectToYaml::WriteInterfaceVariable(std::ostream& os, const SpvReflectInterfaceVariable& iv, uint32_t indent_level) {
+  for (uint32_t i = 0; i < iv.member_count; ++i) {
+    assert(interface_variable_to_index_.find(&iv.members[i]) == interface_variable_to_index_.end());
+    WriteInterfaceVariable(os, iv.members[i], indent_level);
+  }
+
+  const std::string t0 = Indent(indent_level);
+  const std::string t1 = Indent(indent_level + 1);
+  const std::string t2 = Indent(indent_level + 2);
+  const std::string t3 = Indent(indent_level + 3);
+
+  uint32_t interface_variable_index = static_cast<uint32_t>(interface_variable_to_index_.size());
+  interface_variable_to_index_[&iv] = interface_variable_index;
+
+  // typedef struct SpvReflectInterfaceVariable {
+  os << t0 << "- &iv" << interface_variable_index << std::endl;
+  //   uint32_t                            spirv_id;
+  os << t1 << "spirv_id: " << iv.spirv_id << std::endl;
+  //   const char*                         name;
+  os << t1 << "name: " << SafeString(iv.name) << std::endl;
+  //   uint32_t                            location;
+  os << t1 << "location: " << iv.location << std::endl;
+  //   SpvStorageClass                     storage_class;
+  os << t1 << "storage_class: " << iv.storage_class << " # " << ToStringSpvStorageClass(iv.storage_class) << std::endl;
+  //   const char*                         semantic;
+  os << t1 << "semantic: " << SafeString(iv.semantic) << std::endl;
+  //   SpvReflectDecorationFlags           decoration_flags;
+  os << t1 << "decoration_flags: " << AsHexString(iv.decoration_flags) << " # " << ToStringDecorationFlags(iv.decoration_flags)
+     << std::endl;
+  //   SpvBuiltIn                          built_in;
+  os << t1 << "built_in: ";
+  if (iv.decoration_flags & SPV_REFLECT_DECORATION_BLOCK) {
+    for (uint32_t i = 0; i < iv.member_count; i++) {
+      os << iv.members[i].built_in;
+      if (i < (iv.member_count - 1)) {
+        os << ", ";
+      }
+    }
+  } else {
+    os << iv.built_in;
+  }
+  os << " # " << ToStringSpvBuiltIn(iv, false) << std::endl;
+  //   SpvReflectNumericTraits             numeric;
+  // typedef struct SpvReflectNumericTraits {
+  os << t1 << "numeric:" << std::endl;
+  //   struct Scalar {
+  //     uint32_t                        width;
+  //     uint32_t                        signedness;
+  //   } scalar;
+  os << t2 << "scalar: { ";
+  os << "width: " << iv.numeric.scalar.width << ", ";
+  os << "signedness: " << iv.numeric.scalar.signedness << " }" << std::endl;
+  //   struct Vector {
+  //     uint32_t                        component_count;
+  //   } vector;
+  os << t2 << "vector: { ";
+  os << "component_count: " << iv.numeric.vector.component_count << " }" << std::endl;
+  //   struct Matrix {
+  //     uint32_t                        column_count;
+  //     uint32_t                        row_count;
+  //     uint32_t                        stride; // Measured in bytes
+  //   } matrix;
+  os << t2 << "matrix: { ";
+  os << "column_count: " << iv.numeric.matrix.column_count << ", ";
+  os << "row_count: " << iv.numeric.matrix.row_count << ", ";
+  os << "stride: " << iv.numeric.matrix.stride << " }" << std::endl;
+  // } SpvReflectNumericTraits;
+
+  //     SpvReflectArrayTraits           array;
+  os << t1 << "array: { ";
+  // typedef struct SpvReflectArrayTraits {
+  //   uint32_t                          dims_count;
+  os << "dims_count: " << iv.array.dims_count << ", ";
+  //   uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+  os << "dims: [";
+  for (uint32_t i_dim = 0; i_dim < iv.array.dims_count; ++i_dim) {
+    os << iv.array.dims[i_dim] << ",";
+  }
+  os << "], ";
+  //   uint32_t                          stride; // Measured in bytes
+  os << "stride: " << iv.array.stride;
+  // } SpvReflectArrayTraits;
+  os << " }" << std::endl;
+
+  //   uint32_t                            member_count;
+  os << t1 << "member_count: " << iv.member_count << std::endl;
+  //   struct SpvReflectInterfaceVariable* members;
+  os << t1 << "members:" << std::endl;
+  for (uint32_t i = 0; i < iv.member_count; ++i) {
+    auto itor = interface_variable_to_index_.find(&iv.members[i]);
+    assert(itor != interface_variable_to_index_.end());
+    os << t2 << "- *iv" << itor->second << " # " << SafeString(iv.members[i].name) << std::endl;
+  }
+
+  //   SpvReflectFormat                    format;
+  os << t1 << "format: " << iv.format << " # " << ToStringFormat(iv.format) << std::endl;
+
+  if (verbosity_ >= 1) {
+    //   SpvReflectTypeDescription*        type_description;
+    if (!iv.type_description) {
+      os << t1 << "type_description:" << std::endl;
+    } else {
+      auto itor = type_description_to_index_.find(iv.type_description);
+      assert(itor != type_description_to_index_.end());
+      os << t1 << "type_description: *td" << itor->second << std::endl;
+    }
+  }
+
+  //   struct {
+  //     uint32_t                        location;
+  //   } word_offset;
+  os << t1 << "word_offset: { location: " << iv.word_offset.location << " }" << std::endl;
+
+  // } SpvReflectInterfaceVariable;
+}
+
+void SpvReflectToYaml::WriteBlockVariableTypes(std::ostream& os, const SpvReflectBlockVariable& bv, uint32_t indent_level) {
+  const auto* td = bv.type_description;
+  if (td && type_description_to_index_.find(td) == type_description_to_index_.end()) {
+    WriteTypeDescription(os, *td, indent_level);
+  }
+
+  if (bv.flags & SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY) {
+    return;
+  }
+  for (uint32_t i = 0; i < bv.member_count; ++i) {
+    WriteBlockVariableTypes(os, bv.members[i], indent_level);
+  }
+}
+void SpvReflectToYaml::WriteDescriptorBindingTypes(std::ostream& os, const SpvReflectDescriptorBinding& db, uint32_t indent_level) {
+  WriteBlockVariableTypes(os, db.block, indent_level);
+
+  if (db.uav_counter_binding) {
+    WriteDescriptorBindingTypes(os, *(db.uav_counter_binding), indent_level);
+  }
+
+  const auto* td = db.type_description;
+  if (td && type_description_to_index_.find(td) == type_description_to_index_.end()) {
+    WriteTypeDescription(os, *td, indent_level);
+  }
+}
+void SpvReflectToYaml::WriteInterfaceVariableTypes(std::ostream& os, const SpvReflectInterfaceVariable& iv, uint32_t indent_level) {
+  const auto* td = iv.type_description;
+  if (td && type_description_to_index_.find(td) == type_description_to_index_.end()) {
+    WriteTypeDescription(os, *td, indent_level);
+  }
+
+  for (uint32_t i = 0; i < iv.member_count; ++i) {
+    WriteInterfaceVariableTypes(os, iv.members[i], indent_level);
+  }
+}
+
+void SpvReflectToYaml::Write(std::ostream& os) {
+  if (!sm_._internal) {
+    return;
+  }
+
+  uint32_t indent_level = 0;
+  const std::string t0 = Indent(indent_level);
+  const std::string t1 = Indent(indent_level + 1);
+  const std::string t2 = Indent(indent_level + 2);
+  const std::string t3 = Indent(indent_level + 3);
+
+  os << "%YAML 1.0" << std::endl;
+  os << "---" << std::endl;
+
+  type_description_to_index_.clear();
+  if (verbosity_ >= 2) {
+    os << t0 << "all_type_descriptions:" << std::endl;
+    // Write the entire internal type_description table; all type descriptions
+    // are reachable from there, though most of them are purely internal & not
+    // referenced by any of the public-facing structures.
+    for (size_t i = 0; i < sm_._internal->type_description_count; ++i) {
+      WriteTypeDescription(os, sm_._internal->type_descriptions[i], indent_level + 1);
+    }
+  } else if (verbosity_ >= 1) {
+    os << t0 << "all_type_descriptions:" << std::endl;
+    // Iterate through all public-facing structures and write any type
+    // descriptions we find (and their children).
+    for (uint32_t i = 0; i < sm_.descriptor_binding_count; ++i) {
+      WriteDescriptorBindingTypes(os, sm_.descriptor_bindings[i], indent_level + 1);
+    }
+    for (uint32_t i = 0; i < sm_.push_constant_block_count; ++i) {
+      WriteBlockVariableTypes(os, sm_.push_constant_blocks[i], indent_level + 1);
+    }
+    for (uint32_t i = 0; i < sm_.input_variable_count; ++i) {
+      WriteInterfaceVariableTypes(os, *sm_.input_variables[i], indent_level + 1);
+    }
+    for (uint32_t i = 0; i < sm_.output_variable_count; ++i) {
+      WriteInterfaceVariableTypes(os, *sm_.output_variables[i], indent_level + 1);
+    }
+  }
+
+  block_variable_to_index_.clear();
+  os << t0 << "all_block_variables:" << std::endl;
+  for (uint32_t i = 0; i < sm_.descriptor_binding_count; ++i) {
+    WriteBlockVariable(os, sm_.descriptor_bindings[i].block, indent_level + 1);
+  }
+  for (uint32_t i = 0; i < sm_.push_constant_block_count; ++i) {
+    WriteBlockVariable(os, sm_.push_constant_blocks[i], indent_level + 1);
+  }
+
+  descriptor_binding_to_index_.clear();
+  os << t0 << "all_descriptor_bindings:" << std::endl;
+  for (uint32_t i = 0; i < sm_.descriptor_binding_count; ++i) {
+    WriteDescriptorBinding(os, sm_.descriptor_bindings[i], indent_level + 1);
+  }
+
+  interface_variable_to_index_.clear();
+  os << t0 << "all_interface_variables:" << std::endl;
+  for (uint32_t i = 0; i < sm_.input_variable_count; ++i) {
+    WriteInterfaceVariable(os, *sm_.input_variables[i], indent_level + 1);
+  }
+  for (uint32_t i = 0; i < sm_.output_variable_count; ++i) {
+    WriteInterfaceVariable(os, *sm_.output_variables[i], indent_level + 1);
+  }
+
+  // struct SpvReflectShaderModule {
+  os << t0 << "module:" << std::endl;
+  // uint16_t                          generator;
+  os << t1 << "generator: " << sm_.generator << " # " << ToStringGenerator(sm_.generator) << std::endl;
+  // const char*                       entry_point_name;
+  os << t1 << "entry_point_name: " << SafeString(sm_.entry_point_name) << std::endl;
+  // uint32_t                          entry_point_id;
+  os << t1 << "entry_point_id: " << sm_.entry_point_id << std::endl;
+  // SpvSourceLanguage                 source_language;
+  os << t1 << "source_language: " << sm_.source_language << " # " << ToStringSpvSourceLanguage(sm_.source_language) << std::endl;
+  // uint32_t                          source_language_version;
+  os << t1 << "source_language_version: " << sm_.source_language_version << std::endl;
+  // SpvExecutionModel                 spirv_execution_model;
+  os << t1 << "spirv_execution_model: " << sm_.spirv_execution_model << " # "
+     << ToStringSpvExecutionModel(sm_.spirv_execution_model) << std::endl;
+  // SpvShaderStageFlagBits             shader_stage;
+  os << t1 << "shader_stage: " << AsHexString(sm_.shader_stage) << " # " << ToStringShaderStage(sm_.shader_stage) << std::endl;
+  // uint32_t                          descriptor_binding_count;
+  os << t1 << "descriptor_binding_count: " << sm_.descriptor_binding_count << std::endl;
+  // SpvReflectDescriptorBinding*      descriptor_bindings;
+  os << t1 << "descriptor_bindings:" << std::endl;
+  for (uint32_t i = 0; i < sm_.descriptor_binding_count; ++i) {
+    auto itor = descriptor_binding_to_index_.find(&sm_.descriptor_bindings[i]);
+    assert(itor != descriptor_binding_to_index_.end());
+    os << t2 << "- *db" << itor->second << " # " << SafeString(sm_.descriptor_bindings[i].name) << std::endl;
+  }
+  // uint32_t                          descriptor_set_count;
+  os << t1 << "descriptor_set_count: " << sm_.descriptor_set_count << std::endl;
+  // SpvReflectDescriptorSet descriptor_sets[SPV_REFLECT_MAX_DESCRIPTOR_SETS];
+  os << t1 << "descriptor_sets:" << std::endl;
+  for (uint32_t i_set = 0; i_set < sm_.descriptor_set_count; ++i_set) {
+    // typedef struct SpvReflectDescriptorSet {
+    const auto& dset = sm_.descriptor_sets[i_set];
+    //   uint32_t                          set;
+    os << t1 << "- "
+       << "set: " << dset.set << std::endl;
+    //   uint32_t                          binding_count;
+    os << t2 << "binding_count: " << dset.binding_count << std::endl;
+    //   SpvReflectDescriptorBinding**     bindings;
+    os << t2 << "bindings:" << std::endl;
+    for (uint32_t i_binding = 0; i_binding < dset.binding_count; ++i_binding) {
+      auto itor = descriptor_binding_to_index_.find(dset.bindings[i_binding]);
+      assert(itor != descriptor_binding_to_index_.end());
+      os << t3 << "- *db" << itor->second << " # " << SafeString(dset.bindings[i_binding]->name) << std::endl;
+    }
+    // } SpvReflectDescriptorSet;
+  }
+  // uint32_t                          input_variable_count;
+  os << t1 << "input_variable_count: " << sm_.input_variable_count << ",\n";
+  // SpvReflectInterfaceVariable*      input_variables;
+  os << t1 << "input_variables:" << std::endl;
+  for (uint32_t i = 0; i < sm_.input_variable_count; ++i) {
+    auto itor = interface_variable_to_index_.find(sm_.input_variables[i]);
+    assert(itor != interface_variable_to_index_.end());
+    os << t2 << "- *iv" << itor->second << " # " << SafeString(sm_.input_variables[i]->name) << std::endl;
+  }
+  // uint32_t                          output_variable_count;
+  os << t1 << "output_variable_count: " << sm_.output_variable_count << ",\n";
+  // SpvReflectInterfaceVariable*      output_variables;
+  os << t1 << "output_variables:" << std::endl;
+  for (uint32_t i = 0; i < sm_.output_variable_count; ++i) {
+    auto itor = interface_variable_to_index_.find(sm_.output_variables[i]);
+    assert(itor != interface_variable_to_index_.end());
+    os << t2 << "- *iv" << itor->second << " # " << SafeString(sm_.output_variables[i]->name) << std::endl;
+  }
+  // uint32_t                          push_constant_count;
+  os << t1 << "push_constant_count: " << sm_.push_constant_block_count << ",\n";
+  // SpvReflectBlockVariable*          push_constants;
+  os << t1 << "push_constants:" << std::endl;
+  for (uint32_t i = 0; i < sm_.push_constant_block_count; ++i) {
+    auto itor = block_variable_to_index_.find(&sm_.push_constant_blocks[i]);
+    assert(itor != block_variable_to_index_.end());
+    os << t2 << "- *bv" << itor->second << " # " << SafeString(sm_.push_constant_blocks[i].name) << std::endl;
+  }
+
+  // uint32_t                            spec_constant_count;
+  os << t1 << "specialization_constant_count: " << sm_.spec_constant_count << ",\n";
+  // SpvReflectSpecializationConstant*   spec_constants;
+  os << t1 << "specialization_constants:" << std::endl;
+  for (uint32_t i = 0; i < sm_.spec_constant_count; ++i) {
+    os << t3 << "- name: " << SafeString(sm_.spec_constants[i].name) << std::endl;
+    os << t3 << "  spirv_id: " << sm_.spec_constants[i].spirv_id << std::endl;
+    os << t3 << "  constant_id: " << sm_.spec_constants[i].constant_id << std::endl;
+  }
+
+  if (verbosity_ >= 2) {
+    // struct Internal {
+    os << t1 << "_internal:" << std::endl;
+    if (sm_._internal) {
+      //   size_t                          spirv_size;
+      os << t2 << "spirv_size: " << sm_._internal->spirv_size << std::endl;
+      //   uint32_t*                       spirv_code;
+      os << t2 << "spirv_code: [";
+      for (size_t i = 0; i < sm_._internal->spirv_word_count; ++i) {
+        if ((i % 6) == 0) {
+          os << std::endl << t3;
+        }
+        os << AsHexString(sm_._internal->spirv_code[i]) << ",";
+      }
+      os << "]" << std::endl;
+      //   uint32_t                        spirv_word_count;
+      os << t2 << "spirv_word_count: " << sm_._internal->spirv_word_count << std::endl;
+      //   size_t                          type_description_count;
+      os << t2 << "type_description_count: " << sm_._internal->type_description_count << std::endl;
+      //   SpvReflectTypeDescription*      type_descriptions;
+      os << t2 << "type_descriptions:" << std::endl;
+      for (uint32_t i = 0; i < sm_._internal->type_description_count; ++i) {
+        auto itor = type_description_to_index_.find(&sm_._internal->type_descriptions[i]);
+        assert(itor != type_description_to_index_.end());
+        os << t3 << "- *td" << itor->second << std::endl;
+      }
+    }
+    // } * _internal;
+  }
+
+  os << "..." << std::endl;
+}
diff --git a/third-party/SPIRV-Reflect/common/output_stream.h b/third-party/SPIRV-Reflect/common/output_stream.h
new file mode 100644 (file)
index 0000000..d29014a
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef SPIRV_REFLECT_OUTPUT_STREAM_H
+#define SPIRV_REFLECT_OUTPUT_STREAM_H
+
+#include <map>
+#include <ostream>
+#include <string>
+
+#include "spirv_reflect.h"
+
+std::string ToStringSpvSourceLanguage(SpvSourceLanguage lang);
+std::string ToStringSpvExecutionModel(SpvExecutionModel model);
+std::string ToStringSpvStorageClass(SpvStorageClass storage_class);
+std::string ToStringSpvDim(SpvDim dim);
+std::string ToStringSpvBuiltIn(const SpvReflectInterfaceVariable& variable, bool preface);
+std::string ToStringSpvImageFormat(SpvImageFormat fmt);
+
+std::string ToStringGenerator(SpvReflectGenerator generator);
+std::string ToStringShaderStage(SpvReflectShaderStageFlagBits stage);
+std::string ToStringResourceType(SpvReflectResourceType type);
+std::string ToStringDescriptorType(SpvReflectDescriptorType value);
+std::string ToStringTypeFlags(SpvReflectTypeFlags type_flags);
+std::string ToStringDecorationFlags(SpvReflectDecorationFlags decoration_flags);
+std::string ToStringDescriptorType(SpvReflectDescriptorType value);
+std::string ToStringFormat(SpvReflectFormat fmt);
+std::string ToStringComponentType(const SpvReflectTypeDescription& type, uint32_t member_decoration_flags);
+std::string ToStringType(SpvSourceLanguage src_lang, const SpvReflectTypeDescription& type);
+
+// std::ostream& operator<<(std::ostream& os, const spv_reflect::ShaderModule& obj);
+void WriteReflection(const spv_reflect::ShaderModule& obj, bool flatten_cbuffers, std::ostream& os);
+
+class SpvReflectToYaml {
+ public:
+  // verbosity = 0: top-level tables only (module, block variables, interface variables, descriptor bindings).
+  // verbosity = 1: everything above, plus type description tables for all public objects.
+  // verbosity = 2: everything above, plus SPIRV bytecode and full type description table. HUGE.
+  explicit SpvReflectToYaml(const SpvReflectShaderModule& shader_module, uint32_t verbosity = 0);
+
+  friend std::ostream& operator<<(std::ostream& os, SpvReflectToYaml& to_yaml) {
+    to_yaml.Write(os);
+    return os;
+  }
+
+ private:
+  void Write(std::ostream& os);
+
+  SpvReflectToYaml(const SpvReflectToYaml&) = delete;
+  SpvReflectToYaml(const SpvReflectToYaml&&) = delete;
+  static std::string Indent(uint32_t level) { return std::string(2 * level, ' '); }
+  static std::string SafeString(const char* str) { return str ? (std::string("\"") + str + "\"") : ""; }
+  void WriteTypeDescription(std::ostream& os, const SpvReflectTypeDescription& td, uint32_t indent_level);
+  void WriteBlockVariable(std::ostream& os, const SpvReflectBlockVariable& bv, uint32_t indent_level);
+  void WriteDescriptorBinding(std::ostream& os, const SpvReflectDescriptorBinding& db, uint32_t indent_level);
+  void WriteInterfaceVariable(std::ostream& os, const SpvReflectInterfaceVariable& iv, uint32_t indent_level);
+
+  // Write all SpvReflectTypeDescription objects reachable from the specified objects, if they haven't been
+  // written already.
+  void WriteBlockVariableTypes(std::ostream& os, const SpvReflectBlockVariable& bv, uint32_t indent_level);
+  void WriteDescriptorBindingTypes(std::ostream& os, const SpvReflectDescriptorBinding& db, uint32_t indent_level);
+  void WriteInterfaceVariableTypes(std::ostream& os, const SpvReflectInterfaceVariable& iv, uint32_t indent_level);
+
+  const SpvReflectShaderModule& sm_;
+  uint32_t verbosity_ = 0;
+  std::map<const SpvReflectTypeDescription*, uint32_t> type_description_to_index_;
+  std::map<const SpvReflectBlockVariable*, uint32_t> block_variable_to_index_;
+  std::map<const SpvReflectDescriptorBinding*, uint32_t> descriptor_binding_to_index_;
+  std::map<const SpvReflectInterfaceVariable*, uint32_t> interface_variable_to_index_;
+};
+
+#endif
\ No newline at end of file
diff --git a/third-party/SPIRV-Reflect/include/spirv/unified1/spirv.h b/third-party/SPIRV-Reflect/include/spirv/unified1/spirv.h
new file mode 100644 (file)
index 0000000..65becfd
--- /dev/null
@@ -0,0 +1,4890 @@
+/*
+** Copyright (c) 2014-2020 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+/*
+** This header is automatically generated by the same tool that creates
+** the Binary Section of the SPIR-V specification.
+*/
+
+/*
+** Enumeration tokens for SPIR-V, in various styles:
+**   C, C++, C++11, JSON, Lua, Python, C#, D, Beef
+**
+** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+** - C++11 will use enum classes in the spv namespace, e.g.:
+*spv::SourceLanguage::GLSL
+** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+** - C# will use enum classes in the Specification class located in the "Spv"
+*namespace,
+**     e.g.: Spv.Specification.SourceLanguage.GLSL
+** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL
+** - Beef will use enum classes in the Specification class located in the "Spv"
+*namespace,
+**     e.g.: Spv.Specification.SourceLanguage.GLSL
+**
+** Some tokens act like mask values, which can be OR'd together,
+** while others are mutually exclusive.  The mask-like ones have
+** "Mask" in their name, and a parallel enum that has the shift
+** amount (1 << x) for each corresponding enumerant.
+*/
+
+#ifndef spirv_H
+#define spirv_H
+
+typedef unsigned int SpvId;
+
+#define SPV_VERSION 0x10600
+#define SPV_REVISION 1
+
+static const unsigned int SpvMagicNumber = 0x07230203;
+static const unsigned int SpvVersion = 0x00010600;
+static const unsigned int SpvRevision = 1;
+static const unsigned int SpvOpCodeMask = 0xffff;
+static const unsigned int SpvWordCountShift = 16;
+
+typedef enum SpvSourceLanguage_ {
+  SpvSourceLanguageUnknown = 0,
+  SpvSourceLanguageESSL = 1,
+  SpvSourceLanguageGLSL = 2,
+  SpvSourceLanguageOpenCL_C = 3,
+  SpvSourceLanguageOpenCL_CPP = 4,
+  SpvSourceLanguageHLSL = 5,
+  SpvSourceLanguageCPP_for_OpenCL = 6,
+  SpvSourceLanguageSYCL = 7,
+  SpvSourceLanguageHERO_C = 8,
+  SpvSourceLanguageNZSL = 9,
+  SpvSourceLanguageMax = 0x7fffffff,
+} SpvSourceLanguage;
+
+typedef enum SpvExecutionModel_ {
+  SpvExecutionModelVertex = 0,
+  SpvExecutionModelTessellationControl = 1,
+  SpvExecutionModelTessellationEvaluation = 2,
+  SpvExecutionModelGeometry = 3,
+  SpvExecutionModelFragment = 4,
+  SpvExecutionModelGLCompute = 5,
+  SpvExecutionModelKernel = 6,
+  SpvExecutionModelTaskNV = 5267,
+  SpvExecutionModelMeshNV = 5268,
+  SpvExecutionModelRayGenerationKHR = 5313,
+  SpvExecutionModelRayGenerationNV = 5313,
+  SpvExecutionModelIntersectionKHR = 5314,
+  SpvExecutionModelIntersectionNV = 5314,
+  SpvExecutionModelAnyHitKHR = 5315,
+  SpvExecutionModelAnyHitNV = 5315,
+  SpvExecutionModelClosestHitKHR = 5316,
+  SpvExecutionModelClosestHitNV = 5316,
+  SpvExecutionModelMissKHR = 5317,
+  SpvExecutionModelMissNV = 5317,
+  SpvExecutionModelCallableKHR = 5318,
+  SpvExecutionModelCallableNV = 5318,
+  SpvExecutionModelTaskEXT = 5364,
+  SpvExecutionModelMeshEXT = 5365,
+  SpvExecutionModelMax = 0x7fffffff,
+} SpvExecutionModel;
+
+typedef enum SpvAddressingModel_ {
+  SpvAddressingModelLogical = 0,
+  SpvAddressingModelPhysical32 = 1,
+  SpvAddressingModelPhysical64 = 2,
+  SpvAddressingModelPhysicalStorageBuffer64 = 5348,
+  SpvAddressingModelPhysicalStorageBuffer64EXT = 5348,
+  SpvAddressingModelMax = 0x7fffffff,
+} SpvAddressingModel;
+
+typedef enum SpvMemoryModel_ {
+  SpvMemoryModelSimple = 0,
+  SpvMemoryModelGLSL450 = 1,
+  SpvMemoryModelOpenCL = 2,
+  SpvMemoryModelVulkan = 3,
+  SpvMemoryModelVulkanKHR = 3,
+  SpvMemoryModelMax = 0x7fffffff,
+} SpvMemoryModel;
+
+typedef enum SpvExecutionMode_ {
+  SpvExecutionModeInvocations = 0,
+  SpvExecutionModeSpacingEqual = 1,
+  SpvExecutionModeSpacingFractionalEven = 2,
+  SpvExecutionModeSpacingFractionalOdd = 3,
+  SpvExecutionModeVertexOrderCw = 4,
+  SpvExecutionModeVertexOrderCcw = 5,
+  SpvExecutionModePixelCenterInteger = 6,
+  SpvExecutionModeOriginUpperLeft = 7,
+  SpvExecutionModeOriginLowerLeft = 8,
+  SpvExecutionModeEarlyFragmentTests = 9,
+  SpvExecutionModePointMode = 10,
+  SpvExecutionModeXfb = 11,
+  SpvExecutionModeDepthReplacing = 12,
+  SpvExecutionModeDepthGreater = 14,
+  SpvExecutionModeDepthLess = 15,
+  SpvExecutionModeDepthUnchanged = 16,
+  SpvExecutionModeLocalSize = 17,
+  SpvExecutionModeLocalSizeHint = 18,
+  SpvExecutionModeInputPoints = 19,
+  SpvExecutionModeInputLines = 20,
+  SpvExecutionModeInputLinesAdjacency = 21,
+  SpvExecutionModeTriangles = 22,
+  SpvExecutionModeInputTrianglesAdjacency = 23,
+  SpvExecutionModeQuads = 24,
+  SpvExecutionModeIsolines = 25,
+  SpvExecutionModeOutputVertices = 26,
+  SpvExecutionModeOutputPoints = 27,
+  SpvExecutionModeOutputLineStrip = 28,
+  SpvExecutionModeOutputTriangleStrip = 29,
+  SpvExecutionModeVecTypeHint = 30,
+  SpvExecutionModeContractionOff = 31,
+  SpvExecutionModeInitializer = 33,
+  SpvExecutionModeFinalizer = 34,
+  SpvExecutionModeSubgroupSize = 35,
+  SpvExecutionModeSubgroupsPerWorkgroup = 36,
+  SpvExecutionModeSubgroupsPerWorkgroupId = 37,
+  SpvExecutionModeLocalSizeId = 38,
+  SpvExecutionModeLocalSizeHintId = 39,
+  SpvExecutionModeNonCoherentColorAttachmentReadEXT = 4169,
+  SpvExecutionModeNonCoherentDepthAttachmentReadEXT = 4170,
+  SpvExecutionModeNonCoherentStencilAttachmentReadEXT = 4171,
+  SpvExecutionModeSubgroupUniformControlFlowKHR = 4421,
+  SpvExecutionModePostDepthCoverage = 4446,
+  SpvExecutionModeDenormPreserve = 4459,
+  SpvExecutionModeDenormFlushToZero = 4460,
+  SpvExecutionModeSignedZeroInfNanPreserve = 4461,
+  SpvExecutionModeRoundingModeRTE = 4462,
+  SpvExecutionModeRoundingModeRTZ = 4463,
+  SpvExecutionModeEarlyAndLateFragmentTestsAMD = 5017,
+  SpvExecutionModeStencilRefReplacingEXT = 5027,
+  SpvExecutionModeStencilRefUnchangedFrontAMD = 5079,
+  SpvExecutionModeStencilRefGreaterFrontAMD = 5080,
+  SpvExecutionModeStencilRefLessFrontAMD = 5081,
+  SpvExecutionModeStencilRefUnchangedBackAMD = 5082,
+  SpvExecutionModeStencilRefGreaterBackAMD = 5083,
+  SpvExecutionModeStencilRefLessBackAMD = 5084,
+  SpvExecutionModeOutputLinesEXT = 5269,
+  SpvExecutionModeOutputLinesNV = 5269,
+  SpvExecutionModeOutputPrimitivesEXT = 5270,
+  SpvExecutionModeOutputPrimitivesNV = 5270,
+  SpvExecutionModeDerivativeGroupQuadsNV = 5289,
+  SpvExecutionModeDerivativeGroupLinearNV = 5290,
+  SpvExecutionModeOutputTrianglesEXT = 5298,
+  SpvExecutionModeOutputTrianglesNV = 5298,
+  SpvExecutionModePixelInterlockOrderedEXT = 5366,
+  SpvExecutionModePixelInterlockUnorderedEXT = 5367,
+  SpvExecutionModeSampleInterlockOrderedEXT = 5368,
+  SpvExecutionModeSampleInterlockUnorderedEXT = 5369,
+  SpvExecutionModeShadingRateInterlockOrderedEXT = 5370,
+  SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371,
+  SpvExecutionModeSharedLocalMemorySizeINTEL = 5618,
+  SpvExecutionModeRoundingModeRTPINTEL = 5620,
+  SpvExecutionModeRoundingModeRTNINTEL = 5621,
+  SpvExecutionModeFloatingPointModeALTINTEL = 5622,
+  SpvExecutionModeFloatingPointModeIEEEINTEL = 5623,
+  SpvExecutionModeMaxWorkgroupSizeINTEL = 5893,
+  SpvExecutionModeMaxWorkDimINTEL = 5894,
+  SpvExecutionModeNoGlobalOffsetINTEL = 5895,
+  SpvExecutionModeNumSIMDWorkitemsINTEL = 5896,
+  SpvExecutionModeSchedulerTargetFmaxMhzINTEL = 5903,
+  SpvExecutionModeStreamingInterfaceINTEL = 6154,
+  SpvExecutionModeRegisterMapInterfaceINTEL = 6160,
+  SpvExecutionModeNamedBarrierCountINTEL = 6417,
+  SpvExecutionModeMax = 0x7fffffff,
+} SpvExecutionMode;
+
+typedef enum SpvStorageClass_ {
+  SpvStorageClassUniformConstant = 0,
+  SpvStorageClassInput = 1,
+  SpvStorageClassUniform = 2,
+  SpvStorageClassOutput = 3,
+  SpvStorageClassWorkgroup = 4,
+  SpvStorageClassCrossWorkgroup = 5,
+  SpvStorageClassPrivate = 6,
+  SpvStorageClassFunction = 7,
+  SpvStorageClassGeneric = 8,
+  SpvStorageClassPushConstant = 9,
+  SpvStorageClassAtomicCounter = 10,
+  SpvStorageClassImage = 11,
+  SpvStorageClassStorageBuffer = 12,
+  SpvStorageClassTileImageEXT = 4172,
+  SpvStorageClassCallableDataKHR = 5328,
+  SpvStorageClassCallableDataNV = 5328,
+  SpvStorageClassIncomingCallableDataKHR = 5329,
+  SpvStorageClassIncomingCallableDataNV = 5329,
+  SpvStorageClassRayPayloadKHR = 5338,
+  SpvStorageClassRayPayloadNV = 5338,
+  SpvStorageClassHitAttributeKHR = 5339,
+  SpvStorageClassHitAttributeNV = 5339,
+  SpvStorageClassIncomingRayPayloadKHR = 5342,
+  SpvStorageClassIncomingRayPayloadNV = 5342,
+  SpvStorageClassShaderRecordBufferKHR = 5343,
+  SpvStorageClassShaderRecordBufferNV = 5343,
+  SpvStorageClassPhysicalStorageBuffer = 5349,
+  SpvStorageClassPhysicalStorageBufferEXT = 5349,
+  SpvStorageClassHitObjectAttributeNV = 5385,
+  SpvStorageClassTaskPayloadWorkgroupEXT = 5402,
+  SpvStorageClassCodeSectionINTEL = 5605,
+  SpvStorageClassDeviceOnlyINTEL = 5936,
+  SpvStorageClassHostOnlyINTEL = 5937,
+  SpvStorageClassMax = 0x7fffffff,
+} SpvStorageClass;
+
+typedef enum SpvDim_ {
+  SpvDim1D = 0,
+  SpvDim2D = 1,
+  SpvDim3D = 2,
+  SpvDimCube = 3,
+  SpvDimRect = 4,
+  SpvDimBuffer = 5,
+  SpvDimSubpassData = 6,
+  SpvDimTileImageDataEXT = 4173,
+  SpvDimMax = 0x7fffffff,
+} SpvDim;
+
+typedef enum SpvSamplerAddressingMode_ {
+  SpvSamplerAddressingModeNone = 0,
+  SpvSamplerAddressingModeClampToEdge = 1,
+  SpvSamplerAddressingModeClamp = 2,
+  SpvSamplerAddressingModeRepeat = 3,
+  SpvSamplerAddressingModeRepeatMirrored = 4,
+  SpvSamplerAddressingModeMax = 0x7fffffff,
+} SpvSamplerAddressingMode;
+
+typedef enum SpvSamplerFilterMode_ {
+  SpvSamplerFilterModeNearest = 0,
+  SpvSamplerFilterModeLinear = 1,
+  SpvSamplerFilterModeMax = 0x7fffffff,
+} SpvSamplerFilterMode;
+
+typedef enum SpvImageFormat_ {
+  SpvImageFormatUnknown = 0,
+  SpvImageFormatRgba32f = 1,
+  SpvImageFormatRgba16f = 2,
+  SpvImageFormatR32f = 3,
+  SpvImageFormatRgba8 = 4,
+  SpvImageFormatRgba8Snorm = 5,
+  SpvImageFormatRg32f = 6,
+  SpvImageFormatRg16f = 7,
+  SpvImageFormatR11fG11fB10f = 8,
+  SpvImageFormatR16f = 9,
+  SpvImageFormatRgba16 = 10,
+  SpvImageFormatRgb10A2 = 11,
+  SpvImageFormatRg16 = 12,
+  SpvImageFormatRg8 = 13,
+  SpvImageFormatR16 = 14,
+  SpvImageFormatR8 = 15,
+  SpvImageFormatRgba16Snorm = 16,
+  SpvImageFormatRg16Snorm = 17,
+  SpvImageFormatRg8Snorm = 18,
+  SpvImageFormatR16Snorm = 19,
+  SpvImageFormatR8Snorm = 20,
+  SpvImageFormatRgba32i = 21,
+  SpvImageFormatRgba16i = 22,
+  SpvImageFormatRgba8i = 23,
+  SpvImageFormatR32i = 24,
+  SpvImageFormatRg32i = 25,
+  SpvImageFormatRg16i = 26,
+  SpvImageFormatRg8i = 27,
+  SpvImageFormatR16i = 28,
+  SpvImageFormatR8i = 29,
+  SpvImageFormatRgba32ui = 30,
+  SpvImageFormatRgba16ui = 31,
+  SpvImageFormatRgba8ui = 32,
+  SpvImageFormatR32ui = 33,
+  SpvImageFormatRgb10a2ui = 34,
+  SpvImageFormatRg32ui = 35,
+  SpvImageFormatRg16ui = 36,
+  SpvImageFormatRg8ui = 37,
+  SpvImageFormatR16ui = 38,
+  SpvImageFormatR8ui = 39,
+  SpvImageFormatR64ui = 40,
+  SpvImageFormatR64i = 41,
+  SpvImageFormatMax = 0x7fffffff,
+} SpvImageFormat;
+
+typedef enum SpvImageChannelOrder_ {
+  SpvImageChannelOrderR = 0,
+  SpvImageChannelOrderA = 1,
+  SpvImageChannelOrderRG = 2,
+  SpvImageChannelOrderRA = 3,
+  SpvImageChannelOrderRGB = 4,
+  SpvImageChannelOrderRGBA = 5,
+  SpvImageChannelOrderBGRA = 6,
+  SpvImageChannelOrderARGB = 7,
+  SpvImageChannelOrderIntensity = 8,
+  SpvImageChannelOrderLuminance = 9,
+  SpvImageChannelOrderRx = 10,
+  SpvImageChannelOrderRGx = 11,
+  SpvImageChannelOrderRGBx = 12,
+  SpvImageChannelOrderDepth = 13,
+  SpvImageChannelOrderDepthStencil = 14,
+  SpvImageChannelOrdersRGB = 15,
+  SpvImageChannelOrdersRGBx = 16,
+  SpvImageChannelOrdersRGBA = 17,
+  SpvImageChannelOrdersBGRA = 18,
+  SpvImageChannelOrderABGR = 19,
+  SpvImageChannelOrderMax = 0x7fffffff,
+} SpvImageChannelOrder;
+
+typedef enum SpvImageChannelDataType_ {
+  SpvImageChannelDataTypeSnormInt8 = 0,
+  SpvImageChannelDataTypeSnormInt16 = 1,
+  SpvImageChannelDataTypeUnormInt8 = 2,
+  SpvImageChannelDataTypeUnormInt16 = 3,
+  SpvImageChannelDataTypeUnormShort565 = 4,
+  SpvImageChannelDataTypeUnormShort555 = 5,
+  SpvImageChannelDataTypeUnormInt101010 = 6,
+  SpvImageChannelDataTypeSignedInt8 = 7,
+  SpvImageChannelDataTypeSignedInt16 = 8,
+  SpvImageChannelDataTypeSignedInt32 = 9,
+  SpvImageChannelDataTypeUnsignedInt8 = 10,
+  SpvImageChannelDataTypeUnsignedInt16 = 11,
+  SpvImageChannelDataTypeUnsignedInt32 = 12,
+  SpvImageChannelDataTypeHalfFloat = 13,
+  SpvImageChannelDataTypeFloat = 14,
+  SpvImageChannelDataTypeUnormInt24 = 15,
+  SpvImageChannelDataTypeUnormInt101010_2 = 16,
+  SpvImageChannelDataTypeUnsignedIntRaw10EXT = 19,
+  SpvImageChannelDataTypeUnsignedIntRaw12EXT = 20,
+  SpvImageChannelDataTypeMax = 0x7fffffff,
+} SpvImageChannelDataType;
+
+typedef enum SpvImageOperandsShift_ {
+  SpvImageOperandsBiasShift = 0,
+  SpvImageOperandsLodShift = 1,
+  SpvImageOperandsGradShift = 2,
+  SpvImageOperandsConstOffsetShift = 3,
+  SpvImageOperandsOffsetShift = 4,
+  SpvImageOperandsConstOffsetsShift = 5,
+  SpvImageOperandsSampleShift = 6,
+  SpvImageOperandsMinLodShift = 7,
+  SpvImageOperandsMakeTexelAvailableShift = 8,
+  SpvImageOperandsMakeTexelAvailableKHRShift = 8,
+  SpvImageOperandsMakeTexelVisibleShift = 9,
+  SpvImageOperandsMakeTexelVisibleKHRShift = 9,
+  SpvImageOperandsNonPrivateTexelShift = 10,
+  SpvImageOperandsNonPrivateTexelKHRShift = 10,
+  SpvImageOperandsVolatileTexelShift = 11,
+  SpvImageOperandsVolatileTexelKHRShift = 11,
+  SpvImageOperandsSignExtendShift = 12,
+  SpvImageOperandsZeroExtendShift = 13,
+  SpvImageOperandsNontemporalShift = 14,
+  SpvImageOperandsOffsetsShift = 16,
+  SpvImageOperandsMax = 0x7fffffff,
+} SpvImageOperandsShift;
+
+typedef enum SpvImageOperandsMask_ {
+  SpvImageOperandsMaskNone = 0,
+  SpvImageOperandsBiasMask = 0x00000001,
+  SpvImageOperandsLodMask = 0x00000002,
+  SpvImageOperandsGradMask = 0x00000004,
+  SpvImageOperandsConstOffsetMask = 0x00000008,
+  SpvImageOperandsOffsetMask = 0x00000010,
+  SpvImageOperandsConstOffsetsMask = 0x00000020,
+  SpvImageOperandsSampleMask = 0x00000040,
+  SpvImageOperandsMinLodMask = 0x00000080,
+  SpvImageOperandsMakeTexelAvailableMask = 0x00000100,
+  SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100,
+  SpvImageOperandsMakeTexelVisibleMask = 0x00000200,
+  SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200,
+  SpvImageOperandsNonPrivateTexelMask = 0x00000400,
+  SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400,
+  SpvImageOperandsVolatileTexelMask = 0x00000800,
+  SpvImageOperandsVolatileTexelKHRMask = 0x00000800,
+  SpvImageOperandsSignExtendMask = 0x00001000,
+  SpvImageOperandsZeroExtendMask = 0x00002000,
+  SpvImageOperandsNontemporalMask = 0x00004000,
+  SpvImageOperandsOffsetsMask = 0x00010000,
+} SpvImageOperandsMask;
+
+typedef enum SpvFPFastMathModeShift_ {
+  SpvFPFastMathModeNotNaNShift = 0,
+  SpvFPFastMathModeNotInfShift = 1,
+  SpvFPFastMathModeNSZShift = 2,
+  SpvFPFastMathModeAllowRecipShift = 3,
+  SpvFPFastMathModeFastShift = 4,
+  SpvFPFastMathModeAllowContractFastINTELShift = 16,
+  SpvFPFastMathModeAllowReassocINTELShift = 17,
+  SpvFPFastMathModeMax = 0x7fffffff,
+} SpvFPFastMathModeShift;
+
+typedef enum SpvFPFastMathModeMask_ {
+  SpvFPFastMathModeMaskNone = 0,
+  SpvFPFastMathModeNotNaNMask = 0x00000001,
+  SpvFPFastMathModeNotInfMask = 0x00000002,
+  SpvFPFastMathModeNSZMask = 0x00000004,
+  SpvFPFastMathModeAllowRecipMask = 0x00000008,
+  SpvFPFastMathModeFastMask = 0x00000010,
+  SpvFPFastMathModeAllowContractFastINTELMask = 0x00010000,
+  SpvFPFastMathModeAllowReassocINTELMask = 0x00020000,
+} SpvFPFastMathModeMask;
+
+typedef enum SpvFPRoundingMode_ {
+  SpvFPRoundingModeRTE = 0,
+  SpvFPRoundingModeRTZ = 1,
+  SpvFPRoundingModeRTP = 2,
+  SpvFPRoundingModeRTN = 3,
+  SpvFPRoundingModeMax = 0x7fffffff,
+} SpvFPRoundingMode;
+
+typedef enum SpvLinkageType_ {
+  SpvLinkageTypeExport = 0,
+  SpvLinkageTypeImport = 1,
+  SpvLinkageTypeLinkOnceODR = 2,
+  SpvLinkageTypeMax = 0x7fffffff,
+} SpvLinkageType;
+
+typedef enum SpvAccessQualifier_ {
+  SpvAccessQualifierReadOnly = 0,
+  SpvAccessQualifierWriteOnly = 1,
+  SpvAccessQualifierReadWrite = 2,
+  SpvAccessQualifierMax = 0x7fffffff,
+} SpvAccessQualifier;
+
+typedef enum SpvFunctionParameterAttribute_ {
+  SpvFunctionParameterAttributeZext = 0,
+  SpvFunctionParameterAttributeSext = 1,
+  SpvFunctionParameterAttributeByVal = 2,
+  SpvFunctionParameterAttributeSret = 3,
+  SpvFunctionParameterAttributeNoAlias = 4,
+  SpvFunctionParameterAttributeNoCapture = 5,
+  SpvFunctionParameterAttributeNoWrite = 6,
+  SpvFunctionParameterAttributeNoReadWrite = 7,
+  SpvFunctionParameterAttributeRuntimeAlignedINTEL = 5940,
+  SpvFunctionParameterAttributeMax = 0x7fffffff,
+} SpvFunctionParameterAttribute;
+
+typedef enum SpvDecoration_ {
+  SpvDecorationRelaxedPrecision = 0,
+  SpvDecorationSpecId = 1,
+  SpvDecorationBlock = 2,
+  SpvDecorationBufferBlock = 3,
+  SpvDecorationRowMajor = 4,
+  SpvDecorationColMajor = 5,
+  SpvDecorationArrayStride = 6,
+  SpvDecorationMatrixStride = 7,
+  SpvDecorationGLSLShared = 8,
+  SpvDecorationGLSLPacked = 9,
+  SpvDecorationCPacked = 10,
+  SpvDecorationBuiltIn = 11,
+  SpvDecorationNoPerspective = 13,
+  SpvDecorationFlat = 14,
+  SpvDecorationPatch = 15,
+  SpvDecorationCentroid = 16,
+  SpvDecorationSample = 17,
+  SpvDecorationInvariant = 18,
+  SpvDecorationRestrict = 19,
+  SpvDecorationAliased = 20,
+  SpvDecorationVolatile = 21,
+  SpvDecorationConstant = 22,
+  SpvDecorationCoherent = 23,
+  SpvDecorationNonWritable = 24,
+  SpvDecorationNonReadable = 25,
+  SpvDecorationUniform = 26,
+  SpvDecorationUniformId = 27,
+  SpvDecorationSaturatedConversion = 28,
+  SpvDecorationStream = 29,
+  SpvDecorationLocation = 30,
+  SpvDecorationComponent = 31,
+  SpvDecorationIndex = 32,
+  SpvDecorationBinding = 33,
+  SpvDecorationDescriptorSet = 34,
+  SpvDecorationOffset = 35,
+  SpvDecorationXfbBuffer = 36,
+  SpvDecorationXfbStride = 37,
+  SpvDecorationFuncParamAttr = 38,
+  SpvDecorationFPRoundingMode = 39,
+  SpvDecorationFPFastMathMode = 40,
+  SpvDecorationLinkageAttributes = 41,
+  SpvDecorationNoContraction = 42,
+  SpvDecorationInputAttachmentIndex = 43,
+  SpvDecorationAlignment = 44,
+  SpvDecorationMaxByteOffset = 45,
+  SpvDecorationAlignmentId = 46,
+  SpvDecorationMaxByteOffsetId = 47,
+  SpvDecorationNoSignedWrap = 4469,
+  SpvDecorationNoUnsignedWrap = 4470,
+  SpvDecorationWeightTextureQCOM = 4487,
+  SpvDecorationBlockMatchTextureQCOM = 4488,
+  SpvDecorationExplicitInterpAMD = 4999,
+  SpvDecorationOverrideCoverageNV = 5248,
+  SpvDecorationPassthroughNV = 5250,
+  SpvDecorationViewportRelativeNV = 5252,
+  SpvDecorationSecondaryViewportRelativeNV = 5256,
+  SpvDecorationPerPrimitiveEXT = 5271,
+  SpvDecorationPerPrimitiveNV = 5271,
+  SpvDecorationPerViewNV = 5272,
+  SpvDecorationPerTaskNV = 5273,
+  SpvDecorationPerVertexKHR = 5285,
+  SpvDecorationPerVertexNV = 5285,
+  SpvDecorationNonUniform = 5300,
+  SpvDecorationNonUniformEXT = 5300,
+  SpvDecorationRestrictPointer = 5355,
+  SpvDecorationRestrictPointerEXT = 5355,
+  SpvDecorationAliasedPointer = 5356,
+  SpvDecorationAliasedPointerEXT = 5356,
+  SpvDecorationHitObjectShaderRecordBufferNV = 5386,
+  SpvDecorationBindlessSamplerNV = 5398,
+  SpvDecorationBindlessImageNV = 5399,
+  SpvDecorationBoundSamplerNV = 5400,
+  SpvDecorationBoundImageNV = 5401,
+  SpvDecorationSIMTCallINTEL = 5599,
+  SpvDecorationReferencedIndirectlyINTEL = 5602,
+  SpvDecorationClobberINTEL = 5607,
+  SpvDecorationSideEffectsINTEL = 5608,
+  SpvDecorationVectorComputeVariableINTEL = 5624,
+  SpvDecorationFuncParamIOKindINTEL = 5625,
+  SpvDecorationVectorComputeFunctionINTEL = 5626,
+  SpvDecorationStackCallINTEL = 5627,
+  SpvDecorationGlobalVariableOffsetINTEL = 5628,
+  SpvDecorationCounterBuffer = 5634,
+  SpvDecorationHlslCounterBufferGOOGLE = 5634,
+  SpvDecorationHlslSemanticGOOGLE = 5635,
+  SpvDecorationUserSemantic = 5635,
+  SpvDecorationUserTypeGOOGLE = 5636,
+  SpvDecorationFunctionRoundingModeINTEL = 5822,
+  SpvDecorationFunctionDenormModeINTEL = 5823,
+  SpvDecorationRegisterINTEL = 5825,
+  SpvDecorationMemoryINTEL = 5826,
+  SpvDecorationNumbanksINTEL = 5827,
+  SpvDecorationBankwidthINTEL = 5828,
+  SpvDecorationMaxPrivateCopiesINTEL = 5829,
+  SpvDecorationSinglepumpINTEL = 5830,
+  SpvDecorationDoublepumpINTEL = 5831,
+  SpvDecorationMaxReplicatesINTEL = 5832,
+  SpvDecorationSimpleDualPortINTEL = 5833,
+  SpvDecorationMergeINTEL = 5834,
+  SpvDecorationBankBitsINTEL = 5835,
+  SpvDecorationForcePow2DepthINTEL = 5836,
+  SpvDecorationBurstCoalesceINTEL = 5899,
+  SpvDecorationCacheSizeINTEL = 5900,
+  SpvDecorationDontStaticallyCoalesceINTEL = 5901,
+  SpvDecorationPrefetchINTEL = 5902,
+  SpvDecorationStallEnableINTEL = 5905,
+  SpvDecorationFuseLoopsInFunctionINTEL = 5907,
+  SpvDecorationMathOpDSPModeINTEL = 5909,
+  SpvDecorationAliasScopeINTEL = 5914,
+  SpvDecorationNoAliasINTEL = 5915,
+  SpvDecorationInitiationIntervalINTEL = 5917,
+  SpvDecorationMaxConcurrencyINTEL = 5918,
+  SpvDecorationPipelineEnableINTEL = 5919,
+  SpvDecorationBufferLocationINTEL = 5921,
+  SpvDecorationIOPipeStorageINTEL = 5944,
+  SpvDecorationFunctionFloatingPointModeINTEL = 6080,
+  SpvDecorationSingleElementVectorINTEL = 6085,
+  SpvDecorationVectorComputeCallableFunctionINTEL = 6087,
+  SpvDecorationMediaBlockIOINTEL = 6140,
+  SpvDecorationLatencyControlLabelINTEL = 6172,
+  SpvDecorationLatencyControlConstraintINTEL = 6173,
+  SpvDecorationConduitKernelArgumentINTEL = 6175,
+  SpvDecorationRegisterMapKernelArgumentINTEL = 6176,
+  SpvDecorationMMHostInterfaceAddressWidthINTEL = 6177,
+  SpvDecorationMMHostInterfaceDataWidthINTEL = 6178,
+  SpvDecorationMMHostInterfaceLatencyINTEL = 6179,
+  SpvDecorationMMHostInterfaceReadWriteModeINTEL = 6180,
+  SpvDecorationMMHostInterfaceMaxBurstINTEL = 6181,
+  SpvDecorationMMHostInterfaceWaitRequestINTEL = 6182,
+  SpvDecorationStableKernelArgumentINTEL = 6183,
+  SpvDecorationMax = 0x7fffffff,
+} SpvDecoration;
+
+typedef enum SpvBuiltIn_ {
+  SpvBuiltInPosition = 0,
+  SpvBuiltInPointSize = 1,
+  SpvBuiltInClipDistance = 3,
+  SpvBuiltInCullDistance = 4,
+  SpvBuiltInVertexId = 5,
+  SpvBuiltInInstanceId = 6,
+  SpvBuiltInPrimitiveId = 7,
+  SpvBuiltInInvocationId = 8,
+  SpvBuiltInLayer = 9,
+  SpvBuiltInViewportIndex = 10,
+  SpvBuiltInTessLevelOuter = 11,
+  SpvBuiltInTessLevelInner = 12,
+  SpvBuiltInTessCoord = 13,
+  SpvBuiltInPatchVertices = 14,
+  SpvBuiltInFragCoord = 15,
+  SpvBuiltInPointCoord = 16,
+  SpvBuiltInFrontFacing = 17,
+  SpvBuiltInSampleId = 18,
+  SpvBuiltInSamplePosition = 19,
+  SpvBuiltInSampleMask = 20,
+  SpvBuiltInFragDepth = 22,
+  SpvBuiltInHelperInvocation = 23,
+  SpvBuiltInNumWorkgroups = 24,
+  SpvBuiltInWorkgroupSize = 25,
+  SpvBuiltInWorkgroupId = 26,
+  SpvBuiltInLocalInvocationId = 27,
+  SpvBuiltInGlobalInvocationId = 28,
+  SpvBuiltInLocalInvocationIndex = 29,
+  SpvBuiltInWorkDim = 30,
+  SpvBuiltInGlobalSize = 31,
+  SpvBuiltInEnqueuedWorkgroupSize = 32,
+  SpvBuiltInGlobalOffset = 33,
+  SpvBuiltInGlobalLinearId = 34,
+  SpvBuiltInSubgroupSize = 36,
+  SpvBuiltInSubgroupMaxSize = 37,
+  SpvBuiltInNumSubgroups = 38,
+  SpvBuiltInNumEnqueuedSubgroups = 39,
+  SpvBuiltInSubgroupId = 40,
+  SpvBuiltInSubgroupLocalInvocationId = 41,
+  SpvBuiltInVertexIndex = 42,
+  SpvBuiltInInstanceIndex = 43,
+  SpvBuiltInCoreIDARM = 4160,
+  SpvBuiltInCoreCountARM = 4161,
+  SpvBuiltInCoreMaxIDARM = 4162,
+  SpvBuiltInWarpIDARM = 4163,
+  SpvBuiltInWarpMaxIDARM = 4164,
+  SpvBuiltInSubgroupEqMask = 4416,
+  SpvBuiltInSubgroupEqMaskKHR = 4416,
+  SpvBuiltInSubgroupGeMask = 4417,
+  SpvBuiltInSubgroupGeMaskKHR = 4417,
+  SpvBuiltInSubgroupGtMask = 4418,
+  SpvBuiltInSubgroupGtMaskKHR = 4418,
+  SpvBuiltInSubgroupLeMask = 4419,
+  SpvBuiltInSubgroupLeMaskKHR = 4419,
+  SpvBuiltInSubgroupLtMask = 4420,
+  SpvBuiltInSubgroupLtMaskKHR = 4420,
+  SpvBuiltInBaseVertex = 4424,
+  SpvBuiltInBaseInstance = 4425,
+  SpvBuiltInDrawIndex = 4426,
+  SpvBuiltInPrimitiveShadingRateKHR = 4432,
+  SpvBuiltInDeviceIndex = 4438,
+  SpvBuiltInViewIndex = 4440,
+  SpvBuiltInShadingRateKHR = 4444,
+  SpvBuiltInBaryCoordNoPerspAMD = 4992,
+  SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993,
+  SpvBuiltInBaryCoordNoPerspSampleAMD = 4994,
+  SpvBuiltInBaryCoordSmoothAMD = 4995,
+  SpvBuiltInBaryCoordSmoothCentroidAMD = 4996,
+  SpvBuiltInBaryCoordSmoothSampleAMD = 4997,
+  SpvBuiltInBaryCoordPullModelAMD = 4998,
+  SpvBuiltInFragStencilRefEXT = 5014,
+  SpvBuiltInViewportMaskNV = 5253,
+  SpvBuiltInSecondaryPositionNV = 5257,
+  SpvBuiltInSecondaryViewportMaskNV = 5258,
+  SpvBuiltInPositionPerViewNV = 5261,
+  SpvBuiltInViewportMaskPerViewNV = 5262,
+  SpvBuiltInFullyCoveredEXT = 5264,
+  SpvBuiltInTaskCountNV = 5274,
+  SpvBuiltInPrimitiveCountNV = 5275,
+  SpvBuiltInPrimitiveIndicesNV = 5276,
+  SpvBuiltInClipDistancePerViewNV = 5277,
+  SpvBuiltInCullDistancePerViewNV = 5278,
+  SpvBuiltInLayerPerViewNV = 5279,
+  SpvBuiltInMeshViewCountNV = 5280,
+  SpvBuiltInMeshViewIndicesNV = 5281,
+  SpvBuiltInBaryCoordKHR = 5286,
+  SpvBuiltInBaryCoordNV = 5286,
+  SpvBuiltInBaryCoordNoPerspKHR = 5287,
+  SpvBuiltInBaryCoordNoPerspNV = 5287,
+  SpvBuiltInFragSizeEXT = 5292,
+  SpvBuiltInFragmentSizeNV = 5292,
+  SpvBuiltInFragInvocationCountEXT = 5293,
+  SpvBuiltInInvocationsPerPixelNV = 5293,
+  SpvBuiltInPrimitivePointIndicesEXT = 5294,
+  SpvBuiltInPrimitiveLineIndicesEXT = 5295,
+  SpvBuiltInPrimitiveTriangleIndicesEXT = 5296,
+  SpvBuiltInCullPrimitiveEXT = 5299,
+  SpvBuiltInLaunchIdKHR = 5319,
+  SpvBuiltInLaunchIdNV = 5319,
+  SpvBuiltInLaunchSizeKHR = 5320,
+  SpvBuiltInLaunchSizeNV = 5320,
+  SpvBuiltInWorldRayOriginKHR = 5321,
+  SpvBuiltInWorldRayOriginNV = 5321,
+  SpvBuiltInWorldRayDirectionKHR = 5322,
+  SpvBuiltInWorldRayDirectionNV = 5322,
+  SpvBuiltInObjectRayOriginKHR = 5323,
+  SpvBuiltInObjectRayOriginNV = 5323,
+  SpvBuiltInObjectRayDirectionKHR = 5324,
+  SpvBuiltInObjectRayDirectionNV = 5324,
+  SpvBuiltInRayTminKHR = 5325,
+  SpvBuiltInRayTminNV = 5325,
+  SpvBuiltInRayTmaxKHR = 5326,
+  SpvBuiltInRayTmaxNV = 5326,
+  SpvBuiltInInstanceCustomIndexKHR = 5327,
+  SpvBuiltInInstanceCustomIndexNV = 5327,
+  SpvBuiltInObjectToWorldKHR = 5330,
+  SpvBuiltInObjectToWorldNV = 5330,
+  SpvBuiltInWorldToObjectKHR = 5331,
+  SpvBuiltInWorldToObjectNV = 5331,
+  SpvBuiltInHitTNV = 5332,
+  SpvBuiltInHitKindKHR = 5333,
+  SpvBuiltInHitKindNV = 5333,
+  SpvBuiltInCurrentRayTimeNV = 5334,
+  SpvBuiltInHitTriangleVertexPositionsKHR = 5335,
+  SpvBuiltInIncomingRayFlagsKHR = 5351,
+  SpvBuiltInIncomingRayFlagsNV = 5351,
+  SpvBuiltInRayGeometryIndexKHR = 5352,
+  SpvBuiltInWarpsPerSMNV = 5374,
+  SpvBuiltInSMCountNV = 5375,
+  SpvBuiltInWarpIDNV = 5376,
+  SpvBuiltInSMIDNV = 5377,
+  SpvBuiltInCullMaskKHR = 6021,
+  SpvBuiltInMax = 0x7fffffff,
+} SpvBuiltIn;
+
+typedef enum SpvSelectionControlShift_ {
+  SpvSelectionControlFlattenShift = 0,
+  SpvSelectionControlDontFlattenShift = 1,
+  SpvSelectionControlMax = 0x7fffffff,
+} SpvSelectionControlShift;
+
+typedef enum SpvSelectionControlMask_ {
+  SpvSelectionControlMaskNone = 0,
+  SpvSelectionControlFlattenMask = 0x00000001,
+  SpvSelectionControlDontFlattenMask = 0x00000002,
+} SpvSelectionControlMask;
+
+typedef enum SpvLoopControlShift_ {
+  SpvLoopControlUnrollShift = 0,
+  SpvLoopControlDontUnrollShift = 1,
+  SpvLoopControlDependencyInfiniteShift = 2,
+  SpvLoopControlDependencyLengthShift = 3,
+  SpvLoopControlMinIterationsShift = 4,
+  SpvLoopControlMaxIterationsShift = 5,
+  SpvLoopControlIterationMultipleShift = 6,
+  SpvLoopControlPeelCountShift = 7,
+  SpvLoopControlPartialCountShift = 8,
+  SpvLoopControlInitiationIntervalINTELShift = 16,
+  SpvLoopControlMaxConcurrencyINTELShift = 17,
+  SpvLoopControlDependencyArrayINTELShift = 18,
+  SpvLoopControlPipelineEnableINTELShift = 19,
+  SpvLoopControlLoopCoalesceINTELShift = 20,
+  SpvLoopControlMaxInterleavingINTELShift = 21,
+  SpvLoopControlSpeculatedIterationsINTELShift = 22,
+  SpvLoopControlNoFusionINTELShift = 23,
+  SpvLoopControlLoopCountINTELShift = 24,
+  SpvLoopControlMaxReinvocationDelayINTELShift = 25,
+  SpvLoopControlMax = 0x7fffffff,
+} SpvLoopControlShift;
+
+typedef enum SpvLoopControlMask_ {
+  SpvLoopControlMaskNone = 0,
+  SpvLoopControlUnrollMask = 0x00000001,
+  SpvLoopControlDontUnrollMask = 0x00000002,
+  SpvLoopControlDependencyInfiniteMask = 0x00000004,
+  SpvLoopControlDependencyLengthMask = 0x00000008,
+  SpvLoopControlMinIterationsMask = 0x00000010,
+  SpvLoopControlMaxIterationsMask = 0x00000020,
+  SpvLoopControlIterationMultipleMask = 0x00000040,
+  SpvLoopControlPeelCountMask = 0x00000080,
+  SpvLoopControlPartialCountMask = 0x00000100,
+  SpvLoopControlInitiationIntervalINTELMask = 0x00010000,
+  SpvLoopControlMaxConcurrencyINTELMask = 0x00020000,
+  SpvLoopControlDependencyArrayINTELMask = 0x00040000,
+  SpvLoopControlPipelineEnableINTELMask = 0x00080000,
+  SpvLoopControlLoopCoalesceINTELMask = 0x00100000,
+  SpvLoopControlMaxInterleavingINTELMask = 0x00200000,
+  SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000,
+  SpvLoopControlNoFusionINTELMask = 0x00800000,
+  SpvLoopControlLoopCountINTELMask = 0x01000000,
+  SpvLoopControlMaxReinvocationDelayINTELMask = 0x02000000,
+} SpvLoopControlMask;
+
+typedef enum SpvFunctionControlShift_ {
+  SpvFunctionControlInlineShift = 0,
+  SpvFunctionControlDontInlineShift = 1,
+  SpvFunctionControlPureShift = 2,
+  SpvFunctionControlConstShift = 3,
+  SpvFunctionControlOptNoneINTELShift = 16,
+  SpvFunctionControlMax = 0x7fffffff,
+} SpvFunctionControlShift;
+
+typedef enum SpvFunctionControlMask_ {
+  SpvFunctionControlMaskNone = 0,
+  SpvFunctionControlInlineMask = 0x00000001,
+  SpvFunctionControlDontInlineMask = 0x00000002,
+  SpvFunctionControlPureMask = 0x00000004,
+  SpvFunctionControlConstMask = 0x00000008,
+  SpvFunctionControlOptNoneINTELMask = 0x00010000,
+} SpvFunctionControlMask;
+
+typedef enum SpvMemorySemanticsShift_ {
+  SpvMemorySemanticsAcquireShift = 1,
+  SpvMemorySemanticsReleaseShift = 2,
+  SpvMemorySemanticsAcquireReleaseShift = 3,
+  SpvMemorySemanticsSequentiallyConsistentShift = 4,
+  SpvMemorySemanticsUniformMemoryShift = 6,
+  SpvMemorySemanticsSubgroupMemoryShift = 7,
+  SpvMemorySemanticsWorkgroupMemoryShift = 8,
+  SpvMemorySemanticsCrossWorkgroupMemoryShift = 9,
+  SpvMemorySemanticsAtomicCounterMemoryShift = 10,
+  SpvMemorySemanticsImageMemoryShift = 11,
+  SpvMemorySemanticsOutputMemoryShift = 12,
+  SpvMemorySemanticsOutputMemoryKHRShift = 12,
+  SpvMemorySemanticsMakeAvailableShift = 13,
+  SpvMemorySemanticsMakeAvailableKHRShift = 13,
+  SpvMemorySemanticsMakeVisibleShift = 14,
+  SpvMemorySemanticsMakeVisibleKHRShift = 14,
+  SpvMemorySemanticsVolatileShift = 15,
+  SpvMemorySemanticsMax = 0x7fffffff,
+} SpvMemorySemanticsShift;
+
+typedef enum SpvMemorySemanticsMask_ {
+  SpvMemorySemanticsMaskNone = 0,
+  SpvMemorySemanticsAcquireMask = 0x00000002,
+  SpvMemorySemanticsReleaseMask = 0x00000004,
+  SpvMemorySemanticsAcquireReleaseMask = 0x00000008,
+  SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010,
+  SpvMemorySemanticsUniformMemoryMask = 0x00000040,
+  SpvMemorySemanticsSubgroupMemoryMask = 0x00000080,
+  SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100,
+  SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
+  SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400,
+  SpvMemorySemanticsImageMemoryMask = 0x00000800,
+  SpvMemorySemanticsOutputMemoryMask = 0x00001000,
+  SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000,
+  SpvMemorySemanticsMakeAvailableMask = 0x00002000,
+  SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000,
+  SpvMemorySemanticsMakeVisibleMask = 0x00004000,
+  SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000,
+  SpvMemorySemanticsVolatileMask = 0x00008000,
+} SpvMemorySemanticsMask;
+
+typedef enum SpvMemoryAccessShift_ {
+  SpvMemoryAccessVolatileShift = 0,
+  SpvMemoryAccessAlignedShift = 1,
+  SpvMemoryAccessNontemporalShift = 2,
+  SpvMemoryAccessMakePointerAvailableShift = 3,
+  SpvMemoryAccessMakePointerAvailableKHRShift = 3,
+  SpvMemoryAccessMakePointerVisibleShift = 4,
+  SpvMemoryAccessMakePointerVisibleKHRShift = 4,
+  SpvMemoryAccessNonPrivatePointerShift = 5,
+  SpvMemoryAccessNonPrivatePointerKHRShift = 5,
+  SpvMemoryAccessAliasScopeINTELMaskShift = 16,
+  SpvMemoryAccessNoAliasINTELMaskShift = 17,
+  SpvMemoryAccessMax = 0x7fffffff,
+} SpvMemoryAccessShift;
+
+typedef enum SpvMemoryAccessMask_ {
+  SpvMemoryAccessMaskNone = 0,
+  SpvMemoryAccessVolatileMask = 0x00000001,
+  SpvMemoryAccessAlignedMask = 0x00000002,
+  SpvMemoryAccessNontemporalMask = 0x00000004,
+  SpvMemoryAccessMakePointerAvailableMask = 0x00000008,
+  SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008,
+  SpvMemoryAccessMakePointerVisibleMask = 0x00000010,
+  SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010,
+  SpvMemoryAccessNonPrivatePointerMask = 0x00000020,
+  SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020,
+  SpvMemoryAccessAliasScopeINTELMaskMask = 0x00010000,
+  SpvMemoryAccessNoAliasINTELMaskMask = 0x00020000,
+} SpvMemoryAccessMask;
+
+typedef enum SpvScope_ {
+  SpvScopeCrossDevice = 0,
+  SpvScopeDevice = 1,
+  SpvScopeWorkgroup = 2,
+  SpvScopeSubgroup = 3,
+  SpvScopeInvocation = 4,
+  SpvScopeQueueFamily = 5,
+  SpvScopeQueueFamilyKHR = 5,
+  SpvScopeShaderCallKHR = 6,
+  SpvScopeMax = 0x7fffffff,
+} SpvScope;
+
+typedef enum SpvGroupOperation_ {
+  SpvGroupOperationReduce = 0,
+  SpvGroupOperationInclusiveScan = 1,
+  SpvGroupOperationExclusiveScan = 2,
+  SpvGroupOperationClusteredReduce = 3,
+  SpvGroupOperationPartitionedReduceNV = 6,
+  SpvGroupOperationPartitionedInclusiveScanNV = 7,
+  SpvGroupOperationPartitionedExclusiveScanNV = 8,
+  SpvGroupOperationMax = 0x7fffffff,
+} SpvGroupOperation;
+
+typedef enum SpvKernelEnqueueFlags_ {
+  SpvKernelEnqueueFlagsNoWait = 0,
+  SpvKernelEnqueueFlagsWaitKernel = 1,
+  SpvKernelEnqueueFlagsWaitWorkGroup = 2,
+  SpvKernelEnqueueFlagsMax = 0x7fffffff,
+} SpvKernelEnqueueFlags;
+
+typedef enum SpvKernelProfilingInfoShift_ {
+  SpvKernelProfilingInfoCmdExecTimeShift = 0,
+  SpvKernelProfilingInfoMax = 0x7fffffff,
+} SpvKernelProfilingInfoShift;
+
+typedef enum SpvKernelProfilingInfoMask_ {
+  SpvKernelProfilingInfoMaskNone = 0,
+  SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001,
+} SpvKernelProfilingInfoMask;
+
+typedef enum SpvCapability_ {
+  SpvCapabilityMatrix = 0,
+  SpvCapabilityShader = 1,
+  SpvCapabilityGeometry = 2,
+  SpvCapabilityTessellation = 3,
+  SpvCapabilityAddresses = 4,
+  SpvCapabilityLinkage = 5,
+  SpvCapabilityKernel = 6,
+  SpvCapabilityVector16 = 7,
+  SpvCapabilityFloat16Buffer = 8,
+  SpvCapabilityFloat16 = 9,
+  SpvCapabilityFloat64 = 10,
+  SpvCapabilityInt64 = 11,
+  SpvCapabilityInt64Atomics = 12,
+  SpvCapabilityImageBasic = 13,
+  SpvCapabilityImageReadWrite = 14,
+  SpvCapabilityImageMipmap = 15,
+  SpvCapabilityPipes = 17,
+  SpvCapabilityGroups = 18,
+  SpvCapabilityDeviceEnqueue = 19,
+  SpvCapabilityLiteralSampler = 20,
+  SpvCapabilityAtomicStorage = 21,
+  SpvCapabilityInt16 = 22,
+  SpvCapabilityTessellationPointSize = 23,
+  SpvCapabilityGeometryPointSize = 24,
+  SpvCapabilityImageGatherExtended = 25,
+  SpvCapabilityStorageImageMultisample = 27,
+  SpvCapabilityUniformBufferArrayDynamicIndexing = 28,
+  SpvCapabilitySampledImageArrayDynamicIndexing = 29,
+  SpvCapabilityStorageBufferArrayDynamicIndexing = 30,
+  SpvCapabilityStorageImageArrayDynamicIndexing = 31,
+  SpvCapabilityClipDistance = 32,
+  SpvCapabilityCullDistance = 33,
+  SpvCapabilityImageCubeArray = 34,
+  SpvCapabilitySampleRateShading = 35,
+  SpvCapabilityImageRect = 36,
+  SpvCapabilitySampledRect = 37,
+  SpvCapabilityGenericPointer = 38,
+  SpvCapabilityInt8 = 39,
+  SpvCapabilityInputAttachment = 40,
+  SpvCapabilitySparseResidency = 41,
+  SpvCapabilityMinLod = 42,
+  SpvCapabilitySampled1D = 43,
+  SpvCapabilityImage1D = 44,
+  SpvCapabilitySampledCubeArray = 45,
+  SpvCapabilitySampledBuffer = 46,
+  SpvCapabilityImageBuffer = 47,
+  SpvCapabilityImageMSArray = 48,
+  SpvCapabilityStorageImageExtendedFormats = 49,
+  SpvCapabilityImageQuery = 50,
+  SpvCapabilityDerivativeControl = 51,
+  SpvCapabilityInterpolationFunction = 52,
+  SpvCapabilityTransformFeedback = 53,
+  SpvCapabilityGeometryStreams = 54,
+  SpvCapabilityStorageImageReadWithoutFormat = 55,
+  SpvCapabilityStorageImageWriteWithoutFormat = 56,
+  SpvCapabilityMultiViewport = 57,
+  SpvCapabilitySubgroupDispatch = 58,
+  SpvCapabilityNamedBarrier = 59,
+  SpvCapabilityPipeStorage = 60,
+  SpvCapabilityGroupNonUniform = 61,
+  SpvCapabilityGroupNonUniformVote = 62,
+  SpvCapabilityGroupNonUniformArithmetic = 63,
+  SpvCapabilityGroupNonUniformBallot = 64,
+  SpvCapabilityGroupNonUniformShuffle = 65,
+  SpvCapabilityGroupNonUniformShuffleRelative = 66,
+  SpvCapabilityGroupNonUniformClustered = 67,
+  SpvCapabilityGroupNonUniformQuad = 68,
+  SpvCapabilityShaderLayer = 69,
+  SpvCapabilityShaderViewportIndex = 70,
+  SpvCapabilityUniformDecoration = 71,
+  SpvCapabilityCoreBuiltinsARM = 4165,
+  SpvCapabilityTileImageColorReadAccessEXT = 4166,
+  SpvCapabilityTileImageDepthReadAccessEXT = 4167,
+  SpvCapabilityTileImageStencilReadAccessEXT = 4168,
+  SpvCapabilityFragmentShadingRateKHR = 4422,
+  SpvCapabilitySubgroupBallotKHR = 4423,
+  SpvCapabilityDrawParameters = 4427,
+  SpvCapabilityWorkgroupMemoryExplicitLayoutKHR = 4428,
+  SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429,
+  SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430,
+  SpvCapabilitySubgroupVoteKHR = 4431,
+  SpvCapabilityStorageBuffer16BitAccess = 4433,
+  SpvCapabilityStorageUniformBufferBlock16 = 4433,
+  SpvCapabilityStorageUniform16 = 4434,
+  SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434,
+  SpvCapabilityStoragePushConstant16 = 4435,
+  SpvCapabilityStorageInputOutput16 = 4436,
+  SpvCapabilityDeviceGroup = 4437,
+  SpvCapabilityMultiView = 4439,
+  SpvCapabilityVariablePointersStorageBuffer = 4441,
+  SpvCapabilityVariablePointers = 4442,
+  SpvCapabilityAtomicStorageOps = 4445,
+  SpvCapabilitySampleMaskPostDepthCoverage = 4447,
+  SpvCapabilityStorageBuffer8BitAccess = 4448,
+  SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449,
+  SpvCapabilityStoragePushConstant8 = 4450,
+  SpvCapabilityDenormPreserve = 4464,
+  SpvCapabilityDenormFlushToZero = 4465,
+  SpvCapabilitySignedZeroInfNanPreserve = 4466,
+  SpvCapabilityRoundingModeRTE = 4467,
+  SpvCapabilityRoundingModeRTZ = 4468,
+  SpvCapabilityRayQueryProvisionalKHR = 4471,
+  SpvCapabilityRayQueryKHR = 4472,
+  SpvCapabilityRayTraversalPrimitiveCullingKHR = 4478,
+  SpvCapabilityRayTracingKHR = 4479,
+  SpvCapabilityTextureSampleWeightedQCOM = 4484,
+  SpvCapabilityTextureBoxFilterQCOM = 4485,
+  SpvCapabilityTextureBlockMatchQCOM = 4486,
+  SpvCapabilityFloat16ImageAMD = 5008,
+  SpvCapabilityImageGatherBiasLodAMD = 5009,
+  SpvCapabilityFragmentMaskAMD = 5010,
+  SpvCapabilityStencilExportEXT = 5013,
+  SpvCapabilityImageReadWriteLodAMD = 5015,
+  SpvCapabilityInt64ImageEXT = 5016,
+  SpvCapabilityShaderClockKHR = 5055,
+  SpvCapabilitySampleMaskOverrideCoverageNV = 5249,
+  SpvCapabilityGeometryShaderPassthroughNV = 5251,
+  SpvCapabilityShaderViewportIndexLayerEXT = 5254,
+  SpvCapabilityShaderViewportIndexLayerNV = 5254,
+  SpvCapabilityShaderViewportMaskNV = 5255,
+  SpvCapabilityShaderStereoViewNV = 5259,
+  SpvCapabilityPerViewAttributesNV = 5260,
+  SpvCapabilityFragmentFullyCoveredEXT = 5265,
+  SpvCapabilityMeshShadingNV = 5266,
+  SpvCapabilityImageFootprintNV = 5282,
+  SpvCapabilityMeshShadingEXT = 5283,
+  SpvCapabilityFragmentBarycentricKHR = 5284,
+  SpvCapabilityFragmentBarycentricNV = 5284,
+  SpvCapabilityComputeDerivativeGroupQuadsNV = 5288,
+  SpvCapabilityFragmentDensityEXT = 5291,
+  SpvCapabilityShadingRateNV = 5291,
+  SpvCapabilityGroupNonUniformPartitionedNV = 5297,
+  SpvCapabilityShaderNonUniform = 5301,
+  SpvCapabilityShaderNonUniformEXT = 5301,
+  SpvCapabilityRuntimeDescriptorArray = 5302,
+  SpvCapabilityRuntimeDescriptorArrayEXT = 5302,
+  SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303,
+  SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
+  SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304,
+  SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
+  SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305,
+  SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
+  SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306,
+  SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
+  SpvCapabilitySampledImageArrayNonUniformIndexing = 5307,
+  SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
+  SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308,
+  SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
+  SpvCapabilityStorageImageArrayNonUniformIndexing = 5309,
+  SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
+  SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310,
+  SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
+  SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311,
+  SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
+  SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312,
+  SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
+  SpvCapabilityRayTracingPositionFetchKHR = 5336,
+  SpvCapabilityRayTracingNV = 5340,
+  SpvCapabilityRayTracingMotionBlurNV = 5341,
+  SpvCapabilityVulkanMemoryModel = 5345,
+  SpvCapabilityVulkanMemoryModelKHR = 5345,
+  SpvCapabilityVulkanMemoryModelDeviceScope = 5346,
+  SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346,
+  SpvCapabilityPhysicalStorageBufferAddresses = 5347,
+  SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347,
+  SpvCapabilityComputeDerivativeGroupLinearNV = 5350,
+  SpvCapabilityRayTracingProvisionalKHR = 5353,
+  SpvCapabilityCooperativeMatrixNV = 5357,
+  SpvCapabilityFragmentShaderSampleInterlockEXT = 5363,
+  SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372,
+  SpvCapabilityShaderSMBuiltinsNV = 5373,
+  SpvCapabilityFragmentShaderPixelInterlockEXT = 5378,
+  SpvCapabilityDemoteToHelperInvocation = 5379,
+  SpvCapabilityDemoteToHelperInvocationEXT = 5379,
+  SpvCapabilityRayTracingOpacityMicromapEXT = 5381,
+  SpvCapabilityShaderInvocationReorderNV = 5383,
+  SpvCapabilityBindlessTextureNV = 5390,
+  SpvCapabilityRayQueryPositionFetchKHR = 5391,
+  SpvCapabilitySubgroupShuffleINTEL = 5568,
+  SpvCapabilitySubgroupBufferBlockIOINTEL = 5569,
+  SpvCapabilitySubgroupImageBlockIOINTEL = 5570,
+  SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579,
+  SpvCapabilityRoundToInfinityINTEL = 5582,
+  SpvCapabilityFloatingPointModeINTEL = 5583,
+  SpvCapabilityIntegerFunctions2INTEL = 5584,
+  SpvCapabilityFunctionPointersINTEL = 5603,
+  SpvCapabilityIndirectReferencesINTEL = 5604,
+  SpvCapabilityAsmINTEL = 5606,
+  SpvCapabilityAtomicFloat32MinMaxEXT = 5612,
+  SpvCapabilityAtomicFloat64MinMaxEXT = 5613,
+  SpvCapabilityAtomicFloat16MinMaxEXT = 5616,
+  SpvCapabilityVectorComputeINTEL = 5617,
+  SpvCapabilityVectorAnyINTEL = 5619,
+  SpvCapabilityExpectAssumeKHR = 5629,
+  SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696,
+  SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697,
+  SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698,
+  SpvCapabilityVariableLengthArrayINTEL = 5817,
+  SpvCapabilityFunctionFloatControlINTEL = 5821,
+  SpvCapabilityFPGAMemoryAttributesINTEL = 5824,
+  SpvCapabilityFPFastMathModeINTEL = 5837,
+  SpvCapabilityArbitraryPrecisionIntegersINTEL = 5844,
+  SpvCapabilityArbitraryPrecisionFloatingPointINTEL = 5845,
+  SpvCapabilityUnstructuredLoopControlsINTEL = 5886,
+  SpvCapabilityFPGALoopControlsINTEL = 5888,
+  SpvCapabilityKernelAttributesINTEL = 5892,
+  SpvCapabilityFPGAKernelAttributesINTEL = 5897,
+  SpvCapabilityFPGAMemoryAccessesINTEL = 5898,
+  SpvCapabilityFPGAClusterAttributesINTEL = 5904,
+  SpvCapabilityLoopFuseINTEL = 5906,
+  SpvCapabilityFPGADSPControlINTEL = 5908,
+  SpvCapabilityMemoryAccessAliasingINTEL = 5910,
+  SpvCapabilityFPGAInvocationPipeliningAttributesINTEL = 5916,
+  SpvCapabilityFPGABufferLocationINTEL = 5920,
+  SpvCapabilityArbitraryPrecisionFixedPointINTEL = 5922,
+  SpvCapabilityUSMStorageClassesINTEL = 5935,
+  SpvCapabilityRuntimeAlignedAttributeINTEL = 5939,
+  SpvCapabilityIOPipesINTEL = 5943,
+  SpvCapabilityBlockingPipesINTEL = 5945,
+  SpvCapabilityFPGARegINTEL = 5948,
+  SpvCapabilityDotProductInputAll = 6016,
+  SpvCapabilityDotProductInputAllKHR = 6016,
+  SpvCapabilityDotProductInput4x8Bit = 6017,
+  SpvCapabilityDotProductInput4x8BitKHR = 6017,
+  SpvCapabilityDotProductInput4x8BitPacked = 6018,
+  SpvCapabilityDotProductInput4x8BitPackedKHR = 6018,
+  SpvCapabilityDotProduct = 6019,
+  SpvCapabilityDotProductKHR = 6019,
+  SpvCapabilityRayCullMaskKHR = 6020,
+  SpvCapabilityCooperativeMatrixKHR = 6022,
+  SpvCapabilityBitInstructions = 6025,
+  SpvCapabilityGroupNonUniformRotateKHR = 6026,
+  SpvCapabilityAtomicFloat32AddEXT = 6033,
+  SpvCapabilityAtomicFloat64AddEXT = 6034,
+  SpvCapabilityLongConstantCompositeINTEL = 6089,
+  SpvCapabilityOptNoneINTEL = 6094,
+  SpvCapabilityAtomicFloat16AddEXT = 6095,
+  SpvCapabilityDebugInfoModuleINTEL = 6114,
+  SpvCapabilityBFloat16ConversionINTEL = 6115,
+  SpvCapabilitySplitBarrierINTEL = 6141,
+  SpvCapabilityFPGAKernelAttributesv2INTEL = 6161,
+  SpvCapabilityFPGALatencyControlINTEL = 6171,
+  SpvCapabilityFPGAArgumentInterfacesINTEL = 6174,
+  SpvCapabilityGroupUniformArithmeticKHR = 6400,
+  SpvCapabilityMax = 0x7fffffff,
+} SpvCapability;
+
+typedef enum SpvRayFlagsShift_ {
+  SpvRayFlagsOpaqueKHRShift = 0,
+  SpvRayFlagsNoOpaqueKHRShift = 1,
+  SpvRayFlagsTerminateOnFirstHitKHRShift = 2,
+  SpvRayFlagsSkipClosestHitShaderKHRShift = 3,
+  SpvRayFlagsCullBackFacingTrianglesKHRShift = 4,
+  SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5,
+  SpvRayFlagsCullOpaqueKHRShift = 6,
+  SpvRayFlagsCullNoOpaqueKHRShift = 7,
+  SpvRayFlagsSkipTrianglesKHRShift = 8,
+  SpvRayFlagsSkipAABBsKHRShift = 9,
+  SpvRayFlagsForceOpacityMicromap2StateEXTShift = 10,
+  SpvRayFlagsMax = 0x7fffffff,
+} SpvRayFlagsShift;
+
+typedef enum SpvRayFlagsMask_ {
+  SpvRayFlagsMaskNone = 0,
+  SpvRayFlagsOpaqueKHRMask = 0x00000001,
+  SpvRayFlagsNoOpaqueKHRMask = 0x00000002,
+  SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004,
+  SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008,
+  SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010,
+  SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020,
+  SpvRayFlagsCullOpaqueKHRMask = 0x00000040,
+  SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080,
+  SpvRayFlagsSkipTrianglesKHRMask = 0x00000100,
+  SpvRayFlagsSkipAABBsKHRMask = 0x00000200,
+  SpvRayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400,
+} SpvRayFlagsMask;
+
+typedef enum SpvRayQueryIntersection_ {
+  SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0,
+  SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1,
+  SpvRayQueryIntersectionMax = 0x7fffffff,
+} SpvRayQueryIntersection;
+
+typedef enum SpvRayQueryCommittedIntersectionType_ {
+  SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0,
+  SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR =
+      1,
+  SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR =
+      2,
+  SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff,
+} SpvRayQueryCommittedIntersectionType;
+
+typedef enum SpvRayQueryCandidateIntersectionType_ {
+  SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR =
+      0,
+  SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1,
+  SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff,
+} SpvRayQueryCandidateIntersectionType;
+
+typedef enum SpvFragmentShadingRateShift_ {
+  SpvFragmentShadingRateVertical2PixelsShift = 0,
+  SpvFragmentShadingRateVertical4PixelsShift = 1,
+  SpvFragmentShadingRateHorizontal2PixelsShift = 2,
+  SpvFragmentShadingRateHorizontal4PixelsShift = 3,
+  SpvFragmentShadingRateMax = 0x7fffffff,
+} SpvFragmentShadingRateShift;
+
+typedef enum SpvFragmentShadingRateMask_ {
+  SpvFragmentShadingRateMaskNone = 0,
+  SpvFragmentShadingRateVertical2PixelsMask = 0x00000001,
+  SpvFragmentShadingRateVertical4PixelsMask = 0x00000002,
+  SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004,
+  SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008,
+} SpvFragmentShadingRateMask;
+
+typedef enum SpvFPDenormMode_ {
+  SpvFPDenormModePreserve = 0,
+  SpvFPDenormModeFlushToZero = 1,
+  SpvFPDenormModeMax = 0x7fffffff,
+} SpvFPDenormMode;
+
+typedef enum SpvFPOperationMode_ {
+  SpvFPOperationModeIEEE = 0,
+  SpvFPOperationModeALT = 1,
+  SpvFPOperationModeMax = 0x7fffffff,
+} SpvFPOperationMode;
+
+typedef enum SpvQuantizationModes_ {
+  SpvQuantizationModesTRN = 0,
+  SpvQuantizationModesTRN_ZERO = 1,
+  SpvQuantizationModesRND = 2,
+  SpvQuantizationModesRND_ZERO = 3,
+  SpvQuantizationModesRND_INF = 4,
+  SpvQuantizationModesRND_MIN_INF = 5,
+  SpvQuantizationModesRND_CONV = 6,
+  SpvQuantizationModesRND_CONV_ODD = 7,
+  SpvQuantizationModesMax = 0x7fffffff,
+} SpvQuantizationModes;
+
+typedef enum SpvOverflowModes_ {
+  SpvOverflowModesWRAP = 0,
+  SpvOverflowModesSAT = 1,
+  SpvOverflowModesSAT_ZERO = 2,
+  SpvOverflowModesSAT_SYM = 3,
+  SpvOverflowModesMax = 0x7fffffff,
+} SpvOverflowModes;
+
+typedef enum SpvPackedVectorFormat_ {
+  SpvPackedVectorFormatPackedVectorFormat4x8Bit = 0,
+  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR = 0,
+  SpvPackedVectorFormatMax = 0x7fffffff,
+} SpvPackedVectorFormat;
+
+typedef enum SpvCooperativeMatrixOperandsShift_ {
+  SpvCooperativeMatrixOperandsMatrixASignedComponentsShift = 0,
+  SpvCooperativeMatrixOperandsMatrixBSignedComponentsShift = 1,
+  SpvCooperativeMatrixOperandsMatrixCSignedComponentsShift = 2,
+  SpvCooperativeMatrixOperandsMatrixResultSignedComponentsShift = 3,
+  SpvCooperativeMatrixOperandsSaturatingAccumulationShift = 4,
+  SpvCooperativeMatrixOperandsMax = 0x7fffffff,
+} SpvCooperativeMatrixOperandsShift;
+
+typedef enum SpvCooperativeMatrixOperandsMask_ {
+  SpvCooperativeMatrixOperandsMaskNone = 0,
+  SpvCooperativeMatrixOperandsMatrixASignedComponentsMask = 0x00000001,
+  SpvCooperativeMatrixOperandsMatrixBSignedComponentsMask = 0x00000002,
+  SpvCooperativeMatrixOperandsMatrixCSignedComponentsMask = 0x00000004,
+  SpvCooperativeMatrixOperandsMatrixResultSignedComponentsMask = 0x00000008,
+  SpvCooperativeMatrixOperandsSaturatingAccumulationMask = 0x00000010,
+} SpvCooperativeMatrixOperandsMask;
+
+typedef enum SpvCooperativeMatrixLayout_ {
+  SpvCooperativeMatrixLayoutRowMajorKHR = 0,
+  SpvCooperativeMatrixLayoutColumnMajorKHR = 1,
+  SpvCooperativeMatrixLayoutMax = 0x7fffffff,
+} SpvCooperativeMatrixLayout;
+
+typedef enum SpvCooperativeMatrixUse_ {
+  SpvCooperativeMatrixUseMatrixAKHR = 0,
+  SpvCooperativeMatrixUseMatrixBKHR = 1,
+  SpvCooperativeMatrixUseMatrixAccumulatorKHR = 2,
+  SpvCooperativeMatrixUseMax = 0x7fffffff,
+} SpvCooperativeMatrixUse;
+
+typedef enum SpvOp_ {
+  SpvOpNop = 0,
+  SpvOpUndef = 1,
+  SpvOpSourceContinued = 2,
+  SpvOpSource = 3,
+  SpvOpSourceExtension = 4,
+  SpvOpName = 5,
+  SpvOpMemberName = 6,
+  SpvOpString = 7,
+  SpvOpLine = 8,
+  SpvOpExtension = 10,
+  SpvOpExtInstImport = 11,
+  SpvOpExtInst = 12,
+  SpvOpMemoryModel = 14,
+  SpvOpEntryPoint = 15,
+  SpvOpExecutionMode = 16,
+  SpvOpCapability = 17,
+  SpvOpTypeVoid = 19,
+  SpvOpTypeBool = 20,
+  SpvOpTypeInt = 21,
+  SpvOpTypeFloat = 22,
+  SpvOpTypeVector = 23,
+  SpvOpTypeMatrix = 24,
+  SpvOpTypeImage = 25,
+  SpvOpTypeSampler = 26,
+  SpvOpTypeSampledImage = 27,
+  SpvOpTypeArray = 28,
+  SpvOpTypeRuntimeArray = 29,
+  SpvOpTypeStruct = 30,
+  SpvOpTypeOpaque = 31,
+  SpvOpTypePointer = 32,
+  SpvOpTypeFunction = 33,
+  SpvOpTypeEvent = 34,
+  SpvOpTypeDeviceEvent = 35,
+  SpvOpTypeReserveId = 36,
+  SpvOpTypeQueue = 37,
+  SpvOpTypePipe = 38,
+  SpvOpTypeForwardPointer = 39,
+  SpvOpConstantTrue = 41,
+  SpvOpConstantFalse = 42,
+  SpvOpConstant = 43,
+  SpvOpConstantComposite = 44,
+  SpvOpConstantSampler = 45,
+  SpvOpConstantNull = 46,
+  SpvOpSpecConstantTrue = 48,
+  SpvOpSpecConstantFalse = 49,
+  SpvOpSpecConstant = 50,
+  SpvOpSpecConstantComposite = 51,
+  SpvOpSpecConstantOp = 52,
+  SpvOpFunction = 54,
+  SpvOpFunctionParameter = 55,
+  SpvOpFunctionEnd = 56,
+  SpvOpFunctionCall = 57,
+  SpvOpVariable = 59,
+  SpvOpImageTexelPointer = 60,
+  SpvOpLoad = 61,
+  SpvOpStore = 62,
+  SpvOpCopyMemory = 63,
+  SpvOpCopyMemorySized = 64,
+  SpvOpAccessChain = 65,
+  SpvOpInBoundsAccessChain = 66,
+  SpvOpPtrAccessChain = 67,
+  SpvOpArrayLength = 68,
+  SpvOpGenericPtrMemSemantics = 69,
+  SpvOpInBoundsPtrAccessChain = 70,
+  SpvOpDecorate = 71,
+  SpvOpMemberDecorate = 72,
+  SpvOpDecorationGroup = 73,
+  SpvOpGroupDecorate = 74,
+  SpvOpGroupMemberDecorate = 75,
+  SpvOpVectorExtractDynamic = 77,
+  SpvOpVectorInsertDynamic = 78,
+  SpvOpVectorShuffle = 79,
+  SpvOpCompositeConstruct = 80,
+  SpvOpCompositeExtract = 81,
+  SpvOpCompositeInsert = 82,
+  SpvOpCopyObject = 83,
+  SpvOpTranspose = 84,
+  SpvOpSampledImage = 86,
+  SpvOpImageSampleImplicitLod = 87,
+  SpvOpImageSampleExplicitLod = 88,
+  SpvOpImageSampleDrefImplicitLod = 89,
+  SpvOpImageSampleDrefExplicitLod = 90,
+  SpvOpImageSampleProjImplicitLod = 91,
+  SpvOpImageSampleProjExplicitLod = 92,
+  SpvOpImageSampleProjDrefImplicitLod = 93,
+  SpvOpImageSampleProjDrefExplicitLod = 94,
+  SpvOpImageFetch = 95,
+  SpvOpImageGather = 96,
+  SpvOpImageDrefGather = 97,
+  SpvOpImageRead = 98,
+  SpvOpImageWrite = 99,
+  SpvOpImage = 100,
+  SpvOpImageQueryFormat = 101,
+  SpvOpImageQueryOrder = 102,
+  SpvOpImageQuerySizeLod = 103,
+  SpvOpImageQuerySize = 104,
+  SpvOpImageQueryLod = 105,
+  SpvOpImageQueryLevels = 106,
+  SpvOpImageQuerySamples = 107,
+  SpvOpConvertFToU = 109,
+  SpvOpConvertFToS = 110,
+  SpvOpConvertSToF = 111,
+  SpvOpConvertUToF = 112,
+  SpvOpUConvert = 113,
+  SpvOpSConvert = 114,
+  SpvOpFConvert = 115,
+  SpvOpQuantizeToF16 = 116,
+  SpvOpConvertPtrToU = 117,
+  SpvOpSatConvertSToU = 118,
+  SpvOpSatConvertUToS = 119,
+  SpvOpConvertUToPtr = 120,
+  SpvOpPtrCastToGeneric = 121,
+  SpvOpGenericCastToPtr = 122,
+  SpvOpGenericCastToPtrExplicit = 123,
+  SpvOpBitcast = 124,
+  SpvOpSNegate = 126,
+  SpvOpFNegate = 127,
+  SpvOpIAdd = 128,
+  SpvOpFAdd = 129,
+  SpvOpISub = 130,
+  SpvOpFSub = 131,
+  SpvOpIMul = 132,
+  SpvOpFMul = 133,
+  SpvOpUDiv = 134,
+  SpvOpSDiv = 135,
+  SpvOpFDiv = 136,
+  SpvOpUMod = 137,
+  SpvOpSRem = 138,
+  SpvOpSMod = 139,
+  SpvOpFRem = 140,
+  SpvOpFMod = 141,
+  SpvOpVectorTimesScalar = 142,
+  SpvOpMatrixTimesScalar = 143,
+  SpvOpVectorTimesMatrix = 144,
+  SpvOpMatrixTimesVector = 145,
+  SpvOpMatrixTimesMatrix = 146,
+  SpvOpOuterProduct = 147,
+  SpvOpDot = 148,
+  SpvOpIAddCarry = 149,
+  SpvOpISubBorrow = 150,
+  SpvOpUMulExtended = 151,
+  SpvOpSMulExtended = 152,
+  SpvOpAny = 154,
+  SpvOpAll = 155,
+  SpvOpIsNan = 156,
+  SpvOpIsInf = 157,
+  SpvOpIsFinite = 158,
+  SpvOpIsNormal = 159,
+  SpvOpSignBitSet = 160,
+  SpvOpLessOrGreater = 161,
+  SpvOpOrdered = 162,
+  SpvOpUnordered = 163,
+  SpvOpLogicalEqual = 164,
+  SpvOpLogicalNotEqual = 165,
+  SpvOpLogicalOr = 166,
+  SpvOpLogicalAnd = 167,
+  SpvOpLogicalNot = 168,
+  SpvOpSelect = 169,
+  SpvOpIEqual = 170,
+  SpvOpINotEqual = 171,
+  SpvOpUGreaterThan = 172,
+  SpvOpSGreaterThan = 173,
+  SpvOpUGreaterThanEqual = 174,
+  SpvOpSGreaterThanEqual = 175,
+  SpvOpULessThan = 176,
+  SpvOpSLessThan = 177,
+  SpvOpULessThanEqual = 178,
+  SpvOpSLessThanEqual = 179,
+  SpvOpFOrdEqual = 180,
+  SpvOpFUnordEqual = 181,
+  SpvOpFOrdNotEqual = 182,
+  SpvOpFUnordNotEqual = 183,
+  SpvOpFOrdLessThan = 184,
+  SpvOpFUnordLessThan = 185,
+  SpvOpFOrdGreaterThan = 186,
+  SpvOpFUnordGreaterThan = 187,
+  SpvOpFOrdLessThanEqual = 188,
+  SpvOpFUnordLessThanEqual = 189,
+  SpvOpFOrdGreaterThanEqual = 190,
+  SpvOpFUnordGreaterThanEqual = 191,
+  SpvOpShiftRightLogical = 194,
+  SpvOpShiftRightArithmetic = 195,
+  SpvOpShiftLeftLogical = 196,
+  SpvOpBitwiseOr = 197,
+  SpvOpBitwiseXor = 198,
+  SpvOpBitwiseAnd = 199,
+  SpvOpNot = 200,
+  SpvOpBitFieldInsert = 201,
+  SpvOpBitFieldSExtract = 202,
+  SpvOpBitFieldUExtract = 203,
+  SpvOpBitReverse = 204,
+  SpvOpBitCount = 205,
+  SpvOpDPdx = 207,
+  SpvOpDPdy = 208,
+  SpvOpFwidth = 209,
+  SpvOpDPdxFine = 210,
+  SpvOpDPdyFine = 211,
+  SpvOpFwidthFine = 212,
+  SpvOpDPdxCoarse = 213,
+  SpvOpDPdyCoarse = 214,
+  SpvOpFwidthCoarse = 215,
+  SpvOpEmitVertex = 218,
+  SpvOpEndPrimitive = 219,
+  SpvOpEmitStreamVertex = 220,
+  SpvOpEndStreamPrimitive = 221,
+  SpvOpControlBarrier = 224,
+  SpvOpMemoryBarrier = 225,
+  SpvOpAtomicLoad = 227,
+  SpvOpAtomicStore = 228,
+  SpvOpAtomicExchange = 229,
+  SpvOpAtomicCompareExchange = 230,
+  SpvOpAtomicCompareExchangeWeak = 231,
+  SpvOpAtomicIIncrement = 232,
+  SpvOpAtomicIDecrement = 233,
+  SpvOpAtomicIAdd = 234,
+  SpvOpAtomicISub = 235,
+  SpvOpAtomicSMin = 236,
+  SpvOpAtomicUMin = 237,
+  SpvOpAtomicSMax = 238,
+  SpvOpAtomicUMax = 239,
+  SpvOpAtomicAnd = 240,
+  SpvOpAtomicOr = 241,
+  SpvOpAtomicXor = 242,
+  SpvOpPhi = 245,
+  SpvOpLoopMerge = 246,
+  SpvOpSelectionMerge = 247,
+  SpvOpLabel = 248,
+  SpvOpBranch = 249,
+  SpvOpBranchConditional = 250,
+  SpvOpSwitch = 251,
+  SpvOpKill = 252,
+  SpvOpReturn = 253,
+  SpvOpReturnValue = 254,
+  SpvOpUnreachable = 255,
+  SpvOpLifetimeStart = 256,
+  SpvOpLifetimeStop = 257,
+  SpvOpGroupAsyncCopy = 259,
+  SpvOpGroupWaitEvents = 260,
+  SpvOpGroupAll = 261,
+  SpvOpGroupAny = 262,
+  SpvOpGroupBroadcast = 263,
+  SpvOpGroupIAdd = 264,
+  SpvOpGroupFAdd = 265,
+  SpvOpGroupFMin = 266,
+  SpvOpGroupUMin = 267,
+  SpvOpGroupSMin = 268,
+  SpvOpGroupFMax = 269,
+  SpvOpGroupUMax = 270,
+  SpvOpGroupSMax = 271,
+  SpvOpReadPipe = 274,
+  SpvOpWritePipe = 275,
+  SpvOpReservedReadPipe = 276,
+  SpvOpReservedWritePipe = 277,
+  SpvOpReserveReadPipePackets = 278,
+  SpvOpReserveWritePipePackets = 279,
+  SpvOpCommitReadPipe = 280,
+  SpvOpCommitWritePipe = 281,
+  SpvOpIsValidReserveId = 282,
+  SpvOpGetNumPipePackets = 283,
+  SpvOpGetMaxPipePackets = 284,
+  SpvOpGroupReserveReadPipePackets = 285,
+  SpvOpGroupReserveWritePipePackets = 286,
+  SpvOpGroupCommitReadPipe = 287,
+  SpvOpGroupCommitWritePipe = 288,
+  SpvOpEnqueueMarker = 291,
+  SpvOpEnqueueKernel = 292,
+  SpvOpGetKernelNDrangeSubGroupCount = 293,
+  SpvOpGetKernelNDrangeMaxSubGroupSize = 294,
+  SpvOpGetKernelWorkGroupSize = 295,
+  SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296,
+  SpvOpRetainEvent = 297,
+  SpvOpReleaseEvent = 298,
+  SpvOpCreateUserEvent = 299,
+  SpvOpIsValidEvent = 300,
+  SpvOpSetUserEventStatus = 301,
+  SpvOpCaptureEventProfilingInfo = 302,
+  SpvOpGetDefaultQueue = 303,
+  SpvOpBuildNDRange = 304,
+  SpvOpImageSparseSampleImplicitLod = 305,
+  SpvOpImageSparseSampleExplicitLod = 306,
+  SpvOpImageSparseSampleDrefImplicitLod = 307,
+  SpvOpImageSparseSampleDrefExplicitLod = 308,
+  SpvOpImageSparseSampleProjImplicitLod = 309,
+  SpvOpImageSparseSampleProjExplicitLod = 310,
+  SpvOpImageSparseSampleProjDrefImplicitLod = 311,
+  SpvOpImageSparseSampleProjDrefExplicitLod = 312,
+  SpvOpImageSparseFetch = 313,
+  SpvOpImageSparseGather = 314,
+  SpvOpImageSparseDrefGather = 315,
+  SpvOpImageSparseTexelsResident = 316,
+  SpvOpNoLine = 317,
+  SpvOpAtomicFlagTestAndSet = 318,
+  SpvOpAtomicFlagClear = 319,
+  SpvOpImageSparseRead = 320,
+  SpvOpSizeOf = 321,
+  SpvOpTypePipeStorage = 322,
+  SpvOpConstantPipeStorage = 323,
+  SpvOpCreatePipeFromPipeStorage = 324,
+  SpvOpGetKernelLocalSizeForSubgroupCount = 325,
+  SpvOpGetKernelMaxNumSubgroups = 326,
+  SpvOpTypeNamedBarrier = 327,
+  SpvOpNamedBarrierInitialize = 328,
+  SpvOpMemoryNamedBarrier = 329,
+  SpvOpModuleProcessed = 330,
+  SpvOpExecutionModeId = 331,
+  SpvOpDecorateId = 332,
+  SpvOpGroupNonUniformElect = 333,
+  SpvOpGroupNonUniformAll = 334,
+  SpvOpGroupNonUniformAny = 335,
+  SpvOpGroupNonUniformAllEqual = 336,
+  SpvOpGroupNonUniformBroadcast = 337,
+  SpvOpGroupNonUniformBroadcastFirst = 338,
+  SpvOpGroupNonUniformBallot = 339,
+  SpvOpGroupNonUniformInverseBallot = 340,
+  SpvOpGroupNonUniformBallotBitExtract = 341,
+  SpvOpGroupNonUniformBallotBitCount = 342,
+  SpvOpGroupNonUniformBallotFindLSB = 343,
+  SpvOpGroupNonUniformBallotFindMSB = 344,
+  SpvOpGroupNonUniformShuffle = 345,
+  SpvOpGroupNonUniformShuffleXor = 346,
+  SpvOpGroupNonUniformShuffleUp = 347,
+  SpvOpGroupNonUniformShuffleDown = 348,
+  SpvOpGroupNonUniformIAdd = 349,
+  SpvOpGroupNonUniformFAdd = 350,
+  SpvOpGroupNonUniformIMul = 351,
+  SpvOpGroupNonUniformFMul = 352,
+  SpvOpGroupNonUniformSMin = 353,
+  SpvOpGroupNonUniformUMin = 354,
+  SpvOpGroupNonUniformFMin = 355,
+  SpvOpGroupNonUniformSMax = 356,
+  SpvOpGroupNonUniformUMax = 357,
+  SpvOpGroupNonUniformFMax = 358,
+  SpvOpGroupNonUniformBitwiseAnd = 359,
+  SpvOpGroupNonUniformBitwiseOr = 360,
+  SpvOpGroupNonUniformBitwiseXor = 361,
+  SpvOpGroupNonUniformLogicalAnd = 362,
+  SpvOpGroupNonUniformLogicalOr = 363,
+  SpvOpGroupNonUniformLogicalXor = 364,
+  SpvOpGroupNonUniformQuadBroadcast = 365,
+  SpvOpGroupNonUniformQuadSwap = 366,
+  SpvOpCopyLogical = 400,
+  SpvOpPtrEqual = 401,
+  SpvOpPtrNotEqual = 402,
+  SpvOpPtrDiff = 403,
+  SpvOpColorAttachmentReadEXT = 4160,
+  SpvOpDepthAttachmentReadEXT = 4161,
+  SpvOpStencilAttachmentReadEXT = 4162,
+  SpvOpTerminateInvocation = 4416,
+  SpvOpSubgroupBallotKHR = 4421,
+  SpvOpSubgroupFirstInvocationKHR = 4422,
+  SpvOpSubgroupAllKHR = 4428,
+  SpvOpSubgroupAnyKHR = 4429,
+  SpvOpSubgroupAllEqualKHR = 4430,
+  SpvOpGroupNonUniformRotateKHR = 4431,
+  SpvOpSubgroupReadInvocationKHR = 4432,
+  SpvOpTraceRayKHR = 4445,
+  SpvOpExecuteCallableKHR = 4446,
+  SpvOpConvertUToAccelerationStructureKHR = 4447,
+  SpvOpIgnoreIntersectionKHR = 4448,
+  SpvOpTerminateRayKHR = 4449,
+  SpvOpSDot = 4450,
+  SpvOpSDotKHR = 4450,
+  SpvOpUDot = 4451,
+  SpvOpUDotKHR = 4451,
+  SpvOpSUDot = 4452,
+  SpvOpSUDotKHR = 4452,
+  SpvOpSDotAccSat = 4453,
+  SpvOpSDotAccSatKHR = 4453,
+  SpvOpUDotAccSat = 4454,
+  SpvOpUDotAccSatKHR = 4454,
+  SpvOpSUDotAccSat = 4455,
+  SpvOpSUDotAccSatKHR = 4455,
+  SpvOpTypeCooperativeMatrixKHR = 4456,
+  SpvOpCooperativeMatrixLoadKHR = 4457,
+  SpvOpCooperativeMatrixStoreKHR = 4458,
+  SpvOpCooperativeMatrixMulAddKHR = 4459,
+  SpvOpCooperativeMatrixLengthKHR = 4460,
+  SpvOpTypeRayQueryKHR = 4472,
+  SpvOpRayQueryInitializeKHR = 4473,
+  SpvOpRayQueryTerminateKHR = 4474,
+  SpvOpRayQueryGenerateIntersectionKHR = 4475,
+  SpvOpRayQueryConfirmIntersectionKHR = 4476,
+  SpvOpRayQueryProceedKHR = 4477,
+  SpvOpRayQueryGetIntersectionTypeKHR = 4479,
+  SpvOpImageSampleWeightedQCOM = 4480,
+  SpvOpImageBoxFilterQCOM = 4481,
+  SpvOpImageBlockMatchSSDQCOM = 4482,
+  SpvOpImageBlockMatchSADQCOM = 4483,
+  SpvOpGroupIAddNonUniformAMD = 5000,
+  SpvOpGroupFAddNonUniformAMD = 5001,
+  SpvOpGroupFMinNonUniformAMD = 5002,
+  SpvOpGroupUMinNonUniformAMD = 5003,
+  SpvOpGroupSMinNonUniformAMD = 5004,
+  SpvOpGroupFMaxNonUniformAMD = 5005,
+  SpvOpGroupUMaxNonUniformAMD = 5006,
+  SpvOpGroupSMaxNonUniformAMD = 5007,
+  SpvOpFragmentMaskFetchAMD = 5011,
+  SpvOpFragmentFetchAMD = 5012,
+  SpvOpReadClockKHR = 5056,
+  SpvOpHitObjectRecordHitMotionNV = 5249,
+  SpvOpHitObjectRecordHitWithIndexMotionNV = 5250,
+  SpvOpHitObjectRecordMissMotionNV = 5251,
+  SpvOpHitObjectGetWorldToObjectNV = 5252,
+  SpvOpHitObjectGetObjectToWorldNV = 5253,
+  SpvOpHitObjectGetObjectRayDirectionNV = 5254,
+  SpvOpHitObjectGetObjectRayOriginNV = 5255,
+  SpvOpHitObjectTraceRayMotionNV = 5256,
+  SpvOpHitObjectGetShaderRecordBufferHandleNV = 5257,
+  SpvOpHitObjectGetShaderBindingTableRecordIndexNV = 5258,
+  SpvOpHitObjectRecordEmptyNV = 5259,
+  SpvOpHitObjectTraceRayNV = 5260,
+  SpvOpHitObjectRecordHitNV = 5261,
+  SpvOpHitObjectRecordHitWithIndexNV = 5262,
+  SpvOpHitObjectRecordMissNV = 5263,
+  SpvOpHitObjectExecuteShaderNV = 5264,
+  SpvOpHitObjectGetCurrentTimeNV = 5265,
+  SpvOpHitObjectGetAttributesNV = 5266,
+  SpvOpHitObjectGetHitKindNV = 5267,
+  SpvOpHitObjectGetPrimitiveIndexNV = 5268,
+  SpvOpHitObjectGetGeometryIndexNV = 5269,
+  SpvOpHitObjectGetInstanceIdNV = 5270,
+  SpvOpHitObjectGetInstanceCustomIndexNV = 5271,
+  SpvOpHitObjectGetWorldRayDirectionNV = 5272,
+  SpvOpHitObjectGetWorldRayOriginNV = 5273,
+  SpvOpHitObjectGetRayTMaxNV = 5274,
+  SpvOpHitObjectGetRayTMinNV = 5275,
+  SpvOpHitObjectIsEmptyNV = 5276,
+  SpvOpHitObjectIsHitNV = 5277,
+  SpvOpHitObjectIsMissNV = 5278,
+  SpvOpReorderThreadWithHitObjectNV = 5279,
+  SpvOpReorderThreadWithHintNV = 5280,
+  SpvOpTypeHitObjectNV = 5281,
+  SpvOpImageSampleFootprintNV = 5283,
+  SpvOpEmitMeshTasksEXT = 5294,
+  SpvOpSetMeshOutputsEXT = 5295,
+  SpvOpGroupNonUniformPartitionNV = 5296,
+  SpvOpWritePackedPrimitiveIndices4x8NV = 5299,
+  SpvOpReportIntersectionKHR = 5334,
+  SpvOpReportIntersectionNV = 5334,
+  SpvOpIgnoreIntersectionNV = 5335,
+  SpvOpTerminateRayNV = 5336,
+  SpvOpTraceNV = 5337,
+  SpvOpTraceMotionNV = 5338,
+  SpvOpTraceRayMotionNV = 5339,
+  SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340,
+  SpvOpTypeAccelerationStructureKHR = 5341,
+  SpvOpTypeAccelerationStructureNV = 5341,
+  SpvOpExecuteCallableNV = 5344,
+  SpvOpTypeCooperativeMatrixNV = 5358,
+  SpvOpCooperativeMatrixLoadNV = 5359,
+  SpvOpCooperativeMatrixStoreNV = 5360,
+  SpvOpCooperativeMatrixMulAddNV = 5361,
+  SpvOpCooperativeMatrixLengthNV = 5362,
+  SpvOpBeginInvocationInterlockEXT = 5364,
+  SpvOpEndInvocationInterlockEXT = 5365,
+  SpvOpDemoteToHelperInvocation = 5380,
+  SpvOpDemoteToHelperInvocationEXT = 5380,
+  SpvOpIsHelperInvocationEXT = 5381,
+  SpvOpConvertUToImageNV = 5391,
+  SpvOpConvertUToSamplerNV = 5392,
+  SpvOpConvertImageToUNV = 5393,
+  SpvOpConvertSamplerToUNV = 5394,
+  SpvOpConvertUToSampledImageNV = 5395,
+  SpvOpConvertSampledImageToUNV = 5396,
+  SpvOpSamplerImageAddressingModeNV = 5397,
+  SpvOpSubgroupShuffleINTEL = 5571,
+  SpvOpSubgroupShuffleDownINTEL = 5572,
+  SpvOpSubgroupShuffleUpINTEL = 5573,
+  SpvOpSubgroupShuffleXorINTEL = 5574,
+  SpvOpSubgroupBlockReadINTEL = 5575,
+  SpvOpSubgroupBlockWriteINTEL = 5576,
+  SpvOpSubgroupImageBlockReadINTEL = 5577,
+  SpvOpSubgroupImageBlockWriteINTEL = 5578,
+  SpvOpSubgroupImageMediaBlockReadINTEL = 5580,
+  SpvOpSubgroupImageMediaBlockWriteINTEL = 5581,
+  SpvOpUCountLeadingZerosINTEL = 5585,
+  SpvOpUCountTrailingZerosINTEL = 5586,
+  SpvOpAbsISubINTEL = 5587,
+  SpvOpAbsUSubINTEL = 5588,
+  SpvOpIAddSatINTEL = 5589,
+  SpvOpUAddSatINTEL = 5590,
+  SpvOpIAverageINTEL = 5591,
+  SpvOpUAverageINTEL = 5592,
+  SpvOpIAverageRoundedINTEL = 5593,
+  SpvOpUAverageRoundedINTEL = 5594,
+  SpvOpISubSatINTEL = 5595,
+  SpvOpUSubSatINTEL = 5596,
+  SpvOpIMul32x16INTEL = 5597,
+  SpvOpUMul32x16INTEL = 5598,
+  SpvOpConstantFunctionPointerINTEL = 5600,
+  SpvOpFunctionPointerCallINTEL = 5601,
+  SpvOpAsmTargetINTEL = 5609,
+  SpvOpAsmINTEL = 5610,
+  SpvOpAsmCallINTEL = 5611,
+  SpvOpAtomicFMinEXT = 5614,
+  SpvOpAtomicFMaxEXT = 5615,
+  SpvOpAssumeTrueKHR = 5630,
+  SpvOpExpectKHR = 5631,
+  SpvOpDecorateString = 5632,
+  SpvOpDecorateStringGOOGLE = 5632,
+  SpvOpMemberDecorateString = 5633,
+  SpvOpMemberDecorateStringGOOGLE = 5633,
+  SpvOpVmeImageINTEL = 5699,
+  SpvOpTypeVmeImageINTEL = 5700,
+  SpvOpTypeAvcImePayloadINTEL = 5701,
+  SpvOpTypeAvcRefPayloadINTEL = 5702,
+  SpvOpTypeAvcSicPayloadINTEL = 5703,
+  SpvOpTypeAvcMcePayloadINTEL = 5704,
+  SpvOpTypeAvcMceResultINTEL = 5705,
+  SpvOpTypeAvcImeResultINTEL = 5706,
+  SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707,
+  SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708,
+  SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709,
+  SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710,
+  SpvOpTypeAvcRefResultINTEL = 5711,
+  SpvOpTypeAvcSicResultINTEL = 5712,
+  SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713,
+  SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714,
+  SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715,
+  SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716,
+  SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717,
+  SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718,
+  SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719,
+  SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720,
+  SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721,
+  SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722,
+  SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723,
+  SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724,
+  SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725,
+  SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726,
+  SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727,
+  SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728,
+  SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729,
+  SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730,
+  SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731,
+  SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732,
+  SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733,
+  SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734,
+  SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735,
+  SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736,
+  SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737,
+  SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738,
+  SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739,
+  SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740,
+  SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741,
+  SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742,
+  SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743,
+  SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744,
+  SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745,
+  SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746,
+  SpvOpSubgroupAvcImeInitializeINTEL = 5747,
+  SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748,
+  SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749,
+  SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750,
+  SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751,
+  SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752,
+  SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753,
+  SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754,
+  SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755,
+  SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756,
+  SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757,
+  SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758,
+  SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759,
+  SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760,
+  SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761,
+  SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762,
+  SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763,
+  SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764,
+  SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765,
+  SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766,
+  SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767,
+  SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768,
+  SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769,
+  SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL =
+      5770,
+  SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL =
+      5771,
+  SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL =
+      5772,
+  SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL =
+      5773,
+  SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774,
+  SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL =
+      5775,
+  SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776,
+  SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777,
+  SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778,
+  SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779,
+  SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780,
+  SpvOpSubgroupAvcFmeInitializeINTEL = 5781,
+  SpvOpSubgroupAvcBmeInitializeINTEL = 5782,
+  SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783,
+  SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784,
+  SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785,
+  SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786,
+  SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787,
+  SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788,
+  SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789,
+  SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790,
+  SpvOpSubgroupAvcSicInitializeINTEL = 5791,
+  SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792,
+  SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793,
+  SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794,
+  SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795,
+  SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796,
+  SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797,
+  SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798,
+  SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799,
+  SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800,
+  SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801,
+  SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802,
+  SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803,
+  SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804,
+  SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805,
+  SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806,
+  SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807,
+  SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808,
+  SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809,
+  SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810,
+  SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811,
+  SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812,
+  SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813,
+  SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814,
+  SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815,
+  SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816,
+  SpvOpVariableLengthArrayINTEL = 5818,
+  SpvOpSaveMemoryINTEL = 5819,
+  SpvOpRestoreMemoryINTEL = 5820,
+  SpvOpArbitraryFloatSinCosPiINTEL = 5840,
+  SpvOpArbitraryFloatCastINTEL = 5841,
+  SpvOpArbitraryFloatCastFromIntINTEL = 5842,
+  SpvOpArbitraryFloatCastToIntINTEL = 5843,
+  SpvOpArbitraryFloatAddINTEL = 5846,
+  SpvOpArbitraryFloatSubINTEL = 5847,
+  SpvOpArbitraryFloatMulINTEL = 5848,
+  SpvOpArbitraryFloatDivINTEL = 5849,
+  SpvOpArbitraryFloatGTINTEL = 5850,
+  SpvOpArbitraryFloatGEINTEL = 5851,
+  SpvOpArbitraryFloatLTINTEL = 5852,
+  SpvOpArbitraryFloatLEINTEL = 5853,
+  SpvOpArbitraryFloatEQINTEL = 5854,
+  SpvOpArbitraryFloatRecipINTEL = 5855,
+  SpvOpArbitraryFloatRSqrtINTEL = 5856,
+  SpvOpArbitraryFloatCbrtINTEL = 5857,
+  SpvOpArbitraryFloatHypotINTEL = 5858,
+  SpvOpArbitraryFloatSqrtINTEL = 5859,
+  SpvOpArbitraryFloatLogINTEL = 5860,
+  SpvOpArbitraryFloatLog2INTEL = 5861,
+  SpvOpArbitraryFloatLog10INTEL = 5862,
+  SpvOpArbitraryFloatLog1pINTEL = 5863,
+  SpvOpArbitraryFloatExpINTEL = 5864,
+  SpvOpArbitraryFloatExp2INTEL = 5865,
+  SpvOpArbitraryFloatExp10INTEL = 5866,
+  SpvOpArbitraryFloatExpm1INTEL = 5867,
+  SpvOpArbitraryFloatSinINTEL = 5868,
+  SpvOpArbitraryFloatCosINTEL = 5869,
+  SpvOpArbitraryFloatSinCosINTEL = 5870,
+  SpvOpArbitraryFloatSinPiINTEL = 5871,
+  SpvOpArbitraryFloatCosPiINTEL = 5872,
+  SpvOpArbitraryFloatASinINTEL = 5873,
+  SpvOpArbitraryFloatASinPiINTEL = 5874,
+  SpvOpArbitraryFloatACosINTEL = 5875,
+  SpvOpArbitraryFloatACosPiINTEL = 5876,
+  SpvOpArbitraryFloatATanINTEL = 5877,
+  SpvOpArbitraryFloatATanPiINTEL = 5878,
+  SpvOpArbitraryFloatATan2INTEL = 5879,
+  SpvOpArbitraryFloatPowINTEL = 5880,
+  SpvOpArbitraryFloatPowRINTEL = 5881,
+  SpvOpArbitraryFloatPowNINTEL = 5882,
+  SpvOpLoopControlINTEL = 5887,
+  SpvOpAliasDomainDeclINTEL = 5911,
+  SpvOpAliasScopeDeclINTEL = 5912,
+  SpvOpAliasScopeListDeclINTEL = 5913,
+  SpvOpFixedSqrtINTEL = 5923,
+  SpvOpFixedRecipINTEL = 5924,
+  SpvOpFixedRsqrtINTEL = 5925,
+  SpvOpFixedSinINTEL = 5926,
+  SpvOpFixedCosINTEL = 5927,
+  SpvOpFixedSinCosINTEL = 5928,
+  SpvOpFixedSinPiINTEL = 5929,
+  SpvOpFixedCosPiINTEL = 5930,
+  SpvOpFixedSinCosPiINTEL = 5931,
+  SpvOpFixedLogINTEL = 5932,
+  SpvOpFixedExpINTEL = 5933,
+  SpvOpPtrCastToCrossWorkgroupINTEL = 5934,
+  SpvOpCrossWorkgroupCastToPtrINTEL = 5938,
+  SpvOpReadPipeBlockingINTEL = 5946,
+  SpvOpWritePipeBlockingINTEL = 5947,
+  SpvOpFPGARegINTEL = 5949,
+  SpvOpRayQueryGetRayTMinKHR = 6016,
+  SpvOpRayQueryGetRayFlagsKHR = 6017,
+  SpvOpRayQueryGetIntersectionTKHR = 6018,
+  SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019,
+  SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020,
+  SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021,
+  SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022,
+  SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023,
+  SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024,
+  SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025,
+  SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026,
+  SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027,
+  SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028,
+  SpvOpRayQueryGetWorldRayDirectionKHR = 6029,
+  SpvOpRayQueryGetWorldRayOriginKHR = 6030,
+  SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031,
+  SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032,
+  SpvOpAtomicFAddEXT = 6035,
+  SpvOpTypeBufferSurfaceINTEL = 6086,
+  SpvOpTypeStructContinuedINTEL = 6090,
+  SpvOpConstantCompositeContinuedINTEL = 6091,
+  SpvOpSpecConstantCompositeContinuedINTEL = 6092,
+  SpvOpConvertFToBF16INTEL = 6116,
+  SpvOpConvertBF16ToFINTEL = 6117,
+  SpvOpControlBarrierArriveINTEL = 6142,
+  SpvOpControlBarrierWaitINTEL = 6143,
+  SpvOpGroupIMulKHR = 6401,
+  SpvOpGroupFMulKHR = 6402,
+  SpvOpGroupBitwiseAndKHR = 6403,
+  SpvOpGroupBitwiseOrKHR = 6404,
+  SpvOpGroupBitwiseXorKHR = 6405,
+  SpvOpGroupLogicalAndKHR = 6406,
+  SpvOpGroupLogicalOrKHR = 6407,
+  SpvOpGroupLogicalXorKHR = 6408,
+  SpvOpMax = 0x7fffffff,
+} SpvOp;
+
+#ifdef SPV_ENABLE_UTILITY_CODE
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+inline void SpvHasResultAndType(SpvOp opcode, bool* hasResult,
+                                bool* hasResultType) {
+  *hasResult = *hasResultType = false;
+  switch (opcode) {
+    default: /* unknown opcode */
+      break;
+    case SpvOpNop:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpUndef:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSourceContinued:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSource:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSourceExtension:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpName:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpMemberName:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpString:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpLine:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpExtension:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpExtInstImport:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpExtInst:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpMemoryModel:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpEntryPoint:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpExecutionMode:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCapability:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeVoid:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeBool:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeInt:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeFloat:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeVector:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeMatrix:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeImage:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeSampler:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeSampledImage:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeArray:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeRuntimeArray:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeStruct:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeOpaque:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypePointer:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeFunction:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeEvent:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeDeviceEvent:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeReserveId:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeQueue:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypePipe:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeForwardPointer:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpConstantTrue:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConstantFalse:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConstant:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConstantComposite:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConstantSampler:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConstantNull:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSpecConstantTrue:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSpecConstantFalse:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSpecConstant:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSpecConstantComposite:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSpecConstantOp:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFunction:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFunctionParameter:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFunctionEnd:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpFunctionCall:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpVariable:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageTexelPointer:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLoad:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpStore:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCopyMemory:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCopyMemorySized:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpAccessChain:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpInBoundsAccessChain:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPtrAccessChain:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArrayLength:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGenericPtrMemSemantics:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpInBoundsPtrAccessChain:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDecorate:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpMemberDecorate:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpDecorationGroup:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupDecorate:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupMemberDecorate:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpVectorExtractDynamic:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpVectorInsertDynamic:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpVectorShuffle:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCompositeConstruct:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCompositeExtract:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCompositeInsert:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCopyObject:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTranspose:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSampledImage:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleDrefImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleDrefExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleProjImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleProjExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleProjDrefImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleProjDrefExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageFetch:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageGather:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageDrefGather:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageRead:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageWrite:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpImage:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQueryFormat:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQueryOrder:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQuerySizeLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQuerySize:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQueryLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQueryLevels:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageQuerySamples:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertFToU:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertFToS:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertSToF:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertUToF:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUConvert:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSConvert:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFConvert:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpQuantizeToF16:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertPtrToU:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSatConvertSToU:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSatConvertUToS:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertUToPtr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPtrCastToGeneric:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGenericCastToPtr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGenericCastToPtrExplicit:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitcast:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSNegate:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFNegate:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpISub:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFSub:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIMul:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFMul:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUDiv:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSDiv:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFDiv:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUMod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSRem:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSMod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFRem:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFMod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpVectorTimesScalar:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpMatrixTimesScalar:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpVectorTimesMatrix:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpMatrixTimesVector:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpMatrixTimesMatrix:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpOuterProduct:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIAddCarry:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpISubBorrow:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUMulExtended:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSMulExtended:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAny:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAll:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIsNan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIsInf:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIsFinite:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIsNormal:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSignBitSet:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLessOrGreater:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpOrdered:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUnordered:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLogicalEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLogicalNotEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLogicalOr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLogicalAnd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLogicalNot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSelect:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpINotEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUGreaterThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSGreaterThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUGreaterThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSGreaterThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpULessThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSLessThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpULessThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSLessThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFOrdEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFUnordEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFOrdNotEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFUnordNotEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFOrdLessThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFUnordLessThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFOrdGreaterThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFUnordGreaterThan:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFOrdLessThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFUnordLessThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFOrdGreaterThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFUnordGreaterThanEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpShiftRightLogical:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpShiftRightArithmetic:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpShiftLeftLogical:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitwiseOr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitwiseXor:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitwiseAnd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpNot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitFieldInsert:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitFieldSExtract:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitFieldUExtract:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitReverse:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBitCount:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDPdx:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDPdy:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFwidth:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDPdxFine:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDPdyFine:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFwidthFine:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDPdxCoarse:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDPdyCoarse:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFwidthCoarse:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpEmitVertex:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpEndPrimitive:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpEmitStreamVertex:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpEndStreamPrimitive:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpControlBarrier:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpMemoryBarrier:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpAtomicLoad:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicStore:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpAtomicExchange:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicCompareExchange:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicCompareExchangeWeak:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicIIncrement:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicIDecrement:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicIAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicISub:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicSMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicUMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicSMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicUMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicAnd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicOr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicXor:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPhi:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLoopMerge:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSelectionMerge:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpLabel:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpBranch:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpBranchConditional:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSwitch:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpKill:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpReturn:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpReturnValue:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpUnreachable:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpLifetimeStart:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpLifetimeStop:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupAsyncCopy:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupWaitEvents:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupAll:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupAny:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupBroadcast:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupIAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupUMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupSMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupUMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupSMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReadPipe:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpWritePipe:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReservedReadPipe:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReservedWritePipe:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReserveReadPipePackets:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReserveWritePipePackets:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCommitReadPipe:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCommitWritePipe:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpIsValidReserveId:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetNumPipePackets:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetMaxPipePackets:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupReserveReadPipePackets:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupReserveWritePipePackets:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupCommitReadPipe:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupCommitWritePipe:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpEnqueueMarker:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpEnqueueKernel:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetKernelNDrangeSubGroupCount:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetKernelNDrangeMaxSubGroupSize:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetKernelWorkGroupSize:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRetainEvent:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpReleaseEvent:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCreateUserEvent:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIsValidEvent:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSetUserEventStatus:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCaptureEventProfilingInfo:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGetDefaultQueue:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBuildNDRange:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleDrefImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleDrefExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleProjImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleProjExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleProjDrefImplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseSampleProjDrefExplicitLod:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseFetch:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseGather:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseDrefGather:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSparseTexelsResident:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpNoLine:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpAtomicFlagTestAndSet:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicFlagClear:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpImageSparseRead:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSizeOf:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypePipeStorage:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpConstantPipeStorage:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCreatePipeFromPipeStorage:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetKernelLocalSizeForSubgroupCount:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGetKernelMaxNumSubgroups:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypeNamedBarrier:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpNamedBarrierInitialize:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpMemoryNamedBarrier:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpModuleProcessed:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpExecutionModeId:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpDecorateId:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupNonUniformElect:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformAll:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformAny:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformAllEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBroadcast:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBroadcastFirst:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBallot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformInverseBallot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBallotBitExtract:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBallotBitCount:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBallotFindLSB:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBallotFindMSB:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformShuffle:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformShuffleXor:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformShuffleUp:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformShuffleDown:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformIAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformFAdd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformIMul:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformFMul:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformSMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformUMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformFMin:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformSMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformUMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformFMax:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBitwiseAnd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBitwiseOr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformBitwiseXor:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformLogicalAnd:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformLogicalOr:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformLogicalXor:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformQuadBroadcast:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformQuadSwap:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCopyLogical:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPtrEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPtrNotEqual:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPtrDiff:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpColorAttachmentReadEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDepthAttachmentReadEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpStencilAttachmentReadEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTerminateInvocation:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSubgroupBallotKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupFirstInvocationKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAllKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAnyKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAllEqualKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupNonUniformRotateKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupReadInvocationKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTraceRayKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpExecuteCallableKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpConvertUToAccelerationStructureKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIgnoreIntersectionKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTerminateRayKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSDot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUDot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSUDot:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSDotAccSat:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUDotAccSat:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSUDotAccSat:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypeCooperativeMatrixKHR:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpCooperativeMatrixLoadKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCooperativeMatrixStoreKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCooperativeMatrixMulAddKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCooperativeMatrixLengthKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypeRayQueryKHR:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpRayQueryInitializeKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpRayQueryTerminateKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpRayQueryGenerateIntersectionKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpRayQueryConfirmIntersectionKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpRayQueryProceedKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionTypeKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageSampleWeightedQCOM:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageBoxFilterQCOM:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageBlockMatchSSDQCOM:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpImageBlockMatchSADQCOM:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupIAddNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFAddNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFMinNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupUMinNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupSMinNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFMaxNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupUMaxNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupSMaxNonUniformAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFragmentMaskFetchAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFragmentFetchAMD:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReadClockKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectRecordHitMotionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectRecordHitWithIndexMotionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectRecordMissMotionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectGetWorldToObjectNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetObjectToWorldNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetObjectRayDirectionNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetObjectRayOriginNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectTraceRayMotionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectGetShaderRecordBufferHandleNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetShaderBindingTableRecordIndexNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectRecordEmptyNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectTraceRayNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectRecordHitNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectRecordHitWithIndexNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectRecordMissNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectExecuteShaderNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectGetCurrentTimeNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetAttributesNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpHitObjectGetHitKindNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetPrimitiveIndexNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetGeometryIndexNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetInstanceIdNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetInstanceCustomIndexNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetWorldRayDirectionNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetWorldRayOriginNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetRayTMaxNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectGetRayTMinNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectIsEmptyNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectIsHitNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpHitObjectIsMissNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReorderThreadWithHitObjectNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpReorderThreadWithHintNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeHitObjectNV:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpImageSampleFootprintNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpEmitMeshTasksEXT:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSetMeshOutputsEXT:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupNonUniformPartitionNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpWritePackedPrimitiveIndices4x8NV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpReportIntersectionNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIgnoreIntersectionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTerminateRayNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTraceNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTraceMotionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTraceRayMotionNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypeAccelerationStructureNV:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpExecuteCallableNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeCooperativeMatrixNV:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpCooperativeMatrixLoadNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCooperativeMatrixStoreNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpCooperativeMatrixMulAddNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCooperativeMatrixLengthNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpBeginInvocationInterlockEXT:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpEndInvocationInterlockEXT:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpDemoteToHelperInvocation:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpIsHelperInvocationEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertUToImageNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertUToSamplerNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertImageToUNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertSamplerToUNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertUToSampledImageNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertSampledImageToUNV:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSamplerImageAddressingModeNV:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSubgroupShuffleINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupShuffleDownINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupShuffleUpINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupShuffleXorINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupBlockReadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupBlockWriteINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSubgroupImageBlockReadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupImageBlockWriteINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSubgroupImageMediaBlockReadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupImageMediaBlockWriteINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpUCountLeadingZerosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUCountTrailingZerosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAbsISubINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAbsUSubINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIAddSatINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUAddSatINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIAverageINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUAverageINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIAverageRoundedINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUAverageRoundedINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpISubSatINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUSubSatINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpIMul32x16INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpUMul32x16INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConstantFunctionPointerINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFunctionPointerCallINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAsmTargetINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAsmINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAsmCallINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicFMinEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicFMaxEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAssumeTrueKHR:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpExpectKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpDecorateString:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpMemberDecorateString:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpVmeImageINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypeVmeImageINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcImePayloadINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcRefPayloadINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcSicPayloadINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcMcePayloadINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcMceResultINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcImeResultINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcImeSingleReferenceStreaminINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcImeDualReferenceStreaminINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcRefResultINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeAvcSicResultINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceConvertToImePayloadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceConvertToImeResultINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceConvertToRefResultINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceConvertToSicResultINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetMotionVectorsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterDistortionsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterDirectionsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeInitializeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeSetSingleReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeSetDualReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeRefWindowSizeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeSetWeightedSadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeConvertToMceResultINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetBorderReachedINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcFmeInitializeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcBmeInitializeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcRefConvertToMceResultINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicInitializeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicConfigureSkcINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicEvaluateIpeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicConvertToMceResultINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSubgroupAvcSicGetInterRawSadsINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpVariableLengthArrayINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpSaveMemoryINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRestoreMemoryINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpArbitraryFloatSinCosPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatCastINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatCastFromIntINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatCastToIntINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatAddINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatSubINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatMulINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatDivINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatGTINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatGEINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatLTINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatLEINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatEQINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatRecipINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatRSqrtINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatCbrtINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatHypotINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatSqrtINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatLogINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatLog2INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatLog10INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatLog1pINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatExpINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatExp2INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatExp10INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatExpm1INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatSinINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatCosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatSinCosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatSinPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatCosPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatASinINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatASinPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatACosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatACosPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatATanINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatATanPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatATan2INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatPowINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatPowRINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpArbitraryFloatPowNINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpLoopControlINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpAliasDomainDeclINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpAliasScopeDeclINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpAliasScopeListDeclINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpFixedSqrtINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedRecipINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedRsqrtINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedSinINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedCosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedSinCosINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedSinPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedCosPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedSinCosPiINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedLogINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFixedExpINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpPtrCastToCrossWorkgroupINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpCrossWorkgroupCastToPtrINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpReadPipeBlockingINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpWritePipeBlockingINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpFPGARegINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetRayTMinKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetRayFlagsKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionTKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionInstanceIdKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionGeometryIndexKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionBarycentricsKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionFrontFaceKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionObjectRayOriginKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetWorldRayDirectionKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetWorldRayOriginKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionObjectToWorldKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpRayQueryGetIntersectionWorldToObjectKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpAtomicFAddEXT:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpTypeBufferSurfaceINTEL:
+      *hasResult = true;
+      *hasResultType = false;
+      break;
+    case SpvOpTypeStructContinuedINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpConstantCompositeContinuedINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpSpecConstantCompositeContinuedINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpConvertFToBF16INTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpConvertBF16ToFINTEL:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpControlBarrierArriveINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpControlBarrierWaitINTEL:
+      *hasResult = false;
+      *hasResultType = false;
+      break;
+    case SpvOpGroupIMulKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupFMulKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupBitwiseAndKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupBitwiseOrKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupBitwiseXorKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupLogicalAndKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupLogicalOrKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+    case SpvOpGroupLogicalXorKHR:
+      *hasResult = true;
+      *hasResultType = true;
+      break;
+  }
+}
+#endif /* SPV_ENABLE_UTILITY_CODE */
+
+#endif
diff --git a/third-party/SPIRV-Reflect/spirv_reflect.c b/third-party/SPIRV-Reflect/spirv_reflect.c
new file mode 100644 (file)
index 0000000..efb1149
--- /dev/null
@@ -0,0 +1,5482 @@
+/*
+ Copyright 2017-2022 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 <crtdbg.h>
+#include <stdlib.h>
+#else
+#include <stdlib.h>
+#endif
+
+#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 7) || defined(__APPLE_CC__)
+#define FALLTHROUGH __attribute__((fallthrough))
+#else
+#define FALLTHROUGH
+#endif
+
+#if defined(SPIRV_REFLECT_ENABLE_ASSERTS)
+#define SPV_REFLECT_ASSERT(COND) assert(COND);
+#else
+#define SPV_REFLECT_ASSERT(COND)
+#endif
+
+// 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,
+};
+
+enum {
+  INVALID_VALUE  = 0xFFFFFFFF,
+};
+
+enum {
+  MAX_NODE_NAME_LENGTH        = 1024,
+  // Number of unique PhysicalStorageBuffer structs tracked to detect recursion
+  MAX_RECURSIVE_PHYSICAL_POINTER_CHECK = 128,
+};
+
+enum {
+  IMAGE_SAMPLED = 1,
+  IMAGE_STORAGE = 2,
+};
+
+typedef struct SpvReflectPrvArrayTraits {
+  uint32_t                        element_type_id;
+  uint32_t                        length_id;
+} SpvReflectPrvArrayTraits;
+
+typedef struct SpvReflectPrvImageTraits {
+  uint32_t                        sampled_type_id;
+  SpvDim                          dim;
+  uint32_t                        depth;
+  uint32_t                        arrayed;
+  uint32_t                        ms;
+  uint32_t                        sampled;
+  SpvImageFormat                  image_format;
+} SpvReflectPrvImageTraits;
+
+typedef struct SpvReflectPrvNumberDecoration {
+  uint32_t                        word_offset;
+  uint32_t                        value;
+} SpvReflectPrvNumberDecoration;
+
+typedef struct SpvReflectPrvStringDecoration {
+  uint32_t                        word_offset;
+  const char*                     value;
+} SpvReflectPrvStringDecoration;
+
+typedef struct SpvReflectPrvDecorations {
+  bool                            is_relaxed_precision;
+  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;
+  bool                            is_non_readable;
+  bool                            is_patch;
+  bool                            is_per_vertex;
+  bool                            is_per_task;
+  bool                            is_weight_texture;
+  bool                            is_block_match_texture;
+  SpvReflectUserType              user_type;
+  SpvReflectPrvNumberDecoration   set;
+  SpvReflectPrvNumberDecoration   binding;
+  SpvReflectPrvNumberDecoration   input_attachment_index;
+  SpvReflectPrvNumberDecoration   location;
+  SpvReflectPrvNumberDecoration   component;
+  SpvReflectPrvNumberDecoration   offset;
+  SpvReflectPrvNumberDecoration   uav_counter_buffer;
+  SpvReflectPrvStringDecoration   semantic;
+  uint32_t                        array_stride;
+  uint32_t                        matrix_stride;
+  uint32_t                        spec_id;
+  SpvBuiltIn                      built_in;
+} SpvReflectPrvDecorations;
+
+typedef struct SpvReflectPrvNode {
+  uint32_t                        result_id;
+  SpvOp                           op;
+  uint32_t                        result_type_id;
+  uint32_t                        type_id;
+  SpvCapability                   capability;
+  SpvStorageClass                 storage_class;
+  uint32_t                        word_offset;
+  uint32_t                        word_count;
+  bool                            is_type;
+
+  SpvReflectPrvArrayTraits        array_traits;
+  SpvReflectPrvImageTraits        image_traits;
+  uint32_t                        image_type_id;
+
+  const char*                     name;
+  SpvReflectPrvDecorations        decorations;
+  uint32_t                        member_count;
+  const char**                    member_names;
+  SpvReflectPrvDecorations*       member_decorations;
+} SpvReflectPrvNode;
+
+typedef struct SpvReflectPrvString {
+  uint32_t                        result_id;
+  const char*                     string;
+} SpvReflectPrvString;
+
+// There are a limit set of instructions that can touch an OpVariable,
+// these are represented here with how it was accessed
+// Examples:
+//    OpImageRead  -> OpLoad -> OpVariable
+//    OpImageWrite -> OpLoad -> OpVariable
+//    OpStore      -> OpAccessChain -> OpAccessChain -> OpVariable
+//    OpAtomicIAdd -> OpAccessChain -> OpVariable
+//    OpAtomicLoad -> OpImageTexelPointer -> OpVariable
+typedef struct SpvReflectPrvAccessedVariable {
+  SpvReflectPrvNode*     p_node;
+  uint32_t               result_id;
+  uint32_t               variable_ptr;
+  uint32_t               function_id;
+  uint32_t               function_parameter_index;
+} SpvReflectPrvAccessedVariable;
+
+typedef struct SpvReflectPrvFunction {
+  uint32_t                        id;
+  uint32_t                        parameter_count;
+  uint32_t*                       parameters;
+  uint32_t                        callee_count;
+  uint32_t*                       callees;
+  struct SpvReflectPrvFunction**  callee_ptrs;
+  uint32_t                        accessed_variable_count;
+  SpvReflectPrvAccessedVariable*  accessed_variables;
+} SpvReflectPrvFunction;
+
+typedef struct SpvReflectPrvAccessChain {
+  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;
+  //
+  // Block variable ac is pointing to (for block references)
+  SpvReflectBlockVariable*        block_var;
+} SpvReflectPrvAccessChain;
+
+// To prevent infinite recursion, we never walk down a
+// PhysicalStorageBuffer struct twice, but incase a 2nd variable
+// needs to use that struct, save a copy
+typedef struct SpvReflectPrvPhysicalPointerStruct {
+    uint32_t struct_id;
+    // first variable to see the PhysicalStorageBuffer struct
+    SpvReflectBlockVariable* p_var;
+} SpvReflectPrvPhysicalPointerStruct;
+
+typedef struct SpvReflectPrvParser {
+  size_t                          spirv_word_count;
+  uint32_t*                       spirv_code;
+  uint32_t                        string_count;
+  SpvReflectPrvString*            strings;
+  SpvSourceLanguage               source_language;
+  uint32_t                        source_language_version;
+  uint32_t                        source_file_id;
+  const char*                     source_embedded;
+  size_t                          node_count;
+  SpvReflectPrvNode*              nodes;
+  uint32_t                        entry_point_count;
+  uint32_t                        capability_count;
+  uint32_t                        function_count;
+  SpvReflectPrvFunction*          functions;
+  uint32_t                        access_chain_count;
+  SpvReflectPrvAccessChain*       access_chains;
+
+  uint32_t                        type_count;
+  uint32_t                        descriptor_count;
+  uint32_t                        push_constant_count;
+
+  SpvReflectTypeDescription*      physical_pointer_check[MAX_RECURSIVE_PHYSICAL_POINTER_CHECK];
+  uint32_t                        physical_pointer_count;
+
+  SpvReflectPrvPhysicalPointerStruct* physical_pointer_structs;
+  uint32_t                            physical_pointer_struct_count;
+} SpvReflectPrvParser;
+// clang-format on
+
+static uint32_t Max(uint32_t a, uint32_t b) { return a > b ? a : b; }
+static uint32_t Min(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) \
+  {                   \
+    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;
+}
+
+static int SortCompareAccessedVariable(const void* a, const void* b) {
+  const SpvReflectPrvAccessedVariable* p_a = (const SpvReflectPrvAccessedVariable*)a;
+  const SpvReflectPrvAccessedVariable* p_b = (const SpvReflectPrvAccessedVariable*)b;
+
+  return (int)p_a->variable_ptr - (int)p_b->variable_ptr;
+}
+
+//
+// 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 IntersectSortedAccessedVariable(const SpvReflectPrvAccessedVariable* p_arr0, size_t arr0_size,
+                                                        const uint32_t* p_arr1, size_t arr1_size, uint32_t** pp_res,
+                                                        size_t* res_size) {
+  *pp_res = NULL;
+  *res_size = 0;
+  if (IsNull(p_arr0) || IsNull(p_arr1)) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  const SpvReflectPrvAccessedVariable* p_arr0_end = p_arr0 + arr0_size;
+  const uint32_t* p_arr1_end = p_arr1 + arr1_size;
+
+  const SpvReflectPrvAccessedVariable* p_idx0 = p_arr0;
+  const uint32_t* p_idx1 = p_arr1;
+  while (p_idx0 != p_arr0_end && p_idx1 != p_arr1_end) {
+    if (p_idx0->variable_ptr < *p_idx1) {
+      ++p_idx0;
+    } else if (p_idx0->variable_ptr > *p_idx1) {
+      ++p_idx1;
+    } else {
+      ++*res_size;
+      ++p_idx0;
+      ++p_idx1;
+    }
+  }
+
+  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* p_idxr = *pp_res;
+    p_idx0 = p_arr0;
+    p_idx1 = p_arr1;
+    while (p_idx0 != p_arr0_end && p_idx1 != p_arr1_end) {
+      if (p_idx0->variable_ptr < *p_idx1) {
+        ++p_idx0;
+      } else if (p_idx0->variable_ptr > *p_idx1) {
+        ++p_idx1;
+      } else {
+        *(p_idxr++) = p_idx0->variable_ptr;
+        ++p_idx0;
+        ++p_idx1;
+      }
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static bool InRange(const SpvReflectPrvParser* 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(SpvReflectPrvParser* 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 UNCHECKED_READU32(parser, word_offset, value) \
+  { (void)ReadU32(parser, word_offset, (uint32_t*)&(value)); }
+
+#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(SpvReflectPrvParser* 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 SpvReflectPrvDecorations* p_decoration_fields) {
+  SpvReflectDecorationFlags decorations = SPV_REFLECT_DECORATION_NONE;
+  if (p_decoration_fields->is_relaxed_precision) {
+    decorations |= SPV_REFLECT_DECORATION_RELAXED_PRECISION;
+  }
+  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;
+  }
+  if (p_decoration_fields->is_non_readable) {
+    decorations |= SPV_REFLECT_DECORATION_NON_READABLE;
+  }
+  if (p_decoration_fields->is_patch) {
+    decorations |= SPV_REFLECT_DECORATION_PATCH;
+  }
+  if (p_decoration_fields->is_per_vertex) {
+    decorations |= SPV_REFLECT_DECORATION_PER_VERTEX;
+  }
+  if (p_decoration_fields->is_per_task) {
+    decorations |= SPV_REFLECT_DECORATION_PER_TASK;
+  }
+  if (p_decoration_fields->is_weight_texture) {
+    decorations |= SPV_REFLECT_DECORATION_WEIGHT_TEXTURE;
+  }
+  if (p_decoration_fields->is_block_match_texture) {
+    decorations |= SPV_REFLECT_DECORATION_BLOCK_MATCH_TEXTURE;
+  }
+  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 bool IsSpecConstant(const SpvReflectPrvNode* p_node) {
+  return (p_node->op == SpvOpSpecConstant || p_node->op == SpvOpSpecConstantOp || p_node->op == SpvOpSpecConstantTrue ||
+          p_node->op == SpvOpSpecConstantFalse);
+}
+
+static SpvReflectPrvNode* FindNode(SpvReflectPrvParser* p_parser, uint32_t result_id) {
+  SpvReflectPrvNode* p_node = NULL;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* 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 SpvReflectPrvAccessChain* FindAccessChain(SpvReflectPrvParser* p_parser, uint32_t id) {
+  const uint32_t ac_count = p_parser->access_chain_count;
+  for (uint32_t i = 0; i < ac_count; i++) {
+    if (p_parser->access_chains[i].result_id == id) {
+      return &p_parser->access_chains[i];
+    }
+  }
+  return 0;
+}
+
+// Access Chains mostly have their Base ID pointed directly to a OpVariable, but sometimes
+// it will be through a load and this funciton handles the edge cases how to find that
+static uint32_t FindAccessChainBaseVariable(SpvReflectPrvParser* p_parser, SpvReflectPrvAccessChain* p_access_chain) {
+  uint32_t base_id = p_access_chain->base_id;
+  SpvReflectPrvNode* base_node = FindNode(p_parser, base_id);
+  // TODO - This is just a band-aid to fix crashes.
+  // Need to understand why here and hopefully remove
+  // https://github.com/KhronosGroup/SPIRV-Reflect/pull/206
+  if (IsNull(base_node)) {
+    return 0;
+  }
+  while (base_node->op != SpvOpVariable) {
+    switch (base_node->op) {
+      case SpvOpLoad: {
+        UNCHECKED_READU32(p_parser, base_node->word_offset + 3, base_id);
+      } break;
+      case SpvOpFunctionParameter: {
+        UNCHECKED_READU32(p_parser, base_node->word_offset + 2, base_id);
+      } break;
+      case SpvOpBitcast:
+        // This can be caused by something like GL_EXT_buffer_reference_uvec2 trying to load a pointer.
+        // We currently call from a push constant, so no way to have a reference loop back into the PC block
+        return 0;
+      default: {
+        assert(false);
+      } break;
+    }
+
+    SpvReflectPrvAccessChain* base_ac = FindAccessChain(p_parser, base_id);
+    if (base_ac == 0) {
+      return 0;
+    }
+    base_id = base_ac->base_id;
+    base_node = FindNode(p_parser, base_id);
+    if (IsNull(base_node)) {
+      return 0;
+    }
+  }
+  return base_id;
+}
+
+static SpvReflectBlockVariable* GetRefBlkVar(SpvReflectPrvParser* p_parser, SpvReflectPrvAccessChain* p_access_chain) {
+  uint32_t base_id = p_access_chain->base_id;
+  SpvReflectPrvNode* base_node = FindNode(p_parser, base_id);
+  assert(base_node->op == SpvOpLoad);
+  UNCHECKED_READU32(p_parser, base_node->word_offset + 3, base_id);
+  SpvReflectPrvAccessChain* base_ac = FindAccessChain(p_parser, base_id);
+  assert(base_ac != 0);
+  SpvReflectBlockVariable* base_var = base_ac->block_var;
+  assert(base_var != 0);
+  return base_var;
+}
+
+bool IsPointerToPointer(SpvReflectPrvParser* p_parser, uint32_t type_id) {
+  SpvReflectPrvNode* ptr_node = FindNode(p_parser, type_id);
+  if (IsNull(ptr_node) || (ptr_node->op != SpvOpTypePointer)) {
+    return false;
+  }
+  uint32_t pte_id = 0;
+  UNCHECKED_READU32(p_parser, ptr_node->word_offset + 3, pte_id);
+  SpvReflectPrvNode* pte_node = FindNode(p_parser, pte_id);
+  if (IsNull(pte_node)) {
+    return false;
+  }
+  return pte_node->op == SpvOpTypePointer;
+}
+
+static SpvReflectResult CreateParser(size_t size, void* p_code, SpvReflectPrvParser* 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(SpvReflectPrvParser* p_parser) {
+  if (!IsNull(p_parser->nodes)) {
+    // Free nodes
+    for (size_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* 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].parameters);
+      SafeFree(p_parser->functions[i].callees);
+      SafeFree(p_parser->functions[i].callee_ptrs);
+      SafeFree(p_parser->functions[i].accessed_variables);
+    }
+
+    // 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->source_embedded);
+    SafeFree(p_parser->functions);
+    SafeFree(p_parser->access_chains);
+
+    if (IsNotNull(p_parser->physical_pointer_structs)) {
+      SafeFree(p_parser->physical_pointer_structs);
+    }
+    p_parser->node_count = 0;
+  }
+}
+
+static SpvReflectResult ParseNodes(SpvReflectPrvParser* 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 = (SpvReflectPrvNode*)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.component.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.spec_id = (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;
+  p_parser->source_embedded = NULL;
+
+  // Function node
+  uint32_t function_node = (uint32_t)INVALID_VALUE;
+
+  // Allocate access chain
+  if (p_parser->access_chain_count > 0) {
+    p_parser->access_chains = (SpvReflectPrvAccessChain*)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;
+
+    SpvReflectPrvNode* 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);
+        }
+        if (p_node->word_count >= 5) {
+          const char* p_source = (const char*)(p_parser->spirv_code + p_node->word_offset + 4);
+
+          const size_t source_len = strlen(p_source);
+          char* p_source_temp = (char*)calloc(source_len + 1, sizeof(char));
+
+          if (IsNull(p_source_temp)) {
+            return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+          }
+
+#ifdef _WIN32
+          strcpy_s(p_source_temp, source_len + 1, p_source);
+#else
+          strcpy(p_source_temp, p_source);
+#endif
+
+          SafeFree(p_parser->source_embedded);
+          p_parser->source_embedded = p_source_temp;
+        }
+      } break;
+
+      case SpvOpSourceContinued: {
+        const char* p_source = (const char*)(p_parser->spirv_code + p_node->word_offset + 1);
+
+        const size_t source_len = strlen(p_source);
+        const size_t embedded_source_len = strlen(p_parser->source_embedded);
+        char* p_continued_source = (char*)calloc(source_len + embedded_source_len + 1, sizeof(char));
+
+        if (IsNull(p_continued_source)) {
+          return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+        }
+
+#ifdef _WIN32
+        strcpy_s(p_continued_source, embedded_source_len + 1, p_parser->source_embedded);
+        strcat_s(p_continued_source, embedded_source_len + source_len + 1, p_source);
+#else
+        strcpy(p_continued_source, p_parser->source_embedded);
+        strcat(p_continued_source, p_source);
+#endif
+
+        SafeFree(p_parser->source_embedded);
+        p_parser->source_embedded = p_continued_source;
+      } break;
+
+      case SpvOpEntryPoint: {
+        ++(p_parser->entry_point_count);
+      } break;
+
+      case SpvOpCapability: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->capability);
+        ++(p_parser->capability_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;
+        FALLTHROUGH;
+      }  // Fall through
+
+      // This is all the rest of OpType* that need to be tracked
+      // Possible new extensions might expose new type, will need to be added
+      // here
+      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:
+      case SpvOpTypeAccelerationStructureKHR:
+      case SpvOpTypeRayQueryKHR:
+      case SpvOpTypeHitObjectNV:
+      case SpvOpTypeCooperativeMatrixNV:
+      case SpvOpTypeCooperativeMatrixKHR: {
+        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: {
+        uint32_t result_id;
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, result_id);
+        // Look for forward pointer. Clear result id if found
+        SpvReflectPrvNode* p_fwd_node = FindNode(p_parser, result_id);
+        if (p_fwd_node) {
+          p_fwd_node->result_id = 0;
+        }
+        // Register pointer type
+        p_node->result_id = 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: {
+        SpvReflectPrvAccessChain* 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
+            SpvReflectPrvNode* p_index_value_node = FindNode(p_parser, index_id);
+            if ((p_index_value_node != NULL) &&
+                (p_index_value_node->op == SpvOpConstant || p_index_value_node->op == SpvOpSpecConstant)) {
+              // 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) {
+          SpvReflectPrvNode* 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);
+        }
+        FALLTHROUGH;
+      }  // Fall through
+
+      case SpvOpFunctionEnd: {
+        function_node = (uint32_t)INVALID_VALUE;
+      } break;
+      case SpvOpFunctionParameter: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+      } break;
+      case SpvOpBitcast:
+      case SpvOpShiftRightLogical:
+      case SpvOpIAdd:
+      case SpvOpISub:
+      case SpvOpIMul:
+      case SpvOpUDiv:
+      case SpvOpSDiv: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+      } 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(SpvReflectPrvParser* 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 = (SpvReflectPrvString*)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) {
+      SpvReflectPrvNode* 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
+      SpvReflectPrvString* 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(SpvReflectPrvParser* 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) {
+        SpvReflectPrvString* p_string = &(p_parser->strings[i]);
+        if (p_string->result_id == p_parser->source_file_id) {
+          p_module->source_file = p_string->string;
+          break;
+        }
+      }
+    }
+
+    // Source code
+    if (IsNotNull(p_parser->source_embedded)) {
+      const size_t source_len = strlen(p_parser->source_embedded);
+      char* p_source = (char*)calloc(source_len + 1, sizeof(char));
+
+      if (IsNull(p_source)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+
+#ifdef _WIN32
+      strcpy_s(p_source, source_len + 1, p_parser->source_embedded);
+#else
+      strcpy(p_source, p_parser->source_embedded);
+#endif
+
+      p_module->source_source = p_source;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseFunction(SpvReflectPrvParser* p_parser, SpvReflectPrvNode* p_func_node, SpvReflectPrvFunction* p_func,
+                                      size_t first_label_index) {
+  p_func->id = p_func_node->result_id;
+
+  p_func->parameter_count = 0;
+  p_func->callee_count = 0;
+  p_func->accessed_variable_count = 0;
+
+  // First get count to know how much to allocate
+  for (size_t i = first_label_index; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (p_node->op == SpvOpFunctionEnd) {
+      break;
+    }
+    switch (p_node->op) {
+      case SpvOpFunctionParameter: {
+        ++(p_func->parameter_count);
+      } break;
+      case SpvOpFunctionCall: {
+        p_func->accessed_variable_count += p_node->word_count - 4;
+        ++(p_func->callee_count);
+      } break;
+      case SpvOpLoad:
+      case SpvOpAccessChain:
+      case SpvOpInBoundsAccessChain:
+      case SpvOpPtrAccessChain:
+      case SpvOpArrayLength:
+      case SpvOpGenericPtrMemSemantics:
+      case SpvOpInBoundsPtrAccessChain:
+      case SpvOpStore:
+      case SpvOpImageTexelPointer: {
+        ++(p_func->accessed_variable_count);
+      } break;
+      case SpvOpCopyMemory:
+      case SpvOpCopyMemorySized: {
+        p_func->accessed_variable_count += 2;
+      } break;
+      default:
+        break;
+    }
+  }
+
+  if (p_func->parameter_count > 0) {
+    p_func->parameters = (uint32_t*)calloc(p_func->parameter_count, sizeof(*(p_func->parameters)));
+    if (IsNull(p_func->parameters)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  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_variable_count > 0) {
+    p_func->accessed_variables =
+        (SpvReflectPrvAccessedVariable*)calloc(p_func->accessed_variable_count, sizeof(*(p_func->accessed_variables)));
+    if (IsNull(p_func->accessed_variables)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  p_func->parameter_count = 0;
+  p_func->callee_count = 0;
+  p_func->accessed_variable_count = 0;
+  // Now have allocation, fill in values
+  for (size_t i = first_label_index; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (p_node->op == SpvOpFunctionEnd) {
+      break;
+    }
+    switch (p_node->op) {
+      case SpvOpFunctionParameter: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_func->parameters[p_func->parameter_count]);
+        (++p_func->parameter_count);
+      } break;
+      case SpvOpFunctionCall: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_func->callees[p_func->callee_count]);
+        const uint32_t result_index = p_node->word_offset + 2;
+        for (uint32_t j = 0, parameter_count = p_node->word_count - 4; j < parameter_count; j++) {
+          const uint32_t ptr_index = p_node->word_offset + 4 + j;
+          SpvReflectPrvAccessedVariable* access_ptr = &p_func->accessed_variables[p_func->accessed_variable_count];
+
+          access_ptr->p_node = p_node;
+          // Need to track Result ID as not sure there has been any memory access through here yet
+          CHECKED_READU32(p_parser, result_index, access_ptr->result_id);
+          CHECKED_READU32(p_parser, ptr_index, access_ptr->variable_ptr);
+          access_ptr->function_id = p_func->callees[p_func->callee_count];
+          access_ptr->function_parameter_index = j;
+          (++p_func->accessed_variable_count);
+        }
+        (++p_func->callee_count);
+      } break;
+      case SpvOpLoad:
+      case SpvOpAccessChain:
+      case SpvOpInBoundsAccessChain:
+      case SpvOpPtrAccessChain:
+      case SpvOpArrayLength:
+      case SpvOpGenericPtrMemSemantics:
+      case SpvOpInBoundsPtrAccessChain:
+      case SpvOpImageTexelPointer: {
+        const uint32_t result_index = p_node->word_offset + 2;
+        const uint32_t ptr_index = p_node->word_offset + 3;
+        SpvReflectPrvAccessedVariable* access_ptr = &p_func->accessed_variables[p_func->accessed_variable_count];
+
+        access_ptr->p_node = p_node;
+        // Need to track Result ID as not sure there has been any memory access through here yet
+        CHECKED_READU32(p_parser, result_index, access_ptr->result_id);
+        CHECKED_READU32(p_parser, ptr_index, access_ptr->variable_ptr);
+        (++p_func->accessed_variable_count);
+      } break;
+      case SpvOpStore: {
+        const uint32_t result_index = p_node->word_offset + 2;
+        CHECKED_READU32(p_parser, result_index, p_func->accessed_variables[p_func->accessed_variable_count].variable_ptr);
+        p_func->accessed_variables[p_func->accessed_variable_count].p_node = p_node;
+        (++p_func->accessed_variable_count);
+      } break;
+      case SpvOpCopyMemory:
+      case SpvOpCopyMemorySized: {
+        // There is no result_id or node, being zero is same as being invalid
+        CHECKED_READU32(p_parser, p_node->word_offset + 1,
+                        p_func->accessed_variables[p_func->accessed_variable_count].variable_ptr);
+        (++p_func->accessed_variable_count);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2,
+                        p_func->accessed_variables[p_func->accessed_variable_count].variable_ptr);
+        (++p_func->accessed_variable_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_variable_count > 0) {
+    qsort(p_func->accessed_variables, p_func->accessed_variable_count, sizeof(*(p_func->accessed_variables)),
+          SortCompareAccessedVariable);
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static int SortCompareFunctions(const void* a, const void* b) {
+  const SpvReflectPrvFunction* af = (const SpvReflectPrvFunction*)a;
+  const SpvReflectPrvFunction* bf = (const SpvReflectPrvFunction*)b;
+  return (int)af->id - (int)bf->id;
+}
+
+static SpvReflectResult ParseFunctions(SpvReflectPrvParser* 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 = (SpvReflectPrvFunction*)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) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if (p_node->op != SpvOpFunction) {
+        continue;
+      }
+
+      // Skip over function declarations that aren't definitions
+      bool func_definition = false;
+      for (size_t j = i; j < p_parser->node_count; ++j) {
+        if (p_parser->nodes[j].op == SpvOpLabel) {
+          func_definition = true;
+          break;
+        }
+        if (p_parser->nodes[j].op == SpvOpFunctionEnd) {
+          break;
+        }
+      }
+      if (!func_definition) {
+        continue;
+      }
+
+      SpvReflectPrvFunction* 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) {
+      SpvReflectPrvFunction* p_func = &(p_parser->functions[i]);
+      if (p_func->callee_count == 0) {
+        continue;
+      }
+      p_func->callee_ptrs = (SpvReflectPrvFunction**)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(SpvReflectPrvParser* 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) {
+      SpvReflectPrvNode* 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);
+      SpvReflectPrvNode* 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) {
+      SpvReflectPrvNode* 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 = (SpvReflectPrvDecorations*)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(SpvReflectPrvParser* 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) {
+      SpvReflectPrvNode* 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);
+      SpvReflectPrvNode* 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;
+}
+
+// Returns true if user_type matches pattern or if user_type begins with pattern and the next character is ':'
+// For example, UserTypeMatches("rwbuffer", "rwbuffer") will be true, UserTypeMatches("rwbuffer", "rwbuffer:<S>") will be true, and
+// UserTypeMatches("rwbuffer", "rwbufferfoo") will be false.
+static bool UserTypeMatches(const char* user_type, const char* pattern) {
+  const size_t pattern_length = strlen(pattern);
+  if (strncmp(user_type, pattern, pattern_length) == 0) {
+    if (user_type[pattern_length] == ':' || user_type[pattern_length] == '\0') {
+      return true;
+    }
+  }
+  return false;
+}
+
+static SpvReflectResult ParseDecorations(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module) {
+  uint32_t spec_constant_count = 0;
+  for (uint32_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+
+    if ((p_node->op != SpvOpDecorate) && (p_node->op != SpvOpMemberDecorate) && (p_node->op != SpvOpDecorateId) &&
+        (p_node->op != SpvOpDecorateString) && (p_node->op != SpvOpMemberDecorateString)) {
+      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 SpvDecorationRelaxedPrecision:
+      case SpvDecorationBlock:
+      case SpvDecorationBufferBlock:
+      case SpvDecorationColMajor:
+      case SpvDecorationRowMajor:
+      case SpvDecorationArrayStride:
+      case SpvDecorationMatrixStride:
+      case SpvDecorationBuiltIn:
+      case SpvDecorationNoPerspective:
+      case SpvDecorationFlat:
+      case SpvDecorationNonWritable:
+      case SpvDecorationNonReadable:
+      case SpvDecorationPatch:
+      case SpvDecorationPerVertexKHR:
+      case SpvDecorationPerTaskNV:
+      case SpvDecorationLocation:
+      case SpvDecorationComponent:
+      case SpvDecorationBinding:
+      case SpvDecorationDescriptorSet:
+      case SpvDecorationOffset:
+      case SpvDecorationInputAttachmentIndex:
+      case SpvDecorationSpecId:
+      case SpvDecorationWeightTextureQCOM:
+      case SpvDecorationBlockMatchTextureQCOM:
+      case SpvDecorationUserTypeGOOGLE:
+      case SpvDecorationHlslCounterBufferGOOGLE:
+      case SpvDecorationHlslSemanticGOOGLE: {
+        skip = false;
+      } break;
+    }
+    if (skip) {
+      continue;
+    }
+
+    // Find target node
+    uint32_t target_id = 0;
+    CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
+    SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
+    if (IsNull(p_target_node)) {
+      if ((p_node->op == (uint32_t)SpvOpDecorate) && (decoration == SpvDecorationRelaxedPrecision)) {
+        // Many OPs can be decorated that we don't care about. Ignore those.
+        // See https://github.com/KhronosGroup/SPIRV-Reflect/issues/134
+        continue;
+      }
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+    // Get decorations
+    SpvReflectPrvDecorations* p_target_decorations = &(p_target_node->decorations);
+    // Update pointer if this is a 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 SpvDecorationRelaxedPrecision: {
+        p_target_decorations->is_relaxed_precision = true;
+      } 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 SpvDecorationNonReadable: {
+        p_target_decorations->is_non_readable = true;
+      } break;
+
+      case SpvDecorationPatch: {
+        p_target_decorations->is_patch = true;
+      } break;
+
+      case SpvDecorationPerVertexKHR: {
+        p_target_decorations->is_per_vertex = true;
+      } break;
+
+      case SpvDecorationPerTaskNV: {
+        p_target_decorations->is_per_task = 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 SpvDecorationComponent: {
+        uint32_t word_offset = p_node->word_offset + member_offset + 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->component.value);
+        p_target_decorations->component.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 SpvDecorationSpecId: {
+        spec_constant_count++;
+      } break;
+
+      case SpvDecorationHlslCounterBufferGOOGLE: {
+        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 SpvDecorationHlslSemanticGOOGLE: {
+        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;
+
+      case SpvDecorationWeightTextureQCOM: {
+        p_target_decorations->is_weight_texture = true;
+      } break;
+
+      case SpvDecorationBlockMatchTextureQCOM: {
+        p_target_decorations->is_block_match_texture = true;
+      } break;
+    }
+
+    if (p_node->op == SpvOpDecorateString && decoration == SpvDecorationUserTypeGOOGLE) {
+      uint32_t terminator = 0;
+      SpvReflectResult result = ReadStr(p_parser, p_node->word_offset + 3, 0, p_node->word_count, &terminator, NULL);
+      if (result != SPV_REFLECT_RESULT_SUCCESS) {
+        return result;
+      }
+      const char* name = (const char*)(p_parser->spirv_code + p_node->word_offset + 3);
+      if (UserTypeMatches(name, "cbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_CBUFFER;
+      } else if (UserTypeMatches(name, "tbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TBUFFER;
+      } else if (UserTypeMatches(name, "appendstructuredbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_APPEND_STRUCTURED_BUFFER;
+      } else if (UserTypeMatches(name, "buffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_BUFFER;
+      } else if (UserTypeMatches(name, "byteaddressbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_BYTE_ADDRESS_BUFFER;
+      } else if (UserTypeMatches(name, "constantbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_CONSTANT_BUFFER;
+      } else if (UserTypeMatches(name, "consumestructuredbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_CONSUME_STRUCTURED_BUFFER;
+      } else if (UserTypeMatches(name, "inputpatch")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_INPUT_PATCH;
+      } else if (UserTypeMatches(name, "outputpatch")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_OUTPUT_PATCH;
+      } else if (UserTypeMatches(name, "rasterizerorderedbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_BUFFER;
+      } else if (UserTypeMatches(name, "rasterizerorderedbyteaddressbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_BYTE_ADDRESS_BUFFER;
+      } else if (UserTypeMatches(name, "rasterizerorderedstructuredbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_STRUCTURED_BUFFER;
+      } else if (UserTypeMatches(name, "rasterizerorderedtexture1d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_1D;
+      } else if (UserTypeMatches(name, "rasterizerorderedtexture1darray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_1D_ARRAY;
+      } else if (UserTypeMatches(name, "rasterizerorderedtexture2d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_2D;
+      } else if (UserTypeMatches(name, "rasterizerorderedtexture2darray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_2D_ARRAY;
+      } else if (UserTypeMatches(name, "rasterizerorderedtexture3d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_3D;
+      } else if (UserTypeMatches(name, "raytracingaccelerationstructure")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RAYTRACING_ACCELERATION_STRUCTURE;
+      } else if (UserTypeMatches(name, "rwbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_BUFFER;
+      } else if (UserTypeMatches(name, "rwbyteaddressbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_BYTE_ADDRESS_BUFFER;
+      } else if (UserTypeMatches(name, "rwstructuredbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_STRUCTURED_BUFFER;
+      } else if (UserTypeMatches(name, "rwtexture1d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_TEXTURE_1D;
+      } else if (UserTypeMatches(name, "rwtexture1darray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_TEXTURE_1D_ARRAY;
+      } else if (UserTypeMatches(name, "rwtexture2d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_TEXTURE_2D;
+      } else if (UserTypeMatches(name, "rwtexture2darray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_TEXTURE_2D_ARRAY;
+      } else if (UserTypeMatches(name, "rwtexture3d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_RW_TEXTURE_3D;
+      } else if (UserTypeMatches(name, "structuredbuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_STRUCTURED_BUFFER;
+      } else if (UserTypeMatches(name, "subpassinput")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_SUBPASS_INPUT;
+      } else if (UserTypeMatches(name, "subpassinputms")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_SUBPASS_INPUT_MS;
+      } else if (UserTypeMatches(name, "texture1d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_1D;
+      } else if (UserTypeMatches(name, "texture1darray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_1D_ARRAY;
+      } else if (UserTypeMatches(name, "texture2d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_2D;
+      } else if (UserTypeMatches(name, "texture2darray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_2D_ARRAY;
+      } else if (UserTypeMatches(name, "texture2dms")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_2DMS;
+      } else if (UserTypeMatches(name, "texture2dmsarray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_2DMS_ARRAY;
+      } else if (UserTypeMatches(name, "texture3d")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_3D;
+      } else if (UserTypeMatches(name, "texturebuffer")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_BUFFER;
+      } else if (UserTypeMatches(name, "texturecube")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_CUBE;
+      } else if (UserTypeMatches(name, "texturecubearray")) {
+        p_target_decorations->user_type = SPV_REFLECT_USER_TYPE_TEXTURE_CUBE_ARRAY;
+      }
+    }
+  }
+
+  if (spec_constant_count > 0) {
+    p_module->spec_constants = (SpvReflectSpecializationConstant*)calloc(spec_constant_count, sizeof(*p_module->spec_constants));
+    if (IsNull(p_module->spec_constants)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+  for (uint32_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (p_node->op == SpvOpDecorate) {
+      uint32_t decoration = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, decoration);
+      if (decoration == SpvDecorationSpecId) {
+        const uint32_t count = p_module->spec_constant_count;
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_module->spec_constants[count].spirv_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_module->spec_constants[count].constant_id);
+        // If being used for a OpSpecConstantComposite (ex. LocalSizeId), there won't be a name
+        SpvReflectPrvNode* target_node = FindNode(p_parser, p_module->spec_constants[count].spirv_id);
+        if (IsNotNull(target_node)) {
+          p_module->spec_constants[count].name = target_node->name;
+        }
+        p_module->spec_constant_count++;
+      }
+    }
+  }
+
+  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(SpvReflectPrvParser* p_parser, SpvReflectPrvNode* p_node,
+                                  SpvReflectPrvDecorations* p_struct_member_decorations, SpvReflectShaderModule* p_module,
+                                  SpvReflectTypeDescription* p_type) {
+  SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS;
+
+  if (p_node->member_count > 0) {
+    p_type->struct_type_description = FindType(p_module, p_node->result_id);
+    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
+        SpvReflectPrvNode* 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;
+          SPV_REFLECT_ASSERT(false);
+        }
+      } 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);
+        SpvReflectPrvNode* 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;
+          SPV_REFLECT_ASSERT(false);
+        }
+        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;
+        uint32_t sampled_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, sampled_type_id);
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, sampled_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;
+        }
+        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);
+        SpvReflectPrvNode* 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;
+          SPV_REFLECT_ASSERT(false);
+        }
+      } 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
+          SpvReflectPrvNode* p_length_node = FindNode(p_parser, length_id);
+          if (IsNotNull(p_length_node)) {
+            uint32_t dim_index = p_type->traits.array.dims_count;
+            uint32_t length = 0;
+            IF_READU32(result, p_parser, p_length_node->word_offset + 3, length);
+            if (result == SPV_REFLECT_RESULT_SUCCESS) {
+              p_type->traits.array.dims[dim_index] = length;
+              p_type->traits.array.dims_count += 1;
+              p_type->traits.array.spec_constant_op_ids[dim_index] =
+                  IsSpecConstant(p_length_node) ? p_length_node->decorations.spec_id : (uint32_t)INVALID_VALUE;
+            } else {
+              result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+              SPV_REFLECT_ASSERT(false);
+            }
+            // Parse next dimension or element type
+            SpvReflectPrvNode* 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;
+            SPV_REFLECT_ASSERT(false);
+          }
+        }
+      } break;
+
+      case SpvOpTypeRuntimeArray: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_ARRAY;
+        uint32_t element_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, element_type_id);
+        p_type->traits.array.stride = p_node->decorations.array_stride;
+        uint32_t dim_index = p_type->traits.array.dims_count;
+        p_type->traits.array.dims[dim_index] = (uint32_t)SPV_REFLECT_ARRAY_DIM_RUNTIME;
+        p_type->traits.array.spec_constant_op_ids[dim_index] = (uint32_t)INVALID_VALUE;
+        p_type->traits.array.dims_count += 1;
+        // Parse next dimension or element type
+        SpvReflectPrvNode* 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;
+          SPV_REFLECT_ASSERT(false);
+        }
+      } 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
+          SpvReflectPrvNode* p_member_node = FindNode(p_parser, member_id);
+          if (IsNull(p_member_node)) {
+            result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+            SPV_REFLECT_ASSERT(false);
+            break;
+          }
+
+          // Member decorations
+          SpvReflectPrvDecorations* 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: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_REF;
+        IF_READU32_CAST(result, p_parser, p_node->word_offset + 2, SpvStorageClass, p_type->storage_class);
+
+        bool found_recursion = false;
+        if (p_type->storage_class == SpvStorageClassPhysicalStorageBuffer) {
+          // Need to make sure we haven't started an infinite recursive loop
+          for (uint32_t i = 0; i < p_parser->physical_pointer_count; i++) {
+            if (p_type->id == p_parser->physical_pointer_check[i]->id) {
+              found_recursion = true;
+              memcpy(p_type, p_parser->physical_pointer_check[i], sizeof(SpvReflectTypeDescription));
+              p_type->copied = 1;
+              return SPV_REFLECT_RESULT_SUCCESS;
+            }
+          }
+          if (!found_recursion) {
+            p_parser->physical_pointer_struct_count++;
+            p_parser->physical_pointer_check[p_parser->physical_pointer_count] = p_type;
+            p_parser->physical_pointer_count++;
+            if (p_parser->physical_pointer_count >= MAX_RECURSIVE_PHYSICAL_POINTER_CHECK) {
+              return SPV_REFLECT_RESULT_ERROR_SPIRV_MAX_RECURSIVE_EXCEEDED;
+            }
+          }
+        }
+
+        // Parse type
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, p_node->type_id);
+        if (IsNull(p_next_node)) {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          SPV_REFLECT_ASSERT(false);
+        } else if (!found_recursion) {
+          if (p_next_node->op == SpvOpTypeStruct) {
+            p_type->struct_type_description = FindType(p_module, p_next_node->result_id);
+          }
+
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+      } break;
+
+      case SpvOpTypeAccelerationStructureKHR: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE;
+      } 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(SpvReflectPrvParser* 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) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (!p_node->is_type) {
+      continue;
+    }
+
+    SpvReflectTypeDescription* p_type = &(p_module->_internal->type_descriptions[type_index]);
+    p_parser->physical_pointer_count = 0;
+    SpvReflectResult result = ParseType(p_parser, p_node, NULL, p_module, p_type);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+    ++type_index;
+  }
+
+  // allocate now and fill in when parsing struct variable later
+  if (p_parser->physical_pointer_struct_count > 0) {
+    p_parser->physical_pointer_structs = (SpvReflectPrvPhysicalPointerStruct*)calloc(p_parser->physical_pointer_struct_count,
+                                                                                     sizeof(*(p_parser->physical_pointer_structs)));
+    if (IsNull(p_parser->physical_pointer_structs)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseCapabilities(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module) {
+  if (p_parser->capability_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_module->capability_count = p_parser->capability_count;
+  p_module->capabilities = (SpvReflectCapability*)calloc(p_module->capability_count, sizeof(*(p_module->capabilities)));
+  if (IsNull(p_module->capabilities)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  // Mark all types with an invalid state
+  for (size_t i = 0; i < p_module->capability_count; ++i) {
+    SpvReflectCapability* p_cap = &(p_module->capabilities[i]);
+    p_cap->value = SpvCapabilityMax;
+    p_cap->word_offset = (uint32_t)INVALID_VALUE;
+  }
+
+  size_t capability_index = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (SpvOpCapability != p_node->op) {
+      continue;
+    }
+
+    SpvReflectCapability* p_cap = &(p_module->capabilities[capability_index]);
+    p_cap->value = p_node->capability;
+    p_cap->word_offset = p_node->word_offset + 1;
+    ++capability_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(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module) {
+  p_module->descriptor_binding_count = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if ((p_node->op != SpvOpVariable) ||
+        ((p_node->storage_class != SpvStorageClassUniform) && (p_node->storage_class != SpvStorageClassStorageBuffer) &&
+         (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) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if ((p_node->op != SpvOpVariable) ||
+        ((p_node->storage_class != SpvStorageClassUniform) && (p_node->storage_class != SpvStorageClassStorageBuffer) &&
+         (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. We need to retain the storage class
+    // from the pointer so that we can use it to deduce deescriptor types.
+    SpvStorageClass pointer_storage_class = SpvStorageClassMax;
+    if (p_type->op == SpvOpTypePointer) {
+      pointer_storage_class = p_type->storage_class;
+      // Find the type's node
+      SpvReflectPrvNode* 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;
+    p_descriptor->decoration_flags = ApplyDecorations(&p_node->decorations);
+    p_descriptor->user_type = p_node->decorations.user_type;
+
+    // Flags like non-writable and non-readable are found as member decorations only.
+    // If all members have one of those decorations set, promote the decoration up
+    // to the whole descriptor.
+    const SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+    if (IsNotNull(p_type_node) && p_type_node->member_count) {
+      SpvReflectPrvDecorations common_flags = p_type_node->member_decorations[0];
+
+      for (uint32_t m = 1; m < p_type_node->member_count; ++m) {
+        common_flags.is_relaxed_precision &= p_type_node->member_decorations[m].is_relaxed_precision;
+        common_flags.is_block &= p_type_node->member_decorations[m].is_block;
+        common_flags.is_buffer_block &= p_type_node->member_decorations[m].is_buffer_block;
+        common_flags.is_row_major &= p_type_node->member_decorations[m].is_row_major;
+        common_flags.is_column_major &= p_type_node->member_decorations[m].is_column_major;
+        common_flags.is_built_in &= p_type_node->member_decorations[m].is_built_in;
+        common_flags.is_noperspective &= p_type_node->member_decorations[m].is_noperspective;
+        common_flags.is_flat &= p_type_node->member_decorations[m].is_flat;
+        common_flags.is_non_writable &= p_type_node->member_decorations[m].is_non_writable;
+        common_flags.is_non_readable &= p_type_node->member_decorations[m].is_non_readable;
+        common_flags.is_patch &= p_type_node->member_decorations[m].is_patch;
+        common_flags.is_per_vertex &= p_type_node->member_decorations[m].is_per_vertex;
+        common_flags.is_per_task &= p_type_node->member_decorations[m].is_per_task;
+        common_flags.is_weight_texture &= p_type_node->member_decorations[m].is_weight_texture;
+        common_flags.is_block_match_texture &= p_type_node->member_decorations[m].is_block_match_texture;
+      }
+
+      p_descriptor->decoration_flags |= ApplyDecorations(&common_flags);
+    }
+
+    // If this is in the StorageBuffer storage class, it's for sure a storage
+    // buffer descriptor. We need to handle this case earlier because in SPIR-V
+    // there are two ways to indicate a storage buffer:
+    // 1) Uniform storage class + BufferBlock decoration, or
+    // 2) StorageBuffer storage class + Buffer decoration.
+    // The 1) way is deprecated since SPIR-V v1.3. But the Buffer decoration is
+    // also used together with Uniform storage class to mean uniform buffer..
+    // We'll handle the pre-v1.3 cases in ParseDescriptorType().
+    if (pointer_storage_class == SpvStorageClassStorageBuffer) {
+      p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+    }
+
+    // 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;
+
+    if ((int)p_descriptor->descriptor_type == (int)INVALID_VALUE) {
+      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;
+
+        case SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE: {
+          p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
+        } 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;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
+        p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV;
+        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(SpvReflectPrvParser* 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;
+    }
+
+    SpvReflectPrvNode* 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
+    while (p_type_node->op == SpvOpTypeArray || p_type_node->op == SpvOpTypeRuntimeArray) {
+      if (p_type_node->op == SpvOpTypeArray) {
+        p_type_node = FindNode(p_parser, p_type_node->array_traits.element_type_id);
+      } else {
+        // Element type description
+        SpvReflectTypeDescription* p_type_temp = FindType(p_module, p_type_node->array_traits.element_type_id);
+        if (IsNull(p_type_temp)) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        // Element type node
+        p_type_node = FindNode(p_parser, p_type_temp->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];
+      // If pointer type, treat like reference and resolve to pointee type
+      SpvReflectTypeDescription* p_member_ptr_type = 0;
+      bool found_recursion = false;
+
+      if ((p_member_type->storage_class == SpvStorageClassPhysicalStorageBuffer) &&
+          (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_REF)) {
+        // Remember the original type
+        p_member_ptr_type = p_member_type;
+
+        // strip array
+        if (p_member_type->op == SpvOpTypeArray || p_member_type->op == SpvOpTypeRuntimeArray) {
+          SpvReflectPrvNode* p_node = FindNode(p_parser, p_member_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;
+          p_member_type = FindType(p_module, element_type_id);
+          if (p_member_type == NULL) {
+            return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          }
+        }
+
+        // Need to make sure we haven't started an infinite recursive loop
+        for (uint32_t i = 0; i < p_parser->physical_pointer_count; i++) {
+          if (p_member_type->id == p_parser->physical_pointer_check[i]->id) {
+            found_recursion = true;
+            break;  // still need to fill in p_member_type values
+          }
+        }
+        if (!found_recursion) {
+          uint32_t struct_id = FindType(p_module, p_member_type->id)->struct_type_description->id;
+          p_parser->physical_pointer_structs[p_parser->physical_pointer_struct_count].struct_id = struct_id;
+          p_parser->physical_pointer_structs[p_parser->physical_pointer_struct_count].p_var = p_member_var;
+          p_parser->physical_pointer_struct_count++;
+
+          p_parser->physical_pointer_check[p_parser->physical_pointer_count] = p_member_type;
+          p_parser->physical_pointer_count++;
+          if (p_parser->physical_pointer_count >= MAX_RECURSIVE_PHYSICAL_POINTER_CHECK) {
+            return SPV_REFLECT_RESULT_ERROR_SPIRV_MAX_RECURSIVE_EXCEEDED;
+          }
+        }
+
+        SpvReflectPrvNode* p_member_type_node = FindNode(p_parser, p_member_type->id);
+        if (IsNull(p_member_type_node)) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        // Should be the pointee type
+        p_member_type = FindType(p_module, p_member_type_node->type_id);
+        if (IsNull(p_member_type)) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+      }
+      bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT;
+      if (is_struct) {
+        if (!found_recursion) {
+          SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_member_type, p_member_var);
+          if (result != SPV_REFLECT_RESULT_SUCCESS) {
+            return result;
+          }
+        } else {
+          // if 2 member of structs are same PhysicalPointer type, copy the
+          // members values that aren't found skipping the recursion call
+          for (uint32_t i = 0; i < p_parser->physical_pointer_struct_count; i++) {
+            if (p_parser->physical_pointer_structs[i].struct_id == p_member_type->id) {
+              p_member_var->members = p_parser->physical_pointer_structs[i].p_var->members;
+              p_member_var->member_count = p_parser->physical_pointer_structs[i].p_var->member_count;
+              // Set here as it is the first time we need to walk down structs
+              p_member_var->flags |= SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY;
+            }
+          }
+        }
+      }
+
+      if (p_type_node->storage_class == SpvStorageClassPhysicalStorageBuffer && !p_type_node->member_names) {
+        // TODO 212 - If a buffer ref has an array of itself, all members are null
+        continue;
+      }
+
+      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->word_offset.offset = p_type_node->member_decorations[member_index].offset.word_offset;
+      p_member_var->type_description = p_member_ptr_type ? p_member_ptr_type : 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 uint32_t GetPhysicalPointerStructSize(SpvReflectPrvParser* p_parser, uint32_t id) {
+  for (uint32_t i = 0; i < p_parser->physical_pointer_struct_count; i++) {
+    if (p_parser->physical_pointer_structs[i].struct_id == id) {
+      return p_parser->physical_pointer_structs[i].p_var->size;
+    }
+  }
+  return 0;
+}
+
+static SpvReflectResult ParseDescriptorBlockVariableSizes(SpvReflectPrvParser* 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;
+  }
+
+  bool is_parent_ref = p_var->type_description->op == SpvOpTypePointer;
+
+  // 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 : (is_parent_ref ? p_member_var->offset : 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;
+
+    if (!p_member_type) {
+      // TODO 212 - If a buffer ref has an array of itself, all members are null
+      continue;
+    }
+    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) {
+          if (p_member_var->flags & SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY) {
+            p_member_var->size = GetPhysicalPointerStructSize(p_parser, p_member_type->id);
+          } else {
+            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 SpvOpTypePointer: {
+        // Reference. Get to underlying struct type.
+        SpvReflectPrvNode* p_member_type_node = FindNode(p_parser, p_member_type->id);
+        if (IsNull(p_member_type_node)) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        // Get the pointee type
+        p_member_type = FindType(p_module, p_member_type_node->type_id);
+        if (IsNull(p_member_type)) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        assert(p_member_type->op == SpvOpTypeStruct);
+        FALLTHROUGH;
+      }
+
+      case SpvOpTypeStruct: {
+        if (p_member_var->flags & SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY) {
+          p_member_var->size = GetPhysicalPointerStructSize(p_parser, p_member_type->id);
+        } else {
+          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;
+    }
+  }
+
+  // Structs can offset order don't need to match the index order, so first order by offset
+  // example:
+  //     OpMemberDecorate %struct 0 Offset 4
+  //     OpMemberDecorate %struct 1 Offset 0
+  SpvReflectBlockVariable** pp_member_offset_order =
+      (SpvReflectBlockVariable**)calloc(p_var->member_count, sizeof(SpvReflectBlockVariable*));
+  if (IsNull(pp_member_offset_order)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  uint32_t bottom_bound = 0;
+  for (uint32_t i = 0; i < p_var->member_count; ++i) {
+    uint32_t lowest_offset = UINT32_MAX;
+    uint32_t member_index = 0;
+    for (uint32_t j = 0; j < p_var->member_count; ++j) {
+      const uint32_t offset = p_var->members[j].offset;
+      if (offset < lowest_offset && offset >= bottom_bound) {
+        member_index = j;
+        lowest_offset = offset;
+      }
+    }
+    pp_member_offset_order[i] = &p_var->members[member_index];
+    bottom_bound = lowest_offset + 1;  // 2 index can't share the same offset
+  }
+
+  // Parse padded size using offset difference for all member except for the last entry...
+  for (uint32_t i = 0; i < (p_var->member_count - 1); ++i) {
+    SpvReflectBlockVariable* p_member_var = pp_member_offset_order[i];
+    SpvReflectBlockVariable* p_next_member_var = pp_member_offset_order[i + 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.
+  // last entry == entry with largest offset value
+  SpvReflectBlockVariable* p_last_member_var = pp_member_offset_order[p_var->member_count - 1];
+  p_last_member_var->padded_size =
+      RoundUp(p_last_member_var->offset + p_last_member_var->size, SPIRV_DATA_ALIGNMENT) - p_last_member_var->offset;
+  if (p_last_member_var->size > p_last_member_var->padded_size) {
+    p_last_member_var->size = p_last_member_var->padded_size;
+  }
+  if (is_parent_rta) {
+    p_last_member_var->padded_size = p_last_member_var->size;
+  }
+
+  SafeFree(pp_member_offset_order);
+
+  // If buffer ref, sizes are same as uint64_t
+  if (is_parent_ref) {
+    p_var->size = p_var->padded_size = 8;
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  // @TODO validate this with assertion
+  p_var->size = p_last_member_var->offset + p_last_member_var->padded_size;
+  p_var->padded_size = p_var->size;
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static void MarkSelfAndAllMemberVarsAsUsed(SpvReflectBlockVariable* p_var) {
+  // Clear the current variable's UNUSED flag
+  p_var->flags &= ~SPV_REFLECT_VARIABLE_FLAGS_UNUSED;
+
+  SpvOp op_type = p_var->type_description->op;
+  switch (op_type) {
+    default:
+      break;
+
+    case SpvOpTypeArray: {
+    } break;
+
+    case SpvOpTypeStruct: {
+      for (uint32_t i = 0; i < p_var->member_count; ++i) {
+        SpvReflectBlockVariable* p_member_var = &p_var->members[i];
+        MarkSelfAndAllMemberVarsAsUsed(p_member_var);
+      }
+    } break;
+  }
+}
+
+static SpvReflectResult ParseDescriptorBlockVariableUsage(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module,
+                                                          SpvReflectPrvAccessChain* p_access_chain, uint32_t index_index,
+                                                          SpvOp override_op_type, SpvReflectBlockVariable* p_var) {
+  // Clear the current variable's UNUSED flag
+  p_var->flags &= ~SPV_REFLECT_VARIABLE_FLAGS_UNUSED;
+
+  // Parsing arrays requires overriding the op type for
+  // for the lowest dim's element type.
+  SpvReflectTypeDescription* p_type = p_var->type_description;
+  SpvOp op_type = p_type->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
+      while ((p_type->op == SpvOpTypeArray) && (index_index < p_access_chain->index_count)) {
+        // Find the array element type id
+        SpvReflectPrvNode* 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 chain index
+        index_index += 1;
+      }
+
+      // Only continue parsing if there's remaining indices in the access
+      // chain. If the end of the access chain has been reached then all
+      // remaining variables (including those in struct hierarchies)
+      // are considered USED.
+      //
+      // See: https://github.com/KhronosGroup/SPIRV-Reflect/issues/78
+      //
+      if (index_index < p_access_chain->index_count) {
+        // 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;
+        }
+      } else {
+        // Clear UNUSED flag for remaining variables
+        MarkSelfAndAllMemberVarsAsUsed(p_var);
+      }
+    } break;
+
+    case SpvOpTypePointer: {
+      // Reference. Get to underlying struct type.
+      SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+      if (IsNull(p_type_node)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+      // Get the pointee type
+      p_type = FindType(p_module, p_type_node->type_id);
+      if (IsNull(p_type)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+      if (p_type->op != SpvOpTypeStruct) {
+        break;
+      }
+      FALLTHROUGH;
+    }
+
+    case SpvOpTypeStruct: {
+      assert(p_var->member_count > 0);
+      if (p_var->member_count == 0) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_BLOCK_DATA;
+      }
+
+      // The access chain can have zero indexes, if used for a runtime array
+      if (p_access_chain->index_count == 0) {
+        return SPV_REFLECT_RESULT_SUCCESS;
+      }
+
+      // Get member variable at the access's chain current index
+      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];
+
+      bool is_pointer_to_pointer = IsPointerToPointer(p_parser, p_access_chain->result_type_id);
+      if (is_pointer_to_pointer) {
+        // Remember block var for this access chain for downstream dereference
+        p_access_chain->block_var = p_member_var;
+      }
+
+      // Next access chain index
+      index_index += 1;
+
+      // Only continue parsing if there's remaining indices in the access
+      // chain. If the end of the access chain has been reach then all
+      // remaining variables (including those in struct hierarchies)
+      // are considered USED.
+      //
+      // See: https://github.com/KhronosGroup/SPIRV-Reflect/issues/78
+      //
+      if (index_index < p_access_chain->index_count) {
+        SpvReflectResult result =
+            ParseDescriptorBlockVariableUsage(p_parser, p_module, p_access_chain, index_index, (SpvOp)INVALID_VALUE, p_member_var);
+        if (result != SPV_REFLECT_RESULT_SUCCESS) {
+          return result;
+        }
+      } else if (!is_pointer_to_pointer) {
+        // Clear UNUSED flag for remaining variables
+        MarkSelfAndAllMemberVarsAsUsed(p_member_var);
+      }
+    } break;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDescriptorBlocks(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module) {
+  if (p_module->descriptor_binding_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_parser->physical_pointer_struct_count = 0;
+
+  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;
+    p_parser->physical_pointer_count = 0;
+    // 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) {
+      SpvReflectPrvAccessChain* 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 != 0);
+  uint32_t bit_width = p_type->traits.numeric.scalar.width;
+  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 (bit_width) {
+        case 16: {
+          switch (component_count) {
+            case 2:
+              *p_format = SPV_REFLECT_FORMAT_R16G16_SFLOAT;
+              break;
+            case 3:
+              *p_format = SPV_REFLECT_FORMAT_R16G16B16_SFLOAT;
+              break;
+            case 4:
+              *p_format = SPV_REFLECT_FORMAT_R16G16B16A16_SFLOAT;
+              break;
+          }
+        } break;
+
+        case 32: {
+          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;
+          }
+        } break;
+
+        case 64: {
+          switch (component_count) {
+            case 2:
+              *p_format = SPV_REFLECT_FORMAT_R64G64_SFLOAT;
+              break;
+            case 3:
+              *p_format = SPV_REFLECT_FORMAT_R64G64B64_SFLOAT;
+              break;
+            case 4:
+              *p_format = SPV_REFLECT_FORMAT_R64G64B64A64_SFLOAT;
+              break;
+          }
+        }
+      }
+      result = SPV_REFLECT_RESULT_SUCCESS;
+    } else if (p_type->type_flags & (SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_BOOL)) {
+      switch (bit_width) {
+        case 16: {
+          switch (component_count) {
+            case 2:
+              *p_format = signedness ? SPV_REFLECT_FORMAT_R16G16_SINT : SPV_REFLECT_FORMAT_R16G16_UINT;
+              break;
+            case 3:
+              *p_format = signedness ? SPV_REFLECT_FORMAT_R16G16B16_SINT : SPV_REFLECT_FORMAT_R16G16B16_UINT;
+              break;
+            case 4:
+              *p_format = signedness ? SPV_REFLECT_FORMAT_R16G16B16A16_SINT : SPV_REFLECT_FORMAT_R16G16B16A16_UINT;
+              break;
+          }
+        } break;
+
+        case 32: {
+          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;
+          }
+        } break;
+
+        case 64: {
+          switch (component_count) {
+            case 2:
+              *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64_SINT : SPV_REFLECT_FORMAT_R64G64_UINT;
+              break;
+            case 3:
+              *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64B64_SINT : SPV_REFLECT_FORMAT_R64G64B64_UINT;
+              break;
+            case 4:
+              *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64B64A64_SINT : SPV_REFLECT_FORMAT_R64G64B64A64_UINT;
+              break;
+          }
+        }
+      }
+      result = SPV_REFLECT_RESULT_SUCCESS;
+    }
+  } else if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_FLOAT) {
+    switch (bit_width) {
+      case 16:
+        *p_format = SPV_REFLECT_FORMAT_R16_SFLOAT;
+        break;
+      case 32:
+        *p_format = SPV_REFLECT_FORMAT_R32_SFLOAT;
+        break;
+      case 64:
+        *p_format = SPV_REFLECT_FORMAT_R64_SFLOAT;
+        break;
+    }
+    result = SPV_REFLECT_RESULT_SUCCESS;
+  } else if (p_type->type_flags & (SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_BOOL)) {
+    switch (bit_width) {
+      case 16:
+        *p_format = signedness ? SPV_REFLECT_FORMAT_R16_SINT : SPV_REFLECT_FORMAT_R16_UINT;
+        break;
+        break;
+      case 32:
+        *p_format = signedness ? SPV_REFLECT_FORMAT_R32_SINT : SPV_REFLECT_FORMAT_R32_UINT;
+        break;
+        break;
+      case 64:
+        *p_format = signedness ? SPV_REFLECT_FORMAT_R64_SINT : SPV_REFLECT_FORMAT_R64_UINT;
+        break;
+    }
+    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(SpvReflectPrvParser* p_parser,
+                                               const SpvReflectPrvDecorations* p_var_node_decorations,
+                                               const SpvReflectPrvDecorations* p_type_node_decorations,
+                                               SpvReflectShaderModule* p_module, SpvReflectTypeDescription* p_type,
+                                               SpvReflectInterfaceVariable* p_var, bool* p_has_built_in) {
+  SpvReflectPrvNode* 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) {
+      SpvReflectPrvDecorations* 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];
+
+      // Storage class is the same throughout the whole struct
+      p_member_var->storage_class = p_var->storage_class;
+
+      SpvReflectResult result =
+          ParseInterfaceVariable(p_parser, NULL, p_member_decorations, p_module, p_member_type, p_member_var, p_has_built_in);
+      if (result != SPV_REFLECT_RESULT_SUCCESS) {
+        SPV_REFLECT_ASSERT(false);
+        return result;
+      }
+    }
+  }
+
+  p_var->name = p_type_node->name;
+  p_var->decoration_flags = ApplyDecorations(p_type_node_decorations);
+  if (p_var_node_decorations != NULL) {
+    p_var->decoration_flags |= ApplyDecorations(p_var_node_decorations);
+  } else {
+    // Apply member decoration values to struct members
+    p_var->location = p_type_node_decorations->location.value;
+    p_var->component = p_type_node_decorations->component.value;
+  }
+
+  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;
+
+  // Only parse format for interface variables that are input or output
+  if ((p_var->storage_class == SpvStorageClassInput) || (p_var->storage_class == SpvStorageClassOutput)) {
+    SpvReflectResult result = ParseFormat(p_var->type_description, &p_var->format);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      SPV_REFLECT_ASSERT(false);
+      return result;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseInterfaceVariables(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module,
+                                                SpvReflectEntryPoint* p_entry, uint32_t interface_variable_count,
+                                                uint32_t* p_interface_variable_ids) {
+  if (interface_variable_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_entry->interface_variable_count = interface_variable_count;
+  p_entry->input_variable_count = 0;
+  p_entry->output_variable_count = 0;
+  for (size_t i = 0; i < interface_variable_count; ++i) {
+    uint32_t var_result_id = *(p_interface_variable_ids + i);
+    SpvReflectPrvNode* 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;
+    }
+  }
+
+  if (p_entry->interface_variable_count > 0) {
+    p_entry->interface_variables =
+        (SpvReflectInterfaceVariable*)calloc(p_entry->interface_variable_count, sizeof(*(p_entry->interface_variables)));
+    if (IsNull(p_entry->interface_variables)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  size_t input_index = 0;
+  size_t output_index = 0;
+  for (size_t i = 0; i < interface_variable_count; ++i) {
+    uint32_t var_result_id = *(p_interface_variable_ids + i);
+    SpvReflectPrvNode* 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) || 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
+      SpvReflectPrvNode* 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;
+      }
+    }
+
+    SpvReflectPrvNode* 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 = &(p_entry->interface_variables[i]);
+    p_var->storage_class = p_node->storage_class;
+
+    bool has_built_in = p_node->decorations.is_built_in;
+    SpvReflectResult result =
+        ParseInterfaceVariable(p_parser, &p_node->decorations, &p_type_node->decorations, p_module, p_type, p_var, &has_built_in);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      SPV_REFLECT_ASSERT(false);
+      return result;
+    }
+
+    // Input and output variables
+    if (p_var->storage_class == SpvStorageClassInput) {
+      p_entry->input_variables[input_index] = p_var;
+      ++input_index;
+    } else if (p_node->storage_class == SpvStorageClassOutput) {
+      p_entry->output_variables[output_index] = p_var;
+      ++output_index;
+    }
+
+    // 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->component = p_node->decorations.component.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(SpvReflectPrvParser* p_parser, SpvReflectPrvFunction* 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 uint32_t GetUint32Constant(SpvReflectPrvParser* p_parser, uint32_t id) {
+  uint32_t result = (uint32_t)INVALID_VALUE;
+  SpvReflectPrvNode* p_node = FindNode(p_parser, id);
+  if (p_node && p_node->op == SpvOpConstant) {
+    UNCHECKED_READU32(p_parser, p_node->word_offset + 3, result);
+  }
+  return result;
+}
+
+static bool HasByteAddressBufferOffset(SpvReflectPrvNode* p_node, SpvReflectDescriptorBinding* p_binding) {
+  return IsNotNull(p_node) && IsNotNull(p_binding) && p_node->op == SpvOpAccessChain && p_node->word_count == 6 &&
+         (p_binding->user_type == SPV_REFLECT_USER_TYPE_BYTE_ADDRESS_BUFFER ||
+          p_binding->user_type == SPV_REFLECT_USER_TYPE_RW_BYTE_ADDRESS_BUFFER);
+}
+
+static SpvReflectResult ParseByteAddressBuffer(SpvReflectPrvParser* p_parser, SpvReflectPrvNode* p_node,
+                                               SpvReflectDescriptorBinding* p_binding) {
+  const SpvReflectResult not_found = SPV_REFLECT_RESULT_SUCCESS;
+  if (!HasByteAddressBufferOffset(p_node, p_binding)) {
+    return not_found;
+  }
+
+  uint32_t offset = 0;  // starting offset
+
+  uint32_t base_id = 0;
+  // expect first index of 2D access is zero
+  UNCHECKED_READU32(p_parser, p_node->word_offset + 4, base_id);
+  if (GetUint32Constant(p_parser, base_id) != 0) {
+    return not_found;
+  }
+  UNCHECKED_READU32(p_parser, p_node->word_offset + 5, base_id);
+  SpvReflectPrvNode* p_next_node = FindNode(p_parser, base_id);
+  if (IsNull(p_next_node)) {
+    return not_found;
+  } else if (p_next_node->op == SpvOpConstant) {
+    // The access chain might just be a constant right to the offset
+    offset = GetUint32Constant(p_parser, base_id);
+    p_binding->byte_address_buffer_offsets[p_binding->byte_address_buffer_offset_count] = offset;
+    p_binding->byte_address_buffer_offset_count++;
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  // there is usually 2 (sometimes 3) instrucitons that make up the arithmetic logic to calculate the offset
+  SpvReflectPrvNode* arithmetic_node_stack[8];
+  uint32_t arithmetic_count = 0;
+
+  while (IsNotNull(p_next_node)) {
+    if (p_next_node->op == SpvOpLoad || p_next_node->op == SpvOpBitcast || p_next_node->op == SpvOpConstant) {
+      break;  // arithmetic starts here
+    }
+    arithmetic_node_stack[arithmetic_count++] = p_next_node;
+    if (arithmetic_count >= 8) {
+      return not_found;
+    }
+
+    UNCHECKED_READU32(p_parser, p_next_node->word_offset + 3, base_id);
+    p_next_node = FindNode(p_parser, base_id);
+  }
+
+  const uint32_t count = arithmetic_count;
+  for (uint32_t i = 0; i < count; i++) {
+    p_next_node = arithmetic_node_stack[--arithmetic_count];
+    // All arithmetic ops takes 2 operands, assumption is the 2nd operand has the constant
+    UNCHECKED_READU32(p_parser, p_next_node->word_offset + 4, base_id);
+    uint32_t value = GetUint32Constant(p_parser, base_id);
+    if (value == INVALID_VALUE) {
+      return not_found;
+    }
+
+    switch (p_next_node->op) {
+      case SpvOpShiftRightLogical:
+        offset >>= value;
+        break;
+      case SpvOpIAdd:
+        offset += value;
+        break;
+      case SpvOpISub:
+        offset -= value;
+        break;
+      case SpvOpIMul:
+        offset *= value;
+        break;
+      case SpvOpUDiv:
+        offset /= value;
+        break;
+      case SpvOpSDiv:
+        // OpConstant might be signed, but value should never be negative
+        assert((int32_t)value > 0);
+        offset /= value;
+        break;
+      default:
+        return not_found;
+    }
+  }
+
+  p_binding->byte_address_buffer_offsets[p_binding->byte_address_buffer_offset_count] = offset;
+  p_binding->byte_address_buffer_offset_count++;
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseFunctionParameterAccess(SpvReflectPrvParser* p_parser, uint32_t callee_function_id,
+                                                     uint32_t function_parameter_index, uint32_t* p_accessed) {
+  SpvReflectPrvFunction* p_func = NULL;
+  for (size_t i = 0; i < p_parser->function_count; ++i) {
+    if (p_parser->functions[i].id == callee_function_id) {
+      p_func = &(p_parser->functions[i]);
+      break;
+    }
+  }
+  if (p_func == NULL) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+  }
+
+  assert(function_parameter_index < p_func->parameter_count);
+
+  for (size_t i = 0; i < p_func->accessed_variable_count; ++i) {
+    if (p_func->parameters[function_parameter_index] == p_func->accessed_variables[i].variable_ptr) {
+      SpvReflectPrvAccessedVariable* p_var = &p_func->accessed_variables[i];
+      if (p_var->function_id > 0) {
+        SpvReflectResult result =
+            ParseFunctionParameterAccess(p_parser, p_var->function_id, p_var->function_parameter_index, p_accessed);
+        if (result != SPV_REFLECT_RESULT_SUCCESS) {
+          return result;
+        }
+      } else {
+        *p_accessed = 1;
+      }
+      // Early out as soon as p_accessed is true
+      if (*p_accessed) {
+        return SPV_REFLECT_RESULT_SUCCESS;
+      }
+    }
+  }
+
+  *p_accessed = 0;
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseStaticallyUsedResources(SpvReflectPrvParser* 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
+  SpvReflectPrvFunction* 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) {
+    SafeFree(p_called_functions);
+    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_acessed_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_acessed_count += p_parser->functions[j].accessed_variable_count;
+  }
+  SpvReflectPrvAccessedVariable* p_used_accesses = NULL;
+  if (used_acessed_count > 0) {
+    p_used_accesses = (SpvReflectPrvAccessedVariable*)calloc(used_acessed_count, sizeof(SpvReflectPrvAccessedVariable));
+    if (IsNull(p_used_accesses)) {
+      SafeFree(p_called_functions);
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+  used_acessed_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(&p_used_accesses[used_acessed_count], p_parser->functions[j].accessed_variables,
+           p_parser->functions[j].accessed_variable_count * sizeof(SpvReflectPrvAccessedVariable));
+    used_acessed_count += p_parser->functions[j].accessed_variable_count;
+  }
+  SafeFree(p_called_functions);
+
+  if (used_acessed_count > 0) {
+    qsort(p_used_accesses, used_acessed_count, sizeof(*p_used_accesses), SortCompareAccessedVariable);
+  }
+
+  // Do set intersection to find the used uniform and push constants
+  size_t used_uniform_count = 0;
+  result = IntersectSortedAccessedVariable(p_used_accesses, used_acessed_count, uniforms, uniform_count, &p_entry->used_uniforms,
+                                           &used_uniform_count);
+  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+    SafeFree(p_used_accesses);
+    return result;
+  }
+
+  size_t used_push_constant_count = 0;
+  result = IntersectSortedAccessedVariable(p_used_accesses, used_acessed_count, push_constants, push_constant_count,
+                                           &p_entry->used_push_constants, &used_push_constant_count);
+  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+    SafeFree(p_used_accesses);
+    return result;
+  }
+
+  for (uint32_t i = 0; i < p_module->descriptor_binding_count; ++i) {
+    SpvReflectDescriptorBinding* p_binding = &p_module->descriptor_bindings[i];
+    uint32_t byte_address_buffer_offset_count = 0;
+
+    for (uint32_t j = 0; j < used_acessed_count; j++) {
+      SpvReflectPrvAccessedVariable* p_var = &p_used_accesses[j];
+      if (p_var->variable_ptr == p_binding->spirv_id) {
+        if (p_var->function_id > 0) {
+          result =
+              ParseFunctionParameterAccess(p_parser, p_var->function_id, p_var->function_parameter_index, &p_binding->accessed);
+          if (result != SPV_REFLECT_RESULT_SUCCESS) {
+            SafeFree(p_used_accesses);
+            return result;
+          }
+        } else {
+          p_binding->accessed = 1;
+        }
+
+        if (HasByteAddressBufferOffset(p_used_accesses[j].p_node, p_binding)) {
+          byte_address_buffer_offset_count++;
+        }
+      }
+    }
+
+    // only if SPIR-V has ByteAddressBuffer user type
+    if (byte_address_buffer_offset_count > 0) {
+      bool multi_entrypoint = p_binding->byte_address_buffer_offset_count > 0;
+      if (multi_entrypoint) {
+        // If there is a 2nd entrypoint, we can have multiple entry points, in this case we want to just combine the accessed
+        // offsets and then de-duplicate it
+        uint32_t* prev_byte_address_buffer_offsets = p_binding->byte_address_buffer_offsets;
+        p_binding->byte_address_buffer_offsets =
+            (uint32_t*)calloc(byte_address_buffer_offset_count + p_binding->byte_address_buffer_offset_count, sizeof(uint32_t));
+        memcpy(p_binding->byte_address_buffer_offsets, prev_byte_address_buffer_offsets,
+               sizeof(uint32_t) * p_binding->byte_address_buffer_offset_count);
+        SafeFree(prev_byte_address_buffer_offsets);
+      } else {
+        // possible not all allocated offset slots are used, but this will be a max per binding
+        p_binding->byte_address_buffer_offsets = (uint32_t*)calloc(byte_address_buffer_offset_count, sizeof(uint32_t));
+      }
+
+      if (IsNull(p_binding->byte_address_buffer_offsets)) {
+        SafeFree(p_used_accesses);
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+
+      for (uint32_t j = 0; j < used_acessed_count; j++) {
+        if (p_used_accesses[j].variable_ptr == p_binding->spirv_id) {
+          result = ParseByteAddressBuffer(p_parser, p_used_accesses[j].p_node, p_binding);
+          if (result != SPV_REFLECT_RESULT_SUCCESS) {
+            SafeFree(p_used_accesses);
+            return result;
+          }
+        }
+      }
+
+      if (multi_entrypoint) {
+        qsort(p_binding->byte_address_buffer_offsets, p_binding->byte_address_buffer_offset_count,
+              sizeof(*(p_binding->byte_address_buffer_offsets)), SortCompareUint32);
+        p_binding->byte_address_buffer_offset_count =
+            (uint32_t)DedupSortedUint32(p_binding->byte_address_buffer_offsets, p_binding->byte_address_buffer_offset_count);
+      }
+    }
+  }
+
+  SafeFree(p_used_accesses);
+
+  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(SpvReflectPrvParser* 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) {
+    SpvReflectPrvNode* 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;
+      case SpvExecutionModelTaskNV:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TASK_BIT_NV;
+        break;
+      case SpvExecutionModelTaskEXT:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TASK_BIT_EXT;
+        break;
+      case SpvExecutionModelMeshNV:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_MESH_BIT_NV;
+        break;
+      case SpvExecutionModelMeshEXT:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_MESH_BIT_EXT;
+        break;
+      case SpvExecutionModelRayGenerationKHR:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_RAYGEN_BIT_KHR;
+        break;
+      case SpvExecutionModelIntersectionKHR:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_INTERSECTION_BIT_KHR;
+        break;
+      case SpvExecutionModelAnyHitKHR:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_ANY_HIT_BIT_KHR;
+        break;
+      case SpvExecutionModelClosestHitKHR:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
+        break;
+      case SpvExecutionModelMissKHR:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_MISS_BIT_KHR;
+        break;
+      case SpvExecutionModelCallableKHR:
+        p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_CALLABLE_BIT_KHR;
+        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;
+    uint32_t interface_variable_count = (p_node->word_count - (name_start_word_offset + name_word_count));
+    uint32_t* p_interface_variables = NULL;
+    if (interface_variable_count > 0) {
+      p_interface_variables = (uint32_t*)calloc(interface_variable_count, sizeof(*(p_interface_variables)));
+      if (IsNull(p_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);
+      p_interface_variables[var_index] = var_result_id;
+    }
+
+    result = ParseInterfaceVariables(p_parser, p_module, p_entry_point, interface_variable_count, p_interface_variables);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+    SafeFree(p_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 ParseExecutionModes(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module) {
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->nodes));
+  assert(IsNotNull(p_module));
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
+    for (size_t node_idx = 0; node_idx < p_parser->node_count; ++node_idx) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[node_idx]);
+      if (p_node->op != SpvOpExecutionMode && p_node->op != SpvOpExecutionModeId) {
+        continue;
+      }
+
+      // Read entry point id
+      uint32_t entry_point_id = 0;
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, entry_point_id);
+
+      // Find entry point
+      SpvReflectEntryPoint* p_entry_point = NULL;
+      for (size_t entry_point_idx = 0; entry_point_idx < p_module->entry_point_count; ++entry_point_idx) {
+        if (p_module->entry_points[entry_point_idx].id == entry_point_id) {
+          p_entry_point = &p_module->entry_points[entry_point_idx];
+          break;
+        }
+      }
+      // Bail if entry point is null
+      if (IsNull(p_entry_point)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ENTRY_POINT;
+      }
+
+      // Read execution mode
+      uint32_t execution_mode = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, execution_mode);
+
+      // Parse execution mode
+      switch (execution_mode) {
+        case SpvExecutionModeInvocations: {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_entry_point->invocations);
+        } break;
+
+        case SpvExecutionModeLocalSize: {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_entry_point->local_size.x);
+          CHECKED_READU32(p_parser, p_node->word_offset + 4, p_entry_point->local_size.y);
+          CHECKED_READU32(p_parser, p_node->word_offset + 5, p_entry_point->local_size.z);
+        } break;
+        case SpvExecutionModeLocalSizeId: {
+          uint32_t local_size_x_id = 0;
+          uint32_t local_size_y_id = 0;
+          uint32_t local_size_z_id = 0;
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, local_size_x_id);
+          CHECKED_READU32(p_parser, p_node->word_offset + 4, local_size_y_id);
+          CHECKED_READU32(p_parser, p_node->word_offset + 5, local_size_z_id);
+
+          SpvReflectPrvNode* x_node = FindNode(p_parser, local_size_x_id);
+          SpvReflectPrvNode* y_node = FindNode(p_parser, local_size_y_id);
+          SpvReflectPrvNode* z_node = FindNode(p_parser, local_size_z_id);
+          if (IsNotNull(x_node) && IsNotNull(y_node) && IsNotNull(z_node)) {
+            if (IsSpecConstant(x_node)) {
+              p_entry_point->local_size.x = (uint32_t)SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT;
+            } else {
+              CHECKED_READU32(p_parser, x_node->word_offset + 3, p_entry_point->local_size.x);
+            }
+
+            if (IsSpecConstant(y_node)) {
+              p_entry_point->local_size.y = (uint32_t)SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT;
+            } else {
+              CHECKED_READU32(p_parser, y_node->word_offset + 3, p_entry_point->local_size.y);
+            }
+
+            if (IsSpecConstant(z_node)) {
+              p_entry_point->local_size.z = (uint32_t)SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT;
+            } else {
+              CHECKED_READU32(p_parser, z_node->word_offset + 3, p_entry_point->local_size.z);
+            }
+          }
+        } break;
+
+        case SpvExecutionModeOutputVertices: {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_entry_point->output_vertices);
+        } break;
+
+        default:
+          break;
+      }
+      p_entry_point->execution_mode_count++;
+    }
+    uint32_t* indices = (uint32_t*)calloc(p_module->entry_point_count, sizeof(indices));
+    if (IsNull(indices)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+    for (size_t entry_point_idx = 0; entry_point_idx < p_module->entry_point_count; ++entry_point_idx) {
+      SpvReflectEntryPoint* p_entry_point = &p_module->entry_points[entry_point_idx];
+      if (p_entry_point->execution_mode_count > 0) {
+        p_entry_point->execution_modes =
+            (SpvExecutionMode*)calloc(p_entry_point->execution_mode_count, sizeof(*p_entry_point->execution_modes));
+        if (IsNull(p_entry_point->execution_modes)) {
+          SafeFree(indices);
+          return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+        }
+      }
+    }
+
+    for (size_t node_idx = 0; node_idx < p_parser->node_count; ++node_idx) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[node_idx]);
+      if (p_node->op != SpvOpExecutionMode) {
+        continue;
+      }
+
+      // Read entry point id
+      uint32_t entry_point_id = 0;
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, entry_point_id);
+
+      // Find entry point
+      SpvReflectEntryPoint* p_entry_point = NULL;
+      uint32_t* idx = NULL;
+      for (size_t entry_point_idx = 0; entry_point_idx < p_module->entry_point_count; ++entry_point_idx) {
+        if (p_module->entry_points[entry_point_idx].id == entry_point_id) {
+          p_entry_point = &p_module->entry_points[entry_point_idx];
+          idx = &indices[entry_point_idx];
+          break;
+        }
+      }
+
+      // Read execution mode
+      uint32_t execution_mode = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, execution_mode);
+      p_entry_point->execution_modes[(*idx)++] = (SpvExecutionMode)execution_mode;
+    }
+    SafeFree(indices);
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParsePushConstantBlocks(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module) {
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* 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;
+  }
+
+  p_parser->physical_pointer_struct_count = 0;
+  uint32_t push_constant_index = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* 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) || 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
+      SpvReflectPrvNode* 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;
+      }
+    }
+
+    SpvReflectPrvNode* 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;
+    p_parser->physical_pointer_count = 0;
+    SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_type, p_push_constant);
+    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) {
+      SpvReflectPrvAccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
+      // Skip any access chains that aren't touching this push constant block
+      if (p_push_constant->spirv_id != FindAccessChainBaseVariable(p_parser, p_access_chain)) {
+        continue;
+      }
+      SpvReflectBlockVariable* p_var =
+          (p_access_chain->base_id == p_push_constant->spirv_id) ? p_push_constant : GetRefBlkVar(p_parser, p_access_chain);
+      result = ParseDescriptorBlockVariableUsage(p_parser, p_module, p_access_chain, 0, (SpvOp)INVALID_VALUE, p_var);
+      if (result != SPV_REFLECT_RESULT_SUCCESS) {
+        return result;
+      }
+    }
+
+    p_push_constant->name = p_node->name;
+    result = ParseDescriptorBlockVariableSizes(p_parser, p_module, true, false, false, p_push_constant);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+
+    // Get minimum offset for whole Push Constant block
+    // It is not valid SPIR-V to have an empty Push Constant Block
+    p_push_constant->offset = UINT32_MAX;
+    for (uint32_t k = 0; k < p_push_constant->member_count; ++k) {
+      const uint32_t member_offset = p_push_constant->members[k].offset;
+      p_push_constant->offset = Min(p_push_constant->offset, member_offset);
+    }
+
+    ++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;
+}
+
+static SpvReflectResult CreateShaderModule(uint32_t flags, 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;
+  }
+  // Copy flags
+  p_module->_internal->module_flags = flags;
+  // Figure out if we need to copy the SPIR-V code or not
+  if (flags & SPV_REFLECT_MODULE_FLAG_NO_COPY) {
+    // Set internal size and pointer to args passed in
+    p_module->_internal->spirv_size = size;
+#if defined(__cplusplus)
+    p_module->_internal->spirv_code = const_cast<uint32_t*>(static_cast<const uint32_t*>(p_code));  // cast that const away
+#else
+    p_module->_internal->spirv_code = (void*)p_code;  // cast that const away
+#endif
+    p_module->_internal->spirv_word_count = (uint32_t)(size / SPIRV_WORD_SIZE);
+  } else {
+    // 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;
+    }
+    // Copy SPIR-V to code storage
+    memcpy(p_module->_internal->spirv_code, p_code, size);
+  }
+
+  // Initialize everything to zero
+  SpvReflectPrvParser parser;
+  memset(&parser, 0, sizeof(SpvReflectPrvParser));
+
+  // Create parser
+  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);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseStrings(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseSource(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseFunctions(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseMemberCounts(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseNames(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDecorations(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+
+  // 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);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDescriptorBindings(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDescriptorType(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseUAVCounterBindings(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDescriptorBlocks(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParsePushConstantBlocks(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseEntryPoints(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseCapabilities(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  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;
+    p_module->interface_variable_count = p_entry->interface_variable_count;
+    p_module->interface_variables = p_entry->interface_variables;
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = DisambiguateStorageBufferSrvUav(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = SynchronizeDescriptorSets(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseExecutionModes(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+
+  // Destroy module if parse was not successful
+  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+    spvReflectDestroyShaderModule(p_module);
+  }
+
+  DestroyParser(&parser);
+
+  return result;
+}
+
+SpvReflectResult spvReflectCreateShaderModule(size_t size, const void* p_code, SpvReflectShaderModule* p_module) {
+  return CreateShaderModule(0, size, p_code, p_module);
+}
+
+SpvReflectResult spvReflectCreateShaderModule2(uint32_t flags, size_t size, const void* p_code, SpvReflectShaderModule* p_module) {
+  return CreateShaderModule(flags, size, p_code, p_module);
+}
+
+SpvReflectResult spvReflectGetShaderModule(size_t size, const void* p_code, SpvReflectShaderModule* p_module) {
+  return spvReflectCreateShaderModule(size, p_code, p_module);
+}
+
+static void SafeFreeTypes(SpvReflectTypeDescription* p_type) {
+  if (IsNull(p_type) || p_type->copied) {
+    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;
+  }
+
+  // We share pointers to Physical Pointer structs and don't want to double free
+  if (p_block->flags & SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY) {
+    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;
+  }
+
+  SafeFree(p_module->source_source);
+
+  // 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];
+    if (IsNotNull(p_descriptor->byte_address_buffer_offsets)) {
+      SafeFree(p_descriptor->byte_address_buffer_offsets);
+    }
+    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->interface_variable_count; j++) {
+      SafeFreeInterfaceVariable(&p_entry->interface_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->interface_variables);
+    SafeFree(p_entry->used_uniforms);
+    SafeFree(p_entry->used_push_constants);
+    SafeFree(p_entry->execution_modes);
+  }
+  SafeFree(p_module->capabilities);
+  SafeFree(p_module->entry_points);
+  SafeFree(p_module->spec_constants);
+
+  // 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 if there was a copy
+  if ((p_module->_internal->module_flags & SPV_REFLECT_MODULE_FLAG_NO_COPY) == 0) {
+    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 spvReflectEnumerateInterfaceVariables(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->interface_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = &p_module->interface_variables[index];
+      pp_variables[index] = p_var;
+    }
+  } else {
+    *p_count = p_module->interface_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(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->interface_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = &p_entry->interface_variables[index];
+      pp_variables[index] = p_var;
+    }
+  } else {
+    *p_count = p_entry->interface_variable_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 = 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 = 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 = 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 = 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;
+}
+
+SpvReflectResult spvReflectEnumerateSpecializationConstants(const SpvReflectShaderModule* p_module, uint32_t* p_count,
+                                                            SpvReflectSpecializationConstant** pp_constants) {
+  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_constants)) {
+    if (*p_count != p_module->spec_constant_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectSpecializationConstant* p_constant = (SpvReflectSpecializationConstant*)&p_module->spec_constants[index];
+      pp_constants[index] = p_constant;
+    }
+  } else {
+    *p_count = p_module->spec_constant_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 != (uint32_t)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 != (uint32_t)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 != (uint32_t)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 != (uint32_t)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 SpvSourceLanguageESSL:
+      return "ESSL";
+    case SpvSourceLanguageGLSL:
+      return "GLSL";
+    case SpvSourceLanguageOpenCL_C:
+      return "OpenCL_C";
+    case SpvSourceLanguageOpenCL_CPP:
+      return "OpenCL_CPP";
+    case SpvSourceLanguageHLSL:
+      return "HLSL";
+    case SpvSourceLanguageCPP_for_OpenCL:
+      return "CPP_for_OpenCL";
+    case SpvSourceLanguageSYCL:
+      return "SYCL";
+    case SpvSourceLanguageHERO_C:
+      return "Hero C";
+    case SpvSourceLanguageNZSL:
+      return "NZSL";
+    default:
+      break;
+  }
+  // The source language is SpvSourceLanguageUnknown, SpvSourceLanguageMax, or
+  // some other value that does not correspond to a knonwn language.
+  return "Unknown";
+}
+
+const char* spvReflectBlockVariableTypeName(const SpvReflectBlockVariable* p_var) {
+  if (p_var == NULL) {
+    return NULL;
+  }
+  return p_var->type_description->type_name;
+}
diff --git a/third-party/SPIRV-Reflect/spirv_reflect.cpp b/third-party/SPIRV-Reflect/spirv_reflect.cpp
new file mode 100644 (file)
index 0000000..9f991d6
--- /dev/null
@@ -0,0 +1,4 @@
+//
+// This file exists to force compiling spirv_reflect.c as C++.
+//
+#include "spirv_reflect.c"
\ No newline at end of file
diff --git a/third-party/SPIRV-Reflect/spirv_reflect.h b/third-party/SPIRV-Reflect/spirv_reflect.h
new file mode 100644 (file)
index 0000000..9a42f14
--- /dev/null
@@ -0,0 +1,2447 @@
+/*
+ Copyright 2017-2022 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.
+*/
+
+/*
+
+VERSION HISTORY
+
+  1.0   (2018-03-27) Initial public release
+
+*/
+
+// clang-format off
+/*!
+
+ @file spirv_reflect.h
+
+*/
+#ifndef SPIRV_REFLECT_H
+#define SPIRV_REFLECT_H
+
+#if defined(SPIRV_REFLECT_USE_SYSTEM_SPIRV_H)
+#include <spirv/unified1/spirv.h>
+#else
+#include "./include/spirv/unified1/spirv.h"
+#endif
+
+
+#include <stdint.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+  #define SPV_REFLECT_DEPRECATED(msg_str) __declspec(deprecated("This symbol is deprecated. Details: " msg_str))
+#elif defined(__clang__)
+  #define SPV_REFLECT_DEPRECATED(msg_str) __attribute__((deprecated(msg_str)))
+#elif defined(__GNUC__)
+  #if GCC_VERSION >= 40500
+    #define SPV_REFLECT_DEPRECATED(msg_str) __attribute__((deprecated(msg_str)))
+  #else
+    #define SPV_REFLECT_DEPRECATED(msg_str) __attribute__((deprecated))
+  #endif
+#else
+  #define SPV_REFLECT_DEPRECATED(msg_str)
+#endif
+
+/*! @enum SpvReflectResult
+
+*/
+typedef enum SpvReflectResult {
+  SPV_REFLECT_RESULT_SUCCESS,
+  SPV_REFLECT_RESULT_NOT_READY,
+  SPV_REFLECT_RESULT_ERROR_PARSE_FAILED,
+  SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED,
+  SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED,
+  SPV_REFLECT_RESULT_ERROR_NULL_POINTER,
+  SPV_REFLECT_RESULT_ERROR_INTERNAL_ERROR,
+  SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH,
+  SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_CODE_SIZE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_MAGIC_NUMBER,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_SET_NUMBER_OVERFLOW,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_STORAGE_CLASS,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_RECURSION,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_INSTRUCTION,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_BLOCK_DATA,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_BLOCK_MEMBER_REFERENCE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ENTRY_POINT,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_EXECUTION_MODE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_MAX_RECURSIVE_EXCEEDED,
+} SpvReflectResult;
+
+/*! @enum SpvReflectModuleFlagBits
+
+SPV_REFLECT_MODULE_FLAG_NO_COPY - Disables copying of SPIR-V code
+  when a SPIRV-Reflect shader module is created. It is the
+  responsibility of the calling program to ensure that the pointer
+  remains valid and the memory it's pointing to is not freed while
+  SPIRV-Reflect operations are taking place. Freeing the backing
+  memory will cause undefined behavior or most likely a crash.
+  This is flag is intended for cases where the memory overhead of
+  storing the copied SPIR-V is undesirable.
+
+*/
+typedef enum SpvReflectModuleFlagBits {
+  SPV_REFLECT_MODULE_FLAG_NONE    = 0x00000000,
+  SPV_REFLECT_MODULE_FLAG_NO_COPY = 0x00000001,
+} SpvReflectModuleFlagBits;
+
+typedef uint32_t SpvReflectModuleFlags;
+
+/*! @enum SpvReflectTypeFlagBits
+
+*/
+typedef enum SpvReflectTypeFlagBits {
+  SPV_REFLECT_TYPE_FLAG_UNDEFINED                       = 0x00000000,
+  SPV_REFLECT_TYPE_FLAG_VOID                            = 0x00000001,
+  SPV_REFLECT_TYPE_FLAG_BOOL                            = 0x00000002,
+  SPV_REFLECT_TYPE_FLAG_INT                             = 0x00000004,
+  SPV_REFLECT_TYPE_FLAG_FLOAT                           = 0x00000008,
+  SPV_REFLECT_TYPE_FLAG_VECTOR                          = 0x00000100,
+  SPV_REFLECT_TYPE_FLAG_MATRIX                          = 0x00000200,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE                  = 0x00010000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLER                = 0x00020000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE          = 0x00040000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_BLOCK                  = 0x00080000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE = 0x00100000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_MASK                   = 0x00FF0000,
+  SPV_REFLECT_TYPE_FLAG_STRUCT                          = 0x10000000,
+  SPV_REFLECT_TYPE_FLAG_ARRAY                           = 0x20000000,
+  SPV_REFLECT_TYPE_FLAG_REF                             = 0x40000000,
+} SpvReflectTypeFlagBits;
+
+typedef uint32_t SpvReflectTypeFlags;
+
+/*! @enum SpvReflectDecorationBits
+
+NOTE: HLSL row_major and column_major decorations are reversed
+      in SPIR-V. Meaning that matrices declrations with row_major
+      will get reflected as column_major and vice versa. The
+      row and column decorations get appied during the compilation.
+      SPIRV-Reflect reads the data as is and does not make any
+      attempt to correct it to match what's in the source.
+
+      The Patch, PerVertex, and PerTask are used for Interface
+      variables that can have array
+
+*/
+typedef enum SpvReflectDecorationFlagBits {
+  SPV_REFLECT_DECORATION_NONE                   = 0x00000000,
+  SPV_REFLECT_DECORATION_BLOCK                  = 0x00000001,
+  SPV_REFLECT_DECORATION_BUFFER_BLOCK           = 0x00000002,
+  SPV_REFLECT_DECORATION_ROW_MAJOR              = 0x00000004,
+  SPV_REFLECT_DECORATION_COLUMN_MAJOR           = 0x00000008,
+  SPV_REFLECT_DECORATION_BUILT_IN               = 0x00000010,
+  SPV_REFLECT_DECORATION_NOPERSPECTIVE          = 0x00000020,
+  SPV_REFLECT_DECORATION_FLAT                   = 0x00000040,
+  SPV_REFLECT_DECORATION_NON_WRITABLE           = 0x00000080,
+  SPV_REFLECT_DECORATION_RELAXED_PRECISION      = 0x00000100,
+  SPV_REFLECT_DECORATION_NON_READABLE           = 0x00000200,
+  SPV_REFLECT_DECORATION_PATCH                  = 0x00000400,
+  SPV_REFLECT_DECORATION_PER_VERTEX             = 0x00000800,
+  SPV_REFLECT_DECORATION_PER_TASK               = 0x00001000,
+  SPV_REFLECT_DECORATION_WEIGHT_TEXTURE         = 0x00002000,
+  SPV_REFLECT_DECORATION_BLOCK_MATCH_TEXTURE    = 0x00004000,
+} SpvReflectDecorationFlagBits;
+
+typedef uint32_t SpvReflectDecorationFlags;
+
+// Based of SPV_GOOGLE_user_type
+typedef enum SpvReflectUserType {
+  SPV_REFLECT_USER_TYPE_INVALID = 0,
+  SPV_REFLECT_USER_TYPE_CBUFFER,
+  SPV_REFLECT_USER_TYPE_TBUFFER,
+  SPV_REFLECT_USER_TYPE_APPEND_STRUCTURED_BUFFER,
+  SPV_REFLECT_USER_TYPE_BUFFER,
+  SPV_REFLECT_USER_TYPE_BYTE_ADDRESS_BUFFER,
+  SPV_REFLECT_USER_TYPE_CONSTANT_BUFFER,
+  SPV_REFLECT_USER_TYPE_CONSUME_STRUCTURED_BUFFER,
+  SPV_REFLECT_USER_TYPE_INPUT_PATCH,
+  SPV_REFLECT_USER_TYPE_OUTPUT_PATCH,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_BUFFER,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_BYTE_ADDRESS_BUFFER,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_STRUCTURED_BUFFER,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_1D,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_1D_ARRAY,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_2D,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_2D_ARRAY,
+  SPV_REFLECT_USER_TYPE_RASTERIZER_ORDERED_TEXTURE_3D,
+  SPV_REFLECT_USER_TYPE_RAYTRACING_ACCELERATION_STRUCTURE,
+  SPV_REFLECT_USER_TYPE_RW_BUFFER,
+  SPV_REFLECT_USER_TYPE_RW_BYTE_ADDRESS_BUFFER,
+  SPV_REFLECT_USER_TYPE_RW_STRUCTURED_BUFFER,
+  SPV_REFLECT_USER_TYPE_RW_TEXTURE_1D,
+  SPV_REFLECT_USER_TYPE_RW_TEXTURE_1D_ARRAY,
+  SPV_REFLECT_USER_TYPE_RW_TEXTURE_2D,
+  SPV_REFLECT_USER_TYPE_RW_TEXTURE_2D_ARRAY,
+  SPV_REFLECT_USER_TYPE_RW_TEXTURE_3D,
+  SPV_REFLECT_USER_TYPE_STRUCTURED_BUFFER,
+  SPV_REFLECT_USER_TYPE_SUBPASS_INPUT,
+  SPV_REFLECT_USER_TYPE_SUBPASS_INPUT_MS,
+  SPV_REFLECT_USER_TYPE_TEXTURE_1D,
+  SPV_REFLECT_USER_TYPE_TEXTURE_1D_ARRAY,
+  SPV_REFLECT_USER_TYPE_TEXTURE_2D,
+  SPV_REFLECT_USER_TYPE_TEXTURE_2D_ARRAY,
+  SPV_REFLECT_USER_TYPE_TEXTURE_2DMS,
+  SPV_REFLECT_USER_TYPE_TEXTURE_2DMS_ARRAY,
+  SPV_REFLECT_USER_TYPE_TEXTURE_3D,
+  SPV_REFLECT_USER_TYPE_TEXTURE_BUFFER,
+  SPV_REFLECT_USER_TYPE_TEXTURE_CUBE,
+  SPV_REFLECT_USER_TYPE_TEXTURE_CUBE_ARRAY,
+} SpvReflectUserType;
+
+/*! @enum SpvReflectResourceType
+
+*/
+typedef enum SpvReflectResourceType {
+  SPV_REFLECT_RESOURCE_FLAG_UNDEFINED           = 0x00000000,
+  SPV_REFLECT_RESOURCE_FLAG_SAMPLER             = 0x00000001,
+  SPV_REFLECT_RESOURCE_FLAG_CBV                 = 0x00000002,
+  SPV_REFLECT_RESOURCE_FLAG_SRV                 = 0x00000004,
+  SPV_REFLECT_RESOURCE_FLAG_UAV                 = 0x00000008,
+} SpvReflectResourceType;
+
+/*! @enum SpvReflectFormat
+
+*/
+typedef enum SpvReflectFormat {
+  SPV_REFLECT_FORMAT_UNDEFINED           =   0, // = VK_FORMAT_UNDEFINED
+  SPV_REFLECT_FORMAT_R16_UINT            =  74, // = VK_FORMAT_R16_UINT
+  SPV_REFLECT_FORMAT_R16_SINT            =  75, // = VK_FORMAT_R16_SINT
+  SPV_REFLECT_FORMAT_R16_SFLOAT          =  76, // = VK_FORMAT_R16_SFLOAT
+  SPV_REFLECT_FORMAT_R16G16_UINT         =  81, // = VK_FORMAT_R16G16_UINT
+  SPV_REFLECT_FORMAT_R16G16_SINT         =  82, // = VK_FORMAT_R16G16_SINT
+  SPV_REFLECT_FORMAT_R16G16_SFLOAT       =  83, // = VK_FORMAT_R16G16_SFLOAT
+  SPV_REFLECT_FORMAT_R16G16B16_UINT      =  88, // = VK_FORMAT_R16G16B16_UINT
+  SPV_REFLECT_FORMAT_R16G16B16_SINT      =  89, // = VK_FORMAT_R16G16B16_SINT
+  SPV_REFLECT_FORMAT_R16G16B16_SFLOAT    =  90, // = VK_FORMAT_R16G16B16_SFLOAT
+  SPV_REFLECT_FORMAT_R16G16B16A16_UINT   =  95, // = VK_FORMAT_R16G16B16A16_UINT
+  SPV_REFLECT_FORMAT_R16G16B16A16_SINT   =  96, // = VK_FORMAT_R16G16B16A16_SINT
+  SPV_REFLECT_FORMAT_R16G16B16A16_SFLOAT =  97, // = VK_FORMAT_R16G16B16A16_SFLOAT
+  SPV_REFLECT_FORMAT_R32_UINT            =  98, // = VK_FORMAT_R32_UINT
+  SPV_REFLECT_FORMAT_R32_SINT            =  99, // = VK_FORMAT_R32_SINT
+  SPV_REFLECT_FORMAT_R32_SFLOAT          = 100, // = VK_FORMAT_R32_SFLOAT
+  SPV_REFLECT_FORMAT_R32G32_UINT         = 101, // = VK_FORMAT_R32G32_UINT
+  SPV_REFLECT_FORMAT_R32G32_SINT         = 102, // = VK_FORMAT_R32G32_SINT
+  SPV_REFLECT_FORMAT_R32G32_SFLOAT       = 103, // = VK_FORMAT_R32G32_SFLOAT
+  SPV_REFLECT_FORMAT_R32G32B32_UINT      = 104, // = VK_FORMAT_R32G32B32_UINT
+  SPV_REFLECT_FORMAT_R32G32B32_SINT      = 105, // = VK_FORMAT_R32G32B32_SINT
+  SPV_REFLECT_FORMAT_R32G32B32_SFLOAT    = 106, // = VK_FORMAT_R32G32B32_SFLOAT
+  SPV_REFLECT_FORMAT_R32G32B32A32_UINT   = 107, // = VK_FORMAT_R32G32B32A32_UINT
+  SPV_REFLECT_FORMAT_R32G32B32A32_SINT   = 108, // = VK_FORMAT_R32G32B32A32_SINT
+  SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT = 109, // = VK_FORMAT_R32G32B32A32_SFLOAT
+  SPV_REFLECT_FORMAT_R64_UINT            = 110, // = VK_FORMAT_R64_UINT
+  SPV_REFLECT_FORMAT_R64_SINT            = 111, // = VK_FORMAT_R64_SINT
+  SPV_REFLECT_FORMAT_R64_SFLOAT          = 112, // = VK_FORMAT_R64_SFLOAT
+  SPV_REFLECT_FORMAT_R64G64_UINT         = 113, // = VK_FORMAT_R64G64_UINT
+  SPV_REFLECT_FORMAT_R64G64_SINT         = 114, // = VK_FORMAT_R64G64_SINT
+  SPV_REFLECT_FORMAT_R64G64_SFLOAT       = 115, // = VK_FORMAT_R64G64_SFLOAT
+  SPV_REFLECT_FORMAT_R64G64B64_UINT      = 116, // = VK_FORMAT_R64G64B64_UINT
+  SPV_REFLECT_FORMAT_R64G64B64_SINT      = 117, // = VK_FORMAT_R64G64B64_SINT
+  SPV_REFLECT_FORMAT_R64G64B64_SFLOAT    = 118, // = VK_FORMAT_R64G64B64_SFLOAT
+  SPV_REFLECT_FORMAT_R64G64B64A64_UINT   = 119, // = VK_FORMAT_R64G64B64A64_UINT
+  SPV_REFLECT_FORMAT_R64G64B64A64_SINT   = 120, // = VK_FORMAT_R64G64B64A64_SINT
+  SPV_REFLECT_FORMAT_R64G64B64A64_SFLOAT = 121, // = VK_FORMAT_R64G64B64A64_SFLOAT
+} SpvReflectFormat;
+
+/*! @enum SpvReflectVariableFlagBits
+
+*/
+enum SpvReflectVariableFlagBits{
+  SPV_REFLECT_VARIABLE_FLAGS_NONE   = 0x00000000,
+  SPV_REFLECT_VARIABLE_FLAGS_UNUSED = 0x00000001,
+  // If variable points to a copy of the PhysicalStorageBuffer struct
+  SPV_REFLECT_VARIABLE_FLAGS_PHYSICAL_POINTER_COPY = 0x00000002,
+};
+
+typedef uint32_t SpvReflectVariableFlags;
+
+/*! @enum SpvReflectDescriptorType
+
+*/
+typedef enum SpvReflectDescriptorType {
+  SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER                    =  0,        // = VK_DESCRIPTOR_TYPE_SAMPLER
+  SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER     =  1,        // = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+  SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE              =  2,        // = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE              =  3,        // = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
+  SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER       =  4,        // = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER       =  5,        // = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER             =  6,        // = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER             =  7,        // = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC     =  8,        // = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC     =  9,        // = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC
+  SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT           = 10,        // = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
+  SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000 // = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR
+} SpvReflectDescriptorType;
+
+/*! @enum SpvReflectShaderStageFlagBits
+
+*/
+typedef enum SpvReflectShaderStageFlagBits {
+  SPV_REFLECT_SHADER_STAGE_VERTEX_BIT                  = 0x00000001, // = VK_SHADER_STAGE_VERTEX_BIT
+  SPV_REFLECT_SHADER_STAGE_TESSELLATION_CONTROL_BIT    = 0x00000002, // = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT
+  SPV_REFLECT_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, // = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT
+  SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT                = 0x00000008, // = VK_SHADER_STAGE_GEOMETRY_BIT
+  SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT                = 0x00000010, // = VK_SHADER_STAGE_FRAGMENT_BIT
+  SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT                 = 0x00000020, // = VK_SHADER_STAGE_COMPUTE_BIT
+  SPV_REFLECT_SHADER_STAGE_TASK_BIT_NV                 = 0x00000040, // = VK_SHADER_STAGE_TASK_BIT_NV
+  SPV_REFLECT_SHADER_STAGE_TASK_BIT_EXT                = SPV_REFLECT_SHADER_STAGE_TASK_BIT_NV, // = VK_SHADER_STAGE_CALLABLE_BIT_EXT
+  SPV_REFLECT_SHADER_STAGE_MESH_BIT_NV                 = 0x00000080, // = VK_SHADER_STAGE_MESH_BIT_NV
+  SPV_REFLECT_SHADER_STAGE_MESH_BIT_EXT                = SPV_REFLECT_SHADER_STAGE_MESH_BIT_NV, // = VK_SHADER_STAGE_CALLABLE_BIT_EXT
+  SPV_REFLECT_SHADER_STAGE_RAYGEN_BIT_KHR              = 0x00000100, // = VK_SHADER_STAGE_RAYGEN_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_ANY_HIT_BIT_KHR             = 0x00000200, // = VK_SHADER_STAGE_ANY_HIT_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_CLOSEST_HIT_BIT_KHR         = 0x00000400, // = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_MISS_BIT_KHR                = 0x00000800, // = VK_SHADER_STAGE_MISS_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_INTERSECTION_BIT_KHR        = 0x00001000, // = VK_SHADER_STAGE_INTERSECTION_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_CALLABLE_BIT_KHR            = 0x00002000, // = VK_SHADER_STAGE_CALLABLE_BIT_KHR
+
+} SpvReflectShaderStageFlagBits;
+
+/*! @enum SpvReflectGenerator
+
+*/
+typedef enum SpvReflectGenerator {
+  SPV_REFLECT_GENERATOR_KHRONOS_LLVM_SPIRV_TRANSLATOR         = 6,
+  SPV_REFLECT_GENERATOR_KHRONOS_SPIRV_TOOLS_ASSEMBLER         = 7,
+  SPV_REFLECT_GENERATOR_KHRONOS_GLSLANG_REFERENCE_FRONT_END   = 8,
+  SPV_REFLECT_GENERATOR_GOOGLE_SHADERC_OVER_GLSLANG           = 13,
+  SPV_REFLECT_GENERATOR_GOOGLE_SPIREGG                        = 14,
+  SPV_REFLECT_GENERATOR_GOOGLE_RSPIRV                         = 15,
+  SPV_REFLECT_GENERATOR_X_LEGEND_MESA_MESAIR_SPIRV_TRANSLATOR = 16,
+  SPV_REFLECT_GENERATOR_KHRONOS_SPIRV_TOOLS_LINKER            = 17,
+  SPV_REFLECT_GENERATOR_WINE_VKD3D_SHADER_COMPILER            = 18,
+  SPV_REFLECT_GENERATOR_CLAY_CLAY_SHADER_COMPILER             = 19,
+} SpvReflectGenerator;
+
+enum {
+  SPV_REFLECT_MAX_ARRAY_DIMS                    = 32,
+  SPV_REFLECT_MAX_DESCRIPTOR_SETS               = 64,
+};
+
+enum {
+  SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE        = ~0,
+  SPV_REFLECT_SET_NUMBER_DONT_CHANGE            = ~0
+};
+
+typedef struct SpvReflectNumericTraits {
+  struct Scalar {
+    uint32_t                        width;
+    uint32_t                        signedness;
+  } scalar;
+
+  struct Vector {
+    uint32_t                        component_count;
+  } vector;
+
+  struct Matrix {
+    uint32_t                        column_count;
+    uint32_t                        row_count;
+    uint32_t                        stride; // Measured in bytes
+  } matrix;
+} SpvReflectNumericTraits;
+
+typedef struct SpvReflectImageTraits {
+  SpvDim                            dim;
+  uint32_t                          depth;
+  uint32_t                          arrayed;
+  uint32_t                          ms; // 0: single-sampled; 1: multisampled
+  uint32_t                          sampled;
+  SpvImageFormat                    image_format;
+} SpvReflectImageTraits;
+
+typedef enum SpvReflectArrayDimType {
+  SPV_REFLECT_ARRAY_DIM_RUNTIME       = 0,         // OpTypeRuntimeArray
+} SpvReflectArrayDimType;
+
+typedef struct SpvReflectArrayTraits {
+  uint32_t                          dims_count;
+  // Each entry is either:
+  // - specialization constant dimension
+  // - OpTypeRuntimeArray
+  // - the array length otherwise
+  uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+  // Stores Ids for dimensions that are specialization constants
+  uint32_t                          spec_constant_op_ids[SPV_REFLECT_MAX_ARRAY_DIMS];
+  uint32_t                          stride; // Measured in bytes
+} SpvReflectArrayTraits;
+
+typedef struct SpvReflectBindingArrayTraits {
+  uint32_t                          dims_count;
+  uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+} SpvReflectBindingArrayTraits;
+
+/*! @struct SpvReflectTypeDescription
+    @brief Information about an OpType* instruction
+*/
+typedef struct SpvReflectTypeDescription {
+  uint32_t                          id;
+  SpvOp                             op;
+  const char*                       type_name;
+  // Non-NULL if type is member of a struct
+  const char*                       struct_member_name;
+  SpvStorageClass                   storage_class;
+  SpvReflectTypeFlags               type_flags;
+  SpvReflectDecorationFlags         decoration_flags;
+
+  struct Traits {
+    SpvReflectNumericTraits         numeric;
+    SpvReflectImageTraits           image;
+    SpvReflectArrayTraits           array;
+  } traits;
+
+  // If underlying type is a struct (ex. array of structs)
+  // this gives access to the OpTypeStruct
+  struct SpvReflectTypeDescription* struct_type_description;
+
+  // Some pointers to SpvReflectTypeDescription are really
+  // just copies of another reference to the same OpType
+  uint32_t                          copied;
+
+  // @deprecated use struct_type_description instead
+  uint32_t                          member_count;
+  // @deprecated use struct_type_description instead
+  struct SpvReflectTypeDescription* members;
+} SpvReflectTypeDescription;
+
+
+/*! @struct SpvReflectInterfaceVariable
+    @brief The OpVariable that is either an Input or Output to the module
+*/
+typedef struct SpvReflectInterfaceVariable {
+  uint32_t                            spirv_id;
+  const char*                         name;
+  uint32_t                            location;
+  uint32_t                            component;
+  SpvStorageClass                     storage_class;
+  const char*                         semantic;
+  SpvReflectDecorationFlags           decoration_flags;
+  SpvBuiltIn                          built_in;
+  SpvReflectNumericTraits             numeric;
+  SpvReflectArrayTraits               array;
+
+  uint32_t                            member_count;
+  struct SpvReflectInterfaceVariable* members;
+
+  SpvReflectFormat                    format;
+
+  // NOTE: SPIR-V shares type references for variables
+  //       that have the same underlying type. This means
+  //       that the same type name will appear for multiple
+  //       variables.
+  SpvReflectTypeDescription*          type_description;
+
+  struct {
+    uint32_t                          location;
+  } word_offset;
+} SpvReflectInterfaceVariable;
+
+/*! @struct SpvReflectBlockVariable
+
+*/
+typedef struct SpvReflectBlockVariable {
+  uint32_t                          spirv_id;
+  const char*                       name;
+  // For Push Constants, this is the lowest offset of all memebers
+  uint32_t                          offset;           // Measured in bytes
+  uint32_t                          absolute_offset;  // Measured in bytes
+  uint32_t                          size;             // Measured in bytes
+  uint32_t                          padded_size;      // Measured in bytes
+  SpvReflectDecorationFlags         decoration_flags;
+  SpvReflectNumericTraits           numeric;
+  SpvReflectArrayTraits             array;
+  SpvReflectVariableFlags           flags;
+
+  uint32_t                          member_count;
+  struct SpvReflectBlockVariable*   members;
+
+  SpvReflectTypeDescription*        type_description;
+
+  struct {
+    uint32_t                          offset;
+  } word_offset;
+
+} SpvReflectBlockVariable;
+
+/*! @struct SpvReflectDescriptorBinding
+
+*/
+typedef struct SpvReflectDescriptorBinding {
+  uint32_t                            spirv_id;
+  const char*                         name;
+  uint32_t                            binding;
+  uint32_t                            input_attachment_index;
+  uint32_t                            set;
+  SpvReflectDescriptorType            descriptor_type;
+  SpvReflectResourceType              resource_type;
+  SpvReflectImageTraits               image;
+  SpvReflectBlockVariable             block;
+  SpvReflectBindingArrayTraits        array;
+  uint32_t                            count;
+  uint32_t                            accessed;
+  uint32_t                            uav_counter_id;
+  struct SpvReflectDescriptorBinding* uav_counter_binding;
+  uint32_t                            byte_address_buffer_offset_count;
+  uint32_t*                           byte_address_buffer_offsets;
+
+  SpvReflectTypeDescription*          type_description;
+
+  struct {
+    uint32_t                          binding;
+    uint32_t                          set;
+  } word_offset;
+
+  SpvReflectDecorationFlags           decoration_flags;
+  // Requires SPV_GOOGLE_user_type
+  SpvReflectUserType                  user_type;
+} SpvReflectDescriptorBinding;
+
+/*! @struct SpvReflectDescriptorSet
+
+*/
+typedef struct SpvReflectDescriptorSet {
+  uint32_t                          set;
+  uint32_t                          binding_count;
+  SpvReflectDescriptorBinding**     bindings;
+} SpvReflectDescriptorSet;
+
+typedef enum SpvReflectExecutionModeValue {
+  SPV_REFLECT_EXECUTION_MODE_SPEC_CONSTANT = 0xFFFFFFFF // specialization constant
+} SpvReflectExecutionModeValue;
+
+/*! @struct SpvReflectEntryPoint
+
+ */
+typedef struct SpvReflectEntryPoint {
+  const char*                       name;
+  uint32_t                          id;
+
+  SpvExecutionModel                 spirv_execution_model;
+  SpvReflectShaderStageFlagBits     shader_stage;
+
+  uint32_t                          input_variable_count;
+  SpvReflectInterfaceVariable**     input_variables;
+  uint32_t                          output_variable_count;
+  SpvReflectInterfaceVariable**     output_variables;
+  uint32_t                          interface_variable_count;
+  SpvReflectInterfaceVariable*      interface_variables;
+
+  uint32_t                          descriptor_set_count;
+  SpvReflectDescriptorSet*          descriptor_sets;
+
+  uint32_t                          used_uniform_count;
+  uint32_t*                         used_uniforms;
+  uint32_t                          used_push_constant_count;
+  uint32_t*                         used_push_constants;
+
+  uint32_t                          execution_mode_count;
+  SpvExecutionMode*                 execution_modes;
+
+  struct LocalSize {
+    uint32_t                        x;
+    uint32_t                        y;
+    uint32_t                        z;
+  } local_size;
+  uint32_t                          invocations; // valid for geometry
+  uint32_t                          output_vertices; // valid for geometry, tesselation
+} SpvReflectEntryPoint;
+
+/*! @struct SpvReflectCapability
+
+*/
+typedef struct SpvReflectCapability {
+  SpvCapability                     value;
+  uint32_t                          word_offset;
+} SpvReflectCapability;
+
+
+/*! @struct SpvReflectSpecId
+
+*/
+typedef struct SpvReflectSpecializationConstant {
+  uint32_t spirv_id;
+  uint32_t constant_id;
+  const char* name;
+} SpvReflectSpecializationConstant;
+
+/*! @struct SpvReflectShaderModule
+
+*/
+typedef struct SpvReflectShaderModule {
+  SpvReflectGenerator               generator;
+  const char*                       entry_point_name;
+  uint32_t                          entry_point_id;
+  uint32_t                          entry_point_count;
+  SpvReflectEntryPoint*             entry_points;
+  SpvSourceLanguage                 source_language;
+  uint32_t                          source_language_version;
+  const char*                       source_file;
+  const char*                       source_source;
+  uint32_t                          capability_count;
+  SpvReflectCapability*             capabilities;
+  SpvExecutionModel                 spirv_execution_model;                            // Uses value(s) from first entry point
+  SpvReflectShaderStageFlagBits     shader_stage;                                     // Uses value(s) from first entry point
+  uint32_t                          descriptor_binding_count;                         // Uses value(s) from first entry point
+  SpvReflectDescriptorBinding*      descriptor_bindings;                              // Uses value(s) from first entry point
+  uint32_t                          descriptor_set_count;                             // Uses value(s) from first entry point
+  SpvReflectDescriptorSet           descriptor_sets[SPV_REFLECT_MAX_DESCRIPTOR_SETS]; // Uses value(s) from first entry point
+  uint32_t                          input_variable_count;                             // Uses value(s) from first entry point
+  SpvReflectInterfaceVariable**     input_variables;                                  // Uses value(s) from first entry point
+  uint32_t                          output_variable_count;                            // Uses value(s) from first entry point
+  SpvReflectInterfaceVariable**     output_variables;                                 // Uses value(s) from first entry point
+  uint32_t                          interface_variable_count;                         // Uses value(s) from first entry point
+  SpvReflectInterfaceVariable*      interface_variables;                              // Uses value(s) from first entry point
+  uint32_t                          push_constant_block_count;                        // Uses value(s) from first entry point
+  SpvReflectBlockVariable*          push_constant_blocks;                             // Uses value(s) from first entry point
+  uint32_t                          spec_constant_count;                              // Uses value(s) from first entry point
+  SpvReflectSpecializationConstant* spec_constants;                                   // Uses value(s) from first entry point
+
+  struct Internal {
+    SpvReflectModuleFlags           module_flags;
+    size_t                          spirv_size;
+    uint32_t*                       spirv_code;
+    uint32_t                        spirv_word_count;
+
+    size_t                          type_description_count;
+    SpvReflectTypeDescription*      type_descriptions;
+  } * _internal;
+
+} SpvReflectShaderModule;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*! @fn spvReflectCreateShaderModule
+
+ @param  size      Size in bytes of SPIR-V code.
+ @param  p_code    Pointer to SPIR-V code.
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           SPV_REFLECT_RESULT_SUCCESS on success.
+
+*/
+SpvReflectResult spvReflectCreateShaderModule(
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+);
+
+/*! @fn spvReflectCreateShaderModule2
+
+ @param  flags     Flags for module creations.
+ @param  size      Size in bytes of SPIR-V code.
+ @param  p_code    Pointer to SPIR-V code.
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           SPV_REFLECT_RESULT_SUCCESS on success.
+
+*/
+SpvReflectResult spvReflectCreateShaderModule2(
+  SpvReflectModuleFlags    flags,
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+);
+
+SPV_REFLECT_DEPRECATED("renamed to spvReflectCreateShaderModule")
+SpvReflectResult spvReflectGetShaderModule(
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+);
+
+
+/*! @fn spvReflectDestroyShaderModule
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+
+*/
+void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module);
+
+
+/*! @fn spvReflectGetCodeSize
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           Returns the size of the SPIR-V in bytes
+
+*/
+uint32_t spvReflectGetCodeSize(const SpvReflectShaderModule* p_module);
+
+
+/*! @fn spvReflectGetCode
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           Returns a const pointer to the compiled SPIR-V bytecode.
+
+*/
+const uint32_t* spvReflectGetCode(const SpvReflectShaderModule* p_module);
+
+/*! @fn spvReflectGetEntryPoint
+
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  Name of the requested entry point.
+ @return              Returns a const pointer to the requested entry point,
+                      or NULL if it's not found.
+*/
+const SpvReflectEntryPoint* spvReflectGetEntryPoint(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point
+);
+
+/*! @fn spvReflectEnumerateDescriptorBindings
+
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count      If pp_bindings is NULL, the module's descriptor binding
+                      count (across all descriptor sets) will be stored here.
+                      If pp_bindings is not NULL, *p_count must contain the
+                      module's descriptor binding count.
+ @param  pp_bindings  If NULL, the module's total descriptor binding count
+                      will be written to *p_count.
+                      If non-NULL, pp_bindings must point to an array with
+                      *p_count entries, where pointers to the module's
+                      descriptor bindings will be written. The caller must not
+                      free the binding pointers written to this array.
+ @return              If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                      Otherwise, the error code indicates the cause of the
+                      failure.
+
+*/
+SpvReflectResult spvReflectEnumerateDescriptorBindings(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t*                      p_count,
+  SpvReflectDescriptorBinding**  pp_bindings
+);
+
+/*! @fn spvReflectEnumerateEntryPointDescriptorBindings
+ @brief  Creates a listing of all descriptor bindings that are used in the
+         static call tree of the given entry point.
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The name of the entry point to get the descriptor bindings for.
+ @param  p_count      If pp_bindings is NULL, the entry point's descriptor binding
+                      count (across all descriptor sets) will be stored here.
+                      If pp_bindings is not NULL, *p_count must contain the
+                      entry points's descriptor binding count.
+ @param  pp_bindings  If NULL, the entry point's total descriptor binding count
+                      will be written to *p_count.
+                      If non-NULL, pp_bindings must point to an array with
+                      *p_count entries, where pointers to the entry point's
+                      descriptor bindings will be written. The caller must not
+                      free the binding pointers written to this array.
+ @return              If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                      Otherwise, the error code indicates the cause of the
+                      failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointDescriptorBindings(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorBinding** pp_bindings
+);
+
+/*! @fn spvReflectEnumerateDescriptorSets
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count   If pp_sets is NULL, the module's descriptor set
+                   count will be stored here.
+                   If pp_sets is not NULL, *p_count must contain the
+                   module's descriptor set count.
+ @param  pp_sets   If NULL, the module's total descriptor set count
+                   will be written to *p_count.
+                   If non-NULL, pp_sets must point to an array with
+                   *p_count entries, where pointers to the module's
+                   descriptor sets will be written. The caller must not
+                   free the descriptor set pointers written to this array.
+ @return           If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                   Otherwise, the error code indicates the cause of the
+                   failure.
+
+*/
+SpvReflectResult spvReflectEnumerateDescriptorSets(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorSet**     pp_sets
+);
+
+/*! @fn spvReflectEnumerateEntryPointDescriptorSets
+ @brief  Creates a listing of all descriptor sets and their bindings that are
+         used in the static call tree of a given entry point.
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The name of the entry point to get the descriptor bindings for.
+ @param  p_count     If pp_sets is NULL, the module's descriptor set
+                     count will be stored here.
+                     If pp_sets is not NULL, *p_count must contain the
+                     module's descriptor set count.
+ @param  pp_sets     If NULL, the module's total descriptor set count
+                     will be written to *p_count.
+                     If non-NULL, pp_sets must point to an array with
+                     *p_count entries, where pointers to the module's
+                     descriptor sets will be written. The caller must not
+                     free the descriptor set pointers written to this array.
+ @return             If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                     Otherwise, the error code indicates the cause of the
+                     failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointDescriptorSets(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorSet**     pp_sets
+);
+
+
+/*! @fn spvReflectEnumerateInterfaceVariables
+ @brief  If the module contains multiple entry points, this will only get
+         the interface variables for the first one.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the module's interface variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the module's interface variable count.
+ @param  pp_variables  If NULL, the module's interface variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the module's
+                       interface variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateInterfaceVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+/*! @fn spvReflectEnumerateEntryPointInterfaceVariables
+ @brief  Enumerate the interface variables for a given entry point.
+ @param  entry_point The name of the entry point to get the interface variables for.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the entry point's interface variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the entry point's interface variable count.
+ @param  pp_variables  If NULL, the entry point's interface variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the entry point's
+                       interface variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+
+/*! @fn spvReflectEnumerateInputVariables
+ @brief  If the module contains multiple entry points, this will only get
+         the input variables for the first one.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the module's input variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the module's input variable count.
+ @param  pp_variables  If NULL, the module's input variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the module's
+                       input variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateInputVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+/*! @fn spvReflectEnumerateEntryPointInputVariables
+ @brief  Enumerate the input variables for a given entry point.
+ @param  entry_point The name of the entry point to get the input variables for.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the entry point's input variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the entry point's input variable count.
+ @param  pp_variables  If NULL, the entry point's input variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the entry point's
+                       input variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointInputVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+
+/*! @fn spvReflectEnumerateOutputVariables
+ @brief  Note: If the module contains multiple entry points, this will only get
+         the output variables for the first one.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the module's output variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the module's output variable count.
+ @param  pp_variables  If NULL, the module's output variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the module's
+                       output variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateOutputVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+/*! @fn spvReflectEnumerateEntryPointOutputVariables
+ @brief  Enumerate the output variables for a given entry point.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point   The name of the entry point to get the output variables for.
+ @param  p_count       If pp_variables is NULL, the entry point's output variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the entry point's output variable count.
+ @param  pp_variables  If NULL, the entry point's output variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the entry point's
+                       output variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointOutputVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+
+/*! @fn spvReflectEnumeratePushConstantBlocks
+ @brief  Note: If the module contains multiple entry points, this will only get
+         the push constant blocks for the first one.
+ @param  p_module   Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count    If pp_blocks is NULL, the module's push constant
+                    block count will be stored here.
+                    If pp_blocks is not NULL, *p_count must
+                    contain the module's push constant block count.
+ @param  pp_blocks  If NULL, the module's push constant block count
+                    will be written to *p_count.
+                    If non-NULL, pp_blocks must point to an
+                    array with *p_count entries, where pointers to
+                    the module's push constant blocks will be written.
+                    The caller must not free the block variables written
+                    to this array.
+ @return            If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                    Otherwise, the error code indicates the cause of the
+                    failure.
+
+*/
+SpvReflectResult spvReflectEnumeratePushConstantBlocks(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectEnumeratePushConstantBlocks")
+SpvReflectResult spvReflectEnumeratePushConstants(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+);
+
+/*! @fn spvReflectEnumerateEntryPointPushConstantBlocks
+ @brief  Enumerate the push constant blocks used in the static call tree of a
+         given entry point.
+ @param  p_module   Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count    If pp_blocks is NULL, the entry point's push constant
+                    block count will be stored here.
+                    If pp_blocks is not NULL, *p_count must
+                    contain the entry point's push constant block count.
+ @param  pp_blocks  If NULL, the entry point's push constant block count
+                    will be written to *p_count.
+                    If non-NULL, pp_blocks must point to an
+                    array with *p_count entries, where pointers to
+                    the entry point's push constant blocks will be written.
+                    The caller must not free the block variables written
+                    to this array.
+ @return            If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                    Otherwise, the error code indicates the cause of the
+                    failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointPushConstantBlocks(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+);
+
+
+/*! @fn spvReflectEnumerateSpecializationConstants
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_blocks is NULL, the module's specialization constant
+                       count will be stored here. If pp_blocks is not NULL, *p_count
+                       must contain the module's specialization constant count.
+ @param  pp_constants  If NULL, the module's specialization constant count
+                       will be written to *p_count. If non-NULL, pp_blocks must
+                       point to an array with *p_count entries, where pointers to
+                       the module's specialization constant blocks will be written.
+                       The caller must not free the  variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the failure.
+*/
+SpvReflectResult spvReflectEnumerateSpecializationConstants(
+  const SpvReflectShaderModule*      p_module,
+  uint32_t*                          p_count,
+  SpvReflectSpecializationConstant** pp_constants
+);
+
+/*! @fn spvReflectGetDescriptorBinding
+
+ @param  p_module        Pointer to an instance of SpvReflectShaderModule.
+ @param  binding_number  The "binding" value of the requested descriptor
+                         binding.
+ @param  set_number      The "set" value of the requested descriptor binding.
+ @param  p_result        If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                         written to *p_result. Otherwise, a error code
+                         indicating the cause of the failure will be stored
+                         here.
+ @return                 If the module contains a descriptor binding that
+                         matches the provided [binding_number, set_number]
+                         values, a pointer to that binding is returned. The
+                         caller must not free this pointer.
+                         If no match can be found, or if an unrelated error
+                         occurs, the return value will be NULL. Detailed
+                         error results are written to *pResult.
+@note                    If the module contains multiple desriptor bindings
+                         with the same set and binding numbers, there are
+                         no guarantees about which binding will be returned.
+
+*/
+const SpvReflectDescriptorBinding* spvReflectGetDescriptorBinding(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      binding_number,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+/*! @fn spvReflectGetEntryPointDescriptorBinding
+ @brief  Get the descriptor binding with the given binding number and set
+         number that is used in the static call tree of a certain entry
+         point.
+ @param  p_module        Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point     The entry point to get the binding from.
+ @param  binding_number  The "binding" value of the requested descriptor
+                         binding.
+ @param  set_number      The "set" value of the requested descriptor binding.
+ @param  p_result        If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                         written to *p_result. Otherwise, a error code
+                         indicating the cause of the failure will be stored
+                         here.
+ @return                 If the entry point contains a descriptor binding that
+                         matches the provided [binding_number, set_number]
+                         values, a pointer to that binding is returned. The
+                         caller must not free this pointer.
+                         If no match can be found, or if an unrelated error
+                         occurs, the return value will be NULL. Detailed
+                         error results are written to *pResult.
+@note                    If the entry point contains multiple desriptor bindings
+                         with the same set and binding numbers, there are
+                         no guarantees about which binding will be returned.
+
+*/
+const SpvReflectDescriptorBinding* spvReflectGetEntryPointDescriptorBinding(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      binding_number,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+
+/*! @fn spvReflectGetDescriptorSet
+
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  set_number  The "set" value of the requested descriptor set.
+ @param  p_result    If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                     written to *p_result. Otherwise, a error code
+                     indicating the cause of the failure will be stored
+                     here.
+ @return             If the module contains a descriptor set with the
+                     provided set_number, a pointer to that set is
+                     returned. The caller must not free this pointer.
+                     If no match can be found, or if an unrelated error
+                     occurs, the return value will be NULL. Detailed
+                     error results are written to *pResult.
+
+*/
+const SpvReflectDescriptorSet* spvReflectGetDescriptorSet(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+/*! @fn spvReflectGetEntryPointDescriptorSet
+
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The entry point to get the descriptor set from.
+ @param  set_number  The "set" value of the requested descriptor set.
+ @param  p_result    If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                     written to *p_result. Otherwise, a error code
+                     indicating the cause of the failure will be stored
+                     here.
+ @return             If the entry point contains a descriptor set with the
+                     provided set_number, a pointer to that set is
+                     returned. The caller must not free this pointer.
+                     If no match can be found, or if an unrelated error
+                     occurs, the return value will be NULL. Detailed
+                     error results are written to *pResult.
+
+*/
+const SpvReflectDescriptorSet* spvReflectGetEntryPointDescriptorSet(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+
+/* @fn spvReflectGetInputVariableByLocation
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  location  The "location" value of the requested input variable.
+                   A location of 0xFFFFFFFF will always return NULL
+                   with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an input interface variable
+                   with the provided location value, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetInputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectGetInputVariableByLocation")
+const SpvReflectInterfaceVariable* spvReflectGetInputVariable(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetEntryPointInputVariableByLocation
+
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The entry point to get the input variable from.
+ @param  location    The "location" value of the requested input variable.
+                     A location of 0xFFFFFFFF will always return NULL
+                     with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result    If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                     written to *p_result. Otherwise, a error code
+                     indicating the cause of the failure will be stored
+                     here.
+ @return             If the entry point contains an input interface variable
+                     with the provided location value, a pointer to that
+                     variable is returned. The caller must not free this
+                     pointer.
+                     If no match can be found, or if an unrelated error
+                     occurs, the return value will be NULL. Detailed
+                     error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetInputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  semantic  The "semantic" value of the requested input variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an input interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetInputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   semantic,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetEntryPointInputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The entry point to get the input variable from.
+ @param  semantic  The "semantic" value of the requested input variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the entry point contains an input interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  const char*                   semantic,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetOutputVariableByLocation
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  location  The "location" value of the requested output variable.
+                   A location of 0xFFFFFFFF will always return NULL
+                   with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an output interface variable
+                   with the provided location value, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariableByLocation(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectGetOutputVariableByLocation")
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariable(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+);
+
+/* @fn spvReflectGetEntryPointOutputVariableByLocation
+
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The entry point to get the output variable from.
+ @param  location     The "location" value of the requested output variable.
+                      A location of 0xFFFFFFFF will always return NULL
+                      with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result     If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                      written to *p_result. Otherwise, a error code
+                      indicating the cause of the failure will be stored
+                      here.
+ @return              If the entry point contains an output interface variable
+                      with the provided location value, a pointer to that
+                      variable is returned. The caller must not free this
+                      pointer.
+                      If no match can be found, or if an unrelated error
+                      occurs, the return value will be NULL. Detailed
+                      error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableByLocation(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+);
+
+/* @fn spvReflectGetOutputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  semantic  The "semantic" value of the requested output variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an output interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariableBySemantic(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    semantic,
+  SpvReflectResult*              p_result
+);
+
+/* @fn spvReflectGetEntryPointOutputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The entry point to get the output variable from.
+ @param  semantic  The "semantic" value of the requested output variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the entry point contains an output interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableBySemantic(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  const char*                    semantic,
+  SpvReflectResult*              p_result
+);
+
+/*! @fn spvReflectGetPushConstantBlock
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  index     The index of the desired block within the module's
+                   array of push constant blocks.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the provided index is within range, a pointer to
+                   the corresponding push constant block is returned.
+                   The caller must not free this pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+
+*/
+const SpvReflectBlockVariable* spvReflectGetPushConstantBlock(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       index,
+  SpvReflectResult*              p_result
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectGetPushConstantBlock")
+const SpvReflectBlockVariable* spvReflectGetPushConstant(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       index,
+  SpvReflectResult*              p_result
+);
+
+/*! @fn spvReflectGetEntryPointPushConstantBlock
+ @brief  Get the push constant block corresponding to the given entry point.
+         As by the Vulkan specification there can be no more than one push
+         constant block used by a given entry point, so if there is one it will
+         be returned, otherwise NULL will be returned.
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The entry point to get the push constant block from.
+ @param  p_result     If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                      written to *p_result. Otherwise, a error code
+                      indicating the cause of the failure will be stored
+                      here.
+ @return              If the provided index is within range, a pointer to
+                      the corresponding push constant block is returned.
+                      The caller must not free this pointer.
+                      If no match can be found, or if an unrelated error
+                      occurs, the return value will be NULL. Detailed
+                      error results are written to *pResult.
+
+*/
+const SpvReflectBlockVariable* spvReflectGetEntryPointPushConstantBlock(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  SpvReflectResult*              p_result
+);
+
+
+/*! @fn spvReflectChangeDescriptorBindingNumbers
+ @brief  Assign new set and/or binding numbers to a descriptor binding.
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().  If the binding is used in multiple
+         entry points within the module, it will be changed in all of them.
+ @param  p_module            Pointer to an instance of SpvReflectShaderModule.
+ @param  p_binding           Pointer to the descriptor binding to modify.
+ @param  new_binding_number  The new binding number to assign to the
+                             provided descriptor binding.
+                             To leave the binding number unchanged, pass
+                             SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE.
+ @param  new_set_number      The new set number to assign to the
+                             provided descriptor binding. Successfully changing
+                             a descriptor binding's set number invalidates all
+                             existing SpvReflectDescriptorBinding and
+                             SpvReflectDescriptorSet pointers from this module.
+                             To leave the set number unchanged, pass
+                             SPV_REFLECT_SET_NUMBER_DONT_CHANGE.
+ @return                     If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                             Otherwise, the error code indicates the cause of
+                             the failure.
+*/
+SpvReflectResult spvReflectChangeDescriptorBindingNumbers(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectDescriptorBinding* p_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           new_set_number
+);
+SPV_REFLECT_DEPRECATED("Renamed to spvReflectChangeDescriptorBindingNumbers")
+SpvReflectResult spvReflectChangeDescriptorBindingNumber(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectDescriptorBinding* p_descriptor_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           optional_new_set_number
+);
+
+/*! @fn spvReflectChangeDescriptorSetNumber
+ @brief  Assign a new set number to an entire descriptor set (including
+         all descriptor bindings in that set).
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().  If the descriptor set is used in
+         multiple entry points within the module, it will be modified in all
+         of them.
+ @param  p_module        Pointer to an instance of SpvReflectShaderModule.
+ @param  p_set           Pointer to the descriptor binding to modify.
+ @param  new_set_number  The new set number to assign to the
+                         provided descriptor set, and all its descriptor
+                         bindings. Successfully changing a descriptor
+                         binding's set number invalidates all existing
+                         SpvReflectDescriptorBinding and
+                         SpvReflectDescriptorSet pointers from this module.
+                         To leave the set number unchanged, pass
+                         SPV_REFLECT_SET_NUMBER_DONT_CHANGE.
+ @return                 If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                         Otherwise, the error code indicates the cause of
+                         the failure.
+*/
+SpvReflectResult spvReflectChangeDescriptorSetNumber(
+  SpvReflectShaderModule*        p_module,
+  const SpvReflectDescriptorSet* p_set,
+  uint32_t                       new_set_number
+);
+
+/*! @fn spvReflectChangeInputVariableLocation
+ @brief  Assign a new location to an input interface variable.
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().
+         It is the caller's responsibility to avoid assigning the same
+         location to multiple input variables.  If the input variable is used
+         by multiple entry points in the module, it will be changed in all of
+         them.
+ @param  p_module          Pointer to an instance of SpvReflectShaderModule.
+ @param  p_input_variable  Pointer to the input variable to update.
+ @param  new_location      The new location to assign to p_input_variable.
+ @return                   If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                           Otherwise, the error code indicates the cause of
+                           the failure.
+
+*/
+SpvReflectResult spvReflectChangeInputVariableLocation(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectInterfaceVariable* p_input_variable,
+  uint32_t                           new_location
+);
+
+
+/*! @fn spvReflectChangeOutputVariableLocation
+ @brief  Assign a new location to an output interface variable.
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().
+         It is the caller's responsibility to avoid assigning the same
+         location to multiple output variables.  If the output variable is used
+         by multiple entry points in the module, it will be changed in all of
+         them.
+ @param  p_module          Pointer to an instance of SpvReflectShaderModule.
+ @param  p_output_variable Pointer to the output variable to update.
+ @param  new_location      The new location to assign to p_output_variable.
+ @return                   If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                           Otherwise, the error code indicates the cause of
+                           the failure.
+
+*/
+SpvReflectResult spvReflectChangeOutputVariableLocation(
+  SpvReflectShaderModule*             p_module,
+  const SpvReflectInterfaceVariable*  p_output_variable,
+  uint32_t                            new_location
+);
+
+
+/*! @fn spvReflectSourceLanguage
+
+ @param  source_lang  The source language code.
+ @return Returns string of source language specified in \a source_lang.
+         The caller must not free the memory associated with this string.
+*/
+const char* spvReflectSourceLanguage(SpvSourceLanguage source_lang);
+
+/*! @fn spvReflectBlockVariableTypeName
+
+ @param  p_var Pointer to block variable.
+ @return Returns string of block variable's type description type name
+         or NULL if p_var is NULL.
+*/
+const char* spvReflectBlockVariableTypeName(
+  const SpvReflectBlockVariable* p_var
+);
+
+#if defined(__cplusplus)
+};
+#endif
+
+#if defined(__cplusplus) && !defined(SPIRV_REFLECT_DISABLE_CPP_BINDINGS)
+#include <cstdlib>
+#include <string>
+#include <vector>
+
+namespace spv_reflect {
+
+/*! \class ShaderModule
+
+*/
+class ShaderModule {
+public:
+  ShaderModule();
+  ShaderModule(size_t size, const void* p_code, SpvReflectModuleFlags flags = SPV_REFLECT_MODULE_FLAG_NONE);
+  ShaderModule(const std::vector<uint8_t>& code, SpvReflectModuleFlags flags = SPV_REFLECT_MODULE_FLAG_NONE);
+  ShaderModule(const std::vector<uint32_t>& code, SpvReflectModuleFlags flags = SPV_REFLECT_MODULE_FLAG_NONE);
+  ~ShaderModule();
+
+  ShaderModule(ShaderModule&& other);
+  ShaderModule& operator=(ShaderModule&& other);
+
+  SpvReflectResult GetResult() const;
+
+  const SpvReflectShaderModule& GetShaderModule() const;
+
+  uint32_t        GetCodeSize() const;
+  const uint32_t* GetCode() const;
+
+  const char*           GetEntryPointName() const;
+
+  const char*           GetSourceFile() const;
+
+  uint32_t                      GetEntryPointCount() const;
+  const char*                   GetEntryPointName(uint32_t index) const;
+  SpvReflectShaderStageFlagBits GetEntryPointShaderStage(uint32_t index) const;
+
+  SpvReflectShaderStageFlagBits GetShaderStage() const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetShaderStage")
+  SpvReflectShaderStageFlagBits GetVulkanShaderStage() const {
+    return GetShaderStage();
+  }
+
+  SpvReflectResult  EnumerateDescriptorBindings(uint32_t* p_count, SpvReflectDescriptorBinding** pp_bindings) const;
+  SpvReflectResult  EnumerateEntryPointDescriptorBindings(const char* entry_point, uint32_t* p_count, SpvReflectDescriptorBinding** pp_bindings) const;
+  SpvReflectResult  EnumerateDescriptorSets( uint32_t* p_count, SpvReflectDescriptorSet** pp_sets) const ;
+  SpvReflectResult  EnumerateEntryPointDescriptorSets(const char* entry_point, uint32_t* p_count, SpvReflectDescriptorSet** pp_sets) const ;
+  SpvReflectResult  EnumerateInterfaceVariables(uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateEntryPointInterfaceVariables(const char* entry_point, uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateInputVariables(uint32_t* p_count,SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateEntryPointInputVariables(const char* entry_point, uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateOutputVariables(uint32_t* p_count,SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateEntryPointOutputVariables(const char* entry_point, uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumeratePushConstantBlocks(uint32_t* p_count, SpvReflectBlockVariable** pp_blocks) const;
+  SpvReflectResult  EnumerateEntryPointPushConstantBlocks(const char* entry_point, uint32_t* p_count, SpvReflectBlockVariable** pp_blocks) const;
+  SPV_REFLECT_DEPRECATED("Renamed to EnumeratePushConstantBlocks")
+  SpvReflectResult  EnumeratePushConstants(uint32_t* p_count, SpvReflectBlockVariable** pp_blocks) const {
+    return EnumeratePushConstantBlocks(p_count, pp_blocks);
+  }
+  SpvReflectResult  EnumerateSpecializationConstants(uint32_t* p_count, SpvReflectSpecializationConstant** pp_constants) const;
+
+  const SpvReflectDescriptorBinding*  GetDescriptorBinding(uint32_t binding_number, uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectDescriptorBinding*  GetEntryPointDescriptorBinding(const char* entry_point, uint32_t binding_number, uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectDescriptorSet*      GetDescriptorSet(uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectDescriptorSet*      GetEntryPointDescriptorSet(const char* entry_point, uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetInputVariableByLocation(uint32_t location,  SpvReflectResult* p_result = nullptr) const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetInputVariableByLocation")
+  const SpvReflectInterfaceVariable*  GetInputVariable(uint32_t location,  SpvReflectResult* p_result = nullptr) const {
+    return GetInputVariableByLocation(location, p_result);
+  }
+  const SpvReflectInterfaceVariable*  GetEntryPointInputVariableByLocation(const char* entry_point, uint32_t location,  SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetInputVariableBySemantic(const char* semantic,  SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetEntryPointInputVariableBySemantic(const char* entry_point, const char* semantic,  SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetOutputVariableByLocation(uint32_t location, SpvReflectResult*  p_result = nullptr) const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetOutputVariableByLocation")
+  const SpvReflectInterfaceVariable*  GetOutputVariable(uint32_t location, SpvReflectResult*  p_result = nullptr) const {
+    return GetOutputVariableByLocation(location, p_result);
+  }
+  const SpvReflectInterfaceVariable*  GetEntryPointOutputVariableByLocation(const char* entry_point, uint32_t location, SpvReflectResult*  p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetOutputVariableBySemantic(const char* semantic, SpvReflectResult*  p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetEntryPointOutputVariableBySemantic(const char* entry_point, const char* semantic, SpvReflectResult*  p_result = nullptr) const;
+  const SpvReflectBlockVariable*      GetPushConstantBlock(uint32_t index, SpvReflectResult*  p_result = nullptr) const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetPushConstantBlock")
+  const SpvReflectBlockVariable*      GetPushConstant(uint32_t index, SpvReflectResult*  p_result = nullptr) const {
+    return GetPushConstantBlock(index, p_result);
+  }
+  const SpvReflectBlockVariable*      GetEntryPointPushConstantBlock(const char* entry_point, SpvReflectResult*  p_result = nullptr) const;
+
+  SpvReflectResult ChangeDescriptorBindingNumbers(const SpvReflectDescriptorBinding* p_binding,
+      uint32_t new_binding_number = SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE,
+      uint32_t optional_new_set_number = SPV_REFLECT_SET_NUMBER_DONT_CHANGE);
+  SPV_REFLECT_DEPRECATED("Renamed to ChangeDescriptorBindingNumbers")
+  SpvReflectResult ChangeDescriptorBindingNumber(const SpvReflectDescriptorBinding* p_binding, uint32_t new_binding_number = SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE,
+      uint32_t new_set_number = SPV_REFLECT_SET_NUMBER_DONT_CHANGE) {
+    return ChangeDescriptorBindingNumbers(p_binding, new_binding_number, new_set_number);
+  }
+  SpvReflectResult ChangeDescriptorSetNumber(const SpvReflectDescriptorSet* p_set, uint32_t new_set_number = SPV_REFLECT_SET_NUMBER_DONT_CHANGE);
+  SpvReflectResult ChangeInputVariableLocation(const SpvReflectInterfaceVariable* p_input_variable, uint32_t new_location);
+  SpvReflectResult ChangeOutputVariableLocation(const SpvReflectInterfaceVariable* p_output_variable, uint32_t new_location);
+
+private:
+  // Make noncopyable
+  ShaderModule(const ShaderModule&);
+  ShaderModule& operator=(const ShaderModule&);
+
+private:
+  mutable SpvReflectResult  m_result = SPV_REFLECT_RESULT_NOT_READY;
+  SpvReflectShaderModule    m_module = {};
+};
+
+
+// =================================================================================================
+// ShaderModule
+// =================================================================================================
+
+/*! @fn ShaderModule
+
+*/
+inline ShaderModule::ShaderModule() {}
+
+
+/*! @fn ShaderModule
+
+  @param  size
+  @param  p_code
+
+*/
+inline ShaderModule::ShaderModule(size_t size, const void* p_code, SpvReflectModuleFlags flags) {
+  m_result = spvReflectCreateShaderModule2(
+    flags,
+    size,
+    p_code,
+    &m_module);
+}
+
+/*! @fn ShaderModule
+
+  @param  code
+
+*/
+inline ShaderModule::ShaderModule(const std::vector<uint8_t>& code, SpvReflectModuleFlags flags) {
+  m_result = spvReflectCreateShaderModule2(
+    flags,
+    code.size(),
+    code.data(),
+    &m_module);
+}
+
+/*! @fn ShaderModule
+
+  @param  code
+
+*/
+inline ShaderModule::ShaderModule(const std::vector<uint32_t>& code, SpvReflectModuleFlags flags) {
+  m_result = spvReflectCreateShaderModule2(
+    flags,
+    code.size() * sizeof(uint32_t),
+    code.data(),
+    &m_module);
+}
+
+/*! @fn  ~ShaderModule
+
+*/
+inline ShaderModule::~ShaderModule() {
+  spvReflectDestroyShaderModule(&m_module);
+}
+
+
+inline ShaderModule::ShaderModule(ShaderModule&& other)
+{
+    *this = std::move(other);
+}
+
+inline ShaderModule& ShaderModule::operator=(ShaderModule&& other)
+{
+    m_result = std::move(other.m_result);
+    m_module = std::move(other.m_module);
+
+    other.m_module = {};
+    return *this;
+}
+
+/*! @fn GetResult
+
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::GetResult() const {
+  return m_result;
+}
+
+
+/*! @fn GetShaderModule
+
+  @return
+
+*/
+inline const SpvReflectShaderModule& ShaderModule::GetShaderModule() const {
+  return m_module;
+}
+
+
+/*! @fn GetCodeSize
+
+  @return
+
+  */
+inline uint32_t ShaderModule::GetCodeSize() const {
+  return spvReflectGetCodeSize(&m_module);
+}
+
+
+/*! @fn GetCode
+
+  @return
+
+*/
+inline const uint32_t* ShaderModule::GetCode() const {
+  return spvReflectGetCode(&m_module);
+}
+
+
+/*! @fn GetEntryPoint
+
+  @return Returns entry point
+
+*/
+inline const char* ShaderModule::GetEntryPointName() const {
+  return this->GetEntryPointName(0);
+}
+
+/*! @fn GetEntryPoint
+
+  @return Returns entry point
+
+*/
+inline const char* ShaderModule::GetSourceFile() const {
+  return m_module.source_file;
+}
+
+/*! @fn GetEntryPointCount
+
+  @param
+  @return
+*/
+inline uint32_t ShaderModule::GetEntryPointCount() const {
+  return m_module.entry_point_count;
+}
+
+/*! @fn GetEntryPointName
+
+  @param index
+  @return
+*/
+inline const char* ShaderModule::GetEntryPointName(uint32_t index) const {
+  return m_module.entry_points[index].name;
+}
+
+/*! @fn GetEntryPointShaderStage
+
+  @param index
+  @return Returns the shader stage for the entry point at \b index
+*/
+inline SpvReflectShaderStageFlagBits ShaderModule::GetEntryPointShaderStage(uint32_t index) const {
+  return m_module.entry_points[index].shader_stage;
+}
+
+/*! @fn GetShaderStage
+
+  @return Returns shader stage for the first entry point
+
+*/
+inline SpvReflectShaderStageFlagBits ShaderModule::GetShaderStage() const {
+  return m_module.shader_stage;
+}
+
+/*! @fn EnumerateDescriptorBindings
+
+  @param  count
+  @param  p_binding_numbers
+  @param  pp_bindings
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateDescriptorBindings(
+  uint32_t*                     p_count,
+  SpvReflectDescriptorBinding** pp_bindings
+) const
+{
+  m_result = spvReflectEnumerateDescriptorBindings(
+    &m_module,
+    p_count,
+    pp_bindings);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointDescriptorBindings
+
+  @param  entry_point
+  @param  count
+  @param  pp_bindings
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointDescriptorBindings(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorBinding** pp_bindings
+) const
+{
+  m_result = spvReflectEnumerateEntryPointDescriptorBindings(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_bindings);
+  return m_result;
+}
+
+
+/*! @fn EnumerateDescriptorSets
+
+  @param  count
+  @param  pp_sets
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateDescriptorSets(
+  uint32_t*                 p_count,
+  SpvReflectDescriptorSet** pp_sets
+) const
+{
+  m_result = spvReflectEnumerateDescriptorSets(
+    &m_module,
+    p_count,
+    pp_sets);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointDescriptorSets
+
+  @param  entry_point
+  @param  count
+  @param  pp_sets
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointDescriptorSets(
+  const char*               entry_point,
+  uint32_t*                 p_count,
+  SpvReflectDescriptorSet** pp_sets
+) const
+{
+  m_result = spvReflectEnumerateEntryPointDescriptorSets(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_sets);
+  return m_result;
+}
+
+
+/*! @fn EnumerateInterfaceVariables
+
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateInterfaceVariables(
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateInterfaceVariables(
+    &m_module,
+    p_count,
+    pp_variables);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointInterfaceVariables
+
+  @param  entry_point
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointInterfaceVariables(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateEntryPointInterfaceVariables(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_variables);
+  return m_result;
+}
+
+
+/*! @fn EnumerateInputVariables
+
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateInputVariables(
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateInputVariables(
+    &m_module,
+    p_count,
+    pp_variables);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointInputVariables
+
+  @param  entry_point
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointInputVariables(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateEntryPointInputVariables(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_variables);
+  return m_result;
+}
+
+
+/*! @fn EnumerateOutputVariables
+
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateOutputVariables(
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateOutputVariables(
+    &m_module,
+    p_count,
+    pp_variables);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointOutputVariables
+
+  @param  entry_point
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointOutputVariables(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateEntryPointOutputVariables(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_variables);
+  return m_result;
+}
+
+
+/*! @fn EnumeratePushConstantBlocks
+
+  @param  count
+  @param  pp_blocks
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumeratePushConstantBlocks(
+  uint32_t*                 p_count,
+  SpvReflectBlockVariable** pp_blocks
+) const
+{
+  m_result = spvReflectEnumeratePushConstantBlocks(
+    &m_module,
+    p_count,
+    pp_blocks);
+  return m_result;
+}
+
+/*! @fn EnumerateSpecializationConstants
+  @param  p_count
+  @param  pp_constants
+  @return
+*/
+inline SpvReflectResult ShaderModule::EnumerateSpecializationConstants(
+    uint32_t*                          p_count,
+    SpvReflectSpecializationConstant** pp_constants
+) const
+{
+  m_result = spvReflectEnumerateSpecializationConstants(
+    &m_module,
+    p_count,
+    pp_constants
+  );
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointPushConstantBlocks
+
+  @param  entry_point
+  @param  count
+  @param  pp_blocks
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointPushConstantBlocks(
+  const char*               entry_point,
+  uint32_t*                 p_count,
+  SpvReflectBlockVariable** pp_blocks
+) const
+{
+  m_result = spvReflectEnumerateEntryPointPushConstantBlocks(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_blocks);
+  return m_result;
+}
+
+
+/*! @fn GetDescriptorBinding
+
+  @param  binding_number
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorBinding* ShaderModule::GetDescriptorBinding(
+  uint32_t          binding_number,
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetDescriptorBinding(
+    &m_module,
+    binding_number,
+    set_number,
+    p_result);
+}
+
+/*! @fn GetEntryPointDescriptorBinding
+
+  @param  entry_point
+  @param  binding_number
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorBinding* ShaderModule::GetEntryPointDescriptorBinding(
+  const char*       entry_point,
+  uint32_t          binding_number,
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointDescriptorBinding(
+    &m_module,
+    entry_point,
+    binding_number,
+    set_number,
+    p_result);
+}
+
+
+/*! @fn GetDescriptorSet
+
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorSet* ShaderModule::GetDescriptorSet(
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetDescriptorSet(
+    &m_module,
+    set_number,
+    p_result);
+}
+
+/*! @fn GetEntryPointDescriptorSet
+
+  @param  entry_point
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorSet* ShaderModule::GetEntryPointDescriptorSet(
+  const char*       entry_point,
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointDescriptorSet(
+    &m_module,
+    entry_point,
+    set_number,
+    p_result);
+}
+
+
+/*! @fn GetInputVariable
+
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetInputVariableByLocation(
+  uint32_t          location,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetInputVariableByLocation(
+    &m_module,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetInputVariableBySemantic(
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetInputVariableBySemantic(
+    &m_module,
+    semantic,
+    p_result);
+}
+
+/*! @fn GetEntryPointInputVariable
+
+  @param  entry_point
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointInputVariableByLocation(
+  const char*       entry_point,
+  uint32_t          location,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointInputVariableByLocation(
+    &m_module,
+    entry_point,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointInputVariableBySemantic(
+  const char*       entry_point,
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointInputVariableBySemantic(
+    &m_module,
+    entry_point,
+    semantic,
+    p_result);
+}
+
+
+/*! @fn GetOutputVariable
+
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetOutputVariableByLocation(
+  uint32_t           location,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetOutputVariableByLocation(
+    &m_module,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetOutputVariableBySemantic(
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetOutputVariableBySemantic(&m_module,
+    semantic,
+    p_result);
+}
+
+/*! @fn GetEntryPointOutputVariable
+
+  @param  entry_point
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointOutputVariableByLocation(
+  const char*        entry_point,
+  uint32_t           location,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetEntryPointOutputVariableByLocation(
+    &m_module,
+    entry_point,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointOutputVariableBySemantic(
+  const char*       entry_point,
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointOutputVariableBySemantic(
+    &m_module,
+    entry_point,
+    semantic,
+    p_result);
+}
+
+
+/*! @fn GetPushConstant
+
+  @param  index
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectBlockVariable* ShaderModule::GetPushConstantBlock(
+  uint32_t           index,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetPushConstantBlock(
+    &m_module,
+    index,
+    p_result);
+}
+
+/*! @fn GetEntryPointPushConstant
+
+  @param  entry_point
+  @param  index
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectBlockVariable* ShaderModule::GetEntryPointPushConstantBlock(
+  const char*        entry_point,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetEntryPointPushConstantBlock(
+    &m_module,
+    entry_point,
+    p_result);
+}
+
+
+/*! @fn ChangeDescriptorBindingNumbers
+
+  @param  p_binding
+  @param  new_binding_number
+  @param  new_set_number
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeDescriptorBindingNumbers(
+  const SpvReflectDescriptorBinding* p_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           new_set_number
+)
+{
+  return spvReflectChangeDescriptorBindingNumbers(
+    &m_module,
+    p_binding,
+    new_binding_number,
+    new_set_number);
+}
+
+
+/*! @fn ChangeDescriptorSetNumber
+
+  @param  p_set
+  @param  new_set_number
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeDescriptorSetNumber(
+  const SpvReflectDescriptorSet* p_set,
+  uint32_t                       new_set_number
+)
+{
+  return spvReflectChangeDescriptorSetNumber(
+    &m_module,
+    p_set,
+    new_set_number);
+}
+
+
+/*! @fn ChangeInputVariableLocation
+
+  @param  p_input_variable
+  @param  new_location
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeInputVariableLocation(
+  const SpvReflectInterfaceVariable* p_input_variable,
+  uint32_t                           new_location)
+{
+  return spvReflectChangeInputVariableLocation(
+    &m_module,
+    p_input_variable,
+    new_location);
+}
+
+
+/*! @fn ChangeOutputVariableLocation
+
+  @param  p_input_variable
+  @param  new_location
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeOutputVariableLocation(
+  const SpvReflectInterfaceVariable* p_output_variable,
+  uint32_t                           new_location)
+{
+  return spvReflectChangeOutputVariableLocation(
+    &m_module,
+    p_output_variable,
+    new_location);
+}
+
+} // namespace spv_reflect
+#endif // defined(__cplusplus) && !defined(SPIRV_REFLECT_DISABLE_CPP_WRAPPER)
+#endif // SPIRV_REFLECT_H
+
+// clang-format on
index 2587713813ceb3b2af374134d26b54dae8ec7c6b..1f73823dbe9b127c2b6b2a5c8b7b4d00dba4e36b 100644 (file)
@@ -40,3 +40,7 @@ SET( static_libraries_nanosvg_src_files
 SET( adaptor_macos_platform_src_files
     ${adaptor_thirdparty_dir}/macos-platform/thread.cpp
 )
+
+SET( adaptor_libraries_spirv_reflect_src_files
+    ${adaptor_thirdparty_dir}/SPIRV-Reflect/spirv_reflect.cpp
+)
\ No newline at end of file