Add d3d12 resource uploader to create and manage uploading resource lifetimes
This commit is contained in:
parent
d251356783
commit
b947993e1a
|
@ -230,6 +230,8 @@ if (WIN32)
|
|||
${D3D12_DIR}/PipelineLayoutD3D12.h
|
||||
${D3D12_DIR}/QueueD3D12.cpp
|
||||
${D3D12_DIR}/QueueD3D12.h
|
||||
${D3D12_DIR}/ResourceUploader.cpp
|
||||
${D3D12_DIR}/ResourceUploader.h
|
||||
${D3D12_DIR}/ShaderModuleD3D12.cpp
|
||||
${D3D12_DIR}/ShaderModuleD3D12.h
|
||||
)
|
||||
|
|
|
@ -59,41 +59,22 @@ namespace d3d12 {
|
|||
resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
{
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 0;
|
||||
heapProperties.VisibleNodeMask = 0;
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 0;
|
||||
heapProperties.VisibleNodeMask = 0;
|
||||
|
||||
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDescriptor,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&uploadResource)
|
||||
));
|
||||
}
|
||||
|
||||
{
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 0;
|
||||
heapProperties.VisibleNodeMask = 0;
|
||||
|
||||
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDescriptor,
|
||||
D3D12BufferUsage(GetUsage()),
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&resource)
|
||||
));
|
||||
}
|
||||
// TODO(enga@google.com): Use a ResourceAllocationManager
|
||||
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDescriptor,
|
||||
D3D12BufferUsage(GetUsage()),
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&resource)
|
||||
));
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
|
||||
|
@ -123,25 +104,7 @@ namespace d3d12 {
|
|||
}
|
||||
|
||||
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
|
||||
uint32_t begin = start * sizeof(uint32_t);
|
||||
uint32_t end = (start + count) * sizeof(uint32_t);
|
||||
|
||||
uint8_t* mappedResource = nullptr;
|
||||
|
||||
D3D12_RANGE readRange;
|
||||
readRange.Begin = 0;
|
||||
readRange.End = 0;
|
||||
|
||||
ASSERT_SUCCESS(uploadResource->Map(0, &readRange, reinterpret_cast<void**>(&mappedResource)));
|
||||
memcpy(&mappedResource[begin], data, end - begin);
|
||||
|
||||
D3D12_RANGE writeRange;
|
||||
writeRange.Begin = begin;
|
||||
writeRange.End = end;
|
||||
|
||||
uploadResource->Unmap(0, &writeRange);
|
||||
|
||||
device->GetPendingCommandList()->CopyBufferRegion(resource.Get(), begin, uploadResource.Get(), begin, end - begin);
|
||||
device->GetResourceUploader()->UploadToBuffer(resource, start * sizeof(uint32_t), count * sizeof(uint32_t), reinterpret_cast<const uint8_t*>(data));
|
||||
}
|
||||
|
||||
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace d3d12 {
|
|||
ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
Device::Device(ComPtr<ID3D12Device> d3d12Device) : d3d12Device(d3d12Device) {
|
||||
Device::Device(ComPtr<ID3D12Device> d3d12Device) : d3d12Device(d3d12Device), resourceUploader(this) {
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
|
@ -62,6 +62,10 @@ namespace d3d12 {
|
|||
nullptr,
|
||||
IID_PPV_ARGS(&pendingCommandList)
|
||||
));
|
||||
|
||||
ASSERT_SUCCESS(d3d12Device->CreateFence(serial++, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)));
|
||||
fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
ASSERT(fenceEvent != nullptr);
|
||||
}
|
||||
|
||||
Device::~Device() {
|
||||
|
@ -87,10 +91,53 @@ namespace d3d12 {
|
|||
return renderTargetDescriptor;
|
||||
}
|
||||
|
||||
ResourceUploader* Device::GetResourceUploader() {
|
||||
return &resourceUploader;
|
||||
}
|
||||
|
||||
void Device::SetNextRenderTargetDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor) {
|
||||
this->renderTargetDescriptor = renderTargetDescriptor;
|
||||
}
|
||||
|
||||
void Device::Tick() {
|
||||
// Execute any pending commands
|
||||
ASSERT_SUCCESS(pendingCommandList->Close());
|
||||
ID3D12CommandList* commandLists[] = { pendingCommandList.Get() };
|
||||
commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
|
||||
|
||||
// Update state tracking for objects requiring the serial
|
||||
resourceUploader.EnqueueUploadingResources(GetSerial() + 1);
|
||||
|
||||
IncrementSerial();
|
||||
|
||||
// Signal when the pending commands have finished
|
||||
ASSERT_SUCCESS(commandQueue->Signal(fence.Get(), GetSerial()));
|
||||
|
||||
// Handle objects awaiting GPU execution
|
||||
const uint64_t lastCompletedSerial = fence->GetCompletedValue();
|
||||
resourceUploader.FreeCompletedResources(lastCompletedSerial);
|
||||
|
||||
// TODO(enga@google.com): This will stall on the submit because
|
||||
// the commands must finish exeuting before the ID3D12CommandAllocator is reset.
|
||||
// This should be fixed / optimized by using multiple command allocators.
|
||||
const uint64_t currentFence = GetSerial();
|
||||
if (lastCompletedSerial < currentFence) {
|
||||
ASSERT_SUCCESS(fence->SetEventOnCompletion(currentFence, fenceEvent));
|
||||
WaitForSingleObject(fenceEvent, INFINITE);
|
||||
}
|
||||
|
||||
ASSERT_SUCCESS(pendingCommandAllocator->Reset());
|
||||
ASSERT_SUCCESS(pendingCommandList->Reset(pendingCommandAllocator.Get(), NULL));
|
||||
}
|
||||
|
||||
uint64_t Device::GetSerial() const {
|
||||
return serial;
|
||||
}
|
||||
|
||||
void Device::IncrementSerial() {
|
||||
serial++;
|
||||
}
|
||||
|
||||
BindGroupBase* Device::CreateBindGroup(BindGroupBuilder* builder) {
|
||||
return new BindGroup(this, builder);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "common/ToBackend.h"
|
||||
|
||||
#include "d3d12_platform.h"
|
||||
#include "ResourceUploader.h"
|
||||
|
||||
namespace backend {
|
||||
namespace d3d12 {
|
||||
|
@ -109,9 +110,14 @@ namespace d3d12 {
|
|||
ComPtr<ID3D12CommandAllocator> GetPendingCommandAllocator();
|
||||
ComPtr<ID3D12GraphicsCommandList> GetPendingCommandList();
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GetCurrentRenderTargetDescriptor();
|
||||
ResourceUploader* GetResourceUploader();
|
||||
|
||||
void SetNextRenderTargetDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor);
|
||||
|
||||
void Tick();
|
||||
uint64_t GetSerial() const;
|
||||
void IncrementSerial();
|
||||
|
||||
// NXT API
|
||||
void Reference();
|
||||
void Release();
|
||||
|
@ -122,6 +128,12 @@ namespace d3d12 {
|
|||
ComPtr<ID3D12CommandAllocator> pendingCommandAllocator;
|
||||
ComPtr<ID3D12GraphicsCommandList> pendingCommandList;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor;
|
||||
|
||||
ResourceUploader resourceUploader;
|
||||
|
||||
uint64_t serial = 0;
|
||||
ComPtr<ID3D12Fence> fence;
|
||||
HANDLE fenceEvent;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace d3d12 {
|
|||
nullptr,
|
||||
IID_PPV_ARGS(&commandList)
|
||||
));
|
||||
ASSERT_SUCCESS(commandList->Close());
|
||||
|
||||
ASSERT_SUCCESS(device->GetD3D12Device()->CreateFence(fenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)));
|
||||
fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
@ -38,22 +39,12 @@ namespace d3d12 {
|
|||
}
|
||||
|
||||
void Queue::Submit(uint32_t numCommands, CommandBuffer* const * commands) {
|
||||
ComPtr<ID3D12CommandAllocator> pendingCommandAllocator = device->GetPendingCommandAllocator();
|
||||
ComPtr<ID3D12GraphicsCommandList> pendingCommandList = device->GetPendingCommandList();
|
||||
ASSERT_SUCCESS(pendingCommandList->Close());
|
||||
device->Tick();
|
||||
|
||||
for (uint32_t i = 0; i < numCommands; ++i) {
|
||||
commands[i]->FillCommands(commandList);
|
||||
}
|
||||
ASSERT_SUCCESS(commandList->Close());
|
||||
|
||||
ID3D12CommandList* commandLists[] = { pendingCommandList.Get(), commandList.Get() };
|
||||
device->GetCommandQueue()->ExecuteCommandLists(_countof(commandLists), commandLists);
|
||||
|
||||
// TODO(enga@google.com): This will stall on the submit because
|
||||
// TODO(enga@google.com): This will stall on the previous submit because
|
||||
// the commands must finish exeuting before the ID3D12CommandAllocator is reset.
|
||||
// This should be fixed / optimized by using multiple command allocators.
|
||||
const uint64_t currentFence = ++fenceValue;
|
||||
const uint64_t currentFence = fenceValue++;
|
||||
ASSERT_SUCCESS(device->GetCommandQueue()->Signal(fence.Get(), fenceValue));
|
||||
|
||||
if (fence->GetCompletedValue() < currentFence) {
|
||||
|
@ -62,9 +53,15 @@ namespace d3d12 {
|
|||
}
|
||||
|
||||
ASSERT_SUCCESS(commandAllocator->Reset());
|
||||
ASSERT_SUCCESS(pendingCommandAllocator->Reset());
|
||||
ASSERT_SUCCESS(pendingCommandList->Reset(pendingCommandAllocator.Get(), NULL));
|
||||
ASSERT_SUCCESS(commandList->Reset(commandAllocator.Get(), NULL));
|
||||
|
||||
for (uint32_t i = 0; i < numCommands; ++i) {
|
||||
commands[i]->FillCommands(commandList);
|
||||
}
|
||||
ASSERT_SUCCESS(commandList->Close());
|
||||
|
||||
ID3D12CommandList* commandLists[] = { commandList.Get() };
|
||||
device->GetCommandQueue()->ExecuteCommandLists(_countof(commandLists), commandLists);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2017 The NXT Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "ResourceUploader.h"
|
||||
|
||||
#include "D3D12Backend.h"
|
||||
|
||||
namespace backend {
|
||||
namespace d3d12 {
|
||||
|
||||
ResourceUploader::ResourceUploader(Device* device) : device(device) {
|
||||
}
|
||||
|
||||
void ResourceUploader::UploadToBuffer(ComPtr<ID3D12Resource> resource, uint32_t start, uint32_t count, const uint8_t* data) {
|
||||
D3D12_RESOURCE_DESC resourceDescriptor;
|
||||
resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDescriptor.Alignment = 0;
|
||||
resourceDescriptor.Width = count;
|
||||
resourceDescriptor.Height = 1;
|
||||
resourceDescriptor.DepthOrArraySize = 1;
|
||||
resourceDescriptor.MipLevels = 1;
|
||||
resourceDescriptor.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDescriptor.SampleDesc.Count = 1;
|
||||
resourceDescriptor.SampleDesc.Quality = 0;
|
||||
resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
ComPtr<ID3D12Resource> uploadResource;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 0;
|
||||
heapProperties.VisibleNodeMask = 0;
|
||||
|
||||
// TODO(enga@google.com): Use a ResourceAllocationManager
|
||||
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDescriptor,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&uploadResource)
|
||||
));
|
||||
|
||||
D3D12_RANGE readRange;
|
||||
readRange.Begin = 0;
|
||||
readRange.End = 0;
|
||||
|
||||
D3D12_RANGE writeRange;
|
||||
writeRange.Begin = 0;
|
||||
writeRange.End = count;
|
||||
|
||||
uint8_t* mappedResource = nullptr;
|
||||
|
||||
ASSERT_SUCCESS(uploadResource->Map(0, &readRange, reinterpret_cast<void**>(&mappedResource)));
|
||||
memcpy(mappedResource, data, count);
|
||||
uploadResource->Unmap(0, &writeRange);
|
||||
device->GetPendingCommandList()->CopyBufferRegion(resource.Get(), start, uploadResource.Get(), 0, count);
|
||||
|
||||
pendingResources.push_back(uploadResource);
|
||||
}
|
||||
|
||||
void ResourceUploader::EnqueueUploadingResources(const uint64_t serial) {
|
||||
if (pendingResources.size() > 0) {
|
||||
uploadingResources.push_back(std::make_pair(serial, std::move(pendingResources)));
|
||||
pendingResources.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceUploader::FreeCompletedResources(const uint64_t lastCompletedSerial) {
|
||||
auto it = uploadingResources.begin();
|
||||
while (it != uploadingResources.end()) {
|
||||
if (it->first < lastCompletedSerial) {
|
||||
it++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uploadingResources.erase(uploadingResources.begin(), it);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2017 The NXT Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef BACKEND_D3D12_RESOURCEUPLOADER_H_
|
||||
#define BACKEND_D3D12_RESOURCEUPLOADER_H_
|
||||
|
||||
#include "d3d12_platform.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace backend {
|
||||
namespace d3d12 {
|
||||
|
||||
class Device;
|
||||
|
||||
class ResourceUploader {
|
||||
public:
|
||||
ResourceUploader(Device* device);
|
||||
|
||||
void UploadToBuffer(ComPtr<ID3D12Resource> resource, uint32_t start, uint32_t count, const uint8_t* data);
|
||||
void EnqueueUploadingResources(const uint64_t serial);
|
||||
void FreeCompletedResources(const uint64_t lastCompletedSerial);
|
||||
|
||||
private:
|
||||
Device* device;
|
||||
|
||||
std::vector<ComPtr<ID3D12Resource>> pendingResources;
|
||||
std::vector<std::pair<uint64_t, std::vector<ComPtr<ID3D12Resource>>>> uploadingResources;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BACKEND_D3D12_RESOURCEUPLOADER_H_
|
Loading…
Reference in New Issue