Implement Implicit State Decays For D3D12
When resources are accessed on a command list, the resource will implicitly decay in some scenarios. This commit tracks when the decay occurs to more frequently enable implicit promotion. Bug: dawn:167 Change-Id: Ide4c06454efe136baee0d39a3437a407a613bcc7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8243 Commit-Queue: Brandon Jones <brandon1.jones@intel.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
4b24886afe
commit
e896a0da66
|
@ -113,8 +113,20 @@ namespace dawn_native { namespace d3d12 {
|
|||
DestroyInternal();
|
||||
}
|
||||
|
||||
bool Buffer::CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::BufferUsageBit newUsage) const {
|
||||
uint32_t Buffer::GetD3D12Size() const {
|
||||
// TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
|
||||
return Align(GetSize(), 256);
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
|
||||
return mResource;
|
||||
}
|
||||
|
||||
// When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
|
||||
// ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
|
||||
// cause subsequent errors.
|
||||
bool Buffer::TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::BufferUsageBit newUsage) {
|
||||
// Resources in upload and readback heaps must be kept in the COPY_SOURCE/DEST state
|
||||
if (mFixedResourceState) {
|
||||
ASSERT(mLastUsage == newUsage);
|
||||
|
@ -129,29 +141,36 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage);
|
||||
D3D12_RESOURCE_STATES newState = D3D12BufferUsage(newUsage);
|
||||
mLastUsage = newUsage;
|
||||
|
||||
// The COMMON state represents a state where no write operations can be pending, which makes
|
||||
// it possible to transition to some states without synchronizaton (i.e. without an explicit
|
||||
// ResourceBarrier call). This can be to 1) a single write state, or 2) multiple read
|
||||
// states.
|
||||
//
|
||||
// Destination states used in Dawn that qualify for implicit transition for a buffer:
|
||||
// COPY_SOURCE, VERTEX_AND_CONSTANT_BUFFER, INDEX_BUFFER, COPY_DEST, UNORDERED_ACCESS
|
||||
// it possible to transition to and from some states without synchronizaton (i.e. without an
|
||||
// explicit ResourceBarrier call). A buffer can be implicitly promoted to 1) a single write
|
||||
// state, or 2) multiple read states. A buffer that is accessed within a command list will
|
||||
// always implicitly decay to the COMMON state after the call to ExecuteCommandLists
|
||||
// completes - this is because all buffer writes are guaranteed to be completed before the
|
||||
// next ExecuteCommandLists call executes.
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
|
||||
{
|
||||
static constexpr D3D12_RESOURCE_STATES kD3D12BufferReadOnlyStates =
|
||||
D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER |
|
||||
D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
||||
|
||||
// To track implicit decays, we must record the pending serial on which a transition will
|
||||
// occur. When that buffer is used again, the previously recorded serial must be compared to
|
||||
// the last completed serial to determine if the buffer has implicity decayed to the common
|
||||
// state.
|
||||
const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial();
|
||||
if (pendingCommandSerial > mLastUsedSerial) {
|
||||
lastState = D3D12_RESOURCE_STATE_COMMON;
|
||||
mLastUsedSerial = pendingCommandSerial;
|
||||
}
|
||||
|
||||
// All possible buffer states used by Dawn are eligible for implicit promotion from COMMON.
|
||||
// These are: COPY_SOURCE, VERTEX_AND_COPY_BUFFER, INDEX_BUFFER, COPY_DEST,
|
||||
// UNORDERED_ACCESS, and INDIRECT_ARGUMENT. Note that for implicit promotion, the
|
||||
// destination state cannot be 1) more than one write state, or 2) both a read and write
|
||||
// state. This goes unchecked here because it should not be allowed through render/compute
|
||||
// pass validation.
|
||||
if (lastState == D3D12_RESOURCE_STATE_COMMON) {
|
||||
bool singleWriteState = ((newState == D3D12_RESOURCE_STATE_COPY_DEST) ||
|
||||
(newState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
|
||||
bool readOnlyState = newState == (newState & kD3D12BufferReadOnlyStates);
|
||||
if (singleWriteState ^ readOnlyState) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
|
@ -163,28 +182,13 @@ namespace dawn_native { namespace d3d12 {
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32_t Buffer::GetD3D12Size() const {
|
||||
// TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
|
||||
return Align(GetSize(), 256);
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
|
||||
return mResource;
|
||||
}
|
||||
|
||||
void Buffer::SetUsage(dawn::BufferUsageBit newUsage) {
|
||||
mLastUsage = newUsage;
|
||||
}
|
||||
|
||||
void Buffer::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||
dawn::BufferUsageBit usage) {
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
|
||||
if (CreateD3D12ResourceBarrierIfNeeded(&barrier, usage)) {
|
||||
if (TransitionUsageAndGetResourceBarrier(&barrier, usage)) {
|
||||
commandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
mLastUsage = usage;
|
||||
}
|
||||
|
||||
D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const {
|
||||
|
|
|
@ -29,13 +29,12 @@ namespace dawn_native { namespace d3d12 {
|
|||
Buffer(Device* device, const BufferDescriptor* descriptor);
|
||||
~Buffer();
|
||||
|
||||
bool CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::BufferUsageBit newUsage) const;
|
||||
uint32_t GetD3D12Size() const;
|
||||
ComPtr<ID3D12Resource> GetD3D12Resource();
|
||||
D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
|
||||
void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite);
|
||||
void SetUsage(dawn::BufferUsageBit newUsage);
|
||||
bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::BufferUsageBit newUsage);
|
||||
void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||
dawn::BufferUsageBit usage);
|
||||
|
||||
|
@ -52,6 +51,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
ComPtr<ID3D12Resource> mResource;
|
||||
bool mFixedResourceState = false;
|
||||
dawn::BufferUsageBit mLastUsage = dawn::BufferUsageBit::None;
|
||||
Serial mLastUsedSerial = UINT64_MAX;
|
||||
D3D12_RANGE mWrittenMappedRange;
|
||||
};
|
||||
|
||||
|
|
|
@ -435,10 +435,9 @@ namespace dawn_native { namespace d3d12 {
|
|||
for (size_t i = 0; i < usages.buffers.size(); ++i) {
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
if (ToBackend(usages.buffers[i])
|
||||
->CreateD3D12ResourceBarrierIfNeeded(&barrier, usages.bufferUsages[i])) {
|
||||
->TransitionUsageAndGetResourceBarrier(&barrier, usages.bufferUsages[i])) {
|
||||
barriers.push_back(barrier);
|
||||
}
|
||||
ToBackend(usages.buffers[i])->SetUsage(usages.bufferUsages[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < usages.textures.size(); ++i) {
|
||||
|
@ -452,10 +451,9 @@ namespace dawn_native { namespace d3d12 {
|
|||
for (size_t i = 0; i < usages.textures.size(); ++i) {
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
if (ToBackend(usages.textures[i])
|
||||
->CreateD3D12ResourceBarrierIfNeeded(&barrier, usages.textureUsages[i])) {
|
||||
->TransitionUsageAndGetResourceBarrier(&barrier, usages.textureUsages[i])) {
|
||||
barriers.push_back(barrier);
|
||||
}
|
||||
ToBackend(usages.textures[i])->SetUsage(usages.textureUsages[i]);
|
||||
}
|
||||
|
||||
if (barriers.size()) {
|
||||
|
|
|
@ -281,52 +281,6 @@ namespace dawn_native { namespace d3d12 {
|
|||
DestroyInternal();
|
||||
}
|
||||
|
||||
bool Texture::CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::TextureUsageBit newUsage) const {
|
||||
return CreateD3D12ResourceBarrierIfNeeded(barrier,
|
||||
D3D12TextureUsage(newUsage, GetFormat()));
|
||||
}
|
||||
|
||||
bool Texture::CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier,
|
||||
D3D12_RESOURCE_STATES newState) const {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 some states without
|
||||
// synchronization (i.e. without an explicit ResourceBarrier call). This can be to 1) a
|
||||
// single write state, or 2) multiple read states.
|
||||
//
|
||||
// Destination states that qualify for an implicit transition for a non-simulataneous-access
|
||||
// texture: NON_PIXEL_SHADER_RESOURCE, PIXEL_SHADER_RESOURCE, COPY_SRC, COPY_DEST
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
|
||||
{
|
||||
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 (mLastState == D3D12_RESOURCE_STATE_COMMON) {
|
||||
bool singleWriteState = (newState == D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
bool readOnlyState = newState == (newState & kD3D12TextureReadOnlyStates);
|
||||
if (singleWriteState ^ readOnlyState) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier->Transition.pResource = mResource.Get();
|
||||
barrier->Transition.StateBefore = mLastState;
|
||||
barrier->Transition.StateAfter = newState;
|
||||
barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::DestroyImpl() {
|
||||
// If we own the resource, release it.
|
||||
ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
|
||||
|
@ -350,8 +304,83 @@ namespace dawn_native { namespace d3d12 {
|
|||
}
|
||||
}
|
||||
|
||||
void Texture::SetUsage(dawn::TextureUsageBit newUsage) {
|
||||
mLastState = D3D12TextureUsage(newUsage, GetFormat());
|
||||
// When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
|
||||
// ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
|
||||
// cause subsequent errors.
|
||||
bool Texture::TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::TextureUsageBit newUsage) {
|
||||
return TransitionUsageAndGetResourceBarrier(barrier,
|
||||
D3D12TextureUsage(newUsage, GetFormat()));
|
||||
}
|
||||
|
||||
// When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
|
||||
// ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
|
||||
// cause subsequent errors.
|
||||
bool Texture::TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
|
||||
D3D12_RESOURCE_STATES newState) {
|
||||
// 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;
|
||||
|
||||
// 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();
|
||||
if (mValidToDecay && pendingCommandSerial > mLastUsedSerial) {
|
||||
lastState = D3D12_RESOURCE_STATE_COMMON;
|
||||
}
|
||||
|
||||
// Update the tracked state.
|
||||
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;
|
||||
barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier->Transition.pResource = mResource.Get();
|
||||
barrier->Transition.StateBefore = lastState;
|
||||
barrier->Transition.StateAfter = newState;
|
||||
barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
|
||||
mValidToDecay = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||
|
@ -362,11 +391,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||
D3D12_RESOURCE_STATES newState) {
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
if (CreateD3D12ResourceBarrierIfNeeded(&barrier, newState)) {
|
||||
|
||||
if (TransitionUsageAndGetResourceBarrier(&barrier, newState)) {
|
||||
commandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
mLastState = newState;
|
||||
}
|
||||
|
||||
D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t baseMipLevel,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef DAWNNATIVE_D3D12_TEXTURED3D12_H_
|
||||
#define DAWNNATIVE_D3D12_TEXTURED3D12_H_
|
||||
|
||||
#include "common/Serial.h"
|
||||
#include "dawn_native/Texture.h"
|
||||
|
||||
#include "dawn_native/d3d12/d3d12_platform.h"
|
||||
|
@ -31,13 +32,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
Texture(Device* device, const TextureDescriptor* descriptor, ID3D12Resource* nativeTexture);
|
||||
~Texture();
|
||||
|
||||
bool CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::TextureUsageBit newUsage) const;
|
||||
bool CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier,
|
||||
D3D12_RESOURCE_STATES newState) const;
|
||||
DXGI_FORMAT GetD3D12Format() const;
|
||||
ID3D12Resource* GetD3D12Resource() const;
|
||||
void SetUsage(dawn::TextureUsageBit newUsage);
|
||||
bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
|
||||
dawn::TextureUsageBit newUsage);
|
||||
void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||
dawn::TextureUsageBit usage);
|
||||
void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||
|
@ -64,8 +62,14 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
UINT16 GetDepthOrArraySize();
|
||||
|
||||
bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
|
||||
D3D12_RESOURCE_STATES newState);
|
||||
|
||||
ComPtr<ID3D12Resource> mResource;
|
||||
D3D12_RESOURCE_STATES mLastState = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON;
|
||||
|
||||
Serial mLastUsedSerial = UINT64_MAX;
|
||||
bool mValidToDecay = false;
|
||||
};
|
||||
|
||||
class TextureView : public TextureViewBase {
|
||||
|
|
Loading…
Reference in New Issue