From 28232ce9f5dc63b9efdf54a5aaf77f06e056090c Mon Sep 17 00:00:00 2001 From: Natasha Lee Date: Tue, 11 Jun 2019 18:11:05 +0000 Subject: [PATCH] Clear Vulkan Textures at first usage This prevents dirty textures to be used when memory is recycled while destroying/creating textures. If a texture is not cleared at load, it will be cleared to 0 before it is used. Bug: dawn:145 Change-Id: Ia3f02427478fb48649089829186ccb377caa1912 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/6960 Commit-Queue: Natasha Lee Reviewed-by: Kai Ninomiya --- BUILD.gn | 1 + src/common/Constants.h | 7 + src/dawn_native/CommandBuffer.cpp | 11 + src/dawn_native/CommandBuffer.h | 3 + src/dawn_native/Device.cpp | 7 +- src/dawn_native/Device.h | 1 + src/dawn_native/Instance.cpp | 7 +- src/dawn_native/Texture.cpp | 46 ++++ src/dawn_native/Texture.h | 14 + src/dawn_native/Toggles.h | 1 + src/dawn_native/d3d12/TextureD3D12.cpp | 4 - src/dawn_native/d3d12/TextureD3D12.h | 1 - src/dawn_native/vulkan/CommandBufferVk.cpp | 82 +++++- src/dawn_native/vulkan/TextureVk.cpp | 51 ++++ src/dawn_native/vulkan/TextureVk.h | 10 + src/tests/DawnTest.cpp | 8 +- src/tests/DawnTest.h | 4 +- .../end2end/NonzeroTextureCreationTests.cpp | 9 +- src/tests/end2end/TextureZeroInitTests.cpp | 260 ++++++++++++++++++ .../validation/ToggleValidationTests.cpp | 26 +- 20 files changed, 518 insertions(+), 35 deletions(-) create mode 100644 src/tests/end2end/TextureZeroInitTests.cpp diff --git a/BUILD.gn b/BUILD.gn index ab34144a5c..53fc2a6560 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -656,6 +656,7 @@ test("dawn_end2end_tests") { "src/tests/end2end/SamplerTests.cpp", "src/tests/end2end/ScissorTests.cpp", "src/tests/end2end/TextureViewTests.cpp", + "src/tests/end2end/TextureZeroInitTests.cpp", "src/tests/end2end/VertexFormatTests.cpp", "src/tests/end2end/VertexInputTests.cpp", "src/tests/end2end/ViewportOrientationTests.cpp", diff --git a/src/common/Constants.h b/src/common/Constants.h index c2fa5c2d6a..722244dd17 100644 --- a/src/common/Constants.h +++ b/src/common/Constants.h @@ -51,4 +51,11 @@ static constexpr uint32_t kVendorID_Intel = 0x8086; static constexpr uint32_t kVendorID_Nvidia = 0x10DE; static constexpr uint32_t kVendorID_Qualcomm = 0x5143; +// Max texture size constants +static constexpr uint32_t kMaxTextureSize = 8192u; +static constexpr uint32_t kMaxTexture2DArrayLayers = 256u; +static constexpr uint32_t kMaxTexture2DMipLevels = 14u; +static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureSize, + "kMaxTexture2DMipLevels and kMaxTextureSize size mismatch"); + #endif // COMMON_CONSTANTS_H_ diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp index 7c80939281..529fe653bf 100644 --- a/src/dawn_native/CommandBuffer.cpp +++ b/src/dawn_native/CommandBuffer.cpp @@ -15,6 +15,7 @@ #include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandEncoder.h" +#include "dawn_native/Texture.h" namespace dawn_native { @@ -35,4 +36,14 @@ namespace dawn_native { return mResourceUsages; } + bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, + const Extent3D copySize, + const uint32_t mipLevel) { + if (texture->GetSize().depth == copySize.depth && + (texture->GetSize().width >> mipLevel) == copySize.width && + (texture->GetSize().height >> mipLevel) == copySize.height) { + return true; + } + return false; + } } // namespace dawn_native diff --git a/src/dawn_native/CommandBuffer.h b/src/dawn_native/CommandBuffer.h index 0a0004a507..691290206c 100644 --- a/src/dawn_native/CommandBuffer.h +++ b/src/dawn_native/CommandBuffer.h @@ -35,6 +35,9 @@ namespace dawn_native { CommandBufferResourceUsage mResourceUsages; }; + bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, + const Extent3D copySize, + const uint32_t mipLevel); } // namespace dawn_native diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp index 6241fcac4f..26d0d44e28 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -62,6 +62,7 @@ namespace dawn_native { mCaches = std::make_unique(); mFenceSignalTracker = std::make_unique(this); mDynamicUploader = std::make_unique(this); + SetDefaultToggles(); } DeviceBase::~DeviceBase() { @@ -411,7 +412,6 @@ namespace dawn_native { mTogglesSet.SetToggle(toggle, true); } } - for (const char* toggleName : deviceDescriptor->forceDisabledToggles) { Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName); if (toggle != Toggle::InvalidEnum) { @@ -438,6 +438,11 @@ namespace dawn_native { return mTogglesSet.IsEnabled(toggle); } + void DeviceBase::SetDefaultToggles() { + // Sets the default-enabled toggles + mTogglesSet.SetToggle(Toggle::LazyClearResourceOnFirstUse, true); + } + // Implementation details of object creation MaybeError DeviceBase::CreateBindGroupInternal(BindGroupBase** result, diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h index c740e3cf5b..a79cdd9bba 100644 --- a/src/dawn_native/Device.h +++ b/src/dawn_native/Device.h @@ -193,6 +193,7 @@ namespace dawn_native { const TextureViewDescriptor* descriptor); void ConsumeError(ErrorData* error); + void SetDefaultToggles(); AdapterBase* mAdapter = nullptr; diff --git a/src/dawn_native/Instance.cpp b/src/dawn_native/Instance.cpp index 9458dbf382..b5e2f896d7 100644 --- a/src/dawn_native/Instance.cpp +++ b/src/dawn_native/Instance.cpp @@ -81,7 +81,12 @@ namespace dawn_native { "mipmap level and one array layer, and copy the result of MSAA resolve into the " "true resolve target. This workaround is enabled by default on the Metal drivers " "that have bugs when setting non-zero resolveLevel or resolveSlice.", - "https://bugs.chromium.org/p/dawn/issues/detail?id=56"}}}}; + "https://bugs.chromium.org/p/dawn/issues/detail?id=56"}}, + {Toggle::LazyClearResourceOnFirstUse, + {"lazy_clear_resource_on_first_use", + "Clears resource to zero on first usage. This initializes the resource " + "so that no dirty bits from recycled memory is present in the new resource.", + "https://bugs.chromium.org/p/dawn/issues/detail?id=145"}}}}; } // anonymous namespace diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp index c2f12ed166..d94a707e1e 100644 --- a/src/dawn_native/Texture.cpp +++ b/src/dawn_native/Texture.cpp @@ -17,6 +17,7 @@ #include #include "common/Assert.h" +#include "common/Constants.h" #include "common/Math.h" #include "dawn_native/Device.h" #include "dawn_native/ValidationUtils_autogen.h" @@ -399,6 +400,9 @@ namespace dawn_native { mSampleCount(descriptor->sampleCount), mUsage(descriptor->usage), mState(state) { + uint32_t subresourceCount = + GetSubresourceIndex(descriptor->mipLevelCount, descriptor->arrayLayerCount); + mIsSubresourceContentInitializedAtIndex = std::vector(subresourceCount, false); } TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag) @@ -444,6 +448,48 @@ namespace dawn_native { return mState; } + uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const { + ASSERT(arraySlice <= kMaxTexture2DArrayLayers); + ASSERT(mipLevel <= kMaxTexture2DMipLevels); + static_assert(kMaxTexture2DMipLevels <= + std::numeric_limits::max() / kMaxTexture2DArrayLayers, + "texture size overflows uint32_t"); + return GetNumMipLevels() * arraySlice + mipLevel; + } + + bool TextureBase::IsSubresourceContentInitialized(uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) const { + ASSERT(!IsError()); + for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) { + for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; + ++arrayLayer) { + uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer); + ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); + if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) { + return false; + } + } + } + return true; + } + + void TextureBase::SetIsSubresourceContentInitialized(uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + ASSERT(!IsError()); + for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) { + for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; + ++arrayLayer) { + uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer); + ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); + mIsSubresourceContentInitializedAtIndex[subresourceIndex] = true; + } + } + } + MaybeError TextureBase::ValidateCanUseInSubmitNow() const { ASSERT(!IsError()); if (mState == TextureState::Destroyed) { diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h index abfce114d9..dc3771eb0a 100644 --- a/src/dawn_native/Texture.h +++ b/src/dawn_native/Texture.h @@ -21,6 +21,8 @@ #include "dawn_native/dawn_platform.h" +#include + namespace dawn_native { MaybeError ValidateTextureDescriptor(DeviceBase* device, const TextureDescriptor* descriptor); MaybeError ValidateTextureViewDescriptor(const DeviceBase* device, @@ -59,6 +61,15 @@ namespace dawn_native { uint32_t GetSampleCount() const; dawn::TextureUsageBit GetUsage() const; TextureState GetTextureState() const; + uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const; + bool IsSubresourceContentInitialized(uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) const; + void SetIsSubresourceContentInitialized(uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); MaybeError ValidateCanUseInSubmitNow() const; @@ -85,6 +96,9 @@ namespace dawn_native { uint32_t mSampleCount; dawn::TextureUsageBit mUsage = dawn::TextureUsageBit::None; TextureState mState; + + // TODO(natlee@microsoft.com): Use a more optimized data structure to save space + std::vector mIsSubresourceContentInitializedAtIndex; }; class TextureViewBase : public ObjectBase { diff --git a/src/dawn_native/Toggles.h b/src/dawn_native/Toggles.h index 4ff15c1ff2..ac8b3dc1ab 100644 --- a/src/dawn_native/Toggles.h +++ b/src/dawn_native/Toggles.h @@ -25,6 +25,7 @@ namespace dawn_native { EmulateStoreAndMSAAResolve, NonzeroClearResourcesOnCreationForTesting, AlwaysResolveIntoZeroLevelAndLayer, + LazyClearResourceOnFirstUse, EnumCount, InvalidEnum = EnumCount, diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index f686ec7b17..a8153b55f9 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -266,10 +266,6 @@ namespace dawn_native { namespace d3d12 { return rtvDesc; } - uint32_t Texture::GetSubresourceIndex(uint32_t mipmapLevel, uint32_t arraySlice) const { - return GetNumMipLevels() * arraySlice + mipmapLevel; - } - TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) : TextureViewBase(texture, descriptor) { mSrvDesc.Format = D3D12TextureFormat(descriptor->format); diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h index 455a42e1e9..1c76f0dfd4 100644 --- a/src/dawn_native/d3d12/TextureD3D12.h +++ b/src/dawn_native/d3d12/TextureD3D12.h @@ -43,7 +43,6 @@ namespace dawn_native { namespace d3d12 { void TransitionUsageNow(ComPtr commandList, D3D12_RESOURCE_STATES newState); - uint32_t GetSubresourceIndex(uint32_t mipmapLevel, uint32_t arraySlice) const; D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t mipSlice, uint32_t arrayLayers, uint32_t baseArrayLayer) const; diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index 73c7a4f6b9..5f32da72ea 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -171,8 +171,16 @@ namespace dawn_native { namespace vulkan { for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { const auto& attachmentInfo = renderPass->colorAttachments[i]; bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr; - query.SetColor(i, attachmentInfo.view->GetFormat(), attachmentInfo.loadOp, - hasResolveTarget); + + dawn::LoadOp loadOp = attachmentInfo.loadOp; + if (loadOp == dawn::LoadOp::Load && attachmentInfo.view->GetTexture() && + !attachmentInfo.view->GetTexture()->IsSubresourceContentInitialized( + attachmentInfo.view->GetBaseMipLevel(), 1, + attachmentInfo.view->GetBaseArrayLayer(), 1)) { + loadOp = dawn::LoadOp::Clear; + } + + query.SetColor(i, attachmentInfo.view->GetFormat(), loadOp, hasResolveTarget); } if (renderPass->hasDepthStencilAttachment) { @@ -289,6 +297,11 @@ namespace dawn_native { namespace vulkan { } for (size_t i = 0; i < usages.textures.size(); ++i) { Texture* texture = ToBackend(usages.textures[i]); + + // TODO(natlee@microsoft.com): Update clearing here when subresource tracking is + // implemented + texture->EnsureSubresourceContentInitialized( + commands, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers()); texture->TransitionUsageNow(commands, usages.textureUsages[i]); } }; @@ -322,17 +335,27 @@ namespace dawn_native { namespace vulkan { auto& src = copy->source; auto& dst = copy->destination; + VkBufferImageCopy region = + ComputeBufferImageCopyRegion(src, dst, copy->copySize); + VkImageSubresourceLayers subresource = region.imageSubresource; + + if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, + subresource.mipLevel)) { + // Since texture has been overwritten, it has been "initialized" + dst.texture->SetIsSubresourceContentInitialized( + subresource.mipLevel, 1, subresource.baseArrayLayer, 1); + } else { + ToBackend(dst.texture) + ->EnsureSubresourceContentInitialized(commands, subresource.mipLevel, 1, + subresource.baseArrayLayer, 1); + } ToBackend(src.buffer) ->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferSrc); ToBackend(dst.texture) ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst); - VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle(); VkImage dstImage = ToBackend(dst.texture)->GetHandle(); - VkBufferImageCopy region = - ComputeBufferImageCopyRegion(src, dst, copy->copySize); - // The image is written to so the Dawn guarantees make sure it is in the // TRANSFER_DST_OPTIMAL layout device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage, @@ -345,6 +368,14 @@ namespace dawn_native { namespace vulkan { auto& src = copy->source; auto& dst = copy->destination; + VkBufferImageCopy region = + ComputeBufferImageCopyRegion(dst, src, copy->copySize); + VkImageSubresourceLayers subresource = region.imageSubresource; + + ToBackend(src.texture) + ->EnsureSubresourceContentInitialized(commands, subresource.mipLevel, 1, + subresource.baseArrayLayer, 1); + ToBackend(src.texture) ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferSrc); ToBackend(dst.buffer) @@ -352,10 +383,6 @@ namespace dawn_native { namespace vulkan { VkImage srcImage = ToBackend(src.texture)->GetHandle(); VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle(); - - VkBufferImageCopy region = - ComputeBufferImageCopyRegion(dst, src, copy->copySize); - // The Dawn TransferSrc usage is always mapped to GENERAL device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, dstBuffer, 1, ®ion); @@ -367,16 +394,31 @@ namespace dawn_native { namespace vulkan { TextureCopy& src = copy->source; TextureCopy& dst = copy->destination; + VkImageCopy region = ComputeImageCopyRegion(src, dst, copy->copySize); + VkImageSubresourceLayers dstSubresource = region.dstSubresource; + VkImageSubresourceLayers srcSubresource = region.srcSubresource; + + ToBackend(src.texture) + ->EnsureSubresourceContentInitialized(commands, srcSubresource.mipLevel, 1, + srcSubresource.baseArrayLayer, 1); + if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, + dstSubresource.mipLevel)) { + // Since destination texture has been overwritten, it has been "initialized" + dst.texture->SetIsSubresourceContentInitialized( + dstSubresource.mipLevel, 1, dstSubresource.baseArrayLayer, 1); + } else { + ToBackend(dst.texture) + ->EnsureSubresourceContentInitialized(commands, dstSubresource.mipLevel, + 1, dstSubresource.baseArrayLayer, + 1); + } ToBackend(src.texture) ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferSrc); ToBackend(dst.texture) ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst); - VkImage srcImage = ToBackend(src.texture)->GetHandle(); VkImage dstImage = ToBackend(dst.texture)->GetHandle(); - VkImageCopy region = ComputeImageCopyRegion(src, dst, copy->copySize); - // The dstImage is written to so the Dawn guarantees make sure it is in the // TRANSFER_DST_OPTIMAL layout device->fn.CmdCopyImage(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, dstImage, @@ -511,6 +553,20 @@ namespace dawn_native { namespace vulkan { case Command::EndRenderPass: { mCommands.NextCommand(); device->fn.CmdEndRenderPass(commands); + for (uint32_t i : IterateBitSet(renderPassCmd->colorAttachmentsSet)) { + auto& attachmentInfo = renderPassCmd->colorAttachments[i]; + TextureView* view = ToBackend(attachmentInfo.view.Get()); + switch (attachmentInfo.storeOp) { + case dawn::StoreOp::Store: { + attachmentInfo.view->GetTexture() + ->SetIsSubresourceContentInitialized( + view->GetBaseMipLevel(), view->GetLevelCount(), + view->GetBaseArrayLayer(), view->GetLayerCount()); + } break; + + default: { UNREACHABLE(); } break; + } + } return; } break; diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index cfc27a67d1..8fdc63d7ee 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -410,6 +410,57 @@ namespace dawn_native { namespace vulkan { mLastUsage = usage; } + void Texture::ClearTexture(VkCommandBuffer commands, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + if (GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { + VkImageSubresourceRange range = {}; + range.aspectMask = GetVkAspectMask(); + range.baseMipLevel = baseMipLevel; + range.levelCount = levelCount; + range.baseArrayLayer = baseArrayLayer; + range.layerCount = layerCount; + + TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst); + if (TextureFormatHasDepthOrStencil(GetFormat())) { + VkClearDepthStencilValue clear_color[1]; + clear_color[0].depth = 0.0f; + clear_color[0].stencil = 0u; + ToBackend(GetDevice()) + ->fn.CmdClearDepthStencilImage(commands, GetHandle(), + VulkanImageLayout(GetUsage(), GetFormat()), + 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; + ToBackend(GetDevice()) + ->fn.CmdClearColorImage(commands, GetHandle(), + VulkanImageLayout(GetUsage(), GetFormat()), clear_color, + 1, &range); + } + SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, + layerCount); + } + } + + void Texture::EnsureSubresourceContentInitialized(VkCommandBuffer commands, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, + layerCount)) { + // If subresource has not been initialized, clear it to black as it could contain dirty + // bits from recycled memory + ClearTexture(commands, baseMipLevel, levelCount, baseArrayLayer, layerCount); + } + } + // TODO(jiawei.shao@intel.com): create texture view by TextureViewDescriptor TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) : TextureViewBase(texture, descriptor) { diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h index 3909be0ff0..decc997568 100644 --- a/src/dawn_native/vulkan/TextureVk.h +++ b/src/dawn_native/vulkan/TextureVk.h @@ -39,9 +39,19 @@ namespace dawn_native { namespace vulkan { // `commands`. // TODO(cwallez@chromium.org): coalesce barriers and do them early when possible. void TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage); + void EnsureSubresourceContentInitialized(VkCommandBuffer commands, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); private: void DestroyImpl() override; + void ClearTexture(VkCommandBuffer commands, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); VkImage mHandle = VK_NULL_HANDLE; DeviceMemoryAllocation mMemoryAllocation; diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp index 55e182f9bd..c501cf0e2a 100644 --- a/src/tests/DawnTest.cpp +++ b/src/tests/DawnTest.cpp @@ -82,9 +82,11 @@ const DawnTestParam OpenGLBackend(dawn_native::BackendType::OpenGL); const DawnTestParam VulkanBackend(dawn_native::BackendType::Vulkan); DawnTestParam ForceWorkarounds(const DawnTestParam& originParam, - std::initializer_list forceEnabledWorkarounds) { + std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds) { DawnTestParam newTestParam = originParam; newTestParam.forceEnabledWorkarounds = forceEnabledWorkarounds; + newTestParam.forceDisabledWorkarounds = forceDisabledWorkarounds; return newTestParam; } @@ -310,8 +312,12 @@ void DawnTest::SetUp() { for (const char* forceEnabledWorkaround : GetParam().forceEnabledWorkarounds) { ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceEnabledWorkaround) != nullptr); } + for (const char* forceDisabledWorkaround : GetParam().forceDisabledWorkarounds) { + ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceDisabledWorkaround) != nullptr); + } dawn_native::DeviceDescriptor deviceDescriptor; deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds; + deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds; backendDevice = backendAdapter.CreateDevice(&deviceDescriptor); backendProcs = dawn_native::GetProcs(); diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index 4fe638107c..f2b8c1740f 100644 --- a/src/tests/DawnTest.h +++ b/src/tests/DawnTest.h @@ -70,6 +70,7 @@ struct DawnTestParam { dawn_native::BackendType backendType; std::vector forceEnabledWorkarounds; + std::vector forceDisabledWorkarounds; }; // Shorthands for backend types used in the DAWN_INSTANTIATE_TEST @@ -79,7 +80,8 @@ extern const DawnTestParam OpenGLBackend; extern const DawnTestParam VulkanBackend; DawnTestParam ForceWorkarounds(const DawnTestParam& originParam, - std::initializer_list forceEnabledWorkarounds); + std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds = {}); struct GLFWwindow; diff --git a/src/tests/end2end/NonzeroTextureCreationTests.cpp b/src/tests/end2end/NonzeroTextureCreationTests.cpp index 571b7f2e4c..531e5fcbe3 100644 --- a/src/tests/end2end/NonzeroTextureCreationTests.cpp +++ b/src/tests/end2end/NonzeroTextureCreationTests.cpp @@ -96,8 +96,11 @@ TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) { DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests, ForceWorkarounds(D3D12Backend, - {"nonzero_clear_resources_on_creation_for_testing"}), + {"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"}), ForceWorkarounds(OpenGLBackend, - {"nonzero_clear_resources_on_creation_for_testing"}), + {"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"}), ForceWorkarounds(VulkanBackend, - {"nonzero_clear_resources_on_creation_for_testing"})); + {"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"})); diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp new file mode 100644 index 0000000000..3419553f80 --- /dev/null +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -0,0 +1,260 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/DawnHelpers.h" + +class TextureZeroInitTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + } + dawn::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount, + uint32_t arrayLayerCount, + dawn::TextureUsageBit usage) { + dawn::TextureDescriptor descriptor; + descriptor.dimension = dawn::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = 1; + descriptor.arrayLayerCount = arrayLayerCount; + descriptor.sampleCount = 1; + descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = usage; + return descriptor; + } + dawn::TextureViewDescriptor CreateTextureViewDescriptor(uint32_t baseMipLevel, + uint32_t baseArrayLayer) { + dawn::TextureViewDescriptor descriptor; + descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.baseArrayLayer = baseArrayLayer; + descriptor.arrayLayerCount = 1; + descriptor.baseMipLevel = baseMipLevel; + descriptor.mipLevelCount = 1; + descriptor.dimension = dawn::TextureViewDimension::e2D; + return descriptor; + } + constexpr static uint32_t kSize = 128; +}; + +// This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage +TEST_P(TextureZeroInitTest, RecycleTextureMemoryClear) { + dawn::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 1, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc); + dawn::Texture texture = device.CreateTexture(&descriptor); + + // Texture's first usage is in EXPECT_PIXEL_RGBA8_EQ's call to CopyTextureToBuffer + RGBA8 filledWithZeros(0, 0, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(filledWithZeros, texture, 0, 0); +} + +// Test that non-zero mip level clears subresource to Zero after first use +// This goes through the BeginRenderPass's code path +TEST_P(TextureZeroInitTest, MipMapClearsToZero) { + dawn::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc); + dawn::Texture texture = device.CreateTexture(&descriptor); + + dawn::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(2, 0); + dawn::TextureView view = texture.CreateView(&viewDescriptor); + + utils::BasicRenderPass renderPass = + utils::BasicRenderPass(kSize, kSize, texture, dawn::TextureFormat::R8G8B8A8Unorm); + + renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->attachment = view; + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + { + // Texture's first usage is in BeginRenderPass's call to RecordRenderPass + dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.EndPass(); + } + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + uint32_t mipSize = kSize >> 2; + std::vector expected(mipSize * mipSize, {0, 0, 0, 0}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, mipSize, mipSize, 2, 0); +} + +// Test that non-zero array layers clears subresource to Zero after first use. +// This goes through the BeginRenderPass's code path +TEST_P(TextureZeroInitTest, ArrayLayerClearsToZero) { + dawn::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 4, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc); + dawn::Texture texture = device.CreateTexture(&descriptor); + + dawn::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(0, 2); + dawn::TextureView view = texture.CreateView(&viewDescriptor); + + utils::BasicRenderPass renderPass = + utils::BasicRenderPass(kSize, kSize, texture, dawn::TextureFormat::R8G8B8A8Unorm); + + renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->attachment = view; + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + { + dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.EndPass(); + } + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected(kSize * kSize, {0, 0, 0, 0}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, 0, 2); +} + +// This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed. +// TODO(natlee@microsoft.com): Add backdoor to dawn native to query the number of zero-inited +// subresources +TEST_P(TextureZeroInitTest, CopyBufferToTexture) { + dawn::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, + dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled | + dawn::TextureUsageBit::TransferSrc); + dawn::Texture texture = device.CreateTexture(&descriptor); + + std::vector data(4 * kSize * kSize, 100); + dawn::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), dawn::BufferUsageBit::TransferSrc); + dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 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.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected(kSize * kSize, {100, 100, 100, 100}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 0); +} + +// Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other +// half. +TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) { + dawn::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, + dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled | + dawn::TextureUsageBit::TransferSrc); + dawn::Texture texture = device.CreateTexture(&descriptor); + + std::vector data(4 * kSize * kSize, 100); + dawn::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), dawn::BufferUsageBit::TransferSrc); + dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); + dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); + dawn::Extent3D copySize = {kSize / 2, kSize, 1}; + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected100((kSize / 2) * kSize, {100, 100, 100, 100}); + std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); + // first half filled with 100, by the buffer data + EXPECT_TEXTURE_RGBA8_EQ(expected100.data(), texture, 0, 0, kSize / 2, kSize, 0, 0); + // second half should be cleared + EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, 0); +} + +// This tests CopyTextureToTexture fully overwrites copy so lazy init is not needed. +TEST_P(TextureZeroInitTest, CopyTextureToTexture) { + dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor( + 1, 1, dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::TransferSrc); + dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + dawn::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); + + dawn::TextureDescriptor dstDescriptor = CreateTextureDescriptor( + 1, 1, + dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferDst | + dawn::TextureUsageBit::TransferSrc); + dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); + + dawn::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, 0, 0, {0, 0, 0}); + + dawn::Extent3D copySize = {kSize, kSize, 1}; + + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected(kSize * kSize, {0, 0, 0, 0}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, 0, 0, kSize, kSize, 0, 0); +} + +// This Tests the CopyTextureToTexture's copy only to a subset of the subresource, lazy init is +// necessary to clear the other half. +TEST_P(TextureZeroInitTest, CopyTextureToTextureHalf) { + dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor( + 1, 1, + dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::TransferSrc | + dawn::TextureUsageBit::TransferDst); + dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + // fill srcTexture with 100 + { + std::vector data(4 * kSize * kSize, 100); + dawn::Buffer stagingBuffer = + utils::CreateBufferFromData(device, data.data(), static_cast(data.size()), + dawn::BufferUsageBit::TransferSrc); + dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); + dawn::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); + dawn::Extent3D copySize = {kSize, kSize, 1}; + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + dawn::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); + + dawn::TextureDescriptor dstDescriptor = CreateTextureDescriptor( + 1, 1, + dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferDst | + dawn::TextureUsageBit::TransferSrc); + dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); + + dawn::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, 0, 0, {0, 0, 0}); + dawn::Extent3D copySize = {kSize / 2, kSize, 1}; + + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedWithZeros((kSize / 2) * kSize, {0, 0, 0, 0}); + std::vector expectedWith100(kSize * kSize, {100, 100, 100, 100}); + + EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), dstTexture, 0, 0, kSize / 2, kSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), dstTexture, kSize / 2, 0, kSize / 2, kSize, 0, + 0); +} + +DAWN_INSTANTIATE_TEST(TextureZeroInitTest, + ForceWorkarounds(VulkanBackend, + {"nonzero_clear_resources_on_creation_for_testing"})); diff --git a/src/tests/unittests/validation/ToggleValidationTests.cpp b/src/tests/unittests/validation/ToggleValidationTests.cpp index 1dfd545d02..9ebedfb72f 100644 --- a/src/tests/unittests/validation/ToggleValidationTests.cpp +++ b/src/tests/unittests/validation/ToggleValidationTests.cpp @@ -41,12 +41,6 @@ TEST_F(ToggleValidationTest, QueryToggleInfo) { // Tests overriding toggles when creating a device works correctly. TEST_F(ToggleValidationTest, OverrideToggleUsage) { - // Dawn unittests use null Adapters, so there should be no toggles that are enabled by default - { - std::vector toggleNames = dawn_native::GetTogglesUsed(device.Get()); - ASSERT_EQ(0u, toggleNames.size()); - } - // Create device with a valid name of a toggle { const char* kValidToggleName = "emulate_store_and_msaa_resolve"; @@ -55,18 +49,30 @@ TEST_F(ToggleValidationTest, OverrideToggleUsage) { DawnDevice deviceWithToggle = adapter.CreateDevice(&descriptor); std::vector toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle); - ASSERT_EQ(1u, toggleNames.size()); - ASSERT_EQ(0, strcmp(kValidToggleName, toggleNames[0])); + bool validToggleExists = false; + for (const char* toggle : toggleNames) { + if (strcmp(toggle, kValidToggleName) == 0) { + validToggleExists = true; + } + } + ASSERT_EQ(validToggleExists, true); } // Create device with an invalid toggle name { + const char* kInvalidToggleName = "!@#$%^&*"; dawn_native::DeviceDescriptor descriptor; - descriptor.forceEnabledToggles.push_back("!@#$%^&*"); + descriptor.forceEnabledToggles.push_back(kInvalidToggleName); DawnDevice deviceWithToggle = adapter.CreateDevice(&descriptor); std::vector toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle); - ASSERT_EQ(0u, toggleNames.size()); + bool InvalidToggleExists = false; + for (const char* toggle : toggleNames) { + if (strcmp(toggle, kInvalidToggleName) == 0) { + InvalidToggleExists = true; + } + } + ASSERT_EQ(InvalidToggleExists, false); } } } // anonymous namespace