From 612a63abe1b120d4dfbdd9e78009646e2b014b2e Mon Sep 17 00:00:00 2001 From: Yunchao He Date: Mon, 18 Nov 2019 04:28:24 +0000 Subject: [PATCH] Add memory synchronization tests - storage to uniform sync This CL adds end2end tests for memory synchronization tests for buffer. It adds a few tests that write into storage buffer in compute pass, then read via uniform binding from the same buffer in render pass. BUG=dawn:275 Change-Id: Ic98a10aab4cdcddecd60662438d4b8bdd34fafbc Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13580 Commit-Queue: Yunchao He Reviewed-by: Austin Eng Reviewed-by: Corentin Wallez --- BUILD.gn | 1 + .../end2end/GpuMemorySynchronizationTests.cpp | 221 ++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 src/tests/end2end/GpuMemorySynchronizationTests.cpp diff --git a/BUILD.gn b/BUILD.gn index 2da23a10c4..fb11705a0b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -912,6 +912,7 @@ source_set("dawn_end2end_tests_sources") { "src/tests/end2end/DrawTests.cpp", "src/tests/end2end/DynamicBufferOffsetTests.cpp", "src/tests/end2end/FenceTests.cpp", + "src/tests/end2end/GpuMemorySynchronizationTests.cpp", "src/tests/end2end/IndexFormatTests.cpp", "src/tests/end2end/MultisampledRenderingTests.cpp", "src/tests/end2end/NonzeroTextureCreationTests.cpp", diff --git a/src/tests/end2end/GpuMemorySynchronizationTests.cpp b/src/tests/end2end/GpuMemorySynchronizationTests.cpp new file mode 100644 index 0000000000..15ab7eab3e --- /dev/null +++ b/src/tests/end2end/GpuMemorySynchronizationTests.cpp @@ -0,0 +1,221 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "tests/DawnTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class StorageToUniformSyncTests : public DawnTest { + protected: + void CreateBuffer() { + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = sizeof(float); + bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform; + mBuffer = device.CreateBuffer(&bufferDesc); + } + + std::tuple CreatePipelineAndBindGroupForCompute() { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 0) buffer Data { + float a; + } data; + void main() { + data.a = 1.0; + })"); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, + }); + wgpu::PipelineLayout pipelineLayout0 = utils::MakeBasicPipelineLayout(device, &bgl); + + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.layout = pipelineLayout0; + cpDesc.computeStage.module = csModule; + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&cpDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, bgl, {{0, mBuffer, 0, sizeof(float)}}); + return std::make_tuple(pipeline, bindGroup); + } + + std::tuple CreatePipelineAndBindGroupForRender( + wgpu::TextureFormat colorFormat) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 0) uniform Contents{ + float color; + }; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(color, 0.f, 0.f, 1.f); + })"); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}, + }); + wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &bgl); + + utils::ComboRenderPipelineDescriptor rpDesc(device); + rpDesc.layout = pipelineLayout; + rpDesc.vertexStage.module = vsModule; + rpDesc.cFragmentStage.module = fsModule; + rpDesc.primitiveTopology = wgpu::PrimitiveTopology::PointList; + rpDesc.cColorStates[0].format = colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, bgl, {{0, mBuffer, 0, sizeof(float)}}); + return std::make_tuple(pipeline, bindGroup); + } + + wgpu::Buffer mBuffer; +}; + +// Write into a storage buffer in compute pass in a command buffer. Then read that data in a render +// pass. The two passes use the same command buffer. +TEST_P(StorageToUniformSyncTests, ReadAfterWriteWithSameCommandBuffer) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::ComputePipeline compute; + wgpu::BindGroup computeBindGroup; + std::tie(compute, computeBindGroup) = CreatePipelineAndBindGroupForCompute(); + wgpu::RenderPipeline render; + wgpu::BindGroup renderBindGroup; + std::tie(render, renderBindGroup) = CreatePipelineAndBindGroupForRender(renderPass.colorFormat); + + // Write data into a storage buffer in compute pass. + wgpu::CommandEncoder encoder0 = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder0.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, computeBindGroup); + pass0.Dispatch(1, 1, 1); + pass0.EndPass(); + + // Read that data in render pass. + wgpu::RenderPassEncoder pass1 = encoder0.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, renderBindGroup); + pass1.Draw(1, 1, 0, 0); + pass1.EndPass(); + + wgpu::CommandBuffer commands = encoder0.Finish(); + queue.Submit(1, &commands); + + // Verify the rendering result. + EXPECT_PIXEL_RGBA8_EQ(kRed, renderPass.color, 0, 0); +} + +// Write into a storage buffer in compute pass in a command buffer. Then read that data in a render +// pass. The two passes use the different command buffers. The command buffers are submitted to the +// queue in one shot. +TEST_P(StorageToUniformSyncTests, ReadAfterWriteWithDifferentCommandBuffers) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::ComputePipeline compute; + wgpu::BindGroup computeBindGroup; + std::tie(compute, computeBindGroup) = CreatePipelineAndBindGroupForCompute(); + wgpu::RenderPipeline render; + wgpu::BindGroup renderBindGroup; + std::tie(render, renderBindGroup) = CreatePipelineAndBindGroupForRender(renderPass.colorFormat); + + // Write data into a storage buffer in compute pass. + wgpu::CommandBuffer cb[2]; + wgpu::CommandEncoder encoder0 = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder0.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, computeBindGroup); + pass0.Dispatch(1, 1, 1); + pass0.EndPass(); + cb[0] = encoder0.Finish(); + + // Read that data in render pass. + wgpu::CommandEncoder encoder1 = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass1 = encoder1.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, renderBindGroup); + pass1.Draw(1, 1, 0, 0); + pass1.EndPass(); + + cb[1] = encoder1.Finish(); + queue.Submit(2, cb); + + // Verify the rendering result. + EXPECT_PIXEL_RGBA8_EQ(kRed, renderPass.color, 0, 0); +} + +// Write into a storage buffer in compute pass in a command buffer. Then read that data in a render +// pass. The two passes use the different command buffers. The command buffers are submitted to the +// queue separately. +TEST_P(StorageToUniformSyncTests, ReadAfterWriteWithDifferentQueueSubmits) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::ComputePipeline compute; + wgpu::BindGroup computeBindGroup; + std::tie(compute, computeBindGroup) = CreatePipelineAndBindGroupForCompute(); + wgpu::RenderPipeline render; + wgpu::BindGroup renderBindGroup; + std::tie(render, renderBindGroup) = CreatePipelineAndBindGroupForRender(renderPass.colorFormat); + + // Write data into a storage buffer in compute pass. + wgpu::CommandBuffer cb[2]; + wgpu::CommandEncoder encoder0 = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder0.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, computeBindGroup); + pass0.Dispatch(1, 1, 1); + pass0.EndPass(); + cb[0] = encoder0.Finish(); + queue.Submit(1, &cb[0]); + + // Read that data in render pass. + wgpu::CommandEncoder encoder1 = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass1 = encoder1.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, renderBindGroup); + pass1.Draw(1, 1, 0, 0); + pass1.EndPass(); + + cb[1] = encoder1.Finish(); + queue.Submit(1, &cb[1]); + + // Verify the rendering result. + EXPECT_PIXEL_RGBA8_EQ(kRed, renderPass.color, 0, 0); +} + +DAWN_INSTANTIATE_TEST(StorageToUniformSyncTests, + D3D12Backend, + MetalBackend, + OpenGLBackend, + VulkanBackend);