Vulkan: clear nonrenderable texture color formats
Clears nonrenderable color formats and adds a clearValue enum to help
share the code path between zero and nonzero clears.
Bug: dawn:145
Change-Id: I285521cae0ee71625602b949888b21481a05fb8e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10021
Commit-Queue: Natasha Lee <natlee@microsoft.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h
index 066a7d7..e5c59ee 100644
--- a/src/dawn_native/Texture.h
+++ b/src/dawn_native/Texture.h
@@ -43,7 +43,7 @@
class TextureBase : public ObjectBase {
public:
enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
-
+ enum class ClearValue { Zero, NonZero };
TextureBase(DeviceBase* device, const TextureDescriptor* descriptor, TextureState state);
static TextureBase* MakeError(DeviceBase* device);
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 7b1c7fe..bc25ea2 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -27,6 +27,7 @@
#include "dawn_native/vulkan/RenderPassCache.h"
#include "dawn_native/vulkan/RenderPipelineVk.h"
#include "dawn_native/vulkan/TextureVk.h"
+#include "dawn_native/vulkan/UtilsVulkan.h"
namespace dawn_native { namespace vulkan {
@@ -43,59 +44,6 @@
}
}
- // Vulkan SPEC requires the source/destination region specified by each element of
- // pRegions must be a region that is contained within srcImage/dstImage. Here the size of
- // the image refers to the virtual size, while Dawn validates texture copy extent with the
- // physical size, so we need to re-calculate the texture copy extent to ensure it should fit
- // in the virtual size of the subresource.
- Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy,
- const Extent3D& copySize) {
- Extent3D validTextureCopyExtent = copySize;
- const TextureBase* texture = textureCopy.texture.Get();
- Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(textureCopy.mipLevel);
- if (textureCopy.origin.x + copySize.width > virtualSizeAtLevel.width) {
- ASSERT(texture->GetFormat().isCompressed);
- validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x;
- }
- if (textureCopy.origin.y + copySize.height > virtualSizeAtLevel.height) {
- ASSERT(texture->GetFormat().isCompressed);
- validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y;
- }
-
- return validTextureCopyExtent;
- }
-
- VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy,
- const TextureCopy& textureCopy,
- const Extent3D& copySize) {
- const Texture* texture = ToBackend(textureCopy.texture.Get());
-
- VkBufferImageCopy region;
-
- region.bufferOffset = bufferCopy.offset;
- // In Vulkan the row length is in texels while it is in bytes for Dawn
- const Format& format = texture->GetFormat();
- ASSERT(bufferCopy.rowPitch % format.blockByteSize == 0);
- region.bufferRowLength = bufferCopy.rowPitch / format.blockByteSize * format.blockWidth;
- region.bufferImageHeight = bufferCopy.imageHeight;
-
- region.imageSubresource.aspectMask = texture->GetVkAspectMask();
- region.imageSubresource.mipLevel = textureCopy.mipLevel;
- region.imageSubresource.baseArrayLayer = textureCopy.arrayLayer;
- region.imageSubresource.layerCount = 1;
-
- region.imageOffset.x = textureCopy.origin.x;
- region.imageOffset.y = textureCopy.origin.y;
- region.imageOffset.z = textureCopy.origin.z;
-
- Extent3D imageExtent = ComputeTextureCopyExtent(textureCopy, copySize);
- region.imageExtent.width = imageExtent.width;
- region.imageExtent.height = imageExtent.height;
- region.imageExtent.depth = copySize.depth;
-
- return region;
- }
-
VkImageCopy ComputeImageCopyRegion(const TextureCopy& srcCopy,
const TextureCopy& dstCopy,
const Extent3D& copySize) {
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index ad21992..fc2e031 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -16,9 +16,11 @@
#include "dawn_native/VulkanBackend.h"
#include "dawn_native/vulkan/AdapterVk.h"
+#include "dawn_native/vulkan/BufferVk.h"
#include "dawn_native/vulkan/CommandRecordingContext.h"
#include "dawn_native/vulkan/DeviceVk.h"
#include "dawn_native/vulkan/FencedDeleter.h"
+#include "dawn_native/vulkan/UtilsVulkan.h"
namespace dawn_native { namespace vulkan {
@@ -453,33 +455,8 @@
ASSERT(false);
}
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
- VkImageSubresourceRange range = {};
- range.aspectMask = GetVkAspectMask();
- range.baseMipLevel = 0;
- range.levelCount = GetNumMipLevels();
- range.baseArrayLayer = 0;
- range.layerCount = GetArrayLayers();
- TransitionUsageNow(ToBackend(GetDevice())->GetPendingRecordingContext(),
- dawn::TextureUsageBit::CopyDst);
-
- if (GetFormat().HasDepthOrStencil()) {
- VkClearDepthStencilValue clear_color[1];
- clear_color[0].depth = 1.0f;
- clear_color[0].stencil = 1u;
- ToBackend(GetDevice())
- ->fn.CmdClearDepthStencilImage(
- ToBackend(GetDevice())->GetPendingCommandBuffer(), GetHandle(),
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1, &range);
- } else {
- // TODO(natlee@microsoft.com): use correct union member depending on the texture
- // format
- VkClearColorValue clear_color = {{1.0, 1.0, 1.0, 1.0}};
-
- ToBackend(GetDevice())
- ->fn.CmdClearColorImage(ToBackend(GetDevice())->GetPendingCommandBuffer(),
- GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- &clear_color, 1, &range);
- }
+ ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(), 0, GetNumMipLevels(),
+ 0, GetArrayLayers(), TextureBase::ClearValue::NonZero);
}
}
@@ -679,36 +656,80 @@
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
- uint32_t layerCount) {
+ uint32_t layerCount,
+ TextureBase::ClearValue clearValue) {
VkImageSubresourceRange range = {};
range.aspectMask = GetVkAspectMask();
range.baseMipLevel = baseMipLevel;
range.levelCount = levelCount;
range.baseArrayLayer = baseArrayLayer;
range.layerCount = layerCount;
+ uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
TransitionUsageNow(recordingContext, dawn::TextureUsageBit::CopyDst);
if (GetFormat().HasDepthOrStencil()) {
- VkClearDepthStencilValue clear_color[1];
- clear_color[0].depth = 0.0f;
- clear_color[0].stencil = 0u;
+ VkClearDepthStencilValue clearDepthStencilValue[1];
+ clearDepthStencilValue[0].depth = clearColor;
+ clearDepthStencilValue[0].stencil = clearColor;
ToBackend(GetDevice())
->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer, GetHandle(),
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1,
- &range);
- } else {
- VkClearColorValue clear_color[1];
- clear_color[0].float32[0] = 0.0f;
- clear_color[0].float32[1] = 0.0f;
- clear_color[0].float32[2] = 0.0f;
- clear_color[0].float32[3] = 0.0f;
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ clearDepthStencilValue, 1, &range);
+ } else if (GetFormat().isRenderable) {
+ VkClearColorValue clearColorValue = {{clearColor, clearColor, clearColor, clearColor}};
ToBackend(GetDevice())
->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColorValue, 1,
&range);
+ } else {
+ // TODO(natlee@microsoft.com): test compressed textures are cleared
+ // create temp buffer with clear color to copy to the texture image
+ dawn_native::vulkan::Device* device =
+ reinterpret_cast<dawn_native::vulkan::Device*>(GetDevice());
+ dawn_native::BufferDescriptor descriptor;
+ descriptor.size = (GetSize().width / GetFormat().blockWidth) *
+ (GetSize().height / GetFormat().blockHeight) *
+ GetFormat().blockByteSize;
+ descriptor.nextInChain = nullptr;
+ descriptor.usage = dawn::BufferUsageBit::CopySrc | dawn::BufferUsageBit::MapWrite;
+ std::unique_ptr<Buffer> srcBuffer =
+ std::make_unique<dawn_native::vulkan::Buffer>(device, &descriptor);
+ uint8_t* clearBuffer = nullptr;
+ device->ConsumedError(srcBuffer->MapAtCreation(&clearBuffer));
+ std::fill(reinterpret_cast<uint32_t*>(clearBuffer),
+ reinterpret_cast<uint32_t*>(clearBuffer + descriptor.size), clearColor);
+
+ // compute the buffer image copy to set the clear region of entire texture
+ dawn_native::BufferCopy bufferCopy;
+ bufferCopy.buffer = srcBuffer.get();
+ bufferCopy.imageHeight = 0;
+ bufferCopy.offset = 0;
+ bufferCopy.rowPitch = 0;
+
+ dawn_native::TextureCopy textureCopy;
+ textureCopy.texture = this;
+ textureCopy.origin = {0, 0, 0};
+ textureCopy.mipLevel = baseMipLevel;
+ textureCopy.arrayLayer = baseArrayLayer;
+
+ Extent3D copySize = {GetSize().width, GetSize().height, 1};
+
+ VkBufferImageCopy region =
+ ComputeBufferImageCopyRegion(bufferCopy, textureCopy, copySize);
+
+ // copy the clear buffer to the texture image
+ srcBuffer->TransitionUsageNow(recordingContext, dawn::BufferUsageBit::CopySrc);
+ ToBackend(GetDevice())
+ ->fn.CmdCopyBufferToImage(recordingContext->commandBuffer, srcBuffer->GetHandle(),
+ GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
+ ®ion);
}
- SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, layerCount);
- GetDevice()->IncrementLazyClearCountForTesting();
+
+ if (clearValue == TextureBase::ClearValue::Zero) {
+ SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
+ layerCount);
+ GetDevice()->IncrementLazyClearCountForTesting();
+ }
}
void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
@@ -729,7 +750,8 @@
// If subresource has not been initialized, clear it to black as it could contain dirty
// bits from recycled memory
- ClearTexture(recordingContext, baseMipLevel, levelCount, baseArrayLayer, layerCount);
+ ClearTexture(recordingContext, baseMipLevel, levelCount, baseArrayLayer, layerCount,
+ TextureBase::ClearValue::Zero);
}
}
diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h
index 52236d3..75f7a54 100644
--- a/src/dawn_native/vulkan/TextureVk.h
+++ b/src/dawn_native/vulkan/TextureVk.h
@@ -75,7 +75,8 @@
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
- uint32_t layerCount);
+ uint32_t layerCount,
+ TextureBase::ClearValue);
VkImage mHandle = VK_NULL_HANDLE;
DeviceMemoryAllocation mMemoryAllocation;
diff --git a/src/dawn_native/vulkan/UtilsVulkan.cpp b/src/dawn_native/vulkan/UtilsVulkan.cpp
index 723a6bb..dd81f3f 100644
--- a/src/dawn_native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn_native/vulkan/UtilsVulkan.cpp
@@ -15,6 +15,9 @@
#include "dawn_native/vulkan/UtilsVulkan.h"
#include "common/Assert.h"
+#include "dawn_native/Format.h"
+#include "dawn_native/vulkan/Forward.h"
+#include "dawn_native/vulkan/TextureVk.h"
namespace dawn_native { namespace vulkan {
@@ -41,4 +44,55 @@
}
}
+ // Vulkan SPEC requires the source/destination region specified by each element of
+ // pRegions must be a region that is contained within srcImage/dstImage. Here the size of
+ // the image refers to the virtual size, while Dawn validates texture copy extent with the
+ // physical size, so we need to re-calculate the texture copy extent to ensure it should fit
+ // in the virtual size of the subresource.
+ Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, const Extent3D& copySize) {
+ Extent3D validTextureCopyExtent = copySize;
+ const TextureBase* texture = textureCopy.texture.Get();
+ Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(textureCopy.mipLevel);
+ if (textureCopy.origin.x + copySize.width > virtualSizeAtLevel.width) {
+ ASSERT(texture->GetFormat().isCompressed);
+ validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x;
+ }
+ if (textureCopy.origin.y + copySize.height > virtualSizeAtLevel.height) {
+ ASSERT(texture->GetFormat().isCompressed);
+ validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y;
+ }
+
+ return validTextureCopyExtent;
+ }
+
+ VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy,
+ const TextureCopy& textureCopy,
+ const Extent3D& copySize) {
+ const Texture* texture = ToBackend(textureCopy.texture.Get());
+
+ VkBufferImageCopy region;
+
+ region.bufferOffset = bufferCopy.offset;
+ // In Vulkan the row length is in texels while it is in bytes for Dawn
+ const Format& format = texture->GetFormat();
+ ASSERT(bufferCopy.rowPitch % format.blockByteSize == 0);
+ region.bufferRowLength = bufferCopy.rowPitch / format.blockByteSize * format.blockWidth;
+ region.bufferImageHeight = bufferCopy.imageHeight;
+
+ region.imageSubresource.aspectMask = texture->GetVkAspectMask();
+ region.imageSubresource.mipLevel = textureCopy.mipLevel;
+ region.imageSubresource.baseArrayLayer = textureCopy.arrayLayer;
+ region.imageSubresource.layerCount = 1;
+
+ region.imageOffset.x = textureCopy.origin.x;
+ region.imageOffset.y = textureCopy.origin.y;
+ region.imageOffset.z = textureCopy.origin.z;
+
+ Extent3D imageExtent = ComputeTextureCopyExtent(textureCopy, copySize);
+ region.imageExtent.width = imageExtent.width;
+ region.imageExtent.height = imageExtent.height;
+ region.imageExtent.depth = copySize.depth;
+
+ return region;
+ }
}} // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/UtilsVulkan.h b/src/dawn_native/vulkan/UtilsVulkan.h
index 80fa2da..8f4b5ac 100644
--- a/src/dawn_native/vulkan/UtilsVulkan.h
+++ b/src/dawn_native/vulkan/UtilsVulkan.h
@@ -16,12 +16,18 @@
#define DAWNNATIVE_VULKAN_UTILSVULKAN_H_
#include "common/vulkan_platform.h"
+#include "dawn_native/Commands.h"
#include "dawn_native/dawn_platform.h"
namespace dawn_native { namespace vulkan {
VkCompareOp ToVulkanCompareOp(dawn::CompareFunction op);
+ Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, const Extent3D& copySize);
+ VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy,
+ const TextureCopy& textureCopy,
+ const Extent3D& copySize);
+
}} // namespace dawn_native::vulkan
#endif // DAWNNATIVE_VULKAN_UTILSVULKAN_H_
diff --git a/src/tests/end2end/NonzeroTextureCreationTests.cpp b/src/tests/end2end/NonzeroTextureCreationTests.cpp
index 00f26c3..e7fe33b 100644
--- a/src/tests/end2end/NonzeroTextureCreationTests.cpp
+++ b/src/tests/end2end/NonzeroTextureCreationTests.cpp
@@ -94,6 +94,41 @@
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 2);
}
+// Test that nonrenderable texture formats clear to 1's because toggle is enabled
+TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
+ // skip test for other backends since they are not implemented yet
+ DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12());
+ dawn::TextureDescriptor descriptor;
+ descriptor.dimension = dawn::TextureDimension::e2D;
+ descriptor.size.width = kSize;
+ descriptor.size.height = kSize;
+ descriptor.size.depth = 1;
+ descriptor.arrayLayerCount = 1;
+ descriptor.sampleCount = 1;
+ descriptor.format = dawn::TextureFormat::RGBA16Snorm;
+ descriptor.mipLevelCount = 1;
+ descriptor.usage = dawn::TextureUsageBit::CopySrc;
+ dawn::Texture texture = device.CreateTexture(&descriptor);
+
+ // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy
+ uint32_t bufferSize = 8 * kSize * kSize;
+ std::vector<uint8_t> data(bufferSize, 100);
+ dawn::Buffer bufferDst = utils::CreateBufferFromData(
+ device, data.data(), static_cast<uint32_t>(data.size()), dawn::BufferUsageBit::CopySrc);
+
+ dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, 0, 0);
+ dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0});
+ dawn::Extent3D copySize = {kSize, kSize, 1};
+
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size);
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ std::vector<uint32_t> expected(bufferSize, 1);
+ EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
+}
+
DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests,
ForceWorkarounds(D3D12Backend,
{"nonzero_clear_resources_on_creation_for_testing"},
diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp
index bef10dc..9f10de5 100644
--- a/src/tests/end2end/TextureZeroInitTests.cpp
+++ b/src/tests/end2end/TextureZeroInitTests.cpp
@@ -86,6 +86,8 @@
constexpr static dawn::TextureFormat kColorFormat = dawn::TextureFormat::RGBA8Unorm;
constexpr static dawn::TextureFormat kDepthStencilFormat =
dawn::TextureFormat::Depth24PlusStencil8;
+ constexpr static dawn::TextureFormat kNonrenderableColorFormat =
+ dawn::TextureFormat::RGBA16Snorm;
};
// This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage
@@ -563,6 +565,34 @@
EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferTex, 0, 4);
}
+// This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures
+TEST_P(TextureZeroInitTest, NonRenderableTextureClear) {
+ // skip test for other backends since they are not implemented yet
+ DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12());
+
+ dawn::TextureDescriptor descriptor =
+ CreateTextureDescriptor(1, 1, dawn::TextureUsageBit::CopySrc, kNonrenderableColorFormat);
+ dawn::Texture texture = device.CreateTexture(&descriptor);
+
+ // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy
+ uint32_t bufferSize = 8 * kSize * kSize;
+ std::vector<uint8_t> data(bufferSize, 100);
+ dawn::Buffer bufferDst = utils::CreateBufferFromData(
+ device, data.data(), static_cast<uint32_t>(data.size()), dawn::BufferUsageBit::CopySrc);
+
+ dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, 0, 0);
+ dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0});
+ dawn::Extent3D copySize = {kSize, kSize, 1};
+
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size);
+ dawn::CommandBuffer commands = encoder.Finish();
+ EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
+
+ std::vector<uint32_t> expectedWithZeros(bufferSize, 0);
+ EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
+}
+
DAWN_INSTANTIATE_TEST(
TextureZeroInitTest,
ForceWorkarounds(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),