| // Copyright 2026 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "src/dawn/native/d3d12/ResourceTableD3D12.h" | |
| #include <utility> | |
| #include <vector> | |
| #include "src/dawn/common/Enumerator.h" | |
| #include "src/dawn/common/MatchVariant.h" | |
| #include "src/dawn/native/DynamicUploader.h" | |
| #include "src/dawn/native/Queue.h" | |
| #include "src/dawn/native/ResourceTableDefaultResources.h" | |
| #include "src/dawn/native/d3d/D3DError.h" | |
| #include "src/dawn/native/d3d12/DeviceD3D12.h" | |
| #include "src/dawn/native/d3d12/PipelineLayoutD3D12.h" | |
| #include "src/dawn/native/d3d12/SamplerD3D12.h" | |
| #include "src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" | |
| #include "src/dawn/native/d3d12/StagingDescriptorAllocatorD3D12.h" | |
| #include "src/dawn/native/d3d12/TextureD3D12.h" | |
| #include "src/utils/compiler.h" | |
| namespace dawn::native::d3d12 { | |
| // SamplerIndexPool manages a limited pool of sampler indices (e.g. 2048 on D3D12). It also | |
| // implements deduplication by returning the same sampler index for an input Sampler pointer, | |
| // internally tracking ref counts to return the index when all refs to the same Sampler are | |
| // released. Note that this assumes that the same Sampler pointer is returned for the same | |
| // SamplerDescriptor. | |
| class ResourceTable::SamplerIndexPool { | |
| public: | |
| using SamplerIndex = TypedInteger<struct SamplerIndexT, uint16_t>; | |
| explicit SamplerIndexPool(uint32_t numSamplers) { | |
| // Init unused sampler indices with 0..numSamplers-1 | |
| mUnused.resize(numSamplers); | |
| for (uint16_t i = 0; i < mUnused.size(); ++i) { | |
| mUnused[i] = SamplerIndex{i}; | |
| } | |
| } | |
| // Acquire a sampler index for the input sampler. Returns the index, and 'true' if this is a | |
| // newly acquired sampler index, 'false' if it has already been acquired before for the same | |
| // Sampler*. | |
| ResultOrError<std::pair<SamplerIndex, bool>> Acquire(raw_ptr<const SamplerBase> sampler) { | |
| auto [iter, inserted] = mSamplerToEntry.try_emplace(reinterpret_cast<uintptr_t>(&*sampler)); | |
| SamplerEntry& entry = iter->second; | |
| if (inserted) { | |
| // Grab a new one | |
| if (mUnused.empty()) { | |
| // TODO(crbug.com/512089609): Make this an Internal error as this is unrecoverable. | |
| return DAWN_VALIDATION_ERROR("No more unique sampler indices available"); | |
| } | |
| entry.index = mUnused.back(); | |
| mUnused.pop_back(); | |
| DAWN_ASSERT(entry.refCount == 0); | |
| } | |
| ++entry.refCount; | |
| return std::make_pair(entry.index, inserted); | |
| } | |
| // Release the sampler index for the input sampler. If it is the last one, it the sampler index | |
| // is returned to the pool. | |
| void Release(raw_ptr<const SamplerBase> sampler) { | |
| // If this is the last ref to this sampler, return the index to the pool | |
| auto iter = mSamplerToEntry.find(reinterpret_cast<uintptr_t>(&*sampler)); | |
| DAWN_ASSERT(iter != mSamplerToEntry.end()); | |
| auto& entry = iter->second; | |
| DAWN_ASSERT(entry.refCount > 0); | |
| if (--entry.refCount == 0) { | |
| mUnused.push_back(entry.index); | |
| mSamplerToEntry.erase(iter); | |
| } | |
| } | |
| private: | |
| // Unused (available) sampler indices, initialized with range [0, numSamplers-1] | |
| std::vector<SamplerIndex> mUnused; | |
| // Mapping of unique sampler to its index and its reference count (number of slots using it). | |
| // We use a uintptr_t instead of Sampler* to ensure no dereference is ever attempted. | |
| // Note that if a given sampler pointer is released, then acquired in the same update, it is | |
| // possible to get back a different sampler index. This should not pose a problem. | |
| struct SamplerEntry { | |
| SamplerIndex index; | |
| uint32_t refCount; | |
| }; | |
| absl::flat_hash_map<uintptr_t, SamplerEntry> mSamplerToEntry; | |
| }; | |
| // static | |
| ResultOrError<ResourceTable::DescriptorRanges> ResourceTable::GetDescriptorRanges( | |
| const PipelineLayout& layout) { | |
| // For SM 6.5-, we need to create one descriptor table with multiple overlapping ranges, | |
| // each in its own register space (group), per resource type. This is because HLSL does not | |
| // allow overlapping register ranges, and we need separate unbounded array types for each | |
| // resource type: HLSL example: | |
| // Texture2D<float4> TextureTable1[] : register(t0, space1); | |
| // Texture2D<uint4> TextureTable2[] : register(t0, space2); | |
| // ... | |
| // float4 color = TextureTable1[MyTextureIndex + 1].Load(int3(0,0,0)); | |
| // uint4 data = TextureTable2[MyTextureIndex + 2].Load(int3(0,0,0)); | |
| // | |
| // For SM 6.6+, we don't need any descriptor tables, but only a single shader-visible | |
| // descriptor heap to hold the resources, and shaders access the heap via built-in | |
| // ResourceDescriptorHeap. HLSL example: | |
| // Texture2D<float4> myTex = ResourceDescriptorHeap[MyTextureIndex + 1]; | |
| // Texture2D<uint4> myTex = ResourceDescriptorHeap[MyTextureIndex + 2]; | |
| // | |
| // TODO(crbug.com/480110521): Add support for the SM >= 6.6 path | |
| DescriptorRanges ranges; | |
| const uint32_t baseRegisterSpace = layout.GetBaseResourceTableRegisterSpace(); | |
| ityp::span<ResourceTableSlot, const tint::ResourceType> defaultResourceOrder = | |
| ResourceTableDefaultResources::GetOrder(); | |
| const uint32_t defaultNonSamplersCount = | |
| uint32_t{ResourceTableDefaultResources::GetNonSamplerCount()}; | |
| // The metadata storage buffer is bound to (kBaseResourceTableRegisterSpace, 0) | |
| ranges.cbvUavSrvs.push_back(D3D12_DESCRIPTOR_RANGE1{ | |
| .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV, | |
| .NumDescriptors = 1, | |
| .BaseShaderRegister = 0, | |
| .RegisterSpace = baseRegisterSpace, | |
| .Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE, | |
| // Metadata is at register (binding) 0, the rest of the arrays in this range overlap | |
| // starting at register (binding) 1. | |
| .OffsetInDescriptorsFromTableStart = 0, | |
| }); | |
| // Create multiple overlapping ranges for default resources, one per type, bound to | |
| // (1 + baseRegisterSpace + i, 0) | |
| for (auto [i, resourceType] : Enumerate(defaultResourceOrder)) { | |
| std::vector<D3D12_DESCRIPTOR_RANGE1>* range = nullptr; | |
| D3D12_DESCRIPTOR_RANGE_TYPE rangeType{}; | |
| UINT numDescriptors = 0; | |
| UINT offsetFromTableStart = 0; | |
| if (!tint::IsSampler(resourceType)) { | |
| range = &ranges.cbvUavSrvs; | |
| rangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; | |
| numDescriptors = kMaxResourceTableSize + defaultNonSamplersCount; | |
| // Force the same offset in the descriptor table to overlaps these ranges, | |
| // starting at offset 1 because metadata is in register (binding) 0. | |
| offsetFromTableStart = 1; | |
| } else { | |
| range = &ranges.samplers; | |
| rangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; | |
| // This size must includes both user and default samplers | |
| numDescriptors = D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE; | |
| // Force the same offset in the descriptor table to overlaps these ranges. | |
| // Unlike for SRVs, we can overlap them at register (binding) 0 since as there's | |
| // no metadata entry in this range. | |
| offsetFromTableStart = 0; | |
| } | |
| range->push_back(D3D12_DESCRIPTOR_RANGE1{ | |
| .RangeType = rangeType, | |
| .NumDescriptors = numDescriptors, | |
| // HLSL doesn't allow overlapping register ranges, so each one is in its own | |
| // space (group), and starts at register (binding) 0 with no other range | |
| // bound after it. | |
| .BaseShaderRegister = 0, | |
| // Offset by 1 because metadata is in space baseRegisterSpace | |
| .RegisterSpace = baseRegisterSpace + 1 + uint32_t{i}, | |
| // Volatile required for bindless | |
| .Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE, | |
| .OffsetInDescriptorsFromTableStart = offsetFromTableStart, | |
| }); | |
| } | |
| return ranges; | |
| } | |
| // static | |
| ResultOrError<Ref<ResourceTable>> ResourceTable::Create(Device* device, | |
| const ResourceTableDescriptor* descriptor) { | |
| Ref<ResourceTable> table = AcquireRef(new ResourceTable(device, descriptor)); | |
| DAWN_TRY(table->Initialize()); | |
| return table; | |
| } | |
| ResourceTable::~ResourceTable() = default; | |
| MaybeError ResourceTable::Initialize() { | |
| DAWN_TRY(ResourceTableBase::InitializeBase()); | |
| Device* device = ToBackend(GetDevice()); | |
| ID3D12Device* d3d12Device = device->GetD3D12Device(); | |
| // Allocate the CPU descriptor heaps | |
| const uint32_t apiSize = uint32_t{GetAPISize()}; | |
| const uint32_t defaultNonSamplersCount = | |
| uint32_t{ResourceTableDefaultResources::GetNonSamplerCount()}; | |
| const uint32_t numViewDescriptors = 1u + apiSize + defaultNonSamplersCount; | |
| DAWN_TRY_ASSIGN(mViewHeap, AllocateCPUHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, | |
| numViewDescriptors)); | |
| // Samplers are capped to D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE | |
| // TODO(crbug.com/499115444): Detect and OOM if user adds more than | |
| // (D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE - defaultSamplersCount) samplers to the table. | |
| const uint32_t defaultSamplersCount = | |
| uint32_t{ResourceTableDefaultResources::GetSamplerCount()}; | |
| const uint32_t numSamplerDescriptors = std::min<uint32_t>( | |
| apiSize + defaultSamplersCount, D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE); | |
| DAWN_TRY_ASSIGN(mSamplerHeap, AllocateCPUHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, | |
| numSamplerDescriptors)); | |
| // Create sampler index pool with the total number of available sampler descriptors | |
| mSamplerIndexPool = std::make_unique<SamplerIndexPool>(numSamplerDescriptors); | |
| // Resize space for all possible slots | |
| mSlotToSamplerIndex.resize(ResourceTableSlot{GetSizeWithDefaultResources()}, | |
| kInvalidSamplerIndex); | |
| // Only write the metadata buffer to the view heap initially, all the other bindings will be | |
| // written as needed when they are inserted in the ResourceTable. | |
| Buffer* metadataBuffer = ToBackend(GetMetadataBuffer()); | |
| ID3D12Resource* resource = metadataBuffer->GetD3D12Resource(); | |
| DAWN_ASSERT(resource != nullptr); | |
| // Like StorageBuffer, Tint outputs HLSL shaders for readonly storage buffer with | |
| // ByteAddressBuffer. So we must use D3D12_BUFFER_SRV_FLAG_RAW when making the SRV | |
| // descriptor. And it has similar requirement for format, element size, etc. | |
| D3D12_SHADER_RESOURCE_VIEW_DESC desc; | |
| desc.Format = DXGI_FORMAT_R32_TYPELESS; | |
| desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; | |
| desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; | |
| desc.Buffer.FirstElement = 0; | |
| desc.Buffer.NumElements = metadataBuffer->GetSize() / 4; | |
| desc.Buffer.StructureByteStride = 0; | |
| desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; | |
| uint32_t offsetInDescriptorCount = 0; // Metadata buffer is the first element in the table | |
| d3d12Device->CreateShaderResourceView( | |
| resource, &desc, | |
| mViewHeap.cpuAllocation.OffsetFrom(mViewHeap.sizeIncrement, offsetInDescriptorCount)); | |
| return {}; | |
| } | |
| // static | |
| ResultOrError<ResourceTable::Heap> ResourceTable::AllocateCPUHeap( | |
| Device* device, | |
| D3D12_DESCRIPTOR_HEAP_TYPE heapType, | |
| uint32_t descriptorCount) { | |
| ID3D12Device* d3d12Device = device->GetD3D12Device(); | |
| D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; | |
| heapDescriptor.Type = heapType; | |
| heapDescriptor.NumDescriptors = descriptorCount; | |
| heapDescriptor.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; | |
| heapDescriptor.NodeMask = 0; | |
| ComPtr<ID3D12DescriptorHeap> handle; | |
| DAWN_TRY(CheckHRESULT(d3d12Device->CreateDescriptorHeap(&heapDescriptor, IID_PPV_ARGS(&handle)), | |
| "ID3D12Device::CreateDescriptorHeap")); | |
| const D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor = { | |
| handle->GetCPUDescriptorHandleForHeapStart().ptr}; | |
| return ResourceTable::Heap{ | |
| .handle = std::move(handle), | |
| .cpuAllocation = CPUDescriptorHeapAllocation{baseCPUDescriptor, 0}, | |
| .gpuAllocation = {}, // Allocated by Populate* | |
| .sizeIncrement = d3d12Device->GetDescriptorHandleIncrementSize(heapType), | |
| .numDescriptors = descriptorCount, | |
| }; | |
| } | |
| // static | |
| void ResourceTable::FreeCPUHeap(ResourceTable::Heap& heap) { | |
| heap.handle.Reset(); | |
| heap.cpuAllocation.Invalidate(); | |
| heap.sizeIncrement = 0; | |
| heap.numDescriptors = 0; | |
| } | |
| MaybeError ResourceTable::ApplyPendingUpdates( | |
| CommandRecordingContext* recordingContext, | |
| const absl::flat_hash_set<TextureBase*>& writableTextures) { | |
| Updates updates = AcquireDirtySlotUpdates(writableTextures); | |
| // Transition and initialize all required textures | |
| if (!updates.texturesToTransition.empty()) { | |
| DAWN_TRY(TransitionResources(recordingContext, updates.texturesToTransition)); | |
| } | |
| // Update resource bindings before metadata to ensure mSlotToSamplerIndex is up-to-date | |
| if (!updates.resourceDiffs.empty()) { | |
| DAWN_TRY(UpdateResourceBindings(updates.resourceDiffs)); | |
| } | |
| if (!updates.metadataUpdates.empty()) { | |
| DAWN_TRY(UpdateMetadataBuffer(recordingContext, updates.metadataUpdates)); | |
| } | |
| return {}; | |
| } | |
| MaybeError ResourceTable::TransitionResources( | |
| CommandRecordingContext* recordingContext, | |
| const absl::flat_hash_set<Ref<TextureBase>>& textures) { | |
| for (const auto& texture : textures) { | |
| Texture* textureBackend = ToBackend(texture.Get()); | |
| DAWN_TRY(textureBackend->EnsureSubresourceContentInitialized( | |
| recordingContext, textureBackend->GetAllSubresources())); | |
| textureBackend->TrackUsageAndTransitionNow(recordingContext, | |
| wgpu::TextureUsage::TextureBinding, | |
| textureBackend->GetAllSubresources()); | |
| } | |
| return {}; | |
| } | |
| MaybeError ResourceTable::UpdateMetadataBuffer(CommandRecordingContext* recordingContext, | |
| const std::vector<MetadataUpdate>& updates) { | |
| // For metadata updates, we already created an SRV for metadata buffer in Initialize(), so all | |
| // we need to do is update the buffer. | |
| Device* device = ToBackend(GetDevice()); | |
| // Allocate enough space for all the data to modify and schedule the copies. | |
| return device->GetDynamicUploader()->WithUploadReservation( | |
| sizeof(uint32_t) * updates.size(), kCopyBufferToBufferOffsetAlignment, | |
| [&](UploadReservation reservation) -> MaybeError { | |
| uint32_t* stagedData = static_cast<uint32_t*>(reservation.mappedPointer); | |
| // The metadata buffer will be copied to. | |
| Buffer* metadataBuffer = ToBackend(GetMetadataBuffer()); | |
| DAWN_ASSERT(metadataBuffer->IsInitialized()); | |
| auto scopedUseMetadataBuffer = metadataBuffer->UseInternal(); | |
| metadataBuffer->TrackUsageAndTransitionNow(recordingContext, | |
| wgpu::BufferUsage::CopyDst); | |
| // Record a CopyBufferRegion for each update | |
| // TODO(crbug.com/473354062): reduce number of calls by copying contiguous regions | |
| for (auto [i, update] : Enumerate(updates)) { | |
| DAWN_ASSERT((update.data & 0xFFFF0000) == 0); // Only low 16 bits used for type id | |
| if (SamplerIndex samplerIndex = mSlotToSamplerIndex[update.slot]; | |
| samplerIndex != kInvalidSamplerIndex) { | |
| // Store sampler index in high 16 bits, type id in low 16 bits | |
| DAWN_UNSAFE_TODO(stagedData[i]) = update.data | (uint32_t{samplerIndex} << 16); | |
| } else { | |
| DAWN_UNSAFE_TODO(stagedData[i]) = update.data; // Copy to staged | |
| } | |
| // Copy staged to metadata buffer | |
| recordingContext->GetCommandList1()->CopyBufferRegion( | |
| metadataBuffer->GetD3D12Resource(), update.offset, | |
| ToBackend(reservation.buffer)->GetD3D12Resource(), | |
| reservation.offsetInBuffer + i * sizeof(uint32_t), sizeof(uint32_t)); | |
| } | |
| // Transition the buffer back to be used as storage as that's how it will be used for | |
| // shader-side validation. | |
| metadataBuffer->TrackUsageAndTransitionNow(recordingContext, kReadOnlyStorageBuffer); | |
| return {}; | |
| }); | |
| } | |
| MaybeError ResourceTable::UpdateResourceBindings(const std::vector<ResourceDiff>& diffs) { | |
| Device* device = ToBackend(GetDevice()); | |
| ID3D12Device* d3d12Device = device->GetD3D12Device(); | |
| for (const ResourceDiff& diff : diffs) { | |
| // TODO(https://issues.chromium.org/473444515): Support buffer, texel buffers and storage | |
| // textures. | |
| if (auto samplerBase = std::get_if<Ref<SamplerBase>>(&diff.removed)) { | |
| // Release this sampler's index. It'll be returned to the pool if this is the last ref. | |
| mSamplerIndexPool->Release(samplerBase->Get()); | |
| // Clear the table entry | |
| mSlotToSamplerIndex[diff.slot] = kInvalidSamplerIndex; | |
| } | |
| DAWN_TRY(MatchVariant( | |
| diff.added, | |
| [&](std::monostate) -> MaybeError { | |
| // Nothing to do. | |
| return {}; | |
| }, | |
| [&](Ref<TextureViewBase> textureView) -> MaybeError { | |
| auto view = ToBackend(textureView); | |
| ID3D12Resource* resource = ToBackend(view->GetTexture())->GetD3D12Resource(); | |
| if (resource == nullptr) { | |
| // Skip resource if it was destroyed | |
| return {}; | |
| } | |
| // Add 1 to skip metadata descriptor | |
| uint32_t offsetInDescriptorCount = 1 + static_cast<uint32_t>(diff.slot); | |
| d3d12Device->CreateShaderResourceView( | |
| resource, &view->GetSRVDescriptor(), | |
| mViewHeap.cpuAllocation.OffsetFrom(mViewHeap.sizeIncrement, | |
| offsetInDescriptorCount)); | |
| return {}; | |
| }, | |
| [&](Ref<SamplerBase> samplerBase) -> MaybeError { | |
| // Acquire a sampler index. If it's the first time we acquire one for this sampler, | |
| // create descriptor for it in the heap. | |
| std::pair<SamplerIndex, bool> result; | |
| DAWN_TRY_ASSIGN(result, mSamplerIndexPool->Acquire(samplerBase.Get())); | |
| auto [samplerIndex, isNewIndex] = result; | |
| if (isNewIndex) { | |
| const D3D12_SAMPLER_DESC& samplerDesc = | |
| ToBackend(samplerBase)->GetSamplerDescriptor(); | |
| uint32_t offsetInDescriptorCount = uint32_t{samplerIndex}; | |
| d3d12Device->CreateSampler( | |
| &samplerDesc, mSamplerHeap.cpuAllocation.OffsetFrom( | |
| mSamplerHeap.sizeIncrement, offsetInDescriptorCount)); | |
| } | |
| // Update the table entry | |
| mSlotToSamplerIndex[diff.slot] = samplerIndex; | |
| return {}; | |
| })); | |
| } | |
| return {}; | |
| } | |
| bool ResourceTable::PopulateViews(ShaderVisibleDescriptorAllocator* viewAllocator) { | |
| if (viewAllocator->IsAllocationStillValid(mViewHeap.gpuAllocation)) { | |
| return true; | |
| } | |
| // Attempt to allocate descriptors for the currently bound shader-visible heaps. | |
| // Return false if allocation fails to indicate that AllocateAndSwitchShaderVisibleHeap should | |
| // be called. | |
| Device* device = ToBackend(GetDevice()); | |
| D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor; | |
| if (!viewAllocator->AllocateGPUDescriptors(mViewHeap.numDescriptors, | |
| device->GetQueue()->GetPendingCommandSerial(), | |
| &baseCPUDescriptor, &mViewHeap.gpuAllocation)) { | |
| return false; | |
| } | |
| // CPU bindgroups are sparsely allocated across CPU heaps. Instead of doing | |
| // simple copies per bindgroup, a single non-simple copy could be issued. | |
| // TODO(dawn:155): Consider doing this optimization. | |
| device->GetD3D12Device()->CopyDescriptorsSimple(mViewHeap.numDescriptors, baseCPUDescriptor, | |
| mViewHeap.cpuAllocation.GetBaseDescriptor(), | |
| D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); | |
| return true; | |
| } | |
| bool ResourceTable::PopulateSamplers(ShaderVisibleDescriptorAllocator* samplerAllocator) { | |
| if (samplerAllocator->IsAllocationStillValid(mSamplerHeap.gpuAllocation)) { | |
| return true; | |
| } | |
| // Attempt to allocate descriptors for the currently bound shader-visible heaps. | |
| // Return false if allocation fails to indicate that AllocateAndSwitchShaderVisibleHeap should | |
| // be called. | |
| Device* device = ToBackend(GetDevice()); | |
| D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor; | |
| if (!samplerAllocator->AllocateGPUDescriptors( | |
| mSamplerHeap.numDescriptors, device->GetQueue()->GetPendingCommandSerial(), | |
| &baseCPUDescriptor, &mSamplerHeap.gpuAllocation)) { | |
| return false; | |
| } | |
| // CPU bindgroups are sparsely allocated across CPU heaps. Instead of doing | |
| // simple copies per bindgroup, a single non-simple copy could be issued. | |
| // TODO(dawn:155): Consider doing this optimization. | |
| device->GetD3D12Device()->CopyDescriptorsSimple(mSamplerHeap.numDescriptors, baseCPUDescriptor, | |
| mSamplerHeap.cpuAllocation.GetBaseDescriptor(), | |
| D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); | |
| return true; | |
| } | |
| uint32_t ResourceTable::GetViewDescriptorCount() const { | |
| return mViewHeap.numDescriptors; | |
| } | |
| uint32_t ResourceTable::GetSamplerDescriptorCount() const { | |
| return mSamplerHeap.numDescriptors; | |
| } | |
| D3D12_GPU_DESCRIPTOR_HANDLE ResourceTable::GetBaseViewDescriptor() const { | |
| return mViewHeap.gpuAllocation.GetBaseDescriptor(); | |
| } | |
| D3D12_GPU_DESCRIPTOR_HANDLE ResourceTable::GetBaseSamplerDescriptor() const { | |
| return mSamplerHeap.gpuAllocation.GetBaseDescriptor(); | |
| } | |
| void ResourceTable::DestroyImpl(DestroyReason reason) { | |
| ResourceTableBase::DestroyImpl(reason); | |
| FreeCPUHeap(mViewHeap); | |
| FreeCPUHeap(mSamplerHeap); | |
| mSlotToSamplerIndex.clear(); | |
| mSamplerIndexPool = nullptr; | |
| } | |
| void ResourceTable::SetLabelImpl() { | |
| // TODO(crbug.com/473354062): SetDebugName | |
| } | |
| } // namespace dawn::native::d3d12 |