From c532048062befdee8bb57d18f01d3a3c403d49de Mon Sep 17 00:00:00 2001 From: Natasha Lee Date: Fri, 4 Dec 2020 19:31:40 +0000 Subject: [PATCH] Add testing and implementation for lazy init compressed textures Bug: dawn:145 Change-Id: I176ac2fb4c7db708bb147e2ad0118538907b43d3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/33040 Commit-Queue: Natasha Lee Reviewed-by: Austin Eng --- src/dawn_native/d3d12/TextureD3D12.cpp | 14 +- src/dawn_native/vulkan/TextureVk.cpp | 161 +++++--- .../end2end/CompressedTextureFormatTests.cpp | 49 +-- src/tests/end2end/TextureZeroInitTests.cpp | 353 +++++++++++++++++- 4 files changed, 472 insertions(+), 105 deletions(-) diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index a1909f96a9..0cac08bd20 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -851,11 +851,6 @@ namespace dawn_native { namespace d3d12 { MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext, const SubresourceRange& range, TextureBase::ClearValue clearValue) { - // TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies. - if (GetFormat().isCompressed) { - SetIsSubresourceContentInitialized(true, range); - return {}; - } ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); @@ -951,12 +946,7 @@ namespace dawn_native { namespace d3d12 { uint32_t bytesPerRow = Align((GetWidth() / blockInfo.width) * blockInfo.byteSize, kTextureBytesPerRowAlignment); - uint64_t bufferSize64 = bytesPerRow * (GetHeight() / blockInfo.height); - if (bufferSize64 > std::numeric_limits::max()) { - return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); - } - uint32_t bufferSize = static_cast(bufferSize64); - + uint64_t bufferSize = bytesPerRow * (GetHeight() / blockInfo.height); DynamicUploader* uploader = device->GetDynamicUploader(); UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, @@ -967,7 +957,7 @@ namespace dawn_native { namespace d3d12 { for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; ++level) { // compute d3d12 texture copy locations for texture and buffer - Extent3D copySize = GetMipLevelVirtualSize(level); + Extent3D copySize = GetMipLevelPhysicalSize(level); uint32_t rowsPerImage = GetHeight() / blockInfo.height; Texture2DCopySplit copySplit = ComputeTextureCopySplit( diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index 1b4840de4e..81b3d3bc8b 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -1006,65 +1006,116 @@ namespace dawn_native { namespace vulkan { imageRange.levelCount = 1; imageRange.layerCount = 1; - for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; - ++level) { - imageRange.baseMipLevel = level; - for (uint32_t layer = range.baseArrayLayer; - layer < range.baseArrayLayer + range.layerCount; ++layer) { - Aspect aspects = Aspect::None; - for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (GetFormat().isCompressed) { + if (range.aspects == Aspect::None) { + return {}; + } + // need to clear the texture with a copy from buffer + ASSERT(range.aspects == Aspect::Color); + const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block; + + uint32_t bytesPerRow = Align((GetWidth() / blockInfo.width) * blockInfo.byteSize, + device->GetOptimalBytesPerRowAlignment()); + uint64_t bufferSize = bytesPerRow * (GetHeight() / blockInfo.height); + DynamicUploader* uploader = device->GetDynamicUploader(); + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + uploader->Allocate(bufferSize, device->GetPendingCommandSerial(), + blockInfo.byteSize)); + memset(uploadHandle.mappedBuffer, uClearColor, bufferSize); + + std::vector regions; + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + imageRange.baseMipLevel = level; + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleMipAndLayer(level, layer, aspect))) { + SubresourceRange::SingleMipAndLayer(level, layer, range.aspects))) { // Skip lazy clears if already initialized. continue; } - aspects |= aspect; + + TextureDataLayout dataLayout; + dataLayout.offset = uploadHandle.startOffset; + dataLayout.rowsPerImage = GetHeight() / blockInfo.height; + dataLayout.bytesPerRow = bytesPerRow; + TextureCopy textureCopy; + textureCopy.aspect = range.aspects; + textureCopy.mipLevel = level; + textureCopy.origin = {0, 0, layer}; + textureCopy.texture = this; + + regions.push_back(ComputeBufferImageCopyRegion(dataLayout, textureCopy, + GetMipLevelPhysicalSize(level))); } - - if (aspects == Aspect::None) { - continue; - } - - imageRange.aspectMask = VulkanAspectMask(aspects); - imageRange.baseArrayLayer = layer; - - if (aspects & (Aspect::Depth | Aspect::Stencil)) { - VkClearDepthStencilValue clearDepthStencilValue[1]; - clearDepthStencilValue[0].depth = fClearColor; - clearDepthStencilValue[0].stencil = uClearColor; - device->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer, - GetHandle(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - clearDepthStencilValue, 1, &imageRange); - } else { - ASSERT(aspects == Aspect::Color); - VkClearColorValue clearColorValue; - switch (GetFormat().GetAspectInfo(Aspect::Color).baseType) { - case wgpu::TextureComponentType::Float: - clearColorValue.float32[0] = fClearColor; - clearColorValue.float32[1] = fClearColor; - clearColorValue.float32[2] = fClearColor; - clearColorValue.float32[3] = fClearColor; - break; - case wgpu::TextureComponentType::Sint: - clearColorValue.int32[0] = sClearColor; - clearColorValue.int32[1] = sClearColor; - clearColorValue.int32[2] = sClearColor; - clearColorValue.int32[3] = sClearColor; - break; - case wgpu::TextureComponentType::Uint: - clearColorValue.uint32[0] = uClearColor; - clearColorValue.uint32[1] = uClearColor; - clearColorValue.uint32[2] = uClearColor; - clearColorValue.uint32[3] = uClearColor; - break; - case wgpu::TextureComponentType::DepthComparison: - UNREACHABLE(); + } + device->fn.CmdCopyBufferToImage( + recordingContext->commandBuffer, + ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle(), GetHandle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, regions.data()); + } else { + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + imageRange.baseMipLevel = level; + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + Aspect aspects = Aspect::None; + for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleMipAndLayer(level, layer, aspect))) { + // Skip lazy clears if already initialized. + continue; + } + aspects |= aspect; + } + + if (aspects == Aspect::None) { + continue; + } + + imageRange.aspectMask = VulkanAspectMask(aspects); + imageRange.baseArrayLayer = layer; + + if (aspects & (Aspect::Depth | Aspect::Stencil)) { + VkClearDepthStencilValue clearDepthStencilValue[1]; + clearDepthStencilValue[0].depth = fClearColor; + clearDepthStencilValue[0].stencil = uClearColor; + device->fn.CmdClearDepthStencilImage( + recordingContext->commandBuffer, GetHandle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearDepthStencilValue, 1, + &imageRange); + } else { + ASSERT(aspects == Aspect::Color); + VkClearColorValue clearColorValue; + switch (GetFormat().GetAspectInfo(Aspect::Color).baseType) { + case wgpu::TextureComponentType::Float: + clearColorValue.float32[0] = fClearColor; + clearColorValue.float32[1] = fClearColor; + clearColorValue.float32[2] = fClearColor; + clearColorValue.float32[3] = fClearColor; + break; + case wgpu::TextureComponentType::Sint: + clearColorValue.int32[0] = sClearColor; + clearColorValue.int32[1] = sClearColor; + clearColorValue.int32[2] = sClearColor; + clearColorValue.int32[3] = sClearColor; + break; + case wgpu::TextureComponentType::Uint: + clearColorValue.uint32[0] = uClearColor; + clearColorValue.uint32[1] = uClearColor; + clearColorValue.uint32[2] = uClearColor; + clearColorValue.uint32[3] = uClearColor; + break; + case wgpu::TextureComponentType::DepthComparison: + UNREACHABLE(); + } + device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &clearColorValue, 1, &imageRange); } - device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &clearColorValue, 1, &imageRange); } } } @@ -1082,12 +1133,6 @@ namespace dawn_native { namespace vulkan { return; } if (!IsSubresourceContentInitialized(range)) { - // TODO(jiawei.shao@intel.com): initialize textures in BC formats with Buffer-to-Texture - // copies. - if (GetFormat().isCompressed) { - return; - } - // If subresource has not been initialized, clear it to black as it could contain dirty // bits from recycled memory GetDevice()->ConsumedError( diff --git a/src/tests/end2end/CompressedTextureFormatTests.cpp b/src/tests/end2end/CompressedTextureFormatTests.cpp index 453da66e7d..d0b0c30489 100644 --- a/src/tests/end2end/CompressedTextureFormatTests.cpp +++ b/src/tests/end2end/CompressedTextureFormatTests.cpp @@ -419,16 +419,7 @@ class CompressedTextureBCFormatTest : public DawnTest { return sizeAtLevel; } - const std::array kBCFormats = { - wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, - wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, - wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, - wgpu::TextureFormat::BC4RSnorm, wgpu::TextureFormat::BC4RUnorm, - wgpu::TextureFormat::BC5RGSnorm, wgpu::TextureFormat::BC5RGUnorm, - wgpu::TextureFormat::BC6HRGBFloat, wgpu::TextureFormat::BC6HRGBUfloat, - wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; - - // Tthe block width and height in texels are 4 for all BC formats. + // The block width and height in texels are 4 for all BC formats. static constexpr uint32_t kBCBlockWidthInTexels = 4; static constexpr uint32_t kBCBlockHeightInTexels = 4; @@ -454,7 +445,7 @@ TEST_P(CompressedTextureBCFormatTest, Basic) { config.textureDescriptor.size = {8, 8, 1}; config.copyExtent3D = config.textureDescriptor.size; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -477,7 +468,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyIntoSubRegion) { config.copyOrigin3D = kOrigin; config.copyExtent3D = kExtent3D; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -503,7 +494,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyIntoNonZeroArrayLayer) { config.textureDescriptor.size.depth = kArrayLayerCount; config.copyOrigin3D.z = kArrayLayerCount - 1; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -543,7 +534,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyBufferIntoNonZeroMipmapLevel) { config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1}; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -580,7 +571,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyWholeTextureSubResourceIntoNonZeroMipm ASSERT_NE(0u, kVirtualSize.height % kBCBlockHeightInTexels); config.copyExtent3D = kPhysicalSize; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC // compressed data. config.textureDescriptor.format = format; @@ -643,7 +634,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyIntoSubresourceWithPhysicalSizeNotEqua ASSERT_LT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width, kSrcVirtualSize.width); ASSERT_LT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height, kSrcVirtualSize.height); - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC // compressed data. srcConfig.textureDescriptor.format = format; @@ -705,7 +696,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyFromSubresourceWithPhysicalSizeNotEqua ASSERT_GT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width, kSrcVirtualSize.width); ASSERT_GT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height, kSrcVirtualSize.height); - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { srcConfig.textureDescriptor.format = dstConfig.textureDescriptor.format = format; srcConfig.textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; @@ -773,7 +764,7 @@ TEST_P(CompressedTextureBCFormatTest, MultipleCopiesWithPhysicalSizeNotEqualToVi ASSERT_NE(0u, dstVirtualSizes[1].width % kBCBlockWidthInTexels); ASSERT_NE(0u, dstVirtualSizes[1].height % kBCBlockHeightInTexels); - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { std::array bcSrcTextures; std::array bcDstTextures; @@ -832,7 +823,7 @@ TEST_P(CompressedTextureBCFormatTest, BufferOffsetAndExtentFitRowPitch) { const uint32_t blockCountPerRow = config.textureDescriptor.size.width / kBCBlockWidthInTexels; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); @@ -868,7 +859,7 @@ TEST_P(CompressedTextureBCFormatTest, BufferOffsetExceedsSlicePitch) { const uint32_t slicePitchInBytes = config.bytesPerRowAlignment * (textureSizeLevel0.height / kBCBlockHeightInTexels); - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); @@ -902,7 +893,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyWithBufferOffsetAndExtentExceedRowPitc constexpr uint32_t kExceedRowBlockCount = 1; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); @@ -932,7 +923,7 @@ TEST_P(CompressedTextureBCFormatTest, RowPitchEqualToSlicePitch) { const uint32_t blockCountPerRow = config.textureDescriptor.size.width / kBCBlockWidthInTexels; const uint32_t slicePitchInBytes = config.bytesPerRowAlignment; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); @@ -965,7 +956,7 @@ TEST_P(CompressedTextureBCFormatTest, LargeImageHeight) { config.rowsPerImage = config.textureDescriptor.size.height * 2 / kBCBlockHeightInTexels; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -1008,7 +999,7 @@ TEST_P(CompressedTextureBCFormatTest, LargeImageHeightAndClampedCopyExtent) { config.rowsPerImage = kCopyHeightAtLevel * 2 / kBCBlockHeightInTexels; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -1036,7 +1027,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyWhole2DArrayTexture) { config.copyExtent3D = config.textureDescriptor.size; config.copyExtent3D.depth = kArrayLayerCount; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -1066,7 +1057,7 @@ TEST_P(CompressedTextureBCFormatTest, CopyMultiple2DArrayLayers) { config.copyExtent3D = config.textureDescriptor.size; config.copyExtent3D.depth = kCopyLayerCount; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestCopyRegionIntoBCFormatTextures(config); } @@ -1158,7 +1149,7 @@ TEST_P(CompressedTextureWriteTextureTest, Basic) { config.bytesPerRowAlignment = 511; config.rowsPerImage = 5; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestWriteRegionIntoBCFormatTextures(config); } @@ -1179,7 +1170,7 @@ TEST_P(CompressedTextureWriteTextureTest, WriteMultiple2DArrayLayers) { config.bytesPerRowAlignment = 511; config.rowsPerImage = 5; - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { config.textureDescriptor.format = format; TestWriteRegionIntoBCFormatTextures(config); } @@ -1199,7 +1190,7 @@ TEST_P(CompressedTextureWriteTextureTest, // the texture physical size, but doesn't fit in the virtual size. for (unsigned int w : {12, 16}) { for (unsigned int h : {12, 16}) { - for (wgpu::TextureFormat format : kBCFormats) { + for (wgpu::TextureFormat format : utils::kBCFormats) { CopyConfig config; config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; config.textureDescriptor.size = {60, 60, 1}; diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp index e8ec0cc02b..352d97069c 100644 --- a/src/tests/end2end/TextureZeroInitTests.cpp +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -31,7 +31,6 @@ } \ } while (0) -// TODO(natlee@microsoft.com): test compressed textures are cleared class TextureZeroInitTest : public DawnTest { protected: void SetUp() override { @@ -53,10 +52,12 @@ class TextureZeroInitTest : public DawnTest { descriptor.usage = usage; return descriptor; } - wgpu::TextureViewDescriptor CreateTextureViewDescriptor(uint32_t baseMipLevel, - uint32_t baseArrayLayer) { + wgpu::TextureViewDescriptor CreateTextureViewDescriptor( + uint32_t baseMipLevel, + uint32_t baseArrayLayer, + wgpu::TextureFormat format = kColorFormat) { wgpu::TextureViewDescriptor descriptor; - descriptor.format = kColorFormat; + descriptor.format = format; descriptor.baseArrayLayer = baseArrayLayer; descriptor.arrayLayerCount = 1; descriptor.baseMipLevel = baseMipLevel; @@ -108,10 +109,11 @@ class TextureZeroInitTest : public DawnTest { fragColor = texelFetch(sampler2D(texture0, sampler0), ivec2(gl_FragCoord), 0); })"); } + constexpr static uint32_t kSize = 128; constexpr static uint32_t kUnalignedSize = 127; - // All three texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm) have the - // same byte size of 4. + // All texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm, BC formats) + // have the same block byte size of 4. constexpr static uint32_t kFormatBlockByteSize = 4; constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; constexpr static wgpu::TextureFormat kDepthStencilFormat = @@ -1644,3 +1646,342 @@ DAWN_INSTANTIATE_TEST(TextureZeroInitTest, OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}), MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"})); + +class CompressedTextureZeroInitTest : public TextureZeroInitTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + // TODO: find out why this test is flaky on Windows Intel Vulkan bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + } + + std::vector GetRequiredExtensions() override { + mIsBCFormatSupported = SupportsExtensions({"texture_compression_bc"}); + if (!mIsBCFormatSupported) { + return {}; + } + + return {"texture_compression_bc"}; + } + + bool IsBCFormatSupported() const { + return mIsBCFormatSupported; + } + + // Copy the compressed texture data into the destination texture. + void InitializeDataInCompressedTextureAndExpectLazyClear( + wgpu::Texture bcCompressedTexture, + wgpu::TextureDescriptor textureDescriptor, + wgpu::Extent3D copyExtent3D, + uint32_t viewMipmapLevel, + uint32_t baseArrayLayer, + size_t lazyClearCount) { + uint32_t copyWidthInBlock = copyExtent3D.width / kFormatBlockByteSize; + uint32_t copyHeightInBlock = copyExtent3D.height / kFormatBlockByteSize; + uint32_t copyBytesPerRow = + Align(copyWidthInBlock * utils::GetTexelBlockSizeInBytes(textureDescriptor.format), + kTextureBytesPerRowAlignment); + + std::vector data( + utils::RequiredBytesInCopy(copyBytesPerRow, copyHeightInBlock, copyExtent3D, + textureDescriptor.format), + 1); + + // Copy texture data from a staging buffer to the destination texture. + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), data.size(), + wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, copyBytesPerRow, copyHeightInBlock); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView( + bcCompressedTexture, viewMipmapLevel, {0, 0, baseArrayLayer}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Extent3D); + wgpu::CommandBuffer copy = encoder.Finish(); + EXPECT_LAZY_CLEAR(lazyClearCount, queue.Submit(1, ©)); + } + + // Run the tests that copies pre-prepared BC format data into a BC texture and verifies if we + // can render correctly with the pixel values sampled from the BC texture. + // Expect that the texture subresource is initialized + void TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + wgpu::TextureDescriptor textureDescriptor, + wgpu::Extent3D copyExtent3D, + wgpu::Extent3D nonPaddedCopyExtent, + uint32_t viewMipmapLevel, + uint32_t baseArrayLayer, + size_t lazyClearCount, + bool halfCopyTest = false) { + wgpu::Texture bcTexture = device.CreateTexture(&textureDescriptor); + InitializeDataInCompressedTextureAndExpectLazyClear(bcTexture, textureDescriptor, + copyExtent3D, viewMipmapLevel, + baseArrayLayer, lazyClearCount); + + SampleCompressedTextureAndVerifyColor(bcTexture, textureDescriptor, copyExtent3D, + nonPaddedCopyExtent, viewMipmapLevel, baseArrayLayer, + halfCopyTest); + } + + void SampleCompressedTextureAndVerifyColor(wgpu::Texture bcTexture, + wgpu::TextureDescriptor textureDescriptor, + wgpu::Extent3D copyExtent3D, + wgpu::Extent3D nonPaddedCopyExtent, + uint32_t viewMipmapLevel, + uint32_t baseArrayLayer, + bool halfCopyTest = false) { + // Sample the compressed texture and verify the texture colors in the render target + utils::BasicRenderPass renderPass = + utils::CreateBasicRenderPass(device, textureDescriptor.size.width >> viewMipmapLevel, + textureDescriptor.size.height >> viewMipmapLevel); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + renderPipelineDescriptor.cColorStates[0].format = kColorFormat; + renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + renderPipelineDescriptor.cFragmentStage.module = + CreateSampledTextureFragmentShaderForTest(); + wgpu::RenderPipeline renderPipeline = + device.CreateRenderPipeline(&renderPipelineDescriptor); + pass.SetPipeline(renderPipeline); + + wgpu::SamplerDescriptor samplerDesc = {}; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::TextureViewDescriptor textureViewDescriptor = CreateTextureViewDescriptor( + viewMipmapLevel, baseArrayLayer, textureDescriptor.format); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, bcTexture.CreateView(&textureViewDescriptor)}}); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected(nonPaddedCopyExtent.width * nonPaddedCopyExtent.height, + {0x00, 0x20, 0x08, 0xFF}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, nonPaddedCopyExtent.width, + nonPaddedCopyExtent.height, 0, 0); + EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(bcTexture.Get(), viewMipmapLevel, + 1, baseArrayLayer, 1)); + + // If we only copied to half the texture, check the other half is initialized to black + if (halfCopyTest) { + std::vector expectBlack(nonPaddedCopyExtent.width * nonPaddedCopyExtent.height, + {0x00, 0x00, 0x00, 0xFF}); + EXPECT_TEXTURE_RGBA8_EQ(expectBlack.data(), renderPass.color, copyExtent3D.width, 0, + nonPaddedCopyExtent.width, nonPaddedCopyExtent.height, 0, 0); + } + } + + bool mIsBCFormatSupported = false; +}; + +// Test that the clearing is skipped when we use a full mip copy (with the physical size different +// than the virtual mip size) +TEST_P(CompressedTextureZeroInitTest, FullMipCopy) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + textureDescriptor.size = {60, 60, 1}; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.format = utils::kBCFormats[0]; + + TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + textureDescriptor, textureDescriptor.size, textureDescriptor.size, 0, 0, 0u); +} + +// Test that 1 lazy clear count happens when we copy to half the texture +TEST_P(CompressedTextureZeroInitTest, HalfCopyBufferToTexture) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + constexpr static uint32_t kSize = 16; + textureDescriptor.size = {kSize, kSize, 1}; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.format = utils::kBCFormats[0]; + + wgpu::Extent3D copyExtent3D = {kSize / 2, kSize, 1}; + + TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + textureDescriptor, copyExtent3D, copyExtent3D, 0, 0, 1u, true); +} + +// Test that 0 lazy clear count happens when we copy buffer to texture to a nonzero mip level +// (with physical size different from the virtual mip size) +TEST_P(CompressedTextureZeroInitTest, FullCopyToNonZeroMipLevel) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + constexpr static uint32_t kSize = 60; + textureDescriptor.size = {kSize, kSize, 1}; + textureDescriptor.mipLevelCount = 3; + textureDescriptor.format = utils::kBCFormats[0]; + const uint32_t kViewMipLevel = 2; + const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; + + const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); + + wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel, kCopySizeAtLevel, 1}; + + TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + textureDescriptor, copyExtent3D, {kActualSizeAtLevel, kActualSizeAtLevel, 1}, kViewMipLevel, + 0, 0u); +} + +// Test that 1 lazy clear count happens when we copy buffer to half texture to a nonzero mip level +// (with physical size different from the virtual mip size) +TEST_P(CompressedTextureZeroInitTest, HalfCopyToNonZeroMipLevel) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + constexpr static uint32_t kSize = 60; + textureDescriptor.size = {kSize, kSize, 1}; + textureDescriptor.mipLevelCount = 3; + textureDescriptor.format = utils::kBCFormats[0]; + const uint32_t kViewMipLevel = 2; + const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; + + const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); + + wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel / 2, kCopySizeAtLevel, 1}; + + TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + textureDescriptor, copyExtent3D, {kActualSizeAtLevel / 2, kActualSizeAtLevel, 1}, + kViewMipLevel, 0, 1u, true); +} + +// Test that 0 lazy clear count happens when we copy buffer to nonzero array layer +TEST_P(CompressedTextureZeroInitTest, FullCopyToNonZeroArrayLayer) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + constexpr static uint32_t kSize = 16; + constexpr static uint32_t kArrayLayers = 4; + textureDescriptor.size = {kSize, kSize, kArrayLayers}; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.format = utils::kBCFormats[0]; + + wgpu::Extent3D copyExtent3D = {kSize, kSize, 1}; + + TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + textureDescriptor, copyExtent3D, copyExtent3D, 0, kArrayLayers - 2, 0u); +} + +// Test that 1 lazy clear count happens when we copy buffer to half texture to a nonzero array layer +TEST_P(CompressedTextureZeroInitTest, HalfCopyToNonZeroArrayLayer) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + constexpr static uint32_t kSize = 16; + constexpr static uint32_t kArrayLayers = 4; + textureDescriptor.size = {kSize, kSize, kArrayLayers}; + textureDescriptor.mipLevelCount = 3; + textureDescriptor.format = utils::kBCFormats[0]; + + wgpu::Extent3D copyExtent3D = {kSize / 2, kSize, 1}; + + TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( + textureDescriptor, copyExtent3D, copyExtent3D, 0, kArrayLayers - 2, 1u, true); +} + +// full copy texture to texture, 0 lazy clears are needed +TEST_P(CompressedTextureZeroInitTest, FullCopyTextureToTextureMipLevel) { + // create srcTexture and fill it with data + wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( + 3, 1, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, + utils::kBCFormats[0]); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + const uint32_t kViewMipLevel = 2; + const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; + + const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); + + wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel, kCopySizeAtLevel, 1}; + + // fill srcTexture with data + InitializeDataInCompressedTextureAndExpectLazyClear(srcTexture, srcDescriptor, copyExtent3D, + kViewMipLevel, 0, 0u); + + wgpu::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, kViewMipLevel, {0, 0, 0}); + + // create dstTexture that we will copy to + wgpu::TextureDescriptor dstDescriptor = CreateTextureDescriptor( + 3, 1, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled, + utils::kBCFormats[0]); + wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); + + wgpu::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, kViewMipLevel, {0, 0, 0}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Extent3D); + wgpu::CommandBuffer commands = encoder.Finish(); + // the dstTexture does not need to be lazy cleared since it's fully copied to + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + SampleCompressedTextureAndVerifyColor(dstTexture, dstDescriptor, copyExtent3D, + {kActualSizeAtLevel, kActualSizeAtLevel, 1}, + kViewMipLevel, 0); +} + +// half copy texture to texture, lazy clears are needed for noncopied half +TEST_P(CompressedTextureZeroInitTest, HalfCopyTextureToTextureMipLevel) { + // create srcTexture with data + wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( + 3, 1, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, + utils::kBCFormats[0]); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + const uint32_t kViewMipLevel = 2; + const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; + + const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); + + wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel / 2, kCopySizeAtLevel, 1}; + + // fill srcTexture with data + InitializeDataInCompressedTextureAndExpectLazyClear(srcTexture, srcDescriptor, copyExtent3D, + kViewMipLevel, 0, 1u); + + wgpu::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, kViewMipLevel, {0, 0, 0}); + + // create dstTexture that we will copy to + wgpu::TextureDescriptor dstDescriptor = CreateTextureDescriptor( + 3, 1, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled, + utils::kBCFormats[0]); + wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); + + wgpu::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, kViewMipLevel, {0, 0, 0}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Extent3D); + wgpu::CommandBuffer commands = encoder.Finish(); + // expect 1 lazy clear count since the dstTexture needs to be lazy cleared when we only copy to + // half texture + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + SampleCompressedTextureAndVerifyColor(dstTexture, dstDescriptor, copyExtent3D, + {kActualSizeAtLevel / 2, kActualSizeAtLevel, 1}, + kViewMipLevel, 0, true); +} + +DAWN_INSTANTIATE_TEST(CompressedTextureZeroInitTest, + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}), + MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), + VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}));