blob: 101ca4b190c258baec6534bafd980e4fe4524132 [file] [log] [blame]
// Copyright 2020 The Dawn 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 "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
namespace dawn_native { namespace d3d12 {
// Check that d3d heap type enum correctly mirrors the type index used by the static arrays.
static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV == 0, "");
static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER == 1, "");
// Thresholds should be adjusted (lower == faster) to avoid tests taking too long to complete.
static constexpr const uint32_t kShaderVisibleSmallHeapSizes[] = {1024, 512};
uint32_t GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE heapType, bool useSmallSize) {
if (useSmallSize) {
return kShaderVisibleSmallHeapSizes[heapType];
}
switch (heapType) {
case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
return D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1;
case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
return D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE;
default:
UNREACHABLE();
}
}
D3D12_DESCRIPTOR_HEAP_FLAGS GetD3D12HeapFlags(D3D12_DESCRIPTOR_HEAP_TYPE heapType) {
switch (heapType) {
case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
return D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
default:
UNREACHABLE();
}
}
ShaderVisibleDescriptorAllocator::ShaderVisibleDescriptorAllocator(Device* device)
: mDevice(device),
mSizeIncrements{
device->GetD3D12Device()->GetDescriptorHandleIncrementSize(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV),
device->GetD3D12Device()->GetDescriptorHandleIncrementSize(
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER),
} {
}
MaybeError ShaderVisibleDescriptorAllocator::Initialize() {
ASSERT(mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV].heap.Get() == nullptr);
mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV].heapType =
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
ASSERT(mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER].heap.Get() == nullptr);
mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER].heapType =
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
DAWN_TRY(AllocateAndSwitchShaderVisibleHeaps());
return {};
}
MaybeError ShaderVisibleDescriptorAllocator::AllocateAndSwitchShaderVisibleHeaps() {
DAWN_TRY(AllocateGPUHeap(&mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]));
DAWN_TRY(AllocateGPUHeap(&mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]));
// Invalidate all bindgroup allocations on previously bound heaps by incrementing the heap
// serial. When a bindgroup attempts to re-populate, it will compare with its recorded
// heap serial.
mShaderVisibleHeapsSerial++;
return {};
}
ResultOrError<DescriptorHeapAllocation>
ShaderVisibleDescriptorAllocator::AllocateGPUDescriptors(uint32_t descriptorCount,
Serial pendingSerial,
D3D12_DESCRIPTOR_HEAP_TYPE heapType) {
ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
ASSERT(mShaderVisibleBuffers[heapType].heap != nullptr);
const uint64_t startOffset =
mShaderVisibleBuffers[heapType].allocator.Allocate(descriptorCount, pendingSerial);
if (startOffset == RingBufferAllocator::kInvalidOffset) {
return DescriptorHeapAllocation{}; // Invalid
}
ID3D12DescriptorHeap* descriptorHeap = mShaderVisibleBuffers[heapType].heap.Get();
D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor =
descriptorHeap->GetCPUDescriptorHandleForHeapStart();
baseCPUDescriptor.ptr += mSizeIncrements[heapType] * startOffset;
D3D12_GPU_DESCRIPTOR_HANDLE baseGPUDescriptor =
descriptorHeap->GetGPUDescriptorHandleForHeapStart();
baseGPUDescriptor.ptr += mSizeIncrements[heapType] * startOffset;
return DescriptorHeapAllocation{mSizeIncrements[heapType], baseCPUDescriptor,
baseGPUDescriptor};
}
std::array<ID3D12DescriptorHeap*, 2> ShaderVisibleDescriptorAllocator::GetShaderVisibleHeaps()
const {
return {mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV].heap.Get(),
mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER].heap.Get()};
}
void ShaderVisibleDescriptorAllocator::Tick(uint64_t completedSerial) {
for (uint32_t i = 0; i < mShaderVisibleBuffers.size(); i++) {
ASSERT(mShaderVisibleBuffers[i].heap != nullptr);
mShaderVisibleBuffers[i].allocator.Deallocate(completedSerial);
}
}
// Creates a GPU descriptor heap that manages descriptors in a FIFO queue.
MaybeError ShaderVisibleDescriptorAllocator::AllocateGPUHeap(
ShaderVisibleBuffer* shaderVisibleBuffer) {
ComPtr<ID3D12DescriptorHeap> heap;
// Return the switched out heap to the pool and retrieve the oldest heap that is no longer
// used by GPU. This maintains a heap buffer to avoid frequently re-creating heaps for heavy
// users.
// TODO(dawn:256): Consider periodically triming to avoid OOM.
if (shaderVisibleBuffer->heap != nullptr) {
shaderVisibleBuffer->pool.push_back(
{mDevice->GetPendingCommandSerial(), std::move(shaderVisibleBuffer->heap)});
}
// Recycle existing heap if possible.
if (!shaderVisibleBuffer->pool.empty() &&
shaderVisibleBuffer->pool.front().heapSerial <= mDevice->GetCompletedCommandSerial()) {
heap = std::move(shaderVisibleBuffer->pool.front().heap);
shaderVisibleBuffer->pool.pop_front();
}
const D3D12_DESCRIPTOR_HEAP_TYPE heapType = shaderVisibleBuffer->heapType;
// TODO(bryan.bernhart@intel.com): Allocating to max heap size wastes memory
// should the developer not allocate any bindings for the heap type.
// Consider dynamically re-sizing GPU heaps.
const uint32_t descriptorCount = GetD3D12ShaderVisibleHeapSize(
heapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
if (heap == nullptr) {
D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;
heapDescriptor.Type = heapType;
heapDescriptor.NumDescriptors = descriptorCount;
heapDescriptor.Flags = GetD3D12HeapFlags(heapType);
heapDescriptor.NodeMask = 0;
DAWN_TRY(CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreateDescriptorHeap(
&heapDescriptor, IID_PPV_ARGS(&heap)),
"ID3D12Device::CreateDescriptorHeap"));
}
// Create a FIFO buffer from the recently created heap.
shaderVisibleBuffer->heap = std::move(heap);
shaderVisibleBuffer->allocator = RingBufferAllocator(descriptorCount);
return {};
}
Serial ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapsSerial() const {
return mShaderVisibleHeapsSerial;
}
uint64_t ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapSizeForTesting(
D3D12_DESCRIPTOR_HEAP_TYPE heapType) const {
ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
return mShaderVisibleBuffers[heapType].allocator.GetSize();
}
ComPtr<ID3D12DescriptorHeap> ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapForTesting(
D3D12_DESCRIPTOR_HEAP_TYPE heapType) const {
ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
return mShaderVisibleBuffers[heapType].heap;
}
uint64_t ShaderVisibleDescriptorAllocator::GetShaderVisiblePoolSizeForTesting(
D3D12_DESCRIPTOR_HEAP_TYPE heapType) const {
ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
return mShaderVisibleBuffers[heapType].pool.size();
}
bool ShaderVisibleDescriptorAllocator::IsAllocationStillValid(Serial lastUsageSerial,
Serial heapSerial) const {
// Consider valid if allocated for the pending submit and the shader visible heaps
// have not switched over.
return (lastUsageSerial > mDevice->GetCompletedCommandSerial() &&
heapSerial == mShaderVisibleHeapsSerial);
}
}} // namespace dawn_native::d3d12