D3D12: use one barrier to transit states for all subresources

If a texture's old states of all subresources are the same, and its
new states are the same too, then we can use one barrier to transit
states for all subresources. We don't need to use one barrier per
each subresource.

This change can reduce barriers we dispatched, in order to improve
performance for particular situations.

Bug: dawn:441

Change-Id: I9fe9dabda725e05d4ce5a8e69ee7b40e6724a22a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23145
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Yunchao He 2020-06-15 18:32:02 +00:00 committed by Commit Bot service account
parent 1c2a039e04
commit e5be22e949
5 changed files with 80 additions and 26 deletions

View File

@ -506,8 +506,8 @@ namespace dawn_native { namespace d3d12 {
for (size_t i = 0; i < usages.textures.size(); ++i) { for (size_t i = 0; i < usages.textures.size(); ++i) {
ToBackend(usages.textures[i]) ToBackend(usages.textures[i])
->TrackUsageAndGetResourceBarrierForPass( ->TrackUsageAndGetResourceBarrierForPass(commandContext, &barriers,
commandContext, &barriers, usages.textureUsages[i].subresourceUsages); usages.textureUsages[i]);
textureUsages |= usages.textureUsages[i].usage; textureUsages |= usages.textureUsages[i].usage;
} }

View File

@ -582,12 +582,14 @@ namespace dawn_native { namespace d3d12 {
} }
} }
void Texture::TransitionSingleSubresource(std::vector<D3D12_RESOURCE_BARRIER>* barriers, void Texture::TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
D3D12_RESOURCE_STATES newState,
uint32_t index, uint32_t index,
const Serial pendingCommandSerial) { D3D12_RESOURCE_STATES newState,
const Serial pendingCommandSerial,
bool allSubresources) {
StateAndDecay* state = &mSubresourceStateAndDecay[index]; StateAndDecay* state = &mSubresourceStateAndDecay[index];
// Avoid transitioning the texture when it isn't needed. // Reuse the subresource(s) directly and avoid transition when it isn't needed, and
// return false.
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
if (state->lastState == newState) { if (state->lastState == newState) {
return; return;
@ -647,7 +649,8 @@ namespace dawn_native { namespace d3d12 {
barrier.Transition.pResource = GetD3D12Resource(); barrier.Transition.pResource = GetD3D12Resource();
barrier.Transition.StateBefore = lastState; barrier.Transition.StateBefore = lastState;
barrier.Transition.StateAfter = newState; barrier.Transition.StateAfter = newState;
barrier.Transition.Subresource = index; barrier.Transition.Subresource =
allSubresources ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : index;
barriers->push_back(barrier); barriers->push_back(barrier);
// TODO(yunchao.he@intel.com): support subresource for depth/stencil. Depth stencil // TODO(yunchao.he@intel.com): support subresource for depth/stencil. Depth stencil
// texture has different plane slices. While the current implementation only has differernt // texture has different plane slices. While the current implementation only has differernt
@ -656,7 +659,8 @@ namespace dawn_native { namespace d3d12 {
// correctly. We force the transition to be the same for all planes to match what the // correctly. We force the transition to be the same for all planes to match what the
// frontend validation checks for. This hack might be incorrect for stencil-only texture // frontend validation checks for. This hack might be incorrect for stencil-only texture
// because we always set transition barrier for depth plane. // because we always set transition barrier for depth plane.
if (newState == D3D12_RESOURCE_STATE_DEPTH_WRITE && GetFormat().HasStencil()) { if (!allSubresources && newState == D3D12_RESOURCE_STATE_DEPTH_WRITE &&
GetFormat().HasStencil()) {
D3D12_RESOURCE_BARRIER barrierStencil = barrier; D3D12_RESOURCE_BARRIER barrierStencil = barrier;
barrierStencil.Transition.Subresource += GetArrayLayers() * GetNumMipLevels(); barrierStencil.Transition.Subresource += GetArrayLayers() * GetNumMipLevels();
barriers->push_back(barrierStencil); barriers->push_back(barrierStencil);
@ -682,38 +686,85 @@ namespace dawn_native { namespace d3d12 {
HandleTransitionSpecialCases(commandContext); HandleTransitionSpecialCases(commandContext);
const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
uint32_t subresourceCount = GetSubresourceCount();
// This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
// If the usages transitions can cover all subresources, and old usages of all subresources
// are the same, then we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
bool areAllSubresourcesCovered = range.levelCount * range.layerCount == subresourceCount;
if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) {
TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
// TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
// same states. We may need to retain mSubresourceStateAndDecay[0] only.
for (uint32_t i = 1; i < subresourceCount; ++i) {
mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
}
return;
}
for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) { for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) {
for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) { for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) {
uint32_t index = GetSubresourceIndex(range.baseMipLevel + mipLevel, uint32_t index = GetSubresourceIndex(range.baseMipLevel + mipLevel,
range.baseArrayLayer + arrayLayer); range.baseArrayLayer + arrayLayer);
TransitionSingleSubresource(barriers, newState, index, pendingCommandSerial); TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial,
false);
} }
} }
mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
} }
void Texture::TrackUsageAndGetResourceBarrierForPass( void Texture::TrackUsageAndGetResourceBarrierForPass(
CommandRecordingContext* commandContext, CommandRecordingContext* commandContext,
std::vector<D3D12_RESOURCE_BARRIER>* barriers, std::vector<D3D12_RESOURCE_BARRIER>* barriers,
const std::vector<wgpu::TextureUsage>& subresourceUsages) { const PassTextureUsage& textureUsages) {
HandleTransitionSpecialCases(commandContext); HandleTransitionSpecialCases(commandContext);
const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
uint32_t subresourceCount = GetSubresourceCount();
ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
// This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
// If new usages of all subresources are the same and old usages of all subresources are
// the same too, we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
D3D12_RESOURCE_STATES newState = D3D12TextureUsage(textureUsages.usage, GetFormat());
TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
// TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
// same states. We may need to retain mSubresourceStateAndDecay[0] only.
for (uint32_t i = 1; i < subresourceCount; ++i) {
mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
}
return;
}
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer);
// Skip if this subresource is not used during the current pass // Skip if this subresource is not used during the current pass
if (subresourceUsages[index] == wgpu::TextureUsage::None) { if (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) {
continue; continue;
} }
D3D12_RESOURCE_STATES newState = D3D12_RESOURCE_STATES newState =
D3D12TextureUsage(subresourceUsages[index], GetFormat()); D3D12TextureUsage(textureUsages.subresourceUsages[index], GetFormat());
TransitionSingleSubresource(barriers, newState, index, pendingCommandSerial); TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial,
false);
} }
} }
mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
} }
D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel, D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel,

View File

@ -19,6 +19,7 @@
#include "dawn_native/Texture.h" #include "dawn_native/Texture.h"
#include "dawn_native/DawnNative.h" #include "dawn_native/DawnNative.h"
#include "dawn_native/PassResourceUsage.h"
#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" #include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h"
#include "dawn_native/d3d12/d3d12_platform.h" #include "dawn_native/d3d12/d3d12_platform.h"
@ -57,10 +58,9 @@ namespace dawn_native { namespace d3d12 {
void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext, void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
const SubresourceRange& range); const SubresourceRange& range);
void TrackUsageAndGetResourceBarrierForPass( void TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext* commandContext,
CommandRecordingContext* commandContext,
std::vector<D3D12_RESOURCE_BARRIER>* barrier, std::vector<D3D12_RESOURCE_BARRIER>* barrier,
const std::vector<wgpu::TextureUsage>& subresourceUsages); const PassTextureUsage& textureUsages);
void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
wgpu::TextureUsage usage, wgpu::TextureUsage usage,
const SubresourceRange& range); const SubresourceRange& range);
@ -94,12 +94,15 @@ namespace dawn_native { namespace d3d12 {
D3D12_RESOURCE_STATES newState, D3D12_RESOURCE_STATES newState,
const SubresourceRange& range); const SubresourceRange& range);
void TransitionSingleSubresource(std::vector<D3D12_RESOURCE_BARRIER>* barriers, void TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
D3D12_RESOURCE_STATES subresourceNewState,
uint32_t index, uint32_t index,
const Serial pendingCommandSerial); D3D12_RESOURCE_STATES subresourceNewState,
const Serial pendingCommandSerial,
bool allSubresources);
void HandleTransitionSpecialCases(CommandRecordingContext* commandContext); void HandleTransitionSpecialCases(CommandRecordingContext* commandContext);
bool mSameLastUsagesAcrossSubresources = true;
struct StateAndDecay { struct StateAndDecay {
D3D12_RESOURCE_STATES lastState; D3D12_RESOURCE_STATES lastState;
Serial lastDecaySerial; Serial lastDecaySerial;

View File

@ -810,8 +810,8 @@ namespace dawn_native { namespace vulkan {
// are the same, then we can use one barrier to do state transition for all subresources. // are the same, then we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into // Note that if the texture has only one mip level and one array slice, it will fall into
// this category. // this category.
bool isAllSubresourcesCovered = range.levelCount * range.layerCount == subresourceCount; bool areAllSubresourcesCovered = range.levelCount * range.layerCount == subresourceCount;
if (mSameLastUsagesAcrossSubresources && isAllSubresourcesCovered) { if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) {
ASSERT(range.baseMipLevel == 0 && range.baseArrayLayer == 0); ASSERT(range.baseMipLevel == 0 && range.baseArrayLayer == 0);
if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], usage)) { if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], usage)) {
return; return;
@ -852,7 +852,7 @@ namespace dawn_native { namespace vulkan {
->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());
mSameLastUsagesAcrossSubresources = isAllSubresourcesCovered; mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
} }
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext, MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,