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