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_texture_array_dpb_manager.h"
25
26 #include "d3d12_common.h"
27
28 #include "d3d12_util.h"
29
30 ///
31 /// d3d12_texture_array_dpb_manager
32 ///
33
34 // Differences with ArrayOfTextures
35 // Uses a D3D12 Texture Array instead of an std::vector with individual D3D resources as backing storage
36 // Doesn't support extension (by reallocation and copy) of the pool
37
38 void
create_reconstructed_picture_allocations(ID3D12Resource ** ppResource,uint16_t texArraySize)39 d3d12_texture_array_dpb_manager::create_reconstructed_picture_allocations(ID3D12Resource **ppResource,
40 uint16_t texArraySize)
41 {
42 if (texArraySize > 0) {
43 D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT, m_nodeMask, m_nodeMask);
44 CD3DX12_RESOURCE_DESC reconstructedPictureResourceDesc = CD3DX12_RESOURCE_DESC::Tex2D(m_encodeFormat,
45 m_encodeResolution.Width,
46 m_encodeResolution.Height,
47 texArraySize,
48 1,
49 1,
50 0,
51 m_resourceAllocFlags);
52
53 HRESULT hr = m_pDevice->CreateCommittedResource(&Properties,
54 D3D12_HEAP_FLAG_NONE,
55 &reconstructedPictureResourceDesc,
56 D3D12_RESOURCE_STATE_COMMON,
57 nullptr,
58 IID_PPV_ARGS(ppResource));
59 if (FAILED(hr)) {
60 debug_printf("CreateCommittedResource failed with HR %x\n", hr);
61 assert(false);
62 }
63 }
64 }
65
~d3d12_texture_array_dpb_manager()66 d3d12_texture_array_dpb_manager::~d3d12_texture_array_dpb_manager()
67 { }
68
d3d12_texture_array_dpb_manager(uint16_t dpbTextureArraySize,ID3D12Device * pDevice,DXGI_FORMAT encodeSessionFormat,D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC encodeSessionResolution,D3D12_RESOURCE_FLAGS resourceAllocFlags,uint32_t nodeMask)69 d3d12_texture_array_dpb_manager::d3d12_texture_array_dpb_manager(
70 uint16_t dpbTextureArraySize,
71 ID3D12Device * pDevice,
72 DXGI_FORMAT encodeSessionFormat,
73 D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC encodeSessionResolution,
74 D3D12_RESOURCE_FLAGS resourceAllocFlags,
75 uint32_t nodeMask)
76 : m_pDevice(pDevice),
77 m_encodeFormat(encodeSessionFormat),
78 m_encodeResolution(encodeSessionResolution),
79 m_dpbTextureArraySize(dpbTextureArraySize),
80 m_resourceAllocFlags(resourceAllocFlags),
81 m_nodeMask(nodeMask)
82 {
83 // Initialize D3D12 DPB exposed in this class implemented CRUD interface for a DPB
84 clear_decode_picture_buffer();
85
86 // Implement a reusable pool of D3D12 Resources as an array of textures
87 uint16_t poolFixedSize = m_dpbTextureArraySize;
88 m_ResourcesPool.resize(poolFixedSize);
89
90 // Build resource pool with commitedresources with a d3ddevice and the encoding session settings (eg. resolution) and
91 // the reference_only flag
92 create_reconstructed_picture_allocations(m_baseTexArrayResource.GetAddressOf(), poolFixedSize);
93
94 for (uint32_t idxSubres = 0; idxSubres < poolFixedSize; idxSubres++) {
95 m_ResourcesPool[idxSubres].pResource = m_baseTexArrayResource;
96 m_ResourcesPool[idxSubres].subresource = idxSubres;
97 m_ResourcesPool[idxSubres].isFree = true;
98 }
99 }
100
101 uint32_t
clear_decode_picture_buffer()102 d3d12_texture_array_dpb_manager::clear_decode_picture_buffer()
103 {
104 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
105
106 uint32_t untrackCount = 0;
107 // Mark resources used in DPB as re-usable in the resources pool
108 for (uint32_t idx = 0; idx < m_D3D12DPB.pResources.size(); idx++) {
109 // Don't assert the untracking result here in case the DPB contains resources not adquired using the pool methods
110 // in this interface
111 untrackCount +=
112 untrack_reconstructed_picture_allocation({ m_D3D12DPB.pResources[idx], m_D3D12DPB.pSubresources[idx] }) ? 1 :
113 0;
114 }
115
116 // Clear DPB
117 m_D3D12DPB.pResources.clear();
118 m_D3D12DPB.pSubresources.clear();
119 m_D3D12DPB.pHeaps.clear();
120 m_D3D12DPB.pResources.reserve(m_dpbTextureArraySize);
121 m_D3D12DPB.pSubresources.reserve(m_dpbTextureArraySize);
122 m_D3D12DPB.pHeaps.reserve(m_dpbTextureArraySize);
123
124 return untrackCount;
125 }
126
127 // Assigns a reference frame at a given position
128 void
assign_reference_frame(d3d12_video_reconstructed_picture pReconPicture,uint32_t dpbPosition)129 d3d12_texture_array_dpb_manager::assign_reference_frame(d3d12_video_reconstructed_picture pReconPicture,
130 uint32_t dpbPosition)
131 {
132 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
133 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
134
135 assert(dpbPosition < m_D3D12DPB.pResources.size());
136
137 m_D3D12DPB.pResources[dpbPosition] = pReconPicture.pReconstructedPicture;
138 m_D3D12DPB.pSubresources[dpbPosition] = pReconPicture.ReconstructedPictureSubresource;
139 m_D3D12DPB.pHeaps[dpbPosition] = pReconPicture.pVideoHeap;
140 }
141
142 // Adds a new reference frame at a given position
143 void
insert_reference_frame(d3d12_video_reconstructed_picture pReconPicture,uint32_t dpbPosition)144 d3d12_texture_array_dpb_manager::insert_reference_frame(d3d12_video_reconstructed_picture pReconPicture,
145 uint32_t dpbPosition)
146 {
147 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
148 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
149
150 if (dpbPosition > m_D3D12DPB.pResources.size()) {
151 // extend capacity
152 m_D3D12DPB.pResources.resize(dpbPosition);
153 m_D3D12DPB.pSubresources.resize(dpbPosition);
154 m_D3D12DPB.pHeaps.resize(dpbPosition);
155 }
156
157 m_D3D12DPB.pResources.insert(m_D3D12DPB.pResources.begin() + dpbPosition, pReconPicture.pReconstructedPicture);
158 m_D3D12DPB.pSubresources.insert(m_D3D12DPB.pSubresources.begin() + dpbPosition,
159 pReconPicture.ReconstructedPictureSubresource);
160 m_D3D12DPB.pHeaps.insert(m_D3D12DPB.pHeaps.begin() + dpbPosition, pReconPicture.pVideoHeap);
161 }
162
163 // Gets a reference frame at a given position
164 d3d12_video_reconstructed_picture
get_reference_frame(uint32_t dpbPosition)165 d3d12_texture_array_dpb_manager::get_reference_frame(uint32_t dpbPosition)
166 {
167 assert(dpbPosition < m_D3D12DPB.pResources.size());
168
169 d3d12_video_reconstructed_picture retVal = { m_D3D12DPB.pResources[dpbPosition],
170 m_D3D12DPB.pSubresources[dpbPosition],
171 m_D3D12DPB.pHeaps[dpbPosition] };
172
173 return retVal;
174 }
175
176 // Removes a new reference frame at a given position and returns operation success
177 bool
remove_reference_frame(uint32_t dpbPosition,bool * pResourceUntracked)178 d3d12_texture_array_dpb_manager::remove_reference_frame(uint32_t dpbPosition, bool *pResourceUntracked)
179 {
180 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
181 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
182
183 assert(dpbPosition < m_D3D12DPB.pResources.size());
184
185 // If removed resource came from resource pool, mark it as free
186 // to free it for a new usage
187 // Don't assert the untracking result here in case the DPB contains resources not adquired using the pool methods in
188 // this interface
189 bool resUntracked = untrack_reconstructed_picture_allocation(
190 { m_D3D12DPB.pResources[dpbPosition], m_D3D12DPB.pSubresources[dpbPosition] });
191
192 if (pResourceUntracked != nullptr) {
193 *pResourceUntracked = resUntracked;
194 }
195
196 // Remove from DPB tables
197 m_D3D12DPB.pResources.erase(m_D3D12DPB.pResources.begin() + dpbPosition);
198 m_D3D12DPB.pSubresources.erase(m_D3D12DPB.pSubresources.begin() + dpbPosition);
199 m_D3D12DPB.pHeaps.erase(m_D3D12DPB.pHeaps.begin() + dpbPosition);
200
201 return true;
202 }
203
204 // Returns true if the trackedItem was allocated (and is being tracked) by this class
205 bool
is_tracked_allocation(d3d12_video_reconstructed_picture trackedItem)206 d3d12_texture_array_dpb_manager::is_tracked_allocation(d3d12_video_reconstructed_picture trackedItem)
207 {
208 for (auto &reusableRes : m_ResourcesPool) {
209 if ((trackedItem.pReconstructedPicture == reusableRes.pResource.Get()) &&
210 (trackedItem.ReconstructedPictureSubresource == reusableRes.subresource) && !reusableRes.isFree) {
211 return true;
212 }
213 }
214 return false;
215 }
216
217 // Returns whether it found the tracked resource on this instance pool tracking and was able to free it
218 bool
untrack_reconstructed_picture_allocation(d3d12_video_reconstructed_picture trackedItem)219 d3d12_texture_array_dpb_manager::untrack_reconstructed_picture_allocation(d3d12_video_reconstructed_picture trackedItem)
220 {
221 for (auto &reusableRes : m_ResourcesPool) {
222 if ((trackedItem.pReconstructedPicture == reusableRes.pResource.Get()) &&
223 (trackedItem.ReconstructedPictureSubresource == reusableRes.subresource)) {
224 reusableRes.isFree = true;
225 return true;
226 }
227 }
228 return false;
229 }
230
231 // Returns a fresh resource for a NEW picture to be written to
232 // this class implements the dpb allocations as an array of textures
233 d3d12_video_reconstructed_picture
get_new_tracked_picture_allocation()234 d3d12_texture_array_dpb_manager::get_new_tracked_picture_allocation()
235 {
236 d3d12_video_reconstructed_picture freshAllocation = { // pResource
237 nullptr,
238 // subresource
239 0
240 };
241
242 // Find first (if any) available resource to (re-)use
243 bool bAvailableResourceInPool = false;
244 for (auto &reusableRes : m_ResourcesPool) {
245 if (reusableRes.isFree) {
246 bAvailableResourceInPool = true;
247 freshAllocation.pReconstructedPicture = reusableRes.pResource.Get();
248 freshAllocation.ReconstructedPictureSubresource = reusableRes.subresource;
249 reusableRes.isFree = false;
250 break;
251 }
252 }
253
254 if (!bAvailableResourceInPool) {
255 debug_printf("[d3d12_texture_array_dpb_manager] ID3D12Resource pool is full - Pool capacity (%" PRIu32 ") - Returning null allocation",
256 static_cast<uint32_t>(m_ResourcesPool.size()));
257 }
258
259 return freshAllocation;
260 }
261
262 uint32_t
get_number_of_pics_in_dpb()263 d3d12_texture_array_dpb_manager::get_number_of_pics_in_dpb()
264 {
265 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pSubresources.size());
266 assert(m_D3D12DPB.pResources.size() == m_D3D12DPB.pHeaps.size());
267 assert(m_D3D12DPB.pResources.size() < UINT32_MAX);
268 return static_cast<uint32_t>(m_D3D12DPB.pResources.size());
269 }
270
271 d3d12_video_reference_frames
get_current_reference_frames()272 d3d12_texture_array_dpb_manager::get_current_reference_frames()
273 {
274 d3d12_video_reference_frames retVal = {
275 get_number_of_pics_in_dpb(),
276 m_D3D12DPB.pResources.data(),
277 m_D3D12DPB.pSubresources.data(),
278 m_D3D12DPB.pHeaps.data(),
279 };
280
281 return retVal;
282 }
283
284 // number of resources in the pool that are marked as in use
285 uint32_t
get_number_of_in_use_allocations()286 d3d12_texture_array_dpb_manager::get_number_of_in_use_allocations()
287 {
288 uint32_t countOfInUseResourcesInPool = 0;
289 for (auto &reusableRes : m_ResourcesPool) {
290 if (!reusableRes.isFree) {
291 countOfInUseResourcesInPool++;
292 }
293 }
294 return countOfInUseResourcesInPool;
295 }
296
297 // Returns the number of pictures currently stored in the DPB
298 uint32_t
get_number_of_tracked_allocations()299 d3d12_texture_array_dpb_manager::get_number_of_tracked_allocations()
300 {
301 assert(m_ResourcesPool.size() < UINT32_MAX);
302 return static_cast<uint32_t>(m_ResourcesPool.size());
303 }
304