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