• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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/ResourceAllocatorManagerD3D12.h"
16 
17 #include "dawn_native/d3d12/D3D12Error.h"
18 #include "dawn_native/d3d12/DeviceD3D12.h"
19 #include "dawn_native/d3d12/HeapAllocatorD3D12.h"
20 #include "dawn_native/d3d12/HeapD3D12.h"
21 #include "dawn_native/d3d12/ResidencyManagerD3D12.h"
22 #include "dawn_native/d3d12/UtilsD3D12.h"
23 
24 namespace dawn_native { namespace d3d12 {
25     namespace {
GetMemorySegment(Device * device,D3D12_HEAP_TYPE heapType)26         MemorySegment GetMemorySegment(Device* device, D3D12_HEAP_TYPE heapType) {
27             if (device->GetDeviceInfo().isUMA) {
28                 return MemorySegment::Local;
29             }
30 
31             D3D12_HEAP_PROPERTIES heapProperties =
32                 device->GetD3D12Device()->GetCustomHeapProperties(0, heapType);
33 
34             if (heapProperties.MemoryPoolPreference == D3D12_MEMORY_POOL_L1) {
35                 return MemorySegment::Local;
36             }
37 
38             return MemorySegment::NonLocal;
39         }
40 
GetD3D12HeapType(ResourceHeapKind resourceHeapKind)41         D3D12_HEAP_TYPE GetD3D12HeapType(ResourceHeapKind resourceHeapKind) {
42             switch (resourceHeapKind) {
43                 case Readback_OnlyBuffers:
44                 case Readback_AllBuffersAndTextures:
45                     return D3D12_HEAP_TYPE_READBACK;
46                 case Default_AllBuffersAndTextures:
47                 case Default_OnlyBuffers:
48                 case Default_OnlyNonRenderableOrDepthTextures:
49                 case Default_OnlyRenderableOrDepthTextures:
50                     return D3D12_HEAP_TYPE_DEFAULT;
51                 case Upload_OnlyBuffers:
52                 case Upload_AllBuffersAndTextures:
53                     return D3D12_HEAP_TYPE_UPLOAD;
54                 case EnumCount:
55                     UNREACHABLE();
56             }
57         }
58 
GetD3D12HeapFlags(ResourceHeapKind resourceHeapKind)59         D3D12_HEAP_FLAGS GetD3D12HeapFlags(ResourceHeapKind resourceHeapKind) {
60             switch (resourceHeapKind) {
61                 case Default_AllBuffersAndTextures:
62                 case Readback_AllBuffersAndTextures:
63                 case Upload_AllBuffersAndTextures:
64                     return D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES;
65                 case Default_OnlyBuffers:
66                 case Readback_OnlyBuffers:
67                 case Upload_OnlyBuffers:
68                     return D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
69                 case Default_OnlyNonRenderableOrDepthTextures:
70                     return D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
71                 case Default_OnlyRenderableOrDepthTextures:
72                     return D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
73                 case EnumCount:
74                     UNREACHABLE();
75             }
76         }
77 
GetResourceHeapKind(D3D12_RESOURCE_DIMENSION dimension,D3D12_HEAP_TYPE heapType,D3D12_RESOURCE_FLAGS flags,uint32_t resourceHeapTier)78         ResourceHeapKind GetResourceHeapKind(D3D12_RESOURCE_DIMENSION dimension,
79                                              D3D12_HEAP_TYPE heapType,
80                                              D3D12_RESOURCE_FLAGS flags,
81                                              uint32_t resourceHeapTier) {
82             if (resourceHeapTier >= 2) {
83                 switch (heapType) {
84                     case D3D12_HEAP_TYPE_UPLOAD:
85                         return Upload_AllBuffersAndTextures;
86                     case D3D12_HEAP_TYPE_DEFAULT:
87                         return Default_AllBuffersAndTextures;
88                     case D3D12_HEAP_TYPE_READBACK:
89                         return Readback_AllBuffersAndTextures;
90                     default:
91                         UNREACHABLE();
92                 }
93             }
94 
95             switch (dimension) {
96                 case D3D12_RESOURCE_DIMENSION_BUFFER: {
97                     switch (heapType) {
98                         case D3D12_HEAP_TYPE_UPLOAD:
99                             return Upload_OnlyBuffers;
100                         case D3D12_HEAP_TYPE_DEFAULT:
101                             return Default_OnlyBuffers;
102                         case D3D12_HEAP_TYPE_READBACK:
103                             return Readback_OnlyBuffers;
104                         default:
105                             UNREACHABLE();
106                     }
107                     break;
108                 }
109                 case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
110                 case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
111                 case D3D12_RESOURCE_DIMENSION_TEXTURE3D: {
112                     switch (heapType) {
113                         case D3D12_HEAP_TYPE_DEFAULT: {
114                             if ((flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ||
115                                 (flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) {
116                                 return Default_OnlyRenderableOrDepthTextures;
117                             }
118                             return Default_OnlyNonRenderableOrDepthTextures;
119                         }
120 
121                         default:
122                             UNREACHABLE();
123                     }
124                     break;
125                 }
126                 default:
127                     UNREACHABLE();
128             }
129         }
130 
GetResourcePlacementAlignment(ResourceHeapKind resourceHeapKind,uint32_t sampleCount,uint64_t requestedAlignment)131         uint64_t GetResourcePlacementAlignment(ResourceHeapKind resourceHeapKind,
132                                                uint32_t sampleCount,
133                                                uint64_t requestedAlignment) {
134             switch (resourceHeapKind) {
135                 // Small resources can take advantage of smaller alignments. For example,
136                 // if the most detailed mip can fit under 64KB, 4KB alignments can be used.
137                 // Must be non-depth or without render-target to use small resource alignment.
138                 // This also applies to MSAA textures (4MB => 64KB).
139                 //
140                 // Note: Only known to be used for small textures; however, MSDN suggests
141                 // it could be extended for more cases. If so, this could default to always
142                 // attempt small resource placement.
143                 // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_resource_desc
144                 case Default_OnlyNonRenderableOrDepthTextures:
145                     return (sampleCount > 1) ? D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT
146                                              : D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;
147                 default:
148                     return requestedAlignment;
149             }
150         }
151 
IsClearValueOptimizable(const D3D12_RESOURCE_DESC & resourceDescriptor)152         bool IsClearValueOptimizable(const D3D12_RESOURCE_DESC& resourceDescriptor) {
153             // Optimized clear color cannot be set on buffers, non-render-target/depth-stencil
154             // textures, or typeless resources
155             // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createcommittedresource
156             // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource
157             return !IsTypeless(resourceDescriptor.Format) &&
158                    resourceDescriptor.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER &&
159                    (resourceDescriptor.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET |
160                                                 D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;
161         }
162 
163     }  // namespace
164 
ResourceAllocatorManager(Device * device)165     ResourceAllocatorManager::ResourceAllocatorManager(Device* device) : mDevice(device) {
166         mResourceHeapTier = (mDevice->IsToggleEnabled(Toggle::UseD3D12ResourceHeapTier2))
167                                 ? mDevice->GetDeviceInfo().resourceHeapTier
168                                 : 1;
169 
170         for (uint32_t i = 0; i < ResourceHeapKind::EnumCount; i++) {
171             const ResourceHeapKind resourceHeapKind = static_cast<ResourceHeapKind>(i);
172             mHeapAllocators[i] = std::make_unique<HeapAllocator>(
173                 mDevice, GetD3D12HeapType(resourceHeapKind), GetD3D12HeapFlags(resourceHeapKind),
174                 GetMemorySegment(device, GetD3D12HeapType(resourceHeapKind)));
175             mPooledHeapAllocators[i] =
176                 std::make_unique<PooledResourceMemoryAllocator>(mHeapAllocators[i].get());
177             mSubAllocatedResourceAllocators[i] = std::make_unique<BuddyMemoryAllocator>(
178                 kMaxHeapSize, kMinHeapSize, mPooledHeapAllocators[i].get());
179         }
180     }
181 
AllocateMemory(D3D12_HEAP_TYPE heapType,const D3D12_RESOURCE_DESC & resourceDescriptor,D3D12_RESOURCE_STATES initialUsage)182     ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::AllocateMemory(
183         D3D12_HEAP_TYPE heapType,
184         const D3D12_RESOURCE_DESC& resourceDescriptor,
185         D3D12_RESOURCE_STATES initialUsage) {
186         // In order to suppress a warning in the D3D12 debug layer, we need to specify an
187         // optimized clear value. As there are no negative consequences when picking a mismatched
188         // clear value, we use zero as the optimized clear value. This also enables fast clears on
189         // some architectures.
190         D3D12_CLEAR_VALUE zero{};
191         D3D12_CLEAR_VALUE* optimizedClearValue = nullptr;
192         if (IsClearValueOptimizable(resourceDescriptor)) {
193             zero.Format = resourceDescriptor.Format;
194             optimizedClearValue = &zero;
195         }
196 
197         // TODO(crbug.com/dawn/849): Conditionally disable sub-allocation.
198         // For very large resources, there is no benefit to suballocate.
199         // For very small resources, it is inefficent to suballocate given the min. heap
200         // size could be much larger then the resource allocation.
201         // Attempt to satisfy the request using sub-allocation (placed resource in a heap).
202         if (!mDevice->IsToggleEnabled(Toggle::DisableResourceSuballocation)) {
203             ResourceHeapAllocation subAllocation;
204             DAWN_TRY_ASSIGN(subAllocation, CreatePlacedResource(heapType, resourceDescriptor,
205                                                                 optimizedClearValue, initialUsage));
206             if (subAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) {
207                 return std::move(subAllocation);
208             }
209         }
210 
211         // If sub-allocation fails, fall-back to direct allocation (committed resource).
212         ResourceHeapAllocation directAllocation;
213         DAWN_TRY_ASSIGN(directAllocation,
214                         CreateCommittedResource(heapType, resourceDescriptor, optimizedClearValue,
215                                                 initialUsage));
216         if (directAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) {
217             return std::move(directAllocation);
218         }
219 
220         // If direct allocation fails, the system is probably out of memory.
221         return DAWN_OUT_OF_MEMORY_ERROR("Allocation failed");
222     }
223 
Tick(ExecutionSerial completedSerial)224     void ResourceAllocatorManager::Tick(ExecutionSerial completedSerial) {
225         for (ResourceHeapAllocation& allocation :
226              mAllocationsToDelete.IterateUpTo(completedSerial)) {
227             if (allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated) {
228                 FreeMemory(allocation);
229             }
230         }
231         mAllocationsToDelete.ClearUpTo(completedSerial);
232 
233         //dawn cve2022-2399
234         mHeapsToDelete.ClearUpTo(completedSerial);
235     }
236 
DeallocateMemory(ResourceHeapAllocation & allocation)237     void ResourceAllocatorManager::DeallocateMemory(ResourceHeapAllocation& allocation) {
238         if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) {
239             return;
240         }
241 
242         mAllocationsToDelete.Enqueue(allocation, mDevice->GetPendingCommandSerial());
243 
244         // Directly allocated ResourceHeapAllocations are created with a heap object that must be
245         // manually deleted upon deallocation. See ResourceAllocatorManager::CreateCommittedResource
246         // for more information. Acquire this heap as a unique_ptr and add it to the queue of heaps
247         // to delete. It cannot be deleted immediately because it may be in use by in-flight or
248         // pending commands.
249         if (allocation.GetInfo().mMethod == AllocationMethod::kDirect) {
250             //dawn cve2022-2399
251             mHeapsToDelete.Enqueue(std::unique_ptr<ResourceHeapBase>(allocation.GetResourceHeap()),
252                                    mDevice->GetPendingCommandSerial());
253         }
254 
255         // Invalidate the allocation immediately in case one accidentally
256         // calls DeallocateMemory again using the same allocation.
257         allocation.Invalidate();
258 
259         ASSERT(allocation.GetD3D12Resource() == nullptr);
260     }
261 
FreeMemory(ResourceHeapAllocation & allocation)262     void ResourceAllocatorManager::FreeMemory(ResourceHeapAllocation& allocation) {
263         ASSERT(allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated);
264 
265         D3D12_HEAP_PROPERTIES heapProp;
266         allocation.GetD3D12Resource()->GetHeapProperties(&heapProp, nullptr);
267 
268         const D3D12_RESOURCE_DESC resourceDescriptor = allocation.GetD3D12Resource()->GetDesc();
269 
270         const size_t resourceHeapKindIndex =
271             GetResourceHeapKind(resourceDescriptor.Dimension, heapProp.Type,
272                                 resourceDescriptor.Flags, mResourceHeapTier);
273 
274         mSubAllocatedResourceAllocators[resourceHeapKindIndex]->Deallocate(allocation);
275     }
276 
CreatePlacedResource(D3D12_HEAP_TYPE heapType,const D3D12_RESOURCE_DESC & requestedResourceDescriptor,const D3D12_CLEAR_VALUE * optimizedClearValue,D3D12_RESOURCE_STATES initialUsage)277     ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::CreatePlacedResource(
278         D3D12_HEAP_TYPE heapType,
279         const D3D12_RESOURCE_DESC& requestedResourceDescriptor,
280         const D3D12_CLEAR_VALUE* optimizedClearValue,
281         D3D12_RESOURCE_STATES initialUsage) {
282         const ResourceHeapKind resourceHeapKind =
283             GetResourceHeapKind(requestedResourceDescriptor.Dimension, heapType,
284                                 requestedResourceDescriptor.Flags, mResourceHeapTier);
285 
286         D3D12_RESOURCE_DESC resourceDescriptor = requestedResourceDescriptor;
287         resourceDescriptor.Alignment = GetResourcePlacementAlignment(
288             resourceHeapKind, requestedResourceDescriptor.SampleDesc.Count,
289             requestedResourceDescriptor.Alignment);
290 
291         // TODO(bryan.bernhart): Figure out how to compute the alignment without calling this
292         // twice.
293         D3D12_RESOURCE_ALLOCATION_INFO resourceInfo =
294             mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor);
295 
296         // If the requested resource alignment was rejected, let D3D tell us what the
297         // required alignment is for this resource.
298         if (resourceDescriptor.Alignment != 0 &&
299             resourceDescriptor.Alignment != resourceInfo.Alignment) {
300             resourceDescriptor.Alignment = 0;
301             resourceInfo =
302                 mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor);
303         }
304 
305         // If d3d tells us the resource size is invalid, treat the error as OOM.
306         // Otherwise, creating the resource could cause a device loss (too large).
307         // This is because NextPowerOfTwo(UINT64_MAX) overflows and proceeds to
308         // incorrectly allocate a mismatched size.
309         if (resourceInfo.SizeInBytes == 0 ||
310             resourceInfo.SizeInBytes == std::numeric_limits<uint64_t>::max()) {
311             return DAWN_OUT_OF_MEMORY_ERROR("Resource allocation size was invalid.");
312         }
313 
314         BuddyMemoryAllocator* allocator =
315             mSubAllocatedResourceAllocators[static_cast<size_t>(resourceHeapKind)].get();
316 
317         ResourceMemoryAllocation allocation;
318         DAWN_TRY_ASSIGN(allocation,
319                         allocator->Allocate(resourceInfo.SizeInBytes, resourceInfo.Alignment));
320         if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) {
321             return ResourceHeapAllocation{};  // invalid
322         }
323 
324         Heap* heap = ToBackend(allocation.GetResourceHeap());
325 
326         // Before calling CreatePlacedResource, we must ensure the target heap is resident.
327         // CreatePlacedResource will fail if it is not.
328         DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation(heap));
329 
330         // With placed resources, a single heap can be reused.
331         // The resource placed at an offset is only reclaimed
332         // upon Tick or after the last command list using the resource has completed
333         // on the GPU. This means the same physical memory is not reused
334         // within the same command-list and does not require additional synchronization (aliasing
335         // barrier).
336         // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource
337         ComPtr<ID3D12Resource> placedResource;
338         DAWN_TRY(CheckOutOfMemoryHRESULT(
339             mDevice->GetD3D12Device()->CreatePlacedResource(
340                 heap->GetD3D12Heap(), allocation.GetOffset(), &resourceDescriptor, initialUsage,
341                 optimizedClearValue, IID_PPV_ARGS(&placedResource)),
342             "ID3D12Device::CreatePlacedResource"));
343 
344         // After CreatePlacedResource has finished, the heap can be unlocked from residency. This
345         // will insert it into the residency LRU.
346         mDevice->GetResidencyManager()->UnlockAllocation(heap);
347 
348         return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(),
349                                       std::move(placedResource), heap};
350     }
351 
CreateCommittedResource(D3D12_HEAP_TYPE heapType,const D3D12_RESOURCE_DESC & resourceDescriptor,const D3D12_CLEAR_VALUE * optimizedClearValue,D3D12_RESOURCE_STATES initialUsage)352     ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::CreateCommittedResource(
353         D3D12_HEAP_TYPE heapType,
354         const D3D12_RESOURCE_DESC& resourceDescriptor,
355         const D3D12_CLEAR_VALUE* optimizedClearValue,
356         D3D12_RESOURCE_STATES initialUsage) {
357         D3D12_HEAP_PROPERTIES heapProperties;
358         heapProperties.Type = heapType;
359         heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
360         heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
361         heapProperties.CreationNodeMask = 0;
362         heapProperties.VisibleNodeMask = 0;
363 
364         // If d3d tells us the resource size is invalid, treat the error as OOM.
365         // Otherwise, creating the resource could cause a device loss (too large).
366         // This is because NextPowerOfTwo(UINT64_MAX) overflows and proceeds to
367         // incorrectly allocate a mismatched size.
368         D3D12_RESOURCE_ALLOCATION_INFO resourceInfo =
369             mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor);
370         if (resourceInfo.SizeInBytes == 0 ||
371             resourceInfo.SizeInBytes == std::numeric_limits<uint64_t>::max()) {
372             return DAWN_OUT_OF_MEMORY_ERROR("Resource allocation size was invalid.");
373         }
374 
375         if (resourceInfo.SizeInBytes > kMaxHeapSize) {
376             return ResourceHeapAllocation{};  // Invalid
377         }
378 
379         // CreateCommittedResource will implicitly make the created resource resident. We must
380         // ensure enough free memory exists before allocating to avoid an out-of-memory error when
381         // overcommitted.
382         DAWN_TRY(mDevice->GetResidencyManager()->EnsureCanAllocate(
383             resourceInfo.SizeInBytes, GetMemorySegment(mDevice, heapType)));
384 
385         // Note: Heap flags are inferred by the resource descriptor and do not need to be explicitly
386         // provided to CreateCommittedResource.
387         ComPtr<ID3D12Resource> committedResource;
388         DAWN_TRY(CheckOutOfMemoryHRESULT(
389             mDevice->GetD3D12Device()->CreateCommittedResource(
390                 &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDescriptor, initialUsage,
391                 optimizedClearValue, IID_PPV_ARGS(&committedResource)),
392             "ID3D12Device::CreateCommittedResource"));
393 
394         // When using CreateCommittedResource, D3D12 creates an implicit heap that contains the
395         // resource allocation. Because Dawn's memory residency management occurs at the resource
396         // heap granularity, every directly allocated ResourceHeapAllocation also stores a Heap
397         // object. This object is created manually, and must be deleted manually upon deallocation
398         // of the committed resource.
399         Heap* heap = new Heap(committedResource, GetMemorySegment(mDevice, heapType),
400                               resourceInfo.SizeInBytes);
401 
402         // Calling CreateCommittedResource implicitly calls MakeResident on the resource. We must
403         // track this to avoid calling MakeResident a second time.
404         mDevice->GetResidencyManager()->TrackResidentAllocation(heap);
405 
406         AllocationInfo info;
407         info.mMethod = AllocationMethod::kDirect;
408 
409         return ResourceHeapAllocation{info,
410                                       /*offset*/ 0, std::move(committedResource), heap};
411     }
412 
DestroyPool()413     void ResourceAllocatorManager::DestroyPool() {
414         for (auto& alloc : mPooledHeapAllocators) {
415             alloc->DestroyPool();
416         }
417     }
418 
419 }}  // namespace dawn_native::d3d12
420