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:
parent
c8000bb02b
commit
182af0a4c2
|
@ -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);
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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,
|
}
|
||||||
D3D12_RESOURCE_STATES newState) {
|
|
||||||
|
void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
||||||
|
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);
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
TransitionUsageAndGetResourceBarrier(commandContext, &barriers, newState, baseMipLevel,
|
||||||
wgpu::TextureUsage usage) {
|
levelCount, baseArrayLayer, layerCount);
|
||||||
D3D12_RESOURCE_BARRIER barrier;
|
if (barriers.size()) {
|
||||||
|
commandContext->GetCommandList()->ResourceBarrier(barriers.size(), barriers.data());
|
||||||
if (TrackUsageAndGetResourceBarrier(commandContext, &barrier, usage)) {
|
|
||||||
commandContext->GetCommandList()->ResourceBarrier(1, &barrier);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
void Texture::TransitionSingleSubresource(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
||||||
D3D12_RESOURCE_STATES newState) {
|
D3D12_RESOURCE_STATES newState,
|
||||||
D3D12_RESOURCE_BARRIER barrier;
|
uint32_t index,
|
||||||
|
const Serial pendingCommandSerial) {
|
||||||
if (TrackUsageAndGetResourceBarrier(commandContext, &barrier, newState)) {
|
StateAndDecay* state = &mSubresourceStateAndDecay[index];
|
||||||
commandContext->GetCommandList()->ResourceBarrier(1, &barrier);
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_RESOURCE_BARRIER barrier;
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
|
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->isValidToDecay = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
void Texture::TransitionUsageAndGetResourceBarrier(
|
||||||
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
|
CommandRecordingContext* commandContext,
|
||||||
if (mLastState == newState) {
|
std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
||||||
return false;
|
D3D12_RESOURCE_STATES newState,
|
||||||
}
|
uint32_t baseMipLevel,
|
||||||
|
uint32_t levelCount,
|
||||||
|
uint32_t baseArrayLayer,
|
||||||
|
uint32_t layerCount) {
|
||||||
|
HandleTransitionSpecialCases(commandContext);
|
||||||
|
|
||||||
D3D12_RESOURCE_STATES lastState = mLastState;
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -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);
|
wgpu::TextureUsage usage,
|
||||||
|
uint32_t baseMipLevel,
|
||||||
|
uint32_t levelCount,
|
||||||
|
uint32_t baseArrayLayer,
|
||||||
|
uint32_t layerCount);
|
||||||
void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
||||||
D3D12_RESOURCE_STATES newState);
|
D3D12_RESOURCE_STATES newState,
|
||||||
|
uint32_t baseMipLevel,
|
||||||
|
uint32_t levelCount,
|
||||||
|
uint32_t baseArrayLayer,
|
||||||
|
uint32_t layerCount);
|
||||||
|
void TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
||||||
|
wgpu::TextureUsage usage);
|
||||||
|
void TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext,
|
||||||
|
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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
Loading…
Reference in New Issue