1 // Copyright 2020 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/SamplerHeapCacheD3D12.h" 16 17 #include "common/Assert.h" 18 #include "common/HashUtils.h" 19 #include "dawn_native/d3d12/BindGroupD3D12.h" 20 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" 21 #include "dawn_native/d3d12/DeviceD3D12.h" 22 #include "dawn_native/d3d12/Forward.h" 23 #include "dawn_native/d3d12/SamplerD3D12.h" 24 #include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" 25 #include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" 26 27 namespace dawn_native { namespace d3d12 { 28 SamplerHeapCacheEntry(std::vector<Sampler * > samplers)29 SamplerHeapCacheEntry::SamplerHeapCacheEntry(std::vector<Sampler*> samplers) 30 : mSamplers(std::move(samplers)) { 31 } 32 SamplerHeapCacheEntry(SamplerHeapCache * cache,StagingDescriptorAllocator * allocator,std::vector<Sampler * > samplers,CPUDescriptorHeapAllocation allocation)33 SamplerHeapCacheEntry::SamplerHeapCacheEntry(SamplerHeapCache* cache, 34 StagingDescriptorAllocator* allocator, 35 std::vector<Sampler*> samplers, 36 CPUDescriptorHeapAllocation allocation) 37 : mCPUAllocation(std::move(allocation)), 38 mSamplers(std::move(samplers)), 39 mAllocator(allocator), 40 mCache(cache) { 41 ASSERT(mCache != nullptr); 42 ASSERT(mCPUAllocation.IsValid()); 43 ASSERT(!mSamplers.empty()); 44 } 45 AcquireSamplers()46 std::vector<Sampler*>&& SamplerHeapCacheEntry::AcquireSamplers() { 47 return std::move(mSamplers); 48 } 49 ~SamplerHeapCacheEntry()50 SamplerHeapCacheEntry::~SamplerHeapCacheEntry() { 51 // If this is a blueprint then the CPU allocation cannot exist and has no entry to remove. 52 if (mCPUAllocation.IsValid()) { 53 mCache->RemoveCacheEntry(this); 54 mAllocator->Deallocate(&mCPUAllocation); 55 } 56 57 ASSERT(!mCPUAllocation.IsValid()); 58 } 59 Populate(Device * device,ShaderVisibleDescriptorAllocator * allocator)60 bool SamplerHeapCacheEntry::Populate(Device* device, 61 ShaderVisibleDescriptorAllocator* allocator) { 62 if (allocator->IsAllocationStillValid(mGPUAllocation)) { 63 return true; 64 } 65 66 ASSERT(!mSamplers.empty()); 67 68 // Attempt to allocate descriptors for the currently bound shader-visible heaps. 69 // If either failed, return early to re-allocate and switch the heaps. 70 const uint32_t descriptorCount = mSamplers.size(); 71 D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor; 72 if (!allocator->AllocateGPUDescriptors(descriptorCount, device->GetPendingCommandSerial(), 73 &baseCPUDescriptor, &mGPUAllocation)) { 74 return false; 75 } 76 77 // CPU bindgroups are sparsely allocated across CPU heaps. Instead of doing 78 // simple copies per bindgroup, a single non-simple copy could be issued. 79 // TODO(dawn:155): Consider doing this optimization. 80 device->GetD3D12Device()->CopyDescriptorsSimple(descriptorCount, baseCPUDescriptor, 81 mCPUAllocation.GetBaseDescriptor(), 82 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); 83 84 return true; 85 } 86 GetBaseDescriptor() const87 D3D12_GPU_DESCRIPTOR_HANDLE SamplerHeapCacheEntry::GetBaseDescriptor() const { 88 return mGPUAllocation.GetBaseDescriptor(); 89 } 90 GetOrCreate(const BindGroup * group,StagingDescriptorAllocator * samplerAllocator)91 ResultOrError<Ref<SamplerHeapCacheEntry>> SamplerHeapCache::GetOrCreate( 92 const BindGroup* group, 93 StagingDescriptorAllocator* samplerAllocator) { 94 const BindGroupLayout* bgl = ToBackend(group->GetLayout()); 95 96 // If a previously created bindgroup used the same samplers, the backing sampler heap 97 // allocation can be reused. The packed list of samplers acts as the key to lookup the 98 // allocation in a cache. 99 // TODO(dawn:155): Avoid re-allocating the vector each lookup. 100 std::vector<Sampler*> samplers; 101 samplers.reserve(bgl->GetSamplerDescriptorCount()); 102 103 for (BindingIndex bindingIndex = bgl->GetDynamicBufferCount(); 104 bindingIndex < bgl->GetBindingCount(); ++bindingIndex) { 105 const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex); 106 if (bindingInfo.bindingType == BindingInfoType::Sampler) { 107 samplers.push_back(ToBackend(group->GetBindingAsSampler(bindingIndex))); 108 } 109 } 110 111 // Check the cache if there exists a sampler heap allocation that corresponds to the 112 // samplers. 113 SamplerHeapCacheEntry blueprint(std::move(samplers)); 114 auto iter = mCache.find(&blueprint); 115 if (iter != mCache.end()) { 116 return Ref<SamplerHeapCacheEntry>(*iter); 117 } 118 119 // Steal the sampler vector back from the blueprint to avoid creating a new copy for the 120 // real entry below. 121 samplers = std::move(blueprint.AcquireSamplers()); 122 123 CPUDescriptorHeapAllocation allocation; 124 DAWN_TRY_ASSIGN(allocation, samplerAllocator->AllocateCPUDescriptors()); 125 126 const uint32_t samplerSizeIncrement = samplerAllocator->GetSizeIncrement(); 127 ID3D12Device* d3d12Device = mDevice->GetD3D12Device(); 128 129 for (uint32_t i = 0; i < samplers.size(); ++i) { 130 const auto& samplerDesc = samplers[i]->GetSamplerDescriptor(); 131 d3d12Device->CreateSampler(&samplerDesc, 132 allocation.OffsetFrom(samplerSizeIncrement, i)); 133 } 134 135 Ref<SamplerHeapCacheEntry> entry = AcquireRef(new SamplerHeapCacheEntry( 136 this, samplerAllocator, std::move(samplers), std::move(allocation))); 137 mCache.insert(entry.Get()); 138 return std::move(entry); 139 } 140 SamplerHeapCache(Device * device)141 SamplerHeapCache::SamplerHeapCache(Device* device) : mDevice(device) { 142 } 143 ~SamplerHeapCache()144 SamplerHeapCache::~SamplerHeapCache() { 145 ASSERT(mCache.empty()); 146 } 147 RemoveCacheEntry(SamplerHeapCacheEntry * entry)148 void SamplerHeapCache::RemoveCacheEntry(SamplerHeapCacheEntry* entry) { 149 ASSERT(entry->GetRefCountForTesting() == 0); 150 size_t removedCount = mCache.erase(entry); 151 ASSERT(removedCount == 1); 152 } 153 operator ()(const SamplerHeapCacheEntry * entry) const154 size_t SamplerHeapCacheEntry::HashFunc::operator()(const SamplerHeapCacheEntry* entry) const { 155 size_t hash = 0; 156 for (const Sampler* sampler : entry->mSamplers) { 157 HashCombine(&hash, sampler); 158 } 159 return hash; 160 } 161 operator ()(const SamplerHeapCacheEntry * a,const SamplerHeapCacheEntry * b) const162 bool SamplerHeapCacheEntry::EqualityFunc::operator()(const SamplerHeapCacheEntry* a, 163 const SamplerHeapCacheEntry* b) const { 164 return a->mSamplers == b->mSamplers; 165 } 166 }} // namespace dawn_native::d3d12 167