mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-30 17:05:48 +00:00
This reverts commit 3fcf96dd8c262c69029a47ce6ad85314fcd3d69b. Reason for revert: want to enable end2end test piece by piece on bots for d3d11 Original change's description: > d3d11: add d3d11 backend in end2end tests > > Right now, many tests are not passed becasue unimplemented > features in d3d11 backend. HoweverD3D11 backend is disabled on > bots by default, so this CL will not break out bots. > > Bug: dawn:1705 > Change-Id: I57321b86a404bc245b71c467479fdee0464dee9b > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/126260 > Commit-Queue: Peng Huang <penghuang@chromium.org> > Reviewed-by: Austin Eng <enga@chromium.org> > Kokoro: Kokoro <noreply+kokoro@google.com> Bug: dawn:1705 Change-Id: I95a1cc9a0962b01a6b31ea32b6129f109f4b3e42 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127240 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Peng Huang <penghuang@chromium.org>
941 lines
41 KiB
C++
941 lines
41 KiB
C++
// Copyright 2020 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 <utility>
|
|
#include <vector>
|
|
|
|
#include "dawn/common/Assert.h"
|
|
#include "dawn/tests/DawnTest.h"
|
|
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
|
|
#include "dawn/utils/WGPUHelpers.h"
|
|
|
|
namespace {
|
|
using TextureFormat = wgpu::TextureFormat;
|
|
DAWN_TEST_PARAM_STRUCT(DepthStencilSamplingTestParams, TextureFormat);
|
|
|
|
constexpr wgpu::CompareFunction kCompareFunctions[] = {
|
|
wgpu::CompareFunction::Never, wgpu::CompareFunction::Less,
|
|
wgpu::CompareFunction::LessEqual, wgpu::CompareFunction::Greater,
|
|
wgpu::CompareFunction::GreaterEqual, wgpu::CompareFunction::Equal,
|
|
wgpu::CompareFunction::NotEqual, wgpu::CompareFunction::Always,
|
|
};
|
|
|
|
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
|
|
constexpr float kCompareRefs[] = {-0.1, 0.4, 1.2};
|
|
|
|
// Test 0, below the ref, equal to, above the ref, and 1.
|
|
const std::vector<float> kNormalizedTextureValues = {0.0, 0.3, 0.4, 0.5, 1.0};
|
|
|
|
// Test the limits, and some values in between.
|
|
const std::vector<uint32_t> kStencilValues = {0, 1, 38, 255};
|
|
|
|
} // anonymous namespace
|
|
|
|
class DepthStencilSamplingTest : public DawnTestWithParams<DepthStencilSamplingTestParams> {
|
|
protected:
|
|
enum class TestAspectAndSamplerType {
|
|
DepthAsDepth,
|
|
DepthAsFloat,
|
|
StencilAsUint,
|
|
};
|
|
|
|
void SetUp() override {
|
|
DawnTestWithParams<DepthStencilSamplingTestParams>::SetUp();
|
|
|
|
DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
|
|
|
|
wgpu::BufferDescriptor uniformBufferDesc;
|
|
uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
|
|
uniformBufferDesc.size = sizeof(float);
|
|
mUniformBuffer = device.CreateBuffer(&uniformBufferDesc);
|
|
}
|
|
|
|
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
|
|
switch (GetParam().mTextureFormat) {
|
|
case wgpu::TextureFormat::Depth32FloatStencil8:
|
|
if (SupportsFeatures({wgpu::FeatureName::Depth32FloatStencil8})) {
|
|
mIsFormatSupported = true;
|
|
return {wgpu::FeatureName::Depth32FloatStencil8};
|
|
}
|
|
return {};
|
|
default:
|
|
mIsFormatSupported = true;
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void GenerateSamplingShader(const std::vector<TestAspectAndSamplerType>& aspectAndSamplerTypes,
|
|
const std::vector<uint32_t> components,
|
|
std::ostringstream& shaderSource,
|
|
std::ostringstream& shaderBody) {
|
|
shaderSource << "alias StencilValues = array<u32, " << components.size() << ">;\n";
|
|
shaderSource << R"(
|
|
struct DepthResult {
|
|
value : f32
|
|
}
|
|
struct StencilResult {
|
|
values : StencilValues
|
|
})";
|
|
shaderSource << "\n";
|
|
|
|
uint32_t index = 0;
|
|
for (TestAspectAndSamplerType aspectAndSamplerType : aspectAndSamplerTypes) {
|
|
switch (aspectAndSamplerType) {
|
|
case TestAspectAndSamplerType::DepthAsDepth:
|
|
shaderSource << "@group(0) @binding(" << 2 * index << ") var tex" << index
|
|
<< " : texture_depth_2d;\n";
|
|
|
|
shaderSource << "@group(0) @binding(" << 2 * index + 1
|
|
<< ") var<storage, read_write> result" << index
|
|
<< " : DepthResult;\n";
|
|
|
|
ASSERT(components.size() == 1 && components[0] == 0);
|
|
shaderBody << "\nresult" << index << ".value = textureLoad(tex" << index
|
|
<< ", vec2i(0, 0), 0);";
|
|
break;
|
|
case TestAspectAndSamplerType::DepthAsFloat:
|
|
shaderSource << "@group(0) @binding(" << 2 * index << ") var tex" << index
|
|
<< " : texture_2d<f32>;\n";
|
|
|
|
shaderSource << "@group(0) @binding(" << 2 * index + 1
|
|
<< ") var<storage, read_write> result" << index
|
|
<< " : DepthResult;\n";
|
|
|
|
ASSERT(components.size() == 1 && components[0] == 0);
|
|
shaderBody << "\nresult" << index << ".value = textureLoad(tex" << index
|
|
<< ", vec2i(0, 0), 0)[0];";
|
|
break;
|
|
case TestAspectAndSamplerType::StencilAsUint:
|
|
shaderSource << "@group(0) @binding(" << 2 * index << ") var tex" << index
|
|
<< " : texture_2d<u32>;\n";
|
|
|
|
shaderSource << "@group(0) @binding(" << 2 * index + 1
|
|
<< ") var<storage, read_write> result" << index
|
|
<< " : StencilResult;\n";
|
|
|
|
shaderBody << "var texel = textureLoad(tex" << index << ", vec2i(0, 0), 0);";
|
|
|
|
for (uint32_t i = 0; i < components.size(); ++i) {
|
|
shaderBody << "\nresult" << index << ".values[" << i << "] = texel["
|
|
<< components[i] << "];";
|
|
}
|
|
break;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
wgpu::RenderPipeline CreateSamplingRenderPipeline(
|
|
std::vector<TestAspectAndSamplerType> aspectAndSamplerTypes,
|
|
std::vector<uint32_t> components) {
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
|
@vertex fn main() -> @builtin(position) vec4f {
|
|
return vec4f(0.0, 0.0, 0.0, 1.0);
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
|
|
|
|
std::ostringstream shaderSource;
|
|
std::ostringstream shaderOutputStruct;
|
|
std::ostringstream shaderBody;
|
|
|
|
GenerateSamplingShader(aspectAndSamplerTypes, components, shaderSource, shaderBody);
|
|
|
|
shaderSource << "@fragment fn main() -> @location(0) vec4f {\n";
|
|
shaderSource << shaderBody.str() << "return vec4f();\n }";
|
|
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, shaderSource.str().c_str());
|
|
pipelineDescriptor.vertex.module = vsModule;
|
|
pipelineDescriptor.cFragment.module = fsModule;
|
|
pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
|
pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
|
|
|
return device.CreateRenderPipeline(&pipelineDescriptor);
|
|
}
|
|
|
|
wgpu::ComputePipeline CreateSamplingComputePipeline(
|
|
std::vector<TestAspectAndSamplerType> aspectAndSamplerTypes,
|
|
std::vector<uint32_t> components) {
|
|
std::ostringstream shaderSource;
|
|
std::ostringstream shaderBody;
|
|
GenerateSamplingShader(aspectAndSamplerTypes, components, shaderSource, shaderBody);
|
|
|
|
shaderSource << "@compute @workgroup_size(1) fn main() { " << shaderBody.str() << "\n}";
|
|
|
|
wgpu::ShaderModule csModule = utils::CreateShaderModule(device, shaderSource.str().c_str());
|
|
|
|
wgpu::ComputePipelineDescriptor pipelineDescriptor;
|
|
pipelineDescriptor.compute.module = csModule;
|
|
pipelineDescriptor.compute.entryPoint = "main";
|
|
|
|
return device.CreateComputePipeline(&pipelineDescriptor);
|
|
}
|
|
|
|
wgpu::RenderPipeline CreateSamplingRenderPipeline(
|
|
std::vector<TestAspectAndSamplerType> aspectAndSamplerTypes,
|
|
uint32_t componentIndex) {
|
|
return CreateSamplingRenderPipeline(std::move(aspectAndSamplerTypes),
|
|
std::vector<uint32_t>{componentIndex});
|
|
}
|
|
|
|
wgpu::ComputePipeline CreateSamplingComputePipeline(
|
|
std::vector<TestAspectAndSamplerType> aspectAndSamplerTypes,
|
|
uint32_t componentIndex) {
|
|
return CreateSamplingComputePipeline(std::move(aspectAndSamplerTypes),
|
|
std::vector<uint32_t>{componentIndex});
|
|
}
|
|
|
|
wgpu::RenderPipeline CreateComparisonRenderPipeline() {
|
|
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"(
|
|
@group(0) @binding(0) var samp : sampler_comparison;
|
|
@group(0) @binding(1) var tex : texture_depth_2d;
|
|
struct Uniforms {
|
|
compareRef : f32
|
|
}
|
|
@group(0) @binding(2) var<uniform> uniforms : Uniforms;
|
|
|
|
@fragment fn main() -> @location(0) f32 {
|
|
return textureSampleCompare(tex, samp, vec2f(0.5, 0.5), uniforms.compareRef);
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
|
|
pipelineDescriptor.vertex.module = vsModule;
|
|
pipelineDescriptor.cFragment.module = fsModule;
|
|
pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
|
pipelineDescriptor.cTargets[0].format = wgpu::TextureFormat::R32Float;
|
|
|
|
return device.CreateRenderPipeline(&pipelineDescriptor);
|
|
}
|
|
|
|
wgpu::ComputePipeline CreateComparisonComputePipeline() {
|
|
wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
|
|
@group(0) @binding(0) var samp : sampler_comparison;
|
|
@group(0) @binding(1) var tex : texture_depth_2d;
|
|
struct Uniforms {
|
|
compareRef : f32
|
|
}
|
|
@group(0) @binding(2) var<uniform> uniforms : Uniforms;
|
|
|
|
struct SamplerResult {
|
|
value : f32
|
|
}
|
|
@group(0) @binding(3) var<storage, read_write> samplerResult : SamplerResult;
|
|
|
|
@compute @workgroup_size(1) fn main() {
|
|
samplerResult.value = textureSampleCompare(tex, samp, vec2f(0.5, 0.5), uniforms.compareRef);
|
|
})");
|
|
|
|
wgpu::ComputePipelineDescriptor pipelineDescriptor;
|
|
pipelineDescriptor.compute.module = csModule;
|
|
pipelineDescriptor.compute.entryPoint = "main";
|
|
|
|
return device.CreateComputePipeline(&pipelineDescriptor);
|
|
}
|
|
|
|
wgpu::Texture CreateInputTexture(wgpu::TextureFormat format) {
|
|
wgpu::TextureDescriptor inputTextureDesc;
|
|
inputTextureDesc.usage =
|
|
wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
|
|
inputTextureDesc.size = {1, 1, 1};
|
|
inputTextureDesc.format = format;
|
|
return device.CreateTexture(&inputTextureDesc);
|
|
}
|
|
|
|
wgpu::Texture CreateOutputTexture(wgpu::TextureFormat format) {
|
|
wgpu::TextureDescriptor outputTextureDesc;
|
|
outputTextureDesc.usage =
|
|
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
|
outputTextureDesc.size = {1, 1, 1};
|
|
outputTextureDesc.format = format;
|
|
return device.CreateTexture(&outputTextureDesc);
|
|
}
|
|
|
|
wgpu::Buffer CreateOutputBuffer(uint32_t componentCount = 1) {
|
|
wgpu::BufferDescriptor outputBufferDesc;
|
|
outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
|
|
outputBufferDesc.size = sizeof(uint32_t) * componentCount;
|
|
return device.CreateBuffer(&outputBufferDesc);
|
|
}
|
|
|
|
void UpdateInputDepth(wgpu::CommandEncoder commandEncoder,
|
|
wgpu::Texture texture,
|
|
wgpu::TextureFormat format,
|
|
float depthValue) {
|
|
utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
|
|
passDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(format);
|
|
passDescriptor.cDepthStencilAttachmentInfo.depthClearValue = depthValue;
|
|
|
|
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
|
|
pass.End();
|
|
}
|
|
|
|
void UpdateInputStencil(wgpu::CommandEncoder commandEncoder,
|
|
wgpu::Texture texture,
|
|
wgpu::TextureFormat format,
|
|
uint8_t stencilValue) {
|
|
utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
|
|
passDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(format);
|
|
passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue = stencilValue;
|
|
|
|
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
|
|
pass.End();
|
|
}
|
|
|
|
template <typename T, typename CheckBufferFn>
|
|
void DoSamplingTestImpl(TestAspectAndSamplerType aspectAndSamplerType,
|
|
wgpu::RenderPipeline pipeline,
|
|
wgpu::TextureFormat format,
|
|
std::vector<T> textureValues,
|
|
uint32_t componentCount,
|
|
CheckBufferFn CheckBuffer) {
|
|
wgpu::Texture inputTexture = CreateInputTexture(format);
|
|
wgpu::TextureViewDescriptor inputViewDesc = {};
|
|
switch (aspectAndSamplerType) {
|
|
case TestAspectAndSamplerType::DepthAsDepth:
|
|
case TestAspectAndSamplerType::DepthAsFloat:
|
|
inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
|
|
break;
|
|
case TestAspectAndSamplerType::StencilAsUint:
|
|
inputViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
|
|
break;
|
|
}
|
|
|
|
wgpu::Buffer outputBuffer = CreateOutputBuffer(componentCount);
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{{0, inputTexture.CreateView(&inputViewDesc)}, {1, outputBuffer}});
|
|
|
|
for (size_t i = 0; i < textureValues.size(); ++i) {
|
|
// Set the input depth texture to the provided texture value
|
|
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
switch (aspectAndSamplerType) {
|
|
case TestAspectAndSamplerType::DepthAsDepth:
|
|
case TestAspectAndSamplerType::DepthAsFloat:
|
|
UpdateInputDepth(commandEncoder, inputTexture, format, textureValues[i]);
|
|
break;
|
|
case TestAspectAndSamplerType::StencilAsUint:
|
|
UpdateInputStencil(commandEncoder, inputTexture, format, textureValues[i]);
|
|
break;
|
|
}
|
|
|
|
// Render into the output texture
|
|
{
|
|
utils::BasicRenderPass renderPass =
|
|
utils::CreateBasicRenderPass(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm);
|
|
wgpu::RenderPassEncoder pass =
|
|
commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Draw(1);
|
|
pass.End();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
CheckBuffer(textureValues[i], outputBuffer);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename CheckBufferFn>
|
|
void DoSamplingTestImpl(TestAspectAndSamplerType aspectAndSamplerType,
|
|
wgpu::ComputePipeline pipeline,
|
|
wgpu::TextureFormat format,
|
|
std::vector<T> textureValues,
|
|
uint32_t componentCount,
|
|
CheckBufferFn CheckBuffer) {
|
|
wgpu::Texture inputTexture = CreateInputTexture(format);
|
|
wgpu::TextureViewDescriptor inputViewDesc = {};
|
|
switch (aspectAndSamplerType) {
|
|
case TestAspectAndSamplerType::DepthAsDepth:
|
|
case TestAspectAndSamplerType::DepthAsFloat:
|
|
inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
|
|
break;
|
|
case TestAspectAndSamplerType::StencilAsUint:
|
|
inputViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
|
|
break;
|
|
}
|
|
|
|
wgpu::Buffer outputBuffer = CreateOutputBuffer(componentCount);
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{{0, inputTexture.CreateView(&inputViewDesc)}, {1, outputBuffer}});
|
|
|
|
for (size_t i = 0; i < textureValues.size(); ++i) {
|
|
// Set the input depth texture to the provided texture value
|
|
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
switch (aspectAndSamplerType) {
|
|
case TestAspectAndSamplerType::DepthAsDepth:
|
|
case TestAspectAndSamplerType::DepthAsFloat:
|
|
UpdateInputDepth(commandEncoder, inputTexture, format, textureValues[i]);
|
|
break;
|
|
case TestAspectAndSamplerType::StencilAsUint:
|
|
UpdateInputStencil(commandEncoder, inputTexture, format, textureValues[i]);
|
|
break;
|
|
}
|
|
|
|
// Sample into the output buffer
|
|
{
|
|
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.DispatchWorkgroups(1);
|
|
pass.End();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
CheckBuffer(textureValues[i], outputBuffer);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void DoSamplingTest(TestAspectAndSamplerType aspectAndSamplerType,
|
|
wgpu::RenderPipeline pipeline,
|
|
wgpu::TextureFormat format,
|
|
std::vector<T> textureValues,
|
|
T tolerance = {}) {
|
|
DoSamplingTestImpl(aspectAndSamplerType, pipeline, format, textureValues, 1,
|
|
[this, tolerance](T expected, wgpu::Buffer buffer) {
|
|
EXPECT_BUFFER(buffer, 0, sizeof(T),
|
|
new ::detail::ExpectEq<T>(expected, tolerance));
|
|
});
|
|
}
|
|
|
|
template <typename T>
|
|
void DoSamplingTest(TestAspectAndSamplerType aspectAndSamplerType,
|
|
wgpu::ComputePipeline pipeline,
|
|
wgpu::TextureFormat format,
|
|
std::vector<T> textureValues,
|
|
T tolerance = {}) {
|
|
DoSamplingTestImpl(aspectAndSamplerType, pipeline, format, textureValues, 1,
|
|
[this, tolerance](T expected, wgpu::Buffer buffer) {
|
|
EXPECT_BUFFER(buffer, 0, sizeof(T),
|
|
new ::detail::ExpectEq<T>(expected, tolerance));
|
|
});
|
|
}
|
|
|
|
class ExtraStencilComponentsExpectation : public detail::Expectation {
|
|
using StencilData = std::array<uint32_t, 4>;
|
|
|
|
public:
|
|
explicit ExtraStencilComponentsExpectation(uint32_t expected) : mExpected(expected) {}
|
|
|
|
~ExtraStencilComponentsExpectation() override = default;
|
|
|
|
testing::AssertionResult Check(const void* rawData, size_t size) override {
|
|
ASSERT(size == sizeof(StencilData));
|
|
const uint32_t* data = static_cast<const uint32_t*>(rawData);
|
|
|
|
StencilData ssss = {mExpected, mExpected, mExpected, mExpected};
|
|
StencilData s001 = {mExpected, 0, 0, 1};
|
|
|
|
if (memcmp(data, ssss.data(), size) == 0 || memcmp(data, s001.data(), size) == 0) {
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
return testing::AssertionFailure() << "Expected stencil data to be "
|
|
<< "(" << ssss[0] << ", " << ssss[1] << ", "
|
|
<< ssss[2] << ", " << ssss[3] << ") or "
|
|
<< "(" << s001[0] << ", " << s001[1] << ", "
|
|
<< s001[2] << ", " << s001[3] << "). Got "
|
|
<< "(" << data[0] << ", " << data[1] << ", "
|
|
<< data[2] << ", " << data[3] << ").";
|
|
}
|
|
|
|
private:
|
|
uint32_t mExpected;
|
|
};
|
|
|
|
void DoSamplingExtraStencilComponentsRenderTest(TestAspectAndSamplerType aspectAndSamplerType,
|
|
wgpu::TextureFormat format,
|
|
std::vector<uint8_t> textureValues) {
|
|
DoSamplingTestImpl(
|
|
aspectAndSamplerType,
|
|
CreateSamplingRenderPipeline({TestAspectAndSamplerType::StencilAsUint}, {0, 1, 2, 3}),
|
|
format, textureValues, 4, [&](uint32_t expected, wgpu::Buffer buffer) {
|
|
EXPECT_BUFFER(buffer, 0, 4 * sizeof(uint32_t),
|
|
new ExtraStencilComponentsExpectation(expected));
|
|
});
|
|
}
|
|
|
|
void DoSamplingExtraStencilComponentsComputeTest(TestAspectAndSamplerType aspectAndSamplerType,
|
|
wgpu::TextureFormat format,
|
|
std::vector<uint8_t> textureValues) {
|
|
DoSamplingTestImpl(
|
|
aspectAndSamplerType,
|
|
CreateSamplingComputePipeline({TestAspectAndSamplerType::StencilAsUint}, {0, 1, 2, 3}),
|
|
format, textureValues, 4, [&](uint32_t expected, wgpu::Buffer buffer) {
|
|
EXPECT_BUFFER(buffer, 0, 4 * sizeof(uint32_t),
|
|
new ExtraStencilComponentsExpectation(expected));
|
|
});
|
|
}
|
|
|
|
static bool CompareFunctionPasses(float compareRef,
|
|
wgpu::CompareFunction compare,
|
|
float textureValue) {
|
|
switch (compare) {
|
|
case wgpu::CompareFunction::Never:
|
|
return false;
|
|
case wgpu::CompareFunction::Less:
|
|
return compareRef < textureValue;
|
|
case wgpu::CompareFunction::LessEqual:
|
|
return compareRef <= textureValue;
|
|
case wgpu::CompareFunction::Greater:
|
|
return compareRef > textureValue;
|
|
case wgpu::CompareFunction::GreaterEqual:
|
|
return compareRef >= textureValue;
|
|
case wgpu::CompareFunction::Equal:
|
|
return compareRef == textureValue;
|
|
case wgpu::CompareFunction::NotEqual:
|
|
return compareRef != textureValue;
|
|
case wgpu::CompareFunction::Always:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DoDepthCompareRefTest(wgpu::RenderPipeline pipeline,
|
|
wgpu::TextureFormat format,
|
|
float compareRef,
|
|
wgpu::CompareFunction compare,
|
|
std::vector<float> textureValues) {
|
|
queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float));
|
|
|
|
wgpu::SamplerDescriptor samplerDesc;
|
|
samplerDesc.compare = compare;
|
|
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
|
|
|
|
wgpu::Texture inputTexture = CreateInputTexture(format);
|
|
wgpu::TextureViewDescriptor inputViewDesc = {};
|
|
inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{
|
|
{0, sampler},
|
|
{1, inputTexture.CreateView(&inputViewDesc)},
|
|
{2, mUniformBuffer},
|
|
});
|
|
|
|
wgpu::Texture outputTexture = CreateOutputTexture(wgpu::TextureFormat::R32Float);
|
|
for (float textureValue : textureValues) {
|
|
// Set the input depth texture to the provided texture value
|
|
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
UpdateInputDepth(commandEncoder, inputTexture, format, textureValue);
|
|
|
|
// Render into the output texture
|
|
{
|
|
utils::ComboRenderPassDescriptor passDescriptor({outputTexture.CreateView()});
|
|
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Draw(1);
|
|
pass.End();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
EXPECT_TEXTURE_EQ(CompareFunctionPasses(compareRef, compare, textureValue) ? 1.f : 0.f,
|
|
outputTexture, {0, 0});
|
|
}
|
|
}
|
|
|
|
void DoDepthCompareRefTest(wgpu::ComputePipeline pipeline,
|
|
wgpu::TextureFormat format,
|
|
float compareRef,
|
|
wgpu::CompareFunction compare,
|
|
std::vector<float> textureValues) {
|
|
queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float));
|
|
|
|
wgpu::SamplerDescriptor samplerDesc;
|
|
samplerDesc.compare = compare;
|
|
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
|
|
|
|
wgpu::Texture inputTexture = CreateInputTexture(format);
|
|
wgpu::TextureViewDescriptor inputViewDesc = {};
|
|
inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
|
|
|
|
wgpu::Buffer outputBuffer = CreateOutputBuffer();
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{{0, sampler},
|
|
{1, inputTexture.CreateView(&inputViewDesc)},
|
|
{2, mUniformBuffer},
|
|
{3, outputBuffer}});
|
|
|
|
for (float textureValue : textureValues) {
|
|
// Set the input depth texture to the provided texture value
|
|
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
UpdateInputDepth(commandEncoder, inputTexture, format, textureValue);
|
|
|
|
// Sample into the output buffer
|
|
{
|
|
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.DispatchWorkgroups(1);
|
|
pass.End();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
float float0 = 0.f;
|
|
float float1 = 1.f;
|
|
float* expected =
|
|
CompareFunctionPasses(compareRef, compare, textureValue) ? &float1 : &float0;
|
|
|
|
EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), outputBuffer, 0);
|
|
}
|
|
}
|
|
|
|
private:
|
|
wgpu::Buffer mUniformBuffer;
|
|
bool mIsFormatSupported = false;
|
|
};
|
|
|
|
// Repro test for crbug.com/dawn/1187 where sampling a depth texture returns values not in [0, 1]
|
|
TEST_P(DepthStencilSamplingTest, CheckDepthTextureRange) {
|
|
// TODO(crbug.com/dawn/1187): The test fails on ANGLE D3D11, investigate why.
|
|
DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
|
|
|
|
constexpr uint32_t kWidth = 16;
|
|
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
|
|
const kWidth = 16.0;
|
|
|
|
// Write a point per texel of a height = 0 texture with depths varying between 0 and 1.
|
|
@vertex fn vs(@builtin(vertex_index) index : u32) -> @builtin(position) vec4f {
|
|
let x = (f32(index) + 0.5) / kWidth * 2.0 - 1.0;
|
|
let z = f32(index) / (kWidth - 1.0);
|
|
return vec4(x, 0.0, z, 1.0);
|
|
}
|
|
|
|
// Writes an unused color, we only care about the depth.
|
|
@fragment fn fs1() -> @location(0) f32 {
|
|
return -42.0;
|
|
}
|
|
|
|
@group(0) @binding(0) var t : texture_depth_2d;
|
|
@group(0) @binding(1) var s : sampler;
|
|
|
|
// Check each depth texture texel has the expected value and outputs a "bool".
|
|
@fragment fn fs2(@builtin(position) pos : vec4f) -> @location(0) f32 {
|
|
let x = pos.x / kWidth;
|
|
let depth = textureSample(t, s, vec2(x, 0.5));
|
|
|
|
let index = pos.x - 0.5;
|
|
let expectedDepth = index / (kWidth - 1.0);
|
|
|
|
if (abs(depth - expectedDepth) < 0.001) {
|
|
return 1.0;
|
|
}
|
|
return 0.0;
|
|
}
|
|
)");
|
|
|
|
// The first pipeline will write to the depth texture.
|
|
utils::ComboRenderPipelineDescriptor pDesc1;
|
|
pDesc1.vertex.module = module;
|
|
pDesc1.vertex.entryPoint = "vs";
|
|
pDesc1.cFragment.module = module;
|
|
pDesc1.cFragment.entryPoint = "fs1";
|
|
pDesc1.cTargets[0].format = wgpu::TextureFormat::R32Float;
|
|
pDesc1.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
|
pDesc1.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8);
|
|
pDesc1.cDepthStencil.depthWriteEnabled = true;
|
|
wgpu::RenderPipeline pipeline1 = device.CreateRenderPipeline(&pDesc1);
|
|
|
|
// The second pipeline checks the depth texture and outputs 1 to a texel on success.
|
|
utils::ComboRenderPipelineDescriptor pDesc2;
|
|
pDesc2.vertex.module = module;
|
|
pDesc2.vertex.entryPoint = "vs";
|
|
pDesc2.cFragment.module = module;
|
|
pDesc2.cFragment.entryPoint = "fs2";
|
|
pDesc2.cTargets[0].format = wgpu::TextureFormat::R32Float;
|
|
pDesc2.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
|
wgpu::RenderPipeline pipeline2 = device.CreateRenderPipeline(&pDesc2);
|
|
|
|
// Initialize resources.
|
|
wgpu::TextureDescriptor tDesc;
|
|
tDesc.size = {kWidth};
|
|
tDesc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment |
|
|
wgpu::TextureUsage::CopySrc;
|
|
tDesc.format = wgpu::TextureFormat::R32Float;
|
|
wgpu::Texture colorTexture = device.CreateTexture(&tDesc);
|
|
|
|
tDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
|
|
wgpu::Texture depthTexture = device.CreateTexture(&tDesc);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// Render the depth texture with varied depth values.
|
|
utils::ComboRenderPassDescriptor passDesc1({colorTexture.CreateView()},
|
|
depthTexture.CreateView());
|
|
wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&passDesc1);
|
|
pass1.SetPipeline(pipeline1);
|
|
pass1.Draw(kWidth);
|
|
pass1.End();
|
|
|
|
// Check the depth values and output the result in a "boolean" encoded in an f32
|
|
wgpu::TextureViewDescriptor viewDesc;
|
|
viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
|
|
wgpu::BindGroup bg = utils::MakeBindGroup(device, pipeline2.GetBindGroupLayout(0),
|
|
{
|
|
{0, depthTexture.CreateView(&viewDesc)},
|
|
{1, device.CreateSampler()},
|
|
});
|
|
|
|
utils::ComboRenderPassDescriptor passDesc2({colorTexture.CreateView()});
|
|
wgpu::RenderPassEncoder pass2 = encoder.BeginRenderPass(&passDesc2);
|
|
pass2.SetPipeline(pipeline2);
|
|
pass2.SetBindGroup(0, bg);
|
|
pass2.Draw(kWidth);
|
|
pass2.End();
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
// Check all booleans are true.
|
|
for (uint32_t x = 0; x < kWidth; x++) {
|
|
EXPECT_PIXEL_FLOAT_EQ(1.0f, colorTexture, x, 0);
|
|
}
|
|
}
|
|
|
|
// Test that sampling a depth/stencil texture at components 1, 2, and 3 yield 0, 0, and 1
|
|
// respectively
|
|
TEST_P(DepthStencilSamplingTest, SampleExtraComponents) {
|
|
// This test fails on ANGLE (both SwiftShader and D3D11).
|
|
DAWN_SUPPRESS_TEST_IF(IsANGLE());
|
|
|
|
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
|
|
|
DoSamplingExtraStencilComponentsRenderTest(TestAspectAndSamplerType::StencilAsUint, format,
|
|
{uint8_t(42), uint8_t(37)});
|
|
|
|
DoSamplingExtraStencilComponentsComputeTest(TestAspectAndSamplerType::StencilAsUint, format,
|
|
{uint8_t(42), uint8_t(37)});
|
|
}
|
|
|
|
// Test sampling both depth and stencil with a render/compute pipeline works.
|
|
TEST_P(DepthStencilSamplingTest, SampleDepthAndStencilRender) {
|
|
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
|
|
|
wgpu::SamplerDescriptor samplerDesc;
|
|
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
|
|
|
|
wgpu::Texture inputTexture = CreateInputTexture(format);
|
|
|
|
wgpu::TextureViewDescriptor depthViewDesc = {};
|
|
depthViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
|
|
|
|
wgpu::TextureViewDescriptor stencilViewDesc = {};
|
|
stencilViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
|
|
|
|
// With render pipeline
|
|
{
|
|
wgpu::RenderPipeline pipeline = CreateSamplingRenderPipeline(
|
|
{TestAspectAndSamplerType::DepthAsDepth, TestAspectAndSamplerType::StencilAsUint}, 0);
|
|
|
|
wgpu::Buffer depthOutput = CreateOutputBuffer();
|
|
wgpu::Buffer stencilOutput = CreateOutputBuffer();
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{
|
|
{0, inputTexture.CreateView(&depthViewDesc)},
|
|
{1, depthOutput},
|
|
{2, inputTexture.CreateView(&stencilViewDesc)},
|
|
{3, stencilOutput},
|
|
});
|
|
|
|
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
|
|
// Initialize both depth and stencil aspects.
|
|
utils::ComboRenderPassDescriptor passDescriptor({}, inputTexture.CreateView());
|
|
passDescriptor.cDepthStencilAttachmentInfo.depthClearValue = 0.43f;
|
|
passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue = 31;
|
|
|
|
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
|
|
pass.End();
|
|
|
|
// Render into the output textures
|
|
{
|
|
utils::BasicRenderPass renderPass =
|
|
utils::CreateBasicRenderPass(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm);
|
|
wgpu::RenderPassEncoder pass =
|
|
commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Draw(1);
|
|
pass.End();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
float expectedDepth = 0.0f;
|
|
memcpy(&expectedDepth, &passDescriptor.cDepthStencilAttachmentInfo.depthClearValue,
|
|
sizeof(float));
|
|
EXPECT_BUFFER(depthOutput, 0, sizeof(float), new ::detail::ExpectEq<float>(expectedDepth));
|
|
|
|
uint8_t expectedStencil = 0;
|
|
memcpy(&expectedStencil, &passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue,
|
|
sizeof(uint8_t));
|
|
EXPECT_BUFFER_U32_EQ(expectedStencil, stencilOutput, 0);
|
|
}
|
|
|
|
// With compute pipeline
|
|
{
|
|
wgpu::ComputePipeline pipeline = CreateSamplingComputePipeline(
|
|
{TestAspectAndSamplerType::DepthAsDepth, TestAspectAndSamplerType::StencilAsUint}, 0);
|
|
|
|
wgpu::Buffer depthOutput = CreateOutputBuffer();
|
|
wgpu::Buffer stencilOutput = CreateOutputBuffer();
|
|
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{{0, inputTexture.CreateView(&depthViewDesc)},
|
|
{1, depthOutput},
|
|
{2, inputTexture.CreateView(&stencilViewDesc)},
|
|
{3, stencilOutput}});
|
|
|
|
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
|
// Initialize both depth and stencil aspects.
|
|
utils::ComboRenderPassDescriptor passDescriptor({}, inputTexture.CreateView());
|
|
passDescriptor.cDepthStencilAttachmentInfo.depthClearValue = 0.43f;
|
|
passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue = 31;
|
|
|
|
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
|
|
pass.End();
|
|
|
|
// Sample into the output buffers
|
|
{
|
|
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.DispatchWorkgroups(1);
|
|
pass.End();
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
float expectedDepth = 0.0f;
|
|
memcpy(&expectedDepth, &passDescriptor.cDepthStencilAttachmentInfo.depthClearValue,
|
|
sizeof(float));
|
|
EXPECT_BUFFER(depthOutput, 0, sizeof(float), new ::detail::ExpectEq<float>(expectedDepth));
|
|
|
|
uint8_t expectedStencil = 0;
|
|
memcpy(&expectedStencil, &passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue,
|
|
sizeof(uint8_t));
|
|
EXPECT_BUFFER_U32_EQ(expectedStencil, stencilOutput, 0);
|
|
}
|
|
}
|
|
|
|
class DepthSamplingTest : public DepthStencilSamplingTest {};
|
|
|
|
// Test that sampling a depth texture with a render/compute pipeline works
|
|
TEST_P(DepthSamplingTest, SampleDepthOnly) {
|
|
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
|
float tolerance = format == wgpu::TextureFormat::Depth16Unorm ? 0.001f : 0.0f;
|
|
|
|
// Test 0, between [0, 1], and 1.
|
|
DoSamplingTest(TestAspectAndSamplerType::DepthAsDepth,
|
|
CreateSamplingRenderPipeline({TestAspectAndSamplerType::DepthAsDepth}, 0),
|
|
format, kNormalizedTextureValues, tolerance);
|
|
DoSamplingTest(TestAspectAndSamplerType::DepthAsFloat,
|
|
CreateSamplingRenderPipeline({TestAspectAndSamplerType::DepthAsFloat}, 0),
|
|
format, kNormalizedTextureValues, tolerance);
|
|
|
|
DoSamplingTest(TestAspectAndSamplerType::DepthAsDepth,
|
|
CreateSamplingComputePipeline({TestAspectAndSamplerType::DepthAsDepth}, 0),
|
|
format, kNormalizedTextureValues, tolerance);
|
|
DoSamplingTest(TestAspectAndSamplerType::DepthAsFloat,
|
|
CreateSamplingComputePipeline({TestAspectAndSamplerType::DepthAsFloat}, 0),
|
|
format, kNormalizedTextureValues, tolerance);
|
|
}
|
|
|
|
// Test that sampling in a render pipeline with all of the compare functions works.
|
|
TEST_P(DepthSamplingTest, CompareFunctionsRender) {
|
|
// Initialization via renderPass loadOp doesn't work on Mac Intel.
|
|
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
|
|
|
|
// TODO(dawn:1549) Fails on Qualcomm-based Android devices.
|
|
DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsQualcomm());
|
|
|
|
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
|
// Test does not account for precision issues when comparison testing Depth16Unorm.
|
|
DAWN_TEST_UNSUPPORTED_IF(format == wgpu::TextureFormat::Depth16Unorm);
|
|
|
|
wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
|
|
|
|
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
|
|
for (float compareRef : kCompareRefs) {
|
|
// Test 0, below the ref, equal to, above the ref, and 1.
|
|
for (wgpu::CompareFunction f : kCompareFunctions) {
|
|
DoDepthCompareRefTest(pipeline, format, compareRef, f, kNormalizedTextureValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
class StencilSamplingTest : public DepthStencilSamplingTest {};
|
|
|
|
// Test that sampling a stencil texture with a render/compute pipeline works
|
|
TEST_P(StencilSamplingTest, SampleStencilOnly) {
|
|
// This test fails on SwANGLE (although it passes on other ANGLE backends).
|
|
DAWN_TEST_UNSUPPORTED_IF(IsANGLE());
|
|
|
|
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
|
|
|
DoSamplingTest(TestAspectAndSamplerType::StencilAsUint,
|
|
CreateSamplingRenderPipeline({TestAspectAndSamplerType::StencilAsUint}, 0),
|
|
format, kStencilValues);
|
|
|
|
DoSamplingTest(TestAspectAndSamplerType::StencilAsUint,
|
|
CreateSamplingComputePipeline({TestAspectAndSamplerType::StencilAsUint}, 0),
|
|
format, kStencilValues);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST_P(DepthStencilSamplingTest,
|
|
{D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
|
|
VulkanBackend()},
|
|
std::vector<wgpu::TextureFormat>(utils::kDepthAndStencilFormats.begin(),
|
|
utils::kDepthAndStencilFormats.end()));
|
|
|
|
DAWN_INSTANTIATE_TEST_P(DepthSamplingTest,
|
|
{D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
|
|
VulkanBackend()},
|
|
std::vector<wgpu::TextureFormat>(utils::kDepthFormats.begin(),
|
|
utils::kDepthFormats.end()));
|
|
|
|
DAWN_INSTANTIATE_TEST_P(StencilSamplingTest,
|
|
{D3D12Backend(), MetalBackend(),
|
|
MetalBackend({"metal_use_combined_depth_stencil_format_for_stencil8"}),
|
|
OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
|
|
std::vector<wgpu::TextureFormat>(utils::kStencilFormats.begin(),
|
|
utils::kStencilFormats.end()));
|