blob: 3b5c3f851fb419354f13e5ba0423760b5eee2138 [file] [log] [blame] [edit]
// 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 <algorithm>
#include <vector>
#include "dawn/tests/DawnTest.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/WGPUHelpers.h"
namespace dawn {
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<uint16_t, kMipLevelCount> kU16DepthValues = {8192u, 57343u};
constexpr std::array<uint8_t, kMipLevelCount> kStencilValues = {7u, 3u};
class DepthStencilLoadOpTests : public DawnTestWithParams<DepthStencilLoadOpTestParams> {
protected:
void SetUp() override {
DawnTestWithParams<DepthStencilLoadOpTestParams>::SetUp();
DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
// 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::TextureBinding;
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.UnsetDepthStencilLoadStoreOpsForFormat(GetParam().mFormat);
renderPassDescriptor.cDepthStencilAttachmentInfo.depthClearValue =
kDepthValues[mipLevel];
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilClearValue =
kStencilValues[mipLevel];
renderPassDescriptors.push_back(renderPassDescriptor);
}
}
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
switch (GetParam().mFormat) {
case wgpu::TextureFormat::Depth32FloatStencil8:
if (SupportsFeatures({wgpu::FeatureName::Depth32FloatStencil8})) {
mIsFormatSupported = true;
return {wgpu::FeatureName::Depth32FloatStencil8};
}
return {};
default:
mIsFormatSupported = true;
return {};
}
}
void CheckMipLevel(uint32_t mipLevel) {
uint32_t mipSize = std::max(kRTSize >> mipLevel, 1u);
switch (GetParam().mCheck) {
case Check::SampleDepth: {
DAWN_TEST_UNSUPPORTED_IF(utils::IsStencilOnlyFormat(GetParam().mFormat));
std::vector<float> expectedDepth(mipSize * mipSize, kDepthValues[mipLevel]);
ExpectSampledDepthData(
texture, mipSize, mipSize, 0, mipLevel,
new detail::ExpectEq<float>(expectedDepth.data(), expectedDepth.size(), 0.0001))
<< "sample depth mip " << mipLevel;
break;
}
case Check::CopyDepth: {
DAWN_TEST_UNSUPPORTED_IF(utils::IsStencilOnlyFormat(GetParam().mFormat));
if (GetParam().mFormat == wgpu::TextureFormat::Depth16Unorm) {
std::vector<uint16_t> expectedDepth(mipSize * mipSize,
kU16DepthValues[mipLevel]);
EXPECT_TEXTURE_EQ(expectedDepth.data(), texture, {0, 0}, {mipSize, mipSize},
mipLevel, wgpu::TextureAspect::DepthOnly,
/* bytesPerRow */ 0, /* tolerance */ uint16_t(1))
<< "copy depth mip " << mipLevel;
} else {
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: {
DAWN_TEST_UNSUPPORTED_IF(utils::IsStencilOnlyFormat(GetParam().mFormat));
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;
private:
bool mIsFormatSupported = false;
};
} // anonymous namespace
// Check that clearing a mip level works at all.
TEST_P(DepthStencilLoadOpTests, ClearMip0) {
// TODO(crbug.com/dawn/1828): depth16unorm broken on Apple GPUs.
DAWN_SUPPRESS_TEST_IF(IsApple() && GetParam().mFormat == wgpu::TextureFormat::Depth16Unorm);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[0]).End();
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) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[1]).End();
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/1828): depth16unorm broken on Apple GPUs.
DAWN_SUPPRESS_TEST_IF(IsApple() && GetParam().mFormat == wgpu::TextureFormat::Depth16Unorm);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[0]).End();
encoder.BeginRenderPass(&renderPassDescriptors[1]).End();
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/1828): depth16unorm broken on Apple GPUs.
DAWN_SUPPRESS_TEST_IF(IsApple() && GetParam().mFormat == wgpu::TextureFormat::Depth16Unorm);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[1]).End();
encoder.BeginRenderPass(&renderPassDescriptors[0]).End();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(0u);
CheckMipLevel(1u);
}
namespace {
auto GenerateParam() {
auto params1 = MakeParamGenerator<DepthStencilLoadOpTestParams>(
{D3D11Backend(), D3D12Backend(), D3D12Backend({}, {"use_d3d12_render_pass"}),
MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::Depth32Float, wgpu::TextureFormat::Depth16Unorm},
{Check::CopyDepth, Check::DepthTest, Check::SampleDepth});
auto params2 = MakeParamGenerator<DepthStencilLoadOpTestParams>(
{D3D11Backend(), D3D12Backend(), D3D12Backend({}, {"use_d3d12_render_pass"}),
MetalBackend(), MetalBackend({"metal_use_combined_depth_stencil_format_for_stencil8"}),
MetalBackend(
{"metal_use_both_depth_and_stencil_attachments_for_combined_depth_stencil_formats"}),
OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureFormat::Depth32FloatStencil8,
wgpu::TextureFormat::Stencil8},
{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(GenerateParam()),
DawnTestBase::PrintToStringParamName("DepthStencilLoadOpTests"));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DepthStencilLoadOpTests);
class StencilClearValueOverflowTest : public DepthStencilLoadOpTests {};
// Test when stencilClearValue overflows uint8_t (>255), only the last 8 bits will be applied as the
// stencil clear value in encoder.BeginRenderPass() (currently Dawn only supports 8-bit stencil
// format).
TEST_P(StencilClearValueOverflowTest, StencilClearValueOverFlowUint8) {
constexpr uint32_t kOverflowedStencilValue = kStencilValues[0] + 0x100;
renderPassDescriptors[0].cDepthStencilAttachmentInfo.stencilClearValue =
kOverflowedStencilValue;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[0]).End();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(0u);
}
// Test when stencilClearValue overflows uint16_t(>65535), only the last 8 bits will be applied as
// the stencil clear value in encoder.BeginRenderPass() (currently Dawn only supports 8-bit stencil
// format).
TEST_P(StencilClearValueOverflowTest, StencilClearValueOverFlowUint16) {
constexpr uint32_t kOverflowedStencilValue = kStencilValues[0] + 0x10000;
renderPassDescriptors[0].cDepthStencilAttachmentInfo.stencilClearValue =
kOverflowedStencilValue;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.BeginRenderPass(&renderPassDescriptors[0]).End();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
CheckMipLevel(0u);
}
DAWN_INSTANTIATE_TEST_P(StencilClearValueOverflowTest,
{D3D11Backend(), D3D12Backend(),
D3D12Backend({}, {"use_d3d12_render_pass"}), MetalBackend(),
OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::Depth24PlusStencil8,
wgpu::TextureFormat::Depth32FloatStencil8, wgpu::TextureFormat::Stencil8},
{Check::CopyStencil, Check::StencilTest});
// Regression tests to reproduce a flaky failure when running whole WebGPU CTS on Intel Gen12 GPUs.
// See crbug.com/dawn/1487 for more details.
using SupportsTextureBinding = bool;
DAWN_TEST_PARAM_STRUCT(DepthTextureClearTwiceTestParams, Format, SupportsTextureBinding);
class DepthTextureClearTwiceTest : public DawnTestWithParams<DepthTextureClearTwiceTestParams> {
public:
void RecordClearDepthAspectAtLevel(wgpu::CommandEncoder encoder,
wgpu::Texture depthTexture,
uint32_t level,
float clearValue) {
wgpu::TextureViewDescriptor viewDescriptor = {};
viewDescriptor.baseArrayLayer = 0;
viewDescriptor.arrayLayerCount = 1;
viewDescriptor.baseMipLevel = level;
viewDescriptor.mipLevelCount = 1;
wgpu::TextureView view = depthTexture.CreateView(&viewDescriptor);
wgpu::RenderPassDescriptor renderPassDescriptor = {};
renderPassDescriptor.colorAttachmentCount = 0;
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment = {};
depthStencilAttachment.view = view;
depthStencilAttachment.depthClearValue = clearValue;
depthStencilAttachment.depthLoadOp = wgpu::LoadOp::Clear;
depthStencilAttachment.depthStoreOp = wgpu::StoreOp::Store;
if (!utils::IsDepthOnlyFormat(GetParam().mFormat)) {
depthStencilAttachment.stencilLoadOp = wgpu::LoadOp::Load;
depthStencilAttachment.stencilStoreOp = wgpu::StoreOp::Store;
}
renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment;
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
renderPass.End();
}
protected:
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
switch (GetParam().mFormat) {
case wgpu::TextureFormat::Depth32FloatStencil8:
if (SupportsFeatures({wgpu::FeatureName::Depth32FloatStencil8})) {
mIsFormatSupported = true;
return {wgpu::FeatureName::Depth32FloatStencil8};
}
return {};
default:
mIsFormatSupported = true;
return {};
}
}
bool mIsFormatSupported = false;
};
TEST_P(DepthTextureClearTwiceTest, ClearDepthAspectTwice) {
DAWN_SUPPRESS_TEST_IF(!mIsFormatSupported);
constexpr uint32_t kSize = 64;
constexpr uint32_t kLevelCount = 5;
wgpu::TextureFormat depthFormat = GetParam().mFormat;
wgpu::TextureDescriptor descriptor;
descriptor.size = {kSize, kSize};
descriptor.format = depthFormat;
descriptor.mipLevelCount = kLevelCount;
// The toggle "d3d12_force_initialize_copyable_depth_stencil_texture_on_creation" is not related
// to this test as we don't specify wgpu::TextureUsage::CopyDst in the test.
descriptor.usage = wgpu::TextureUsage::RenderAttachment;
if (GetParam().mSupportsTextureBinding) {
descriptor.usage |= wgpu::TextureUsage::TextureBinding;
}
wgpu::Texture depthTexture = device.CreateTexture(&descriptor);
// First, clear all the subresources to 0.
{
constexpr float kClearValue = 0.f;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
for (uint32_t level = 0; level < kLevelCount; ++level) {
RecordClearDepthAspectAtLevel(encoder, depthTexture, level, kClearValue);
}
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
}
// Then, clear several mipmap levels to 0.8.
{
constexpr float kClearValue = 0.8f;
constexpr std::array<uint32_t, 2> kLevelsToSet = {2u, 4u};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
for (uint32_t level : kLevelsToSet) {
RecordClearDepthAspectAtLevel(encoder, depthTexture, level, kClearValue);
}
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
}
// Check if the data in remaining mipmap levels is still 0.
{
constexpr std::array<uint32_t, 3> kLevelsToTest = {0, 1u, 3u};
for (uint32_t level : kLevelsToTest) {
uint32_t sizeAtLevel = kSize >> level;
std::vector<float> expectedValue(sizeAtLevel * sizeAtLevel, 0.f);
ExpectAttachmentDepthTestData(depthTexture, GetParam().mFormat, sizeAtLevel,
sizeAtLevel, 0, level, expectedValue);
}
}
}
DAWN_INSTANTIATE_TEST_P(DepthTextureClearTwiceTest,
{D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::Depth16Unorm, wgpu::TextureFormat::Depth24Plus,
wgpu::TextureFormat::Depth32Float,
wgpu::TextureFormat::Depth32FloatStencil8,
wgpu::TextureFormat::Depth24PlusStencil8},
{true, false});
} // anonymous namespace
} // namespace dawn