D3D12: Keep a reference to pipelines until unused

Previously we would remove the reference to pipelines in the destructor
of the d3d12::*Pipeline objects which could cause the D3D12 pipeline
state to be destroyed while still used by in-flight commands. Add a
global queue of ComPtrs to keep alive in the d3d12::Device to fix this.
This commit is contained in:
Corentin Wallez 2018-03-02 11:07:07 -05:00 committed by Corentin Wallez
parent 57f7bc750a
commit cf0ac7570d
6 changed files with 32 additions and 3 deletions

View File

@ -24,7 +24,7 @@
namespace backend { namespace d3d12 {
ComputePipeline::ComputePipeline(ComputePipelineBuilder* builder)
: ComputePipelineBase(builder) {
: ComputePipelineBase(builder), mDevice(ToBackend(builder->GetDevice())) {
uint32_t compileFlags = 0;
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
@ -57,6 +57,10 @@ namespace backend { namespace d3d12 {
IID_PPV_ARGS(&mPipelineState));
}
ComputePipeline::~ComputePipeline() {
mDevice->ReferenceUntilUnused(mPipelineState);
}
ComPtr<ID3D12PipelineState> ComputePipeline::GetPipelineState() {
return mPipelineState;
}

View File

@ -21,14 +21,18 @@
namespace backend { namespace d3d12 {
class Device;
class ComputePipeline : public ComputePipelineBase {
public:
ComputePipeline(ComputePipelineBuilder* builder);
~ComputePipeline();
ComPtr<ID3D12PipelineState> GetPipelineState();
private:
ComPtr<ID3D12PipelineState> mPipelineState;
Device* mDevice = nullptr;
};
}} // namespace backend::d3d12

View File

@ -149,6 +149,8 @@ namespace backend { namespace d3d12 {
NextSerial();
WaitForSerial(currentSerial); // Wait for all in-flight commands to finish executing
TickImpl(); // Call tick one last time so resources are cleaned up
ASSERT(mUsedComObjectRefs.Empty());
delete mCommandAllocatorManager;
delete mDescriptorHeapAllocator;
delete mMapReadRequestTracker;
@ -214,6 +216,7 @@ namespace backend { namespace d3d12 {
mCommandAllocatorManager->Tick(lastCompletedSerial);
mDescriptorHeapAllocator->Tick(lastCompletedSerial);
mMapReadRequestTracker->Tick(lastCompletedSerial);
mUsedComObjectRefs.ClearUpTo(lastCompletedSerial);
ExecuteCommandLists({});
NextSerial();
}
@ -234,6 +237,10 @@ namespace backend { namespace d3d12 {
}
}
void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) {
mUsedComObjectRefs.Enqueue(object, mSerial);
}
void Device::ExecuteCommandLists(std::initializer_list<ID3D12CommandList*> commandLists) {
// If there are pending commands, prepend them to ExecuteCommandLists
if (mPendingCommands.open) {

View File

@ -21,8 +21,8 @@
#include "backend/Device.h"
#include "backend/RenderPass.h"
#include "backend/ToBackend.h"
#include "backend/d3d12/d3d12_platform.h"
#include "common/SerialQueue.h"
namespace backend { namespace d3d12 {
@ -127,6 +127,8 @@ namespace backend { namespace d3d12 {
void NextSerial();
void WaitForSerial(uint64_t serial);
void ReferenceUntilUnused(ComPtr<IUnknown> object);
void ExecuteCommandLists(std::initializer_list<ID3D12CommandList*> commandLists);
private:
@ -149,6 +151,8 @@ namespace backend { namespace d3d12 {
ComPtr<ID3D12GraphicsCommandList> commandList;
bool open = false;
} mPendingCommands;
SerialQueue<ComPtr<IUnknown>> mUsedComObjectRefs;
};
class RenderPass : public RenderPassBase {

View File

@ -64,7 +64,8 @@ namespace backend { namespace d3d12 {
RenderPipeline::RenderPipeline(RenderPipelineBuilder* builder)
: RenderPipelineBase(builder),
mD3d12PrimitiveTopology(D3D12PrimitiveTopology(GetPrimitiveTopology())) {
mD3d12PrimitiveTopology(D3D12PrimitiveTopology(GetPrimitiveTopology())),
mDevice(ToBackend(builder->GetDevice())) {
uint32_t compileFlags = 0;
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
@ -171,6 +172,10 @@ namespace backend { namespace d3d12 {
&descriptor, IID_PPV_ARGS(&mPipelineState)));
}
RenderPipeline::~RenderPipeline() {
mDevice->ReferenceUntilUnused(mPipelineState);
}
D3D12_PRIMITIVE_TOPOLOGY RenderPipeline::GetD3D12PrimitiveTopology() const {
return mD3d12PrimitiveTopology;
}

View File

@ -21,9 +21,12 @@
namespace backend { namespace d3d12 {
class Device;
class RenderPipeline : public RenderPipelineBase {
public:
RenderPipeline(RenderPipelineBuilder* builder);
~RenderPipeline();
D3D12_PRIMITIVE_TOPOLOGY GetD3D12PrimitiveTopology() const;
ComPtr<ID3D12PipelineState> GetPipelineState();
@ -31,6 +34,8 @@ namespace backend { namespace d3d12 {
private:
D3D12_PRIMITIVE_TOPOLOGY mD3d12PrimitiveTopology;
ComPtr<ID3D12PipelineState> mPipelineState;
Device* mDevice = nullptr;
};
}} // namespace backend::d3d12