From af607f7f807bb73bd527f0c97257eed17233027d Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Tue, 16 Mar 2021 17:30:04 +0000 Subject: [PATCH] Port the MinimumBufferSizeValidationTests to WGSL. Bug: dawn:572 Change-Id: I24d5e37711aecdef582c33cb4ad55d0e5fb30004 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/44860 Auto-Submit: Corentin Wallez Commit-Queue: Ben Clayton Reviewed-by: Ben Clayton --- .../MinimumBufferSizeValidationTests.cpp | 237 ++++++++---------- 1 file changed, 104 insertions(+), 133 deletions(-) diff --git a/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp b/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp index 4b09d2e501..71e0914396 100644 --- a/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp +++ b/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp @@ -22,7 +22,7 @@ namespace { // Helper for describing bindings throughout the tests struct BindingDescriptor { - uint32_t set; + uint32_t group; uint32_t binding; std::string text; uint64_t size; @@ -66,62 +66,52 @@ namespace { [&](const std::vector& sizes) { func(sizes, true); }); } - // Convert binding type to a glsl string - std::string BufferBindingTypeToStr(wgpu::BufferBindingType type) { - switch (type) { - case wgpu::BufferBindingType::Uniform: - return "uniform"; - case wgpu::BufferBindingType::Storage: - return "buffer"; - case wgpu::BufferBindingType::ReadOnlyStorage: - return "readonly buffer"; - default: - UNREACHABLE(); - return ""; - } - } - // Creates a bind group with given bindings for shader text - std::string GenerateBindingString(const std::string& layout, - const std::vector& bindings) { + std::string GenerateBindingString(const std::vector& bindings) { std::ostringstream ostream; - size_t ctr = 0; + size_t index = 0; for (const BindingDescriptor& b : bindings) { - ostream << "layout(" << layout << ", set = " << b.set << ", binding = " << b.binding - << ") " << BufferBindingTypeToStr(b.type) << " b" << ctr++ << "{\n" - << b.text << ";\n};\n"; + ostream << "[[block]] struct S" << index << " { " << b.text << "};\n"; + ostream << "[[group(" << b.group << "), binding(" << b.binding << ")]] "; + switch (b.type) { + case wgpu::BufferBindingType::Uniform: + ostream << "var b" << index << " : S" << index << ";\n"; + break; + case wgpu::BufferBindingType::Storage: + ostream << "var b" << index << " : [[access(read_write)]] S" << index + << ";\n"; + break; + case wgpu::BufferBindingType::ReadOnlyStorage: + ostream << "var b" << index << " : [[access(read)]] S" << index + << ";\n"; + break; + default: + UNREACHABLE(); + } + index++; } return ostream.str(); } // Used for adding custom types available throughout the tests - static const std::string kStructs = "struct ThreeFloats{float f1; float f2; float f3;};\n"; + static const std::string kStructs = "struct ThreeFloats {f1 : f32; f2 : f32; f3 : f32;};\n"; // Creates a compute shader with given bindings - std::string CreateComputeShaderWithBindings(const std::string& layoutType, - const std::vector& bindings) { - return R"( - #version 450 - layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; - )" + - kStructs + GenerateBindingString(layoutType, bindings) + "void main() {}"; + std::string CreateComputeShaderWithBindings(const std::vector& bindings) { + return kStructs + GenerateBindingString(bindings) + + "[[stage(compute), workgroup_size(1,1,1)]] fn main() -> void {}"; } // Creates a vertex shader with given bindings - std::string CreateVertexShaderWithBindings(const std::string& layoutType, - const std::vector& bindings) { - return "#version 450\n" + kStructs + GenerateBindingString(layoutType, bindings) + - "void main() {}"; + std::string CreateVertexShaderWithBindings(const std::vector& bindings) { + return kStructs + GenerateBindingString(bindings) + + "[[stage(vertex)]] fn main() -> void {}"; } // Creates a fragment shader with given bindings - std::string CreateFragmentShaderWithBindings(const std::string& layoutType, - const std::vector& bindings) { - return R"( - #version 450 - layout(location = 0) out vec4 fragColor; - )" + - kStructs + GenerateBindingString(layoutType, bindings) + "void main() {}"; + std::string CreateFragmentShaderWithBindings(const std::vector& bindings) { + return kStructs + GenerateBindingString(bindings) + + "[[stage(fragment)]] fn main() -> void {}"; } // Concatenates vectors containing BindingDescriptor @@ -152,8 +142,7 @@ class MinBufferSizeTestsBase : public ValidationTest { // Creates compute pipeline given a layout and shader wgpu::ComputePipeline CreateComputePipeline(const std::vector& layouts, const std::string& shader) { - wgpu::ShaderModule csModule = - utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader.c_str()); + wgpu::ShaderModule csModule = utils::CreateShaderModuleFromWGSL(device, shader.c_str()); wgpu::ComputePipelineDescriptor csDesc; csDesc.layout = nullptr; @@ -178,11 +167,10 @@ class MinBufferSizeTestsBase : public ValidationTest { wgpu::RenderPipeline CreateRenderPipeline(const std::vector& layouts, const std::string& vertexShader, const std::string& fragShader) { - wgpu::ShaderModule vsModule = utils::CreateShaderModule( - device, utils::SingleShaderStage::Vertex, vertexShader.c_str()); + wgpu::ShaderModule vsModule = + utils::CreateShaderModuleFromWGSL(device, vertexShader.c_str()); - wgpu::ShaderModule fsModule = utils::CreateShaderModule( - device, utils::SingleShaderStage::Fragment, fragShader.c_str()); + wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, fragShader.c_str()); utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); pipelineDescriptor.vertexStage.module = vsModule; @@ -316,11 +304,12 @@ class MinBufferSizePipelineCreationTests : public MinBufferSizeTestsBase {}; // Pipeline can be created if minimum buffer size in layout is specified as 0 TEST_F(MinBufferSizePipelineCreationTests, ZeroMinBufferSize) { - std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + std::vector bindings = {{0, 0, "a : f32; b : f32;", 8}, + {0, 1, "c : f32;", 4}}; - std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); - std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); - std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + std::string computeShader = CreateComputeShaderWithBindings(bindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(bindings); wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {0, 0}); CreateRenderPipeline({layout}, vertexShader, fragShader); @@ -329,11 +318,12 @@ TEST_F(MinBufferSizePipelineCreationTests, ZeroMinBufferSize) { // Fail if layout given has non-zero minimum sizes smaller than shader requirements TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmall) { - std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + std::vector bindings = {{0, 0, "a : f32; b : f32;", 8}, + {0, 1, "c : f32;", 4}}; - std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); - std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); - std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + std::string computeShader = CreateComputeShaderWithBindings(bindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(bindings); CheckSizeBounds({8, 4}, [&](const std::vector& sizes, bool expectation) { wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, sizes); @@ -349,17 +339,17 @@ TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmall) { // Fail if layout given has non-zero minimum sizes smaller than shader requirements TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmallMultipleGroups) { - std::vector bg0Bindings = {{0, 0, "float a; float b", 8}, - {0, 1, "float c", 4}}; - std::vector bg1Bindings = {{1, 0, "float d; float e; float f", 12}, - {1, 1, "mat2 g", 32}}; + std::vector bg0Bindings = {{0, 0, "a : f32; b : f32;", 8}, + {0, 1, "c : f32;", 4}}; + std::vector bg1Bindings = {{1, 0, "d : f32; e : f32; f : f32;", 12}, + {1, 1, "g : mat2x2;", 16}}; std::vector bindings = CombineBindings({bg0Bindings, bg1Bindings}); - std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); - std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); - std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + std::string computeShader = CreateComputeShaderWithBindings(bindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(bindings); - CheckSizeBounds({8, 4, 12, 32}, [&](const std::vector& sizes, bool expectation) { + CheckSizeBounds({8, 4, 12, 16}, [&](const std::vector& sizes, bool expectation) { wgpu::BindGroupLayout layout0 = CreateBindGroupLayout(bg0Bindings, {sizes[0], sizes[1]}); wgpu::BindGroupLayout layout1 = CreateBindGroupLayout(bg1Bindings, {sizes[2], sizes[3]}); if (expectation) { @@ -377,7 +367,8 @@ class MinBufferSizeBindGroupCreationTests : public MinBufferSizeTestsBase {}; // Fail if a binding is smaller than minimum buffer size TEST_F(MinBufferSizeBindGroupCreationTests, BindingTooSmall) { - std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + std::vector bindings = {{0, 0, "a : f32; b : f32;", 8}, + {0, 1, "c : f32;", 4}}; wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {8, 4}); CheckSizeBounds({8, 4}, [&](const std::vector& sizes, bool expectation) { @@ -410,11 +401,12 @@ class MinBufferSizeDrawTimeValidationTests : public MinBufferSizeTestsBase {}; // Fail if binding sizes are too small at draw time TEST_F(MinBufferSizeDrawTimeValidationTests, ZeroMinSizeAndTooSmallBinding) { - std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + std::vector bindings = {{0, 0, "a : f32; b : f32;", 8}, + {0, 1, "c : f32;", 4}}; - std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); - std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); - std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + std::string computeShader = CreateComputeShaderWithBindings(bindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(bindings); wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {0, 0}); @@ -430,13 +422,13 @@ TEST_F(MinBufferSizeDrawTimeValidationTests, ZeroMinSizeAndTooSmallBinding) { // Draw time validation works for non-contiguous bindings TEST_F(MinBufferSizeDrawTimeValidationTests, UnorderedBindings) { - std::vector bindings = {{0, 2, "float a; float b", 8}, - {0, 0, "float c", 4}, - {0, 4, "float d; float e; float f", 12}}; + std::vector bindings = {{0, 2, "a : f32; b : f32;", 8}, + {0, 0, "c : f32;", 4}, + {0, 4, "d : f32; e : f32; f : f32;", 12}}; - std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); - std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); - std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + std::string computeShader = CreateComputeShaderWithBindings(bindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(bindings); wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {0, 0, 0}); @@ -452,15 +444,15 @@ TEST_F(MinBufferSizeDrawTimeValidationTests, UnorderedBindings) { // Draw time validation works for multiple bind groups TEST_F(MinBufferSizeDrawTimeValidationTests, MultipleGroups) { - std::vector bg0Bindings = {{0, 0, "float a; float b", 8}, - {0, 1, "float c", 4}}; - std::vector bg1Bindings = {{1, 0, "float d; float e; float f", 12}, - {1, 1, "mat2 g", 32}}; + std::vector bg0Bindings = {{0, 0, "a : f32; b : f32;", 8}, + {0, 1, "c : f32;", 4}}; + std::vector bg1Bindings = {{1, 0, "d : f32; e : f32; f : f32;", 12}, + {1, 1, "g : mat2x2;", 16}}; std::vector bindings = CombineBindings({bg0Bindings, bg1Bindings}); - std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); - std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); - std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + std::string computeShader = CreateComputeShaderWithBindings(bindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(bindings); wgpu::BindGroupLayout layout0 = CreateBindGroupLayout(bg0Bindings, {0, 0}); wgpu::BindGroupLayout layout1 = CreateBindGroupLayout(bg1Bindings, {0, 0}); @@ -470,7 +462,7 @@ TEST_F(MinBufferSizeDrawTimeValidationTests, MultipleGroups) { wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({layout0, layout1}, vertexShader, fragShader); - CheckSizeBounds({8, 4, 12, 32}, [&](const std::vector& sizes, bool expectation) { + CheckSizeBounds({8, 4, 12, 16}, [&](const std::vector& sizes, bool expectation) { wgpu::BindGroup bindGroup0 = CreateBindGroup(layout0, bg0Bindings, {sizes[0], sizes[1]}); wgpu::BindGroup bindGroup1 = CreateBindGroup(layout0, bg0Bindings, {sizes[2], sizes[3]}); TestDispatch(computePipeline, {bindGroup0, bindGroup1}, expectation); @@ -502,12 +494,11 @@ class MinBufferSizeDefaultLayoutTests : public MinBufferSizeTestsBase { // Constructs shaders with given layout type and bindings, checking defaulted sizes match sizes // in |bindings| void CheckShaderBindingSizeReflection( - const std::string& layoutType, std::initializer_list> bindings) { std::vector combinedBindings = CombineBindings(bindings); - std::string computeShader = CreateComputeShaderWithBindings(layoutType, combinedBindings); - std::string vertexShader = CreateVertexShaderWithBindings(layoutType, {}); - std::string fragShader = CreateFragmentShaderWithBindings(layoutType, combinedBindings); + std::string computeShader = CreateComputeShaderWithBindings(combinedBindings); + std::string vertexShader = CreateVertexShaderWithBindings({}); + std::string fragShader = CreateFragmentShaderWithBindings(combinedBindings); size_t i = 0; for (const std::vector& b : bindings) { @@ -522,67 +513,47 @@ class MinBufferSizeDefaultLayoutTests : public MinBufferSizeTestsBase { } }; -// Various bindings in std140 have correct minimum size reflection -TEST_F(MinBufferSizeDefaultLayoutTests, std140Inferred) { +// Test the minimum size computations for various WGSL types. +TEST_F(MinBufferSizeDefaultLayoutTests, DefaultLayoutVariousWGSLTypes) { CheckShaderBindingSizeReflection( - "std140", {{{0, 0, "float a", 4}, {0, 1, "float b[]", 16}, {0, 2, "mat2 c", 32}}}); - CheckShaderBindingSizeReflection("std140", {{{0, 3, "int d; float e[]", 32}, - {0, 4, "ThreeFloats f", 12}, - {0, 5, "ThreeFloats g[]", 16}}}); + {{{0, 0, "a : f32;", 4}, {0, 1, "b : array;", 4}, {0, 2, "c : mat2x2;", 16}}}); + CheckShaderBindingSizeReflection({{{0, 3, "d : u32; e : array;", 8}, + {0, 4, "f : ThreeFloats;", 12}, + {0, 5, "g : array;", 12}}}); } -// Various bindings in std430 have correct minimum size reflection -TEST_F(MinBufferSizeDefaultLayoutTests, std430Inferred) { +// Test the minimum size computations for various buffer binding types. +TEST_F(MinBufferSizeDefaultLayoutTests, DefaultLayoutVariousBindingTypes) { CheckShaderBindingSizeReflection( - "std430", {{{0, 0, "float a", 4}, {0, 1, "float b[]", 4}, {0, 2, "mat2 c", 16}}}); - CheckShaderBindingSizeReflection("std430", {{{0, 3, "int d; float e[]", 8}, - {0, 4, "ThreeFloats f", 12}, - {0, 5, "ThreeFloats g[]", 12}}}); + {{{0, 0, "a : f32;", 4, wgpu::BufferBindingType::Uniform}, + {0, 1, "a : f32; b : f32;", 8, wgpu::BufferBindingType::Storage}, + {0, 2, "a : f32; b : f32; c: f32;", 12, wgpu::BufferBindingType::ReadOnlyStorage}}}); } -// Sizes are inferred for all binding types with std140 layout -TEST_F(MinBufferSizeDefaultLayoutTests, std140BindingTypes) { +// Test the minimum size computations works with multiple bind groups. +TEST_F(MinBufferSizeDefaultLayoutTests, MultipleBindGroups) { CheckShaderBindingSizeReflection( - "std140", {{{0, 0, "int d; float e[]", 32, wgpu::BufferBindingType::Uniform}, - {0, 1, "ThreeFloats f", 12, wgpu::BufferBindingType::Storage}, - {0, 2, "ThreeFloats g[]", 16, wgpu::BufferBindingType::ReadOnlyStorage}}}); + {{{0, 0, "a : f32;", 4, wgpu::BufferBindingType::Uniform}}, + {{1, 0, "a : f32; b : f32;", 8, wgpu::BufferBindingType::Storage}}, + {{2, 0, "a : f32; b : f32; c : f32;", 12, wgpu::BufferBindingType::ReadOnlyStorage}}}); } -// Sizes are inferred for all binding types with std430 layout -TEST_F(MinBufferSizeDefaultLayoutTests, std430BindingTypes) { - CheckShaderBindingSizeReflection( - "std430", {{{0, 0, "float a", 4, wgpu::BufferBindingType::Storage}, - {0, 1, "ThreeFloats b[]", 12, wgpu::BufferBindingType::ReadOnlyStorage}}}); +// Test the minimum size computations with manual size/align/stride decorations. +TEST_F(MinBufferSizeDefaultLayoutTests, NonDefaultLayout) { + CheckShaderBindingSizeReflection({{{0, 0, "[[size(256)]] a : u32; b : u32;", 260}, + {0, 1, "c : u32; [[align(16)]] d : u32;", 20}, + {0, 2, "d : [[stride(40)]] array;", 120}, + {0, 3, "e : [[stride(40)]] array;", 40}}}); } -// Various bindings have correct size across multiple groups -TEST_F(MinBufferSizeDefaultLayoutTests, std140MultipleBindGroups) { - CheckShaderBindingSizeReflection("std140", - {{{0, 0, "float a", 4}, {0, 1, "float b[]", 16}}, - {{1, 2, "mat2 c", 32}, {1, 3, "int d; float e[]", 32}}}); - CheckShaderBindingSizeReflection( - "std140", {{{0, 4, "ThreeFloats f", 12}, {0, 1, "float b[]", 16}}, - {{1, 5, "ThreeFloats g[]", 16}, {1, 3, "int d; float e[]", 32}}}); -} - -// Various bindings have correct size across multiple groups -TEST_F(MinBufferSizeDefaultLayoutTests, std430MultipleBindGroups) { - CheckShaderBindingSizeReflection("std430", - {{{0, 0, "float a", 4}, {0, 1, "float b[]", 4}}, - {{1, 2, "mat2 c", 16}, {1, 3, "int d; float e[]", 8}}}); - CheckShaderBindingSizeReflection( - "std430", {{{0, 4, "ThreeFloats f", 12}, {0, 1, "float b[]", 4}}, - {{1, 5, "ThreeFloats g[]", 12}, {1, 3, "int d; float e[]", 8}}}); -} - -// Minimum size should be the max requirement of both vertex and fragment stages +// Minimum size should be the max requirement of both vertex and fragment stages. TEST_F(MinBufferSizeDefaultLayoutTests, RenderPassConsidersBothStages) { std::string vertexShader = CreateVertexShaderWithBindings( - "std140", {{0, 0, "float a", 4, wgpu::BufferBindingType::Uniform}, - {0, 1, "float b[]", 16, wgpu::BufferBindingType::Uniform}}); + {{0, 0, "a : f32;", 4, wgpu::BufferBindingType::Uniform}, + {0, 1, "b : vec4;", 16, wgpu::BufferBindingType::Uniform}}); std::string fragShader = CreateFragmentShaderWithBindings( - "std140", {{0, 0, "float a; float b", 8, wgpu::BufferBindingType::Uniform}, - {0, 1, "float c; float d", 8, wgpu::BufferBindingType::Uniform}}); + {{0, 0, "a : f32; b : f32;", 8, wgpu::BufferBindingType::Uniform}, + {0, 1, "c : f32; d : f32;", 8, wgpu::BufferBindingType::Uniform}}); wgpu::BindGroupLayout renderLayout = GetBGLFromRenderShaders(vertexShader, fragShader, 0);