xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/d3d12/d3d12_video_array_of_textures_dpb_manager.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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