mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-14 03:11:29 +00:00
Bug: tint:1382 Change-Id: I58ad2c88fde1e7c96f2ae8257e6df924b94b61db Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/77660 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com>
703 lines
27 KiB
C++
703 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/DawnTest.h"
|
|
|
|
#include "common/Assert.h"
|
|
#include "common/Math.h"
|
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
using wgpu::VertexFormat;
|
|
using wgpu::VertexStepMode;
|
|
|
|
// Input state tests all work the same way: the test will render triangles in a grid up to 4x4. Each
|
|
// triangle is position in the grid such that X will correspond to the "triangle number" and the Y
|
|
// to the instance number. Each test will set up an input state and buffers, and the vertex shader
|
|
// will check that the vertex attributes corresponds to predetermined values. On success it outputs
|
|
// green, otherwise red.
|
|
//
|
|
// The predetermined values are "K * gl_VertexID + componentIndex" for vertex-indexed buffers, and
|
|
// "K * gl_InstanceID + componentIndex" for instance-indexed buffers.
|
|
|
|
constexpr static unsigned int kRTSize = 400;
|
|
constexpr static unsigned int kRTCellOffset = 50;
|
|
constexpr static unsigned int kRTCellSize = 100;
|
|
|
|
class VertexStateTest : public DawnTest {
|
|
protected:
|
|
void SetUp() override {
|
|
DawnTest::SetUp();
|
|
|
|
renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
|
|
}
|
|
|
|
bool ShouldComponentBeDefault(VertexFormat format, int component) {
|
|
EXPECT_TRUE(component >= 0 && component < 4);
|
|
switch (format) {
|
|
case VertexFormat::Float32x4:
|
|
case VertexFormat::Unorm8x4:
|
|
return component >= 4;
|
|
case VertexFormat::Float32x3:
|
|
return component >= 3;
|
|
case VertexFormat::Float32x2:
|
|
case VertexFormat::Unorm8x2:
|
|
return component >= 2;
|
|
case VertexFormat::Float32:
|
|
return component >= 1;
|
|
default:
|
|
DAWN_UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
struct ShaderTestSpec {
|
|
uint32_t location;
|
|
VertexFormat format;
|
|
VertexStepMode step;
|
|
};
|
|
wgpu::RenderPipeline MakeTestPipeline(const utils::ComboVertexState& vertexState,
|
|
int multiplier,
|
|
const std::vector<ShaderTestSpec>& testSpec) {
|
|
std::ostringstream vs;
|
|
vs << "struct VertexIn {\n";
|
|
|
|
// TODO(cwallez@chromium.org): this only handles float attributes, we should extend it to
|
|
// other types Adds line of the form
|
|
// @location(1) input1 : vec4<f32>;
|
|
for (const auto& input : testSpec) {
|
|
vs << "@location(" << input.location << ") input" << input.location
|
|
<< " : vec4<f32>;\n";
|
|
}
|
|
|
|
vs << R"(
|
|
@builtin(vertex_index) VertexIndex : u32;
|
|
@builtin(instance_index) InstanceIndex : u32;
|
|
};
|
|
|
|
struct VertexOut {
|
|
@location(0) color : vec4<f32>;
|
|
@builtin(position) position : vec4<f32>;
|
|
};
|
|
|
|
@stage(vertex) fn main(input : VertexIn) -> VertexOut {
|
|
var output : VertexOut;
|
|
)";
|
|
|
|
// Hard code the triangle in the shader so that we don't have to add a vertex input for it.
|
|
// Also this places the triangle in the grid based on its VertexID and InstanceID
|
|
vs << " var pos = array<vec2<f32>, 3>(\n"
|
|
" vec2<f32>(0.5, 1.0), vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0));\n";
|
|
vs << " var offset : vec2<f32> = vec2<f32>(f32(input.VertexIndex / 3u), "
|
|
"f32(input.InstanceIndex));\n";
|
|
vs << " var worldPos = pos[input.VertexIndex % 3u] + offset;\n";
|
|
vs << " var position = vec4<f32>(0.5 * worldPos - vec2<f32>(1.0, 1.0), 0.0, "
|
|
"1.0);\n";
|
|
vs << " output.position = vec4<f32>(position.x, -position.y, position.z, position.w);\n";
|
|
|
|
// Perform the checks by successively ANDing a boolean
|
|
vs << " var success = true;\n";
|
|
for (const auto& input : testSpec) {
|
|
for (int component = 0; component < 4; ++component) {
|
|
vs << " success = success && (input.input" << input.location << "[" << component
|
|
<< "] == ";
|
|
if (ShouldComponentBeDefault(input.format, component)) {
|
|
vs << (component == 3 ? "1.0" : "0.0");
|
|
} else {
|
|
if (input.step == VertexStepMode::Vertex) {
|
|
vs << "f32(" << multiplier << "u * input.VertexIndex) + " << component
|
|
<< ".0";
|
|
} else {
|
|
vs << "f32(" << multiplier << "u * input.InstanceIndex) + " << component
|
|
<< ".0";
|
|
}
|
|
}
|
|
vs << ");\n";
|
|
}
|
|
}
|
|
|
|
// Choose the color
|
|
vs << R"(
|
|
if (success) {
|
|
output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
|
|
} else {
|
|
output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
}
|
|
return output;
|
|
})";
|
|
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vs.str().c_str());
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
|
@stage(fragment)
|
|
fn main(@location(0) color : vec4<f32>) -> @location(0) vec4<f32> {
|
|
return color;
|
|
}
|
|
)");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor;
|
|
descriptor.vertex.module = vsModule;
|
|
descriptor.cFragment.module = fsModule;
|
|
descriptor.vertex.bufferCount = vertexState.vertexBufferCount;
|
|
descriptor.vertex.buffers = &vertexState.cVertexBuffers[0];
|
|
descriptor.cTargets[0].format = renderPass.colorFormat;
|
|
|
|
return device.CreateRenderPipeline(&descriptor);
|
|
}
|
|
|
|
struct VertexAttributeSpec {
|
|
uint32_t location;
|
|
uint64_t offset;
|
|
VertexFormat format;
|
|
};
|
|
struct VertexBufferSpec {
|
|
uint64_t arrayStride;
|
|
VertexStepMode step;
|
|
std::vector<VertexAttributeSpec> attributes;
|
|
};
|
|
|
|
void MakeVertexState(const std::vector<VertexBufferSpec>& buffers,
|
|
utils::ComboVertexState* vertexState) {
|
|
uint32_t vertexBufferCount = 0;
|
|
uint32_t totalNumAttributes = 0;
|
|
for (const VertexBufferSpec& buffer : buffers) {
|
|
vertexState->cVertexBuffers[vertexBufferCount].arrayStride = buffer.arrayStride;
|
|
vertexState->cVertexBuffers[vertexBufferCount].stepMode = buffer.step;
|
|
|
|
vertexState->cVertexBuffers[vertexBufferCount].attributes =
|
|
&vertexState->cAttributes[totalNumAttributes];
|
|
|
|
for (const VertexAttributeSpec& attribute : buffer.attributes) {
|
|
vertexState->cAttributes[totalNumAttributes].shaderLocation = attribute.location;
|
|
vertexState->cAttributes[totalNumAttributes].offset = attribute.offset;
|
|
vertexState->cAttributes[totalNumAttributes].format = attribute.format;
|
|
totalNumAttributes++;
|
|
}
|
|
vertexState->cVertexBuffers[vertexBufferCount].attributeCount =
|
|
static_cast<uint32_t>(buffer.attributes.size());
|
|
|
|
vertexBufferCount++;
|
|
}
|
|
|
|
vertexState->vertexBufferCount = vertexBufferCount;
|
|
}
|
|
|
|
template <typename T>
|
|
wgpu::Buffer MakeVertexBuffer(std::vector<T> data) {
|
|
return utils::CreateBufferFromData(device, data.data(),
|
|
static_cast<uint32_t>(data.size() * sizeof(T)),
|
|
wgpu::BufferUsage::Vertex);
|
|
}
|
|
|
|
struct DrawVertexBuffer {
|
|
uint32_t location;
|
|
wgpu::Buffer* buffer;
|
|
};
|
|
void DoTestDraw(const wgpu::RenderPipeline& pipeline,
|
|
unsigned int triangles,
|
|
unsigned int instances,
|
|
std::vector<DrawVertexBuffer> vertexBuffers) {
|
|
EXPECT_LE(triangles, 4u);
|
|
EXPECT_LE(instances, 4u);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
pass.SetPipeline(pipeline);
|
|
|
|
for (const DrawVertexBuffer& buffer : vertexBuffers) {
|
|
pass.SetVertexBuffer(buffer.location, *buffer.buffer);
|
|
}
|
|
|
|
pass.Draw(triangles * 3, instances);
|
|
pass.EndPass();
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
CheckResult(triangles, instances);
|
|
}
|
|
|
|
void CheckResult(unsigned int triangles, unsigned int instances) {
|
|
// Check that the center of each triangle is pure green, so that if a single vertex shader
|
|
// instance fails, linear interpolation makes the pixel check fail.
|
|
for (unsigned int triangle = 0; triangle < 4; triangle++) {
|
|
for (unsigned int instance = 0; instance < 4; instance++) {
|
|
unsigned int x = kRTCellOffset + kRTCellSize * triangle;
|
|
unsigned int y = kRTCellOffset + kRTCellSize * instance;
|
|
if (triangle < triangles && instance < instances) {
|
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, x, y);
|
|
} else {
|
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
utils::BasicRenderPass renderPass;
|
|
};
|
|
|
|
// Test compilation and usage of the fixture :)
|
|
TEST_P(VertexStateTest, Basic) {
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState(
|
|
{{4 * sizeof(float), VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32x4}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Vertex}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3,
|
|
1, 2, 3, 4,
|
|
2, 3, 4, 5
|
|
});
|
|
// clang-format on
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
|
|
// Test a stride of 0 works
|
|
TEST_P(VertexStateTest, ZeroStride) {
|
|
// This test was failing only on AMD but the OpenGL backend doesn't gather PCI info yet.
|
|
DAWN_SUPPRESS_TEST_IF(IsLinux() && IsOpenGL());
|
|
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState({{0, VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32x4}}}}, &vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 0, {{0, VertexFormat::Float32x4, VertexStepMode::Vertex}});
|
|
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
});
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
|
|
// Test attributes defaults to (0, 0, 0, 1) if the input state doesn't have all components
|
|
TEST_P(VertexStateTest, AttributeExpanding) {
|
|
// This test was failing only on AMD but the OpenGL backend doesn't gather PCI info yet.
|
|
DAWN_SUPPRESS_TEST_IF(IsLinux() && IsOpenGL());
|
|
|
|
// R32F case
|
|
{
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState({{0, VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 0, {{0, VertexFormat::Float32, VertexStepMode::Vertex}});
|
|
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({0, 1, 2, 3});
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
// RG32F case
|
|
{
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState({{0, VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32x2}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline = MakeTestPipeline(
|
|
vertexState, 0, {{0, VertexFormat::Float32x2, VertexStepMode::Vertex}});
|
|
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({0, 1, 2, 3});
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
// RGB32F case
|
|
{
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState({{0, VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32x3}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline = MakeTestPipeline(
|
|
vertexState, 0, {{0, VertexFormat::Float32x3, VertexStepMode::Vertex}});
|
|
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({0, 1, 2, 3});
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
}
|
|
|
|
// Test a stride larger than the attributes
|
|
TEST_P(VertexStateTest, StrideLargerThanAttributes) {
|
|
// This test was failing only on AMD but the OpenGL backend doesn't gather PCI info yet.
|
|
DAWN_SUPPRESS_TEST_IF(IsLinux() && IsOpenGL());
|
|
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState(
|
|
{{8 * sizeof(float), VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32x4}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Vertex}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3, 0, 0, 0, 0,
|
|
1, 2, 3, 4, 0, 0, 0, 0,
|
|
2, 3, 4, 5, 0, 0, 0, 0,
|
|
});
|
|
// clang-format on
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
|
|
// Test two attributes at an offset, vertex version
|
|
TEST_P(VertexStateTest, TwoAttributesAtAnOffsetVertex) {
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState(
|
|
{{8 * sizeof(float),
|
|
VertexStepMode::Vertex,
|
|
{{0, 0, VertexFormat::Float32x4}, {1, 4 * sizeof(float), VertexFormat::Float32x4}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Vertex}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3, 0, 1, 2, 3,
|
|
1, 2, 3, 4, 1, 2, 3, 4,
|
|
2, 3, 4, 5, 2, 3, 4, 5,
|
|
});
|
|
// clang-format on
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
|
|
// Test two attributes at an offset, instance version
|
|
TEST_P(VertexStateTest, TwoAttributesAtAnOffsetInstance) {
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState(
|
|
{{8 * sizeof(float),
|
|
VertexStepMode::Instance,
|
|
{{0, 0, VertexFormat::Float32x4}, {1, 4 * sizeof(float), VertexFormat::Float32x4}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Instance}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3, 0, 1, 2, 3,
|
|
1, 2, 3, 4, 1, 2, 3, 4,
|
|
2, 3, 4, 5, 2, 3, 4, 5,
|
|
});
|
|
// clang-format on
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
|
|
// Test a pure-instance input state
|
|
TEST_P(VertexStateTest, PureInstance) {
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState(
|
|
{{4 * sizeof(float), VertexStepMode::Instance, {{0, 0, VertexFormat::Float32x4}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Instance}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3,
|
|
1, 2, 3, 4,
|
|
2, 3, 4, 5,
|
|
3, 4, 5, 6,
|
|
});
|
|
// clang-format on
|
|
DoTestDraw(pipeline, 1, 4, {DrawVertexBuffer{0, &buffer0}});
|
|
}
|
|
|
|
// Test with mixed everything, vertex vs. instance, different stride and offsets
|
|
// different attribute types
|
|
TEST_P(VertexStateTest, MixedEverything) {
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState(
|
|
{{12 * sizeof(float),
|
|
VertexStepMode::Vertex,
|
|
{{0, 0, VertexFormat::Float32}, {1, 6 * sizeof(float), VertexFormat::Float32x2}}},
|
|
{10 * sizeof(float),
|
|
VertexStepMode::Instance,
|
|
{{2, 0, VertexFormat::Float32x3}, {3, 5 * sizeof(float), VertexFormat::Float32x4}}}},
|
|
&vertexState);
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1,
|
|
{{0, VertexFormat::Float32, VertexStepMode::Vertex},
|
|
{1, VertexFormat::Float32x2, VertexStepMode::Vertex},
|
|
{2, VertexFormat::Float32x3, VertexStepMode::Instance},
|
|
{3, VertexFormat::Float32x4, VertexStepMode::Instance}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 0, 0,
|
|
1, 2, 3, 4, 0, 0, 1, 2, 3, 4, 0, 0,
|
|
2, 3, 4, 5, 0, 0, 2, 3, 4, 5, 0, 0,
|
|
3, 4, 5, 6, 0, 0, 3, 4, 5, 6, 0, 0,
|
|
});
|
|
wgpu::Buffer buffer1 = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3, 0, 0, 1, 2, 3, 0,
|
|
1, 2, 3, 4, 0, 1, 2, 3, 4, 0,
|
|
2, 3, 4, 5, 0, 2, 3, 4, 5, 0,
|
|
3, 4, 5, 6, 0, 3, 4, 5, 6, 0,
|
|
});
|
|
// clang-format on
|
|
DoTestDraw(pipeline, 1, 1, {{0, &buffer0}, {1, &buffer1}});
|
|
}
|
|
|
|
// Test input state is unaffected by unused vertex slot
|
|
TEST_P(VertexStateTest, UnusedVertexSlot) {
|
|
// Instance input state, using slot 1
|
|
utils::ComboVertexState instanceVertexState;
|
|
MakeVertexState(
|
|
{{0, VertexStepMode::Vertex, {}},
|
|
{4 * sizeof(float), VertexStepMode::Instance, {{0, 0, VertexFormat::Float32x4}}}},
|
|
&instanceVertexState);
|
|
wgpu::RenderPipeline instancePipeline = MakeTestPipeline(
|
|
instanceVertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Instance}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3,
|
|
1, 2, 3, 4,
|
|
2, 3, 4, 5,
|
|
3, 4, 5, 6,
|
|
});
|
|
// clang-format on
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
|
|
pass.SetVertexBuffer(0, buffer);
|
|
pass.SetVertexBuffer(1, buffer);
|
|
|
|
pass.SetPipeline(instancePipeline);
|
|
pass.Draw(3, 4);
|
|
|
|
pass.EndPass();
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
CheckResult(1, 4);
|
|
}
|
|
|
|
// Test setting a different pipeline with a different input state.
|
|
// This was a problem with the D3D12 backend where SetVertexBuffer
|
|
// was getting the input from the last set pipeline, not the current.
|
|
// SetVertexBuffer should be reapplied when the input state changes.
|
|
TEST_P(VertexStateTest, MultiplePipelinesMixedVertexState) {
|
|
// Basic input state, using slot 0
|
|
utils::ComboVertexState vertexVertexState;
|
|
MakeVertexState(
|
|
{{4 * sizeof(float), VertexStepMode::Vertex, {{0, 0, VertexFormat::Float32x4}}}},
|
|
&vertexVertexState);
|
|
wgpu::RenderPipeline vertexPipeline = MakeTestPipeline(
|
|
vertexVertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Vertex}});
|
|
|
|
// Instance input state, using slot 1
|
|
utils::ComboVertexState instanceVertexState;
|
|
MakeVertexState(
|
|
{{0, VertexStepMode::Instance, {}},
|
|
{4 * sizeof(float), VertexStepMode::Instance, {{0, 0, VertexFormat::Float32x4}}}},
|
|
&instanceVertexState);
|
|
wgpu::RenderPipeline instancePipeline = MakeTestPipeline(
|
|
instanceVertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Instance}});
|
|
|
|
// clang-format off
|
|
wgpu::Buffer buffer = MakeVertexBuffer<float>({
|
|
0, 1, 2, 3,
|
|
1, 2, 3, 4,
|
|
2, 3, 4, 5,
|
|
3, 4, 5, 6,
|
|
});
|
|
// clang-format on
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
|
|
pass.SetVertexBuffer(0, buffer);
|
|
pass.SetVertexBuffer(1, buffer);
|
|
|
|
pass.SetPipeline(vertexPipeline);
|
|
pass.Draw(3);
|
|
|
|
pass.SetPipeline(instancePipeline);
|
|
pass.Draw(3, 4);
|
|
|
|
pass.EndPass();
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
CheckResult(1, 4);
|
|
}
|
|
|
|
// Checks that using the last vertex buffer doesn't overflow the vertex buffer table in Metal.
|
|
TEST_P(VertexStateTest, LastAllowedVertexBuffer) {
|
|
constexpr uint32_t kBufferIndex = kMaxVertexBuffers - 1;
|
|
|
|
utils::ComboVertexState vertexState;
|
|
// All the other vertex buffers default to no attributes
|
|
vertexState.vertexBufferCount = kMaxVertexBuffers;
|
|
vertexState.cVertexBuffers[kBufferIndex].arrayStride = 4 * sizeof(float);
|
|
vertexState.cVertexBuffers[kBufferIndex].stepMode = VertexStepMode::Vertex;
|
|
vertexState.cVertexBuffers[kBufferIndex].attributeCount = 1;
|
|
vertexState.cVertexBuffers[kBufferIndex].attributes = &vertexState.cAttributes[0];
|
|
vertexState.cAttributes[0].shaderLocation = 0;
|
|
vertexState.cAttributes[0].offset = 0;
|
|
vertexState.cAttributes[0].format = VertexFormat::Float32x4;
|
|
|
|
wgpu::RenderPipeline pipeline =
|
|
MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float32x4, VertexStepMode::Vertex}});
|
|
|
|
wgpu::Buffer buffer0 = MakeVertexBuffer<float>({0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5});
|
|
DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{kMaxVertexBuffers - 1, &buffer0}});
|
|
}
|
|
|
|
// Test that overlapping vertex attributes are permitted and load data correctly
|
|
TEST_P(VertexStateTest, OverlappingVertexAttributes) {
|
|
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 3, 3);
|
|
|
|
utils::ComboVertexState vertexState;
|
|
MakeVertexState({{16,
|
|
VertexStepMode::Vertex,
|
|
{
|
|
// "****" represents the bytes we'll actually read in the shader.
|
|
{0, 0 /* offset */, VertexFormat::Float32x4}, // |****|----|----|----|
|
|
{1, 4 /* offset */, VertexFormat::Uint32x2}, // |****|****|
|
|
{2, 8 /* offset */, VertexFormat::Float16x4}, // |-----****|
|
|
{3, 0 /* offset */, VertexFormat::Float32}, // |****|
|
|
}}},
|
|
&vertexState);
|
|
|
|
struct Data {
|
|
float fvalue;
|
|
uint32_t uints[2];
|
|
uint16_t halfs[2];
|
|
};
|
|
static_assert(sizeof(Data) == 16, "");
|
|
Data data{1.f, {2u, 3u}, {Float32ToFloat16(4.f), Float32ToFloat16(5.f)}};
|
|
|
|
wgpu::Buffer vertexBuffer =
|
|
utils::CreateBufferFromData(device, &data, sizeof(data), wgpu::BufferUsage::Vertex);
|
|
|
|
utils::ComboRenderPipelineDescriptor pipelineDesc;
|
|
pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
|
|
struct VertexIn {
|
|
@location(0) attr0 : vec4<f32>;
|
|
@location(1) attr1 : vec2<u32>;
|
|
@location(2) attr2 : vec4<f32>;
|
|
@location(3) attr3 : f32;
|
|
};
|
|
|
|
struct VertexOut {
|
|
@location(0) color : vec4<f32>;
|
|
@builtin(position) position : vec4<f32>;
|
|
};
|
|
|
|
@stage(vertex) fn main(input : VertexIn) -> VertexOut {
|
|
var output : VertexOut;
|
|
output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
|
|
var success : bool = (
|
|
input.attr0.x == 1.0 &&
|
|
input.attr1.x == 2u &&
|
|
input.attr1.y == 3u &&
|
|
input.attr2.z == 4.0 &&
|
|
input.attr2.w == 5.0 &&
|
|
input.attr3 == 1.0
|
|
);
|
|
if (success) {
|
|
output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
|
|
} else {
|
|
output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
}
|
|
return output;
|
|
})");
|
|
pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
|
|
@stage(fragment)
|
|
fn main(@location(0) color : vec4<f32>) -> @location(0) vec4<f32> {
|
|
return color;
|
|
})");
|
|
pipelineDesc.vertex.bufferCount = vertexState.vertexBufferCount;
|
|
pipelineDesc.vertex.buffers = &vertexState.cVertexBuffers[0];
|
|
pipelineDesc.cTargets[0].format = renderPass.colorFormat;
|
|
pipelineDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
|
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetVertexBuffer(0, vertexBuffer);
|
|
pass.Draw(1);
|
|
pass.EndPass();
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 1, 1);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(VertexStateTest,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
// TODO for the input state:
|
|
// - Add more vertex formats
|
|
// - Add checks that the stride is enough to contain all attributes
|
|
// - Add checks stride less than some limit
|
|
// - Add checks for alignement of vertex buffers and attributes if needed
|
|
// - Check for attribute narrowing
|
|
// - Check that the input state and the pipeline vertex input types match
|
|
|
|
class OptionalVertexStateTest : public DawnTest {};
|
|
|
|
// Test that vertex input is not required in render pipeline descriptor.
|
|
TEST_P(OptionalVertexStateTest, Basic) {
|
|
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 3, 3);
|
|
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
|
@stage(vertex) fn main() -> @builtin(position) vec4<f32> {
|
|
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
})");
|
|
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
|
@stage(fragment) fn main() -> @location(0) vec4<f32> {
|
|
return vec4<f32>(0.0, 1.0, 0.0, 1.0);
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor;
|
|
descriptor.vertex.module = vsModule;
|
|
descriptor.cFragment.module = fsModule;
|
|
descriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
|
descriptor.vertex.bufferCount = 0;
|
|
descriptor.vertex.buffers = nullptr;
|
|
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
{
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
pass.SetPipeline(pipeline);
|
|
pass.Draw(1);
|
|
pass.EndPass();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 1, 1);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(OptionalVertexStateTest,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|