From 134cdfd09be5da571e640ba44e3f8d77edc55d89 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 7 Feb 2019 12:29:13 +0000 Subject: [PATCH] Add tests to CBuffer Zoos to test alternate uniform sources on APIs * Vulkan: Specialization constants * OpenGL: Bare uniforms (not in a UBO) * D3D12: Root signature constants --- util/test/demos/d3d12/d3d12_cbuffer_zoo.cpp | 47 ++++- util/test/demos/gl/gl_cbuffer_zoo.cpp | 191 ++++++++++++++++++++ util/test/demos/vk/vk_cbuffer_zoo.cpp | 25 +++ util/test/rdtest/testcase.py | 18 +- util/test/tests/D3D12/D3D12_CBuffer_Zoo.py | 29 +++ util/test/tests/GL/GL_CBuffer_Zoo.py | 108 +++++++++++ util/test/tests/Vulkan/VK_CBuffer_Zoo.py | 24 +++ 7 files changed, 438 insertions(+), 4 deletions(-) diff --git a/util/test/demos/d3d12/d3d12_cbuffer_zoo.cpp b/util/test/demos/d3d12/d3d12_cbuffer_zoo.cpp index 10b49defa..d6a23d322 100644 --- a/util/test/demos/d3d12/d3d12_cbuffer_zoo.cpp +++ b/util/test/demos/d3d12/d3d12_cbuffer_zoo.cpp @@ -229,13 +229,36 @@ cbuffer consts : register(b0) float4 test; // {440, 441, 442, 443} }; +// this comes from root signature constants +cbuffer rootconsts : register(b1) +{ + float4 root_zero; + float4 root_a; + float2 root_b, root_c; + float3_1 root_d; +}; + float4 main() : SV_Target0 { - return test + float4(0.1f, 0.0f, 0.0f, 0.0f); + return test + root_zero + float4(0.1f, 0.0f, 0.0f, 0.0f); } )EOSHADER"; + struct float3_1 + { + float a[3]; + float b; + }; + + struct RootData + { + float root_zero[4]; + float root_a[4]; + float root_b[2], root_c[2]; + float3_1 root_d; + }; + int main(int argc, char **argv) { // initialise, create window, create device, etc @@ -250,11 +273,32 @@ float4 main() : SV_Target0 for(int i = 0; i < 512; i++) cbufferdata[i] = Vec4f(float(i * 4 + 0), float(i * 4 + 1), float(i * 4 + 2), float(i * 4 + 3)); + RootData rootData = {}; + + rootData.root_a[0] = 10.0f; + rootData.root_a[1] = 20.0f; + rootData.root_a[2] = 30.0f; + rootData.root_a[3] = 40.0f; + + rootData.root_b[0] = 50.0f; + rootData.root_b[1] = 60.0f; + + rootData.root_c[0] = 70.0f; + rootData.root_c[1] = 80.0f; + + rootData.root_d.a[0] = 90.0f; + rootData.root_d.a[1] = 100.0f; + rootData.root_d.a[2] = 110.0f; + rootData.root_d.b = 120.0f; + + static_assert(sizeof(rootData) == 64, "Root data is mis-sized"); + ID3D12ResourcePtr vb = MakeBuffer().Data(DefaultTri); ID3D12ResourcePtr cb = MakeBuffer().Data(cbufferdata); ID3D12RootSignaturePtr sig = MakeSig({ cbvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 0), + constParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 1, sizeof(rootData) / sizeof(uint32_t)), }); ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob).RTVs( @@ -290,6 +334,7 @@ float4 main() : SV_Target0 cmd->SetPipelineState(pso); cmd->SetGraphicsRootSignature(sig); cmd->SetGraphicsRootConstantBufferView(0, cb->GetGPUVirtualAddress()); + cmd->SetGraphicsRoot32BitConstants(1, sizeof(rootData) / sizeof(uint32_t), &rootData, 0); RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); diff --git a/util/test/demos/gl/gl_cbuffer_zoo.cpp b/util/test/demos/gl/gl_cbuffer_zoo.cpp index 446fc56b8..0986156ea 100644 --- a/util/test/demos/gl/gl_cbuffer_zoo.cpp +++ b/util/test/demos/gl/gl_cbuffer_zoo.cpp @@ -288,6 +288,14 @@ layout(binding = 0, std140) uniform constsbuf // [3][2][1] = {..., ..., ..., ...} }; +uniform vec4 A; +uniform vec2 B; +uniform vec3 C; +uniform mat2x3 D; +uniform float E[3]; +uniform vec4 F[3][2][2]; +uniform nested G[2]; + void main() { // we need to ref all of the variables we want to include to force GL to include them :(. @@ -295,6 +303,8 @@ void main() blah += n[0] + o[0] + p.x; blah += q[0].x + r[0].x + s[0].x + t[0].x + u[0].x + v[0].x + w[0].x + x[0].x + y[0].x + z; blah += multiarray[0][0].x + ac[0][0] + ad[0][0] + ae[0][0][0] + af[0][0][0]; + blah += A.z + B.x + C.y + D[0][1] + E[2] + F[1][0][1].y; + blah += G[0].a.b + G[1].a.b + G[1].b[3].w + G[1].c[3].a.y; blah *= vertIn.uv.z; Color = blah + test + vec4(0.1f, 0.0f, 0.0f, 0.0f); } @@ -359,6 +369,187 @@ void main() glUseProgram(program); + GLint location = -1; + + location = glGetUniformLocation(program, "A"); + if(location != -1) + glUniform4f(location, 10.0f, 20.0f, 30.0f, 40.0f); + + location = glGetUniformLocation(program, "B"); + if(location != -1) + glUniform2f(location, 50.0f, 60.0f); + + location = glGetUniformLocation(program, "C"); + if(location != -1) + glUniform3f(location, 70.0f, 80.0f, 90.0f); + + // implicitly column major, so: + // {100.0, 130.0} + // {110.0, 140.0} + // {120.0, 150.0} + float D[2 * 3] = { + 100.0f, 110.0f, 120.0f, 130.0f, 140.0f, 150.0f, + }; + location = glGetUniformLocation(program, "D"); + if(location != -1) + glUniformMatrix2x3fv(location, 1, false, D); + + float E[3] = {160.0f, 170.0f, 180.0f}; + location = glGetUniformLocation(program, "E"); + if(location != -1) + glUniform1fv(location, 3, E); + + float val = 190.0f; + + float F[2 * 4] = {}; + +#define NEXT_F() \ + for(int x = 0; x < 2 * 4; x++) \ + { \ + F[x] = val; \ + val += 10.0f; \ + } + + NEXT_F(); + location = glGetUniformLocation(program, "F[0][0]"); + if(location != -1) + glUniform4fv(location, 2, F); + + NEXT_F(); + location = glGetUniformLocation(program, "F[0][1]"); + if(location != -1) + glUniform4fv(location, 2, F); + + NEXT_F(); + location = glGetUniformLocation(program, "F[1][0]"); + if(location != -1) + glUniform4fv(location, 2, F); + + NEXT_F(); + location = glGetUniformLocation(program, "F[1][1]"); + if(location != -1) + glUniform4fv(location, 2, F); + + NEXT_F(); + location = glGetUniformLocation(program, "F[2][0]"); + if(location != -1) + glUniform4fv(location, 2, F); + + NEXT_F(); + location = glGetUniformLocation(program, "F[2][1]"); + if(location != -1) + glUniform4fv(location, 2, F); + + location = glGetUniformLocation(program, "G[0].a.a"); + if(location != -1) + glUniform3f(location, 680.0f, 690.0f, 700.0f); + + location = glGetUniformLocation(program, "G[0].a.b"); + if(location != -1) + glUniform1f(location, 710.0f); + + float vals[] = { + // b[0] + 720.0f, 730.0f, 740.0f, 750.0f, + // b[1] + 760.0f, 770.0f, 780.0f, 790.0f, + // b[2] + 800.0f, 810.0f, 820.0f, 830.0f, + // b[3] + 840.0f, 850.0f, 860.0f, 870.0f, + }; + + location = glGetUniformLocation(program, "G[0].b"); + if(location != -1) + glUniform4fv(location, 4, vals); + + location = glGetUniformLocation(program, "G[0].c[0].a"); + if(location != -1) + glUniform3f(location, 880.0f, 890.0f, 900.0f); + + location = glGetUniformLocation(program, "G[0].c[0].b"); + if(location != -1) + glUniform1f(location, 910.0f); + + location = glGetUniformLocation(program, "G[0].c[1].a"); + if(location != -1) + glUniform3f(location, 920.0f, 930.0f, 940.0f); + + location = glGetUniformLocation(program, "G[0].c[1].b"); + if(location != -1) + glUniform1f(location, 950.0f); + + location = glGetUniformLocation(program, "G[0].c[2].a"); + if(location != -1) + glUniform3f(location, 960.0f, 970.0f, 980.0f); + + location = glGetUniformLocation(program, "G[0].c[2].b"); + if(location != -1) + glUniform1f(location, 990.0f); + + location = glGetUniformLocation(program, "G[0].c[3].a"); + if(location != -1) + glUniform3f(location, 1000.0f, 1010.0f, 1020.0f); + + location = glGetUniformLocation(program, "G[0].c[3].b"); + if(location != -1) + glUniform1f(location, 1030.0f); + + location = glGetUniformLocation(program, "G[1].a.a"); + if(location != -1) + glUniform3f(location, 1040.0f, 1050.0f, 1060.0f); + + location = glGetUniformLocation(program, "G[1].a.b"); + if(location != -1) + glUniform1f(location, 1070.0f); + + float vals2[] = { + // b[0] + 1080.0f, 1090.0f, 1100.0f, 1110.0f, + // b[1] + 1120.0f, 1130.0f, 1140.0f, 1150.0f, + // b[2] + 1160.0f, 1170.0f, 1180.0f, 1190.0f, + // b[3] + 1200.0f, 1210.0f, 1220.0f, 1230.0f, + }; + + location = glGetUniformLocation(program, "G[1].b"); + if(location != -1) + glUniform4fv(location, 4, vals2); + + location = glGetUniformLocation(program, "G[1].c[0].a"); + if(location != -1) + glUniform3f(location, 1240.0f, 1250.0f, 1260.0f); + + location = glGetUniformLocation(program, "G[1].c[0].b"); + if(location != -1) + glUniform1f(location, 1270.0f); + + location = glGetUniformLocation(program, "G[1].c[1].a"); + if(location != -1) + glUniform3f(location, 1280.0f, 1290.0f, 1300.0f); + + location = glGetUniformLocation(program, "G[1].c[1].b"); + if(location != -1) + glUniform1f(location, 1310.0f); + + location = glGetUniformLocation(program, "G[1].c[2].a"); + if(location != -1) + glUniform3f(location, 1320.0f, 1330.0f, 1340.0f); + + location = glGetUniformLocation(program, "G[1].c[2].b"); + if(location != -1) + glUniform1f(location, 1350.0f); + + location = glGetUniformLocation(program, "G[1].c[3].a"); + if(location != -1) + glUniform3f(location, 1360.0f, 1370.0f, 1380.0f); + + location = glGetUniformLocation(program, "G[1].c[3].b"); + if(location != -1) + glUniform1f(location, 1390.0f); + glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight)); glDrawArrays(GL_TRIANGLES, 0, 3); diff --git a/util/test/demos/vk/vk_cbuffer_zoo.cpp b/util/test/demos/vk/vk_cbuffer_zoo.cpp index 91b7559ca..82867c92e 100644 --- a/util/test/demos/vk/vk_cbuffer_zoo.cpp +++ b/util/test/demos/vk/vk_cbuffer_zoo.cpp @@ -259,6 +259,10 @@ layout(set = 0, binding = 0, std140) uniform constsbuf vec4 test; // {440, 441, 442, 443} }; +layout (constant_id = 0) const int A = 10; +layout (constant_id = 1) const float B = 0; +layout (constant_id = 3) const bool C = false; + void main() { Color = test + vec4(0.1f, 0.0f, 0.0f, 0.0f); @@ -480,6 +484,9 @@ float4 main() : SV_Target0 int main(int argc, char **argv) { + // needed for HLSL packing + devExts.push_back(VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME); + // initialise, create window, create context, etc if(!Init(argc, argv)) return 3; @@ -527,6 +534,24 @@ float4 main() : SV_Target0 CompileShaderModule(common + glslpixel, ShaderLang::glsl, ShaderStage::frag, "main"), }; + float data[2] = {20.0f, 0.0f}; + + // data[1] is a bool + VkBool32 btrue = true; + memcpy(&data[1], &btrue, sizeof(btrue)); + + VkSpecializationMapEntry specmap[2] = { + {1, 0, sizeof(float)}, {3, 4, sizeof(VkBool32)}, + }; + + VkSpecializationInfo spec = {}; + spec.mapEntryCount = 2; + spec.pMapEntries = specmap; + spec.dataSize = sizeof(data); + spec.pData = data; + + pipeCreateInfo.stages[1].pSpecializationInfo = &spec; + VkPipeline glslpipe = createGraphicsPipeline(pipeCreateInfo); pipeCreateInfo.stages[1] = diff --git a/util/test/rdtest/testcase.py b/util/test/rdtest/testcase.py index 242046399..48613dcd3 100644 --- a/util/test/rdtest/testcase.py +++ b/util/test/rdtest/testcase.py @@ -30,11 +30,23 @@ class ShaderVariableCheck: return self + def type(self, type_: rd.VarType): + if self.var.type != type_: + raise TestFailureException("Variable {} type mismatch, expected {} but got {}" + .format(self.var.name, type_, self.var.type)) + + return self + def value(self, value_: list): count = len(value_) - if self.var.value.fv[0:count] != value_: - raise TestFailureException("Variable {} value mismatch, expected {} but got {}" - .format(self.var.name, value_, self.var.value.fv[0:count])) + if isinstance(value_[0], float): + if self.var.value.fv[0:count] != value_: + raise TestFailureException("Float variable {} value mismatch, expected {} but got {}" + .format(self.var.name, value_, self.var.value.fv[0:count])) + else: + if self.var.value.iv[0:count] != value_: + raise TestFailureException("Int variable {} value mismatch, expected {} but got {}" + .format(self.var.name, value_, self.var.value.iv[0:count])) return self diff --git a/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py b/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py index 865b99155..24dbd0d88 100644 --- a/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py +++ b/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py @@ -331,4 +331,33 @@ class D3D12_CBuffer_Zoo(rdtest.TestCase): rdtest.log.success("Picked value is as expected") + cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0) + + var_check = rdtest.ConstantBufferChecker( + self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + pipe.GetShaderEntryPoint(stage), 1, + cbuf.resourceId, cbuf.byteOffset)) + + # float4 zero; + var_check.check('root_zero').rows(1).cols(4).value([0.0, 0.0, 0.0, 0.0]) + + # float4 a; + var_check.check('root_a').rows(1).cols(4).value([10.0, 20.0, 30.0, 40.0]) + + # float2 b; + var_check.check('root_b').rows(1).cols(2).value([50.0, 60.0]) + + # float2 c; + var_check.check('root_c').rows(1).cols(2).value([70.0, 80.0]) + + # float3_1 d; + var_check.check('root_d').rows(0).cols(0).structSize(2).members({ + 'a': lambda y: y.rows(1).cols(3).value([90.0, 100.0, 110.0]), + 'b': lambda y: y.rows(1).cols(1).value([120.0]), + }) + + var_check.done() + + rdtest.log.success("Root signature variables are as expected") + out.Shutdown() diff --git a/util/test/tests/GL/GL_CBuffer_Zoo.py b/util/test/tests/GL/GL_CBuffer_Zoo.py index 73b605fe7..8ffb27ca3 100644 --- a/util/test/tests/GL/GL_CBuffer_Zoo.py +++ b/util/test/tests/GL/GL_CBuffer_Zoo.py @@ -378,4 +378,112 @@ class GL_CBuffer_Zoo(rdtest.TestCase): rdtest.log.success("Picked value is as expected") + cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0) + + var_check = rdtest.ConstantBufferChecker( + self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + pipe.GetShaderEntryPoint(stage), 1, + cbuf.resourceId, cbuf.byteOffset)) + + # For bare uniforms we have partial data - only values used in the shader need to get assigned locations and + # some drivers are aggressive about stripping any others. Only uniforms with locations get upload values. + # Hence some of these checks don't check for the value - that means they might not be present + + # vec4 A; + var_check.check('A').cols(4).rows(1).value([10.0, 20.0, 30.0, 40.0]) + + # vec2 B; + var_check.check('B').cols(2).rows(1).value([50.0, 60.0]) + + # vec3 C; + var_check.check('C').cols(3).rows(1).value([70.0, 80.0, 90.0]) + + # mat2x3 D; + var_check.check('D').cols(2).rows(3).value([100.0, 130.0, + 110.0, 140.0, + 120.0, 150.0]) + + # float E[3]; + var_check.check('E').cols(0).rows(0).arraySize(3).members({ + 0: lambda x: x.cols(1).rows(1).value([160.0]), + 1: lambda x: x.cols(1).rows(1).value([170.0]), + 2: lambda x: x.cols(1).rows(1).value([180.0]), + }) + + # vec4 F[3][2][2]; + # Multidimensional arrays are represented as structs with N members + # Due to lacking reflection, we only access F[1][0][1] so that's the only one present + var_check.check('F').cols(0).rows(0).structSize(1).members({ + '[1]': lambda x: x.cols(0).rows(0).structSize(1).members({ + '[0]': lambda x: x.cols(0).rows(0).arraySize(2).members({ + 0: lambda x: x.cols(4).rows(1).value([350.0, 360.0, 370.0, 380.0]), + 1: lambda x: x.cols(4).rows(1).value([390.0, 400.0, 410.0, 420.0]), + }), + }), + }) + + # struct vec3_1 { vec3 a; float b; }; + # struct nested { vec3_1 a; vec4 b[4]; vec3_1 c[4]; }; + # nested G[2]; + # Due to lacking reflection, we don't know that G[x].a.a exists, as we only reference G[n].a.b + # Similarly we don't know that G[x].c[y].b exists + var_check.check('G').cols(0).rows(0).arraySize(2).members({ + # G[0] + 0: lambda s: s.cols(0).rows(0).structSize(3).members({ + 'a': lambda x: x.cols(0).rows(0).structSize(1).members({ + 'b': lambda y: y.cols(1).rows(1).value([710.0]), + }), + 'b': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda y: y.cols(4).rows(1), + 1: lambda y: y.cols(4).rows(1), + 2: lambda y: y.cols(4).rows(1), + 3: lambda y: y.cols(4).rows(1), + }), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + 1: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + 2: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + 3: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + }), + }), + # G[1] + 1: lambda s: s.cols(0).rows(0).structSize(3).members({ + 'a': lambda x: x.cols(0).rows(0).structSize(1).members({ + 'b': lambda y: y.cols(1).rows(1).value([1070.0]), + }), + 'b': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda y: y.cols(4).rows(1).value([1080.0, 1090.0, 1100.0, 1110.0]), + 1: lambda y: y.cols(4).rows(1).value([1120.0, 1130.0, 1140.0, 1150.0]), + 2: lambda y: y.cols(4).rows(1).value([1160.0, 1170.0, 1180.0, 1190.0]), + 3: lambda y: y.cols(4).rows(1).value([1200.0, 1210.0, 1220.0, 1230.0]), + }), + 'c': lambda x: x.cols(0).rows(0).arraySize(4).members({ + 0: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + 1: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + 2: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1), + }), + 3: lambda y: y.cols(0).rows(0).structSize(1).members({ + 'a': lambda z: z.cols(3).rows(1).value([1360.0, 1370.0, 1380.0]), + }), + }), + }), + }) + + var_check.done() + + rdtest.log.success("Bare uniform variables are as expected") + out.Shutdown() diff --git a/util/test/tests/Vulkan/VK_CBuffer_Zoo.py b/util/test/tests/Vulkan/VK_CBuffer_Zoo.py index 190840cc7..15e2c7dd2 100644 --- a/util/test/tests/Vulkan/VK_CBuffer_Zoo.py +++ b/util/test/tests/Vulkan/VK_CBuffer_Zoo.py @@ -320,6 +320,30 @@ class VK_CBuffer_Zoo(rdtest.TestCase): rdtest.log.success("GLSL picked value is as expected") + # Check the specialization constants + cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0) + + var_check = rdtest.ConstantBufferChecker( + self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + pipe.GetShaderEntryPoint(stage), 1, + cbuf.resourceId, cbuf.byteOffset)) + + # int A; + # Default value 10, untouched + var_check.check('A').type(rd.VarType.SInt).rows(1).cols(1).value([10]) + + # float B; + # Value 20 from spec constants + var_check.check('B').type(rd.VarType.Float).rows(1).cols(1).value([20.0]) + + # bool C; + # Value True from spec constants + var_check.check('C').type(rd.VarType.UInt).rows(1).cols(1).value([1]) + + var_check.done() + + rdtest.log.success("Specialization constants are as expected") + # Move to the HLSL draw draw = draw.next