dawn-cmake/src/tests/end2end/ComputeDispatchTests.cpp
Brandon Jones 0d50a2c770 ComputePipelineDescriptor.computeStage->compute
Deprecates the computeStage member of the descriptor in favor of compute
as described by the spec. In order to support both variants without
breaking backwards compatibility some code had to be manually added to
the wire client to copy from the deprecated member to the new one and
visa versa.

Change-Id: I9d5c2fc9c446c927c5792c9af9ed56c90060b65b
Bug: dawn:800
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/53884
Commit-Queue: Brandon Jones <bajones@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
2021-06-09 18:07:32 +00:00

210 lines
7.3 KiB
C++

// 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 "tests/DawnTest.h"
#include "utils/WGPUHelpers.h"
#include <initializer_list>
constexpr static std::initializer_list<uint32_t> kSentinelData{0, 0, 0};
class ComputeDispatchTests : public DawnTest {
protected:
void SetUp() override {
DawnTest::SetUp();
// Write workgroup number into the output buffer if we saw the biggest dispatch
// This is a workaround since D3D12 doesn't have gl_NumWorkGroups
// To make sure the dispatch was not called, write maximum u32 value for 0 dispatches
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
[[block]] struct InputBuf {
expectedDispatch : vec3<u32>;
};
[[block]] struct OutputBuf {
workGroups : vec3<u32>;
};
[[group(0), binding(0)]] var<uniform> input : InputBuf;
[[group(0), binding(1)]] var<storage, read_write> output : OutputBuf;
[[stage(compute), workgroup_size(1, 1, 1)]]
fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
let dispatch : vec3<u32> = input.expectedDispatch;
if (dispatch.x == 0u || dispatch.y == 0u || dispatch.z == 0u) {
output.workGroups = vec3<u32>(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
return;
}
if (all(GlobalInvocationID == dispatch - vec3<u32>(1u, 1u, 1u))) {
output.workGroups = dispatch;
}
})");
wgpu::ComputePipelineDescriptor csDesc;
csDesc.compute.module = module;
csDesc.compute.entryPoint = "main";
pipeline = device.CreateComputePipeline(&csDesc);
}
void DirectTest(uint32_t x, uint32_t y, uint32_t z) {
// Set up dst storage buffer to contain dispatch x, y, z
wgpu::Buffer dst = utils::CreateBufferFromData<uint32_t>(
device,
wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst,
kSentinelData);
std::initializer_list<uint32_t> expectedBufferData{x, y, z};
wgpu::Buffer expectedBuffer = utils::CreateBufferFromData<uint32_t>(
device, wgpu::BufferUsage::Uniform, expectedBufferData);
// Set up bind group and issue dispatch
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, expectedBuffer, 0, 3 * sizeof(uint32_t)},
{1, dst, 0, 3 * sizeof(uint32_t)},
});
wgpu::CommandBuffer commands;
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Dispatch(x, y, z);
pass.EndPass();
commands = encoder.Finish();
}
queue.Submit(1, &commands);
std::vector<uint32_t> expected =
x == 0 || y == 0 || z == 0 ? kSentinelData : expectedBufferData;
// Verify the dispatch got called if all group counts are not zero
EXPECT_BUFFER_U32_RANGE_EQ(&expected[0], dst, 0, 3);
}
void IndirectTest(std::vector<uint32_t> indirectBufferData, uint64_t indirectOffset) {
// Set up dst storage buffer to contain dispatch x, y, z
wgpu::Buffer dst = utils::CreateBufferFromData<uint32_t>(
device,
wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst,
kSentinelData);
wgpu::Buffer indirectBuffer = utils::CreateBufferFromData(
device, &indirectBufferData[0], indirectBufferData.size() * sizeof(uint32_t),
wgpu::BufferUsage::Indirect);
uint32_t indirectStart = indirectOffset / sizeof(uint32_t);
wgpu::Buffer expectedBuffer =
utils::CreateBufferFromData(device, &indirectBufferData[indirectStart],
3 * sizeof(uint32_t), wgpu::BufferUsage::Uniform);
// Set up bind group and issue dispatch
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, expectedBuffer, 0, 3 * sizeof(uint32_t)},
{1, dst, 0, 3 * sizeof(uint32_t)},
});
wgpu::CommandBuffer commands;
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.DispatchIndirect(indirectBuffer, indirectOffset);
pass.EndPass();
commands = encoder.Finish();
}
queue.Submit(1, &commands);
std::vector<uint32_t> expected;
if (indirectBufferData[indirectStart] == 0 || indirectBufferData[indirectStart + 1] == 0 ||
indirectBufferData[indirectStart + 2] == 0) {
expected = kSentinelData;
} else {
expected.assign(indirectBufferData.begin() + indirectStart,
indirectBufferData.begin() + indirectStart + 3);
}
// Verify the dispatch got called with group counts in indirect buffer if all group counts
// are not zero
EXPECT_BUFFER_U32_RANGE_EQ(&expected[0], dst, 0, 3);
}
private:
wgpu::ComputePipeline pipeline;
};
// Test basic direct
TEST_P(ComputeDispatchTests, DirectBasic) {
DirectTest(2, 3, 4);
}
// Test no-op direct
TEST_P(ComputeDispatchTests, DirectNoop) {
// All dimensions are 0s
DirectTest(0, 0, 0);
// Only x dimension is 0
DirectTest(0, 3, 4);
// Only y dimension is 0
DirectTest(2, 0, 4);
// Only z dimension is 0
DirectTest(2, 3, 0);
}
// Test basic indirect
TEST_P(ComputeDispatchTests, IndirectBasic) {
IndirectTest({2, 3, 4}, 0);
}
// Test no-op indirect
TEST_P(ComputeDispatchTests, IndirectNoop) {
// All dimensions are 0s
IndirectTest({0, 0, 0}, 0);
// Only x dimension is 0
IndirectTest({0, 3, 4}, 0);
// Only y dimension is 0
IndirectTest({2, 0, 4}, 0);
// Only z dimension is 0
IndirectTest({2, 3, 0}, 0);
}
// Test indirect with buffer offset
TEST_P(ComputeDispatchTests, IndirectOffset) {
IndirectTest({0, 0, 0, 2, 3, 4}, 3 * sizeof(uint32_t));
}
DAWN_INSTANTIATE_TEST(ComputeDispatchTests,
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),
OpenGLESBackend(),
VulkanBackend());