yum-mirror/glslang
git clone https://git.yummers.dev/yum-mirror/glslang
ffcdd3ea
main
1// Copyright (C) 2025 NVIDIA Corporation 2// 3// Permission is hereby granted, free of charge, to any person obtaining a copy 4// of this software and associated documentation files (the "Software"), to deal 5// in the Software without restriction, including without limitation the rights 6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7// copies of the Software, and to permit persons to whom the Software is 8// furnished to do so, subject to the following conditions: 9// 10// The above copyright notice and this permission notice shall be included in all 11// copies or substantial portions of the Software. 12// 13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19// SOFTWARE. 20 21#include <gtest/gtest.h> 22 23#include "TestFixture.h" 24 25namespace glslangtest { 26namespace { 27 28class UboUnsizedArrayTest :public GlslangTest < ::testing::Test > { 29protected : 30// Helper function to compile shader and check for specific error message. 31bool compileShouldFailWith (const std::string & code ,const std::string & expectedError , 32EShLanguage stage = EShLangVertex ) 33 { 34 glslang::TShader shader (stage ); 35EShMessages controls = static_cast < EShMessages > (EShMsgDefault |EShMsgSpvRules |EShMsgVulkanRules ); 36 37bool success = compile (& shader ,code ,"" ,controls ); 38 39if (success ) { 40// Compilation should have failed. 41return false; 42 } 43 44 std::string errorLog = shader .getInfoLog (); 45return errorLog .find (expectedError )!= std::string::npos ; 46 } 47}; 48 49// Test that unsized arrays in uniform blocks work when extension is enabled. 50TEST_F (UboUnsizedArrayTest ,BasicFunctionality ) 51{ 52const std::string code = R"( 53#version 450 54#extension GL_EXT_uniform_buffer_unsized_array : require 5556 layout(std140, binding=0) uniform DataBlock { 57float scale; 58float values[]; // unsized array as last member 59}; 6061 void main() { 62gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0); 63} 64)" ; 65 66 glslang::TShader shader (EShLangVertex ); 67EShMessages controls = static_cast < EShMessages > (EShMsgDefault |EShMsgSpvRules |EShMsgVulkanRules ); 68EXPECT_TRUE (compile (& shader ,code ,"" ,controls )); 69} 70 71// Test that unsized arrays work when extension is enabled. 72TEST_F (UboUnsizedArrayTest ,ExtensionRequired ) 73{ 74const std::string code = R"( 75#version 450 76#extension GL_EXT_uniform_buffer_unsized_array : require 7778 layout(std140, binding=0) uniform DataBlock { 79float scale; 80float values[]; // Should work with extension 81}; 8283 void main() { 84gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0); 85} 86)" ; 87 88 glslang::TShader shader (EShLangVertex ); 89EShMessages controls = static_cast < EShMessages > (EShMsgDefault |EShMsgSpvRules |EShMsgVulkanRules ); 90EXPECT_TRUE (compile (& shader ,code ,"" ,controls )); 91} 92 93// Test that only the last member can be unsized. 94TEST_F (UboUnsizedArrayTest ,OnlyLastMemberCanBeUnsized ) 95{ 96const std::string code = R"( 97#version 450 98#extension GL_EXT_uniform_buffer_unsized_array : require 99100 layout(std140, binding=0) uniform DataBlock { 101float scale; 102float values[]; // Last member - should work 103}; 104105 void main() { 106gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0); 107} 108)" ; 109 110 glslang::TShader shader (EShLangVertex ); 111EShMessages controls = static_cast < EShMessages > (EShMsgDefault |EShMsgSpvRules |EShMsgVulkanRules ); 112EXPECT_TRUE (compile (& shader ,code ,"" ,controls )); 113} 114 115// Test that .length() method fails on unsized arrays. 116TEST_F (UboUnsizedArrayTest ,LengthMethodNotAllowed ) 117{ 118const std::string code = R"( 119#version 450 120#extension GL_EXT_uniform_buffer_unsized_array : require 121122 layout(std140, binding=0) uniform DataBlock { 123float scale; 124float values[]; 125}; 126127 layout(location = 0) out vec4 fragColor; 128129 void main() { 130int len = values.length(); // Should fail - length() not supported for unsized arrays in uniform blocks 131fragColor = vec4(len, 0.0, 0.0, 1.0); 132} 133)" ; 134 135EXPECT_TRUE ( 136compileShouldFailWith (code ,"array must be declared with a size before using this method" ,EShLangFragment )); 137} 138 139// Test that function parameters cannot be unsized arrays from uniform blocks. 140TEST_F (UboUnsizedArrayTest ,FunctionParameterRestriction ) 141{ 142const std::string code = R"( 143#version 450 144#extension GL_EXT_uniform_buffer_unsized_array : require 145146 layout(std140, binding=0) uniform DataBlock { 147float scale; 148float values[]; 149}; 150151 void processArray(float arr[10]) { 152// Process array 153} 154155 void main() { 156processArray(values); // Should fail - cannot pass unsized arrays as function arguments 157gl_Position = vec4(0.0, 0.0, 0.0, 1.0); 158} 159)" ; 160 161EXPECT_TRUE (compileShouldFailWith (code ,"no matching overloaded function found" )); 162} 163 164// Test negative constant indexing. 165TEST_F (UboUnsizedArrayTest ,NegativeIndexingNotAllowed ) 166{ 167const std::string code = R"( 168#version 450 169#extension GL_EXT_uniform_buffer_unsized_array : require 170171 layout(std140, binding=0) uniform DataBlock { 172float scale; 173float values[]; 174}; 175176 void main() { 177float value = values[-1]; // Should fail 178gl_Position = vec4(value * scale, 0.0, 0.0, 1.0); 179} 180)" ; 181 182EXPECT_TRUE (compileShouldFailWith (code ,"index out of range" )); 183} 184 185// Test that different data types work correctly. 186TEST_F (UboUnsizedArrayTest ,MultipleDataTypes ) 187{ 188const std::string code = R"( 189#version 450 190#extension GL_EXT_uniform_buffer_unsized_array : require 191192 layout(std140, binding=0) uniform FloatBlock { 193float scale; 194float floatValues[]; 195}; 196197 layout(std140, binding=1) uniform IntBlock { 198int count; 199int intValues[]; 200}; 201202 layout(std140, binding=2) uniform VecBlock { 203mat4 transform; 204vec4 vecValues[]; 205}; 206207 void main() { 208int baseIndex = gl_VertexIndex % 10; 209float value = floatValues[baseIndex] * scale; 210int ivalue = intValues[baseIndex] * count; 211vec4 vvalue = vecValues[baseIndex] * transform; 212213 gl_Position = vec4(value + float(ivalue), vvalue.xy, 1.0); 214} 215)" ; 216 217 glslang::TShader shader (EShLangVertex ); 218EShMessages controls = static_cast < EShMessages > (EShMsgDefault |EShMsgSpvRules |EShMsgVulkanRules ); 219EXPECT_TRUE (compile (& shader ,code ,"" ,controls )); 220} 221 222// Test that general integer expressions work for indexing. 223TEST_F (UboUnsizedArrayTest ,GeneralIntegerIndexing ) 224{ 225const std::string code = R"( 226#version 450 227#extension GL_EXT_uniform_buffer_unsized_array : require 228229 layout(std140, binding=0) uniform DataBlock { 230float scale; 231float values[]; 232}; 233234 layout(std140, binding=1) uniform SizeInfo { 235int arraySize; 236}; 237238 void main() { 239// Various forms of general integer expressions 240int baseIndex = gl_VertexIndex % arraySize; 241int offsetIndex = (baseIndex + 1) % arraySize; 242int computedIndex = min(baseIndex + offsetIndex, arraySize - 1); 243244 float result = values[baseIndex] + values[offsetIndex] + values[computedIndex]; 245gl_Position = vec4(result * scale, 0.0, 0.0, 1.0); 246} 247)" ; 248 249 glslang::TShader shader (EShLangVertex ); 250EShMessages controls = static_cast < EShMessages > (EShMsgDefault |EShMsgSpvRules |EShMsgVulkanRules ); 251EXPECT_TRUE (compile (& shader ,code ,"" ,controls )); 252} 253 254// Test SPIR-V generation for unsized arrays in uniform blocks. 255TEST_F (UboUnsizedArrayTest ,SpvGeneration ) 256{ 257const std::string code = R"( 258#version 450 259#extension GL_EXT_uniform_buffer_unsized_array : require 260261 layout(std140, binding=0) uniform DataBlock { 262float scale; 263float values[]; 264}; 265266 layout(std140, binding=1) uniform SizeBlock { 267int arraySize; 268}; 269270 void main() { 271int index = gl_VertexIndex % arraySize; 272float value = values[index]; 273gl_Position = vec4(value * scale, 0.0, 0.0, 1.0); 274} 275)" ; 276 277// Compile the shader. 278 glslang::TShader shader (EShLangVertex ); 279const char * shaderStrings [1 ]= {code .c_str ()}; 280shader .setStrings (shaderStrings ,1 ); 281 282// Set up compilation options. 283EShMessages messages = static_cast < EShMessages > (EShMsgSpvRules |EShMsgVulkanRules ); 284shader .setEnvInput (glslang::EShSourceGlsl ,EShLangVertex , glslang::EShClientVulkan ,450 ); 285shader .setEnvClient (glslang::EShClientVulkan , glslang::EShTargetVulkan_1_0 ); 286shader .setEnvTarget (glslang::EShTargetSpv , glslang::EShTargetSpv_1_0 ); 287 288// Compile. 289bool success = shader .parse (GetDefaultResources (),450 , false,messages ); 290EXPECT_TRUE (success ) <<"Shader compilation failed: " <<shader .getInfoLog (); 291 292// Link and generate SPIR-V. 293 glslang::TProgram program ; 294program .addShader (& shader ); 295success = program .link (messages ); 296EXPECT_TRUE (success ) <<"Program linking failed: " <<program .getInfoLog (); 297 298// Generate SPIR-V. 299 spv::SpvBuildLogger logger ; 300 std::vector < uint32_t > spirv ; 301 glslang::SpvOptions options ; 302 glslang::GlslangToSpv (* program .getIntermediate (EShLangVertex ),spirv ,& logger ,& options ); 303 304// Disassemble SPIR-V to text for easier checking. 305 std::ostringstream disassembly_stream ; 306 spv::Disassemble (disassembly_stream ,spirv ); 307 std::string spirvText = disassembly_stream .str (); 308 309// Check for key SPIR-V elements that indicate successful compilation. 310// 1. SourceExtension for the extension 311EXPECT_TRUE (spirvText .find ("SourceExtension" )!= std::string::npos ) 312 <<"SPIR-V should contain SourceExtension for GL_EXT_uniform_buffer_unsized_array" ; 313 314// 2. TypeRuntimeArray for the unsized array 315EXPECT_TRUE (spirvText .find ("TypeRuntimeArray" )!= std::string::npos ) 316 <<"SPIR-V should contain TypeRuntimeArray for unsized arrays" ; 317 318// 3. Block decoration (for uniform blocks with runtime arrays) 319EXPECT_TRUE (spirvText .find ("Block" )!= std::string::npos ) 320 <<"SPIR-V should contain Block decoration for uniform blocks with runtime arrays" ; 321 322// 4. RuntimeDescriptorArrayEXT capability 323EXPECT_TRUE (spirvText .find ("RuntimeDescriptorArrayEXT" )!= std::string::npos ) 324 <<"SPIR-V should contain RuntimeDescriptorArrayEXT capability" ; 325 326// 5. SPV_EXT_descriptor_indexing extension 327EXPECT_TRUE (spirvText .find ("SPV_EXT_descriptor_indexing" )!= std::string::npos ) 328 <<"SPIR-V should contain SPV_EXT_descriptor_indexing extension" ; 329} 330 331}// anonymous namespace 332}// namespace glslangtest