266 lines
11 KiB
C++
266 lines
11 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 "common/Assert.h"
|
||
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
||
|
#include "utils/DawnHelpers.h"
|
||
|
|
||
|
class OpArrayLengthTest : public DawnTest {
|
||
|
protected:
|
||
|
void SetUp() {
|
||
|
DawnTest::SetUp();
|
||
|
|
||
|
// Create buffers of various size to check the length() implementation
|
||
|
dawn::BufferDescriptor bufferDesc;
|
||
|
bufferDesc.size = 4;
|
||
|
bufferDesc.usage = dawn::BufferUsageBit::Storage;
|
||
|
mStorageBuffer4 = device.CreateBuffer(&bufferDesc);
|
||
|
|
||
|
bufferDesc.size = 256;
|
||
|
mStorageBuffer256 = device.CreateBuffer(&bufferDesc);
|
||
|
|
||
|
bufferDesc.size = 512;
|
||
|
mStorageBuffer512 = device.CreateBuffer(&bufferDesc);
|
||
|
|
||
|
// Put them all in a bind group for tests to bind them easily.
|
||
|
dawn::ShaderStageBit kAllStages = dawn::ShaderStageBit::Fragment |
|
||
|
dawn::ShaderStageBit::Vertex |
|
||
|
dawn::ShaderStageBit::Compute;
|
||
|
mBindGroupLayout =
|
||
|
utils::MakeBindGroupLayout(device, {{0, kAllStages, dawn::BindingType::StorageBuffer},
|
||
|
{1, kAllStages, dawn::BindingType::StorageBuffer},
|
||
|
{2, kAllStages, dawn::BindingType::StorageBuffer}});
|
||
|
|
||
|
mBindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
|
||
|
{
|
||
|
{0, mStorageBuffer4, 0, 4},
|
||
|
{1, mStorageBuffer256, 0, dawn::kWholeSize},
|
||
|
{2, mStorageBuffer512, 0, 512},
|
||
|
});
|
||
|
|
||
|
// Common shader code to use these buffers in shaders, assuming they are in bindgroup index
|
||
|
// 0.
|
||
|
mShaderInterface = R"(
|
||
|
// The length should be 1 because the buffer is 4-byte long.
|
||
|
layout(std430, set = 0, binding = 0) buffer Buffer1 {
|
||
|
float data[];
|
||
|
} buffer1;
|
||
|
|
||
|
// The length should be 64 because the buffer is 256 bytes long.
|
||
|
layout(std430, set = 0, binding = 1) buffer Buffer2 {
|
||
|
float data[];
|
||
|
} buffer2;
|
||
|
|
||
|
// The length should be (512 - 16*4) / 8 = 56 because the buffer is 512 bytes long
|
||
|
// and the structure is 8 bytes big.
|
||
|
struct Buffer3Data {float a; int b;};
|
||
|
layout(std430, set = 0, binding = 2) buffer Buffer3 {
|
||
|
mat4 garbage;
|
||
|
Buffer3Data data[];
|
||
|
} buffer3;
|
||
|
)";
|
||
|
|
||
|
// See comments in the shader for an explanation of these values
|
||
|
mExpectedLengths = {1, 64, 56};
|
||
|
}
|
||
|
|
||
|
dawn::Buffer mStorageBuffer4;
|
||
|
dawn::Buffer mStorageBuffer256;
|
||
|
dawn::Buffer mStorageBuffer512;
|
||
|
|
||
|
dawn::BindGroupLayout mBindGroupLayout;
|
||
|
dawn::BindGroup mBindGroup;
|
||
|
std::string mShaderInterface;
|
||
|
std::array<uint32_t, 3> mExpectedLengths;
|
||
|
};
|
||
|
|
||
|
// Test OpArrayLength in the compute stage
|
||
|
TEST_P(OpArrayLengthTest, Compute) {
|
||
|
// TODO(cwallez@chromium.org): The computations for length() of unsized buffer is broken on
|
||
|
// Nvidia OpenGL. See https://bugs.chromium.org/p/dawn/issues/detail?id=197
|
||
|
DAWN_SKIP_TEST_IF(IsNvidia() && IsOpenGL());
|
||
|
|
||
|
// Create a buffer to hold the result sizes and create a bindgroup for it.
|
||
|
dawn::BufferDescriptor bufferDesc;
|
||
|
bufferDesc.usage = dawn::BufferUsageBit::Storage | dawn::BufferUsageBit::CopySrc;
|
||
|
bufferDesc.size = sizeof(uint32_t) * mExpectedLengths.size();
|
||
|
dawn::Buffer resultBuffer = device.CreateBuffer(&bufferDesc);
|
||
|
|
||
|
dawn::BindGroupLayout resultLayout = utils::MakeBindGroupLayout(
|
||
|
device, {{0, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}});
|
||
|
|
||
|
dawn::BindGroup resultBindGroup =
|
||
|
utils::MakeBindGroup(device, resultLayout, {{0, resultBuffer, 0, dawn::kWholeSize}});
|
||
|
|
||
|
// Create the compute pipeline that stores the length()s in the result buffer.
|
||
|
dawn::BindGroupLayout bgls[] = {mBindGroupLayout, resultLayout};
|
||
|
dawn::PipelineLayoutDescriptor plDesc;
|
||
|
plDesc.bindGroupLayoutCount = 2;
|
||
|
plDesc.bindGroupLayouts = bgls;
|
||
|
dawn::PipelineLayout pl = device.CreatePipelineLayout(&plDesc);
|
||
|
|
||
|
dawn::PipelineStageDescriptor computeStage;
|
||
|
computeStage.entryPoint = "main";
|
||
|
computeStage.module = utils::CreateShaderModule(device, utils::ShaderStage::Compute,
|
||
|
(R"(#version 450
|
||
|
layout(std430, set = 1, binding = 0) buffer ResultBuffer {
|
||
|
uint result[3];
|
||
|
};
|
||
|
)" + mShaderInterface + R"(
|
||
|
void main() {
|
||
|
result[0] = buffer1.data.length();
|
||
|
result[1] = buffer2.data.length();
|
||
|
result[2] = buffer3.data.length();
|
||
|
})")
|
||
|
.c_str());
|
||
|
|
||
|
dawn::ComputePipelineDescriptor pipelineDesc;
|
||
|
pipelineDesc.layout = pl;
|
||
|
pipelineDesc.computeStage = &computeStage;
|
||
|
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||
|
|
||
|
// Run a single instance of the compute shader
|
||
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||
|
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||
|
pass.SetPipeline(pipeline);
|
||
|
pass.SetBindGroup(0, mBindGroup, 0, nullptr);
|
||
|
pass.SetBindGroup(1, resultBindGroup, 0, nullptr);
|
||
|
pass.Dispatch(1, 1, 1);
|
||
|
pass.EndPass();
|
||
|
|
||
|
dawn::CommandBuffer commands = encoder.Finish();
|
||
|
queue.Submit(1, &commands);
|
||
|
|
||
|
EXPECT_BUFFER_U32_RANGE_EQ(mExpectedLengths.data(), resultBuffer, 0, 3);
|
||
|
}
|
||
|
|
||
|
// Test OpArrayLength in the fragment stage
|
||
|
TEST_P(OpArrayLengthTest, Fragment) {
|
||
|
// TODO(cwallez@chromium.org): The computations for length() of unsized buffer is broken on
|
||
|
// Nvidia OpenGL. See https://bugs.chromium.org/p/dawn/issues/detail?id=197
|
||
|
DAWN_SKIP_TEST_IF(IsNvidia() && IsOpenGL());
|
||
|
|
||
|
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
|
||
|
|
||
|
// Create the pipeline that computes the length of the buffers and writes it to the only render
|
||
|
// pass pixel.
|
||
|
dawn::ShaderModule vsModule = utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"(
|
||
|
#version 450
|
||
|
void main() {
|
||
|
gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||
|
gl_PointSize = 1.0;
|
||
|
})");
|
||
|
|
||
|
dawn::ShaderModule fsModule = utils::CreateShaderModule(device, utils::ShaderStage::Fragment,
|
||
|
(R"(
|
||
|
#version 450
|
||
|
)" + mShaderInterface + R"(
|
||
|
layout(location = 0) out vec4 fragColor;
|
||
|
void main() {
|
||
|
fragColor.r = buffer1.data.length() / 255.0f;
|
||
|
fragColor.g = buffer2.data.length() / 255.0f;
|
||
|
fragColor.b = buffer3.data.length() / 255.0f;
|
||
|
fragColor.a = 0.0f;
|
||
|
})")
|
||
|
.c_str());
|
||
|
|
||
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
||
|
descriptor.cVertexStage.module = vsModule;
|
||
|
descriptor.cFragmentStage.module = fsModule;
|
||
|
descriptor.primitiveTopology = dawn::PrimitiveTopology::PointList;
|
||
|
descriptor.cColorStates[0]->format = renderPass.colorFormat;
|
||
|
descriptor.layout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
|
||
|
dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
|
||
|
|
||
|
// "Draw" the lengths to the texture.
|
||
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||
|
{
|
||
|
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
||
|
pass.SetPipeline(pipeline);
|
||
|
pass.SetBindGroup(0, mBindGroup, 0, nullptr);
|
||
|
pass.Draw(1, 1, 0, 0);
|
||
|
pass.EndPass();
|
||
|
}
|
||
|
|
||
|
dawn::CommandBuffer commands = encoder.Finish();
|
||
|
queue.Submit(1, &commands);
|
||
|
|
||
|
RGBA8 expectedColor = RGBA8(mExpectedLengths[0], mExpectedLengths[1], mExpectedLengths[2], 0);
|
||
|
EXPECT_PIXEL_RGBA8_EQ(expectedColor, renderPass.color, 0, 0);
|
||
|
}
|
||
|
|
||
|
// Test OpArrayLength in the vertex stage
|
||
|
TEST_P(OpArrayLengthTest, Vertex) {
|
||
|
// TODO(cwallez@chromium.org): The computations for length() of unsized buffer is broken on
|
||
|
// Nvidia OpenGL. See https://bugs.chromium.org/p/dawn/issues/detail?id=197
|
||
|
DAWN_SKIP_TEST_IF(IsNvidia() && IsOpenGL());
|
||
|
|
||
|
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
|
||
|
|
||
|
// Create the pipeline that computes the length of the buffers and writes it to the only render
|
||
|
// pass pixel.
|
||
|
dawn::ShaderModule vsModule = utils::CreateShaderModule(device, utils::ShaderStage::Vertex,
|
||
|
(R"(
|
||
|
#version 450
|
||
|
)" + mShaderInterface + R"(
|
||
|
layout(location = 0) out vec4 pointColor;
|
||
|
void main() {
|
||
|
pointColor.r = buffer1.data.length() / 255.0f;
|
||
|
pointColor.g = buffer2.data.length() / 255.0f;
|
||
|
pointColor.b = buffer3.data.length() / 255.0f;
|
||
|
pointColor.a = 0.0f;
|
||
|
|
||
|
gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||
|
gl_PointSize = 1.0;
|
||
|
})")
|
||
|
.c_str());
|
||
|
|
||
|
dawn::ShaderModule fsModule =
|
||
|
utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"(
|
||
|
#version 450
|
||
|
layout(location = 0) out vec4 fragColor;
|
||
|
layout(location = 0) in vec4 pointColor;
|
||
|
void main() {
|
||
|
fragColor = pointColor;
|
||
|
})");
|
||
|
|
||
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
||
|
descriptor.cVertexStage.module = vsModule;
|
||
|
descriptor.cFragmentStage.module = fsModule;
|
||
|
descriptor.primitiveTopology = dawn::PrimitiveTopology::PointList;
|
||
|
descriptor.cColorStates[0]->format = renderPass.colorFormat;
|
||
|
descriptor.layout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
|
||
|
dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
|
||
|
|
||
|
// "Draw" the lengths to the texture.
|
||
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||
|
{
|
||
|
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
||
|
pass.SetPipeline(pipeline);
|
||
|
pass.SetBindGroup(0, mBindGroup, 0, nullptr);
|
||
|
pass.Draw(1, 1, 0, 0);
|
||
|
pass.EndPass();
|
||
|
}
|
||
|
|
||
|
dawn::CommandBuffer commands = encoder.Finish();
|
||
|
queue.Submit(1, &commands);
|
||
|
|
||
|
RGBA8 expectedColor = RGBA8(mExpectedLengths[0], mExpectedLengths[1], mExpectedLengths[2], 0);
|
||
|
EXPECT_PIXEL_RGBA8_EQ(expectedColor, renderPass.color, 0, 0);
|
||
|
}
|
||
|
|
||
|
DAWN_INSTANTIATE_TEST(OpArrayLengthTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
|