1 // Copyright 2017 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/d3d12/DeviceD3D12.h" 16 17 #include "common/Assert.h" 18 #include "dawn_native/BackendConnection.h" 19 #include "dawn_native/DynamicUploader.h" 20 #include "dawn_native/d3d12/AdapterD3D12.h" 21 #include "dawn_native/d3d12/BackendD3D12.h" 22 #include "dawn_native/d3d12/BindGroupD3D12.h" 23 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" 24 #include "dawn_native/d3d12/BufferD3D12.h" 25 #include "dawn_native/d3d12/CommandAllocatorManager.h" 26 #include "dawn_native/d3d12/CommandBufferD3D12.h" 27 #include "dawn_native/d3d12/ComputePipelineD3D12.h" 28 #include "dawn_native/d3d12/DescriptorHeapAllocator.h" 29 #include "dawn_native/d3d12/PipelineLayoutD3D12.h" 30 #include "dawn_native/d3d12/PlatformFunctions.h" 31 #include "dawn_native/d3d12/QueueD3D12.h" 32 #include "dawn_native/d3d12/RenderPipelineD3D12.h" 33 #include "dawn_native/d3d12/ResourceAllocator.h" 34 #include "dawn_native/d3d12/SamplerD3D12.h" 35 #include "dawn_native/d3d12/ShaderModuleD3D12.h" 36 #include "dawn_native/d3d12/StagingBufferD3D12.h" 37 #include "dawn_native/d3d12/SwapChainD3D12.h" 38 #include "dawn_native/d3d12/TextureD3D12.h" 39 40 namespace dawn_native { namespace d3d12 { 41 ASSERT_SUCCESS(HRESULT hr)42 void ASSERT_SUCCESS(HRESULT hr) { 43 ASSERT(SUCCEEDED(hr)); 44 } 45 Device(Adapter * adapter,const DeviceDescriptor * descriptor)46 Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor) 47 : DeviceBase(adapter, descriptor) { 48 if (descriptor != nullptr) { 49 ApplyToggleOverrides(descriptor); 50 } 51 } 52 Initialize()53 MaybeError Device::Initialize() { 54 mD3d12Device = ToBackend(GetAdapter())->GetDevice(); 55 56 ASSERT(mD3d12Device != nullptr); 57 58 // Create device-global objects 59 D3D12_COMMAND_QUEUE_DESC queueDesc = {}; 60 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 61 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 62 ASSERT_SUCCESS(mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue))); 63 64 ASSERT_SUCCESS(mD3d12Device->CreateFence(mLastSubmittedSerial, D3D12_FENCE_FLAG_NONE, 65 IID_PPV_ARGS(&mFence))); 66 mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); 67 ASSERT(mFenceEvent != nullptr); 68 69 // Initialize backend services 70 mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this); 71 mDescriptorHeapAllocator = std::make_unique<DescriptorHeapAllocator>(this); 72 mMapRequestTracker = std::make_unique<MapRequestTracker>(this); 73 mResourceAllocator = std::make_unique<ResourceAllocator>(this); 74 75 NextSerial(); 76 77 // Initialize indirect commands 78 D3D12_INDIRECT_ARGUMENT_DESC argumentDesc = {}; 79 argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; 80 81 D3D12_COMMAND_SIGNATURE_DESC programDesc = {}; 82 programDesc.ByteStride = 3 * sizeof(uint32_t); 83 programDesc.NumArgumentDescs = 1; 84 programDesc.pArgumentDescs = &argumentDesc; 85 86 GetD3D12Device()->CreateCommandSignature(&programDesc, NULL, 87 IID_PPV_ARGS(&mDispatchIndirectSignature)); 88 89 argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW; 90 programDesc.ByteStride = 4 * sizeof(uint32_t); 91 92 GetD3D12Device()->CreateCommandSignature(&programDesc, NULL, 93 IID_PPV_ARGS(&mDrawIndirectSignature)); 94 95 argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; 96 programDesc.ByteStride = 5 * sizeof(uint32_t); 97 98 GetD3D12Device()->CreateCommandSignature(&programDesc, NULL, 99 IID_PPV_ARGS(&mDrawIndexedIndirectSignature)); 100 101 return {}; 102 } 103 ~Device()104 Device::~Device() { 105 // Immediately forget about all pending commands 106 if (mPendingCommands.open) { 107 mPendingCommands.commandList->Close(); 108 mPendingCommands.open = false; 109 mPendingCommands.commandList = nullptr; 110 } 111 NextSerial(); 112 WaitForSerial(mLastSubmittedSerial); // Wait for all in-flight commands to finish executing 113 TickImpl(); // Call tick one last time so resources are cleaned up 114 115 // Free services explicitly so that they can free D3D12 resources before destruction of the 116 // device. 117 mDynamicUploader = nullptr; 118 119 // Releasing the uploader enqueues buffers to be released. 120 // Call Tick() again to clear them before releasing the allocator. 121 mResourceAllocator->Tick(mCompletedSerial); 122 123 ASSERT(mUsedComObjectRefs.Empty()); 124 ASSERT(mPendingCommands.commandList == nullptr); 125 } 126 GetD3D12Device() const127 ComPtr<ID3D12Device> Device::GetD3D12Device() const { 128 return mD3d12Device; 129 } 130 GetCommandQueue() const131 ComPtr<ID3D12CommandQueue> Device::GetCommandQueue() const { 132 return mCommandQueue; 133 } 134 GetDispatchIndirectSignature() const135 ComPtr<ID3D12CommandSignature> Device::GetDispatchIndirectSignature() const { 136 return mDispatchIndirectSignature; 137 } 138 GetDrawIndirectSignature() const139 ComPtr<ID3D12CommandSignature> Device::GetDrawIndirectSignature() const { 140 return mDrawIndirectSignature; 141 } 142 GetDrawIndexedIndirectSignature() const143 ComPtr<ID3D12CommandSignature> Device::GetDrawIndexedIndirectSignature() const { 144 return mDrawIndexedIndirectSignature; 145 } 146 GetDescriptorHeapAllocator() const147 DescriptorHeapAllocator* Device::GetDescriptorHeapAllocator() const { 148 return mDescriptorHeapAllocator.get(); 149 } 150 GetFactory() const151 ComPtr<IDXGIFactory4> Device::GetFactory() const { 152 return ToBackend(GetAdapter())->GetBackend()->GetFactory(); 153 } 154 GetFunctions() const155 const PlatformFunctions* Device::GetFunctions() const { 156 return ToBackend(GetAdapter())->GetBackend()->GetFunctions(); 157 } 158 GetMapRequestTracker() const159 MapRequestTracker* Device::GetMapRequestTracker() const { 160 return mMapRequestTracker.get(); 161 } 162 GetResourceAllocator() const163 ResourceAllocator* Device::GetResourceAllocator() const { 164 return mResourceAllocator.get(); 165 } 166 OpenCommandList(ComPtr<ID3D12GraphicsCommandList> * commandList)167 void Device::OpenCommandList(ComPtr<ID3D12GraphicsCommandList>* commandList) { 168 ComPtr<ID3D12GraphicsCommandList>& cmdList = *commandList; 169 if (!cmdList) { 170 ASSERT_SUCCESS(mD3d12Device->CreateCommandList( 171 0, D3D12_COMMAND_LIST_TYPE_DIRECT, 172 mCommandAllocatorManager->ReserveCommandAllocator().Get(), nullptr, 173 IID_PPV_ARGS(&cmdList))); 174 } else { 175 ASSERT_SUCCESS( 176 cmdList->Reset(mCommandAllocatorManager->ReserveCommandAllocator().Get(), nullptr)); 177 } 178 } 179 GetPendingCommandList()180 ComPtr<ID3D12GraphicsCommandList> Device::GetPendingCommandList() { 181 // Callers of GetPendingCommandList do so to record commands. Only reserve a command 182 // allocator when it is needed so we don't submit empty command lists 183 if (!mPendingCommands.open) { 184 OpenCommandList(&mPendingCommands.commandList); 185 mPendingCommands.open = true; 186 } 187 return mPendingCommands.commandList; 188 } 189 GetCompletedCommandSerial() const190 Serial Device::GetCompletedCommandSerial() const { 191 return mCompletedSerial; 192 } 193 GetLastSubmittedCommandSerial() const194 Serial Device::GetLastSubmittedCommandSerial() const { 195 return mLastSubmittedSerial; 196 } 197 GetPendingCommandSerial() const198 Serial Device::GetPendingCommandSerial() const { 199 return mLastSubmittedSerial + 1; 200 } 201 TickImpl()202 void Device::TickImpl() { 203 // Perform cleanup operations to free unused objects 204 mCompletedSerial = mFence->GetCompletedValue(); 205 206 // Uploader should tick before the resource allocator 207 // as it enqueues resources to be released. 208 mDynamicUploader->Tick(mCompletedSerial); 209 210 mResourceAllocator->Tick(mCompletedSerial); 211 mCommandAllocatorManager->Tick(mCompletedSerial); 212 mDescriptorHeapAllocator->Tick(mCompletedSerial); 213 mMapRequestTracker->Tick(mCompletedSerial); 214 mUsedComObjectRefs.ClearUpTo(mCompletedSerial); 215 ExecuteCommandLists({}); 216 NextSerial(); 217 } 218 NextSerial()219 void Device::NextSerial() { 220 mLastSubmittedSerial++; 221 ASSERT_SUCCESS(mCommandQueue->Signal(mFence.Get(), mLastSubmittedSerial)); 222 } 223 WaitForSerial(uint64_t serial)224 void Device::WaitForSerial(uint64_t serial) { 225 mCompletedSerial = mFence->GetCompletedValue(); 226 if (mCompletedSerial < serial) { 227 ASSERT_SUCCESS(mFence->SetEventOnCompletion(serial, mFenceEvent)); 228 WaitForSingleObject(mFenceEvent, INFINITE); 229 } 230 } 231 ReferenceUntilUnused(ComPtr<IUnknown> object)232 void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) { 233 mUsedComObjectRefs.Enqueue(object, GetPendingCommandSerial()); 234 } 235 ExecuteCommandLists(std::initializer_list<ID3D12CommandList * > commandLists)236 void Device::ExecuteCommandLists(std::initializer_list<ID3D12CommandList*> commandLists) { 237 // If there are pending commands, prepend them to ExecuteCommandLists 238 if (mPendingCommands.open) { 239 std::vector<ID3D12CommandList*> lists(commandLists.size() + 1); 240 mPendingCommands.commandList->Close(); 241 mPendingCommands.open = false; 242 lists[0] = mPendingCommands.commandList.Get(); 243 std::copy(commandLists.begin(), commandLists.end(), lists.begin() + 1); 244 mCommandQueue->ExecuteCommandLists(static_cast<UINT>(commandLists.size() + 1), 245 lists.data()); 246 mPendingCommands.commandList = nullptr; 247 } else { 248 std::vector<ID3D12CommandList*> lists(commandLists); 249 mCommandQueue->ExecuteCommandLists(static_cast<UINT>(commandLists.size()), 250 lists.data()); 251 } 252 } 253 CreateBindGroupImpl(const BindGroupDescriptor * descriptor)254 ResultOrError<BindGroupBase*> Device::CreateBindGroupImpl( 255 const BindGroupDescriptor* descriptor) { 256 return new BindGroup(this, descriptor); 257 } CreateBindGroupLayoutImpl(const BindGroupLayoutDescriptor * descriptor)258 ResultOrError<BindGroupLayoutBase*> Device::CreateBindGroupLayoutImpl( 259 const BindGroupLayoutDescriptor* descriptor) { 260 return new BindGroupLayout(this, descriptor); 261 } CreateBufferImpl(const BufferDescriptor * descriptor)262 ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { 263 return new Buffer(this, descriptor); 264 } CreateCommandBuffer(CommandEncoderBase * encoder,const CommandBufferDescriptor * descriptor)265 CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder, 266 const CommandBufferDescriptor* descriptor) { 267 return new CommandBuffer(encoder, descriptor); 268 } CreateComputePipelineImpl(const ComputePipelineDescriptor * descriptor)269 ResultOrError<ComputePipelineBase*> Device::CreateComputePipelineImpl( 270 const ComputePipelineDescriptor* descriptor) { 271 return new ComputePipeline(this, descriptor); 272 } CreatePipelineLayoutImpl(const PipelineLayoutDescriptor * descriptor)273 ResultOrError<PipelineLayoutBase*> Device::CreatePipelineLayoutImpl( 274 const PipelineLayoutDescriptor* descriptor) { 275 return new PipelineLayout(this, descriptor); 276 } CreateQueueImpl()277 ResultOrError<QueueBase*> Device::CreateQueueImpl() { 278 return new Queue(this); 279 } CreateRenderPipelineImpl(const RenderPipelineDescriptor * descriptor)280 ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl( 281 const RenderPipelineDescriptor* descriptor) { 282 return new RenderPipeline(this, descriptor); 283 } CreateSamplerImpl(const SamplerDescriptor * descriptor)284 ResultOrError<SamplerBase*> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) { 285 return new Sampler(this, descriptor); 286 } CreateShaderModuleImpl(const ShaderModuleDescriptor * descriptor)287 ResultOrError<ShaderModuleBase*> Device::CreateShaderModuleImpl( 288 const ShaderModuleDescriptor* descriptor) { 289 return new ShaderModule(this, descriptor); 290 } CreateSwapChainImpl(const SwapChainDescriptor * descriptor)291 ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl( 292 const SwapChainDescriptor* descriptor) { 293 return new SwapChain(this, descriptor); 294 } CreateTextureImpl(const TextureDescriptor * descriptor)295 ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { 296 return new Texture(this, descriptor); 297 } CreateTextureViewImpl(TextureBase * texture,const TextureViewDescriptor * descriptor)298 ResultOrError<TextureViewBase*> Device::CreateTextureViewImpl( 299 TextureBase* texture, 300 const TextureViewDescriptor* descriptor) { 301 return new TextureView(texture, descriptor); 302 } 303 CreateStagingBuffer(size_t size)304 ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) { 305 std::unique_ptr<StagingBufferBase> stagingBuffer = 306 std::make_unique<StagingBuffer>(size, this); 307 return std::move(stagingBuffer); 308 } 309 CopyFromStagingToBuffer(StagingBufferBase * source,uint64_t sourceOffset,BufferBase * destination,uint64_t destinationOffset,uint64_t size)310 MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, 311 uint64_t sourceOffset, 312 BufferBase* destination, 313 uint64_t destinationOffset, 314 uint64_t size) { 315 ToBackend(destination) 316 ->TransitionUsageNow(GetPendingCommandList(), dawn::BufferUsageBit::CopyDst); 317 318 GetPendingCommandList()->CopyBufferRegion( 319 ToBackend(destination)->GetD3D12Resource().Get(), destinationOffset, 320 ToBackend(source)->GetResource(), sourceOffset, size); 321 322 return {}; 323 } 324 325 }} // namespace dawn_native::d3d12 326