Vulkan: use one barrier to transit states for all subresources

If a texture follow this rule:
* old usages of all subresources are the same
* new usages of all subresources are the same
Then we can use one barrier to transit states for all subresources.
We don't need to use one barrier per each subresource.

This patch adds a flag at front-end to capture many (but not all)
situations which follow the rule above, then we don't need to loop
over every subresource at Vulkan backend.

Bug: dawn:441
Change-Id: I6124ee2cb09c9142fefd8f057dc6d2659301e2d4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22702
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Yunchao He 2020-06-11 17:28:35 +00:00 committed by Commit Bot service account
parent 1a15991c84
commit 2532a718c5
5 changed files with 100 additions and 53 deletions

View File

@ -28,11 +28,25 @@ namespace dawn_native {
enum class PassType { Render, Compute }; enum class PassType { Render, Compute };
// Describe the usage of the whole texture and its subresources. // Describe the usage of the whole texture and its subresources.
// subresourceUsages vector is used to track every subresource's usage within a texture. // - subresourceUsages vector is used to track every subresource's usage within a texture.
// usage variable is used the track the whole texture even though it can be deduced from //
// subresources' usages. This is designed deliberately to track texture usage in a fast path. // - usage variable is used the track the whole texture even though it can be deduced from
// subresources' usages. This is designed deliberately to track texture usage in a fast path
// at frontend.
//
// - sameUsagesAcrossSubresources is used for optimization at backend. If the texture view
// we are using covers all subresources, then the texture's usages of all subresources are
// the same. Otherwise the texture's usages of all subresources are thought as different,
// although we can deliberately design some particular cases in which we have a few texture
// views and all of them have the same usages and they cover all subresources of the texture
// altogether.
// TODO(yunchao.he@intel.com): if sameUsagesAcrossSubresources is true, we don't need
// the vector to record every single subresource's Usages. The texture usage is enough. And we
// can decompress texture usage to a vector if necessary.
struct PassTextureUsage { struct PassTextureUsage {
wgpu::TextureUsage usage; wgpu::TextureUsage usage;
bool sameUsagesAcrossSubresources;
std::vector<wgpu::TextureUsage> subresourceUsages; std::vector<wgpu::TextureUsage> subresourceUsages;
}; };

View File

@ -40,11 +40,12 @@ namespace dawn_native {
// TODO (yunchao.he@intel.com): optimize this // TODO (yunchao.he@intel.com): optimize this
PassTextureUsage& textureUsage = mTextureUsages[texture]; PassTextureUsage& textureUsage = mTextureUsages[texture];
// Set usage for the whole texture // Set parameters for the whole texture
textureUsage.usage |= usage; textureUsage.usage |= usage;
uint32_t subresourceCount = texture->GetSubresourceCount();
textureUsage.sameUsagesAcrossSubresources = levelCount * layerCount == subresourceCount;
// Set usages for subresources // Set usages for subresources
uint32_t subresourceCount = texture->GetSubresourceCount();
if (!textureUsage.subresourceUsages.size()) { if (!textureUsage.subresourceUsages.size()) {
textureUsage.subresourceUsages = textureUsage.subresourceUsages =
std::vector<wgpu::TextureUsage>(subresourceCount, wgpu::TextureUsage::None); std::vector<wgpu::TextureUsage>(subresourceCount, wgpu::TextureUsage::None);
@ -63,6 +64,7 @@ namespace dawn_native {
const PassTextureUsage& textureUsage) { const PassTextureUsage& textureUsage) {
PassTextureUsage& passTextureUsage = mTextureUsages[texture]; PassTextureUsage& passTextureUsage = mTextureUsages[texture];
passTextureUsage.usage |= textureUsage.usage; passTextureUsage.usage |= textureUsage.usage;
passTextureUsage.sameUsagesAcrossSubresources &= textureUsage.sameUsagesAcrossSubresources;
uint32_t subresourceCount = texture->GetSubresourceCount(); uint32_t subresourceCount = texture->GetSubresourceCount();
ASSERT(textureUsage.subresourceUsages.size() == subresourceCount); ASSERT(textureUsage.subresourceUsages.size() == subresourceCount);

View File

@ -399,8 +399,7 @@ namespace dawn_native { namespace vulkan {
texture->GetNumMipLevels(), 0, texture->GetNumMipLevels(), 0,
texture->GetArrayLayers()); texture->GetArrayLayers());
} }
texture->TransitionUsageForPass(recordingContext, texture->TransitionUsageForPass(recordingContext, usages.textureUsages[i],
usages.textureUsages[i].subresourceUsages,
&imageBarriers, &srcStages, &dstStages); &imageBarriers, &srcStages, &dstStages);
} }

View File

@ -229,8 +229,10 @@ namespace dawn_native { namespace vulkan {
const VkImage& image, const VkImage& image,
wgpu::TextureUsage lastUsage, wgpu::TextureUsage lastUsage,
wgpu::TextureUsage usage, wgpu::TextureUsage usage,
uint32_t mipLevel, uint32_t baseMipLevel,
uint32_t arrayLayer) { uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) {
VkImageMemoryBarrier barrier; VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = nullptr; barrier.pNext = nullptr;
@ -240,10 +242,10 @@ namespace dawn_native { namespace vulkan {
barrier.newLayout = VulkanImageLayout(usage, format); barrier.newLayout = VulkanImageLayout(usage, format);
barrier.image = image; barrier.image = image;
barrier.subresourceRange.aspectMask = VulkanAspectMask(format); barrier.subresourceRange.aspectMask = VulkanAspectMask(format);
barrier.subresourceRange.baseMipLevel = mipLevel; barrier.subresourceRange.baseMipLevel = baseMipLevel;
barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = arrayLayer; barrier.subresourceRange.baseArrayLayer = baseArrayLayer;
barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.layerCount = layerCount;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@ -683,7 +685,7 @@ namespace dawn_native { namespace vulkan {
if (barriers->size() == transitionBarrierStart) { if (barriers->size() == transitionBarrierStart) {
barriers->push_back(BuildMemoryBarrier(GetFormat(), mHandle, barriers->push_back(BuildMemoryBarrier(GetFormat(), mHandle,
wgpu::TextureUsage::None, wgpu::TextureUsage::None,
wgpu::TextureUsage::None, 0, 0)); wgpu::TextureUsage::None, 0, 1, 0, 1));
} }
// Transfer texture from external queue to graphics queue // Transfer texture from external queue to graphics queue
@ -697,7 +699,7 @@ namespace dawn_native { namespace vulkan {
if (barriers->size() == transitionBarrierStart) { if (barriers->size() == transitionBarrierStart) {
barriers->push_back(BuildMemoryBarrier(GetFormat(), mHandle, barriers->push_back(BuildMemoryBarrier(GetFormat(), mHandle,
wgpu::TextureUsage::None, wgpu::TextureUsage::None,
wgpu::TextureUsage::None, 0, 0)); wgpu::TextureUsage::None, 0, 1, 0, 1));
} }
// Transfer texture from graphics queue to external queue // Transfer texture from graphics queue to external queue
@ -715,13 +717,22 @@ namespace dawn_native { namespace vulkan {
mWaitRequirements.clear(); mWaitRequirements.clear();
} }
bool Texture::CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage, wgpu::TextureUsage usage) {
// Reuse the texture directly and avoid encoding barriers when it isn't needed.
bool lastReadOnly = (lastUsage & kReadOnlyTextureUsages) == lastUsage;
if (lastReadOnly && lastUsage == usage && mLastExternalState == mExternalState) {
return true;
}
return false;
}
void Texture::TransitionFullUsage(CommandRecordingContext* recordingContext, void Texture::TransitionFullUsage(CommandRecordingContext* recordingContext,
wgpu::TextureUsage usage) { wgpu::TextureUsage usage) {
TransitionUsageNow(recordingContext, usage, 0, GetNumMipLevels(), 0, GetArrayLayers()); TransitionUsageNow(recordingContext, usage, 0, GetNumMipLevels(), 0, GetArrayLayers());
} }
void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext, void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext,
const std::vector<wgpu::TextureUsage>& subresourceUsages, const PassTextureUsage& textureUsages,
std::vector<VkImageMemoryBarrier>* imageBarriers, std::vector<VkImageMemoryBarrier>* imageBarriers,
VkPipelineStageFlags* srcStages, VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages) { VkPipelineStageFlags* dstStages) {
@ -731,32 +742,49 @@ namespace dawn_native { namespace vulkan {
wgpu::TextureUsage allUsages = wgpu::TextureUsage::None; wgpu::TextureUsage allUsages = wgpu::TextureUsage::None;
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None; wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
ASSERT(subresourceUsages.size() == GetSubresourceCount()); uint32_t subresourceCount = GetSubresourceCount();
ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
// This transitions assume it is a 2D texture // This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D); ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { // If new usages of all subresources are the same and old usages of all subresources are
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { // the same too, we can use one barrier to do state transition for all subresources.
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); // Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], textureUsages.usage)) {
return;
}
// Avoid encoding barriers when it isn't needed. imageBarriers->push_back(BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0],
if (subresourceUsages[index] == wgpu::TextureUsage::None) { textureUsages.usage, 0, GetNumMipLevels(),
continue; 0, GetArrayLayers()));
allLastUsages = mSubresourceLastUsages[0];
allUsages = textureUsages.usage;
for (uint32_t i = 0; i < subresourceCount; ++i) {
mSubresourceLastUsages[i] = textureUsages.usage;
}
} else {
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 (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) {
continue;
}
if (CanReuseWithoutBarrier(mSubresourceLastUsages[index],
textureUsages.subresourceUsages[index])) {
continue;
}
imageBarriers->push_back(BuildMemoryBarrier(
format, mHandle, mSubresourceLastUsages[index],
textureUsages.subresourceUsages[index], mipLevel, 1, arrayLayer, 1));
allLastUsages |= mSubresourceLastUsages[index];
allUsages |= textureUsages.subresourceUsages[index];
mSubresourceLastUsages[index] = textureUsages.subresourceUsages[index];
} }
bool lastReadOnly = (mLastSubresourceUsages[index] & kReadOnlyTextureUsages) ==
mLastSubresourceUsages[index];
if (lastReadOnly && mLastSubresourceUsages[index] == subresourceUsages[index] &&
mLastExternalState == mExternalState) {
continue;
}
imageBarriers->push_back(
BuildMemoryBarrier(format, mHandle, mLastSubresourceUsages[index],
subresourceUsages[index], mipLevel, arrayLayer));
allUsages |= subresourceUsages[index];
allLastUsages |= mLastSubresourceUsages[index];
mLastSubresourceUsages[index] = subresourceUsages[index];
} }
} }
@ -767,6 +795,7 @@ namespace dawn_native { namespace vulkan {
*srcStages |= VulkanPipelineStage(allLastUsages, format); *srcStages |= VulkanPipelineStage(allLastUsages, format);
*dstStages |= VulkanPipelineStage(allUsages, format); *dstStages |= VulkanPipelineStage(allUsages, format);
mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
} }
void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext, void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext,
@ -783,23 +812,18 @@ namespace dawn_native { namespace vulkan {
// This transitions assume it is a 2D texture // This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D); ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
for (uint32_t arrayLayer = 0; arrayLayer < layerCount; ++arrayLayer) { for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount; ++layer) {
for (uint32_t mipLevel = 0; mipLevel < levelCount; ++mipLevel) { for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
uint32_t index = uint32_t index = GetSubresourceIndex(level, layer);
GetSubresourceIndex(baseMipLevel + mipLevel, baseArrayLayer + arrayLayer);
wgpu::TextureUsage lastUsage = mLastSubresourceUsages[index];
// Avoid encoding barriers when it isn't needed. if (CanReuseWithoutBarrier(mSubresourceLastUsages[index], usage)) {
bool lastReadOnly = (lastUsage & kReadOnlyTextureUsages) == lastUsage; continue;
if (lastReadOnly && lastUsage == usage && mLastExternalState == mExternalState) {
return;
} }
barriers.push_back(BuildMemoryBarrier(format, mHandle, lastUsage, usage, barriers.push_back(BuildMemoryBarrier(
baseMipLevel + mipLevel, format, mHandle, mSubresourceLastUsages[index], usage, level, 1, layer, 1));
baseArrayLayer + arrayLayer)); allLastUsages |= mSubresourceLastUsages[index];
allLastUsages |= lastUsage; mSubresourceLastUsages[index] = usage;
mLastSubresourceUsages[index] = usage;
} }
} }
@ -812,6 +836,10 @@ namespace dawn_native { namespace vulkan {
ToBackend(GetDevice()) ToBackend(GetDevice())
->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0, ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
nullptr, 0, nullptr, barriers.size(), barriers.data()); nullptr, 0, nullptr, barriers.size(), barriers.data());
// TODO(yunchao.he@intel.com): do the optimization to combine all barriers into a single one
// for a texture if possible.
mSameLastUsagesAcrossSubresources = levelCount * layerCount == GetSubresourceCount();
} }
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext, MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,

View File

@ -18,6 +18,7 @@
#include "dawn_native/Texture.h" #include "dawn_native/Texture.h"
#include "common/vulkan_platform.h" #include "common/vulkan_platform.h"
#include "dawn_native/PassResourceUsage.h"
#include "dawn_native/ResourceMemoryAllocation.h" #include "dawn_native/ResourceMemoryAllocation.h"
#include "dawn_native/vulkan/ExternalHandle.h" #include "dawn_native/vulkan/ExternalHandle.h"
#include "dawn_native/vulkan/external_memory/MemoryService.h" #include "dawn_native/vulkan/external_memory/MemoryService.h"
@ -73,7 +74,7 @@ namespace dawn_native { namespace vulkan {
uint32_t baseArrayLayer, uint32_t baseArrayLayer,
uint32_t layerCount); uint32_t layerCount);
void TransitionUsageForPass(CommandRecordingContext* recordingContext, void TransitionUsageForPass(CommandRecordingContext* recordingContext,
const std::vector<wgpu::TextureUsage>& subresourceUsages, const PassTextureUsage& textureUsages,
std::vector<VkImageMemoryBarrier>* imageBarriers, std::vector<VkImageMemoryBarrier>* imageBarriers,
VkPipelineStageFlags* srcStages, VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages); VkPipelineStageFlags* dstStages);
@ -112,6 +113,7 @@ namespace dawn_native { namespace vulkan {
void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext, void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
std::vector<VkImageMemoryBarrier>* barriers, std::vector<VkImageMemoryBarrier>* barriers,
size_t transitionBarrierStart); size_t transitionBarrierStart);
bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage, wgpu::TextureUsage usage);
VkImage mHandle = VK_NULL_HANDLE; VkImage mHandle = VK_NULL_HANDLE;
ResourceMemoryAllocation mMemoryAllocation; ResourceMemoryAllocation mMemoryAllocation;
@ -130,9 +132,11 @@ namespace dawn_native { namespace vulkan {
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE; VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
std::vector<VkSemaphore> mWaitRequirements; std::vector<VkSemaphore> mWaitRequirements;
bool mSameLastUsagesAcrossSubresources = true;
// A usage of none will make sure the texture is transitioned before its first use as // A usage of none will make sure the texture is transitioned before its first use as
// required by the Vulkan spec. // required by the Vulkan spec.
std::vector<wgpu::TextureUsage> mLastSubresourceUsages = std::vector<wgpu::TextureUsage> mSubresourceLastUsages =
std::vector<wgpu::TextureUsage>(GetSubresourceCount(), wgpu::TextureUsage::None); std::vector<wgpu::TextureUsage>(GetSubresourceCount(), wgpu::TextureUsage::None);
}; };