1 /*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "d3d12_video_array_of_textures_dpb_manager.h"
25 #include <algorithm>
26 ///
27 /// d3d12_array_of_textures_dpb_manager
28 ///
29 // Differences with d3d12_texture_array_dpb_manager
30 // Uses an std::vector with individual D3D resources as backing storage instead of an D3D12 Texture Array
31 // Supports dynamic pool capacity extension (by pushing back a new D3D12Resource) of the pool
32
33 #include "d3d12_common.h"
34
35 #include "d3d12_util.h"
36
37 void
create_reconstructed_picture_allocations(ID3D12Resource ** ppResource)38 d3d12_array_of_textures_dpb_manager::create_reconstructed_picture_allocations(ID3D12Resource **ppResource)
39 {
40 D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT, m_nodeMask, m_nodeMask);
41
42 CD3DX12_RESOURCE_DESC reconstructedPictureResourceDesc = CD3DX12_RESOURCE_DESC::Tex2D(m_encodeFormat,
43 m_encodeResolution.Width,
44 m_encodeResolution.Height,
45 1,
46 1,
47 1,
48 0,
49 m_resourceAllocFlags);
50 HRESULT hr = m_pDevice->CreateCommittedResource(&Properties,
51 D3D12_HEAP_FLAG_NONE,
52 &reconstructedPictureResourceDesc,
53 D3D12_RESOURCE_STATE_COMMON,
54 nullptr,
55 IID_PPV_ARGS(ppResource));
56 if (FAILED(hr)) {
57 debug_printf("CreateCommittedResource failed with HR %x\n", hr);
58 assert(false);
59 }
60 }
61
d3d12_array_of_textures_dpb_manager(uint32_t dpbInitialSize,ID3D12Device * pDevice,DXGI_FORMAT encodeSessionFormat,D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC encodeSessionResolution,D3D12_RESOURCE_FLAGS resourceAllocFlags,bool setNullSubresourcesOnAllZero,uint32_t nodeMask,bool allocatePool)62 d3d12_array_of_textures_dpb_manager::d3d12_array_of_textures_dpb_manager(
63 uint32_t dpbInitialSize,
64 ID3D12Device * pDevice,
65 DXGI_FORMAT encodeSessionFormat,
66 D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC encodeSessionResolution,
67 D3D12_RESOURCE_FLAGS resourceAllocFlags,
68 bool setNullSubresourcesOnAllZero,
69 uint32_t nodeMask,
70 bool allocatePool)
71 : m_dpbInitialSize(dpbInitialSize),
72 m_pDevice(pDevice),
73 m_encodeFormat(encodeSessionFormat),
74 m_encodeResolution(encodeSessionResolution),
75 m_resourceAllocFlags(resourceAllocFlags),
76 m_NullSubresourcesOnAllZero(setNullSubresourcesOnAllZero),
77 m_nodeMask(nodeMask)
78 {
79 // Initialize D3D12 DPB exposed in this class implemented CRUD interface for a DPB
80 clear_decode_picture_buffer();
81
82 // Sometimes the client of this class can reuse allocations from an upper layer
83 // and doesn't need to get fresh/tracked allocations
84 if(allocatePool)
85 {
86 // Implement a reusable pool of D3D12 Resources as an array of textures
87 m_ResourcesPool.resize(m_dpbInitialSize);
88
89 // Build resource pool with commitedresources with a d3ddevice and the encoding session settings (eg. resolution) and
90 // the reference_only flag
91 for (auto &reusableRes : m_ResourcesPool) {
92 reusableRes.isFree = true;
93 create_reconstructed_picture_allocations(reusableRes.pResource.GetAddressOf());
94 }
95 }
96 }
97
98 uint32_t
clear_decode_picture_buffer()99 d3d12_array_of_textures_dpb_manager::clear_decode_picture_buffer()
100 {
101 uint32_t untrackCount = 0;
102 // Mark resources used in DPB as re-usable in the resources pool
103 for (auto &dpbResource : m_D3D12DPB.pResources) {
104 // Don't assert the untracking result here in case the DPB contains resources not adquired using the pool methods
105 // in this interface
106 untrackCount += untrack_reconstructed_picture_allocation({ dpbResource, 0 }) ? 1 : 0;
107 }
108
109 // Clear DPB
110 m_D3D12DPB.pResources.clear();
111 m_D3D12DPB.pSubresources.clear();
112 m_D3D12DPB.pHeaps.clear();
113 m_D3D12DPB.pResources.reserve(m_dpbInitialSize);
114 m_D3D12DPB.pSubresources.reserve(m_dpbInitialSize);
115 m_D3D12DPB.pHeaps.reserve(m_dpbInitialSize);
116
117 return untrackCount;
118 }
119
120 // Assigns a reference frame at a given position
121 void
assign_reference_frame(d3d12_video_reconstructed_picture pReconPicture,uint32_t dpbPosition)122 d3d12_array_of_textures_dpb_manager::assign_reference_frame(d3d12_video_reconstructed_picture pReconPicture,
123 uint32_t dpbPosition)
124 {
125 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
126 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
127
128 assert (dpbPosition < m_D3D12DPB.pResources.size());
129
130 m_D3D12DPB.pResources[dpbPosition] = pReconPicture.pReconstructedPicture;
131 m_D3D12DPB.pSubresources[dpbPosition] = pReconPicture.ReconstructedPictureSubresource;
132 m_D3D12DPB.pHeaps[dpbPosition] = pReconPicture.pVideoHeap;
133 }
134
135 // Adds a new reference frame at a given position
136 void
insert_reference_frame(d3d12_video_reconstructed_picture pReconPicture,uint32_t dpbPosition)137 d3d12_array_of_textures_dpb_manager::insert_reference_frame(d3d12_video_reconstructed_picture pReconPicture,
138 uint32_t dpbPosition)
139 {
140 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
141 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
142
143 if (dpbPosition > m_D3D12DPB.pResources.size()) {
144 // extend capacity
145 m_D3D12DPB.pResources.resize(dpbPosition);
146 m_D3D12DPB.pSubresources.resize(dpbPosition);
147 m_D3D12DPB.pHeaps.resize(dpbPosition);
148 }
149
150 m_D3D12DPB.pResources.insert(m_D3D12DPB.pResources.begin() + dpbPosition, pReconPicture.pReconstructedPicture);
151 m_D3D12DPB.pSubresources.insert(m_D3D12DPB.pSubresources.begin() + dpbPosition,
152 pReconPicture.ReconstructedPictureSubresource);
153 m_D3D12DPB.pHeaps.insert(m_D3D12DPB.pHeaps.begin() + dpbPosition, pReconPicture.pVideoHeap);
154 }
155
156 // Gets a reference frame at a given position
157 d3d12_video_reconstructed_picture
get_reference_frame(uint32_t dpbPosition)158 d3d12_array_of_textures_dpb_manager::get_reference_frame(uint32_t dpbPosition)
159 {
160 assert(dpbPosition < m_D3D12DPB.pResources.size());
161
162 d3d12_video_reconstructed_picture retVal = { m_D3D12DPB.pResources[dpbPosition],
163 m_D3D12DPB.pSubresources[dpbPosition],
164 m_D3D12DPB.pHeaps[dpbPosition] };
165
166 return retVal;
167 }
168
169 // Removes a new reference frame at a given position and returns operation success
170 bool
remove_reference_frame(uint32_t dpbPosition,bool * pResourceUntracked)171 d3d12_array_of_textures_dpb_manager::remove_reference_frame(uint32_t dpbPosition, bool *pResourceUntracked)
172 {
173 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
174 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
175
176 assert(dpbPosition < m_D3D12DPB.pResources.size());
177
178 // If removed resource came from resource pool, mark it as free
179 // to free it for a new usage
180 // Don't assert the untracking result here in case the DPB contains resources not adquired using the pool methods in
181 // this interface
182 bool resUntracked = untrack_reconstructed_picture_allocation({ m_D3D12DPB.pResources[dpbPosition], 0 });
183
184 if (pResourceUntracked != nullptr) {
185 *pResourceUntracked = resUntracked;
186 }
187
188 // Remove from DPB tables
189 m_D3D12DPB.pResources.erase(m_D3D12DPB.pResources.begin() + dpbPosition);
190 m_D3D12DPB.pSubresources.erase(m_D3D12DPB.pSubresources.begin() + dpbPosition);
191 m_D3D12DPB.pHeaps.erase(m_D3D12DPB.pHeaps.begin() + dpbPosition);
192
193 return true;
194 }
195
196 // Returns true if the trackedItem was allocated (and is being tracked) by this class
197 bool
is_tracked_allocation(d3d12_video_reconstructed_picture trackedItem)198 d3d12_array_of_textures_dpb_manager::is_tracked_allocation(d3d12_video_reconstructed_picture trackedItem)
199 {
200 for (auto &reusableRes : m_ResourcesPool) {
201 if (trackedItem.pReconstructedPicture == reusableRes.pResource.Get() && !reusableRes.isFree) {
202 return true;
203 }
204 }
205 return false;
206 }
207
208 // Returns whether it found the tracked resource on this instance pool tracking and was able to free it
209 bool
untrack_reconstructed_picture_allocation(d3d12_video_reconstructed_picture trackedItem)210 d3d12_array_of_textures_dpb_manager::untrack_reconstructed_picture_allocation(
211 d3d12_video_reconstructed_picture trackedItem)
212 {
213 for (auto &reusableRes : m_ResourcesPool) {
214 if (trackedItem.pReconstructedPicture == reusableRes.pResource.Get()) {
215 reusableRes.isFree = true;
216 return true;
217 }
218 }
219 return false;
220 }
221
222 // Returns a fresh resource for a new reconstructed picture to be written to
223 // this class implements the dpb allocations as an array of textures
224 d3d12_video_reconstructed_picture
get_new_tracked_picture_allocation()225 d3d12_array_of_textures_dpb_manager::get_new_tracked_picture_allocation()
226 {
227 d3d12_video_reconstructed_picture freshAllocation = { // pResource
228 nullptr,
229 // subresource
230 0
231 };
232
233 // Find first (if any) available resource to (re-)use
234 bool bAvailableResourceInPool = false;
235 for (auto &reusableRes : m_ResourcesPool) {
236 if (reusableRes.isFree) {
237 bAvailableResourceInPool = true;
238 freshAllocation.pReconstructedPicture = reusableRes.pResource.Get();
239 reusableRes.isFree = false;
240 break;
241 }
242 }
243
244 if (!bAvailableResourceInPool) {
245 // Expand resources pool by one
246 assert(m_ResourcesPool.size() < UINT32_MAX);
247 debug_printf(
248 "[d3d12_array_of_textures_dpb_manager] ID3D12Resource Pool capacity (%" PRIu32 ") exceeded - extending capacity "
249 "and appending new allocation at the end",
250 static_cast<uint32_t>(m_ResourcesPool.size()));
251 d3d12_reusable_resource newPoolEntry = {};
252 newPoolEntry.isFree = false;
253 create_reconstructed_picture_allocations(newPoolEntry.pResource.GetAddressOf());
254 m_ResourcesPool.push_back(newPoolEntry);
255
256 // Assign it to current ask
257 freshAllocation.pReconstructedPicture = newPoolEntry.pResource.Get();
258 }
259
260 return freshAllocation;
261 }
262
263 uint32_t
get_number_of_pics_in_dpb()264 d3d12_array_of_textures_dpb_manager::get_number_of_pics_in_dpb()
265 {
266 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
267 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
268
269 assert(m_D3D12DPB.pResources.size() < UINT32_MAX);
270 return static_cast<uint32_t>(m_D3D12DPB.pResources.size());
271 }
272
273 d3d12_video_reference_frames
get_current_reference_frames()274 d3d12_array_of_textures_dpb_manager::get_current_reference_frames()
275 {
276 // If all subresources are 0, the DPB is loaded with an array of individual textures, the D3D Encode API expects
277 // pSubresources to be null in this case The D3D Decode API expects it to be non-null even with all zeroes.
278 uint32_t *pSubresources = m_D3D12DPB.pSubresources.data();
279 if ((std::all_of(m_D3D12DPB.pSubresources.cbegin(), m_D3D12DPB.pSubresources.cend(), [](int i) { return i == 0; })) &&
280 m_NullSubresourcesOnAllZero) {
281 pSubresources = nullptr;
282 }
283
284 d3d12_video_reference_frames retVal = { get_number_of_pics_in_dpb(),
285 m_D3D12DPB.pResources.data(),
286 pSubresources,
287 m_D3D12DPB.pHeaps.data() };
288
289 return retVal;
290 }
291
292 // number of resources in the pool that are marked as in use
293 uint32_t
get_number_of_in_use_allocations()294 d3d12_array_of_textures_dpb_manager::get_number_of_in_use_allocations()
295 {
296 uint32_t countOfInUseResourcesInPool = 0;
297 for (auto &reusableRes : m_ResourcesPool) {
298 if (!reusableRes.isFree) {
299 countOfInUseResourcesInPool++;
300 }
301 }
302 return countOfInUseResourcesInPool;
303 }
304
305 // Returns the number of pictures currently stored in the DPB
306 uint32_t
get_number_of_tracked_allocations()307 d3d12_array_of_textures_dpb_manager::get_number_of_tracked_allocations()
308 {
309 assert(m_ResourcesPool.size() < UINT32_MAX);
310 return static_cast<uint32_t>(m_ResourcesPool.size());
311 }
312