blob: 9c86138e8edb11b9ad27ad044e2c19616d348bea [file] [log] [blame]
// 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 <vector>
#include "dawn/common/Assert.h"
#include "dawn/common/Math.h"
#include "dawn/tests/DawnTest.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/WGPUHelpers.h"
namespace dawn {
namespace {
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 : vec4f;
for (const auto& input : testSpec) {
vs << "@location(" << input.location << ") input" << input.location << " : vec4f,\n";
}
vs << R"(
@builtin(vertex_index) VertexIndex : u32,
@builtin(instance_index) InstanceIndex : u32,
}
struct VertexOut {
@location(0) color : vec4f,
@builtin(position) position : vec4f,
}
@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(\n"
" vec2f(0.5, 1.0), vec2f(0.0, 0.0), vec2f(1.0, 0.0));\n";
vs << " var offset : vec2f = vec2f(f32(input.VertexIndex / 3u), "
"f32(input.InstanceIndex));\n";
vs << " var worldPos = pos[input.VertexIndex % 3u] + offset;\n";
vs << " var position = vec4f(0.5 * worldPos - vec2f(1.0, 1.0), 0.0, "
"1.0);\n";
vs << " output.position = vec4f(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 = vec4f(0.0, 1.0, 0.0, 1.0);
} else {
output.color = vec4f(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"(
@fragment
fn main(@location(0) color : vec4f) -> @location(0) vec4f {
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.End();
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(utils::RGBA8::kGreen, renderPass.color, x, y);
} else {
EXPECT_PIXEL_RGBA8_EQ(utils::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.End();
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.End();
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;
for (uint32_t i = 0; i < kBufferIndex; i++) {
vertexState.cVertexBuffers[i].stepMode = VertexStepMode::VertexBufferNotUsed;
}
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 : vec4f,
@location(1) attr1 : vec2u,
@location(2) attr2 : vec4f,
@location(3) attr3 : f32,
}
struct VertexOut {
@location(0) color : vec4f,
@builtin(position) position : vec4f,
}
@vertex fn main(input : VertexIn) -> VertexOut {
var output : VertexOut;
output.position = vec4f(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 = vec4f(0.0, 1.0, 0.0, 1.0);
} else {
output.color = vec4f(1.0, 0.0, 0.0, 1.0);
}
return output;
})");
pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
@fragment
fn main(@location(0) color : vec4f) -> @location(0) vec4f {
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.End();
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderPass.color, 1, 1);
}
DAWN_INSTANTIATE_TEST(VertexStateTest,
D3D11Backend(),
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),
OpenGLESBackend(),
VulkanBackend());
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"(
@vertex fn main() -> @builtin(position) vec4f {
return vec4f(0.0, 0.0, 0.0, 1.0);
})");
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@fragment fn main() -> @location(0) vec4f {
return vec4f(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.End();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderPass.color, 1, 1);
}
DAWN_INSTANTIATE_TEST(OptionalVertexStateTest,
D3D11Backend(),
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),
OpenGLESBackend(),
VulkanBackend());
} // anonymous namespace
} // namespace dawn