diff --git a/src/backend/d3d12/BufferD3D12.cpp b/src/backend/d3d12/BufferD3D12.cpp index e51eec8a40..f2dba3ddd4 100644 --- a/src/backend/d3d12/BufferD3D12.cpp +++ b/src/backend/d3d12/BufferD3D12.cpp @@ -67,7 +67,7 @@ namespace backend { namespace d3d12 { } } // namespace - Buffer::Buffer(Device* device, BufferBuilder* builder) : BufferBase(builder), mDevice(device) { + Buffer::Buffer(BufferBuilder* builder) : BufferBase(builder) { D3D12_RESOURCE_DESC resourceDescriptor; resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; resourceDescriptor.Alignment = 0; @@ -82,26 +82,31 @@ namespace backend { namespace d3d12 { resourceDescriptor.Flags = D3D12ResourceFlags(GetAllowedUsage()); auto heapType = D3D12HeapType(GetAllowedUsage()); - auto bufferUsage = D3D12BufferUsage(GetUsage()); + auto bufferUsage = D3D12_RESOURCE_STATE_COMMON; // D3D12 requires buffers on the READBACK heap to have the D3D12_RESOURCE_STATE_COPY_DEST // state if (heapType == D3D12_HEAP_TYPE_READBACK) { bufferUsage |= D3D12_RESOURCE_STATE_COPY_DEST; + mFixedResourceState = true; + mLastUsage = nxt::BufferUsageBit::TransferDst; } // D3D12 requires buffers on the UPLOAD heap to have the D3D12_RESOURCE_STATE_GENERIC_READ // state if (heapType == D3D12_HEAP_TYPE_UPLOAD) { bufferUsage |= D3D12_RESOURCE_STATE_GENERIC_READ; + mFixedResourceState = true; + mLastUsage = nxt::BufferUsageBit::TransferSrc; } - mResource = - device->GetResourceAllocator()->Allocate(heapType, resourceDescriptor, bufferUsage); + mResource = ToBackend(GetDevice()) + ->GetResourceAllocator() + ->Allocate(heapType, resourceDescriptor, bufferUsage); } Buffer::~Buffer() { - mDevice->GetResourceAllocator()->Release(mResource); + ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource); } uint32_t Buffer::GetD3D12Size() const { @@ -113,31 +118,35 @@ namespace backend { namespace d3d12 { return mResource; } - bool Buffer::GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, - nxt::BufferUsageBit targetUsage, - D3D12_RESOURCE_BARRIER* barrier) { - if (GetAllowedUsage() & (nxt::BufferUsageBit::MapRead | nxt::BufferUsageBit::MapWrite)) { - // Transitions are never needed for mapped buffers because they are created with and - // always need the Transfer(Dst|Src) state. Mapped buffers cannot have states outside of - // (MapRead|TransferDst) and (MapWrite|TransferSrc) - return false; + void Buffer::TransitionUsageNow(ComPtr commandList, + nxt::BufferUsageBit usage) { + // Resources in upload and readback heaps must be kept in the COPY_SOURCE/DEST state + if (mFixedResourceState) { + ASSERT(usage == mLastUsage); + return; } - D3D12_RESOURCE_STATES stateBefore = D3D12BufferUsage(currentUsage); - D3D12_RESOURCE_STATES stateAfter = D3D12BufferUsage(targetUsage); - - if (stateBefore == stateAfter) { - return false; + // We can skip transitions to already current usages. + // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. + bool lastIncludesTarget = (mLastUsage & usage) == usage; + if (lastIncludesTarget) { + return; } - barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier->Transition.pResource = mResource.Get(); - barrier->Transition.StateBefore = stateBefore; - barrier->Transition.StateAfter = stateAfter; - barrier->Transition.Subresource = 0; + D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage); + D3D12_RESOURCE_STATES newState = D3D12BufferUsage(usage); - return true; + D3D12_RESOURCE_BARRIER barrier; + 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 = 0; + + commandList->ResourceBarrier(1, &barrier); + + mLastUsage = usage; } D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const { @@ -153,7 +162,10 @@ namespace backend { namespace d3d12 { } void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) { - mDevice->GetResourceUploader()->BufferSubData(mResource, start, count, data); + Device* device = ToBackend(GetDevice()); + + TransitionUsageNow(device->GetPendingCommandList(), nxt::BufferUsageBit::TransferDst); + device->GetResourceUploader()->BufferSubData(mResource, start, count, data); } void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) { @@ -161,6 +173,8 @@ namespace backend { namespace d3d12 { char* data = nullptr; ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast(&data))); + // There is no need to transition the resource to a new state: D3D12 seems to make the GPU + // writes available when the fence is passed. MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker(); tracker->Track(this, serial, data + start, false); } @@ -170,6 +184,8 @@ namespace backend { namespace d3d12 { char* data = nullptr; ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast(&data))); + // There is no need to transition the resource to a new state: D3D12 seems to make the CPU + // writes available on queue submission. MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker(); tracker->Track(this, serial, data + start, true); } @@ -179,15 +195,10 @@ namespace backend { namespace d3d12 { // modified D3D12_RANGE writeRange = {}; mResource->Unmap(0, &writeRange); - mDevice->GetResourceAllocator()->Release(mResource); + ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource); } - void Buffer::TransitionUsageImpl(nxt::BufferUsageBit currentUsage, - nxt::BufferUsageBit targetUsage) { - D3D12_RESOURCE_BARRIER barrier; - if (GetResourceTransitionBarrier(currentUsage, targetUsage, &barrier)) { - mDevice->GetPendingCommandList()->ResourceBarrier(1, &barrier); - } + void Buffer::TransitionUsageImpl(nxt::BufferUsageBit, nxt::BufferUsageBit) { } BufferView::BufferView(BufferViewBuilder* builder) : BufferViewBase(builder) { diff --git a/src/backend/d3d12/BufferD3D12.h b/src/backend/d3d12/BufferD3D12.h index 4a9e49f8a4..e6bb6a54ef 100644 --- a/src/backend/d3d12/BufferD3D12.h +++ b/src/backend/d3d12/BufferD3D12.h @@ -26,21 +26,18 @@ namespace backend { namespace d3d12 { class Buffer : public BufferBase { public: - Buffer(Device* device, BufferBuilder* builder); + Buffer(BufferBuilder* builder); ~Buffer(); uint32_t GetD3D12Size() const; ComPtr GetD3D12Resource(); D3D12_GPU_VIRTUAL_ADDRESS GetVA() const; - bool GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, - nxt::BufferUsageBit targetUsage, - D3D12_RESOURCE_BARRIER* barrier); void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite); - private: - Device* mDevice; - ComPtr mResource; + void TransitionUsageNow(ComPtr commandList, + nxt::BufferUsageBit usage); + private: // NXT API void SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; @@ -48,6 +45,10 @@ namespace backend { namespace d3d12 { void UnmapImpl() override; void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage) override; + + ComPtr mResource; + bool mFixedResourceState = false; + nxt::BufferUsageBit mLastUsage = nxt::BufferUsageBit::None; }; class BufferView : public BufferViewBase { diff --git a/src/backend/d3d12/CommandBufferD3D12.cpp b/src/backend/d3d12/CommandBufferD3D12.cpp index 9dccf60345..d0d857704a 100644 --- a/src/backend/d3d12/CommandBufferD3D12.cpp +++ b/src/backend/d3d12/CommandBufferD3D12.cpp @@ -230,7 +230,9 @@ namespace backend { namespace d3d12 { } // anonymous namespace CommandBuffer::CommandBuffer(CommandBufferBuilder* builder) - : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) { + : CommandBufferBase(builder), + mCommands(builder->AcquireCommands()), + mPassResourceUsages(builder->AcquirePassResourceUsage()) { } CommandBuffer::~CommandBuffer() { @@ -261,27 +263,55 @@ namespace backend { namespace d3d12 { } } + // Records the necessary barriers for the resource usage pre-computed by the frontend + auto TransitionForPass = [](ComPtr commandList, + const PassResourceUsage& usages) { + for (size_t i = 0; i < usages.buffers.size(); ++i) { + Buffer* buffer = ToBackend(usages.buffers[i]); + buffer->TransitionUsageNow(commandList, usages.bufferUsages[i]); + } + for (size_t i = 0; i < usages.textures.size(); ++i) { + Texture* texture = ToBackend(usages.textures[i]); + texture->TransitionUsageNow(commandList, usages.textureUsages[i]); + } + }; + + uint32_t nextPassNumber = 0; + Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { mCommands.NextCommand(); + + TransitionForPass(commandList, mPassResourceUsages[nextPassNumber]); RecordComputePass(commandList, &bindingTracker); + + nextPassNumber++; } break; case Command::BeginRenderPass: { BeginRenderPassCmd* beginRenderPassCmd = mCommands.NextCommand(); + + TransitionForPass(commandList, mPassResourceUsages[nextPassNumber]); RecordRenderPass(commandList, &bindingTracker, ToBackend(beginRenderPassCmd->info.Get())); + + nextPassNumber++; } break; case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); - auto src = ToBackend(copy->source.buffer.Get())->GetD3D12Resource(); - auto dst = ToBackend(copy->destination.buffer.Get())->GetD3D12Resource(); - commandList->CopyBufferRegion(dst.Get(), copy->destination.offset, src.Get(), - copy->source.offset, copy->size); + Buffer* srcBuffer = ToBackend(copy->source.buffer.Get()); + Buffer* dstBuffer = ToBackend(copy->destination.buffer.Get()); + + srcBuffer->TransitionUsageNow(commandList, nxt::BufferUsageBit::TransferSrc); + dstBuffer->TransitionUsageNow(commandList, nxt::BufferUsageBit::TransferDst); + + commandList->CopyBufferRegion( + dstBuffer->GetD3D12Resource().Get(), copy->destination.offset, + srcBuffer->GetD3D12Resource().Get(), copy->source.offset, copy->size); } break; case Command::CopyBufferToTexture: { @@ -289,6 +319,9 @@ namespace backend { namespace d3d12 { Buffer* buffer = ToBackend(copy->source.buffer.Get()); Texture* texture = ToBackend(copy->destination.texture.Get()); + buffer->TransitionUsageNow(commandList, nxt::BufferUsageBit::TransferSrc); + texture->TransitionUsageNow(commandList, nxt::TextureUsageBit::TransferDst); + auto copySplit = ComputeTextureCopySplit( copy->destination.x, copy->destination.y, copy->destination.z, copy->destination.width, copy->destination.height, copy->destination.depth, @@ -332,6 +365,9 @@ namespace backend { namespace d3d12 { Texture* texture = ToBackend(copy->source.texture.Get()); Buffer* buffer = ToBackend(copy->destination.buffer.Get()); + texture->TransitionUsageNow(commandList, nxt::TextureUsageBit::TransferSrc); + buffer->TransitionUsageNow(commandList, nxt::BufferUsageBit::TransferDst); + auto copySplit = ComputeTextureCopySplit( copy->source.x, copy->source.y, copy->source.z, copy->source.width, copy->source.height, copy->source.depth, @@ -373,31 +409,13 @@ namespace backend { namespace d3d12 { case Command::TransitionBufferUsage: { TransitionBufferUsageCmd* cmd = mCommands.NextCommand(); - - Buffer* buffer = ToBackend(cmd->buffer.Get()); - - D3D12_RESOURCE_BARRIER barrier; - if (buffer->GetResourceTransitionBarrier(buffer->GetUsage(), cmd->usage, - &barrier)) { - commandList->ResourceBarrier(1, &barrier); - } - - buffer->UpdateUsageInternal(cmd->usage); + cmd->buffer->UpdateUsageInternal(cmd->usage); } break; case Command::TransitionTextureUsage: { TransitionTextureUsageCmd* cmd = mCommands.NextCommand(); - - Texture* texture = ToBackend(cmd->texture.Get()); - - D3D12_RESOURCE_BARRIER barrier; - if (texture->GetResourceTransitionBarrier(texture->GetUsage(), cmd->usage, - &barrier)) { - commandList->ResourceBarrier(1, &barrier); - } - - texture->UpdateUsageInternal(cmd->usage); + cmd->texture->UpdateUsageInternal(cmd->usage); } break; default: { UNREACHABLE(); } break; @@ -456,8 +474,6 @@ namespace backend { namespace d3d12 { // It's already validated that this texture is either frozen to the correct // usage, or not frozen. if (!texture->IsFrozen()) { - texture->TransitionUsageImpl(texture->GetUsage(), - nxt::TextureUsageBit::OutputAttachment); texture->UpdateUsageInternal(nxt::TextureUsageBit::OutputAttachment); } @@ -476,8 +492,6 @@ namespace backend { namespace d3d12 { // It's already validated that this texture is either frozen to the correct // usage, or not frozen. if (!texture->IsFrozen()) { - texture->TransitionUsageImpl(texture->GetUsage(), - nxt::TextureUsageBit::OutputAttachment); texture->UpdateUsageInternal(nxt::TextureUsageBit::OutputAttachment); } diff --git a/src/backend/d3d12/CommandBufferD3D12.h b/src/backend/d3d12/CommandBufferD3D12.h index 93f038a98f..2f356b23db 100644 --- a/src/backend/d3d12/CommandBufferD3D12.h +++ b/src/backend/d3d12/CommandBufferD3D12.h @@ -42,6 +42,7 @@ namespace backend { namespace d3d12 { RenderPassDescriptor* renderPass); CommandIterator mCommands; + std::vector mPassResourceUsages; }; }} // namespace backend::d3d12 diff --git a/src/backend/d3d12/DeviceD3D12.cpp b/src/backend/d3d12/DeviceD3D12.cpp index 746d5abd76..1aa7aff3ad 100644 --- a/src/backend/d3d12/DeviceD3D12.cpp +++ b/src/backend/d3d12/DeviceD3D12.cpp @@ -274,7 +274,7 @@ namespace backend { namespace d3d12 { return new BlendState(builder); } BufferBase* Device::CreateBuffer(BufferBuilder* builder) { - return new Buffer(this, builder); + return new Buffer(builder); } BufferViewBase* Device::CreateBufferView(BufferViewBuilder* builder) { return new BufferView(builder); diff --git a/src/backend/d3d12/NativeSwapChainImplD3D12.cpp b/src/backend/d3d12/NativeSwapChainImplD3D12.cpp index e99fc13268..269417bbcd 100644 --- a/src/backend/d3d12/NativeSwapChainImplD3D12.cpp +++ b/src/backend/d3d12/NativeSwapChainImplD3D12.cpp @@ -101,8 +101,7 @@ namespace backend { namespace d3d12 { } nxtSwapChainError NativeSwapChainImpl::Present() { - // Flush pending commands that include the transition of the texture to present. - mDevice->ExecuteCommandLists({}); + // This assumes the texture has already been transition to the PRESENT state. ASSERT_SUCCESS(mSwapChain->Present(1, 0)); // TODO(cwallez@chromium.org): Make the serial ticking implicit. diff --git a/src/backend/d3d12/SwapChainD3D12.cpp b/src/backend/d3d12/SwapChainD3D12.cpp index 11a7fd3b2d..85b214ffda 100644 --- a/src/backend/d3d12/SwapChainD3D12.cpp +++ b/src/backend/d3d12/SwapChainD3D12.cpp @@ -26,6 +26,9 @@ namespace backend { namespace d3d12 { nxtWSIContextD3D12 wsiContext = {}; wsiContext.device = reinterpret_cast(GetDevice()); im.Init(im.userData, &wsiContext); + + ASSERT(im.textureUsage != NXT_TEXTURE_USAGE_BIT_NONE); + mTextureUsage = static_cast(im.textureUsage); } SwapChain::~SwapChain() { @@ -44,7 +47,13 @@ namespace backend { namespace d3d12 { return new Texture(builder, nativeTexture); } - void SwapChain::OnBeforePresent(TextureBase*) { + void SwapChain::OnBeforePresent(TextureBase* texture) { + Device* device = ToBackend(GetDevice()); + + // Perform the necessary transition for the texture to be presented. + ToBackend(texture)->TransitionUsageNow(device->GetPendingCommandList(), mTextureUsage); + + device->ExecuteCommandLists({}); } }} // namespace backend::d3d12 diff --git a/src/backend/d3d12/SwapChainD3D12.h b/src/backend/d3d12/SwapChainD3D12.h index 33e039f898..9ff8467c52 100644 --- a/src/backend/d3d12/SwapChainD3D12.h +++ b/src/backend/d3d12/SwapChainD3D12.h @@ -27,6 +27,8 @@ namespace backend { namespace d3d12 { protected: TextureBase* GetNextTextureImpl(TextureBuilder* builder) override; void OnBeforePresent(TextureBase* texture) override; + + nxt::TextureUsageBit mTextureUsage; }; }} // namespace backend::d3d12 diff --git a/src/backend/d3d12/TextureD3D12.cpp b/src/backend/d3d12/TextureD3D12.cpp index de50558f65..edba271b4f 100644 --- a/src/backend/d3d12/TextureD3D12.cpp +++ b/src/backend/d3d12/TextureD3D12.cpp @@ -150,32 +150,31 @@ namespace backend { namespace d3d12 { return mResourcePtr; } - bool Texture::GetResourceTransitionBarrier(nxt::TextureUsageBit currentUsage, - nxt::TextureUsageBit targetUsage, - D3D12_RESOURCE_BARRIER* barrier) { - D3D12_RESOURCE_STATES stateBefore = D3D12TextureUsage(currentUsage, GetFormat()); - D3D12_RESOURCE_STATES stateAfter = D3D12TextureUsage(targetUsage, GetFormat()); - - if (stateBefore == stateAfter) { - return false; + void Texture::TransitionUsageNow(ComPtr commandList, + nxt::TextureUsageBit usage) { + // Avoid transitioning the texture when it isn't needed. + // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. + if (usage == mLastUsage) { + return; } - barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier->Transition.pResource = mResourcePtr; - barrier->Transition.StateBefore = stateBefore; - barrier->Transition.StateAfter = stateAfter; - barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + D3D12_RESOURCE_STATES lastState = D3D12TextureUsage(mLastUsage, GetFormat()); + D3D12_RESOURCE_STATES newState = D3D12TextureUsage(usage, GetFormat()); - return true; + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = mResourcePtr; + barrier.Transition.StateBefore = lastState; + barrier.Transition.StateAfter = newState; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + commandList->ResourceBarrier(1, &barrier); + + mLastUsage = usage; } - void Texture::TransitionUsageImpl(nxt::TextureUsageBit currentUsage, - nxt::TextureUsageBit targetUsage) { - D3D12_RESOURCE_BARRIER barrier; - if (GetResourceTransitionBarrier(currentUsage, targetUsage, &barrier)) { - mDevice->GetPendingCommandList()->ResourceBarrier(1, &barrier); - } + void Texture::TransitionUsageImpl(nxt::TextureUsageBit, nxt::TextureUsageBit) { } TextureView::TextureView(TextureViewBuilder* builder) : TextureViewBase(builder) { diff --git a/src/backend/d3d12/TextureD3D12.h b/src/backend/d3d12/TextureD3D12.h index af36c1ad8f..fc837607c8 100644 --- a/src/backend/d3d12/TextureD3D12.h +++ b/src/backend/d3d12/TextureD3D12.h @@ -33,17 +33,18 @@ namespace backend { namespace d3d12 { DXGI_FORMAT GetD3D12Format() const; ID3D12Resource* GetD3D12Resource(); - bool GetResourceTransitionBarrier(nxt::TextureUsageBit currentUsage, - nxt::TextureUsageBit targetUsage, - D3D12_RESOURCE_BARRIER* barrier); + void TransitionUsageNow(ComPtr commandList, + nxt::TextureUsageBit usage); + + private: void TransitionUsageImpl(nxt::TextureUsageBit currentUsage, nxt::TextureUsageBit targetUsage) override; - private: Device* mDevice; ComPtr mResource = {}; ID3D12Resource* mResourcePtr = nullptr; + nxt::TextureUsageBit mLastUsage = nxt::TextureUsageBit::None; }; class TextureView : public TextureViewBase {