diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index 558e5760aa..e5c2106db8 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -150,11 +150,13 @@ namespace dawn_native { namespace vulkan { case wgpu::BindingType::ReadonlyStorageTexture: case wgpu::BindingType::WriteonlyStorageTexture: + // TODO (yunchao.he@intel.com): Do the transition for texture's + // subresource via its view. ToBackend( static_cast(mBindings[index][bindingIndex]) ->GetTexture()) - ->TransitionUsageNow(recordingContext, - wgpu::TextureUsage::Storage); + ->TransitionFullUsage(recordingContext, + wgpu::TextureUsage::Storage); break; case wgpu::BindingType::StorageTexture: @@ -386,7 +388,8 @@ namespace dawn_native { namespace vulkan { texture->GetNumMipLevels(), 0, texture->GetArrayLayers()); } - texture->TransitionUsageNow(recordingContext, usages.textureUsages[i].usage); + texture->TransitionUsageForPass(recordingContext, + usages.textureUsages[i].subresourceUsages); } }; const std::vector& passResourceUsages = GetResourceUsages().perPass; @@ -437,7 +440,9 @@ namespace dawn_native { namespace vulkan { ToBackend(src.buffer) ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); ToBackend(dst.texture) - ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, + subresource.mipLevel, 1, subresource.baseArrayLayer, + 1); VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle(); VkImage dstImage = ToBackend(dst.texture)->GetHandle(); @@ -464,7 +469,9 @@ namespace dawn_native { namespace vulkan { subresource.baseArrayLayer, 1); ToBackend(src.texture) - ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, + subresource.mipLevel, 1, subresource.baseArrayLayer, + 1); ToBackend(dst.buffer) ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); @@ -497,9 +504,11 @@ namespace dawn_native { namespace vulkan { } ToBackend(src.texture) - ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, + src.mipLevel, 1, src.arrayLayer, 1); ToBackend(dst.texture) - ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, + dst.mipLevel, 1, dst.arrayLayer, 1); // In some situations we cannot do texture-to-texture copies with vkCmdCopyImage // because as Vulkan SPEC always validates image copies with the virtual size of diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp index 0c6aad3e60..99c4775717 100644 --- a/src/dawn_native/vulkan/SwapChainVk.cpp +++ b/src/dawn_native/vulkan/SwapChainVk.cpp @@ -59,7 +59,7 @@ namespace dawn_native { namespace vulkan { // Perform the necessary pipeline barriers for the texture to be used with the usage // requested by the implementation. CommandRecordingContext* recordingContext = device->GetPendingRecordingContext(); - ToBackend(texture)->TransitionUsageNow(recordingContext, mTextureUsage); + ToBackend(texture)->TransitionFullUsage(recordingContext, mTextureUsage); DAWN_TRY(device->SubmitPendingCommands()); diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index bd036dd875..fa3c20b774 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -225,6 +225,31 @@ namespace dawn_native { namespace vulkan { return {extent.width, extent.height, extent.depth}; } + VkImageMemoryBarrier BuildMemoryBarrier(const Format& format, + const VkImage& image, + wgpu::TextureUsage lastUsage, + wgpu::TextureUsage usage, + uint32_t mipLevel, + uint32_t arrayLayer) { + VkImageMemoryBarrier barrier; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = VulkanAccessFlags(lastUsage, format); + barrier.dstAccessMask = VulkanAccessFlags(usage, format); + barrier.oldLayout = VulkanImageLayout(lastUsage, format); + barrier.newLayout = VulkanImageLayout(usage, format); + barrier.image = image; + barrier.subresourceRange.aspectMask = VulkanAspectMask(format); + barrier.subresourceRange.baseMipLevel = mipLevel; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = arrayLayer; + barrier.subresourceRange.layerCount = 1; + + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + return barrier; + } + } // namespace // Converts Dawn texture format to Vulkan formats. @@ -594,7 +619,7 @@ namespace dawn_native { namespace vulkan { // Release the texture mExternalState = ExternalState::PendingRelease; - TransitionUsageNow(device->GetPendingRecordingContext(), wgpu::TextureUsage::None); + TransitionFullUsage(device->GetPendingRecordingContext(), wgpu::TextureUsage::None); // Queue submit to signal we are done with the texture device->GetPendingRecordingContext()->signalSemaphores.push_back(mSignalSemaphore); @@ -644,65 +669,141 @@ namespace dawn_native { namespace vulkan { return VulkanAspectMask(GetFormat()); } - void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext, - wgpu::TextureUsage usage) { - // Avoid encoding barriers when it isn't needed. - bool lastReadOnly = (mLastUsage & kReadOnlyTextureUsages) == mLastUsage; - if (lastReadOnly && mLastUsage == usage && mLastExternalState == mExternalState) { - return; - } - - const Format& format = GetFormat(); - - VkPipelineStageFlags srcStages = VulkanPipelineStage(mLastUsage, format); - VkPipelineStageFlags dstStages = VulkanPipelineStage(usage, format); - - VkImageMemoryBarrier barrier; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.pNext = nullptr; - barrier.srcAccessMask = VulkanAccessFlags(mLastUsage, format); - barrier.dstAccessMask = VulkanAccessFlags(usage, format); - barrier.oldLayout = VulkanImageLayout(mLastUsage, format); - barrier.newLayout = VulkanImageLayout(usage, format); - barrier.image = mHandle; - // This transitions the whole resource but assumes it is a 2D texture - ASSERT(GetDimension() == wgpu::TextureDimension::e2D); - barrier.subresourceRange.aspectMask = VulkanAspectMask(format); - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = GetNumMipLevels(); - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = GetArrayLayers(); - - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext, + std::vector* barriers) { + ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1); + ASSERT(barriers->size() <= 1); if (mExternalState == ExternalState::PendingAcquire) { + if (!barriers->size()) { + barriers->push_back(BuildMemoryBarrier(GetFormat(), mHandle, + wgpu::TextureUsage::None, + wgpu::TextureUsage::None, 0, 0)); + } + // Transfer texture from external queue to graphics queue - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; - barrier.dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily(); + (*barriers)[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; + (*barriers)[0].dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily(); // Don't override oldLayout to leave it as VK_IMAGE_LAYOUT_UNDEFINED // TODO(http://crbug.com/dawn/200) mExternalState = ExternalState::Acquired; - } else if (mExternalState == ExternalState::PendingRelease) { + if (!barriers->size()) { + barriers->push_back(BuildMemoryBarrier(GetFormat(), mHandle, + wgpu::TextureUsage::None, + wgpu::TextureUsage::None, 0, 0)); + } + // Transfer texture from graphics queue to external queue - barrier.srcQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily(); - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + (*barriers)[0].srcQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily(); + (*barriers)[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; + (*barriers)[0].newLayout = VK_IMAGE_LAYOUT_GENERAL; mExternalState = ExternalState::Released; } - // Move required semaphores into waitSemaphores + mLastExternalState = mExternalState; + recordingContext->waitSemaphores.insert(recordingContext->waitSemaphores.end(), mWaitRequirements.begin(), mWaitRequirements.end()); mWaitRequirements.clear(); + } + void Texture::TransitionFullUsage(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage) { + TransitionUsageNow(recordingContext, usage, 0, GetNumMipLevels(), 0, GetArrayLayers()); + } + + void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext, + const std::vector& subresourceUsages) { + std::vector barriers; + const Format& format = GetFormat(); + + wgpu::TextureUsage allUsages = wgpu::TextureUsage::None; + wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None; + + ASSERT(subresourceUsages.size() == GetSubresourceCount()); + // This transitions assume it is a 2D texture + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); + + for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { + for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { + uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); + + // Avoid encoding barriers when it isn't needed. + if (subresourceUsages[index] == wgpu::TextureUsage::None) { + continue; + } + bool lastReadOnly = (mLastSubresourceUsages[index] & kReadOnlyTextureUsages) == + mLastSubresourceUsages[index]; + if (lastReadOnly && mLastSubresourceUsages[index] == subresourceUsages[index] && + mLastExternalState == mExternalState) { + continue; + } + + barriers.push_back( + BuildMemoryBarrier(format, mHandle, mLastSubresourceUsages[index], + subresourceUsages[index], mipLevel, arrayLayer)); + + allUsages |= subresourceUsages[index]; + allLastUsages |= mLastSubresourceUsages[index]; + mLastSubresourceUsages[index] = subresourceUsages[index]; + } + } + + if (mExternalState != ExternalState::InternalOnly) { + TweakTransitionForExternalUsage(recordingContext, &barriers); + } + + VkPipelineStageFlags srcStages = VulkanPipelineStage(allLastUsages, format); + VkPipelineStageFlags dstStages = VulkanPipelineStage(allUsages, format); ToBackend(GetDevice()) ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0, - nullptr, 0, nullptr, 1, &barrier); + nullptr, 0, nullptr, barriers.size(), barriers.data()); + } - mLastUsage = usage; - mLastExternalState = mExternalState; + void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + std::vector barriers; + const Format& format = GetFormat(); + + wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None; + + // This transitions assume it is a 2D texture + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); + + for (uint32_t arrayLayer = 0; arrayLayer < layerCount; ++arrayLayer) { + for (uint32_t mipLevel = 0; mipLevel < levelCount; ++mipLevel) { + uint32_t index = + GetSubresourceIndex(baseMipLevel + mipLevel, baseArrayLayer + arrayLayer); + wgpu::TextureUsage lastUsage = mLastSubresourceUsages[index]; + + // Avoid encoding barriers when it isn't needed. + bool lastReadOnly = (lastUsage & kReadOnlyTextureUsages) == lastUsage; + if (lastReadOnly && lastUsage == usage && mLastExternalState == mExternalState) { + return; + } + + barriers.push_back(BuildMemoryBarrier(format, mHandle, lastUsage, usage, + baseMipLevel + mipLevel, + baseArrayLayer + arrayLayer)); + allLastUsages |= lastUsage; + mLastSubresourceUsages[index] = usage; + } + } + + if (mExternalState != ExternalState::InternalOnly) { + TweakTransitionForExternalUsage(recordingContext, &barriers); + } + + VkPipelineStageFlags srcStages = VulkanPipelineStage(allLastUsages, format); + VkPipelineStageFlags dstStages = VulkanPipelineStage(usage, format); + ToBackend(GetDevice()) + ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0, + nullptr, 0, nullptr, barriers.size(), barriers.data()); } MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext, @@ -716,7 +817,8 @@ namespace dawn_native { namespace vulkan { uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f; - TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst); + TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, baseMipLevel, levelCount, + baseArrayLayer, layerCount); if (GetFormat().isRenderable) { VkImageSubresourceRange range = {}; range.aspectMask = GetVkAspectMask(); diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h index 42c6216a9d..1ac37c4992 100644 --- a/src/dawn_native/vulkan/TextureVk.h +++ b/src/dawn_native/vulkan/TextureVk.h @@ -63,8 +63,18 @@ namespace dawn_native { namespace vulkan { // Transitions the texture to be used as `usage`, recording any necessary barrier in // `commands`. // TODO(cwallez@chromium.org): coalesce barriers and do them early when possible. + void TransitionFullUsage(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage); + void TransitionUsageNow(CommandRecordingContext* recordingContext, - wgpu::TextureUsage usage); + wgpu::TextureUsage usage, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); + void TransitionUsageForPass(CommandRecordingContext* recordingContext, + const std::vector& subresourceUsages); + void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext, uint32_t baseMipLevel, uint32_t levelCount, @@ -96,6 +106,9 @@ namespace dawn_native { namespace vulkan { uint32_t layerCount, TextureBase::ClearValue); + void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext, + std::vector* barriers); + VkImage mHandle = VK_NULL_HANDLE; ResourceMemoryAllocation mMemoryAllocation; VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE; @@ -115,7 +128,8 @@ namespace dawn_native { namespace vulkan { // A usage of none will make sure the texture is transitioned before its first use as // required by the Vulkan spec. - wgpu::TextureUsage mLastUsage = wgpu::TextureUsage::None; + std::vector mLastSubresourceUsages = + std::vector(GetSubresourceCount(), wgpu::TextureUsage::None); }; class TextureView final : public TextureViewBase { diff --git a/src/tests/end2end/TextureSubresourceTests.cpp b/src/tests/end2end/TextureSubresourceTests.cpp index fb3b67dce9..03dd486566 100644 --- a/src/tests/end2end/TextureSubresourceTests.cpp +++ b/src/tests/end2end/TextureSubresourceTests.cpp @@ -188,7 +188,12 @@ TEST_P(TextureSubresourceTest, ArrayLayersTest) { EXPECT_TEXTURE_RGBA8_EQ(&bottomLeft, texture, 0, kSize - 1, 1, 1, 0, 1); } -// TODO (yunchao.he@intel.com): add tests for storage texture and sampler across miplevel or +// TODO (yunchao.he@intel.com): +// * add tests for storage texture and sampler across miplevel or // arraylayer dimensions in the same texture +// +// * add tests for copy operation upon texture subresource if needed +// +// * add tests for clear operation upon texture subresource if needed -DAWN_INSTANTIATE_TEST(TextureSubresourceTest, MetalBackend(), OpenGLBackend()); +DAWN_INSTANTIATE_TEST(TextureSubresourceTest, MetalBackend(), OpenGLBackend(), VulkanBackend());