| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- // Copyright (C) 2025 NVIDIA Corporation
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // 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 Software.
- //
- // THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- // SOFTWARE.
- #include <gtest/gtest.h>
- #include "TestFixture.h"
- namespace glslangtest {
- namespace {
- class UboUnsizedArrayTest : public GlslangTest<::testing::Test> {
- protected:
- // Helper function to compile shader and check for specific error message.
- bool compileShouldFailWith(const std::string& code, const std::string& expectedError,
- EShLanguage stage = EShLangVertex)
- {
- glslang::TShader shader(stage);
- EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
- bool success = compile(&shader, code, "", controls);
- if (success) {
- // Compilation should have failed.
- return false;
- }
- std::string errorLog = shader.getInfoLog();
- return errorLog.find(expectedError) != std::string::npos;
- }
- };
- // Test that unsized arrays in uniform blocks work when extension is enabled.
- TEST_F(UboUnsizedArrayTest, BasicFunctionality)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[]; // unsized array as last member
- };
-
- void main() {
- gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0);
- }
- )";
- glslang::TShader shader(EShLangVertex);
- EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
- EXPECT_TRUE(compile(&shader, code, "", controls));
- }
- // Test that unsized arrays work when extension is enabled.
- TEST_F(UboUnsizedArrayTest, ExtensionRequired)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[]; // Should work with extension
- };
-
- void main() {
- gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0);
- }
- )";
- glslang::TShader shader(EShLangVertex);
- EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
- EXPECT_TRUE(compile(&shader, code, "", controls));
- }
- // Test that only the last member can be unsized.
- TEST_F(UboUnsizedArrayTest, OnlyLastMemberCanBeUnsized)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[]; // Last member - should work
- };
-
- void main() {
- gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0);
- }
- )";
- glslang::TShader shader(EShLangVertex);
- EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
- EXPECT_TRUE(compile(&shader, code, "", controls));
- }
- // Test that .length() method fails on unsized arrays.
- TEST_F(UboUnsizedArrayTest, LengthMethodNotAllowed)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[];
- };
-
- layout(location = 0) out vec4 fragColor;
-
- void main() {
- int len = values.length(); // Should fail - length() not supported for unsized arrays in uniform blocks
- fragColor = vec4(len, 0.0, 0.0, 1.0);
- }
- )";
- EXPECT_TRUE(
- compileShouldFailWith(code, "array must be declared with a size before using this method", EShLangFragment));
- }
- // Test that function parameters cannot be unsized arrays from uniform blocks.
- TEST_F(UboUnsizedArrayTest, FunctionParameterRestriction)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[];
- };
-
- void processArray(float arr[10]) {
- // Process array
- }
-
- void main() {
- processArray(values); // Should fail - cannot pass unsized arrays as function arguments
- gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
- }
- )";
- EXPECT_TRUE(compileShouldFailWith(code, "no matching overloaded function found"));
- }
- // Test negative constant indexing.
- TEST_F(UboUnsizedArrayTest, NegativeIndexingNotAllowed)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[];
- };
-
- void main() {
- float value = values[-1]; // Should fail
- gl_Position = vec4(value * scale, 0.0, 0.0, 1.0);
- }
- )";
- EXPECT_TRUE(compileShouldFailWith(code, "index out of range"));
- }
- // Test that different data types work correctly.
- TEST_F(UboUnsizedArrayTest, MultipleDataTypes)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform FloatBlock {
- float scale;
- float floatValues[];
- };
-
- layout(std140, binding=1) uniform IntBlock {
- int count;
- int intValues[];
- };
-
- layout(std140, binding=2) uniform VecBlock {
- mat4 transform;
- vec4 vecValues[];
- };
-
- void main() {
- int baseIndex = gl_VertexIndex % 10;
- float value = floatValues[baseIndex] * scale;
- int ivalue = intValues[baseIndex] * count;
- vec4 vvalue = vecValues[baseIndex] * transform;
-
- gl_Position = vec4(value + float(ivalue), vvalue.xy, 1.0);
- }
- )";
- glslang::TShader shader(EShLangVertex);
- EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
- EXPECT_TRUE(compile(&shader, code, "", controls));
- }
- // Test that general integer expressions work for indexing.
- TEST_F(UboUnsizedArrayTest, GeneralIntegerIndexing)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[];
- };
-
- layout(std140, binding=1) uniform SizeInfo {
- int arraySize;
- };
-
- void main() {
- // Various forms of general integer expressions
- int baseIndex = gl_VertexIndex % arraySize;
- int offsetIndex = (baseIndex + 1) % arraySize;
- int computedIndex = min(baseIndex + offsetIndex, arraySize - 1);
-
- float result = values[baseIndex] + values[offsetIndex] + values[computedIndex];
- gl_Position = vec4(result * scale, 0.0, 0.0, 1.0);
- }
- )";
- glslang::TShader shader(EShLangVertex);
- EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
- EXPECT_TRUE(compile(&shader, code, "", controls));
- }
- // Test SPIR-V generation for unsized arrays in uniform blocks.
- TEST_F(UboUnsizedArrayTest, SpvGeneration)
- {
- const std::string code = R"(
- #version 450
- #extension GL_EXT_uniform_buffer_unsized_array : require
-
- layout(std140, binding=0) uniform DataBlock {
- float scale;
- float values[];
- };
-
- layout(std140, binding=1) uniform SizeBlock {
- int arraySize;
- };
-
- void main() {
- int index = gl_VertexIndex % arraySize;
- float value = values[index];
- gl_Position = vec4(value * scale, 0.0, 0.0, 1.0);
- }
- )";
- // Compile the shader.
- glslang::TShader shader(EShLangVertex);
- const char* shaderStrings[1] = {code.c_str()};
- shader.setStrings(shaderStrings, 1);
- // Set up compilation options.
- EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
- shader.setEnvInput(glslang::EShSourceGlsl, EShLangVertex, glslang::EShClientVulkan, 450);
- shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
- shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
- // Compile.
- bool success = shader.parse(GetDefaultResources(), 450, false, messages);
- EXPECT_TRUE(success) << "Shader compilation failed: " << shader.getInfoLog();
- // Link and generate SPIR-V.
- glslang::TProgram program;
- program.addShader(&shader);
- success = program.link(messages);
- EXPECT_TRUE(success) << "Program linking failed: " << program.getInfoLog();
- // Generate SPIR-V.
- spv::SpvBuildLogger logger;
- std::vector<uint32_t> spirv;
- glslang::SpvOptions options;
- glslang::GlslangToSpv(*program.getIntermediate(EShLangVertex), spirv, &logger, &options);
- // Disassemble SPIR-V to text for easier checking.
- std::ostringstream disassembly_stream;
- spv::Disassemble(disassembly_stream, spirv);
- std::string spirvText = disassembly_stream.str();
- // Check for key SPIR-V elements that indicate successful compilation.
- // 1. SourceExtension for the extension
- EXPECT_TRUE(spirvText.find("SourceExtension") != std::string::npos)
- << "SPIR-V should contain SourceExtension for GL_EXT_uniform_buffer_unsized_array";
- // 2. TypeRuntimeArray for the unsized array
- EXPECT_TRUE(spirvText.find("TypeRuntimeArray") != std::string::npos)
- << "SPIR-V should contain TypeRuntimeArray for unsized arrays";
- // 3. Block decoration (for uniform blocks with runtime arrays)
- EXPECT_TRUE(spirvText.find("Block") != std::string::npos)
- << "SPIR-V should contain Block decoration for uniform blocks with runtime arrays";
- // 4. RuntimeDescriptorArrayEXT capability
- EXPECT_TRUE(spirvText.find("RuntimeDescriptorArrayEXT") != std::string::npos)
- << "SPIR-V should contain RuntimeDescriptorArrayEXT capability";
- // 5. SPV_EXT_descriptor_indexing extension
- EXPECT_TRUE(spirvText.find("SPV_EXT_descriptor_indexing") != std::string::npos)
- << "SPIR-V should contain SPV_EXT_descriptor_indexing extension";
- }
- } // anonymous namespace
- } // namespace glslangtest
|