xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/d3d12/d3d12_video_dec_references_mgr.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_dec_references_mgr.h"
25 #include "d3d12_video_dec_h264.h"
26 #include "d3d12_video_dec_hevc.h"
27 #include "d3d12_video_dec_av1.h"
28 #include "d3d12_video_dec_vp9.h"
29 #include "d3d12_video_texture_array_dpb_manager.h"
30 #include "d3d12_video_array_of_textures_dpb_manager.h"
31 #include "d3d12_screen.h"
32 #include "d3d12_resource.h"
33 #include "d3d12_video_buffer.h"
34 #include <algorithm>
35 #include <string>
36 
37 //----------------------------------------------------------------------------------------------------------------------------------
38 static uint16_t
GetInvalidReferenceIndex(d3d12_video_decode_profile_type DecodeProfileType)39 GetInvalidReferenceIndex(d3d12_video_decode_profile_type DecodeProfileType)
40 {
41    assert(DecodeProfileType <= d3d12_video_decode_profile_type_max_valid);
42 
43    switch (DecodeProfileType) {
44       case d3d12_video_decode_profile_type_h264:
45          return DXVA_H264_INVALID_PICTURE_INDEX;
46       case d3d12_video_decode_profile_type_hevc:
47          return DXVA_HEVC_INVALID_PICTURE_INDEX;
48       case d3d12_video_decode_profile_type_av1:
49          return DXVA_AV1_INVALID_PICTURE_INDEX;
50       case d3d12_video_decode_profile_type_vp9:
51          return DXVA_VP9_INVALID_PICTURE_INDEX;
52       default:
53          return 0;
54    };
55 }
56 
57 //----------------------------------------------------------------------------------------------------------------------------------
58 ///
59 /// This should always be a clear (non ref only) texture, to be presented downstream as the decoded texture
60 /// Please see get_reference_only_output for the current frame recon pic ref only allocation
61 ///
62 void
get_current_frame_decode_output_texture(struct pipe_video_buffer * pCurrentDecodeTarget,ID3D12Resource ** ppOutTexture2D,uint32_t * pOutSubresourceIndex)63 d3d12_video_decoder_references_manager::get_current_frame_decode_output_texture(struct pipe_video_buffer *  pCurrentDecodeTarget,
64                                                                                 ID3D12Resource **ppOutTexture2D,
65                                                                                 uint32_t *       pOutSubresourceIndex)
66 {
67 // First try to find if there's an existing entry for this pCurrentDecodeTarget already in the DPB
68    // For interlaced scenarios, multiple end_frame calls will need to reference the same texture for top/bottom
69    assert(m_DecodeTargetToOriginalIndex7Bits.count(pCurrentDecodeTarget) > 0); // Needs to already have a Index7Bits assigned for current pic params
70    uint16_t remappedIdx = find_remapped_index(m_DecodeTargetToOriginalIndex7Bits[pCurrentDecodeTarget]);
71 
72    if((remappedIdx != m_invalidIndex) && !(is_reference_only())) { // If it already has a remapped index in use, reuse that allocation
73       // return the existing allocation for this decode target
74       d3d12_video_reconstructed_picture reconPicture = m_upD3D12TexturesStorageManager->get_reference_frame(remappedIdx);
75       *ppOutTexture2D       = reconPicture.pReconstructedPicture;
76       *pOutSubresourceIndex = reconPicture.ReconstructedPictureSubresource;
77    } else {
78       if (is_pipe_buffer_underlying_output_decode_allocation()) {
79          auto vidBuffer = (struct d3d12_video_buffer *)(pCurrentDecodeTarget);
80          *ppOutTexture2D       = d3d12_resource_resource(vidBuffer->texture);
81          *pOutSubresourceIndex = 0;
82       } else {
83          d3d12_video_reconstructed_picture pFreshAllocation =
84             m_upD3D12TexturesStorageManager->get_new_tracked_picture_allocation();
85          *ppOutTexture2D       = pFreshAllocation.pReconstructedPicture;
86          *pOutSubresourceIndex = pFreshAllocation.ReconstructedPictureSubresource;
87       }
88    }
89 }
90 
91 //----------------------------------------------------------------------------------------------------------------------------------
92 _Use_decl_annotations_ void
get_reference_only_output(struct pipe_video_buffer * pCurrentDecodeTarget,ID3D12Resource ** ppOutputReference,uint32_t * pOutputSubresource,bool & outNeedsTransitionToDecodeWrite)93 d3d12_video_decoder_references_manager::get_reference_only_output(
94    struct pipe_video_buffer *  pCurrentDecodeTarget,
95    ID3D12Resource **ppOutputReference,     // out -> new reference slot assigned or nullptr
96    uint32_t *       pOutputSubresource,    // out -> new reference slot assigned or nullptr
97    bool &outNeedsTransitionToDecodeWrite   // out -> indicates if output resource argument has to be transitioned to
98                                            // D3D12_RESOURCE_STATE_VIDEO_DECODE_READ by the caller
99 )
100 {
101    assert(is_reference_only());
102 
103    // First try to find if there's an existing entry for this pCurrentDecodeTarget already in the DPB
104    // For interlaced scenarios, multiple end_frame calls will need to reference the same texture for top/bottom
105    assert(m_DecodeTargetToOriginalIndex7Bits.count(pCurrentDecodeTarget) > 0); // Needs to already have a Index7Bits assigned for current pic params
106    uint16_t remappedIdx = find_remapped_index(m_DecodeTargetToOriginalIndex7Bits[pCurrentDecodeTarget]);
107 
108    if(remappedIdx != m_invalidIndex) { // If it already has a remapped index in use, reuse that allocation
109       // return the existing allocation for this decode target
110       d3d12_video_reconstructed_picture reconPicture = m_upD3D12TexturesStorageManager->get_reference_frame(remappedIdx);
111       *ppOutputReference              = reconPicture.pReconstructedPicture;
112       *pOutputSubresource             = reconPicture.ReconstructedPictureSubresource;
113       outNeedsTransitionToDecodeWrite = true;
114    } else {
115       // The DPB Storage only has REFERENCE_ONLY allocations, use one of those.
116       d3d12_video_reconstructed_picture pFreshAllocation =
117          m_upD3D12TexturesStorageManager->get_new_tracked_picture_allocation();
118       *ppOutputReference              = pFreshAllocation.pReconstructedPicture;
119       *pOutputSubresource             = pFreshAllocation.ReconstructedPictureSubresource;
120       outNeedsTransitionToDecodeWrite = true;
121    }
122 }
123 
124 //----------------------------------------------------------------------------------------------------------------------------------
125 D3D12_VIDEO_DECODE_REFERENCE_FRAMES
get_current_reference_frames()126 d3d12_video_decoder_references_manager::get_current_reference_frames()
127 {
128    d3d12_video_reference_frames args = m_upD3D12TexturesStorageManager->get_current_reference_frames();
129 
130 
131    // Convert generic IUnknown into the actual decoder heap object
132    m_ppHeaps.resize(args.NumTexture2Ds, nullptr);
133    HRESULT hr = S_OK;
134    for (uint32_t i = 0; i < args.NumTexture2Ds; i++) {
135       if (args.ppHeaps[i]) {
136          hr = args.ppHeaps[i]->QueryInterface(IID_PPV_ARGS(&m_ppHeaps[i]));
137          assert(SUCCEEDED(hr));
138       } else {
139          m_ppHeaps[i] = nullptr;
140       }
141    }
142 
143    D3D12_VIDEO_DECODE_REFERENCE_FRAMES retVal = {
144       args.NumTexture2Ds,
145       args.ppTexture2Ds,
146       args.pSubresources,
147       m_ppHeaps.data(),
148    };
149 
150    return retVal;
151 }
152 
153 //----------------------------------------------------------------------------------------------------------------------------------
154 _Use_decl_annotations_
d3d12_video_decoder_references_manager(const struct d3d12_screen * pD3D12Screen,uint32_t NodeMask,d3d12_video_decode_profile_type DecodeProfileType,d3d12_video_decode_dpb_descriptor m_dpbDescriptor)155 d3d12_video_decoder_references_manager::d3d12_video_decoder_references_manager(
156    const struct d3d12_screen *       pD3D12Screen,
157    uint32_t                          NodeMask,
158    d3d12_video_decode_profile_type   DecodeProfileType,
159    d3d12_video_decode_dpb_descriptor m_dpbDescriptor)
160    : m_DecodeTargetToOriginalIndex7Bits({ }),
161      m_pD3D12Screen(pD3D12Screen),
162      m_invalidIndex(GetInvalidReferenceIndex(DecodeProfileType)),
163      m_dpbDescriptor(m_dpbDescriptor),
164      m_formatInfo({ m_dpbDescriptor.Format })
165 {
166    ASSERTED HRESULT hr = m_pD3D12Screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &m_formatInfo, sizeof(m_formatInfo));
167    assert(SUCCEEDED(hr));
168    D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC targetFrameResolution = { static_cast<uint32_t>(m_dpbDescriptor.Width),
169                                                                          m_dpbDescriptor.Height };
170    D3D12_RESOURCE_FLAGS                        resourceAllocFlags =
171       m_dpbDescriptor.fReferenceOnly ?
172          (D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) :
173          D3D12_RESOURCE_FLAG_NONE;
174 
175    if (m_dpbDescriptor.fArrayOfTexture) {
176       // If all subresources are 0, the DPB is loaded with an array of individual textures, the D3D Encode API expects
177       // pSubresources to be null in this case The D3D Decode API expects it to be non-null even with all zeroes.
178       bool setNullSubresourcesOnAllZero = false;
179       m_upD3D12TexturesStorageManager =
180          std::make_unique<d3d12_array_of_textures_dpb_manager>(m_dpbDescriptor.dpbSize,
181                                                                m_pD3D12Screen->dev,
182                                                                m_dpbDescriptor.Format,
183                                                                targetFrameResolution,
184                                                                resourceAllocFlags,
185                                                                setNullSubresourcesOnAllZero,
186                                                                m_dpbDescriptor.m_NodeMask,
187                                                                !is_pipe_buffer_underlying_output_decode_allocation());
188    } else {
189       m_upD3D12TexturesStorageManager = std::make_unique<d3d12_texture_array_dpb_manager>(m_dpbDescriptor.dpbSize,
190                                                                                           m_pD3D12Screen->dev,
191                                                                                           m_dpbDescriptor.Format,
192                                                                                           targetFrameResolution,
193                                                                                           resourceAllocFlags,
194                                                                                           m_dpbDescriptor.m_NodeMask);
195    }
196 
197    m_referenceDXVAIndices.resize(m_dpbDescriptor.dpbSize);
198 
199    d3d12_video_reconstructed_picture reconPicture = { nullptr, 0, nullptr };
200 
201    for (uint32_t dpbIdx = 0; dpbIdx < m_dpbDescriptor.dpbSize; dpbIdx++) {
202       m_upD3D12TexturesStorageManager->insert_reference_frame(reconPicture, dpbIdx);
203    }
204 
205    mark_all_references_as_unused();
206    release_unused_references_texture_memory();
207 }
208 
209 //----------------------------------------------------------------------------------------------------------------------------------
210 uint16_t
find_remapped_index(uint16_t originalIndex)211 d3d12_video_decoder_references_manager::find_remapped_index(uint16_t originalIndex)
212 {
213    // Check if the index is already mapped.
214    for (uint16_t remappedIndex = 0; remappedIndex < m_dpbDescriptor.dpbSize; remappedIndex++) {
215       if (m_referenceDXVAIndices[remappedIndex].originalIndex == originalIndex) {
216          return remappedIndex;
217       }
218    }
219 
220    return m_invalidIndex;
221 }
222 
223 //----------------------------------------------------------------------------------------------------------------------------------
224 uint16_t
update_entry(uint16_t index,ID3D12Resource * & pOutputReference,uint32_t & OutputSubresource,bool & outNeedsTransitionToDecodeRead)225 d3d12_video_decoder_references_manager::update_entry(
226    uint16_t         index,                // in
227    ID3D12Resource *&pOutputReference,     // out -> new reference slot assigned or nullptr
228    uint32_t &       OutputSubresource,    // out -> new reference slot assigned or 0
229    bool &outNeedsTransitionToDecodeRead   // out -> indicates if output resource argument has to be transitioned to
230                                           // D3D12_RESOURCE_STATE_VIDEO_DECODE_READ by the caller
231 )
232 {
233    uint16_t remappedIndex         = m_invalidIndex;
234    outNeedsTransitionToDecodeRead = false;
235 
236    if (index != m_invalidIndex) {
237       remappedIndex = find_remapped_index(index);
238 
239       outNeedsTransitionToDecodeRead = true;
240       if (remappedIndex == m_invalidIndex || remappedIndex == m_currentOutputIndex) {
241          debug_printf("[d3d12_video_decoder_references_manager] update_entry - Invalid Reference Index\n");
242 
243          remappedIndex                  = m_currentOutputIndex;
244          outNeedsTransitionToDecodeRead = false;
245       }
246 
247       d3d12_video_reconstructed_picture reconPicture =
248          m_upD3D12TexturesStorageManager->get_reference_frame(remappedIndex);
249       pOutputReference  = outNeedsTransitionToDecodeRead ? reconPicture.pReconstructedPicture : nullptr;
250       OutputSubresource = outNeedsTransitionToDecodeRead ? reconPicture.ReconstructedPictureSubresource : 0u;
251    }
252 
253    return remappedIndex;
254 }
255 
256 //----------------------------------------------------------------------------------------------------------------------------------
257 _Use_decl_annotations_ uint16_t
store_future_reference(uint16_t index,ComPtr<ID3D12VideoDecoderHeap> & decoderHeap,ID3D12Resource * pTexture2D,uint32_t subresourceIndex)258 d3d12_video_decoder_references_manager::store_future_reference(uint16_t                        index,
259                                                                ComPtr<ID3D12VideoDecoderHeap> &decoderHeap,
260                                                                ID3D12Resource *                pTexture2D,
261                                                                uint32_t                        subresourceIndex)
262 {
263    // Check if the index was in use.
264    uint16_t remappedIndex = find_remapped_index(index);
265 
266    if (remappedIndex == m_invalidIndex) {
267       // The current output index was not used last frame.  Get an unused entry.
268       remappedIndex = find_remapped_index(m_invalidIndex);
269    }
270 
271    if (remappedIndex == m_invalidIndex) {
272       debug_printf(
273          "[d3d12_video_decoder_references_manager] d3d12_video_decoder_references_manager - Decode - No available "
274          "reference map entry for output.\n");
275       assert(false);
276    }
277 
278    // Set the index as the key in this map entry.
279    m_referenceDXVAIndices[remappedIndex].originalIndex = index;
280    IUnknown *pUnkHeap                                  = nullptr;
281    ASSERTED HRESULT hr = decoderHeap.Get()->QueryInterface(IID_PPV_ARGS(&pUnkHeap));
282    assert(SUCCEEDED(hr));
283    d3d12_video_reconstructed_picture reconPic = { pTexture2D, subresourceIndex, pUnkHeap };
284 
285    m_upD3D12TexturesStorageManager->assign_reference_frame(reconPic, remappedIndex);
286 
287    // Store the index to use for error handling when caller specifies and invalid reference index.
288    m_currentOutputIndex = remappedIndex;
289    m_currentSubresourceIndex = subresourceIndex;
290    m_currentResource = pTexture2D;
291 
292    return remappedIndex;
293 }
294 
295 //----------------------------------------------------------------------------------------------------------------------------------
296 void
mark_reference_in_use(uint16_t index)297 d3d12_video_decoder_references_manager::mark_reference_in_use(uint16_t index)
298 {
299    if (index != m_invalidIndex) {
300       uint16_t remappedIndex = find_remapped_index(index);
301       if (remappedIndex != m_invalidIndex) {
302          m_referenceDXVAIndices[remappedIndex].fUsed = true;
303       }
304    }
305 }
306 
307 //----------------------------------------------------------------------------------------------------------------------------------
308 void
release_unused_references_texture_memory()309 d3d12_video_decoder_references_manager::release_unused_references_texture_memory()
310 {
311    for (uint32_t index = 0; index < m_dpbDescriptor.dpbSize; index++) {
312       if (!m_referenceDXVAIndices[index].fUsed) {
313          d3d12_video_reconstructed_picture reconPicture = m_upD3D12TexturesStorageManager->get_reference_frame(index);
314          if (reconPicture.pReconstructedPicture != nullptr) {
315             ASSERTED bool wasTracked = m_upD3D12TexturesStorageManager->untrack_reconstructed_picture_allocation(reconPicture);
316             // Untrack this resource, will mark it as free un the underlying storage buffer pool
317             // if not tracked, must be due to no-copies allocation
318             assert (wasTracked || is_pipe_buffer_underlying_output_decode_allocation());
319 
320             d3d12_video_reconstructed_picture nullReconPic = { nullptr, 0, nullptr };
321 
322             // Mark the unused refpic as null/empty in the DPB
323             m_upD3D12TexturesStorageManager->assign_reference_frame(nullReconPic, index);
324 
325             // Remove the entry in m_DecodeTargetToOriginalIndex7Bits
326             auto value = m_referenceDXVAIndices[index].originalIndex;
327             auto it = std::find_if(m_DecodeTargetToOriginalIndex7Bits.begin(), m_DecodeTargetToOriginalIndex7Bits.end(),
328                [&value](const std::pair< struct pipe_video_buffer*, uint8_t > &p) {
329                   return p.second == value;
330                });
331 
332             assert(it != m_DecodeTargetToOriginalIndex7Bits.end());
333 
334             m_DecodeTargetToOriginalIndex7Bits.erase(it);
335          }
336 
337 
338          m_referenceDXVAIndices[index].originalIndex = m_invalidIndex;
339       }
340    }
341 }
342 
343 //----------------------------------------------------------------------------------------------------------------------------------
344 void
mark_all_references_as_unused()345 d3d12_video_decoder_references_manager::mark_all_references_as_unused()
346 {
347    for (uint32_t index = 0; index < m_dpbDescriptor.dpbSize; index++) {
348       m_referenceDXVAIndices[index].fUsed = false;
349    }
350 }
351 
352 //----------------------------------------------------------------------------------------------------------------------------------
353 void
print_dpb()354 d3d12_video_decoder_references_manager::print_dpb()
355 {
356    // Resource backing storage always has to match dpbsize
357    if(!is_pipe_buffer_underlying_output_decode_allocation()) {
358       assert(m_upD3D12TexturesStorageManager->get_number_of_tracked_allocations() == m_dpbDescriptor.dpbSize);
359    }
360 
361    // get_current_reference_frames query-interfaces the pVideoHeap's.
362    D3D12_VIDEO_DECODE_REFERENCE_FRAMES curRefFrames = get_current_reference_frames();
363    std::string dpbContents;
364    for (uint32_t dpbResIdx = 0;dpbResIdx < curRefFrames.NumTexture2Ds;dpbResIdx++) {
365       dpbContents += "\t{ DPBidx: ";
366       dpbContents += std::to_string(dpbResIdx);
367       dpbContents += " - ResourcePtr: ";
368       char strBufTex[256];
369       memset(&strBufTex, '\0', 256);
370       sprintf(strBufTex, "%p", curRefFrames.ppTexture2Ds[dpbResIdx]);
371       dpbContents += std::string(strBufTex);
372       dpbContents += " - SubresourceIdx: ";
373       dpbContents += (curRefFrames.pSubresources ? std::to_string(curRefFrames.pSubresources[dpbResIdx]) : "0");
374       dpbContents += " - DecoderHeapPtr: ";
375       char strBufHeap[256];
376       memset(&strBufHeap, '\0', 256);
377       if(curRefFrames.ppHeaps && curRefFrames.ppHeaps[dpbResIdx]) {
378          sprintf(strBufHeap, "%p", curRefFrames.ppHeaps[dpbResIdx]);
379          dpbContents += std::string(strBufHeap);
380       } else {
381          dpbContents += "(nil)";
382       }
383       dpbContents += " - Slot type: ";
384       dpbContents +=  ((m_currentResource == curRefFrames.ppTexture2Ds[dpbResIdx]) && (m_currentSubresourceIndex == curRefFrames.pSubresources[dpbResIdx])) ? "Current decoded frame output" : "Reference frame";
385       dpbContents += " - DXVA_PicParams Reference Index: ";
386       dpbContents += (m_referenceDXVAIndices[dpbResIdx].originalIndex != m_invalidIndex) ? std::to_string(m_referenceDXVAIndices[dpbResIdx].originalIndex) : "DXVA_UNUSED_PICENTRY";
387       dpbContents += "}\n";
388    }
389 
390    debug_printf("[D3D12 Video Decoder Picture Manager] Decode session information:\n"
391                "\tDPB Maximum Size (max_ref_count + one_slot_curpic): %d\n"
392                "\tDXGI_FORMAT: %d\n"
393                "\tTexture resolution: (%" PRIu64 ", %d)\n"
394                "\tD3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY enforced: %d\n"
395                "\tAllocation Mode: %s\n"
396                "\n ----------------------\n\tCurrent frame information:\n"
397                "\tD3D12_VIDEO_DECODE_REFERENCE_FRAMES.NumTexture2Ds: %d\n"
398                "\tDPB Contents Table:\n%s",
399                m_upD3D12TexturesStorageManager->get_number_of_tracked_allocations(),
400                m_dpbDescriptor.Format,
401                m_dpbDescriptor.Width,
402                m_dpbDescriptor.Height,
403                m_dpbDescriptor.fReferenceOnly,
404                (m_dpbDescriptor.fArrayOfTexture ? "ArrayOfTextures" : "TextureArray"),
405                m_upD3D12TexturesStorageManager->get_number_of_pics_in_dpb(),
406                dpbContents.c_str());
407 }
408