UboUnsizedArray.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. #include <gtest/gtest.h>
  21. #include "TestFixture.h"
  22. namespace glslangtest {
  23. namespace {
  24. class UboUnsizedArrayTest : public GlslangTest<::testing::Test> {
  25. protected:
  26. // Helper function to compile shader and check for specific error message.
  27. bool compileShouldFailWith(const std::string& code, const std::string& expectedError,
  28. EShLanguage stage = EShLangVertex)
  29. {
  30. glslang::TShader shader(stage);
  31. EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
  32. bool success = compile(&shader, code, "", controls);
  33. if (success) {
  34. // Compilation should have failed.
  35. return false;
  36. }
  37. std::string errorLog = shader.getInfoLog();
  38. return errorLog.find(expectedError) != std::string::npos;
  39. }
  40. };
  41. // Test that unsized arrays in uniform blocks work when extension is enabled.
  42. TEST_F(UboUnsizedArrayTest, BasicFunctionality)
  43. {
  44. const std::string code = R"(
  45. #version 450
  46. #extension GL_EXT_uniform_buffer_unsized_array : require
  47. layout(std140, binding=0) uniform DataBlock {
  48. float scale;
  49. float values[]; // unsized array as last member
  50. };
  51. void main() {
  52. gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0);
  53. }
  54. )";
  55. glslang::TShader shader(EShLangVertex);
  56. EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
  57. EXPECT_TRUE(compile(&shader, code, "", controls));
  58. }
  59. // Test that unsized arrays work when extension is enabled.
  60. TEST_F(UboUnsizedArrayTest, ExtensionRequired)
  61. {
  62. const std::string code = R"(
  63. #version 450
  64. #extension GL_EXT_uniform_buffer_unsized_array : require
  65. layout(std140, binding=0) uniform DataBlock {
  66. float scale;
  67. float values[]; // Should work with extension
  68. };
  69. void main() {
  70. gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0);
  71. }
  72. )";
  73. glslang::TShader shader(EShLangVertex);
  74. EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
  75. EXPECT_TRUE(compile(&shader, code, "", controls));
  76. }
  77. // Test that only the last member can be unsized.
  78. TEST_F(UboUnsizedArrayTest, OnlyLastMemberCanBeUnsized)
  79. {
  80. const std::string code = R"(
  81. #version 450
  82. #extension GL_EXT_uniform_buffer_unsized_array : require
  83. layout(std140, binding=0) uniform DataBlock {
  84. float scale;
  85. float values[]; // Last member - should work
  86. };
  87. void main() {
  88. gl_Position = vec4(values[0] * scale, 0.0, 0.0, 1.0);
  89. }
  90. )";
  91. glslang::TShader shader(EShLangVertex);
  92. EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
  93. EXPECT_TRUE(compile(&shader, code, "", controls));
  94. }
  95. // Test that .length() method fails on unsized arrays.
  96. TEST_F(UboUnsizedArrayTest, LengthMethodNotAllowed)
  97. {
  98. const std::string code = R"(
  99. #version 450
  100. #extension GL_EXT_uniform_buffer_unsized_array : require
  101. layout(std140, binding=0) uniform DataBlock {
  102. float scale;
  103. float values[];
  104. };
  105. layout(location = 0) out vec4 fragColor;
  106. void main() {
  107. int len = values.length(); // Should fail - length() not supported for unsized arrays in uniform blocks
  108. fragColor = vec4(len, 0.0, 0.0, 1.0);
  109. }
  110. )";
  111. EXPECT_TRUE(
  112. compileShouldFailWith(code, "array must be declared with a size before using this method", EShLangFragment));
  113. }
  114. // Test that function parameters cannot be unsized arrays from uniform blocks.
  115. TEST_F(UboUnsizedArrayTest, FunctionParameterRestriction)
  116. {
  117. const std::string code = R"(
  118. #version 450
  119. #extension GL_EXT_uniform_buffer_unsized_array : require
  120. layout(std140, binding=0) uniform DataBlock {
  121. float scale;
  122. float values[];
  123. };
  124. void processArray(float arr[10]) {
  125. // Process array
  126. }
  127. void main() {
  128. processArray(values); // Should fail - cannot pass unsized arrays as function arguments
  129. gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
  130. }
  131. )";
  132. EXPECT_TRUE(compileShouldFailWith(code, "no matching overloaded function found"));
  133. }
  134. // Test negative constant indexing.
  135. TEST_F(UboUnsizedArrayTest, NegativeIndexingNotAllowed)
  136. {
  137. const std::string code = R"(
  138. #version 450
  139. #extension GL_EXT_uniform_buffer_unsized_array : require
  140. layout(std140, binding=0) uniform DataBlock {
  141. float scale;
  142. float values[];
  143. };
  144. void main() {
  145. float value = values[-1]; // Should fail
  146. gl_Position = vec4(value * scale, 0.0, 0.0, 1.0);
  147. }
  148. )";
  149. EXPECT_TRUE(compileShouldFailWith(code, "index out of range"));
  150. }
  151. // Test that different data types work correctly.
  152. TEST_F(UboUnsizedArrayTest, MultipleDataTypes)
  153. {
  154. const std::string code = R"(
  155. #version 450
  156. #extension GL_EXT_uniform_buffer_unsized_array : require
  157. layout(std140, binding=0) uniform FloatBlock {
  158. float scale;
  159. float floatValues[];
  160. };
  161. layout(std140, binding=1) uniform IntBlock {
  162. int count;
  163. int intValues[];
  164. };
  165. layout(std140, binding=2) uniform VecBlock {
  166. mat4 transform;
  167. vec4 vecValues[];
  168. };
  169. void main() {
  170. int baseIndex = gl_VertexIndex % 10;
  171. float value = floatValues[baseIndex] * scale;
  172. int ivalue = intValues[baseIndex] * count;
  173. vec4 vvalue = vecValues[baseIndex] * transform;
  174. gl_Position = vec4(value + float(ivalue), vvalue.xy, 1.0);
  175. }
  176. )";
  177. glslang::TShader shader(EShLangVertex);
  178. EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
  179. EXPECT_TRUE(compile(&shader, code, "", controls));
  180. }
  181. // Test that general integer expressions work for indexing.
  182. TEST_F(UboUnsizedArrayTest, GeneralIntegerIndexing)
  183. {
  184. const std::string code = R"(
  185. #version 450
  186. #extension GL_EXT_uniform_buffer_unsized_array : require
  187. layout(std140, binding=0) uniform DataBlock {
  188. float scale;
  189. float values[];
  190. };
  191. layout(std140, binding=1) uniform SizeInfo {
  192. int arraySize;
  193. };
  194. void main() {
  195. // Various forms of general integer expressions
  196. int baseIndex = gl_VertexIndex % arraySize;
  197. int offsetIndex = (baseIndex + 1) % arraySize;
  198. int computedIndex = min(baseIndex + offsetIndex, arraySize - 1);
  199. float result = values[baseIndex] + values[offsetIndex] + values[computedIndex];
  200. gl_Position = vec4(result * scale, 0.0, 0.0, 1.0);
  201. }
  202. )";
  203. glslang::TShader shader(EShLangVertex);
  204. EShMessages controls = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
  205. EXPECT_TRUE(compile(&shader, code, "", controls));
  206. }
  207. // Test SPIR-V generation for unsized arrays in uniform blocks.
  208. TEST_F(UboUnsizedArrayTest, SpvGeneration)
  209. {
  210. const std::string code = R"(
  211. #version 450
  212. #extension GL_EXT_uniform_buffer_unsized_array : require
  213. layout(std140, binding=0) uniform DataBlock {
  214. float scale;
  215. float values[];
  216. };
  217. layout(std140, binding=1) uniform SizeBlock {
  218. int arraySize;
  219. };
  220. void main() {
  221. int index = gl_VertexIndex % arraySize;
  222. float value = values[index];
  223. gl_Position = vec4(value * scale, 0.0, 0.0, 1.0);
  224. }
  225. )";
  226. // Compile the shader.
  227. glslang::TShader shader(EShLangVertex);
  228. const char* shaderStrings[1] = {code.c_str()};
  229. shader.setStrings(shaderStrings, 1);
  230. // Set up compilation options.
  231. EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
  232. shader.setEnvInput(glslang::EShSourceGlsl, EShLangVertex, glslang::EShClientVulkan, 450);
  233. shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
  234. shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
  235. // Compile.
  236. bool success = shader.parse(GetDefaultResources(), 450, false, messages);
  237. EXPECT_TRUE(success) << "Shader compilation failed: " << shader.getInfoLog();
  238. // Link and generate SPIR-V.
  239. glslang::TProgram program;
  240. program.addShader(&shader);
  241. success = program.link(messages);
  242. EXPECT_TRUE(success) << "Program linking failed: " << program.getInfoLog();
  243. // Generate SPIR-V.
  244. spv::SpvBuildLogger logger;
  245. std::vector<uint32_t> spirv;
  246. glslang::SpvOptions options;
  247. glslang::GlslangToSpv(*program.getIntermediate(EShLangVertex), spirv, &logger, &options);
  248. // Disassemble SPIR-V to text for easier checking.
  249. std::ostringstream disassembly_stream;
  250. spv::Disassemble(disassembly_stream, spirv);
  251. std::string spirvText = disassembly_stream.str();
  252. // Check for key SPIR-V elements that indicate successful compilation.
  253. // 1. SourceExtension for the extension
  254. EXPECT_TRUE(spirvText.find("SourceExtension") != std::string::npos)
  255. << "SPIR-V should contain SourceExtension for GL_EXT_uniform_buffer_unsized_array";
  256. // 2. TypeRuntimeArray for the unsized array
  257. EXPECT_TRUE(spirvText.find("TypeRuntimeArray") != std::string::npos)
  258. << "SPIR-V should contain TypeRuntimeArray for unsized arrays";
  259. // 3. Block decoration (for uniform blocks with runtime arrays)
  260. EXPECT_TRUE(spirvText.find("Block") != std::string::npos)
  261. << "SPIR-V should contain Block decoration for uniform blocks with runtime arrays";
  262. // 4. RuntimeDescriptorArrayEXT capability
  263. EXPECT_TRUE(spirvText.find("RuntimeDescriptorArrayEXT") != std::string::npos)
  264. << "SPIR-V should contain RuntimeDescriptorArrayEXT capability";
  265. // 5. SPV_EXT_descriptor_indexing extension
  266. EXPECT_TRUE(spirvText.find("SPV_EXT_descriptor_indexing") != std::string::npos)
  267. << "SPIR-V should contain SPV_EXT_descriptor_indexing extension";
  268. }
  269. } // anonymous namespace
  270. } // namespace glslangtest