mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-19 19:46:04 +00:00
This has been removed from WGSL and is now deprecated in Tint. Bug: tint:1324 Change-Id: Ic187ce3c5ce0723db6f3ca6483e9f5e73ce27acc Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/72880 Auto-Submit: James Price <jrprice@google.com> Reviewed-by: Corentin Wallez <cwallez@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
743 lines
25 KiB
C++
743 lines
25 KiB
C++
// Copyright 2021 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 "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
#include <numeric>
|
|
#include <vector>
|
|
|
|
class ShaderTests : public DawnTest {
|
|
public:
|
|
wgpu::Buffer CreateBuffer(const uint32_t count) {
|
|
std::vector<uint32_t> data(count, 0);
|
|
uint64_t bufferSize = static_cast<uint64_t>(data.size() * sizeof(uint32_t));
|
|
return utils::CreateBufferFromData(device, data.data(), bufferSize,
|
|
wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc);
|
|
}
|
|
wgpu::ComputePipeline CreateComputePipeline(
|
|
const std::string& shader,
|
|
const char* entryPoint,
|
|
const std::vector<wgpu::ConstantEntry>* constants = nullptr) {
|
|
wgpu::ComputePipelineDescriptor csDesc;
|
|
csDesc.compute.module = utils::CreateShaderModule(device, shader.c_str());
|
|
csDesc.compute.entryPoint = entryPoint;
|
|
if (constants) {
|
|
csDesc.compute.constants = constants->data();
|
|
csDesc.compute.constantCount = constants->size();
|
|
}
|
|
return device.CreateComputePipeline(&csDesc);
|
|
}
|
|
};
|
|
|
|
// Test that log2 is being properly calculated, base on crbug.com/1046622
|
|
TEST_P(ShaderTests, ComputeLog2) {
|
|
uint32_t const kSteps = 19;
|
|
std::vector<uint32_t> expected{0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 32};
|
|
wgpu::Buffer buffer = CreateBuffer(kSteps);
|
|
|
|
std::string shader = R"(
|
|
struct Buf {
|
|
data : array<u32, 19>;
|
|
};
|
|
|
|
[[group(0), binding(0)]] var<storage, read_write> buf : Buf;
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main() {
|
|
let factor : f32 = 1.0001;
|
|
|
|
buf.data[0] = u32(log2(1.0 * factor));
|
|
buf.data[1] = u32(log2(2.0 * factor));
|
|
buf.data[2] = u32(log2(3.0 * factor));
|
|
buf.data[3] = u32(log2(4.0 * factor));
|
|
buf.data[4] = u32(log2(7.0 * factor));
|
|
buf.data[5] = u32(log2(8.0 * factor));
|
|
buf.data[6] = u32(log2(15.0 * factor));
|
|
buf.data[7] = u32(log2(16.0 * factor));
|
|
buf.data[8] = u32(log2(31.0 * factor));
|
|
buf.data[9] = u32(log2(32.0 * factor));
|
|
buf.data[10] = u32(log2(63.0 * factor));
|
|
buf.data[11] = u32(log2(64.0 * factor));
|
|
buf.data[12] = u32(log2(127.0 * factor));
|
|
buf.data[13] = u32(log2(128.0 * factor));
|
|
buf.data[14] = u32(log2(255.0 * factor));
|
|
buf.data[15] = u32(log2(256.0 * factor));
|
|
buf.data[16] = u32(log2(511.0 * factor));
|
|
buf.data[17] = u32(log2(512.0 * factor));
|
|
buf.data[18] = u32(log2(4294967295.0 * factor));
|
|
})";
|
|
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(shader, "main");
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer}});
|
|
|
|
wgpu::CommandBuffer commands;
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Dispatch(1);
|
|
pass.EndPass();
|
|
|
|
commands = encoder.Finish();
|
|
}
|
|
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), buffer, 0, kSteps);
|
|
}
|
|
|
|
TEST_P(ShaderTests, BadWGSL) {
|
|
DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation"));
|
|
|
|
std::string shader = R"(
|
|
I am an invalid shader and should never pass validation!
|
|
})";
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, shader.c_str()));
|
|
}
|
|
|
|
// Tests that shaders using non-struct function parameters and return values for shader stage I/O
|
|
// can compile and link successfully.
|
|
TEST_P(ShaderTests, WGSLParamIO) {
|
|
std::string vertexShader = R"(
|
|
[[stage(vertex)]]
|
|
fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
|
|
var pos = array<vec2<f32>, 3>(
|
|
vec2<f32>(-1.0, 1.0),
|
|
vec2<f32>( 1.0, 1.0),
|
|
vec2<f32>( 0.0, -1.0));
|
|
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
})";
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vertexShader.c_str());
|
|
|
|
std::string fragmentShader = R"(
|
|
[[stage(fragment)]]
|
|
fn main([[builtin(position)]] fragCoord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
|
return vec4<f32>(fragCoord.xy, 0.0, 1.0);
|
|
})";
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fragmentShader.c_str());
|
|
|
|
utils::ComboRenderPipelineDescriptor rpDesc;
|
|
rpDesc.vertex.module = vsModule;
|
|
rpDesc.cFragment.module = fsModule;
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc);
|
|
}
|
|
|
|
// Tests that a vertex shader using struct function parameters and return values for shader stage
|
|
// I/O can compile and link successfully against a fragement shader using compatible non-struct I/O.
|
|
TEST_P(ShaderTests, WGSLMixedStructParamIO) {
|
|
std::string vertexShader = R"(
|
|
struct VertexIn {
|
|
[[location(0)]] position : vec3<f32>;
|
|
[[location(1)]] color : vec4<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>(input.position, 1.0);
|
|
output.color = input.color;
|
|
return output;
|
|
})";
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vertexShader.c_str());
|
|
|
|
std::string fragmentShader = R"(
|
|
[[stage(fragment)]]
|
|
fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
|
return color;
|
|
})";
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fragmentShader.c_str());
|
|
|
|
utils::ComboRenderPipelineDescriptor rpDesc;
|
|
rpDesc.vertex.module = vsModule;
|
|
rpDesc.cFragment.module = fsModule;
|
|
rpDesc.vertex.bufferCount = 1;
|
|
rpDesc.cBuffers[0].attributeCount = 2;
|
|
rpDesc.cBuffers[0].arrayStride = 28;
|
|
rpDesc.cAttributes[0].shaderLocation = 0;
|
|
rpDesc.cAttributes[0].format = wgpu::VertexFormat::Float32x3;
|
|
rpDesc.cAttributes[1].shaderLocation = 1;
|
|
rpDesc.cAttributes[1].format = wgpu::VertexFormat::Float32x4;
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc);
|
|
}
|
|
|
|
// Tests that shaders using struct function parameters and return values for shader stage I/O
|
|
// can compile and link successfully.
|
|
TEST_P(ShaderTests, WGSLStructIO) {
|
|
std::string vertexShader = R"(
|
|
struct VertexIn {
|
|
[[location(0)]] position : vec3<f32>;
|
|
[[location(1)]] color : vec4<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>(input.position, 1.0);
|
|
output.color = input.color;
|
|
return output;
|
|
})";
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vertexShader.c_str());
|
|
|
|
std::string fragmentShader = R"(
|
|
struct FragmentIn {
|
|
[[location(0)]] color : vec4<f32>;
|
|
[[builtin(position)]] fragCoord : vec4<f32>;
|
|
};
|
|
|
|
[[stage(fragment)]]
|
|
fn main(input : FragmentIn) -> [[location(0)]] vec4<f32> {
|
|
return input.color * input.fragCoord;
|
|
})";
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fragmentShader.c_str());
|
|
|
|
utils::ComboRenderPipelineDescriptor rpDesc;
|
|
rpDesc.vertex.module = vsModule;
|
|
rpDesc.cFragment.module = fsModule;
|
|
rpDesc.vertex.bufferCount = 1;
|
|
rpDesc.cBuffers[0].attributeCount = 2;
|
|
rpDesc.cBuffers[0].arrayStride = 28;
|
|
rpDesc.cAttributes[0].shaderLocation = 0;
|
|
rpDesc.cAttributes[0].format = wgpu::VertexFormat::Float32x3;
|
|
rpDesc.cAttributes[1].shaderLocation = 1;
|
|
rpDesc.cAttributes[1].format = wgpu::VertexFormat::Float32x4;
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc);
|
|
}
|
|
|
|
// Tests that shaders I/O structs that us compatible locations but are not sorted by hand can link.
|
|
TEST_P(ShaderTests, WGSLUnsortedStructIO) {
|
|
std::string vertexShader = R"(
|
|
struct VertexIn {
|
|
[[location(0)]] position : vec3<f32>;
|
|
[[location(1)]] color : vec4<f32>;
|
|
};
|
|
|
|
struct VertexOut {
|
|
[[builtin(position)]] position : vec4<f32>;
|
|
[[location(0)]] color : vec4<f32>;
|
|
};
|
|
|
|
[[stage(vertex)]]
|
|
fn main(input : VertexIn) -> VertexOut {
|
|
var output : VertexOut;
|
|
output.position = vec4<f32>(input.position, 1.0);
|
|
output.color = input.color;
|
|
return output;
|
|
})";
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vertexShader.c_str());
|
|
|
|
std::string fragmentShader = R"(
|
|
struct FragmentIn {
|
|
[[location(0)]] color : vec4<f32>;
|
|
[[builtin(position)]] fragCoord : vec4<f32>;
|
|
};
|
|
|
|
[[stage(fragment)]]
|
|
fn main(input : FragmentIn) -> [[location(0)]] vec4<f32> {
|
|
return input.color * input.fragCoord;
|
|
})";
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fragmentShader.c_str());
|
|
|
|
utils::ComboRenderPipelineDescriptor rpDesc;
|
|
rpDesc.vertex.module = vsModule;
|
|
rpDesc.cFragment.module = fsModule;
|
|
rpDesc.vertex.bufferCount = 1;
|
|
rpDesc.cBuffers[0].attributeCount = 2;
|
|
rpDesc.cBuffers[0].arrayStride = 28;
|
|
rpDesc.cAttributes[0].shaderLocation = 0;
|
|
rpDesc.cAttributes[0].format = wgpu::VertexFormat::Float32x3;
|
|
rpDesc.cAttributes[1].shaderLocation = 1;
|
|
rpDesc.cAttributes[1].format = wgpu::VertexFormat::Float32x4;
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc);
|
|
}
|
|
|
|
// Tests that shaders I/O structs can be shared between vertex and fragment shaders.
|
|
TEST_P(ShaderTests, WGSLSharedStructIO) {
|
|
std::string shader = R"(
|
|
struct VertexIn {
|
|
[[location(0)]] position : vec3<f32>;
|
|
[[location(1)]] color : vec4<f32>;
|
|
};
|
|
|
|
struct VertexOut {
|
|
[[location(0)]] color : vec4<f32>;
|
|
[[builtin(position)]] position : vec4<f32>;
|
|
};
|
|
|
|
[[stage(vertex)]]
|
|
fn vertexMain(input : VertexIn) -> VertexOut {
|
|
var output : VertexOut;
|
|
output.position = vec4<f32>(input.position, 1.0);
|
|
output.color = input.color;
|
|
return output;
|
|
}
|
|
|
|
[[stage(fragment)]]
|
|
fn fragmentMain(input : VertexOut) -> [[location(0)]] vec4<f32> {
|
|
return input.color;
|
|
})";
|
|
wgpu::ShaderModule shaderModule = utils::CreateShaderModule(device, shader.c_str());
|
|
|
|
utils::ComboRenderPipelineDescriptor rpDesc;
|
|
rpDesc.vertex.module = shaderModule;
|
|
rpDesc.vertex.entryPoint = "vertexMain";
|
|
rpDesc.cFragment.module = shaderModule;
|
|
rpDesc.cFragment.entryPoint = "fragmentMain";
|
|
rpDesc.vertex.bufferCount = 1;
|
|
rpDesc.cBuffers[0].attributeCount = 2;
|
|
rpDesc.cBuffers[0].arrayStride = 28;
|
|
rpDesc.cAttributes[0].shaderLocation = 0;
|
|
rpDesc.cAttributes[0].format = wgpu::VertexFormat::Float32x3;
|
|
rpDesc.cAttributes[1].shaderLocation = 1;
|
|
rpDesc.cAttributes[1].format = wgpu::VertexFormat::Float32x4;
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc);
|
|
}
|
|
|
|
// This is a regression test for an issue caused by the FirstIndexOffset transfrom being done before
|
|
// the BindingRemapper, causing an intermediate AST to be invalid (and fail the overall
|
|
// compilation).
|
|
TEST_P(ShaderTests, FirstIndexOffsetRegisterConflictInHLSLTransforms) {
|
|
// TODO(crbug.com/dawn/658): Crashes on bots because there are two entrypoints in the shader.
|
|
DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
|
|
|
|
const char* shader = R"(
|
|
// Dumped WGSL:
|
|
|
|
struct Inputs {
|
|
[[location(1)]] attrib1 : u32;
|
|
// The extra register added to handle base_vertex for vertex_index conflicts with [1]
|
|
[[builtin(vertex_index)]] vertexIndex: u32;
|
|
};
|
|
|
|
// [1] a binding point that conflicts with the regitster
|
|
struct S1 { data : array<vec4<u32>, 20>; };
|
|
[[group(0), binding(1)]] var<uniform> providedData1 : S1;
|
|
|
|
[[stage(vertex)]] fn vsMain(input : Inputs) -> [[builtin(position)]] vec4<f32> {
|
|
_ = providedData1.data[input.vertexIndex][0];
|
|
return vec4<f32>();
|
|
}
|
|
|
|
[[stage(fragment)]] fn fsMain() -> [[location(0)]] vec4<f32> {
|
|
return vec4<f32>();
|
|
}
|
|
)";
|
|
auto module = utils::CreateShaderModule(device, shader);
|
|
|
|
utils::ComboRenderPipelineDescriptor rpDesc;
|
|
rpDesc.vertex.module = module;
|
|
rpDesc.vertex.entryPoint = "vsMain";
|
|
rpDesc.cFragment.module = module;
|
|
rpDesc.cFragment.entryPoint = "fsMain";
|
|
rpDesc.vertex.bufferCount = 1;
|
|
rpDesc.cBuffers[0].attributeCount = 1;
|
|
rpDesc.cBuffers[0].arrayStride = 16;
|
|
rpDesc.cAttributes[0].shaderLocation = 1;
|
|
rpDesc.cAttributes[0].format = wgpu::VertexFormat::Uint8x2;
|
|
device.CreateRenderPipeline(&rpDesc);
|
|
}
|
|
|
|
// Test that WGSL built-in variable [[sample_index]] can be used in fragment shaders.
|
|
TEST_P(ShaderTests, SampleIndex) {
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
|
[[stage(vertex)]]
|
|
fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
|
|
return pos;
|
|
})");
|
|
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
|
[[stage(fragment)]] fn main([[builtin(sample_index)]] sampleIndex : u32)
|
|
-> [[location(0)]] vec4<f32> {
|
|
return vec4<f32>(f32(sampleIndex), 1.0, 0.0, 1.0);
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor;
|
|
descriptor.vertex.module = vsModule;
|
|
descriptor.cFragment.module = fsModule;
|
|
descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
|
|
descriptor.vertex.bufferCount = 1;
|
|
descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
|
|
descriptor.cBuffers[0].attributeCount = 1;
|
|
descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
|
|
descriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
device.CreateRenderPipeline(&descriptor);
|
|
}
|
|
|
|
// Test overridable constants without numeric identifiers
|
|
TEST_P(ShaderTests, OverridableConstants) {
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
|
|
uint32_t const kCount = 11;
|
|
std::vector<uint32_t> expected(kCount);
|
|
std::iota(expected.begin(), expected.end(), 0);
|
|
wgpu::Buffer buffer = CreateBuffer(kCount);
|
|
|
|
std::string shader = R"(
|
|
[[override]] let c0: bool; // type: bool
|
|
[[override]] let c1: bool = false; // default override
|
|
[[override]] let c2: f32; // type: float32
|
|
[[override]] let c3: f32 = 0.0; // default override
|
|
[[override]] let c4: f32 = 4.0; // default
|
|
[[override]] let c5: i32; // type: int32
|
|
[[override]] let c6: i32 = 0; // default override
|
|
[[override]] let c7: i32 = 7; // default
|
|
[[override]] let c8: u32; // type: uint32
|
|
[[override]] let c9: u32 = 0u; // default override
|
|
[[override]] let c10: u32 = 10u; // default
|
|
|
|
struct Buf {
|
|
data : array<u32, 11>;
|
|
};
|
|
|
|
[[group(0), binding(0)]] var<storage, read_write> buf : Buf;
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main() {
|
|
buf.data[0] = u32(c0);
|
|
buf.data[1] = u32(c1);
|
|
buf.data[2] = u32(c2);
|
|
buf.data[3] = u32(c3);
|
|
buf.data[4] = u32(c4);
|
|
buf.data[5] = u32(c5);
|
|
buf.data[6] = u32(c6);
|
|
buf.data[7] = u32(c7);
|
|
buf.data[8] = u32(c8);
|
|
buf.data[9] = u32(c9);
|
|
buf.data[10] = u32(c10);
|
|
})";
|
|
|
|
std::vector<wgpu::ConstantEntry> constants;
|
|
constants.push_back({nullptr, "c0", 0});
|
|
constants.push_back({nullptr, "c1", 1});
|
|
constants.push_back({nullptr, "c2", 2});
|
|
constants.push_back({nullptr, "c3", 3});
|
|
// c4 is not assigned, testing default value
|
|
constants.push_back({nullptr, "c5", 5});
|
|
constants.push_back({nullptr, "c6", 6});
|
|
// c7 is not assigned, testing default value
|
|
constants.push_back({nullptr, "c8", 8});
|
|
constants.push_back({nullptr, "c9", 9});
|
|
// c10 is not assigned, testing default value
|
|
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(shader, "main", &constants);
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer}});
|
|
|
|
wgpu::CommandBuffer commands;
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Dispatch(1);
|
|
pass.EndPass();
|
|
|
|
commands = encoder.Finish();
|
|
}
|
|
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), buffer, 0, kCount);
|
|
}
|
|
|
|
// Test overridable constants with numeric identifiers
|
|
TEST_P(ShaderTests, OverridableConstantsNumericIdentifiers) {
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
|
|
uint32_t const kCount = 4;
|
|
std::vector<uint32_t> expected{1u, 2u, 3u, 0u};
|
|
wgpu::Buffer buffer = CreateBuffer(kCount);
|
|
|
|
std::string shader = R"(
|
|
[[override(1001)]] let c1: u32; // some big numeric id
|
|
[[override(1)]] let c2: u32 = 0u; // id == 1 might collide with some generated constant id
|
|
[[override(1003)]] let c3: u32 = 3u; // default
|
|
[[override(1004)]] let c4: u32; // default unspecified
|
|
|
|
struct Buf {
|
|
data : array<u32, 4>;
|
|
};
|
|
|
|
[[group(0), binding(0)]] var<storage, read_write> buf : Buf;
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main() {
|
|
buf.data[0] = c1;
|
|
buf.data[1] = c2;
|
|
buf.data[2] = c3;
|
|
buf.data[3] = c4;
|
|
})";
|
|
|
|
std::vector<wgpu::ConstantEntry> constants;
|
|
constants.push_back({nullptr, "1001", 1});
|
|
constants.push_back({nullptr, "1", 2});
|
|
// c3 is not assigned, testing default value
|
|
constants.push_back({nullptr, "1004", 0});
|
|
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(shader, "main", &constants);
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer}});
|
|
|
|
wgpu::CommandBuffer commands;
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Dispatch(1);
|
|
pass.EndPass();
|
|
|
|
commands = encoder.Finish();
|
|
}
|
|
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), buffer, 0, kCount);
|
|
}
|
|
|
|
// Test overridable constants precision
|
|
// D3D12 HLSL shader uses defines so we want float number to have enough precision
|
|
TEST_P(ShaderTests, OverridableConstantsPrecision) {
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
|
|
uint32_t const kCount = 2;
|
|
float const kValue1 = 3.14159;
|
|
float const kValue2 = 3.141592653589793238;
|
|
std::vector<float> expected{kValue1, kValue2};
|
|
wgpu::Buffer buffer = CreateBuffer(kCount);
|
|
|
|
std::string shader = R"(
|
|
[[override(1001)]] let c1: f32;
|
|
[[override(1002)]] let c2: f32;
|
|
|
|
struct Buf {
|
|
data : array<f32, 2>;
|
|
};
|
|
|
|
[[group(0), binding(0)]] var<storage, read_write> buf : Buf;
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main() {
|
|
buf.data[0] = c1;
|
|
buf.data[1] = c2;
|
|
})";
|
|
|
|
std::vector<wgpu::ConstantEntry> constants;
|
|
constants.push_back({nullptr, "1001", kValue1});
|
|
constants.push_back({nullptr, "1002", kValue2});
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(shader, "main", &constants);
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer}});
|
|
|
|
wgpu::CommandBuffer commands;
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Dispatch(1);
|
|
pass.EndPass();
|
|
|
|
commands = encoder.Finish();
|
|
}
|
|
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_BUFFER_FLOAT_RANGE_EQ(expected.data(), buffer, 0, kCount);
|
|
}
|
|
|
|
// Test overridable constants for different entry points
|
|
TEST_P(ShaderTests, OverridableConstantsMultipleEntryPoints) {
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
|
|
uint32_t const kCount = 1;
|
|
std::vector<uint32_t> expected1{1u};
|
|
std::vector<uint32_t> expected2{2u};
|
|
std::vector<uint32_t> expected3{3u};
|
|
|
|
wgpu::Buffer buffer1 = CreateBuffer(kCount);
|
|
wgpu::Buffer buffer2 = CreateBuffer(kCount);
|
|
wgpu::Buffer buffer3 = CreateBuffer(kCount);
|
|
|
|
std::string shader = R"(
|
|
[[override(1001)]] let c1: u32;
|
|
[[override(1002)]] let c2: u32;
|
|
|
|
struct Buf {
|
|
data : array<u32, 1>;
|
|
};
|
|
|
|
[[group(0), binding(0)]] var<storage, read_write> buf : Buf;
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main1() {
|
|
buf.data[0] = c1;
|
|
}
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main2() {
|
|
buf.data[0] = c2;
|
|
}
|
|
|
|
[[stage(compute), workgroup_size(1)]] fn main3() {
|
|
buf.data[0] = 3u;
|
|
}
|
|
)";
|
|
|
|
std::vector<wgpu::ConstantEntry> constants1;
|
|
constants1.push_back({nullptr, "1001", 1});
|
|
std::vector<wgpu::ConstantEntry> constants2;
|
|
constants2.push_back({nullptr, "1002", 2});
|
|
|
|
wgpu::ShaderModule shaderModule = utils::CreateShaderModule(device, shader.c_str());
|
|
|
|
wgpu::ComputePipelineDescriptor csDesc1;
|
|
csDesc1.compute.module = shaderModule;
|
|
csDesc1.compute.entryPoint = "main1";
|
|
csDesc1.compute.constants = constants1.data();
|
|
csDesc1.compute.constantCount = constants1.size();
|
|
wgpu::ComputePipeline pipeline1 = device.CreateComputePipeline(&csDesc1);
|
|
|
|
wgpu::ComputePipelineDescriptor csDesc2;
|
|
csDesc2.compute.module = shaderModule;
|
|
csDesc2.compute.entryPoint = "main2";
|
|
csDesc2.compute.constants = constants2.data();
|
|
csDesc2.compute.constantCount = constants2.size();
|
|
wgpu::ComputePipeline pipeline2 = device.CreateComputePipeline(&csDesc2);
|
|
|
|
wgpu::ComputePipelineDescriptor csDesc3;
|
|
csDesc3.compute.module = shaderModule;
|
|
csDesc3.compute.entryPoint = "main3";
|
|
wgpu::ComputePipeline pipeline3 = device.CreateComputePipeline(&csDesc3);
|
|
|
|
wgpu::BindGroup bindGroup1 =
|
|
utils::MakeBindGroup(device, pipeline1.GetBindGroupLayout(0), {{0, buffer1}});
|
|
wgpu::BindGroup bindGroup2 =
|
|
utils::MakeBindGroup(device, pipeline2.GetBindGroupLayout(0), {{0, buffer2}});
|
|
wgpu::BindGroup bindGroup3 =
|
|
utils::MakeBindGroup(device, pipeline3.GetBindGroupLayout(0), {{0, buffer3}});
|
|
|
|
wgpu::CommandBuffer commands;
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline1);
|
|
pass.SetBindGroup(0, bindGroup1);
|
|
pass.Dispatch(1);
|
|
|
|
pass.SetPipeline(pipeline2);
|
|
pass.SetBindGroup(0, bindGroup2);
|
|
pass.Dispatch(1);
|
|
|
|
pass.SetPipeline(pipeline3);
|
|
pass.SetBindGroup(0, bindGroup3);
|
|
pass.Dispatch(1);
|
|
|
|
pass.EndPass();
|
|
|
|
commands = encoder.Finish();
|
|
}
|
|
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(expected1.data(), buffer1, 0, kCount);
|
|
EXPECT_BUFFER_U32_RANGE_EQ(expected2.data(), buffer2, 0, kCount);
|
|
EXPECT_BUFFER_U32_RANGE_EQ(expected3.data(), buffer3, 0, kCount);
|
|
}
|
|
|
|
// Test overridable constants with render pipeline
|
|
// Draw a triangle covering the render target, with vertex position and color values from
|
|
// overridable constants
|
|
TEST_P(ShaderTests, OverridableConstantsRenderPipeline) {
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
|
[[override(1111)]] let xright: f32;
|
|
[[override(2222)]] let ytop: f32;
|
|
[[stage(vertex)]]
|
|
fn main([[builtin(vertex_index)]] VertexIndex : u32)
|
|
-> [[builtin(position)]] vec4<f32> {
|
|
var pos = array<vec2<f32>, 3>(
|
|
vec2<f32>(-1.0, ytop),
|
|
vec2<f32>(-1.0, -ytop),
|
|
vec2<f32>(xright, 0.0));
|
|
|
|
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
})");
|
|
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
|
[[override(1000)]] let intensity: f32 = 0.0;
|
|
[[stage(fragment)]] fn main()
|
|
-> [[location(0)]] vec4<f32> {
|
|
return vec4<f32>(intensity, intensity, intensity, 1.0);
|
|
})");
|
|
|
|
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor;
|
|
descriptor.vertex.module = vsModule;
|
|
descriptor.cFragment.module = fsModule;
|
|
descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
|
|
descriptor.cTargets[0].format = renderPass.colorFormat;
|
|
|
|
std::vector<wgpu::ConstantEntry> vertexConstants;
|
|
vertexConstants.push_back({nullptr, "1111", 3.0}); // x right
|
|
vertexConstants.push_back({nullptr, "2222", 3.0}); // y top
|
|
descriptor.vertex.constants = vertexConstants.data();
|
|
descriptor.vertex.constantCount = vertexConstants.size();
|
|
std::vector<wgpu::ConstantEntry> fragmentConstants;
|
|
fragmentConstants.push_back({nullptr, "1000", 1.0}); // color intensity
|
|
descriptor.cFragment.constants = fragmentConstants.data();
|
|
descriptor.cFragment.constantCount = fragmentConstants.size();
|
|
|
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
pass.SetPipeline(pipeline);
|
|
pass.Draw(3);
|
|
pass.EndPass();
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(255, 255, 255, 255), renderPass.color, 0, 0);
|
|
}
|
|
|
|
// TODO(tint:1155): Test overridable constants used for workgroup size
|
|
|
|
DAWN_INSTANTIATE_TEST(ShaderTests,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|