Support sampling depth and stencil of combined d/s formats

This CL adds sampling of depth-only and stencil-only texture
views on all backends. However, Metal on macOS <= 10.11 will
need a workaround to use separate depth/stencil textures for
each aspect since it is impossible to sample the stencil
aspect of a combined depth/stencil texture.

Also fixes sampling of depth24plus on D3D12 which had an
incomplete check for determining if a TYPELESS format is
necessary.

Bug: dawn:439, dawn:553
Change-Id: Id4991c565f822add200054296714e2dcd330119a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/30725
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Austin Eng
2020-11-04 15:27:11 +00:00
committed by Commit Bot service account
parent 9d6265bc07
commit a0f1725c4f
9 changed files with 633 additions and 153 deletions

View File

@@ -19,6 +19,16 @@
namespace {
constexpr wgpu::TextureFormat kDepthFormats[] = {
wgpu::TextureFormat::Depth32Float,
wgpu::TextureFormat::Depth24Plus,
wgpu::TextureFormat::Depth24PlusStencil8,
};
constexpr wgpu::TextureFormat kStencilFormats[] = {
wgpu::TextureFormat::Depth24PlusStencil8,
};
constexpr wgpu::CompareFunction kCompareFunctions[] = {
wgpu::CompareFunction::Never, wgpu::CompareFunction::Less,
wgpu::CompareFunction::LessEqual, wgpu::CompareFunction::Greater,
@@ -32,10 +42,19 @@ namespace {
// 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<uint8_t> kStencilValues = {uint8_t(0), uint8_t(1), uint8_t(38), uint8_t(255),
uint8_t(256)};
} // anonymous namespace
class DepthSamplingTest : public DawnTest {
class DepthStencilSamplingTest : public DawnTest {
protected:
enum class TestAspect {
Depth,
Stencil,
};
void SetUp() override {
DawnTest::SetUp();
@@ -43,32 +62,10 @@ class DepthSamplingTest : public DawnTest {
uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
uniformBufferDesc.size = sizeof(float);
mUniformBuffer = device.CreateBuffer(&uniformBufferDesc);
wgpu::BufferDescriptor textureUploadDesc;
textureUploadDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
textureUploadDesc.size = sizeof(float);
mTextureUploadBuffer = device.CreateBuffer(&textureUploadDesc);
wgpu::TextureDescriptor inputTextureDesc;
inputTextureDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::RenderAttachment;
inputTextureDesc.size = {1, 1, 1};
inputTextureDesc.format = wgpu::TextureFormat::Depth32Float;
mInputTexture = device.CreateTexture(&inputTextureDesc);
wgpu::TextureDescriptor outputTextureDesc;
outputTextureDesc.usage =
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
outputTextureDesc.size = {1, 1, 1};
outputTextureDesc.format = wgpu::TextureFormat::R32Float;
mOutputTexture = device.CreateTexture(&outputTextureDesc);
wgpu::BufferDescriptor outputBufferDesc;
outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
outputBufferDesc.size = sizeof(float);
mOutputBuffer = device.CreateBuffer(&outputBufferDesc);
}
wgpu::RenderPipeline CreateSamplingRenderPipeline() {
wgpu::RenderPipeline CreateSamplingRenderPipeline(std::vector<TestAspect> aspects,
uint32_t componentIndex) {
wgpu::ShaderModule vsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
#version 450
@@ -78,42 +75,102 @@ class DepthSamplingTest : public DawnTest {
}
)");
wgpu::ShaderModule fsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(location = 0) out float samplerResult;
void main() {
samplerResult = texture(sampler2D(tex, samp), vec2(0.5, 0.5)).r;
}
)");
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
std::ostringstream shaderSource;
std::ostringstream shaderBody;
shaderSource << R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
)";
uint32_t index = 0;
for (TestAspect aspect : aspects) {
switch (aspect) {
case TestAspect::Depth:
shaderSource << "layout(set = 0, binding = " << 1 + index
<< ") uniform texture2D tex" << index << ";\n";
shaderSource << "layout(location = " << index << ") out float result" << index
<< ";\n";
shaderBody << "result" << index << " = texture(sampler2D(tex" << index
<< ", samp), vec2(0.5, 0.5))[" << componentIndex << "];\n";
pipelineDescriptor.cColorStates[index].format = wgpu::TextureFormat::R32Float;
break;
case TestAspect::Stencil:
shaderSource << "layout(set = 0, binding = " << 1 + index
<< ") uniform utexture2D tex" << index << ";\n";
shaderSource << "layout(location = " << index << ") out uint result" << index
<< ";\n";
shaderBody << "result" << index << " = texelFetch(usampler2D(tex" << index
<< ", samp), ivec2(0, 0), 0)[" << componentIndex << "];\n";
pipelineDescriptor.cColorStates[index].format = wgpu::TextureFormat::R8Uint;
break;
}
index++;
}
shaderSource << "void main() { " << shaderBody.str() << " }";
wgpu::ShaderModule fsModule = utils::CreateShaderModule(
device, utils::SingleShaderStage::Fragment, shaderSource.str().c_str());
pipelineDescriptor.vertexStage.module = vsModule;
pipelineDescriptor.cFragmentStage.module = fsModule;
pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float;
pipelineDescriptor.colorStateCount = static_cast<uint32_t>(aspects.size());
return device.CreateRenderPipeline(&pipelineDescriptor);
}
wgpu::ComputePipeline CreateSamplingComputePipeline() {
wgpu::ShaderModule csModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(set = 0, binding = 2) writeonly buffer SamplerResult {
float samplerResult;
};
wgpu::ComputePipeline CreateSamplingComputePipeline(std::vector<TestAspect> aspects,
uint32_t componentIndex) {
std::ostringstream shaderSource;
std::ostringstream shaderBody;
shaderSource << R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
)";
void main() {
samplerResult = texture(sampler2D(tex, samp), vec2(0.5, 0.5)).r;
}
)");
uint32_t index = 0;
for (TestAspect aspect : aspects) {
switch (aspect) {
case TestAspect::Depth:
shaderSource << "layout(set = 0, binding = " << 1 + 2 * index
<< ") uniform texture2D tex" << index << ";\n";
shaderSource << "layout(set = 0, binding = " << 1 + 2 * index + 1
<< ") writeonly buffer Result" << index << " {\n"
<< " float result" << index << ";\n"
<< "};\n";
shaderBody << "result" << index << " = texture(sampler2D(tex" << index
<< ", samp), vec2(0.5, 0.5))[" << componentIndex << "];\n";
break;
case TestAspect::Stencil:
shaderSource << "layout(set = 0, binding = " << 1 + 2 * index
<< ") uniform utexture2D tex" << index << ";\n";
shaderSource << "layout(set = 0, binding = " << 1 + 2 * index + 1
<< ") writeonly buffer Result" << index << " {\n"
<< " uint result" << index << ";\n"
<< "};\n";
shaderBody << "result" << index << " = texelFetch(usampler2D(tex" << index
<< ", samp), ivec2(0, 0), 0)[" << componentIndex << "];\n";
break;
}
index++;
}
shaderSource << "void main() { " << shaderBody.str() << " }";
wgpu::ShaderModule csModule = utils::CreateShaderModule(
device, utils::SingleShaderStage::Compute, shaderSource.str().c_str());
wgpu::ComputePipelineDescriptor pipelineDescriptor;
pipelineDescriptor.computeStage.module = csModule;
@@ -202,32 +259,97 @@ class DepthSamplingTest : public DawnTest {
return device.CreateComputePipeline(&pipelineDescriptor);
}
void UpdateInputTexture(wgpu::CommandEncoder commandEncoder, float textureValue) {
utils::ComboRenderPassDescriptor passDescriptor({}, mInputTexture.CreateView());
passDescriptor.cDepthStencilAttachmentInfo.clearDepth = textureValue;
wgpu::Texture CreateInputTexture(wgpu::TextureFormat format) {
wgpu::TextureDescriptor inputTextureDesc;
inputTextureDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment;
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::OutputAttachment | wgpu::TextureUsage::CopySrc;
outputTextureDesc.size = {1, 1, 1};
outputTextureDesc.format = format;
return device.CreateTexture(&outputTextureDesc);
}
wgpu::Buffer CreateOutputBuffer() {
wgpu::BufferDescriptor outputBufferDesc;
outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
outputBufferDesc.size = sizeof(float); // Large enough for both float and uint8_t
return device.CreateBuffer(&outputBufferDesc);
}
void UpdateInputDepth(wgpu::CommandEncoder commandEncoder,
wgpu::Texture texture,
float depthValue) {
utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
passDescriptor.cDepthStencilAttachmentInfo.clearDepth = depthValue;
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.EndPass();
}
void DoSamplingTest(wgpu::RenderPipeline pipeline, std::vector<float> textureValues) {
void UpdateInputStencil(wgpu::CommandEncoder commandEncoder,
wgpu::Texture texture,
uint8_t stencilValue) {
utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
passDescriptor.cDepthStencilAttachmentInfo.clearStencil = stencilValue;
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.EndPass();
}
template <typename T>
void DoSamplingTest(TestAspect aspect,
wgpu::RenderPipeline pipeline,
wgpu::TextureFormat format,
std::vector<T> textureValues,
std::vector<T> expectedValues) {
ASSERT(textureValues.size() == expectedValues.size());
wgpu::SamplerDescriptor samplerDesc;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, mInputTexture.CreateView()},
});
wgpu::Texture outputTexture;
wgpu::Texture inputTexture = CreateInputTexture(format);
wgpu::TextureViewDescriptor inputViewDesc = {};
switch (aspect) {
case TestAspect::Depth:
inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
outputTexture = CreateOutputTexture(wgpu::TextureFormat::R32Float);
break;
case TestAspect::Stencil:
inputViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
outputTexture = CreateOutputTexture(wgpu::TextureFormat::R8Uint);
break;
}
for (float textureValue : textureValues) {
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, inputTexture.CreateView(&inputViewDesc)},
});
for (size_t i = 0; i < textureValues.size(); ++i) {
// Set the input depth texture to the provided texture value
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
UpdateInputTexture(commandEncoder, textureValue);
switch (aspect) {
case TestAspect::Depth:
UpdateInputDepth(commandEncoder, inputTexture, textureValues[i]);
break;
case TestAspect::Stencil:
UpdateInputStencil(commandEncoder, inputTexture, textureValues[i]);
break;
}
// Render into the output texture
{
utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()});
utils::ComboRenderPassDescriptor passDescriptor({outputTexture.CreateView()});
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
@@ -238,22 +360,49 @@ class DepthSamplingTest : public DawnTest {
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_FLOAT_EQ(textureValue, mOutputTexture, 0, 0);
EXPECT_TEXTURE_EQ(expectedValues[i], outputTexture, 0, 0);
}
}
void DoSamplingTest(wgpu::ComputePipeline pipeline, std::vector<float> textureValues) {
template <typename T>
void DoSamplingTest(TestAspect aspect,
wgpu::ComputePipeline pipeline,
wgpu::TextureFormat format,
std::vector<T> textureValues,
std::vector<T> expectedValues) {
ASSERT(textureValues.size() == expectedValues.size());
wgpu::SamplerDescriptor samplerDesc;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::Texture inputTexture = CreateInputTexture(format);
wgpu::TextureViewDescriptor inputViewDesc = {};
switch (aspect) {
case TestAspect::Depth:
inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
break;
case TestAspect::Stencil:
inputViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
break;
}
wgpu::Buffer outputBuffer = CreateOutputBuffer();
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
device, pipeline.GetBindGroupLayout(0),
{{0, sampler}, {1, mInputTexture.CreateView()}, {2, mOutputBuffer}});
{{0, sampler}, {1, inputTexture.CreateView(&inputViewDesc)}, {2, outputBuffer}});
for (float textureValue : textureValues) {
for (size_t i = 0; i < textureValues.size(); ++i) {
// Set the input depth texture to the provided texture value
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
UpdateInputTexture(commandEncoder, textureValue);
switch (aspect) {
case TestAspect::Depth:
UpdateInputDepth(commandEncoder, inputTexture, textureValues[i]);
break;
case TestAspect::Stencil:
UpdateInputStencil(commandEncoder, inputTexture, textureValues[i]);
break;
}
// Sample into the output buffer
{
@@ -267,10 +416,28 @@ class DepthSamplingTest : public DawnTest {
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(&textureValue), mOutputBuffer, 0);
uint32_t expectedValueU32 = 0;
memcpy(&expectedValueU32, &expectedValues[i], std::min(sizeof(T), sizeof(uint32_t)));
EXPECT_BUFFER_U32_EQ(expectedValueU32, outputBuffer, 0);
}
}
template <typename T>
void DoSamplingTest(TestAspect aspect,
wgpu::RenderPipeline pipeline,
wgpu::TextureFormat format,
std::vector<T> textureValues) {
DoSamplingTest(aspect, pipeline, format, textureValues, textureValues);
}
template <typename T>
void DoSamplingTest(TestAspect aspect,
wgpu::ComputePipeline pipeline,
wgpu::TextureFormat format,
std::vector<T> textureValues) {
DoSamplingTest(aspect, pipeline, format, textureValues, textureValues);
}
static bool CompareFunctionPasses(float compareRef,
wgpu::CompareFunction compare,
float textureValue) {
@@ -296,31 +463,38 @@ class DepthSamplingTest : public DawnTest {
}
}
void DoCompareRefTest(wgpu::RenderPipeline pipeline,
float compareRef,
wgpu::CompareFunction compare,
std::vector<float> textureValues) {
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::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, mInputTexture.CreateView()},
{2, mUniformBuffer},
});
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();
UpdateInputTexture(commandEncoder, textureValue);
UpdateInputDepth(commandEncoder, inputTexture, textureValue);
// Render into the output texture
{
utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()});
utils::ComboRenderPassDescriptor passDescriptor({outputTexture.CreateView()});
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
@@ -331,32 +505,39 @@ class DepthSamplingTest : public DawnTest {
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_FLOAT_EQ(
CompareFunctionPasses(compareRef, compare, textureValue) ? 1.f : 0.f,
mOutputTexture, 0, 0);
EXPECT_TEXTURE_EQ(CompareFunctionPasses(compareRef, compare, textureValue) ? 1.f : 0.f,
outputTexture, 0, 0);
}
}
void DoCompareRefTest(wgpu::ComputePipeline pipeline,
float compareRef,
wgpu::CompareFunction compare,
std::vector<float> textureValues) {
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::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, sampler},
{1, mInputTexture.CreateView()},
{2, mUniformBuffer},
{3, mOutputBuffer}});
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();
UpdateInputTexture(commandEncoder, textureValue);
UpdateInputDepth(commandEncoder, inputTexture, textureValue);
// Sample into the output buffer
{
@@ -375,63 +556,212 @@ class DepthSamplingTest : public DawnTest {
float* expected =
CompareFunctionPasses(compareRef, compare, textureValue) ? &float1 : &float0;
EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), mOutputBuffer, 0);
EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), outputBuffer, 0);
}
}
private:
wgpu::Buffer mUniformBuffer;
wgpu::Buffer mTextureUploadBuffer;
wgpu::Texture mInputTexture;
wgpu::Texture mOutputTexture;
wgpu::Buffer mOutputBuffer;
};
// Test that sampling a depth texture with a render pipeline works
TEST_P(DepthSamplingTest, SampleRender) {
// Test 0, between [0, 1], and 1.
DoSamplingTest(CreateSamplingRenderPipeline(), kNormalizedTextureValues);
// Test that sampling a depth texture with a render/compute pipeline works
TEST_P(DepthStencilSamplingTest, SampleDepth) {
for (wgpu::TextureFormat format : kDepthFormats) {
// Test 0, between [0, 1], and 1.
DoSamplingTest(TestAspect::Depth, CreateSamplingRenderPipeline({TestAspect::Depth}, 0),
format, kNormalizedTextureValues);
DoSamplingTest(TestAspect::Depth, CreateSamplingComputePipeline({TestAspect::Depth}, 0),
format, kNormalizedTextureValues);
}
}
// Test that sampling a depth texture with a compute pipeline works
TEST_P(DepthSamplingTest, SampleCompute) {
// Test 0, between [0, 1], and 1.
DoSamplingTest(CreateSamplingComputePipeline(), kNormalizedTextureValues);
// Test that sampling a stencil texture with a render/compute pipeline works
TEST_P(DepthStencilSamplingTest, SampleStencil) {
for (wgpu::TextureFormat format : kStencilFormats) {
DoSamplingTest(TestAspect::Stencil, CreateSamplingRenderPipeline({TestAspect::Stencil}, 0),
format, kStencilValues);
DoSamplingTest(TestAspect::Stencil, CreateSamplingComputePipeline({TestAspect::Stencil}, 0),
format, kStencilValues);
}
}
// Test that sampling a depth/stencil texture at components 1, 2, and 3 yield 0, 0, and 1
// respectively
TEST_P(DepthStencilSamplingTest, SampleExtraComponents) {
// TODO(enga): In Metal, color textures' unspecified default components values
// are (0, 0, 0, 1). Depth/stencil textures are undefined! Figure out what
// to do here.
// See Section 6.10 of the Metal Shading Language Specification
DAWN_SKIP_TEST_IF(IsMetal());
float expectedDepth[4] = {0, 0, 0, 1};
uint8_t expectedStencil[4] = {0, 0, 0, 1};
for (uint32_t component : {1, 2, 3}) {
DoSamplingTest<float>(
TestAspect::Depth, CreateSamplingRenderPipeline({TestAspect::Depth}, component),
wgpu::TextureFormat::Depth24PlusStencil8, {0.2f}, {expectedDepth[component]});
DoSamplingTest<float>(
TestAspect::Depth, CreateSamplingComputePipeline({TestAspect::Depth}, component),
wgpu::TextureFormat::Depth24PlusStencil8, {0.2f}, {expectedDepth[component]});
DoSamplingTest<uint8_t>(
TestAspect::Stencil, CreateSamplingRenderPipeline({TestAspect::Stencil}, component),
wgpu::TextureFormat::Depth24PlusStencil8, {uint8_t(37)}, {expectedStencil[component]});
DoSamplingTest<uint8_t>(
TestAspect::Stencil, CreateSamplingComputePipeline({TestAspect::Stencil}, component),
wgpu::TextureFormat::Depth24PlusStencil8, {uint8_t(37)}, {expectedStencil[component]});
}
}
// Test sampling both depth and stencil with a render/compute pipeline works.
TEST_P(DepthStencilSamplingTest, SampleDepthAndStencilRender) {
wgpu::SamplerDescriptor samplerDesc;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::Texture inputTexture = CreateInputTexture(wgpu::TextureFormat::Depth24PlusStencil8);
wgpu::TextureViewDescriptor depthViewDesc = {};
depthViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
wgpu::TextureViewDescriptor stencilViewDesc = {};
stencilViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
// With render pipeline
{
wgpu::RenderPipeline pipeline =
CreateSamplingRenderPipeline({TestAspect::Depth, TestAspect::Stencil}, 0);
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, inputTexture.CreateView(&depthViewDesc)},
{2, inputTexture.CreateView(&stencilViewDesc)},
});
wgpu::Texture depthOutput = CreateOutputTexture(wgpu::TextureFormat::R32Float);
wgpu::Texture stencilOutput = CreateOutputTexture(wgpu::TextureFormat::R8Uint);
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
// Initialize both depth and stencil aspects.
utils::ComboRenderPassDescriptor passDescriptor({}, inputTexture.CreateView());
passDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.43f;
passDescriptor.cDepthStencilAttachmentInfo.clearStencil = 31;
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.EndPass();
// Render into the output textures
{
utils::ComboRenderPassDescriptor passDescriptor(
{depthOutput.CreateView(), stencilOutput.CreateView()});
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_TEXTURE_EQ(passDescriptor.cDepthStencilAttachmentInfo.clearDepth, depthOutput, 0, 0);
EXPECT_TEXTURE_EQ(uint8_t(passDescriptor.cDepthStencilAttachmentInfo.clearStencil),
stencilOutput, 0, 0);
}
// With compute pipeline
{
wgpu::ComputePipeline pipeline =
CreateSamplingComputePipeline({TestAspect::Depth, TestAspect::Stencil}, 0);
wgpu::Buffer depthOutput = CreateOutputBuffer();
wgpu::Buffer stencilOutput = CreateOutputBuffer();
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, sampler},
{1, inputTexture.CreateView(&depthViewDesc)},
{2, depthOutput},
{3, inputTexture.CreateView(&stencilViewDesc)},
{4, stencilOutput}});
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
// Initialize both depth and stencil aspects.
utils::ComboRenderPassDescriptor passDescriptor({}, inputTexture.CreateView());
passDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.43f;
passDescriptor.cDepthStencilAttachmentInfo.clearStencil = 31;
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.EndPass();
// Sample into the output buffers
{
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Dispatch(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
uint32_t expectedValueU32 = 0;
memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
sizeof(float));
EXPECT_BUFFER_U32_EQ(expectedValueU32, depthOutput, 0);
expectedValueU32 = 0;
memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
sizeof(uint8_t));
EXPECT_BUFFER_U32_EQ(expectedValueU32, stencilOutput, 0);
}
}
// Test that sampling in a render pipeline with all of the compare functions works.
TEST_P(DepthSamplingTest, CompareFunctionsRender) {
TEST_P(DepthStencilSamplingTest, CompareFunctionsRender) {
// Initialization via renderPass loadOp doesn't work on Mac Intel.
DAWN_SKIP_TEST_IF(IsMetal() && IsIntel());
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) {
DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues);
for (wgpu::TextureFormat format : kDepthFormats) {
// 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);
}
}
}
}
// Test that sampling in a render pipeline with all of the compare functions works.
TEST_P(DepthSamplingTest, CompareFunctionsCompute) {
TEST_P(DepthStencilSamplingTest, CompareFunctionsCompute) {
// Initialization via renderPass loadOp doesn't work on Mac Intel.
DAWN_SKIP_TEST_IF(IsMetal() && IsIntel());
wgpu::ComputePipeline pipeline = CreateComparisonComputePipeline();
// 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) {
DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues);
for (wgpu::TextureFormat format : kDepthFormats) {
// 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);
}
}
}
}
DAWN_INSTANTIATE_TEST(DepthSamplingTest,
DAWN_INSTANTIATE_TEST(DepthStencilSamplingTest,
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),

View File

@@ -15,9 +15,23 @@
#include <gtest/gtest.h>
#include "common/Math.h"
#include "dawn/EnumClassBitmasks.h"
#include <cmath>
namespace wgpu {
enum class TestEnum {
A = 0x1,
B = 0x2,
C = 0x4,
};
} // namespace wgpu
template <>
struct wgpu::IsDawnBitmask<wgpu::TestEnum> {
static constexpr bool enable = true;
};
// Tests for ScanForward
TEST(Math, ScanForward) {
// Test extrema
@@ -260,3 +274,32 @@ TEST(Math, RoundUp) {
ASSERT_EQ(RoundUp(0x7FFFFFFFFFFFFFFFull, 0x8000000000000000ull), 0x8000000000000000ull);
ASSERT_EQ(RoundUp(1, 1), 1u);
}
// Tests for IsSubset
TEST(Math, IsSubset) {
// single value is a subset
ASSERT_TRUE(IsSubset(0b100, 0b101));
ASSERT_FALSE(IsSubset(0b010, 0b101));
ASSERT_TRUE(IsSubset(0b001, 0b101));
// empty set is a subset
ASSERT_TRUE(IsSubset(0b000, 0b101));
// equal-to is a subset
ASSERT_TRUE(IsSubset(0b101, 0b101));
// superset is not a subset
ASSERT_FALSE(IsSubset(0b111, 0b101));
// only empty is a subset of empty
ASSERT_FALSE(IsSubset(0b100, 0b000));
ASSERT_FALSE(IsSubset(0b010, 0b000));
ASSERT_FALSE(IsSubset(0b001, 0b000));
ASSERT_TRUE(IsSubset(0b000, 0b000));
// Test with enums
ASSERT_TRUE(IsSubset(wgpu::TestEnum::A, wgpu::TestEnum::A));
ASSERT_TRUE(IsSubset(wgpu::TestEnum::A, wgpu::TestEnum::A | wgpu::TestEnum::B));
ASSERT_FALSE(IsSubset(wgpu::TestEnum::C, wgpu::TestEnum::A | wgpu::TestEnum::B));
ASSERT_FALSE(IsSubset(wgpu::TestEnum::A | wgpu::TestEnum::C, wgpu::TestEnum::A));
}

View File

@@ -311,10 +311,8 @@ TEST_F(BindGroupValidationTest, TextureComponentType) {
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, uintTextureView}}));
}
// Test which depth-stencil formats are allowed to be sampled.
// This is a regression test for a change in dawn_native mistakenly allowing the depth24plus formats
// to be sampled without proper backend support.
TEST_F(BindGroupValidationTest, SamplingDepthTexture) {
// Test which depth-stencil formats are allowed to be sampled (all).
TEST_F(BindGroupValidationTest, SamplingDepthStencilTexture) {
wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}});
@@ -330,20 +328,30 @@ TEST_F(BindGroupValidationTest, SamplingDepthTexture) {
utils::MakeBindGroup(device, layout, {{0, texture.CreateView()}});
}
// Depth24Plus is not allowed to be sampled.
// Depth24Plus is allowed to be sampled.
{
desc.format = wgpu::TextureFormat::Depth24Plus;
wgpu::Texture texture = device.CreateTexture(&desc);
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, texture.CreateView()}}));
utils::MakeBindGroup(device, layout, {{0, texture.CreateView()}});
}
// Depth24PlusStencil8 is not allowed to be sampled.
// Depth24PlusStencil8 is allowed to be sampled, if the depth or stencil aspect is selected.
{
desc.format = wgpu::TextureFormat::Depth24PlusStencil8;
wgpu::Texture texture = device.CreateTexture(&desc);
wgpu::TextureViewDescriptor viewDesc = {};
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, texture.CreateView()}}));
viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
utils::MakeBindGroup(device, layout, {{0, texture.CreateView(&viewDesc)}});
wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::Fragment,
wgpu::BindingType::SampledTexture};
entry.textureComponentType = wgpu::TextureComponentType::Uint;
layout = utils::MakeBindGroupLayout(device, {entry});
viewDesc.aspect = wgpu::TextureAspect::StencilOnly;
utils::MakeBindGroup(device, layout, {{0, texture.CreateView(&viewDesc)}});
}
}