mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-14 03:11:29 +00:00
Also drive-by fixes some other deprecated constructs (const -> let, and a disabled test having ancient WGSL). Bug: dawn:755 Change-Id: I924dfbcbd0a7d0478f3e9b3766585751a0392499 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/47620 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Auto-Submit: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
341 lines
13 KiB
C++
341 lines
13 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 "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
class VertexStateTest : public ValidationTest {
|
|
protected:
|
|
void CreatePipeline(bool success,
|
|
const utils::ComboVertexStateDescriptor& state,
|
|
const char* vertexSource) {
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vertexSource);
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
|
[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
|
|
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
}
|
|
)");
|
|
|
|
utils::ComboRenderPipelineDescriptor2 descriptor;
|
|
descriptor.vertex.module = vsModule;
|
|
descriptor.vertex.bufferCount = state.vertexBufferCount;
|
|
descriptor.vertex.buffers = &state.cVertexBuffers[0];
|
|
descriptor.cFragment.module = fsModule;
|
|
descriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
if (!success) {
|
|
ASSERT_DEVICE_ERROR(device.CreateRenderPipeline2(&descriptor));
|
|
} else {
|
|
device.CreateRenderPipeline2(&descriptor);
|
|
}
|
|
}
|
|
|
|
const char* kDummyVertexShader = R"(
|
|
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
|
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
)";
|
|
};
|
|
|
|
// Check an empty vertex input is valid
|
|
TEST_F(VertexStateTest, EmptyIsOk) {
|
|
utils::ComboVertexStateDescriptor state;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check null buffer is valid
|
|
TEST_F(VertexStateTest, NullBufferIsOk) {
|
|
utils::ComboVertexStateDescriptor state;
|
|
// One null buffer (buffer[0]) is OK
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = 0;
|
|
state.cVertexBuffers[0].attributeCount = 0;
|
|
state.cVertexBuffers[0].attributes = nullptr;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// One null buffer (buffer[0]) followed by a buffer (buffer[1]) is OK
|
|
state.vertexBufferCount = 2;
|
|
state.cVertexBuffers[1].arrayStride = 0;
|
|
state.cVertexBuffers[1].attributeCount = 1;
|
|
state.cVertexBuffers[1].attributes = &state.cAttributes[0];
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Null buffer (buffer[2]) sitting between buffers (buffer[1] and buffer[3]) is OK
|
|
state.vertexBufferCount = 4;
|
|
state.cVertexBuffers[2].attributeCount = 0;
|
|
state.cVertexBuffers[2].attributes = nullptr;
|
|
state.cVertexBuffers[3].attributeCount = 1;
|
|
state.cVertexBuffers[3].attributes = &state.cAttributes[1];
|
|
state.cAttributes[1].shaderLocation = 1;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check validation that pipeline vertex buffers are backed by attributes in the vertex input
|
|
// Check validation that pipeline vertex buffers are backed by attributes in the vertex input
|
|
TEST_F(VertexStateTest, PipelineCompatibility) {
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = 2 * sizeof(float);
|
|
state.cVertexBuffers[0].attributeCount = 2;
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
state.cAttributes[1].shaderLocation = 1;
|
|
state.cAttributes[1].offset = sizeof(float);
|
|
|
|
// Control case: pipeline with one input per attribute
|
|
CreatePipeline(true, state, R"(
|
|
[[stage(vertex)]] fn main(
|
|
[[location(0)]] a : vec4<f32>,
|
|
[[location(1)]] b : vec4<f32>
|
|
) -> [[builtin(position)]] vec4<f32> {
|
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
)");
|
|
|
|
// Check it is valid for the pipeline to use a subset of the VertexState
|
|
CreatePipeline(true, state, R"(
|
|
[[stage(vertex)]] fn main(
|
|
[[location(0)]] a : vec4<f32>
|
|
) -> [[builtin(position)]] vec4<f32> {
|
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
)");
|
|
|
|
// Check for an error when the pipeline uses an attribute not in the vertex input
|
|
CreatePipeline(false, state, R"(
|
|
[[stage(vertex)]] fn main(
|
|
[[location(2)]] a : vec4<f32>
|
|
) -> [[builtin(position)]] vec4<f32> {
|
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
)");
|
|
}
|
|
|
|
// Test that a arrayStride of 0 is valid
|
|
TEST_F(VertexStateTest, StrideZero) {
|
|
// Works ok without attributes
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = 0;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Works ok with attributes at a large-ish offset
|
|
state.cAttributes[0].offset = 128;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check validation that vertex attribute offset should be within vertex buffer arrayStride,
|
|
// if vertex buffer arrayStride is not zero.
|
|
TEST_F(VertexStateTest, SetOffsetOutOfBounds) {
|
|
// Control case, setting correct arrayStride and offset
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = 2 * sizeof(float);
|
|
state.cVertexBuffers[0].attributeCount = 2;
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
state.cAttributes[1].shaderLocation = 1;
|
|
state.cAttributes[1].offset = sizeof(float);
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test vertex attribute offset exceed vertex buffer arrayStride range
|
|
state.cVertexBuffers[0].arrayStride = sizeof(float);
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
|
|
// It's OK if arrayStride is zero
|
|
state.cVertexBuffers[0].arrayStride = 0;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check out of bounds condition on total number of vertex buffers
|
|
TEST_F(VertexStateTest, SetVertexBuffersNumLimit) {
|
|
// Control case, setting max vertex buffer number
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = kMaxVertexBuffers;
|
|
for (uint32_t i = 0; i < kMaxVertexBuffers; ++i) {
|
|
state.cVertexBuffers[i].attributeCount = 1;
|
|
state.cVertexBuffers[i].attributes = &state.cAttributes[i];
|
|
state.cAttributes[i].shaderLocation = i;
|
|
}
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test vertex buffer number exceed the limit
|
|
state.vertexBufferCount = kMaxVertexBuffers + 1;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check out of bounds condition on total number of vertex attributes
|
|
TEST_F(VertexStateTest, SetVertexAttributesNumLimit) {
|
|
// Control case, setting max vertex attribute number
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 2;
|
|
state.cVertexBuffers[0].attributeCount = kMaxVertexAttributes;
|
|
for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) {
|
|
state.cAttributes[i].shaderLocation = i;
|
|
}
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test vertex attribute number exceed the limit
|
|
state.cVertexBuffers[1].attributeCount = 1;
|
|
state.cVertexBuffers[1].attributes = &state.cAttributes[kMaxVertexAttributes - 1];
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check out of bounds condition on input arrayStride
|
|
TEST_F(VertexStateTest, SetInputStrideOutOfBounds) {
|
|
// Control case, setting max input arrayStride
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = kMaxVertexBufferStride;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test input arrayStride OOB
|
|
state.cVertexBuffers[0].arrayStride = kMaxVertexBufferStride + 1;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check multiple of 4 bytes constraint on input arrayStride
|
|
TEST_F(VertexStateTest, SetInputStrideNotAligned) {
|
|
// Control case, setting input arrayStride 4 bytes.
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = 4;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test input arrayStride not multiple of 4 bytes
|
|
state.cVertexBuffers[0].arrayStride = 2;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Test that we cannot set an already set attribute
|
|
TEST_F(VertexStateTest, AlreadySetAttribute) {
|
|
// Control case, setting attribute 0
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Oh no, attribute 0 is set twice
|
|
state.cVertexBuffers[0].attributeCount = 2;
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
state.cAttributes[1].shaderLocation = 0;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Test that a arrayStride of 0 is valid
|
|
TEST_F(VertexStateTest, SetSameShaderLocation) {
|
|
// Control case, setting different shader locations in two attributes
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].attributeCount = 2;
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
state.cAttributes[1].shaderLocation = 1;
|
|
state.cAttributes[1].offset = sizeof(float);
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test same shader location in two attributes in the same buffer
|
|
state.cAttributes[1].shaderLocation = 0;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
|
|
// Test same shader location in two attributes in different buffers
|
|
state.vertexBufferCount = 2;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
state.cAttributes[0].shaderLocation = 0;
|
|
state.cVertexBuffers[1].attributeCount = 1;
|
|
state.cVertexBuffers[1].attributes = &state.cAttributes[1];
|
|
state.cAttributes[1].shaderLocation = 0;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check out of bounds condition on attribute shader location
|
|
TEST_F(VertexStateTest, SetAttributeLocationOutOfBounds) {
|
|
// Control case, setting last attribute shader location
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
state.cAttributes[0].shaderLocation = kMaxVertexAttributes - 1;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test attribute location OOB
|
|
state.cAttributes[0].shaderLocation = kMaxVertexAttributes;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check attribute offset out of bounds
|
|
TEST_F(VertexStateTest, SetAttributeOffsetOutOfBounds) {
|
|
// Control case, setting max attribute offset for FloatR32 vertex format
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
state.cAttributes[0].offset = kMaxVertexBufferStride - sizeof(wgpu::VertexFormat::Float32);
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test attribute offset out of bounds
|
|
state.cAttributes[0].offset = kMaxVertexBufferStride - 1;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check the "component byte size" alignment constraint for the offset.
|
|
TEST_F(VertexStateTest, SetOffsetNotAligned) {
|
|
// Control case, setting the offset at the correct alignments.
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
|
|
state.cAttributes[0].format = wgpu::VertexFormat::Float32;
|
|
state.cAttributes[0].offset = 4;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
state.cAttributes[0].format = wgpu::VertexFormat::Snorm16x2;
|
|
state.cAttributes[0].offset = 2;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
state.cAttributes[0].format = wgpu::VertexFormat::Uint8x2;
|
|
state.cAttributes[0].offset = 1;
|
|
CreatePipeline(true, state, kDummyVertexShader);
|
|
|
|
// Test offset not multiple of the component byte size.
|
|
state.cAttributes[0].format = wgpu::VertexFormat::Float32;
|
|
state.cAttributes[0].offset = 2;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
|
|
state.cAttributes[0].format = wgpu::VertexFormat::Snorm16x2;
|
|
state.cAttributes[0].offset = 1;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check attribute offset overflow
|
|
TEST_F(VertexStateTest, SetAttributeOffsetOverflow) {
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
state.cAttributes[0].offset = std::numeric_limits<uint32_t>::max();
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|
|
|
|
// Check for some potential underflow in the vertex input validation
|
|
TEST_F(VertexStateTest, VertexFormatLargerThanNonZeroStride) {
|
|
utils::ComboVertexStateDescriptor state;
|
|
state.vertexBufferCount = 1;
|
|
state.cVertexBuffers[0].arrayStride = 4;
|
|
state.cVertexBuffers[0].attributeCount = 1;
|
|
state.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
|
|
CreatePipeline(false, state, kDummyVertexShader);
|
|
}
|