#include #include #include #include #include #include #include "../common/output_stream.h" #include "gtest/gtest.h" #include "spirv_reflect.h" #if defined(_MSC_VER) #include #define posix_chdir(d) _chdir(d) #else #include #define posix_chdir(d) chdir(d) #endif #if defined(SPIRV_REFLECT_HAS_VULKAN_H) // clang-format off // Verify that SpvReflect enums match their Vk equivalents where appropriate #include // SpvReflectFormat == VkFormat static_assert((uint32_t)SPV_REFLECT_FORMAT_UNDEFINED == (uint32_t)VK_FORMAT_UNDEFINED, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32_UINT == (uint32_t)VK_FORMAT_R32_UINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32_SINT == (uint32_t)VK_FORMAT_R32_SINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32_SFLOAT == (uint32_t)VK_FORMAT_R32_SFLOAT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32_UINT == (uint32_t)VK_FORMAT_R32G32_UINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32_SINT == (uint32_t)VK_FORMAT_R32G32_SINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32_SFLOAT == (uint32_t)VK_FORMAT_R32G32_SFLOAT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32B32_UINT == (uint32_t)VK_FORMAT_R32G32B32_UINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32B32_SINT == (uint32_t)VK_FORMAT_R32G32B32_SINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32B32_SFLOAT == (uint32_t)VK_FORMAT_R32G32B32_SFLOAT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32B32A32_UINT == (uint32_t)VK_FORMAT_R32G32B32A32_UINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32B32A32_SINT == (uint32_t)VK_FORMAT_R32G32B32A32_SINT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT == (uint32_t)VK_FORMAT_R32G32B32A32_SFLOAT, "SpvReflect/Vk enum mismatch"); // SpvReflectDescriptorType == VkDescriptorType static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER == (uint32_t)VK_DESCRIPTOR_TYPE_SAMPLER, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == (uint32_t)VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE == (uint32_t)VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE == (uint32_t)VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == (uint32_t)VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER == (uint32_t)VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER == (uint32_t)VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER == (uint32_t)VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC == (uint32_t)VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC == (uint32_t)VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT == (uint32_t)VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, "SpvReflect/Vk enum mismatch"); // SpvReflectShaderStageFlagBits == VkShaderStageFlagBits static_assert((uint32_t)SPV_REFLECT_SHADER_STAGE_VERTEX_BIT == (uint32_t)VK_SHADER_STAGE_VERTEX_BIT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_SHADER_STAGE_TESSELLATION_CONTROL_BIT == (uint32_t)VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_SHADER_STAGE_TESSELLATION_EVALUATION_BIT == (uint32_t)VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT == (uint32_t)VK_SHADER_STAGE_GEOMETRY_BIT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT == (uint32_t)VK_SHADER_STAGE_FRAGMENT_BIT, "SpvReflect/Vk enum mismatch"); static_assert((uint32_t)SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT == (uint32_t)VK_SHADER_STAGE_COMPUTE_BIT, "SpvReflect/Vk enum mismatch"); #endif // defined(SPIRV_REFLECT_HAS_VULKAN_H) // clang-format on TEST(SpirvReflectTestCase, SourceLanguage) { EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageESSL), "ESSL"); EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageGLSL), "GLSL"); EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageOpenCL_C), "OpenCL_C"); EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageOpenCL_CPP), "OpenCL_CPP"); EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageHLSL), "HLSL"); EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageUnknown), "Unknown"); // Invalid inputs EXPECT_STREQ(spvReflectSourceLanguage(SpvSourceLanguageMax), "Unknown"); EXPECT_STREQ(spvReflectSourceLanguage( static_cast(SpvSourceLanguageMax - 1)), "Unknown"); } class SpirvReflectTest : public ::testing::TestWithParam { public: // optional: initialize static data to be shared by all tests in this test // case. Note that for parameterized tests, the specific parameter value is a // non-static member data static void SetUpTestCase() { FILE* f = fopen("tests/glsl/built_in_format.spv", "r"); if (!f) { posix_chdir(".."); f = fopen("tests/glsl/built_in_format.spv", "r"); } EXPECT_NE(f, nullptr) << "Couldn't find test shaders!"; if (f) { fclose(f); } } static void TearDownTestCase() {} protected: SpirvReflectTest() { // set-up work for each test } ~SpirvReflectTest() override { // clean-up work that doesn't throw exceptions } // optional: called after constructor & before destructor, respectively. // Used if you have initialization steps that can throw exceptions or must // otherwise be deferred. void SetUp() override { // called after constructor before each test spirv_path_ = GetParam(); std::ifstream spirv_file(spirv_path_, std::ios::binary | std::ios::ate); std::streampos spirv_file_nbytes = spirv_file.tellg(); spirv_file.seekg(0); spirv_.resize(spirv_file_nbytes); spirv_file.read(reinterpret_cast(spirv_.data()), spirv_.size()); SpvReflectResult result = spvReflectCreateShaderModule(spirv_.size(), spirv_.data(), &module_); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result) << "spvReflectCreateShaderModule() failed"; } // optional: void TearDown() override { // called before destructor after all tests spvReflectDestroyShaderModule(&module_); } // members here will be accessible in all tests in this test case std::string spirv_path_; std::vector spirv_; SpvReflectShaderModule module_; // static members will be accessible to all tests in this test case static std::string test_shaders_dir; }; TEST_P(SpirvReflectTest, GetCodeSize) { EXPECT_EQ(spvReflectGetCodeSize(&module_), spirv_.size()); } TEST(SpirvReflectTestCase, GetCodeSize_Errors) { // NULL module EXPECT_EQ(spvReflectGetCodeSize(nullptr), 0); } TEST_P(SpirvReflectTest, GetCode) { int code_compare = memcmp(spvReflectGetCode(&module_), spirv_.data(), spirv_.size()); EXPECT_EQ(code_compare, 0); } TEST(SpirvReflectTestCase, GetCode_Errors) { // NULL module EXPECT_EQ(spvReflectGetCode(nullptr), nullptr); } TEST_P(SpirvReflectTest, GetDescriptorBinding) { uint32_t binding_count = 0; SpvReflectResult result; result = spvReflectEnumerateDescriptorBindings(&module_, &binding_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector bindings(binding_count); result = spvReflectEnumerateDescriptorBindings(&module_, &binding_count, bindings.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (const auto* db : bindings) { const SpvReflectDescriptorBinding* also_db = spvReflectGetDescriptorBinding(&module_, db->binding, db->set, &result); EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(db, also_db); } } TEST_P(SpirvReflectTest, EnumerateDescriptorBindings_Errors) { uint32_t binding_count = 0; // NULL module EXPECT_EQ( spvReflectEnumerateDescriptorBindings(nullptr, &binding_count, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL binding count EXPECT_EQ(spvReflectEnumerateDescriptorBindings(&module_, nullptr, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // binding count / module binding count mismatch EXPECT_EQ( spvReflectEnumerateDescriptorBindings(&module_, &binding_count, nullptr), SPV_REFLECT_RESULT_SUCCESS); uint32_t bad_binding_count = binding_count + 1; std::vector bindings(bad_binding_count); EXPECT_EQ(spvReflectEnumerateDescriptorBindings(&module_, &bad_binding_count, bindings.data()), SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH); } TEST_P(SpirvReflectTest, GetDescriptorBinding_Errors) { SpvReflectResult result; // NULL module EXPECT_EQ(spvReflectGetDescriptorBinding(nullptr, 0, 0, nullptr), nullptr); EXPECT_EQ(spvReflectGetDescriptorBinding(nullptr, 0, 0, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // Invalid binding number EXPECT_EQ(spvReflectGetDescriptorBinding(&module_, 0xdeadbeef, 0, nullptr), nullptr); EXPECT_EQ(spvReflectGetDescriptorBinding(&module_, 0xdeadbeef, 0, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // Invalid set number EXPECT_EQ(spvReflectGetDescriptorBinding(&module_, 0, 0xdeadbeef, nullptr), nullptr); EXPECT_EQ(spvReflectGetDescriptorBinding(&module_, 0, 0xdeadbeef, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, GetDescriptorSet) { uint32_t set_count = 0; SpvReflectResult result; result = spvReflectEnumerateDescriptorSets(&module_, &set_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector sets(set_count); result = spvReflectEnumerateDescriptorSets(&module_, &set_count, sets.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (const auto* ds : sets) { const SpvReflectDescriptorSet* also_ds = spvReflectGetDescriptorSet(&module_, ds->set, &result); EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(ds, also_ds); } } TEST_P(SpirvReflectTest, EnumerateDescriptorSets_Errors) { uint32_t set_count = 0; // NULL module EXPECT_EQ(spvReflectEnumerateDescriptorSets(nullptr, &set_count, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL set count EXPECT_EQ(spvReflectEnumerateDescriptorSets(&module_, nullptr, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // set count / module set count mismatch EXPECT_EQ(spvReflectEnumerateDescriptorSets(&module_, &set_count, nullptr), SPV_REFLECT_RESULT_SUCCESS); uint32_t bad_set_count = set_count + 1; std::vector sets(bad_set_count); EXPECT_EQ( spvReflectEnumerateDescriptorSets(&module_, &bad_set_count, sets.data()), SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH); } TEST_P(SpirvReflectTest, GetDescriptorSet_Errors) { SpvReflectResult result; // NULL module EXPECT_EQ(spvReflectGetDescriptorSet(nullptr, 0, nullptr), nullptr); EXPECT_EQ(spvReflectGetDescriptorSet(nullptr, 0, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // Invalid set number EXPECT_EQ(spvReflectGetDescriptorSet(&module_, 0xdeadbeef, nullptr), nullptr); EXPECT_EQ(spvReflectGetDescriptorSet(&module_, 0xdeadbeef, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, GetInputVariableByLocation) { uint32_t iv_count = 0; SpvReflectResult result; result = spvReflectEnumerateInputVariables(&module_, &iv_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector ivars(iv_count); result = spvReflectEnumerateInputVariables(&module_, &iv_count, ivars.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (const auto* iv : ivars) { const SpvReflectInterfaceVariable* also_iv = spvReflectGetInputVariableByLocation(&module_, iv->location, &result); if (iv->location == UINT32_MAX) { // Not all elements have valid locations. EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); EXPECT_EQ(also_iv, nullptr); } else { EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(iv, also_iv); } } } TEST_P(SpirvReflectTest, EnumerateInputVariables_Errors) { uint32_t var_count = 0; // NULL module EXPECT_EQ(spvReflectEnumerateInputVariables(nullptr, &var_count, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL var count EXPECT_EQ(spvReflectEnumerateInputVariables(&module_, nullptr, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // var count / module var count mismatch EXPECT_EQ(spvReflectEnumerateInputVariables(&module_, &var_count, nullptr), SPV_REFLECT_RESULT_SUCCESS); uint32_t bad_var_count = var_count + 1; std::vector vars(bad_var_count); EXPECT_EQ( spvReflectEnumerateInputVariables(&module_, &bad_var_count, vars.data()), SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH); } TEST_P(SpirvReflectTest, GetInputVariableByLocation_Errors) { SpvReflectResult result; // NULL module EXPECT_EQ(spvReflectGetInputVariableByLocation(nullptr, 0, nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableByLocation(nullptr, 0, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // explicitly invalid location (UINT32_MAX is *always* not found) EXPECT_EQ(spvReflectGetInputVariableByLocation(&module_, UINT32_MAX, nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableByLocation(&module_, UINT32_MAX, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // implicitly invalid location (0xdeadbeef is potentially valid) EXPECT_EQ(spvReflectGetInputVariableByLocation(&module_, 0xdeadbeef, nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableByLocation(&module_, 0xdeadbeef, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, GetInputVariableBySemantic) { uint32_t iv_count = 0; SpvReflectResult result; result = spvReflectEnumerateInputVariables(&module_, &iv_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector ivars(iv_count); result = spvReflectEnumerateInputVariables(&module_, &iv_count, ivars.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (const auto* iv : ivars) { const SpvReflectInterfaceVariable* also_iv = spvReflectGetInputVariableBySemantic(&module_, iv->semantic, &result); if (iv->semantic == nullptr) { // Not all elements have valid semantics EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); EXPECT_EQ(also_iv, nullptr); } else if (iv->semantic[0] == '\0') { // Not all elements have valid semantics EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); EXPECT_EQ(also_iv, nullptr); } else { EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(iv, also_iv); } } } TEST_P(SpirvReflectTest, GetInputVariableBySemantic_Errors) { SpvReflectResult result; // NULL module EXPECT_EQ(spvReflectGetInputVariableBySemantic(nullptr, nullptr, nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableBySemantic(nullptr, nullptr, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL semantic EXPECT_EQ(spvReflectGetInputVariableBySemantic(&module_, nullptr, nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableBySemantic(&module_, nullptr, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // empty semantic ("" is explicitly not found) EXPECT_EQ(spvReflectGetInputVariableBySemantic(&module_, "", nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableBySemantic(&module_, "", &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // implicitly invalid semantic EXPECT_EQ(spvReflectGetInputVariableBySemantic( &module_, "SV_PLAUSIBLE_BUT_INVALID", nullptr), nullptr); EXPECT_EQ(spvReflectGetInputVariableBySemantic( &module_, "SV_PLAUSIBLE_BUT_INVALID", &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, GetOutputVariableByLocation) { uint32_t ov_count = 0; SpvReflectResult result; result = spvReflectEnumerateOutputVariables(&module_, &ov_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector ovars(ov_count); result = spvReflectEnumerateOutputVariables(&module_, &ov_count, ovars.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (const auto* ov : ovars) { const SpvReflectInterfaceVariable* also_ov = spvReflectGetOutputVariableByLocation(&module_, ov->location, &result); if (ov->location == UINT32_MAX) { // Not all elements have valid locations. EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); EXPECT_EQ(also_ov, nullptr); } else { EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(ov, also_ov); } } } TEST_P(SpirvReflectTest, EnumerateOutputVariables_Errors) { uint32_t var_count = 0; // NULL module EXPECT_EQ(spvReflectEnumerateOutputVariables(nullptr, &var_count, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL var count EXPECT_EQ(spvReflectEnumerateOutputVariables(&module_, nullptr, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // var count / module var count mismatch EXPECT_EQ(spvReflectEnumerateOutputVariables(&module_, &var_count, nullptr), SPV_REFLECT_RESULT_SUCCESS); uint32_t bad_var_count = var_count + 1; std::vector vars(bad_var_count); EXPECT_EQ( spvReflectEnumerateOutputVariables(&module_, &bad_var_count, vars.data()), SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH); } TEST_P(SpirvReflectTest, GetOutputVariableByLocation_Errors) { SpvReflectResult result; // NULL module EXPECT_EQ(spvReflectGetOutputVariableByLocation(nullptr, 0, nullptr), nullptr); EXPECT_EQ(spvReflectGetOutputVariableByLocation(nullptr, 0, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // explicitly invalid location (UINT32_MAX is *always* not found) EXPECT_EQ( spvReflectGetOutputVariableByLocation(&module_, UINT32_MAX, nullptr), nullptr); EXPECT_EQ( spvReflectGetOutputVariableByLocation(&module_, UINT32_MAX, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // implicitly invalid location (0xdeadbeef is potentially valid) EXPECT_EQ( spvReflectGetOutputVariableByLocation(&module_, 0xdeadbeef, nullptr), nullptr); EXPECT_EQ( spvReflectGetOutputVariableByLocation(&module_, 0xdeadbeef, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, GetOutputVariableBySemantic) { uint32_t ov_count = 0; SpvReflectResult result; result = spvReflectEnumerateOutputVariables(&module_, &ov_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector ovars(ov_count); result = spvReflectEnumerateOutputVariables(&module_, &ov_count, ovars.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (const auto* ov : ovars) { const SpvReflectInterfaceVariable* also_ov = spvReflectGetOutputVariableBySemantic(&module_, ov->semantic, &result); if (ov->semantic == nullptr) { // Not all elements have valid semantics EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); EXPECT_EQ(also_ov, nullptr); } else if (ov->semantic[0] == '\0') { // Not all elements have valid semantics EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); EXPECT_EQ(also_ov, nullptr); } else { EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(ov, also_ov); } } } TEST_P(SpirvReflectTest, GetOutputVariableBySemantic_Errors) { SpvReflectResult result; // NULL module EXPECT_EQ(spvReflectGetOutputVariableBySemantic(nullptr, nullptr, nullptr), nullptr); EXPECT_EQ(spvReflectGetOutputVariableBySemantic(nullptr, nullptr, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL semantic EXPECT_EQ(spvReflectGetOutputVariableBySemantic(&module_, nullptr, nullptr), nullptr); EXPECT_EQ(spvReflectGetOutputVariableBySemantic(&module_, nullptr, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // empty semantic ("" is explicitly not found) EXPECT_EQ(spvReflectGetOutputVariableBySemantic(&module_, "", nullptr), nullptr); EXPECT_EQ(spvReflectGetOutputVariableBySemantic(&module_, "", &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // implicitly invalid semantic EXPECT_EQ(spvReflectGetOutputVariableBySemantic( &module_, "SV_PLAUSIBLE_BUT_INVALID", nullptr), nullptr); EXPECT_EQ(spvReflectGetOutputVariableBySemantic( &module_, "SV_PLAUSIBLE_BUT_INVALID", &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, GetPushConstantBlock) { uint32_t block_count = 0; SpvReflectResult result; result = spvReflectEnumeratePushConstantBlocks(&module_, &block_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); std::vector blocks(block_count); result = spvReflectEnumeratePushConstantBlocks(&module_, &block_count, blocks.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); for (uint32_t i = 0; i < block_count; ++i) { const SpvReflectBlockVariable* b = blocks[i]; const SpvReflectBlockVariable* also_b = spvReflectGetPushConstantBlock(&module_, i, &result); EXPECT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(b, also_b); } } TEST_P(SpirvReflectTest, EnumeratePushConstantBlocks_Errors) { uint32_t block_count = 0; // NULL module EXPECT_EQ( spvReflectEnumeratePushConstantBlocks(nullptr, &block_count, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL var count EXPECT_EQ(spvReflectEnumeratePushConstantBlocks(&module_, nullptr, nullptr), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // var count / module var count mismatch EXPECT_EQ( spvReflectEnumeratePushConstantBlocks(&module_, &block_count, nullptr), SPV_REFLECT_RESULT_SUCCESS); uint32_t bad_block_count = block_count + 1; std::vector blocks(bad_block_count); EXPECT_EQ(spvReflectEnumeratePushConstantBlocks(&module_, &bad_block_count, blocks.data()), SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH); } TEST_P(SpirvReflectTest, GetPushConstantBlock_Errors) { uint32_t block_count = 0; SpvReflectResult result; result = spvReflectEnumeratePushConstantBlocks(&module_, &block_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); // NULL module EXPECT_EQ(spvReflectGetPushConstantBlock(nullptr, 0, nullptr), nullptr); EXPECT_EQ(spvReflectGetPushConstantBlock(nullptr, 0, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // invalid block index EXPECT_EQ(spvReflectGetPushConstantBlock(&module_, block_count, nullptr), nullptr); EXPECT_EQ(spvReflectGetPushConstantBlock(&module_, block_count, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); } TEST_P(SpirvReflectTest, ChangeDescriptorBindingNumber) { uint32_t binding_count = 0; SpvReflectResult result; result = spvReflectEnumerateDescriptorBindings(&module_, &binding_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); if (binding_count == 0) { return; // can't change binding numbers if there are no bindings! } std::vector bindings(binding_count); result = spvReflectEnumerateDescriptorBindings(&module_, &binding_count, bindings.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); uint32_t set_count = 0; result = spvReflectEnumerateDescriptorSets(&module_, &set_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_GT(set_count, 0U); std::vector sets(set_count); result = spvReflectEnumerateDescriptorSets(&module_, &set_count, sets.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); SpvReflectDescriptorBinding* b = bindings[0]; const uint32_t new_binding_number = 1000; const uint32_t set_number = b->set; // Make sure no binding exists at the binding number we're about to change to. ASSERT_EQ(spvReflectGetDescriptorBinding(&module_, new_binding_number, set_number, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // Modify the binding number (leaving the set number unchanged) result = spvReflectChangeDescriptorBindingNumbers( &module_, b, new_binding_number, SPV_REFLECT_SET_NUMBER_DONT_CHANGE); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); // We should now be able to retrieve the binding at the new number const SpvReflectDescriptorBinding* new_binding = spvReflectGetDescriptorBinding(&module_, new_binding_number, set_number, &result); ASSERT_NE(new_binding, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(new_binding->binding, new_binding_number); EXPECT_EQ(new_binding->set, set_number); // The set count & sets contents should not have changed, since we didn't // change the set number. result = spvReflectEnumerateDescriptorSets(&module_, &set_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(set_count, sets.size()); std::vector new_sets(set_count); result = spvReflectEnumerateDescriptorSets(&module_, &set_count, new_sets.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(sets, new_sets); // TODO: confirm that the modified SPIR-V code is still valid, either by // running spirv-val or calling vkCreateShaderModule(). } TEST_P(SpirvReflectTest, ChangeDescriptorBindingNumbers_Errors) { // NULL module EXPECT_EQ(spvReflectChangeDescriptorBindingNumbers(nullptr, nullptr, 0, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL descriptor binding EXPECT_EQ(spvReflectChangeDescriptorBindingNumbers(&module_, nullptr, 0, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); } TEST_P(SpirvReflectTest, ChangeDescriptorSetNumber) { uint32_t binding_count = 0; SpvReflectResult result; result = spvReflectEnumerateDescriptorBindings(&module_, &binding_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); if (binding_count == 0) { return; // can't change set numbers if there are no bindings! } std::vector bindings(binding_count); result = spvReflectEnumerateDescriptorBindings(&module_, &binding_count, bindings.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); uint32_t set_count = 0; result = spvReflectEnumerateDescriptorSets(&module_, &set_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_GT(set_count, 0U); std::vector sets(set_count); result = spvReflectEnumerateDescriptorSets(&module_, &set_count, sets.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); SpvReflectDescriptorSet* s = sets[0]; const uint32_t new_set_number = 13; const uint32_t set_binding_count = s->binding_count; // Make sure no set exists at the binding number we're about to change to. ASSERT_EQ(spvReflectGetDescriptorSet(&module_, new_set_number, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // Modify the set number result = spvReflectChangeDescriptorSetNumber(&module_, s, new_set_number); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); // We should now be able to retrieve the set at the new number const SpvReflectDescriptorSet* new_set = spvReflectGetDescriptorSet(&module_, new_set_number, &result); ASSERT_NE(new_set, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(new_set->set, new_set_number); EXPECT_EQ(new_set->binding_count, set_binding_count); // The set count should not have changed, since we didn't // change the set number. result = spvReflectEnumerateDescriptorSets(&module_, &set_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(set_count, sets.size()); // TODO: confirm that the modified SPIR-V code is still valid, either by // running spirv-val or calling vkCreateShaderModule(). } TEST_P(SpirvReflectTest, ChangeDescriptorSetNumber_Errors) { // NULL module EXPECT_EQ(spvReflectChangeDescriptorSetNumber(nullptr, nullptr, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL descriptor set EXPECT_EQ(spvReflectChangeDescriptorSetNumber(&module_, nullptr, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); } TEST_P(SpirvReflectTest, ChangeInputVariableLocation) { uint32_t iv_count = 0; SpvReflectResult result; result = spvReflectEnumerateInputVariables(&module_, &iv_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); if (iv_count == 0) { return; // can't change variable locations if there are no variables! } std::vector ivars(iv_count); result = spvReflectEnumerateInputVariables(&module_, &iv_count, ivars.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); SpvReflectInterfaceVariable* iv = ivars[0]; const uint32_t new_location = 37; // Make sure no var exists at the location we're about to change to. ASSERT_EQ( spvReflectGetInputVariableByLocation(&module_, new_location, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // Modify the location result = spvReflectChangeInputVariableLocation(&module_, iv, new_location); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); // We should now be able to retrieve the variable at its new location const SpvReflectInterfaceVariable* new_iv = spvReflectGetInputVariableByLocation(&module_, new_location, &result); ASSERT_NE(new_iv, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(new_iv->location, new_location); // The variable count should not have changed result = spvReflectEnumerateInputVariables(&module_, &iv_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(iv_count, ivars.size()); // TODO: confirm that the modified SPIR-V code is still valid, either by // running spirv-val or calling vkCreateShaderModule(). } TEST_P(SpirvReflectTest, ChangeInputVariableLocation_Errors) { // NULL module EXPECT_EQ(spvReflectChangeInputVariableLocation(nullptr, nullptr, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL variable EXPECT_EQ(spvReflectChangeInputVariableLocation(&module_, nullptr, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); } TEST_P(SpirvReflectTest, ChangeOutputVariableLocation) { uint32_t ov_count = 0; SpvReflectResult result; result = spvReflectEnumerateOutputVariables(&module_, &ov_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); if (ov_count == 0) { return; // can't change variable locations if there are no variables! } std::vector ovars(ov_count); result = spvReflectEnumerateOutputVariables(&module_, &ov_count, ovars.data()); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); SpvReflectInterfaceVariable* ov = ovars[0]; const uint32_t new_location = 37; // Make sure no var exists at the location we're about to change to. ASSERT_EQ( spvReflectGetOutputVariableByLocation(&module_, new_location, &result), nullptr); EXPECT_EQ(result, SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND); // Modify the location result = spvReflectChangeOutputVariableLocation(&module_, ov, new_location); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); // We should now be able to retrieve the variable at its new location const SpvReflectInterfaceVariable* new_ov = spvReflectGetOutputVariableByLocation(&module_, new_location, &result); ASSERT_NE(new_ov, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(new_ov->location, new_location); // The variable count should not have changed result = spvReflectEnumerateOutputVariables(&module_, &ov_count, nullptr); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); EXPECT_EQ(ov_count, ovars.size()); // TODO: confirm that the modified SPIR-V code is still valid, either by // running spirv-val or calling vkCreateShaderModule(). } TEST_P(SpirvReflectTest, ChangeOutputVariableLocation_Errors) { // NULL module EXPECT_EQ(spvReflectChangeOutputVariableLocation(nullptr, nullptr, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); // NULL variable EXPECT_EQ(spvReflectChangeOutputVariableLocation(&module_, nullptr, 0), SPV_REFLECT_RESULT_ERROR_NULL_POINTER); } TEST_P(SpirvReflectTest, CheckYamlOutput) { const uint32_t yaml_verbosity = 1; SpvReflectToYaml yamlizer(module_, yaml_verbosity); std::stringstream test_yaml; test_yaml << yamlizer; std::string test_yaml_str = test_yaml.str(); std::string golden_yaml_path = spirv_path_ + ".yaml"; std::ifstream golden_yaml_file(golden_yaml_path); ASSERT_TRUE(golden_yaml_file.good()); std::stringstream golden_yaml; golden_yaml << golden_yaml_file.rdbuf(); golden_yaml_file.close(); std::string golden_yaml_str = golden_yaml.str(); // Quick workaround for potential line ending differences, perf be damned! test_yaml_str = std::regex_replace(test_yaml_str, std::regex("\r"), ""); golden_yaml_str = std::regex_replace(golden_yaml_str, std::regex("\r"), ""); ASSERT_EQ(test_yaml_str.size(), golden_yaml_str.size()); // TODO: I wish there were a better way to show what changed, but the // differences (if any) tend to be pretty large, even for small changes. bool yaml_contents_match = (test_yaml_str == golden_yaml_str); EXPECT_TRUE(yaml_contents_match) << "YAML output mismatch; try regenerating the golden YAML with " "\"tests/build_golden_yaml.py\" and see what changed."; } namespace { // TODO - have this glob search all .spv files const std::vector all_spirv_paths = { // clang-format off "../tests/16bit/vert_in_out_16.spv", "../tests/access_chains/array_length_from_access_chain.spv", "../tests/access_chains/pointer_access_chain_phy_storage_buffer.spv", "../tests/cbuffer_unused/cbuffer_unused_001.spv", "../tests/glsl/built_in_format.spv", "../tests/entry_exec_mode/comp_local_size.spv", "../tests/entry_exec_mode/geom_inv_out_vert.spv", "../tests/execution_mode/local_size_id_spec.spv", "../tests/execution_mode/local_size_id.spv", "../tests/glsl/buffer_handle_0.spv", "../tests/glsl/buffer_handle_1.spv", "../tests/glsl/buffer_handle_2.spv", "../tests/glsl/buffer_handle_3.spv", "../tests/glsl/buffer_handle_4.spv", "../tests/glsl/buffer_handle_5.spv", "../tests/glsl/buffer_handle_6.spv", "../tests/glsl/buffer_handle_7.spv", "../tests/glsl/buffer_handle_8.spv", "../tests/glsl/buffer_handle_9.spv", "../tests/glsl/buffer_handle_uvec2_pc.spv", "../tests/glsl/buffer_handle_uvec2_ssbo.spv", "../tests/glsl/buffer_pointer.spv", "../tests/glsl/built_in_format.spv", "../tests/glsl/fn_struct_param.spv", "../tests/glsl/frag_array_input.spv", "../tests/glsl/frag_barycentric.spv", "../tests/glsl/input_attachment.spv", "../tests/glsl/io_vars_vs.spv", "../tests/glsl/matrix_major_order_glsl.spv", "../tests/glsl/non_writable_image.spv", "../tests/glsl/readonly_writeonly.spv", "../tests/glsl/runtime_array_of_array_of_struct.spv", "../tests/glsl/storage_buffer.spv", "../tests/glsl/texel_buffer.spv", "../tests/hlsl/append_consume.spv", "../tests/hlsl/array_of_structured_buffer.spv", "../tests/hlsl/binding_array.spv", "../tests/hlsl/binding_types.spv", "../tests/hlsl/cbuffer.spv", "../tests/hlsl/constantbuffer.spv", "../tests/hlsl/constantbuffer_nested_structs.spv", "../tests/hlsl/counter_buffers.spv", "../tests/hlsl/localsize.spv", "../tests/hlsl/matrix_major_order_hlsl.spv", "../tests/hlsl/pushconstant.spv", "../tests/hlsl/semantics.spv", "../tests/hlsl/structuredbuffer.spv", "../tests/hlsl/user_type.spv", "../tests/interface/geom_input_builtin_array.spv", "../tests/interface/vertex_input_builtin_block.spv", "../tests/interface/vertex_input_builtin_non_block.spv", "../tests/issues/77/hlsl/array_from_ubo.spv", "../tests/issues/77/hlsl/array_from_ubo_with_O0.spv", "../tests/issues/77/hlsl/rocketz.spv", "../tests/issues/102/function_parameter_access.spv", "../tests/issues/178/vertex_input_struct.spv", "../tests/issues/178/vertex_input_struct2.spv", "../tests/issues/227/null_node.spv", "../tests/mesh_shader_ext/mesh_shader_ext.task.hlsl.spv", "../tests/mesh_shader_ext/mesh_shader_ext.mesh.hlsl.spv", "../tests/multi_entrypoint/multi_entrypoint.spv", "../tests/push_constants/non_zero_block_offset.spv", "../tests/push_constants/pointer_in_struct.spv", "../tests/push_constants/pointer_in_struct_2.spv", "../tests/push_constants/push_constant_basic_0.spv", "../tests/push_constants/push_constant_basic_1.spv", "../tests/push_constants/push_constant_basic_2.spv", "../tests/push_constants/push_constant_basic_3.spv", "../tests/raytrace/rayquery_equal.cs.spv", "../tests/raytrace/rayquery_init_ds.spv", "../tests/raytrace/rayquery_init_gs.spv", "../tests/raytrace/rayquery_init_hs.spv", "../tests/raytrace/rayquery_init_ps.spv", "../tests/raytrace/rayquery_init_rahit.spv", "../tests/raytrace/rayquery_init_rcall.spv", "../tests/raytrace/rayquery_init_rchit.spv", "../tests/raytrace/rayquery_init_rgen.spv", "../tests/raytrace/rayquery_init_rmiss.spv", "../tests/raytrace/raytracing.acceleration-structure.spv", "../tests/raytrace/raytracing.khr.closesthit.spv", "../tests/raytrace/raytracing.nv.acceleration-structure.spv", "../tests/raytrace/raytracing.nv.anyhit.spv", "../tests/raytrace/raytracing.nv.callable.spv", "../tests/raytrace/raytracing.nv.closesthit.spv", "../tests/raytrace/raytracing.nv.enum.spv", "../tests/raytrace/raytracing.nv.intersection.spv", "../tests/raytrace/raytracing.nv.library.spv", "../tests/raytrace/raytracing.nv.miss.spv", "../tests/raytrace/raytracing.nv.raygen.spv", "../tests/spec_constants/basic.spv", "../tests/spec_constants/convert.spv", "../tests/spec_constants/local_size_id_10.spv", "../tests/spec_constants/local_size_id_13.spv", "../tests/spec_constants/ssbo_array.spv", "../tests/spec_constants/test_32bit.spv", "../tests/spec_constants/test_64bit.spv", "../tests/spirv15/VertexShader.spv", "../tests/user_type/byte_address_buffer_0.spv", "../tests/user_type/byte_address_buffer_1.spv", "../tests/user_type/byte_address_buffer_2.spv", "../tests/user_type/byte_address_buffer_3.spv", "../tests/user_type/rw_byte_address_buffer.spv", "../tests/variable_access/atomics_0.spv", "../tests/variable_access/atomics_1.spv", "../tests/variable_access/copy_array_0.spv", "../tests/variable_access/copy_array_1.spv", "../tests/variable_access/copy_array_2.spv", "../tests/variable_access/copy_array_3.spv", "../tests/variable_access/copy_memory.spv", "../tests/variable_access/copy_struct_0.spv", "../tests/variable_access/copy_struct_1.spv", "../tests/variable_access/descriptor_indexing_0.spv", "../tests/variable_access/descriptor_indexing_1.spv", "../tests/variable_access/phy_storage_buffer_used_0.spv", "../tests/variable_access/phy_storage_buffer_used_1.spv", "../tests/variable_access/phy_storage_buffer_used_2.spv", "../tests/variable_access/phy_storage_buffer_used_3.spv", // clang-format on }; } // namespace INSTANTIATE_TEST_CASE_P(ForAllShaders, SpirvReflectTest, ::testing::ValuesIn(all_spirv_paths)); TEST(SpirvReflectTestCase, TestComputeLocalSize) { std::vector spirv_; SpvReflectShaderModule module_; const std::string spirv_path = "../tests/entry_exec_mode/comp_local_size.spv"; std::ifstream spirv_file(spirv_path, std::ios::binary | std::ios::ate); std::streampos spirv_file_nbytes = spirv_file.tellg(); spirv_file.seekg(0); spirv_.resize(spirv_file_nbytes); spirv_file.read(reinterpret_cast(spirv_.data()), spirv_.size()); SpvReflectResult result = spvReflectCreateShaderModule(spirv_.size(), spirv_.data(), &module_); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result) << "spvReflectCreateShaderModule() failed"; ASSERT_EQ(module_.entry_point_count, 1); ASSERT_EQ(module_.entry_points[0].shader_stage, SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT); ASSERT_EQ(module_.entry_points[0].local_size.x, 1); ASSERT_EQ(module_.entry_points[0].local_size.y, 1); ASSERT_EQ(module_.entry_points[0].local_size.z, 1); spvReflectDestroyShaderModule(&module_); } TEST(SpirvReflectTestCase, TestTaskShaderEXT) { std::vector spirv_; SpvReflectShaderModule module_; const std::string spirv_path = "../tests/mesh_shader_ext/mesh_shader_ext.task.hlsl.spv"; std::ifstream spirv_file(spirv_path, std::ios::binary | std::ios::ate); std::streampos spirv_file_nbytes = spirv_file.tellg(); spirv_file.seekg(0); spirv_.resize(spirv_file_nbytes); spirv_file.read(reinterpret_cast(spirv_.data()), spirv_.size()); SpvReflectResult result = spvReflectCreateShaderModule(spirv_.size(), spirv_.data(), &module_); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result) << "spvReflectCreateShaderModule() failed"; ASSERT_EQ(module_.entry_point_count, 1); ASSERT_EQ(module_.entry_points[0].shader_stage, SPV_REFLECT_SHADER_STAGE_TASK_BIT_EXT); spvReflectDestroyShaderModule(&module_); } TEST(SpirvReflectTestCase, TestMeshShaderEXT) { std::vector spirv_; SpvReflectShaderModule module_; const std::string spirv_path = "../tests/mesh_shader_ext/mesh_shader_ext.mesh.hlsl.spv"; std::ifstream spirv_file(spirv_path, std::ios::binary | std::ios::ate); std::streampos spirv_file_nbytes = spirv_file.tellg(); spirv_file.seekg(0); spirv_.resize(spirv_file_nbytes); spirv_file.read(reinterpret_cast(spirv_.data()), spirv_.size()); SpvReflectResult result = spvReflectCreateShaderModule(spirv_.size(), spirv_.data(), &module_); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result) << "spvReflectCreateShaderModule() failed"; ASSERT_EQ(module_.entry_point_count, 1); ASSERT_EQ(module_.entry_points[0].shader_stage, SPV_REFLECT_SHADER_STAGE_MESH_BIT_EXT); spvReflectDestroyShaderModule(&module_); } TEST(SpirvReflectTestCase, TestGeometryInvocationsOutputVertices) { std::vector spirv_; SpvReflectShaderModule module_; const std::string spirv_path = "../tests/entry_exec_mode/geom_inv_out_vert.spv"; std::ifstream spirv_file(spirv_path, std::ios::binary | std::ios::ate); std::streampos spirv_file_nbytes = spirv_file.tellg(); spirv_file.seekg(0); spirv_.resize(spirv_file_nbytes); spirv_file.read(reinterpret_cast(spirv_.data()), spirv_.size()); SpvReflectResult result = spvReflectCreateShaderModule(spirv_.size(), spirv_.data(), &module_); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result) << "spvReflectCreateShaderModule() failed"; ASSERT_EQ(module_.entry_point_count, 1); ASSERT_EQ(module_.entry_points[0].shader_stage, SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT); ASSERT_EQ(module_.entry_points[0].invocations, 2); ASSERT_EQ(module_.entry_points[0].output_vertices, 2); spvReflectDestroyShaderModule(&module_); } class SpirvReflectMultiEntryPointTest : public ::testing::TestWithParam { protected: void SetUp() override { // called after constructor before each test const std::string spirv_path = "../tests/multi_entrypoint/multi_entrypoint.spv"; std::ifstream spirv_file(spirv_path, std::ios::binary | std::ios::ate); std::streampos spirv_file_nbytes = spirv_file.tellg(); spirv_file.seekg(0); spirv_.resize(spirv_file_nbytes); spirv_file.read(reinterpret_cast(spirv_.data()), spirv_.size()); SpvReflectResult result = spvReflectCreateShaderModule(spirv_.size(), spirv_.data(), &module_); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result) << "spvReflectCreateShaderModule() failed"; } // optional: void TearDown() override { // called before destructor after all tests spvReflectDestroyShaderModule(&module_); } const char* eps_[2] = {"entry_vert", "entry_frag"}; std::vector spirv_; SpvReflectShaderModule module_; }; TEST_F(SpirvReflectMultiEntryPointTest, GetEntryPoint) { ASSERT_EQ(&module_.entry_points[0], spvReflectGetEntryPoint(&module_, eps_[0])); ASSERT_EQ(&module_.entry_points[1], spvReflectGetEntryPoint(&module_, eps_[1])); ASSERT_EQ(NULL, spvReflectGetEntryPoint(&module_, "entry_tess")); } TEST_F(SpirvReflectMultiEntryPointTest, GetDescriptorBindings0) { uint32_t binding_count = 0; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorBindings( &module_, eps_[0], &binding_count, NULL)); ASSERT_EQ(binding_count, 1); SpvReflectDescriptorBinding* binding; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorBindings( &module_, eps_[0], &binding_count, &binding)); ASSERT_EQ(binding->set, 0); ASSERT_EQ(binding->binding, 1); ASSERT_EQ(strcmp(binding->name, "ubo"), 0); ASSERT_EQ(binding->descriptor_type, SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER); SpvReflectResult result; ASSERT_EQ(binding, spvReflectGetEntryPointDescriptorBinding( &module_, eps_[0], binding->binding, binding->set, &result)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); } TEST_F(SpirvReflectMultiEntryPointTest, GetDescriptorBindings1) { uint32_t binding_count = 0; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorBindings( &module_, eps_[1], &binding_count, NULL)); ASSERT_EQ(binding_count, 2); SpvReflectDescriptorBinding* bindings[2]; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorBindings( &module_, eps_[1], &binding_count, bindings)); ASSERT_EQ(bindings[0]->set, 0); ASSERT_EQ(bindings[0]->binding, 0); ASSERT_EQ(strcmp(bindings[0]->name, "tex"), 0); ASSERT_EQ(bindings[0]->descriptor_type, SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); ASSERT_EQ(bindings[1]->set, 0); ASSERT_EQ(bindings[1]->binding, 1); ASSERT_EQ(strcmp(bindings[1]->name, "ubo"), 0); ASSERT_EQ(bindings[1]->descriptor_type, SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER); for (size_t i = 0; i < 2; ++i) { SpvReflectResult result; ASSERT_EQ(bindings[i], spvReflectGetEntryPointDescriptorBinding( &module_, eps_[1], bindings[i]->binding, bindings[i]->set, &result)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); } } TEST_F(SpirvReflectMultiEntryPointTest, GetDescriptorBindingsShared) { uint32_t vert_count = 1; SpvReflectDescriptorBinding* vert_binding; uint32_t frag_count = 2; SpvReflectDescriptorBinding* frag_binding[2]; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorBindings( &module_, eps_[0], &vert_count, &vert_binding)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorBindings( &module_, eps_[1], &frag_count, frag_binding)); ASSERT_EQ(vert_binding, frag_binding[1]); } TEST_F(SpirvReflectMultiEntryPointTest, GetDescriptorSets0) { uint32_t set_count; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorSets(&module_, eps_[0], &set_count, NULL)); ASSERT_EQ(set_count, 1); SpvReflectDescriptorSet* set; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorSets(&module_, eps_[0], &set_count, &set)); ASSERT_EQ(set->set, 0); ASSERT_EQ(set->binding_count, 1); ASSERT_EQ(set, &module_.entry_points[0].descriptor_sets[0]); SpvReflectResult result; ASSERT_EQ(set, spvReflectGetEntryPointDescriptorSet(&module_, eps_[0], set->set, &result)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); } TEST_F(SpirvReflectMultiEntryPointTest, GetDescriptorSets1) { uint32_t set_count; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorSets(&module_, eps_[1], &set_count, NULL)); ASSERT_EQ(set_count, 1); SpvReflectDescriptorSet* set; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointDescriptorSets(&module_, eps_[1], &set_count, &set)); ASSERT_EQ(set->set, 0); ASSERT_EQ(set->binding_count, 2); ASSERT_EQ(set, &module_.entry_points[1].descriptor_sets[0]); SpvReflectResult result; ASSERT_EQ(set, spvReflectGetEntryPointDescriptorSet(&module_, eps_[1], set->set, &result)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); } TEST_F(SpirvReflectMultiEntryPointTest, GetInputVariables) { const uint32_t counts[2] = {2, 1}; const char* names[2][2] = {{"iUV", "pos"}, {"iUV", NULL}}; for (size_t i = 0; i < 2; ++i) { uint32_t count = 0; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointInputVariables(&module_, eps_[i], &count, NULL)); ASSERT_EQ(count, counts[i]); // 2 is the max count SpvReflectInterfaceVariable* vars[2]; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointInputVariables(&module_, eps_[i], &count, vars)); for (size_t j = 0; j < counts[i]; ++j) { if (vars[j]->decoration_flags & SPV_REFLECT_DECORATION_BUILT_IN) { // built-ins don't have reasonable locations continue; } SpvReflectResult result; const SpvReflectInterfaceVariable* var = spvReflectGetEntryPointInputVariableByLocation( &module_, eps_[i], vars[j]->location, &result); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); ASSERT_EQ(var, vars[j]); ASSERT_EQ(strcmp(var->name, names[i][var->location]), 0); } } } TEST_F(SpirvReflectMultiEntryPointTest, GetOutputVariables) { const uint32_t counts[2] = {2, 1}; // One of the outputs from the first entry point is a builtin so it has no // position. const char* names[2][1] = {{"oUV"}, {"colour"}}; for (size_t i = 0; i < 2; ++i) { uint32_t count = 0; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointOutputVariables(&module_, eps_[i], &count, NULL)); ASSERT_EQ(count, counts[i]); // 2 is the max count SpvReflectInterfaceVariable* vars[2]; ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointOutputVariables(&module_, eps_[i], &count, vars)); for (size_t j = 0; j < counts[i]; ++j) { if (vars[j]->decoration_flags & SPV_REFLECT_DECORATION_BUILT_IN) { // built-ins don't have reasonable locations continue; } SpvReflectResult result; const SpvReflectInterfaceVariable* var = spvReflectGetEntryPointOutputVariableByLocation( &module_, eps_[i], vars[j]->location, &result); ASSERT_EQ(result, SPV_REFLECT_RESULT_SUCCESS); ASSERT_EQ(var, vars[j]); ASSERT_EQ(strcmp(var->name, names[i][var->location]), 0); } } } TEST_F(SpirvReflectMultiEntryPointTest, GetPushConstants) { for (size_t i = 0; i < 2; ++i) { SpvReflectBlockVariable* var; uint32_t count = 0; ASSERT_EQ(SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH, spvReflectEnumerateEntryPointPushConstantBlocks(&module_, eps_[i], &count, &var)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointPushConstantBlocks(&module_, eps_[i], &count, NULL)); ASSERT_EQ(count, 1); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectEnumerateEntryPointPushConstantBlocks(&module_, eps_[i], &count, &var)); SpvReflectResult result; ASSERT_EQ(var, spvReflectGetEntryPointPushConstantBlock(&module_, eps_[i], &result)); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); } } TEST_F(SpirvReflectMultiEntryPointTest, ChangeDescriptorBindingNumber) { const SpvReflectDescriptorBinding* binding = spvReflectGetEntryPointDescriptorBinding(&module_, eps_[0], 1, 0, NULL); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectChangeDescriptorBindingNumbers(&module_, binding, 2, 1)); // Change descriptor binding numbers doesn't currently resort so it won't // invalidate binding, but if that changes this test will need to be fixed. ASSERT_EQ(binding->set, 1); ASSERT_EQ(binding->binding, 2); SpvReflectResult result; const SpvReflectDescriptorSet* set0 = spvReflectGetEntryPointDescriptorSet(&module_, eps_[0], 1, &result); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); ASSERT_EQ(set0->binding_count, 1); const SpvReflectDescriptorSet* set1 = spvReflectGetEntryPointDescriptorSet(&module_, eps_[1], 1, &result); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); ASSERT_EQ(set1->binding_count, 1); ASSERT_EQ(set0->bindings[0], set1->bindings[0]); } TEST_F(SpirvReflectMultiEntryPointTest, ChangeDescriptorSetNumber) { const SpvReflectDescriptorSet* set = spvReflectGetDescriptorSet(&module_, 0, NULL); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, spvReflectChangeDescriptorSetNumber(&module_, set, 1)); // Change descriptor binding numbers doesn't currently resort so it won't // invalidate binding, but if that changes this test will need to be fixed. ASSERT_EQ(set->set, 1); SpvReflectResult result; const SpvReflectDescriptorSet* set0 = spvReflectGetEntryPointDescriptorSet(&module_, eps_[0], 1, &result); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); ASSERT_EQ(set0->binding_count, 1); const SpvReflectDescriptorSet* set1 = spvReflectGetEntryPointDescriptorSet(&module_, eps_[1], 1, &result); ASSERT_EQ(SPV_REFLECT_RESULT_SUCCESS, result); ASSERT_EQ(set1->binding_count, 2); ASSERT_EQ(set0->bindings[0], set1->bindings[1]); ASSERT_EQ(set0->bindings[0]->set, 1); }