Test readback after loadOp value w/ mipmapped depth/stencil textures

There have been some bugs seen with this on some platforms.

Bug: dawn:838
Change-Id: I29fa483eee3c299960d2c998fce90d918ac4dc9d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/52560
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Austin Eng 2021-06-08 00:24:43 +00:00 committed by Dawn LUCI CQ
parent 83d4d4cce1
commit 735d5046b2
5 changed files with 482 additions and 166 deletions

View File

@ -304,6 +304,7 @@ source_set("dawn_end2end_tests_sources") {
"end2end/DeprecatedAPITests.cpp", "end2end/DeprecatedAPITests.cpp",
"end2end/DepthBiasTests.cpp", "end2end/DepthBiasTests.cpp",
"end2end/DepthStencilCopyTests.cpp", "end2end/DepthStencilCopyTests.cpp",
"end2end/DepthStencilLoadOpTests.cpp",
"end2end/DepthStencilSamplingTests.cpp", "end2end/DepthStencilSamplingTests.cpp",
"end2end/DepthStencilStateTests.cpp", "end2end/DepthStencilStateTests.cpp",
"end2end/DestroyTests.cpp", "end2end/DestroyTests.cpp",

View File

@ -1144,7 +1144,137 @@ std::ostringstream& DawnTestBase::ExpectSampledDepthData(wgpu::Texture texture,
wgpu::CommandBuffer commands = commandEncoder.Finish(); wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands); queue.Submit(1, &commands);
return EXPECT_BUFFER_FLOAT_RANGE_EQ(expected.data(), readbackBuffer, 0, expected.size()); return EXPECT_BUFFER_FLOAT_RANGE_ABOUT_EQ(expected.data(), readbackBuffer, 0, expected.size(),
0.00001);
}
std::ostringstream& DawnTestBase::ExpectAttachmentDepthStencilTestData(
wgpu::Texture texture,
wgpu::TextureFormat format,
uint32_t width,
uint32_t height,
uint32_t arrayLayer,
uint32_t mipLevel,
std::vector<float> expectedDepth,
uint8_t* expectedStencil) {
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
// Make the color attachment that we'll use to read back.
wgpu::TextureDescriptor colorTexDesc = {};
colorTexDesc.size = {width, height, 1};
colorTexDesc.format = wgpu::TextureFormat::R32Uint;
colorTexDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
wgpu::Texture colorTexture = device.CreateTexture(&colorTexDesc);
wgpu::Texture depthDataTexture = nullptr;
if (expectedDepth.size() > 0) {
// Make a sampleable texture to store the depth data. We'll sample this in the
// shader to output depth.
wgpu::TextureDescriptor depthDataDesc = {};
depthDataDesc.size = {width, height, 1};
depthDataDesc.format = wgpu::TextureFormat::R32Float;
depthDataDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopyDst;
depthDataTexture = device.CreateTexture(&depthDataDesc);
// Upload the depth data.
wgpu::ImageCopyTexture imageCopyTexture =
utils::CreateImageCopyTexture(depthDataTexture, 0, {0, 0, 0});
wgpu::TextureDataLayout textureDataLayout =
utils::CreateTextureDataLayout(0, sizeof(float) * width);
wgpu::Extent3D copyExtent = {width, height, 1};
queue.WriteTexture(&imageCopyTexture, expectedDepth.data(),
sizeof(float) * expectedDepth.size(), &textureDataLayout, &copyExtent);
}
// Pipeline for a full screen quad.
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
[[stage(vertex)]]
fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0),
vec2<f32>( 3.0, -1.0),
vec2<f32>(-1.0, 3.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
})");
if (depthDataTexture) {
// Sample the input texture and write out depth. |result| will only be set to 1 if we
// pass the depth test.
pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var texture0 : texture_2d<f32>;
struct FragmentOut {
[[location(0)]] result : u32;
[[builtin(frag_depth)]] fragDepth : f32;
};
[[stage(fragment)]]
fn main([[builtin(position)]] FragCoord : vec4<f32>) -> FragmentOut {
var output : FragmentOut;
output.result = 1u;
output.fragDepth = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0)[0];
return output;
})");
} else {
pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
[[stage(fragment)]]
fn main() -> [[location(0)]] u32 {
return 1u;
})");
}
wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil(format);
if (depthDataTexture) {
// Pass the depth test only if the depth is equal.
depthStencil->depthCompare = wgpu::CompareFunction::Equal;
// TODO(jiawei.shao@intel.com): The Intel Mesa Vulkan driver can't set gl_FragDepth unless
// depthWriteEnabled == true. This either needs to be fixed in the driver or restricted by
// the WebGPU API.
depthStencil->depthWriteEnabled = true;
}
if (expectedStencil != nullptr) {
// Pass the stencil test only if the stencil is equal.
depthStencil->stencilFront.compare = wgpu::CompareFunction::Equal;
}
pipelineDescriptor.cTargets[0].format = colorTexDesc.format;
wgpu::TextureViewDescriptor viewDesc = {};
viewDesc.baseMipLevel = mipLevel;
viewDesc.mipLevelCount = 1;
viewDesc.baseArrayLayer = arrayLayer;
viewDesc.arrayLayerCount = 1;
utils::ComboRenderPassDescriptor passDescriptor({colorTexture.CreateView()},
texture.CreateView(&viewDesc));
passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
if (expectedStencil != nullptr) {
pass.SetStencilReference(*expectedStencil);
}
pass.SetPipeline(pipeline);
if (depthDataTexture) {
// Bind the depth data texture.
pass.SetBindGroup(0, utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, depthDataTexture.CreateView()}}));
}
pass.Draw(3);
pass.EndPass();
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
std::vector<uint32_t> colorData(width * height, 1u);
return EXPECT_TEXTURE_EQ(colorData.data(), colorTexture, {0, 0}, {width, height});
} }
void DawnTestBase::WaitABit() { void DawnTestBase::WaitABit() {
@ -1301,27 +1431,53 @@ namespace detail {
// Helper classes to set expectations // Helper classes to set expectations
template <typename T> template <typename T>
ExpectEq<T>::ExpectEq(T singleValue) { ExpectEq<T>::ExpectEq(T singleValue, T tolerance) : mTolerance(tolerance) {
mExpected.push_back(singleValue); mExpected.push_back(singleValue);
} }
template <typename T> template <typename T>
ExpectEq<T>::ExpectEq(const T* values, const unsigned int count) { ExpectEq<T>::ExpectEq(const T* values, const unsigned int count, T tolerance)
: mTolerance(tolerance) {
mExpected.assign(values, values + count); mExpected.assign(values, values + count);
} }
namespace {
template <typename T>
testing::AssertionResult CheckImpl(const T& expected, const T& actual, const T& tolerance) {
ASSERT(tolerance == T{});
if (expected != actual) {
return testing::AssertionFailure() << expected << ", actual " << actual;
}
return testing::AssertionSuccess();
}
template <>
testing::AssertionResult CheckImpl<float>(const float& expected,
const float& actual,
const float& tolerance) {
if (abs(expected - actual) > tolerance) {
return tolerance == 0.0
? testing::AssertionFailure() << expected << ", actual " << actual
: testing::AssertionFailure() << "within " << tolerance << " of "
<< expected << ", actual " << actual;
}
return testing::AssertionSuccess();
}
} // namespace
template <typename T> template <typename T>
testing::AssertionResult ExpectEq<T>::Check(const void* data, size_t size) { testing::AssertionResult ExpectEq<T>::Check(const void* data, size_t size) {
DAWN_ASSERT(size == sizeof(T) * mExpected.size()); DAWN_ASSERT(size == sizeof(T) * mExpected.size());
const T* actual = static_cast<const T*>(data); const T* actual = static_cast<const T*>(data);
for (size_t i = 0; i < mExpected.size(); ++i) { for (size_t i = 0; i < mExpected.size(); ++i) {
if (actual[i] != mExpected[i]) { testing::AssertionResult check = CheckImpl(mExpected[i], actual[i], mTolerance);
if (!check) {
testing::AssertionResult result = testing::AssertionFailure() testing::AssertionResult result = testing::AssertionFailure()
<< "Expected data[" << i << "] to be " << "Expected data[" << i << "] to be "
<< mExpected[i] << ", actual " << actual[i] << check.message() << std::endl;
<< std::endl;
if (mExpected.size() <= 1024) { if (mExpected.size() <= 1024) {
result << "Expected:" << std::endl; result << "Expected:" << std::endl;
@ -1334,7 +1490,6 @@ namespace detail {
return result; return result;
} }
} }
return testing::AssertionSuccess(); return testing::AssertionSuccess();
} }

View File

@ -74,6 +74,10 @@
EXPECT_BUFFER(buffer, offset, sizeof(float) * (count), \ EXPECT_BUFFER(buffer, offset, sizeof(float) * (count), \
new ::detail::ExpectEq<float>(expected, count)) new ::detail::ExpectEq<float>(expected, count))
#define EXPECT_BUFFER_FLOAT_RANGE_ABOUT_EQ(expected, buffer, offset, count, tolerance) \
EXPECT_BUFFER(buffer, offset, sizeof(float) * (count), \
new ::detail::ExpectEq<float>(expected, count, tolerance))
// Test a pixel of the mip level 0 of a 2D texture. // Test a pixel of the mip level 0 of a 2D texture.
#define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y) \ #define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y) \
AddTextureExpectation(__FILE__, __LINE__, expected, texture, {x, y}) AddTextureExpectation(__FILE__, __LINE__, expected, texture, {x, y})
@ -408,6 +412,42 @@ class DawnTestBase {
uint32_t mipLevel, uint32_t mipLevel,
const std::vector<float>& expected); const std::vector<float>& expected);
// Check depth by uploading expected data to a sampled texture, writing it out as a depth
// attachment, and then using the "equals" depth test to check the contents are the same.
// Check stencil by rendering a full screen quad and using the "equals" stencil test with
// a stencil reference value. Note that checking stencil checks that the entire stencil
// buffer is equal to the expected stencil value.
std::ostringstream& ExpectAttachmentDepthStencilTestData(wgpu::Texture texture,
wgpu::TextureFormat format,
uint32_t width,
uint32_t height,
uint32_t arrayLayer,
uint32_t mipLevel,
std::vector<float> expectedDepth,
uint8_t* expectedStencil);
std::ostringstream& ExpectAttachmentDepthTestData(wgpu::Texture texture,
wgpu::TextureFormat format,
uint32_t width,
uint32_t height,
uint32_t arrayLayer,
uint32_t mipLevel,
std::vector<float> expectedDepth) {
return ExpectAttachmentDepthStencilTestData(texture, format, width, height, arrayLayer,
mipLevel, std::move(expectedDepth), nullptr);
}
std::ostringstream& ExpectAttachmentStencilTestData(wgpu::Texture texture,
wgpu::TextureFormat format,
uint32_t width,
uint32_t height,
uint32_t arrayLayer,
uint32_t mipLevel,
uint8_t expectedStencil) {
return ExpectAttachmentDepthStencilTestData(texture, format, width, height, arrayLayer,
mipLevel, {}, &expectedStencil);
}
void WaitABit(); void WaitABit();
void FlushWire(); void FlushWire();
void WaitForAllOperations(); void WaitForAllOperations();
@ -630,13 +670,14 @@ namespace detail {
template <typename T> template <typename T>
class ExpectEq : public Expectation { class ExpectEq : public Expectation {
public: public:
ExpectEq(T singleValue); ExpectEq(T singleValue, T tolerance = {});
ExpectEq(const T* values, const unsigned int count); ExpectEq(const T* values, const unsigned int count, T tolerance = {});
testing::AssertionResult Check(const void* data, size_t size) override; testing::AssertionResult Check(const void* data, size_t size) override;
private: private:
std::vector<T> mExpected; std::vector<T> mExpected;
T mTolerance;
}; };
extern template class ExpectEq<uint8_t>; extern template class ExpectEq<uint8_t>;
extern template class ExpectEq<int16_t>; extern template class ExpectEq<int16_t>;

View File

@ -182,126 +182,6 @@ class DepthStencilCopyTests : public DawnTest {
return dst; return dst;
} }
// Check depth by uploading expected data to a sampled texture, writing it out as a depth
// attachment, and then using the "equals" depth test to check the contents are the same.
void ExpectDepthData(wgpu::Texture depthTexture,
wgpu::TextureFormat depthFormat,
uint32_t width,
uint32_t height,
uint32_t mipLevel,
std::vector<float> expected) {
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
// Make the color attachment that we'll use to read back.
wgpu::TextureDescriptor colorTexDesc = {};
colorTexDesc.size = {width, height, 1};
colorTexDesc.format = wgpu::TextureFormat::R32Uint;
colorTexDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
wgpu::Texture colorTexture = device.CreateTexture(&colorTexDesc);
// Make a sampleable texture to store the depth data. We'll sample this in the
// shader to output depth.
wgpu::TextureDescriptor depthDataDesc = {};
depthDataDesc.size = {width, height, 1};
depthDataDesc.format = wgpu::TextureFormat::R32Float;
depthDataDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopyDst;
wgpu::Texture depthDataTexture = device.CreateTexture(&depthDataDesc);
// Upload the depth data.
uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(wgpu::TextureFormat::R32Float, width);
wgpu::BufferDescriptor uploadBufferDesc = {};
uploadBufferDesc.size = utils::RequiredBytesInCopy(bytesPerRow, height, depthDataDesc.size,
wgpu::TextureFormat::R32Float);
uploadBufferDesc.usage = wgpu::BufferUsage::CopySrc;
uploadBufferDesc.mappedAtCreation = true;
// TODO(crbug.com/dawn/822): Use WriteTexture when implemented on OpenGL.
wgpu::Buffer uploadBuffer = device.CreateBuffer(&uploadBufferDesc);
uint8_t* dst = static_cast<uint8_t*>(uploadBuffer.GetMappedRange());
float* src = expected.data();
for (uint32_t y = 0; y < height; ++y) {
memcpy(dst, src, width * sizeof(float));
dst += bytesPerRow;
src += width;
}
uploadBuffer.Unmap();
wgpu::ImageCopyBuffer bufferCopy =
utils::CreateImageCopyBuffer(uploadBuffer, 0, bytesPerRow, height);
wgpu::ImageCopyTexture textureCopy =
utils::CreateImageCopyTexture(depthDataTexture, 0, {0, 0, 0}, wgpu::TextureAspect::All);
commandEncoder.CopyBufferToTexture(&bufferCopy, &textureCopy, &depthDataDesc.size);
// Pipeline for a full screen quad.
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
[[stage(vertex)]]
fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0),
vec2<f32>( 3.0, -1.0),
vec2<f32>(-1.0, 3.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
})");
// Sample the input texture and write out depth. |result| will only be set to 1 if we
// pass the depth test.
pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var texture0 : texture_2d<f32>;
struct FragmentOut {
[[location(0)]] result : u32;
[[builtin(frag_depth)]] fragDepth : f32;
};
[[stage(fragment)]]
fn main([[builtin(position)]] FragCoord : vec4<f32>) -> FragmentOut {
var output : FragmentOut;
output.result = 1u;
output.fragDepth = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0)[0];
return output;
})");
// Pass the depth test only if the depth is equal.
pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil(depthFormat);
depthStencil->depthCompare = wgpu::CompareFunction::Equal;
pipelineDescriptor.cTargets[0].format = colorTexDesc.format;
// TODO(crbug.com/dawn/821): The Intel Mesa Vulkan driver can't set gl_FragDepth unless
// depthWriteEnabled == true. This either needs to be fixed in the driver or restricted by
// the WebGPU API.
depthStencil->depthWriteEnabled = true;
wgpu::TextureViewDescriptor viewDesc = {};
viewDesc.baseMipLevel = mipLevel;
viewDesc.mipLevelCount = 1;
utils::ComboRenderPassDescriptor passDescriptor({colorTexture.CreateView()},
depthTexture.CreateView(&viewDesc));
passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
// Bind the depth data texture.
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, depthDataTexture.CreateView()}});
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(3);
pass.EndPass();
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
std::vector<uint32_t> colorData(width * height, 1u);
EXPECT_TEXTURE_EQ(colorData.data(), colorTexture, {0, 0}, {width, height});
}
wgpu::ShaderModule mVertexModule; wgpu::ShaderModule mVertexModule;
}; };
@ -481,7 +361,8 @@ TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyDepth) {
0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::RenderAttachment); 0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::RenderAttachment);
// Check the depth // Check the depth
ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth,
kHeight, 0, 0,
{ {
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
@ -496,7 +377,7 @@ TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonZeroMipDepth) {
0.1f, 0.3f, 1u, 3u, 8, 8, wgpu::TextureUsage::RenderAttachment, 1); 0.1f, 0.3f, 1u, 3u, 8, 8, wgpu::TextureUsage::RenderAttachment, 1);
// Check the depth // Check the depth
ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, 4, 4, 1, ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, 4, 4, 0, 1,
{ {
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
@ -528,7 +409,8 @@ TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyStencilThenDepth) {
wgpu::TextureAspect::StencilOnly); wgpu::TextureAspect::StencilOnly);
// Check the depth // Check the depth
ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth,
kHeight, 0, 0,
{ {
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
@ -558,7 +440,8 @@ TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyDepthThenStencil) {
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment);
// Check the depth // Check the depth
ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth,
kHeight, 0, 0,
{ {
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, //
@ -674,8 +557,8 @@ TEST_P(DepthStencilCopyTests, ToStencilAspect) {
EXPECT_TEXTURE_EQ(expectedStencilData.data(), depthStencilTexture, {0, 0}, {kWidth, kHeight}, 0, EXPECT_TEXTURE_EQ(expectedStencilData.data(), depthStencilTexture, {0, 0}, {kWidth, kHeight}, 0,
wgpu::TextureAspect::StencilOnly); wgpu::TextureAspect::StencilOnly);
ExpectDepthData(depthStencilTexture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, ExpectAttachmentDepthTestData(depthStencilTexture, wgpu::TextureFormat::Depth24PlusStencil8,
0, kWidth, kHeight, 0, 0,
{ {
0.7, 0.7, 0.7, 0.7, // 0.7, 0.7, 0.7, 0.7, //
0.7, 0.7, 0.7, 0.7, // 0.7, 0.7, 0.7, 0.7, //

View File

@ -0,0 +1,236 @@
// 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"
namespace {
using Format = wgpu::TextureFormat;
enum class Check {
CopyStencil,
StencilTest,
CopyDepth,
DepthTest,
SampleDepth,
};
std::ostream& operator<<(std::ostream& o, Check check) {
switch (check) {
case Check::CopyStencil:
o << "CopyStencil";
break;
case Check::StencilTest:
o << "StencilTest";
break;
case Check::CopyDepth:
o << "CopyDepth";
break;
case Check::DepthTest:
o << "DepthTest";
break;
case Check::SampleDepth:
o << "SampleDepth";
break;
}
return o;
}
DAWN_TEST_PARAM_STRUCT(DepthStencilLoadOpTestParams, Format, Check)
constexpr static uint32_t kRTSize = 16;
constexpr uint32_t kMipLevelCount = 2u;
constexpr std::array<float, kMipLevelCount> kDepthValues = {0.125f, 0.875f};
constexpr std::array<uint8_t, kMipLevelCount> kStencilValues = {7u, 3u};
class DepthStencilLoadOpTests : public DawnTestWithParams<DepthStencilLoadOpTestParams> {
protected:
void SetUp() override {
DawnTestWithParams<DepthStencilLoadOpTestParams>::SetUp();
// TODO(crbug.com/tint/827): HLSL writer produces invalid code.
DAWN_SUPPRESS_TEST_IF(IsD3D12() && HasToggleEnabled("use_tint_generator") &&
GetParam().mCheck == Check::SampleDepth);
// Readback of Depth/Stencil textures not fully supported on GL right now.
// Also depends on glTextureView which is not supported on ES.
DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
wgpu::TextureDescriptor descriptor;
descriptor.size = {kRTSize, kRTSize};
descriptor.format = GetParam().mFormat;
descriptor.mipLevelCount = kMipLevelCount;
descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::Sampled;
texture = device.CreateTexture(&descriptor);
wgpu::TextureViewDescriptor textureViewDesc = {};
textureViewDesc.mipLevelCount = 1;
for (uint32_t mipLevel = 0; mipLevel < kMipLevelCount; ++mipLevel) {
textureViewDesc.baseMipLevel = mipLevel;
textureViews[mipLevel] = texture.CreateView(&textureViewDesc);
utils::ComboRenderPassDescriptor renderPassDescriptor({}, textureViews[mipLevel]);
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth =
kDepthValues[mipLevel];
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil =
kStencilValues[mipLevel];
renderPassDescriptors.push_back(renderPassDescriptor);
}
}
void CheckMipLevel(uint32_t mipLevel) {
uint32_t mipSize = std::max(kRTSize >> mipLevel, 1u);
switch (GetParam().mCheck) {
case Check::SampleDepth: {
std::vector<float> expectedDepth(mipSize * mipSize, kDepthValues[mipLevel]);
ExpectSampledDepthData(texture, mipSize, mipSize, 0, mipLevel, expectedDepth)
<< "sample depth mip " << mipLevel;
break;
}
case Check::CopyDepth: {
std::vector<float> expectedDepth(mipSize * mipSize, kDepthValues[mipLevel]);
EXPECT_TEXTURE_EQ(expectedDepth.data(), texture, {0, 0}, {mipSize, mipSize},
mipLevel, wgpu::TextureAspect::DepthOnly)
<< "copy depth mip " << mipLevel;
break;
}
case Check::CopyStencil: {
std::vector<uint8_t> expectedStencil(mipSize * mipSize,
kStencilValues[mipLevel]);
EXPECT_TEXTURE_EQ(expectedStencil.data(), texture, {0, 0}, {mipSize, mipSize},
mipLevel, wgpu::TextureAspect::StencilOnly)
<< "copy stencil mip " << mipLevel;
break;
}
case Check::DepthTest: {
std::vector<float> expectedDepth(mipSize * mipSize, kDepthValues[mipLevel]);
ExpectAttachmentDepthTestData(texture, GetParam().mFormat, mipSize, mipSize, 0,
mipLevel, expectedDepth)
<< "depth test mip " << mipLevel;
break;
}
case Check::StencilTest: {
ExpectAttachmentStencilTestData(texture, GetParam().mFormat, mipSize, mipSize,
0, mipLevel, kStencilValues[mipLevel])
<< "stencil test mip " << mipLevel;
break;
}
}
}
wgpu::Texture texture;
std::array<wgpu::TextureView, kMipLevelCount> textureViews;
// Vector instead of array because there is no default constructor.
std::vector<utils::ComboRenderPassDescriptor> renderPassDescriptors;
};
} // anonymous namespace
// Check that clearing a mip level works at all.
TEST_P(DepthStencilLoadOpTests, ClearMip0) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[0]).EndPass();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(0u);
}
// Check that clearing a non-zero mip level works at all.
TEST_P(DepthStencilLoadOpTests, ClearMip1) {
// TODO(crbug.com/dawn/838): Sampling from the non-zero mip does not work.
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel() && GetParam().mCheck == Check::SampleDepth);
// TODO(crbug.com/dawn/838): Copying from the non-zero mip here sometimes returns uninitialized
// data! (from mip 0 of a previous test run).
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel() && GetParam().mCheck == Check::CopyDepth);
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel() && GetParam().mCheck == Check::CopyStencil);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[1]).EndPass();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(1u);
}
// Clear first mip then the second mip. Check both mip levels.
TEST_P(DepthStencilLoadOpTests, ClearBothMip0Then1) {
// TODO(crbug.com/dawn/838): Sampling from the non-zero mip does not work.
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel() && GetParam().mCheck == Check::SampleDepth);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[0]).EndPass();
encoder.BeginRenderPass(&renderPassDescriptors[1]).EndPass();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(0u);
CheckMipLevel(1u);
}
// Clear second mip then the first mip. Check both mip levels.
TEST_P(DepthStencilLoadOpTests, ClearBothMip1Then0) {
// TODO(crbug.com/dawn/838): Sampling from the non-zero mip does not work.
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel() && GetParam().mCheck == Check::SampleDepth);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[1]).EndPass();
encoder.BeginRenderPass(&renderPassDescriptors[0]).EndPass();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(0u);
CheckMipLevel(1u);
}
namespace {
auto GenerateParams() {
auto params1 = MakeParamGenerator<DepthStencilLoadOpTestParams>(
{D3D12Backend(), D3D12Backend({}, {"use_d3d12_render_pass"}), MetalBackend(),
OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::Depth32Float},
{Check::CopyDepth, Check::DepthTest, Check::SampleDepth});
auto params2 = MakeParamGenerator<DepthStencilLoadOpTestParams>(
{D3D12Backend(), D3D12Backend({}, {"use_d3d12_render_pass"}), MetalBackend(),
OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::Depth24PlusStencil8},
{Check::CopyStencil, Check::StencilTest, Check::DepthTest, Check::SampleDepth});
std::vector<DepthStencilLoadOpTestParams> allParams;
allParams.insert(allParams.end(), params1.begin(), params1.end());
allParams.insert(allParams.end(), params2.begin(), params2.end());
return allParams;
}
INSTANTIATE_TEST_SUITE_P(,
DepthStencilLoadOpTests,
::testing::ValuesIn(GenerateParams()),
DawnTestBase::PrintToStringParamName("DepthStencilLoadOpTests"));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DepthStencilLoadOpTests);
} // namespace