Add memory synchronization tests - data deps chain among passes
This CL adds end2end tests for memory synchronization tests for buffer.
It adds a few tests that iterate data read-add-write operations a few
times in buffer for compute and render respectively, and verifies
that data dependency among interations is well synchronized.
BUG=dawn:275
Change-Id: Idfe627e90de795d664ee64787d5c5d2bfcee676b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13700
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/tests/end2end/GpuMemorySynchronizationTests.cpp b/src/tests/end2end/GpuMemorySynchronizationTests.cpp
index 6616aff..134a554 100644
--- a/src/tests/end2end/GpuMemorySynchronizationTests.cpp
+++ b/src/tests/end2end/GpuMemorySynchronizationTests.cpp
@@ -19,6 +19,141 @@
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/WGPUHelpers.h"
+class GpuMemorySyncTests : public DawnTest {
+ protected:
+ wgpu::Buffer CreateBuffer() {
+ wgpu::BufferDescriptor srcDesc;
+ srcDesc.size = 4;
+ srcDesc.usage =
+ wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Storage;
+ wgpu::Buffer buffer = device.CreateBuffer(&srcDesc);
+
+ int myData = 0;
+ buffer.SetSubData(0, sizeof(myData), &myData);
+ return buffer;
+ }
+};
+
+// Clear storage buffer with zero. Then read data, add one, and write the result to storage buffer
+// in compute pass. Iterate this read-add-write steps per compute pass a few time. The successive
+// iteration reads the result in buffer from last iteration, which makes the iterations a data
+// dependency chain. The test verifies that data in buffer among iterations in compute passes is
+// correctly synchronized.
+TEST_P(GpuMemorySyncTests, ComputePass) {
+ // Create pipeline, bind group, and buffer for compute pass.
+ wgpu::ShaderModule csModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
+ #version 450
+ layout(std140, set = 0, binding = 0) buffer Data {
+ int a;
+ } data;
+ void main() {
+ data.a += 1;
+ })");
+
+ wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
+ device, {
+ {0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer},
+ });
+ wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &bgl);
+
+ wgpu::ComputePipelineDescriptor cpDesc;
+ cpDesc.layout = pipelineLayout;
+ cpDesc.computeStage.module = csModule;
+ cpDesc.computeStage.entryPoint = "main";
+ wgpu::ComputePipeline compute = device.CreateComputePipeline(&cpDesc);
+
+ wgpu::Buffer buffer = CreateBuffer();
+
+ wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+
+ // Iterate the read-add-write operations in compute pass a few times.
+ int iteration = 3;
+ for (int i = 0; i < iteration; ++i) {
+ wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+ pass.SetPipeline(compute);
+ pass.SetBindGroup(0, bindGroup);
+ pass.Dispatch(1, 1, 1);
+ pass.EndPass();
+ }
+
+ // Verify the result.
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_BUFFER_U32_EQ(iteration, buffer, 0);
+}
+
+// Clear storage buffer with zero. Then read data, add one, and write the result to storage buffer
+// in render pass. Iterate this read-add-write steps per render pass a few time. The successive
+// iteration reads the result in buffer from last iteration, which makes the iterations a data
+// dependency chain. In addition, color output by fragment shader depends on the data in storage
+// buffer, so we can check color in render target to verify that data in buffer among iterations in
+// render passes is correctly synchronized.
+TEST_P(GpuMemorySyncTests, RenderPass) {
+ // Create pipeline, bind group, and buffer for render pass.
+ 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) buffer Data {
+ int i;
+ } data;
+ layout(location = 0) out vec4 fragColor;
+ void main() {
+ data.i += 1;
+ fragColor = vec4(data.i / 255.f, 0.f, 0.f, 1.f);
+ })");
+
+ wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
+ device, {
+ {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer},
+ });
+ wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &bgl);
+
+ utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
+
+ utils::ComboRenderPipelineDescriptor rpDesc(device);
+ rpDesc.layout = pipelineLayout;
+ rpDesc.vertexStage.module = vsModule;
+ rpDesc.cFragmentStage.module = fsModule;
+ rpDesc.primitiveTopology = wgpu::PrimitiveTopology::PointList;
+ rpDesc.cColorStates[0].format = renderPass.colorFormat;
+
+ wgpu::RenderPipeline render = device.CreateRenderPipeline(&rpDesc);
+
+ wgpu::Buffer buffer = CreateBuffer();
+
+ wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+
+ // Iterate the read-add-write operations in render pass a few times.
+ int iteration = 3;
+ for (int i = 0; i < iteration; ++i) {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.SetPipeline(render);
+ pass.SetBindGroup(0, bindGroup);
+ pass.Draw(1, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ // Verify the result.
+ EXPECT_PIXEL_RGBA8_EQ(RGBA8(iteration, 0, 0, 255), renderPass.color, 0, 0);
+}
+
class StorageToUniformSyncTests : public DawnTest {
protected:
void CreateBuffer() {
@@ -214,6 +349,8 @@
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0);
}
+DAWN_INSTANTIATE_TEST(GpuMemorySyncTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
+
DAWN_INSTANTIATE_TEST(StorageToUniformSyncTests,
D3D12Backend,
MetalBackend,