mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-13 15:39:09 +00:00
In a typical application, most draws will use different uniforms values for things like the world position and orientation. In the current state of WebGPU this means that a new bind group needs to be created for each draw to set the right uniforms. Bind group creation is expected to be more expensive than recording draws because they incur an allocation. This feature is to reduce the number of bind groups that need to be created. The patch implemented dynamic buffer offset validation logics and adding unittests. BUG=dawn:55 Change-Id: If6200a87bfedba825abcbfe60f336eab2e27226a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7100 Commit-Queue: Shaobo Yan <shaobo.yan@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
707 lines
27 KiB
C++
707 lines
27 KiB
C++
// Copyright 2017 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/unittests/validation/ValidationTest.h"
|
|
|
|
#include "common/Constants.h"
|
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/DawnHelpers.h"
|
|
|
|
class BindGroupValidationTest : public ValidationTest {
|
|
public:
|
|
void SetUp() override {
|
|
// Create objects to use as resources inside test bind groups.
|
|
{
|
|
dawn::BufferDescriptor descriptor;
|
|
descriptor.size = 1024;
|
|
descriptor.usage = dawn::BufferUsageBit::Uniform;
|
|
mUBO = device.CreateBuffer(&descriptor);
|
|
}
|
|
{
|
|
dawn::BufferDescriptor descriptor;
|
|
descriptor.size = 1024;
|
|
descriptor.usage = dawn::BufferUsageBit::Storage;
|
|
mSSBO = device.CreateBuffer(&descriptor);
|
|
}
|
|
{
|
|
dawn::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor();
|
|
mSampler = device.CreateSampler(&descriptor);
|
|
}
|
|
{
|
|
dawn::TextureDescriptor descriptor;
|
|
descriptor.dimension = dawn::TextureDimension::e2D;
|
|
descriptor.size = {16, 16, 1};
|
|
descriptor.arrayLayerCount = 1;
|
|
descriptor.sampleCount = 1;
|
|
descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm;
|
|
descriptor.mipLevelCount = 1;
|
|
descriptor.usage = dawn::TextureUsageBit::Sampled;
|
|
mSampledTexture = device.CreateTexture(&descriptor);
|
|
mSampledTextureView = mSampledTexture.CreateDefaultView();
|
|
}
|
|
}
|
|
|
|
protected:
|
|
dawn::Buffer mUBO;
|
|
dawn::Buffer mSSBO;
|
|
dawn::Sampler mSampler;
|
|
dawn::Texture mSampledTexture;
|
|
dawn::TextureView mSampledTextureView;
|
|
};
|
|
|
|
// Test the validation of BindGroupDescriptor::nextInChain
|
|
TEST_F(BindGroupValidationTest, NextInChainNullptr) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {});
|
|
|
|
dawn::BindGroupDescriptor descriptor;
|
|
descriptor.layout = layout;
|
|
descriptor.bindingCount = 0;
|
|
descriptor.bindings = nullptr;
|
|
|
|
// Control case: check that nextInChain = nullptr is valid
|
|
descriptor.nextInChain = nullptr;
|
|
device.CreateBindGroup(&descriptor);
|
|
|
|
// Check that nextInChain != nullptr is an error.
|
|
descriptor.nextInChain = static_cast<void*>(&descriptor);
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
}
|
|
|
|
// Check constraints on bindingCount
|
|
TEST_F(BindGroupValidationTest, bindingCountMismatch) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}
|
|
});
|
|
|
|
// Control case: check that a descriptor with one binding is ok
|
|
utils::MakeBindGroup(device, layout, {{0, mSampler}});
|
|
|
|
// Check that bindingCount != layout.bindingCount fails.
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {}));
|
|
}
|
|
|
|
// Check constraints on BindGroupBinding::binding
|
|
TEST_F(BindGroupValidationTest, WrongBindings) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}
|
|
});
|
|
|
|
// Control case: check that a descriptor with a binding matching the layout's is ok
|
|
utils::MakeBindGroup(device, layout, {{0, mSampler}});
|
|
|
|
// Check that binding must be present in the layout
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{1, mSampler}}));
|
|
|
|
// Check that binding >= kMaxBindingsPerGroup fails.
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{kMaxBindingsPerGroup, mSampler}}));
|
|
}
|
|
|
|
// Check that the same binding cannot be set twice
|
|
TEST_F(BindGroupValidationTest, BindingSetTwice) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler},
|
|
{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}
|
|
});
|
|
|
|
// Control case: check that different bindings work
|
|
utils::MakeBindGroup(device, layout, {
|
|
{0, mSampler},
|
|
{1, mSampler}
|
|
});
|
|
|
|
// Check that setting the same binding twice is invalid
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {
|
|
{0, mSampler},
|
|
{0, mSampler}
|
|
}));
|
|
}
|
|
|
|
// Check that a sampler binding must contain exactly one sampler
|
|
TEST_F(BindGroupValidationTest, SamplerBindingType) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}
|
|
});
|
|
|
|
dawn::BindGroupBinding binding;
|
|
binding.binding = 0;
|
|
binding.sampler = nullptr;
|
|
binding.textureView = nullptr;
|
|
binding.buffer = nullptr;
|
|
binding.offset = 0;
|
|
binding.size = 0;
|
|
|
|
dawn::BindGroupDescriptor descriptor;
|
|
descriptor.layout = layout;
|
|
descriptor.bindingCount = 1;
|
|
descriptor.bindings = &binding;
|
|
|
|
// Not setting anything fails
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
|
|
// Control case: setting just the sampler works
|
|
binding.sampler = mSampler;
|
|
device.CreateBindGroup(&descriptor);
|
|
|
|
// Setting the texture view as well is an error
|
|
binding.textureView = mSampledTextureView;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.textureView = nullptr;
|
|
|
|
// Setting the buffer as well is an error
|
|
binding.buffer = mUBO;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.buffer = nullptr;
|
|
|
|
// Setting the sampler to an error sampler is an error.
|
|
{
|
|
dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
|
|
samplerDesc.minFilter = static_cast<dawn::FilterMode>(0xFFFFFFFF);
|
|
|
|
dawn::Sampler errorSampler;
|
|
ASSERT_DEVICE_ERROR(errorSampler = device.CreateSampler(&samplerDesc));
|
|
|
|
binding.sampler = errorSampler;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.sampler = nullptr;
|
|
}
|
|
}
|
|
|
|
// Check that a texture binding must contain exactly a texture view
|
|
TEST_F(BindGroupValidationTest, TextureBindingType) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}
|
|
});
|
|
|
|
dawn::BindGroupBinding binding;
|
|
binding.binding = 0;
|
|
binding.sampler = nullptr;
|
|
binding.textureView = nullptr;
|
|
binding.buffer = nullptr;
|
|
binding.offset = 0;
|
|
binding.size = 0;
|
|
|
|
dawn::BindGroupDescriptor descriptor;
|
|
descriptor.layout = layout;
|
|
descriptor.bindingCount = 1;
|
|
descriptor.bindings = &binding;
|
|
|
|
// Not setting anything fails
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
|
|
// Control case: setting just the texture view works
|
|
binding.textureView = mSampledTextureView;
|
|
device.CreateBindGroup(&descriptor);
|
|
|
|
// Setting the sampler as well is an error
|
|
binding.sampler = mSampler;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.textureView = nullptr;
|
|
|
|
// Setting the buffer as well is an error
|
|
binding.buffer = mUBO;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.buffer = nullptr;
|
|
|
|
// Setting the texture view to an error texture view is an error.
|
|
{
|
|
dawn::TextureViewDescriptor viewDesc;
|
|
viewDesc.format = dawn::TextureFormat::R8G8B8A8Unorm;
|
|
viewDesc.dimension = dawn::TextureViewDimension::e2D;
|
|
viewDesc.baseMipLevel = 0;
|
|
viewDesc.mipLevelCount = 0;
|
|
viewDesc.baseArrayLayer = 0;
|
|
viewDesc.arrayLayerCount = 0;
|
|
|
|
dawn::TextureView errorView;
|
|
ASSERT_DEVICE_ERROR(errorView = mSampledTexture.CreateView(&viewDesc));
|
|
|
|
binding.textureView = errorView;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.textureView = nullptr;
|
|
}
|
|
}
|
|
|
|
// Check that a buffer binding must contain exactly a buffer
|
|
TEST_F(BindGroupValidationTest, BufferBindingType) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}
|
|
});
|
|
|
|
dawn::BindGroupBinding binding;
|
|
binding.binding = 0;
|
|
binding.sampler = nullptr;
|
|
binding.textureView = nullptr;
|
|
binding.buffer = nullptr;
|
|
binding.offset = 0;
|
|
binding.size = 0;
|
|
|
|
dawn::BindGroupDescriptor descriptor;
|
|
descriptor.layout = layout;
|
|
descriptor.bindingCount = 1;
|
|
descriptor.bindings = &binding;
|
|
|
|
// Not setting anything fails
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
|
|
// Control case: setting just the buffer works
|
|
binding.buffer = mUBO;
|
|
device.CreateBindGroup(&descriptor);
|
|
|
|
// Setting the texture view as well is an error
|
|
binding.textureView = mSampledTextureView;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.textureView = nullptr;
|
|
|
|
// Setting the sampler as well is an error
|
|
binding.sampler = mSampler;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.sampler = nullptr;
|
|
|
|
// Setting the buffer to an error buffer is an error.
|
|
{
|
|
dawn::BufferDescriptor bufferDesc;
|
|
bufferDesc.size = 1024;
|
|
bufferDesc.usage = static_cast<dawn::BufferUsageBit>(0xFFFFFFFF);
|
|
|
|
dawn::Buffer errorBuffer;
|
|
ASSERT_DEVICE_ERROR(errorBuffer = device.CreateBuffer(&bufferDesc));
|
|
|
|
binding.buffer = errorBuffer;
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
|
binding.buffer = nullptr;
|
|
}
|
|
}
|
|
|
|
// Check that a texture must have the correct usage
|
|
TEST_F(BindGroupValidationTest, TextureUsage) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}
|
|
});
|
|
|
|
// Control case: setting a sampleable texture view works.
|
|
utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}});
|
|
|
|
// Make an output attachment texture and try to set it for a SampledTexture binding
|
|
dawn::TextureDescriptor descriptor;
|
|
descriptor.dimension = dawn::TextureDimension::e2D;
|
|
descriptor.size = {16, 16, 1};
|
|
descriptor.arrayLayerCount = 1;
|
|
descriptor.sampleCount = 1;
|
|
descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm;
|
|
descriptor.mipLevelCount = 1;
|
|
descriptor.usage = dawn::TextureUsageBit::OutputAttachment;
|
|
dawn::Texture outputTexture = device.CreateTexture(&descriptor);
|
|
dawn::TextureView outputTextureView = outputTexture.CreateDefaultView();
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, outputTextureView}}));
|
|
}
|
|
|
|
// Check that a UBO must have the correct usage
|
|
TEST_F(BindGroupValidationTest, BufferUsageUBO) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}
|
|
});
|
|
|
|
// Control case: using a buffer with the uniform usage works
|
|
utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}});
|
|
|
|
// Using a buffer without the uniform usage fails
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}}));
|
|
}
|
|
|
|
// Check that a SSBO must have the correct usage
|
|
TEST_F(BindGroupValidationTest, BufferUsageSSBO) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::StorageBuffer}
|
|
});
|
|
|
|
// Control case: using a buffer with the storage usage works
|
|
utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}});
|
|
|
|
// Using a buffer without the storage usage fails
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}}));
|
|
}
|
|
|
|
// Tests constraints on the buffer offset for bind groups.
|
|
TEST_F(BindGroupValidationTest, BufferOffsetAlignment) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
});
|
|
|
|
// Check that offset 0 is valid
|
|
utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 512}});
|
|
|
|
// Check that offset 256 (aligned) is valid
|
|
utils::MakeBindGroup(device, layout, {{0, mUBO, 256, 256}});
|
|
|
|
// Check cases where unaligned buffer offset is invalid
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 1, 256}}));
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 128, 256}}));
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 255, 256}}));
|
|
}
|
|
|
|
// Tests constraints to be sure the buffer binding fits in the buffer
|
|
TEST_F(BindGroupValidationTest, BufferBindingOOB) {
|
|
dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
});
|
|
|
|
dawn::BufferDescriptor descriptor;
|
|
descriptor.size = 1024;
|
|
descriptor.usage = dawn::BufferUsageBit::Uniform;
|
|
dawn::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
|
|
// Success case, touching the start of the buffer works
|
|
utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256}});
|
|
|
|
// Success case, touching the end of the buffer works
|
|
utils::MakeBindGroup(device, layout, {{0, buffer, 3*256, 256}});
|
|
utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 0}});
|
|
|
|
// Success case, touching the full buffer works
|
|
utils::MakeBindGroup(device, layout, {{0, buffer, 0, 1024}});
|
|
|
|
// Error case, offset is OOB
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256*5, 0}}));
|
|
|
|
// Error case, size is OOB
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256*5}}));
|
|
|
|
// Error case, offset+size is OOB
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 1}}));
|
|
|
|
// Error case, offset+size overflows to be 0
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256, uint32_t(0) - uint32_t(256)}}));
|
|
}
|
|
|
|
// Test what happens when the layout is an error.
|
|
TEST_F(BindGroupValidationTest, ErrorLayout) {
|
|
dawn::BindGroupLayout goodLayout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
});
|
|
|
|
dawn::BindGroupLayout errorLayout;
|
|
ASSERT_DEVICE_ERROR(errorLayout = utils::MakeBindGroupLayout(device, {
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
}));
|
|
|
|
// Control case, creating with the good layout works
|
|
utils::MakeBindGroup(device, goodLayout, {{0, mUBO, 0, 256}});
|
|
|
|
// Control case, creating with the good layout works
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, errorLayout, {{0, mUBO, 0, 256}}));
|
|
}
|
|
|
|
class BindGroupLayoutValidationTest : public ValidationTest {
|
|
};
|
|
|
|
// Tests setting OOB checks for kMaxBindingsPerGroup in bind group layouts.
|
|
TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutBindingOOB) {
|
|
// Checks that kMaxBindingsPerGroup - 1 is valid.
|
|
utils::MakeBindGroupLayout(device, {
|
|
{kMaxBindingsPerGroup - 1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}
|
|
});
|
|
|
|
// Checks that kMaxBindingsPerGroup is OOB
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, {
|
|
{kMaxBindingsPerGroup, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}
|
|
}));
|
|
}
|
|
|
|
// This test verifies that the BindGroupLayout bindings are correctly validated, even if the
|
|
// binding ids are out-of-order.
|
|
TEST_F(BindGroupLayoutValidationTest, BindGroupBinding) {
|
|
auto layout = utils::MakeBindGroupLayout(
|
|
device, {
|
|
{1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
});
|
|
}
|
|
|
|
|
|
// This test verifies that the BindGroupLayout cache is successfully caching/deduplicating objects.
|
|
//
|
|
// NOTE: This test only works currently because unittests are run without the wire - so the returned
|
|
// BindGroupLayout pointers are actually visibly equivalent. With the wire, this would not be true.
|
|
TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutCache) {
|
|
auto layout1 = utils::MakeBindGroupLayout(
|
|
device, {
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
});
|
|
auto layout2 = utils::MakeBindGroupLayout(
|
|
device, {
|
|
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
|
|
});
|
|
|
|
// Caching should cause these to be the same.
|
|
ASSERT_EQ(layout1.Get(), layout2.Get());
|
|
}
|
|
|
|
constexpr uint32_t kBufferElementsCount = kMinDynamicBufferOffsetAlignment / sizeof(uint32_t) + 2;
|
|
constexpr uint32_t kBufferSize = kBufferElementsCount * sizeof(uint32_t);
|
|
|
|
class SetBindGroupValidationTest : public ValidationTest {
|
|
public:
|
|
void SetUp() override {
|
|
std::array<float, kBufferElementsCount> uniformData = {0};
|
|
|
|
uniformData[0] = 1.0;
|
|
uniformData[1] = 2.0;
|
|
uniformData[uniformData.size() - 2] = 5.0;
|
|
uniformData[uniformData.size() - 1] = 6.0;
|
|
|
|
dawn::BufferDescriptor bufferDescriptor;
|
|
bufferDescriptor.size = kBufferSize;
|
|
bufferDescriptor.usage = dawn::BufferUsageBit::Storage;
|
|
|
|
mUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize,
|
|
dawn::BufferUsageBit::Uniform);
|
|
mStorageBuffer = device.CreateBuffer(&bufferDescriptor);
|
|
|
|
mBindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment,
|
|
dawn::BindingType::DynamicUniformBuffer},
|
|
{1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment,
|
|
dawn::BindingType::DynamicStorageBuffer}});
|
|
|
|
mBindGroup = utils::MakeBindGroup(
|
|
device, mBindGroupLayout,
|
|
{{0, mUniformBuffer, 0, kBufferSize}, {1, mStorageBuffer, 0, kBufferSize}});
|
|
}
|
|
// Create objects to use as resources inside test bind groups.
|
|
|
|
dawn::BindGroup mBindGroup;
|
|
dawn::BindGroupLayout mBindGroupLayout;
|
|
dawn::Buffer mUniformBuffer;
|
|
dawn::Buffer mStorageBuffer;
|
|
|
|
dawn::RenderPipeline CreateRenderPipeline() {
|
|
dawn::ShaderModule vsModule =
|
|
utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
|
|
#version 450
|
|
void main() {
|
|
})");
|
|
|
|
dawn::ShaderModule fsModule =
|
|
utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
|
|
#version 450
|
|
layout(std140, set = 0, binding = 0) uniform uBuffer {
|
|
vec2 value1;
|
|
};
|
|
layout(std140, set = 0, binding = 1) buffer SBuffer {
|
|
vec2 value2;
|
|
} sBuffer;
|
|
layout(location = 0) out uvec4 fragColor;
|
|
void main() {
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
|
|
pipelineDescriptor.cVertexStage.module = vsModule;
|
|
pipelineDescriptor.cFragmentStage.module = fsModule;
|
|
dawn::PipelineLayout pipelineLayout =
|
|
utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
|
|
pipelineDescriptor.layout = pipelineLayout;
|
|
return device.CreateRenderPipeline(&pipelineDescriptor);
|
|
}
|
|
|
|
dawn::ComputePipeline CreateComputePipeline() {
|
|
dawn::ShaderModule csModule =
|
|
utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"(
|
|
#version 450
|
|
const uint kTileSize = 4;
|
|
const uint kInstances = 11;
|
|
|
|
layout(local_size_x = kTileSize, local_size_y = kTileSize, local_size_z = 1) in;
|
|
layout(std140, set = 0, binding = 0) uniform UniformBuffer {
|
|
float value1;
|
|
};
|
|
layout(std140, set = 0, binding = 1) buffer SBuffer {
|
|
float value2;
|
|
} dst;
|
|
|
|
void main() {
|
|
})");
|
|
|
|
dawn::ComputePipelineDescriptor csDesc;
|
|
dawn::PipelineLayout pipelineLayout =
|
|
utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
|
|
csDesc.layout = pipelineLayout;
|
|
|
|
dawn::PipelineStageDescriptor computeStage;
|
|
computeStage.module = csModule;
|
|
computeStage.entryPoint = "main";
|
|
csDesc.computeStage = &computeStage;
|
|
|
|
return device.CreateComputePipeline(&csDesc);
|
|
}
|
|
};
|
|
|
|
// This is the test case that should work.
|
|
TEST_F(SetBindGroupValidationTest, Basic) {
|
|
std::array<uint64_t, 2> offsets = {256, 0};
|
|
|
|
// RenderPipeline SetBindGroup
|
|
{
|
|
dawn::RenderPipeline renderPipeline = CreateRenderPipeline();
|
|
DummyRenderPass renderPass(device);
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
|
renderPassEncoder.SetPipeline(renderPipeline);
|
|
renderPassEncoder.SetBindGroup(0, mBindGroup, 2, offsets.data());
|
|
renderPassEncoder.Draw(3, 1, 0, 0);
|
|
renderPassEncoder.EndPass();
|
|
commandEncoder.Finish();
|
|
}
|
|
|
|
{
|
|
dawn::ComputePipeline computePipeline = CreateComputePipeline();
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
|
|
computePassEncoder.SetPipeline(computePipeline);
|
|
computePassEncoder.SetBindGroup(0, mBindGroup, 2, offsets.data());
|
|
computePassEncoder.Dispatch(1, 1, 1);
|
|
computePassEncoder.EndPass();
|
|
commandEncoder.Finish();
|
|
}
|
|
}
|
|
|
|
// Test cases that test dynamic offsets count mismatch with bind group layout.
|
|
TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) {
|
|
std::array<uint64_t, 1> mismatchOffsets = {0};
|
|
|
|
// RenderPipeline SetBindGroup
|
|
{
|
|
dawn::RenderPipeline pipeline = CreateRenderPipeline();
|
|
DummyRenderPass renderPass(device);
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
|
renderPassEncoder.SetPipeline(pipeline);
|
|
renderPassEncoder.SetBindGroup(0, mBindGroup, 1, mismatchOffsets.data());
|
|
renderPassEncoder.Draw(3, 1, 0, 0);
|
|
renderPassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
|
|
{
|
|
dawn::ComputePipeline computePipeline = CreateComputePipeline();
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
|
|
computePassEncoder.SetPipeline(computePipeline);
|
|
computePassEncoder.SetBindGroup(0, mBindGroup, 1, mismatchOffsets.data());
|
|
computePassEncoder.Dispatch(1, 1, 1);
|
|
computePassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test cases that test dynamic offsets not aligned
|
|
TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) {
|
|
std::array<uint64_t, 2> notAlignedOffsets = {1, 2};
|
|
|
|
// RenderPipeline SetBindGroup
|
|
{
|
|
dawn::RenderPipeline pipeline = CreateRenderPipeline();
|
|
DummyRenderPass renderPass(device);
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
|
renderPassEncoder.SetPipeline(pipeline);
|
|
renderPassEncoder.SetBindGroup(0, mBindGroup, 2, notAlignedOffsets.data());
|
|
renderPassEncoder.Draw(3, 1, 0, 0);
|
|
renderPassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
|
|
// ComputePipeline SetBindGroup
|
|
{
|
|
dawn::ComputePipeline pipeline = CreateComputePipeline();
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
|
|
computePassEncoder.SetPipeline(pipeline);
|
|
computePassEncoder.SetBindGroup(0, mBindGroup, 2, notAlignedOffsets.data());
|
|
computePassEncoder.Dispatch(1, 1, 1);
|
|
computePassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test cases that test dynamic uniform buffer out of bound situation.
|
|
TEST_F(SetBindGroupValidationTest, OutOfBoundDynamicUniformBuffer) {
|
|
std::array<uint64_t, 2> overFlowOffsets = {512, 0};
|
|
|
|
// RenderPipeline SetBindGroup
|
|
{
|
|
dawn::RenderPipeline pipeline = CreateRenderPipeline();
|
|
DummyRenderPass renderPass(device);
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
|
renderPassEncoder.SetPipeline(pipeline);
|
|
renderPassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
|
|
renderPassEncoder.Draw(3, 1, 0, 0);
|
|
renderPassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
|
|
// ComputePipeline SetBindGroup
|
|
{
|
|
dawn::ComputePipeline pipeline = CreateComputePipeline();
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
|
|
computePassEncoder.SetPipeline(pipeline);
|
|
computePassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
|
|
computePassEncoder.Dispatch(1, 1, 1);
|
|
computePassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test cases that test dynamic storage buffer out of bound situation.
|
|
TEST_F(SetBindGroupValidationTest, OutOfBoundDynamicStorageBuffer) {
|
|
std::array<uint64_t, 2> overFlowOffsets = {0, 512};
|
|
|
|
// RenderPipeline SetBindGroup
|
|
{
|
|
dawn::RenderPipeline pipeline = CreateRenderPipeline();
|
|
DummyRenderPass renderPass(device);
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
|
|
renderPassEncoder.SetPipeline(pipeline);
|
|
renderPassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
|
|
renderPassEncoder.Draw(3, 1, 0, 0);
|
|
renderPassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
|
|
// ComputePipeline SetBindGroup
|
|
{
|
|
dawn::ComputePipeline pipeline = CreateComputePipeline();
|
|
|
|
dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
|
|
computePassEncoder.SetPipeline(pipeline);
|
|
computePassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
|
|
computePassEncoder.Dispatch(1, 1, 1);
|
|
computePassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(commandEncoder.Finish());
|
|
}
|
|
}
|