Implement texture subresource on D3D12

When we use a texture for different purpose, we need to add proper
barrier(s) in order to make it ready. Previously, the barrier is
done per entire texture. So it is invalid to sample/read/copy from
one subresource (say a mip/array slice) and render/write/copy to
another subresource of the same texture at the same time.

With this patch, barrier is set per each texture subresource. So it is
valid to use a subresource as source and use another subresource of the
same texture as destination at the same time.

However, planar slices like depth/stencil planes are not handled
gracefully. This is a TODO task. Another task is to combine barriers
into one if they can be combined. I will do this optimization in
another patch in near future.

Bug: dawn:157

Change-Id: I783a76cb88fcdffb60c307ddfb89d50f1583201a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22101
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
Yunchao He 2020-06-09 16:18:04 +00:00 committed by Commit Bot service account
parent c8000bb02b
commit 182af0a4c2
6 changed files with 257 additions and 144 deletions

View File

@ -169,20 +169,26 @@ namespace dawn_native { namespace d3d12 {
wgpu::BufferUsage::Storage); wgpu::BufferUsage::Storage);
break; break;
case wgpu::BindingType::ReadonlyStorageTexture: case wgpu::BindingType::ReadonlyStorageTexture: {
ToBackend(static_cast<TextureView*>(mBindings[index][binding]) TextureViewBase* view =
->GetTexture()) static_cast<TextureViewBase*>(mBindings[index][binding]);
->TrackUsageAndTransitionNow(commandContext, ToBackend(view->GetTexture())
kReadonlyStorageTexture); ->TrackUsageAndTransitionNow(
commandContext, kReadonlyStorageTexture,
view->GetBaseMipLevel(), view->GetLevelCount(),
view->GetBaseArrayLayer(), view->GetLayerCount());
break; break;
}
case wgpu::BindingType::WriteonlyStorageTexture: case wgpu::BindingType::WriteonlyStorageTexture: {
ToBackend(static_cast<TextureView*>(mBindings[index][binding]) TextureViewBase* view =
->GetTexture()) static_cast<TextureViewBase*>(mBindings[index][binding]);
->TrackUsageAndTransitionNow(commandContext, ToBackend(view->GetTexture())
wgpu::TextureUsage::Storage); ->TrackUsageAndTransitionNow(
commandContext, wgpu::TextureUsage::Storage,
view->GetBaseMipLevel(), view->GetLevelCount(),
view->GetBaseArrayLayer(), view->GetLayerCount());
break; break;
}
case wgpu::BindingType::StorageTexture: case wgpu::BindingType::StorageTexture:
// Not implemented. // Not implemented.
@ -434,15 +440,19 @@ namespace dawn_native { namespace d3d12 {
continue; continue;
} }
Texture* colorTexture = TextureViewBase* colorView = renderPass->colorAttachments[i].view.Get();
ToBackend(renderPass->colorAttachments[i].view->GetTexture()); Texture* colorTexture = ToBackend(colorView->GetTexture());
Texture* resolveTexture = ToBackend(resolveTarget->GetTexture()); Texture* resolveTexture = ToBackend(resolveTarget->GetTexture());
// Transition the usages of the color attachment and resolve target. // Transition the usages of the color attachment and resolve target.
colorTexture->TrackUsageAndTransitionNow(commandContext, colorTexture->TrackUsageAndTransitionNow(
D3D12_RESOURCE_STATE_RESOLVE_SOURCE); commandContext, D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
resolveTexture->TrackUsageAndTransitionNow(commandContext, colorView->GetBaseMipLevel(), colorView->GetLevelCount(),
D3D12_RESOURCE_STATE_RESOLVE_DEST); colorView->GetBaseArrayLayer(), colorView->GetLayerCount());
resolveTexture->TrackUsageAndTransitionNow(
commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST,
resolveTarget->GetBaseMipLevel(), resolveTarget->GetLevelCount(),
resolveTarget->GetBaseArrayLayer(), resolveTarget->GetLayerCount());
// Do MSAA resolve with ResolveSubResource(). // Do MSAA resolve with ResolveSubResource().
ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource(); ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource();
@ -510,12 +520,9 @@ namespace dawn_native { namespace d3d12 {
wgpu::TextureUsage textureUsages = wgpu::TextureUsage::None; wgpu::TextureUsage textureUsages = wgpu::TextureUsage::None;
for (size_t i = 0; i < usages.textures.size(); ++i) { for (size_t i = 0; i < usages.textures.size(); ++i) {
D3D12_RESOURCE_BARRIER barrier; ToBackend(usages.textures[i])
if (ToBackend(usages.textures[i]) ->TrackUsageAndGetResourceBarrierForPass(
->TrackUsageAndGetResourceBarrier(commandContext, &barrier, commandContext, &barriers, usages.textureUsages[i].subresourceUsages);
usages.textureUsages[i].usage)) {
barriers.push_back(barrier);
}
textureUsages |= usages.textureUsages[i].usage; textureUsages |= usages.textureUsages[i].usage;
} }
@ -593,8 +600,9 @@ namespace dawn_native { namespace d3d12 {
} }
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc); buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc);
texture->TrackUsageAndTransitionNow(commandContext, texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst,
wgpu::TextureUsage::CopyDst); copy->destination.mipLevel, 1,
copy->destination.arrayLayer, 1);
auto copySplit = ComputeTextureCopySplit( auto copySplit = ComputeTextureCopySplit(
copy->destination.origin, copy->copySize, texture->GetFormat(), copy->destination.origin, copy->copySize, texture->GetFormat(),
@ -629,8 +637,9 @@ namespace dawn_native { namespace d3d12 {
texture->EnsureSubresourceContentInitialized( texture->EnsureSubresourceContentInitialized(
commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1); commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1);
texture->TrackUsageAndTransitionNow(commandContext, texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc,
wgpu::TextureUsage::CopySrc); copy->source.mipLevel, 1,
copy->source.arrayLayer, 1);
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst); buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
TextureCopySplit copySplit = ComputeTextureCopySplit( TextureCopySplit copySplit = ComputeTextureCopySplit(
@ -680,9 +689,12 @@ namespace dawn_native { namespace d3d12 {
commandContext, copy->destination.mipLevel, 1, commandContext, copy->destination.mipLevel, 1,
copy->destination.arrayLayer, copy->copySize.depth); copy->destination.arrayLayer, copy->copySize.depth);
} }
source->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc); source->TrackUsageAndTransitionNow(
destination->TrackUsageAndTransitionNow(commandContext, commandContext, wgpu::TextureUsage::CopySrc, copy->source.mipLevel, 1,
wgpu::TextureUsage::CopyDst); copy->source.arrayLayer, copy->copySize.depth);
destination->TrackUsageAndTransitionNow(
commandContext, wgpu::TextureUsage::CopyDst, copy->destination.mipLevel, 1,
copy->destination.arrayLayer, copy->copySize.depth);
if (CanUseCopyResource(source, destination, copy->copySize)) { if (CanUseCopyResource(source, destination, copy->copySize)) {
commandList->CopyResource(destination->GetD3D12Resource(), commandList->CopyResource(destination->GetD3D12Resource(),
@ -870,7 +882,11 @@ namespace dawn_native { namespace d3d12 {
ToBackend(resolveDestinationView->GetTexture()); ToBackend(resolveDestinationView->GetTexture());
resolveDestinationTexture->TrackUsageAndTransitionNow( resolveDestinationTexture->TrackUsageAndTransitionNow(
commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST); commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST,
resolveDestinationView->GetBaseMipLevel(),
resolveDestinationView->GetLevelCount(),
resolveDestinationView->GetBaseArrayLayer(),
resolveDestinationView->GetLayerCount());
renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp, renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp,
view, resolveDestinationView); view, resolveDestinationView);

View File

@ -61,7 +61,7 @@ namespace dawn_native { namespace d3d12 {
// common state right before command list submission. TransitionUsageNow itself ensures // common state right before command list submission. TransitionUsageNow itself ensures
// no unnecessary transitions happen if the resources is already in the common state. // no unnecessary transitions happen if the resources is already in the common state.
for (Texture* texture : mSharedTextures) { for (Texture* texture : mSharedTextures) {
texture->TrackUsageAndTransitionNow(this, D3D12_RESOURCE_STATE_COMMON); texture->TrackAllUsageAndTransitionNow(this, D3D12_RESOURCE_STATE_COMMON);
} }
MaybeError error = MaybeError error =

View File

@ -55,7 +55,7 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
// Perform the necessary transition for the texture to be presented. // Perform the necessary transition for the texture to be presented.
ToBackend(texture)->TrackUsageAndTransitionNow(commandContext, mTextureUsage); ToBackend(texture)->TrackAllUsageAndTransitionNow(commandContext, mTextureUsage);
DAWN_TRY(device->ExecutePendingCommandContext()); DAWN_TRY(device->ExecutePendingCommandContext());

View File

@ -486,10 +486,17 @@ namespace dawn_native { namespace d3d12 {
return {}; return {};
} }
Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
: TextureBase(device, descriptor, state),
mSubresourceStateAndDecay(
GetSubresourceCount(),
{D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON, UINT64_MAX, false}) {
}
Texture::Texture(Device* device, Texture::Texture(Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> nativeTexture) ComPtr<ID3D12Resource> nativeTexture)
: TextureBase(device, descriptor, TextureState::OwnedExternal) { : Texture(device, descriptor, TextureState::OwnedExternal) {
AllocationInfo info; AllocationInfo info;
info.mMethod = AllocationMethod::kExternal; info.mMethod = AllocationMethod::kExternal;
// When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
@ -546,126 +553,185 @@ namespace dawn_native { namespace d3d12 {
} }
} }
// When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
// ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can wgpu::TextureUsage usage,
// cause subsequent errors. uint32_t mipLevel,
bool Texture::TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, uint32_t levelCount,
D3D12_RESOURCE_BARRIER* barrier, uint32_t arrayLayer,
wgpu::TextureUsage newUsage) { uint32_t layerCount) {
return TrackUsageAndGetResourceBarrier(commandContext, barrier, TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()), mipLevel,
D3D12TextureUsage(newUsage, GetFormat())); levelCount, arrayLayer, layerCount);
} }
// When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
// ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can wgpu::TextureUsage usage) {
// cause subsequent errors. TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()), 0,
bool Texture::TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, GetNumMipLevels(), 0, GetArrayLayers());
D3D12_RESOURCE_BARRIER* barrier, }
void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
D3D12_RESOURCE_STATES newState) { D3D12_RESOURCE_STATES newState) {
TrackUsageAndTransitionNow(commandContext, newState, 0, GetNumMipLevels(), 0,
GetArrayLayers());
}
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
D3D12_RESOURCE_STATES newState,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) {
if (mResourceAllocation.GetInfo().mMethod != AllocationMethod::kExternal) { if (mResourceAllocation.GetInfo().mMethod != AllocationMethod::kExternal) {
// Track the underlying heap to ensure residency. // Track the underlying heap to ensure residency.
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial()); commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial());
} }
// Return the resource barrier. std::vector<D3D12_RESOURCE_BARRIER> barriers;
return TransitionUsageAndGetResourceBarrier(commandContext, barrier, newState); barriers.reserve(levelCount * layerCount);
TransitionUsageAndGetResourceBarrier(commandContext, &barriers, newState, baseMipLevel,
levelCount, baseArrayLayer, layerCount);
if (barriers.size()) {
commandContext->GetCommandList()->ResourceBarrier(barriers.size(), barriers.data());
}
}
void Texture::TransitionSingleSubresource(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
D3D12_RESOURCE_STATES newState,
uint32_t index,
const Serial pendingCommandSerial) {
StateAndDecay* state = &mSubresourceStateAndDecay[index];
// Avoid transitioning the texture when it isn't needed.
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
if (state->lastState == newState) {
return;
}
D3D12_RESOURCE_STATES lastState = state->lastState;
// The COMMON state represents a state where no write operations can be pending, and
// where all pixels are uncompressed. This makes it possible to transition to and
// from some states without synchronization (i.e. without an explicit
// ResourceBarrier call). Textures can be implicitly promoted to 1) a single write
// state, or 2) multiple read states. Textures will implicitly decay to the COMMON
// state when all of the following are true: 1) the texture is accessed on a command
// list, 2) the ExecuteCommandLists call that uses that command list has ended, and
// 3) the texture was promoted implicitly to a read-only state and is still in that
// state.
// https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
// To track implicit decays, we must record the pending serial on which that
// transition will occur. When that texture is used again, the previously recorded
// serial must be compared to the last completed serial to determine if the texture
// has implicity decayed to the common state.
if (state->isValidToDecay && pendingCommandSerial > state->lastDecaySerial) {
lastState = D3D12_RESOURCE_STATE_COMMON;
}
// Update the tracked state.
state->lastState = newState;
// Destination states that qualify for an implicit promotion for a
// non-simultaneous-access texture: NON_PIXEL_SHADER_RESOURCE,
// PIXEL_SHADER_RESOURCE, COPY_SRC, COPY_DEST.
{
static constexpr D3D12_RESOURCE_STATES kD3D12PromotableReadOnlyStates =
D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
if (lastState == D3D12_RESOURCE_STATE_COMMON) {
if (newState == (newState & kD3D12PromotableReadOnlyStates)) {
// Implicit texture state decays can only occur when the texture was implicitly
// transitioned to a read-only state. isValidToDecay is needed to differentiate
// between resources that were implictly or explicitly transitioned to a
// read-only state.
state->isValidToDecay = true;
state->lastDecaySerial = pendingCommandSerial;
return;
} else if (newState == D3D12_RESOURCE_STATE_COPY_DEST) {
state->isValidToDecay = false;
return;
}
}
} }
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
wgpu::TextureUsage usage) {
D3D12_RESOURCE_BARRIER barrier; D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
if (TrackUsageAndGetResourceBarrier(commandContext, &barrier, usage)) { barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
commandContext->GetCommandList()->ResourceBarrier(1, &barrier); barrier.Transition.pResource = GetD3D12Resource();
} barrier.Transition.StateBefore = lastState;
barrier.Transition.StateAfter = newState;
barrier.Transition.Subresource = index;
barriers->push_back(barrier);
// TODO(yunchao.he@intel.com): support subresource for depth/stencil. Depth stencil
// texture has different plane slices. While the current implementation only has differernt
// mip slices and array slices for subresources.
// This is a hack because Dawn doesn't handle subresource of multiplanar resources
// 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
// because we always set transition barrier for depth plane.
if (newState == D3D12_RESOURCE_STATE_DEPTH_WRITE && GetFormat().HasStencil()) {
D3D12_RESOURCE_BARRIER barrierStencil = barrier;
barrierStencil.Transition.Subresource += GetArrayLayers() * GetNumMipLevels();
barriers->push_back(barrierStencil);
} }
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, state->isValidToDecay = false;
D3D12_RESOURCE_STATES newState) {
D3D12_RESOURCE_BARRIER barrier;
if (TrackUsageAndGetResourceBarrier(commandContext, &barrier, newState)) {
commandContext->GetCommandList()->ResourceBarrier(1, &barrier);
}
} }
// When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a void Texture::HandleTransitionSpecialCases(CommandRecordingContext* commandContext) {
// ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
// cause subsequent errors.
bool Texture::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
D3D12_RESOURCE_BARRIER* barrier,
D3D12_RESOURCE_STATES newState) {
// Textures with keyed mutexes can be written from other graphics queues. Hence, they // Textures with keyed mutexes can be written from other graphics queues. Hence, they
// must be acquired before command list submission to ensure work from the other queues // must be acquired before command list submission to ensure work from the other queues
// has finished. See Device::ExecuteCommandContext. // has finished. See Device::ExecuteCommandContext.
if (mDxgiKeyedMutex != nullptr) { if (mDxgiKeyedMutex != nullptr) {
commandContext->AddToSharedTextureList(this); commandContext->AddToSharedTextureList(this);
} }
// Avoid transitioning the texture when it isn't needed.
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
if (mLastState == newState) {
return false;
} }
D3D12_RESOURCE_STATES lastState = mLastState; void Texture::TransitionUsageAndGetResourceBarrier(
CommandRecordingContext* commandContext,
std::vector<D3D12_RESOURCE_BARRIER>* barriers,
D3D12_RESOURCE_STATES newState,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) {
HandleTransitionSpecialCases(commandContext);
// The COMMON state represents a state where no write operations can be pending, and where
// all pixels are uncompressed. This makes it possible to transition to and from some states
// without synchronization (i.e. without an explicit ResourceBarrier call). Textures can be
// implicitly promoted to 1) a single write state, or 2) multiple read states. Textures will
// implicitly decay to the COMMON state when all of the following are true: 1) the texture
// is accessed on a command list, 2) the ExecuteCommandLists call that uses that command
// list has ended, and 3) the texture was promoted implicitly to a read-only state and is
// still in that state.
// https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
// To track implicit decays, we must record the pending serial on which that transition will
// occur. When that texture is used again, the previously recorded serial must be compared
// to the last completed serial to determine if the texture has implicity decayed to the
// common state.
const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
if (mValidToDecay && pendingCommandSerial > mLastUsedSerial) { for (uint32_t arrayLayer = 0; arrayLayer < layerCount; ++arrayLayer) {
lastState = D3D12_RESOURCE_STATE_COMMON; for (uint32_t mipLevel = 0; mipLevel < levelCount; ++mipLevel) {
} uint32_t index =
GetSubresourceIndex(baseMipLevel + mipLevel, baseArrayLayer + arrayLayer);
// Update the tracked state. TransitionSingleSubresource(barriers, newState, index, pendingCommandSerial);
mLastState = newState;
// Destination states that qualify for an implicit promotion for a non-simultaneous-access
// texture: NON_PIXEL_SHADER_RESOURCE, PIXEL_SHADER_RESOURCE, COPY_SRC, COPY_DEST.
{
static constexpr D3D12_RESOURCE_STATES kD3D12TextureReadOnlyStates =
D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
if (lastState == D3D12_RESOURCE_STATE_COMMON) {
if (newState == (newState & kD3D12TextureReadOnlyStates)) {
// Implicit texture state decays can only occur when the texture was implicitly
// transitioned to a read-only state. mValidToDecay is needed to differentiate
// between resources that were implictly or explicitly transitioned to a
// read-only state.
mValidToDecay = true;
mLastUsedSerial = pendingCommandSerial;
return false;
} else if (newState == D3D12_RESOURCE_STATE_COPY_DEST) {
mValidToDecay = false;
return false;
} }
} }
} }
barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; void Texture::TrackUsageAndGetResourceBarrierForPass(
barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; CommandRecordingContext* commandContext,
barrier->Transition.pResource = GetD3D12Resource(); std::vector<D3D12_RESOURCE_BARRIER>* barriers,
barrier->Transition.StateBefore = lastState; const std::vector<wgpu::TextureUsage>& subresourceUsages) {
barrier->Transition.StateAfter = newState; HandleTransitionSpecialCases(commandContext);
barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
mValidToDecay = false; const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer);
return true; // Skip if this subresource is not used during the current pass
if (subresourceUsages[index] == wgpu::TextureUsage::None) {
continue;
}
D3D12_RESOURCE_STATES newState =
D3D12TextureUsage(subresourceUsages[index], GetFormat());
TransitionSingleSubresource(barriers, newState, index, pendingCommandSerial);
}
}
} }
D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel, D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel,
@ -741,7 +807,8 @@ namespace dawn_native { namespace d3d12 {
if (GetFormat().isRenderable) { if (GetFormat().isRenderable) {
if (GetFormat().HasDepthOrStencil()) { if (GetFormat().HasDepthOrStencil()) {
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE); TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE,
baseMipLevel, levelCount, baseArrayLayer, layerCount);
D3D12_CLEAR_FLAGS clearFlags = {}; D3D12_CLEAR_FLAGS clearFlags = {};
@ -775,7 +842,8 @@ namespace dawn_native { namespace d3d12 {
} }
} }
} else { } else {
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET); TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET,
baseMipLevel, levelCount, baseArrayLayer, layerCount);
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor, const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
fClearColor}; fClearColor};
@ -818,7 +886,8 @@ namespace dawn_native { namespace d3d12 {
uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize); memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST); TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, baseMipLevel,
levelCount, baseArrayLayer, layerCount);
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) { for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
// compute d3d12 texture copy locations for texture and buffer // compute d3d12 texture copy locations for texture and buffer

View File

@ -60,15 +60,29 @@ namespace dawn_native { namespace d3d12 {
uint32_t baseArrayLayer, uint32_t baseArrayLayer,
uint32_t layerCount); uint32_t layerCount);
bool TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, void TrackUsageAndGetResourceBarrierForPass(
D3D12_RESOURCE_BARRIER* barrier, CommandRecordingContext* commandContext,
wgpu::TextureUsage newUsage); std::vector<D3D12_RESOURCE_BARRIER>* barrier,
const std::vector<wgpu::TextureUsage>& subresourceUsages);
void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
wgpu::TextureUsage usage,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount);
void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
D3D12_RESOURCE_STATES newState,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount);
void TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
wgpu::TextureUsage usage); wgpu::TextureUsage usage);
void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, void TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
D3D12_RESOURCE_STATES newState); D3D12_RESOURCE_STATES newState);
private: private:
Texture(Device* device, const TextureDescriptor* descriptor, TextureState state);
~Texture() override; ~Texture() override;
using TextureBase::TextureBase; using TextureBase::TextureBase;
@ -89,18 +103,28 @@ namespace dawn_native { namespace d3d12 {
UINT16 GetDepthOrArraySize(); UINT16 GetDepthOrArraySize();
bool TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, void TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
D3D12_RESOURCE_BARRIER* barrier, std::vector<D3D12_RESOURCE_BARRIER>* barrier,
D3D12_RESOURCE_STATES newState); D3D12_RESOURCE_STATES newState,
bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, uint32_t baseMipLevel,
D3D12_RESOURCE_BARRIER* barrier, uint32_t levelCount,
D3D12_RESOURCE_STATES newState); uint32_t baseArrayLayer,
uint32_t layerCount);
void TransitionSingleSubresource(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
D3D12_RESOURCE_STATES subresourceNewState,
uint32_t index,
const Serial pendingCommandSerial);
void HandleTransitionSpecialCases(CommandRecordingContext* commandContext);
struct StateAndDecay {
D3D12_RESOURCE_STATES lastState;
Serial lastDecaySerial;
bool isValidToDecay;
};
std::vector<StateAndDecay> mSubresourceStateAndDecay;
ResourceHeapAllocation mResourceAllocation; ResourceHeapAllocation mResourceAllocation;
D3D12_RESOURCE_STATES mLastState = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON;
Serial mLastUsedSerial = UINT64_MAX;
bool mValidToDecay = false;
bool mSwapChainTexture = false; bool mSwapChainTexture = false;
Serial mAcquireMutexKey = 0; Serial mAcquireMutexKey = 0;

View File

@ -196,4 +196,8 @@ TEST_P(TextureSubresourceTest, ArrayLayersTest) {
// //
// * add tests for clear operation upon texture subresource if needed // * add tests for clear operation upon texture subresource if needed
DAWN_INSTANTIATE_TEST(TextureSubresourceTest, MetalBackend(), OpenGLBackend(), VulkanBackend()); DAWN_INSTANTIATE_TEST(TextureSubresourceTest,
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),
VulkanBackend());