Expand NonzeroTextureCreationTests to DepthStencil textures

And fix a bug where the Metal backend hit an ASSERT when
clearing combined depth/stencil textures by copy.

Bug: dawn:780
Change-Id: Idf3061570f63d0abd3e8a41d0c251db4f6dc7e0c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51840
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Austin Eng 2021-06-01 21:25:33 +00:00 committed by Dawn LUCI CQ
parent ff70f98545
commit 3cd8c43857
4 changed files with 193 additions and 33 deletions

View File

@ -531,39 +531,39 @@ namespace dawn_native { namespace metal {
}
}
} else {
// Compute the buffer size big enough to fill the largest mip.
Extent3D largestMipSize = GetMipLevelVirtualSize(range.baseMipLevel);
const TexelBlockInfo& blockInfo =
GetFormat().GetAspectInfo(wgpu::TextureAspect::All).block;
// Metal validation layers: sourceBytesPerRow must be at least 64.
uint32_t largestMipBytesPerRow =
std::max((largestMipSize.width / blockInfo.width) * blockInfo.byteSize, 64u);
// Metal validation layers: sourceBytesPerImage must be at least 512.
uint64_t largestMipBytesPerImage =
std::max(static_cast<uint64_t>(largestMipBytesPerRow) *
(largestMipSize.height / blockInfo.height),
512llu);
uint64_t bufferSize = largestMipBytesPerImage * largestMipSize.depthOrArrayLayers;
if (bufferSize > std::numeric_limits<NSUInteger>::max()) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
}
DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial(),
blockInfo.byteSize));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
id<MTLBlitCommandEncoder> encoder = commandContext->EnsureBlit();
id<MTLBuffer> uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle();
// Encode a buffer to texture copy to clear each subresource.
for (Aspect aspect : IterateEnumMask(range.aspects)) {
// Compute the buffer size big enough to fill the largest mip.
const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(aspect).block;
// Metal validation layers: sourceBytesPerRow must be at least 64.
uint32_t largestMipBytesPerRow =
std::max((largestMipSize.width / blockInfo.width) * blockInfo.byteSize, 64u);
// Metal validation layers: sourceBytesPerImage must be at least 512.
uint64_t largestMipBytesPerImage =
std::max(static_cast<uint64_t>(largestMipBytesPerRow) *
(largestMipSize.height / blockInfo.height),
512llu);
uint64_t bufferSize = largestMipBytesPerImage * largestMipSize.depthOrArrayLayers;
if (bufferSize > std::numeric_limits<NSUInteger>::max()) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
}
DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial(),
blockInfo.byteSize));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
id<MTLBuffer> uploadBuffer =
ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle();
for (uint32_t level = range.baseMipLevel;
level < range.baseMipLevel + range.levelCount; ++level) {
Extent3D virtualSize = GetMipLevelVirtualSize(level);
@ -578,7 +578,8 @@ namespace dawn_native { namespace metal {
}
MTLBlitOption blitOption = ComputeMTLBlitOption(GetFormat(), aspect);
[encoder copyFromBuffer:uploadBuffer
[commandContext->EnsureBlit()
copyFromBuffer:uploadBuffer
sourceOffset:uploadHandle.startOffset
sourceBytesPerRow:largestMipBytesPerRow
sourceBytesPerImage:largestMipBytesPerImage

View File

@ -23,6 +23,7 @@
#include "dawn/dawn_proc.h"
#include "dawn_wire/WireClient.h"
#include "dawn_wire/WireServer.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/PlatformDebugLogger.h"
#include "utils/SystemUtils.h"
#include "utils/TerribleCommandBuffer.h"
@ -1085,6 +1086,69 @@ std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file,
return *(mDeferredExpectations.back().message.get());
}
std::ostringstream& DawnTestBase::ExpectSampledDepthData(wgpu::Texture texture,
uint32_t width,
uint32_t height,
uint32_t arrayLayer,
uint32_t mipLevel,
const std::vector<float>& expected) {
std::ostringstream shaderSource;
shaderSource << "let width : u32 = " << width << "u;\n";
shaderSource << R"(
[[block]] struct Result {
values : array<f32>;
};
[[group(0), binding(0)]] var tex : texture_depth_2d;
[[group(0), binding(1)]] var<storage> result : [[access(read_write)]] Result;
[[stage(compute)]] fn main(
[[builtin(global_invocation_id)]] GlobalInvocationId : vec3<u32>
) {
result.values[GlobalInvocationId.y * width + GlobalInvocationId.x] = textureLoad(
tex, vec2<i32>(i32(GlobalInvocationId.x), i32(GlobalInvocationId.y)), 0);
}
)";
wgpu::ShaderModule csModule = utils::CreateShaderModule(device, shaderSource.str().c_str());
wgpu::ComputePipelineDescriptor pipelineDescriptor;
pipelineDescriptor.computeStage.module = csModule;
pipelineDescriptor.computeStage.entryPoint = "main";
wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor);
// Create and initialize the slot buffer so that it won't unexpectedly affect the count of
// resources lazily cleared.
const std::vector<float> initialBufferData(width * height, 0.f);
wgpu::Buffer readbackBuffer = utils::CreateBufferFromData(
device, initialBufferData.data(), sizeof(float) * width * height,
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage);
wgpu::TextureViewDescriptor viewDesc = {};
viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
viewDesc.dimension = wgpu::TextureViewDimension::e2D;
viewDesc.baseMipLevel = mipLevel;
viewDesc.mipLevelCount = 1;
viewDesc.baseArrayLayer = arrayLayer;
viewDesc.arrayLayerCount = 1;
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, texture.CreateView(&viewDesc)}, {1, readbackBuffer}});
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Dispatch(width, height);
pass.EndPass();
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
return EXPECT_BUFFER_FLOAT_RANGE_EQ(expected.data(), readbackBuffer, 0, expected.size());
}
void DawnTestBase::WaitABit() {
device.Tick();
FlushWire();

View File

@ -402,6 +402,13 @@ class DawnTestBase {
level, aspect, sizeof(T), bytesPerRow);
}
std::ostringstream& ExpectSampledDepthData(wgpu::Texture depthTexture,
uint32_t width,
uint32_t height,
uint32_t arrayLayer,
uint32_t mipLevel,
const std::vector<float>& expected);
void WaitABit();
void FlushWire();
void WaitForAllOperations();

View File

@ -23,12 +23,13 @@
namespace {
using Format = wgpu::TextureFormat;
using Aspect = wgpu::TextureAspect;
using Usage = wgpu::TextureUsage;
using Dimension = wgpu::TextureDimension;
using DepthOrArrayLayers = uint32_t;
using Mip = uint32_t;
DAWN_TEST_PARAM_STRUCT(Params, Format, Usage, Dimension, DepthOrArrayLayers, Mip)
DAWN_TEST_PARAM_STRUCT(Params, Format, Aspect, Usage, Dimension, DepthOrArrayLayers, Mip)
template <typename T>
class ExpectNonZero : public detail::CustomTextureExpectation {
@ -90,17 +91,40 @@ namespace {
// TODO(crbug.com/dawn/791): Determine Intel specific platforms this occurs on, and
// implement a workaround on all backends (happens on Windows too, but not on our test
// machines).
DAWN_SUPPRESS_TEST_IF(GetParam().mFormat == wgpu::TextureFormat::Depth32Float &&
IsMetal() && IsIntel() && GetParam().mMip != 0);
DAWN_SUPPRESS_TEST_IF(
(GetParam().mFormat == wgpu::TextureFormat::Depth32Float ||
GetParam().mFormat == wgpu::TextureFormat::Depth24PlusStencil8) &&
IsMetal() && IsIntel() && GetParam().mMip != 0);
// Copies from depth textures not fully supported on the OpenGL backend right now.
DAWN_SUPPRESS_TEST_IF(GetParam().mFormat == wgpu::TextureFormat::Depth32Float &&
(IsOpenGL() || IsOpenGLES()));
// Copies from stencil textures not fully supported on the OpenGL backend right now.
DAWN_SUPPRESS_TEST_IF(GetParam().mFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
GetParam().mAspect == wgpu::TextureAspect::StencilOnly &&
(IsOpenGL() || IsOpenGLES()));
// TODO(crbug.com/dawn/593): Test depends on glTextureView which is unsupported on GLES.
DAWN_SUPPRESS_TEST_IF(GetParam().mFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
GetParam().mAspect == wgpu::TextureAspect::DepthOnly &&
IsOpenGLES());
// Sampled depth only populates the first texel when running on OpenGL Mesa.
DAWN_SUPPRESS_TEST_IF(GetParam().mFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
GetParam().mAspect == wgpu::TextureAspect::DepthOnly &&
IsOpenGL() && IsLinux());
// GL may support the extension, but reading data back is not implemented.
DAWN_TEST_UNSUPPORTED_IF(GetParam().mFormat == wgpu::TextureFormat::BC1RGBAUnorm &&
(IsOpenGL() || IsOpenGLES()));
// TODO(crbug.com/tint/827): HLSL writer produces invalid code.
DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("use_tint_generator") &&
GetParam().mFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
GetParam().mAspect == wgpu::TextureAspect::DepthOnly &&
IsD3D12());
wgpu::TextureDescriptor descriptor;
descriptor.dimension = GetParam().mDimension;
descriptor.size.width = kSize;
@ -141,6 +165,39 @@ namespace {
{mipSize, mipSize, depthOrArrayLayers}, mip);
break;
}
case wgpu::TextureFormat::Depth24PlusStencil8: {
switch (GetParam().mAspect) {
case wgpu::TextureAspect::DepthOnly: {
uint32_t value = 0x01010101;
float fValue = *reinterpret_cast<float*>(&value);
std::vector<float> expectedDepth(
mipSize * mipSize,
(IsVulkan() || IsOpenGL() ||
(GetParam().mUsage & wgpu::TextureUsage::RenderAttachment) != 0)
? 1.f
: fValue);
for (uint32_t arrayLayer = 0;
arrayLayer < GetParam().mDepthOrArrayLayers; ++arrayLayer) {
ExpectSampledDepthData(texture, mipSize, mipSize, arrayLayer, mip,
expectedDepth)
<< "arrayLayer " << arrayLayer;
}
break;
}
case wgpu::TextureAspect::StencilOnly: {
uint32_t texelCount = mipSize * mipSize * depthOrArrayLayers;
std::vector<uint8_t> expectedStencil(texelCount, 1);
EXPECT_TEXTURE_EQ(expectedStencil.data(), texture, {0, 0, 0},
{mipSize, mipSize, depthOrArrayLayers}, mip,
wgpu::TextureAspect::StencilOnly);
break;
}
default:
UNREACHABLE();
}
break;
}
case wgpu::TextureFormat::BC1RGBAUnorm: {
// Set buffer with dirty data so we know it is cleared by the lazy cleared
// texture copy
@ -192,6 +249,7 @@ namespace {
class NonzeroNonrenderableTextureCreationTests : public NonzeroTextureCreationTests {};
class NonzeroCompressedTextureCreationTests : public NonzeroTextureCreationTests {};
class NonzeroDepthTextureCreationTests : public NonzeroTextureCreationTests {};
class NonzeroDepthStencilTextureCreationTests : public NonzeroTextureCreationTests {};
} // anonymous namespace
@ -215,6 +273,11 @@ TEST_P(NonzeroDepthTextureCreationTests, TextureCreationClears) {
Run();
}
// Test that texture clears to a non-zero value because toggle is enabled.
TEST_P(NonzeroDepthStencilTextureCreationTests, TextureCreationClears) {
Run();
}
// TODO(crbug.com/794): Test/implement texture initialization for multisampled textures.
DAWN_INSTANTIATE_TEST_P(
@ -230,6 +293,7 @@ DAWN_INSTANTIATE_TEST_P(
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"})},
{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, wgpu::TextureFormat::RGBA8Unorm},
{wgpu::TextureAspect::All},
{wgpu::TextureUsage(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc),
wgpu::TextureUsage::CopySrc},
{wgpu::TextureDimension::e2D, wgpu::TextureDimension::e3D},
@ -248,6 +312,7 @@ DAWN_INSTANTIATE_TEST_P(NonzeroNonrenderableTextureCreationTests,
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"})},
{wgpu::TextureFormat::RGBA8Snorm},
{wgpu::TextureAspect::All},
{wgpu::TextureUsage::CopySrc},
{wgpu::TextureDimension::e2D, wgpu::TextureDimension::e3D},
{1u, 7u},
@ -265,6 +330,7 @@ DAWN_INSTANTIATE_TEST_P(NonzeroCompressedTextureCreationTests,
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"})},
{wgpu::TextureFormat::BC1RGBAUnorm},
{wgpu::TextureAspect::All},
{wgpu::TextureUsage::CopySrc},
{wgpu::TextureDimension::e2D},
{1u, 7u},
@ -282,9 +348,31 @@ DAWN_INSTANTIATE_TEST_P(NonzeroDepthTextureCreationTests,
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"})},
{wgpu::TextureFormat::Depth32Float},
{wgpu::TextureAspect::All},
{wgpu::TextureUsage(wgpu::TextureUsage::RenderAttachment |
wgpu::TextureUsage::CopySrc),
wgpu::TextureUsage::CopySrc},
{wgpu::TextureDimension::e2D},
{1u, 7u},
{0u, 1u, 2u, 3u});
DAWN_INSTANTIATE_TEST_P(
NonzeroDepthStencilTextureCreationTests,
{D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
MetalBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
OpenGLESBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"})},
{wgpu::TextureFormat::Depth24PlusStencil8},
{wgpu::TextureAspect::DepthOnly, wgpu::TextureAspect::StencilOnly},
{wgpu::TextureUsage(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::Sampled),
wgpu::TextureUsage(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc)},
{wgpu::TextureDimension::e2D},
{1u, 7u},
{0u, 1u, 2u, 3u});