2 // Copyright (c) Microsoft. All rights reserved.
3 // This code is licensed under the MIT License (MIT).
4 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
5 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
6 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
7 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
9 // Developed by Minigraph
11 // Author: James Stanard
15 #include "CommandListManager.h"
17 CommandQueue::CommandQueue(D3D12_COMMAND_LIST_TYPE Type
) :
18 m_CommandQueue(nullptr),
20 m_AllocatorPool(Type
),
22 m_NextFenceValue((uint64_t)Type
<< 56 | 1),
23 m_LastCompletedFenceValue((uint64_t)Type
<< 56)
27 CommandQueue::~CommandQueue()
32 void CommandQueue::Shutdown()
34 if (m_CommandQueue
== nullptr)
37 m_AllocatorPool
.Shutdown();
39 port_destroy_event(m_FenceEventHandle
);
44 m_CommandQueue
->Release();
45 m_CommandQueue
= nullptr;
48 CommandListManager::CommandListManager() :
50 m_GraphicsQueue(D3D12_COMMAND_LIST_TYPE_DIRECT
),
51 m_ComputeQueue(D3D12_COMMAND_LIST_TYPE_COMPUTE
),
52 m_CopyQueue(D3D12_COMMAND_LIST_TYPE_COPY
)
56 CommandListManager::~CommandListManager()
61 void CommandListManager::Shutdown()
63 m_GraphicsQueue
.Shutdown();
64 m_ComputeQueue
.Shutdown();
65 m_CopyQueue
.Shutdown();
68 void CommandQueue::Create(ID3D12Device
* pDevice
)
70 ASSERT(pDevice
!= nullptr);
72 ASSERT(m_AllocatorPool
.Size() == 0);
74 D3D12_COMMAND_QUEUE_DESC QueueDesc
= {};
75 QueueDesc
.Type
= m_Type
;
76 QueueDesc
.NodeMask
= 1;
77 pDevice
->CreateCommandQueue(&QueueDesc
, MY_IID_PPV_ARGS(&m_CommandQueue
));
78 m_CommandQueue
->SetName(L
"CommandListManager::m_CommandQueue");
80 ASSERT_SUCCEEDED(pDevice
->CreateFence(0, D3D12_FENCE_FLAG_NONE
, MY_IID_PPV_ARGS(&m_pFence
)));
81 m_pFence
->SetName(L
"CommandListManager::m_pFence");
82 m_pFence
->Signal((uint64_t)m_Type
<< 56);
84 m_FenceEventHandle
= port_create_event();
85 ASSERT(m_FenceEventHandle
!= INVALID_HANDLE_VALUE
);
87 m_AllocatorPool
.Create(pDevice
);
92 void CommandListManager::Create(ID3D12Device
* pDevice
)
94 ASSERT(pDevice
!= nullptr);
98 m_GraphicsQueue
.Create(pDevice
);
99 m_ComputeQueue
.Create(pDevice
);
100 m_CopyQueue
.Create(pDevice
);
103 void CommandListManager::CreateNewCommandList( D3D12_COMMAND_LIST_TYPE Type
, ID3D12GraphicsCommandList
** List
, ID3D12CommandAllocator
** Allocator
)
105 ASSERT(Type
!= D3D12_COMMAND_LIST_TYPE_BUNDLE
, "Bundles are not yet supported");
108 case D3D12_COMMAND_LIST_TYPE_DIRECT
: *Allocator
= m_GraphicsQueue
.RequestAllocator(); break;
109 case D3D12_COMMAND_LIST_TYPE_BUNDLE
: break;
110 case D3D12_COMMAND_LIST_TYPE_COMPUTE
: *Allocator
= m_ComputeQueue
.RequestAllocator(); break;
111 case D3D12_COMMAND_LIST_TYPE_COPY
: *Allocator
= m_CopyQueue
.RequestAllocator(); break;
114 ASSERT_SUCCEEDED( m_Device
->CreateCommandList(1, Type
, *Allocator
, nullptr, MY_IID_PPV_ARGS(List
)) );
115 (*List
)->SetName(L
"CommandList");
118 uint64_t CommandQueue::ExecuteCommandList( ID3D12CommandList
* List
)
120 std::lock_guard
<std::mutex
> LockGuard(m_FenceMutex
);
122 ASSERT_SUCCEEDED(((ID3D12GraphicsCommandList
*)List
)->Close());
124 // Kickoff the command list
125 m_CommandQueue
->ExecuteCommandLists(1, &List
);
127 // Signal the next fence value (with the GPU)
128 m_CommandQueue
->Signal(m_pFence
, m_NextFenceValue
);
130 // And increment the fence value.
131 return m_NextFenceValue
++;
134 uint64_t CommandQueue::IncrementFence(void)
136 std::lock_guard
<std::mutex
> LockGuard(m_FenceMutex
);
137 m_CommandQueue
->Signal(m_pFence
, m_NextFenceValue
);
138 return m_NextFenceValue
++;
141 bool CommandQueue::IsFenceComplete(uint64_t FenceValue
)
143 // Avoid querying the fence value by testing against the last one seen.
144 // The max() is to protect against an unlikely race condition that could cause the last
145 // completed fence value to regress.
146 if (FenceValue
> m_LastCompletedFenceValue
)
147 m_LastCompletedFenceValue
= std::max(m_LastCompletedFenceValue
, m_pFence
->GetCompletedValue());
149 return FenceValue
<= m_LastCompletedFenceValue
;
154 extern CommandListManager g_CommandManager
;
157 void CommandQueue::StallForFence(uint64_t FenceValue
)
159 CommandQueue
& Producer
= Graphics::g_CommandManager
.GetQueue((D3D12_COMMAND_LIST_TYPE
)(FenceValue
>> 56));
160 m_CommandQueue
->Wait(Producer
.m_pFence
, FenceValue
);
163 void CommandQueue::StallForProducer(CommandQueue
& Producer
)
165 ASSERT(Producer
.m_NextFenceValue
> 0);
166 m_CommandQueue
->Wait(Producer
.m_pFence
, Producer
.m_NextFenceValue
- 1);
169 void CommandQueue::WaitForFence(uint64_t FenceValue
)
171 if (IsFenceComplete(FenceValue
))
174 // TODO: Think about how this might affect a multi-threaded situation. Suppose thread A
175 // wants to wait for fence 100, then thread B comes along and wants to wait for 99. If
176 // the fence can only have one event set on completion, then thread B has to wait for
177 // 100 before it knows 99 is ready. Maybe insert sequential events?
179 std::lock_guard
<std::mutex
> LockGuard(m_EventMutex
);
181 m_pFence
->SetEventOnCompletion(FenceValue
, m_FenceEventHandle
);
182 port_wait_event(m_FenceEventHandle
, INFINITE
);
183 m_LastCompletedFenceValue
= FenceValue
;
187 void CommandListManager::WaitForFence(uint64_t FenceValue
)
189 CommandQueue
& Producer
= Graphics::g_CommandManager
.GetQueue((D3D12_COMMAND_LIST_TYPE
)(FenceValue
>> 56));
190 Producer
.WaitForFence(FenceValue
);
193 ID3D12CommandAllocator
* CommandQueue::RequestAllocator()
195 uint64_t CompletedFence
= m_pFence
->GetCompletedValue();
197 return m_AllocatorPool
.RequestAllocator(CompletedFence
);
200 void CommandQueue::DiscardAllocator(uint64_t FenceValue
, ID3D12CommandAllocator
* Allocator
)
202 m_AllocatorPool
.DiscardAllocator(FenceValue
, Allocator
);