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:
parent
ff70f98545
commit
3cd8c43857
src
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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});
|
||||
|
|
Loading…
Reference in New Issue