diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm index 355cf91f3e..cd0cbb09ec 100644 --- a/src/dawn_native/metal/TextureMTL.mm +++ b/src/dawn_native/metal/TextureMTL.mm @@ -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(largestMipBytesPerRow) * - (largestMipSize.height / blockInfo.height), - 512llu); - - uint64_t bufferSize = largestMipBytesPerImage * largestMipSize.depthOrArrayLayers; - - if (bufferSize > std::numeric_limits::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 encoder = commandContext->EnsureBlit(); - id 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(largestMipBytesPerRow) * + (largestMipSize.height / blockInfo.height), + 512llu); + + uint64_t bufferSize = largestMipBytesPerImage * largestMipSize.depthOrArrayLayers; + + if (bufferSize > std::numeric_limits::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 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 diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp index a7e0aa4ed3..7496d6a36a 100644 --- a/src/tests/DawnTest.cpp +++ b/src/tests/DawnTest.cpp @@ -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& expected) { + std::ostringstream shaderSource; + shaderSource << "let width : u32 = " << width << "u;\n"; + shaderSource << R"( + [[block]] struct Result { + values : array; + }; + + [[group(0), binding(0)]] var tex : texture_depth_2d; + [[group(0), binding(1)]] var result : [[access(read_write)]] Result; + + [[stage(compute)]] fn main( + [[builtin(global_invocation_id)]] GlobalInvocationId : vec3 + ) { + result.values[GlobalInvocationId.y * width + GlobalInvocationId.x] = textureLoad( + tex, vec2(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 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(); diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index b016a33480..7b837a5d48 100644 --- a/src/tests/DawnTest.h +++ b/src/tests/DawnTest.h @@ -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& expected); + void WaitABit(); void FlushWire(); void WaitForAllOperations(); diff --git a/src/tests/end2end/NonzeroTextureCreationTests.cpp b/src/tests/end2end/NonzeroTextureCreationTests.cpp index b20d2ec39c..42ab21238a 100644 --- a/src/tests/end2end/NonzeroTextureCreationTests.cpp +++ b/src/tests/end2end/NonzeroTextureCreationTests.cpp @@ -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 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(&value); + std::vector 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 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});