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 <yunchao.he@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 2da23a1..fb11705 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -912,6 +912,7 @@
"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 0000000..15ab7ea
--- /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<wgpu::ComputePipeline, wgpu::BindGroup> 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<wgpu::RenderPipeline, wgpu::BindGroup> 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);