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_enc.h"
25 #include "d3d12_video_enc_av1.h"
26 #include "d3d12_video_encoder_references_manager_av1.h"
27 #include <algorithm>
28 #include <string>
29 #include "d3d12_screen.h"
30
31 using namespace std;
32
d3d12_video_encoder_references_manager_av1(bool gopHasInterFrames,d3d12_video_dpb_storage_manager_interface & rDpbStorageManager)33 d3d12_video_encoder_references_manager_av1::d3d12_video_encoder_references_manager_av1(
34 bool gopHasInterFrames, d3d12_video_dpb_storage_manager_interface &rDpbStorageManager)
35 : m_CurrentFrameReferencesData({}),
36 m_PhysicalAllocationsStorage(rDpbStorageManager),
37 m_gopHasInterFrames(gopHasInterFrames),
38 m_isCurrentFrameUsedAsReference(false),
39 m_CurrentFramePicParams({})
40 {
41 assert((NUM_REF_FRAMES + 1 /*extra for cur frame output recon pic*/) ==
42 m_PhysicalAllocationsStorage.get_number_of_tracked_allocations());
43
44 debug_printf("[D3D12 Video Encoder Picture Manager AV1] Completed construction of "
45 "d3d12_video_encoder_references_manager_AV1 instance.\n");
46 }
47
48 void
clear_dpb()49 d3d12_video_encoder_references_manager_av1::clear_dpb()
50 {
51 // Reset m_CurrentFrameReferencesData tracking
52 m_CurrentFrameReferencesData.pVirtualDPBEntries.clear();
53 m_CurrentFrameReferencesData.pVirtualDPBEntries.resize(NUM_REF_FRAMES);
54 m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
55
56 // Initialize DPB slots as unused
57 for (size_t i = 0; i < NUM_REF_FRAMES; i++)
58 m_CurrentFrameReferencesData.pVirtualDPBEntries[i].ReconstructedPictureResourceIndex =
59 UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX;
60
61 // Reset physical DPB underlying storage
62 ASSERTED uint32_t numPicsBeforeClearInDPB = m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb();
63 ASSERTED uint32_t cFreedResources = m_PhysicalAllocationsStorage.clear_decode_picture_buffer();
64 assert(numPicsBeforeClearInDPB == cFreedResources);
65 }
66
67 D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
get_current_frame_recon_pic_output_allocation()68 d3d12_video_encoder_references_manager_av1::get_current_frame_recon_pic_output_allocation()
69 {
70 return m_CurrentFrameReferencesData.ReconstructedPicTexture;
71 }
72
73 bool
is_current_frame_used_as_reference()74 d3d12_video_encoder_references_manager_av1::is_current_frame_used_as_reference()
75 {
76 return m_isCurrentFrameUsedAsReference;
77 }
78
79 void
begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData,bool bUsedAsReference,struct pipe_picture_desc * picture)80 d3d12_video_encoder_references_manager_av1::begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData,
81 bool bUsedAsReference,
82 struct pipe_picture_desc *picture)
83 {
84 m_CurrentFramePicParams = *curFrameData.pAV1PicData;
85 m_isCurrentFrameUsedAsReference = bUsedAsReference;
86
87 if (m_CurrentFramePicParams.FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME)
88 clear_dpb();
89
90 // Prepare current frame recon pic allocation
91 m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
92 if (is_current_frame_used_as_reference() && m_gopHasInterFrames) {
93 auto reconPic = m_PhysicalAllocationsStorage.get_new_tracked_picture_allocation();
94 m_CurrentFrameReferencesData.ReconstructedPicTexture.pReconstructedPicture = reconPic.pReconstructedPicture;
95 m_CurrentFrameReferencesData.ReconstructedPicTexture.ReconstructedPictureSubresource =
96 reconPic.ReconstructedPictureSubresource;
97 }
98
99 #if MESA_DEBUG
100 assert(m_PhysicalAllocationsStorage.get_number_of_tracked_allocations() <=
101 (NUM_REF_FRAMES + 1)); // pool is not extended beyond maximum expected usage
102
103 if (m_CurrentFramePicParams.FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME) {
104 // After clearing the DPB, outstanding used allocations should be 1u only for the first allocation for the
105 // reconstructed picture of the initial KEY_FRAME
106 assert(m_PhysicalAllocationsStorage.get_number_of_in_use_allocations() ==
107 ((is_current_frame_used_as_reference() && m_gopHasInterFrames) ? 1u : 0u));
108 }
109 #endif
110 }
111
112 void
end_frame()113 d3d12_video_encoder_references_manager_av1::end_frame()
114 {
115 refresh_dpb_slots_with_current_frame_reconpic();
116 }
117
118 D3D12_VIDEO_ENCODE_REFERENCE_FRAMES
get_current_reference_frames()119 d3d12_video_encoder_references_manager_av1::get_current_reference_frames()
120 {
121 D3D12_VIDEO_ENCODE_REFERENCE_FRAMES retVal = { 0,
122 // ppTexture2Ds
123 nullptr,
124 // pSubresources
125 nullptr };
126
127 if ((m_CurrentFramePicParams.FrameType != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME) && m_gopHasInterFrames) {
128 auto curRef = m_PhysicalAllocationsStorage.get_current_reference_frames();
129 retVal.NumTexture2Ds = curRef.NumTexture2Ds;
130 retVal.ppTexture2Ds = curRef.ppTexture2Ds;
131 retVal.pSubresources = curRef.pSubresources;
132 }
133
134 return retVal;
135 }
136
137 void
print_ref_frame_idx()138 d3d12_video_encoder_references_manager_av1::print_ref_frame_idx()
139 {
140 std::string refListContentsString;
141 for (uint32_t idx = 0; idx < REFS_PER_FRAME; idx++) {
142 uint32_t reference = 0;
143 reference = m_CurrentFramePicParams.ReferenceIndices[idx];
144 refListContentsString += "{ ref_frame_idx[" + std::to_string(idx) + "] - ";
145 refListContentsString += " DPBidx: ";
146 refListContentsString += std::to_string(reference);
147 if (m_CurrentFrameReferencesData.pVirtualDPBEntries[reference].ReconstructedPictureResourceIndex !=
148 UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) {
149 refListContentsString += " - OrderHint: ";
150 refListContentsString += std::to_string(m_CurrentFrameReferencesData.pVirtualDPBEntries[reference].OrderHint);
151 refListContentsString += " - PictureIndex: ";
152 refListContentsString +=
153 std::to_string(m_CurrentFrameReferencesData.pVirtualDPBEntries[reference].PictureIndex);
154 } else {
155 refListContentsString += " - Unused Virtual DPB slot ";
156 }
157
158 refListContentsString += " }\n";
159 }
160
161 debug_printf("[D3D12 Video Encoder Picture Manager AV1] ref_frame_idx[REFS_PER_FRAME] for frame with OrderHint %d "
162 "(PictureIndex %d) is: \n%s \n",
163 m_CurrentFramePicParams.OrderHint,
164 m_CurrentFramePicParams.PictureIndex,
165 refListContentsString.c_str());
166 debug_printf("[D3D12 Video Encoder Picture Manager AV1] Requested PrimaryRefFrame: %d\n",
167 m_CurrentFramePicParams.PrimaryRefFrame);
168 debug_printf("[D3D12 Video Encoder Picture Manager AV1] Requested RefreshFrameFlags: %d\n",
169 m_CurrentFramePicParams.RefreshFrameFlags);
170 }
171
172 void
print_virtual_dpb_entries()173 d3d12_video_encoder_references_manager_av1::print_virtual_dpb_entries()
174 {
175 std::string dpbContents;
176 for (uint32_t dpbResIdx = 0; dpbResIdx < m_CurrentFrameReferencesData.pVirtualDPBEntries.size(); dpbResIdx++) {
177 auto &dpbDesc = m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbResIdx];
178
179 if (dpbDesc.ReconstructedPictureResourceIndex != UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) {
180 d3d12_video_reconstructed_picture dpbEntry =
181 m_PhysicalAllocationsStorage.get_reference_frame(dpbDesc.ReconstructedPictureResourceIndex);
182 dpbContents += "{ DPBidx: ";
183 dpbContents += std::to_string(dpbResIdx);
184 dpbContents += " - OrderHint: ";
185 dpbContents += std::to_string(dpbDesc.OrderHint);
186 dpbContents += " - PictureIndex: ";
187 dpbContents += std::to_string(dpbDesc.PictureIndex);
188 dpbContents += " - DPBStorageIdx: ";
189 dpbContents += std::to_string(dpbDesc.ReconstructedPictureResourceIndex);
190 dpbContents += " - DPBStorageResourcePtr: ";
191 char strBuf[256];
192 memset(&strBuf, '\0', 256);
193 sprintf(strBuf, "%p", dpbEntry.pReconstructedPicture);
194 dpbContents += std::string(strBuf);
195 dpbContents += " - DPBStorageSubresource: ";
196 dpbContents += std::to_string(dpbEntry.ReconstructedPictureSubresource);
197 dpbContents += " - RefCount (from any ref_frame_idx[0..6]): ";
198 dpbContents += std::to_string(get_dpb_virtual_slot_refcount_from_ref_frame_idx(dpbResIdx));
199 dpbContents += " }\n";
200 } else {
201 dpbContents += "{ DPBidx: ";
202 dpbContents += std::to_string(dpbResIdx);
203 dpbContents += " < Unused Virtual DPB slot > }\n";
204 }
205 }
206
207 debug_printf(
208 "[D3D12 Video Encoder Picture Manager AV1] DPB Current output reconstructed picture %p subresource %d\n",
209 m_CurrentFrameReferencesData.ReconstructedPicTexture.pReconstructedPicture,
210 m_CurrentFrameReferencesData.ReconstructedPicTexture.ReconstructedPictureSubresource);
211
212 debug_printf("[D3D12 Video Encoder Picture Manager AV1] NumTexture2Ds is %d frames - "
213 "Number of DPB virtual entries is %" PRIu64 " entries for frame with OrderHint "
214 "%d (PictureIndex %d) are: \n%s \n",
215 m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb(),
216 static_cast<uint64_t>(m_CurrentFrameReferencesData.pVirtualDPBEntries.size()),
217 m_CurrentFramePicParams.OrderHint,
218 m_CurrentFramePicParams.PictureIndex,
219 dpbContents.c_str());
220 }
221
222 void
print_physical_resource_references()223 d3d12_video_encoder_references_manager_av1::print_physical_resource_references()
224 {
225 debug_printf("[D3D12 Video Encoder Picture Manager AV1] %d physical resources IN USE out of a total of %d physical "
226 "resources ALLOCATED "
227 "resources at end_frame for frame with OrderHint %d (PictureIndex %d)\n",
228 m_PhysicalAllocationsStorage.get_number_of_in_use_allocations(),
229 m_PhysicalAllocationsStorage.get_number_of_tracked_allocations(),
230 m_CurrentFramePicParams.OrderHint,
231 m_CurrentFramePicParams.PictureIndex);
232
233 D3D12_VIDEO_ENCODE_REFERENCE_FRAMES curRefs = get_current_reference_frames();
234 std::string descContents;
235 for (uint32_t index = 0; index < curRefs.NumTexture2Ds; index++) {
236 assert(curRefs.ppTexture2Ds[index]); // These must be contiguous when sending down to D3D12 API
237 descContents += "{ REFERENCE_FRAMES Index: ";
238 descContents += std::to_string(index);
239 descContents += " - ppTextures ptr: ";
240 char strBuf[256];
241 memset(&strBuf, '\0', 256);
242 sprintf(strBuf, "%p", curRefs.ppTexture2Ds[index]);
243 descContents += std::string(strBuf);
244 descContents += " - ppSubresources index: ";
245 if (curRefs.pSubresources != nullptr)
246 descContents += std::to_string(curRefs.pSubresources[index]);
247 else
248 descContents += "(nil)";
249 descContents += " - RefCount (from any virtual dpb slot [0..REFS_PER_FRAME]): ";
250 descContents += std::to_string(get_dpb_physical_slot_refcount_from_virtual_dpb(index));
251 descContents += " }\n";
252 }
253
254 debug_printf("[D3D12 Video Encoder Picture Manager AV1] D3D12_VIDEO_ENCODE_REFERENCE_FRAMES has %d physical "
255 "resources in ppTexture2Ds for OrderHint "
256 "%d (PictureIndex %d) are: \n%s \n",
257 curRefs.NumTexture2Ds,
258 m_CurrentFramePicParams.OrderHint,
259 m_CurrentFramePicParams.PictureIndex,
260 descContents.c_str());
261 }
262
263 //
264 // Returns the number of Reference<XXX> (ie. LAST, LAST2, ..., ALTREF, etc)
265 // pointing to dpbSlotIndex
266 //
267 uint32_t
get_dpb_virtual_slot_refcount_from_ref_frame_idx(uint32_t dpbSlotIndex)268 d3d12_video_encoder_references_manager_av1::get_dpb_virtual_slot_refcount_from_ref_frame_idx(uint32_t dpbSlotIndex)
269 {
270 uint32_t refCount = 0;
271 for (uint8_t i = 0; i < ARRAY_SIZE(m_CurrentFramePicParams.ReferenceIndices); i++) {
272 if (m_CurrentFramePicParams.ReferenceIndices[i] == dpbSlotIndex) {
273 refCount++;
274 }
275 }
276
277 return refCount;
278 }
279
280 //
281 // Returns the number of entries in the virtual DPB descriptors
282 // that point to physicalSlotIndex
283 //
284 uint32_t
get_dpb_physical_slot_refcount_from_virtual_dpb(uint32_t physicalSlotIndex)285 d3d12_video_encoder_references_manager_av1::get_dpb_physical_slot_refcount_from_virtual_dpb(uint32_t physicalSlotIndex)
286 {
287 uint32_t refCount = 0;
288 for (unsigned dpbSlotIndex = 0; dpbSlotIndex < m_CurrentFrameReferencesData.pVirtualDPBEntries.size();
289 dpbSlotIndex++) {
290 if (m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIndex].ReconstructedPictureResourceIndex ==
291 physicalSlotIndex)
292 refCount++;
293 }
294 return refCount;
295 }
296
297 bool
get_current_frame_picture_control_data(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA & codecAllocation)298 d3d12_video_encoder_references_manager_av1::get_current_frame_picture_control_data(
299 D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation)
300 {
301 assert(m_CurrentFrameReferencesData.pVirtualDPBEntries.size() == NUM_REF_FRAMES);
302 assert(codecAllocation.DataSize == sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA));
303
304 // Some apps don't clean this up for INTRA/KEY frames
305 if ((m_CurrentFramePicParams.FrameType != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME)
306 && (m_CurrentFramePicParams.FrameType != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME))
307 {
308 for (uint8_t i = 0; i < ARRAY_SIZE(m_CurrentFramePicParams.ReferenceIndices); i++) {
309 m_CurrentFramePicParams.ReferenceIndices[i] = 0;
310 }
311 }
312
313 for (uint8_t i = 0; i < NUM_REF_FRAMES; i++)
314 m_CurrentFramePicParams.ReferenceFramesReconPictureDescriptors[i] =
315 m_CurrentFrameReferencesData.pVirtualDPBEntries[i];
316
317 #if MESA_DEBUG // Otherwise may iterate over structures and do no-op debug_printf
318 print_ref_frame_idx();
319 print_virtual_dpb_entries();
320 print_physical_resource_references();
321 #endif
322
323 *codecAllocation.pAV1PicData = m_CurrentFramePicParams;
324 return true;
325 }
326
327 void
refresh_dpb_slots_with_current_frame_reconpic()328 d3d12_video_encoder_references_manager_av1::refresh_dpb_slots_with_current_frame_reconpic()
329 {
330 UINT refresh_frame_flags = m_CurrentFramePicParams.RefreshFrameFlags;
331 debug_printf("[D3D12 Video Encoder Picture Manager AV1] refresh_frame_flags 0x%x for frame with OrderHint %d "
332 "(PictureIndex %d)\n",
333 refresh_frame_flags,
334 m_CurrentFramePicParams.OrderHint,
335 m_CurrentFramePicParams.PictureIndex);
336
337 //
338 // Put current frame in all slots of DPB indicated by refresh_frame_flags
339 //
340 if (is_current_frame_used_as_reference() && m_gopHasInterFrames && (refresh_frame_flags != 0)) {
341
342 //
343 // First do a eviction pass and update virtual DPB physical indices in case the physical array shifted with an
344 // eviction (erasing an ppTexture2Ds entry)
345 //
346
347 for (unsigned dpbSlotIdx = 0; dpbSlotIdx < NUM_REF_FRAMES; dpbSlotIdx++) {
348 if (((refresh_frame_flags >> dpbSlotIdx) & 0x1) != 0) {
349
350 //
351 // Check if the slot this reconpic will take in the virtual DPB will leave an unreferenced
352 // physical allocation, that may need to be evicted (if no other virtual dpb slot references it)
353 //
354
355 if (m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex !=
356 UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) {
357
358 // If this is a virtual dpb valid entry, there has to be a valid underlying physical allocation
359 assert(m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex <
360 m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb());
361
362 // Get the number of entries in the virtual DPB descriptors that point to
363 // ReconstructedPictureResourceIndex of the current virtual dpb slot (counting the current dpbSlotIdx we
364 // didn't clear yet)
365 uint32_t numRefs = get_dpb_physical_slot_refcount_from_virtual_dpb(
366 m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex);
367 if (numRefs == 1) {
368 // When refreshing this dpbSlotIdx, we will leave an unreferenced physical allocation
369 // so we can just remove it (and release the ID3D12Resource allocation back to the unused pool)
370
371 bool wasTracked = false;
372 m_PhysicalAllocationsStorage.remove_reference_frame(
373 m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex,
374 &wasTracked);
375 assert(wasTracked);
376
377 // Indices in the virtual dpb higher or equal than
378 // m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex
379 // must be shifted back one place as we erased the entry in the physical allocations array (ppTexture2Ds)
380 for (auto &dpbVirtualEntry : m_CurrentFrameReferencesData.pVirtualDPBEntries) {
381 if (
382 // Check for slot being used
383 (dpbVirtualEntry.ReconstructedPictureResourceIndex != UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) &&
384 // Check for slot to be affected by removing the entry in ppTexture2Ds above
385 (dpbVirtualEntry.ReconstructedPictureResourceIndex >
386 m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex)) {
387
388 // Decrease the index to compensate for the removed ppTexture2Ds entry
389 dpbVirtualEntry.ReconstructedPictureResourceIndex--;
390 }
391 }
392 }
393
394 // Clear this virtual dpb entry (so next iterations will decrease refcount)
395 m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex =
396 UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX;
397 }
398 }
399 }
400
401 //
402 // Find a new free physical index for the current recon pic; always put new physical entry at the end to avoid
403 // having to shift existing indices in virtual DPB
404 //
405 UINT allocationSlotIdx = m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb();
406 assert(static_cast<uint32_t>(allocationSlotIdx) < NUM_REF_FRAMES);
407 D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE recAlloc = get_current_frame_recon_pic_output_allocation();
408 d3d12_video_reconstructed_picture refFrameDesc = {};
409 refFrameDesc.pReconstructedPicture = recAlloc.pReconstructedPicture;
410 refFrameDesc.ReconstructedPictureSubresource = recAlloc.ReconstructedPictureSubresource;
411 m_PhysicalAllocationsStorage.insert_reference_frame(refFrameDesc, allocationSlotIdx);
412
413 //
414 // Update virtual DPB entries with the current frame recon picture
415 //
416 for (unsigned dpbSlotIdx = 0; dpbSlotIdx < NUM_REF_FRAMES; dpbSlotIdx++) {
417 if (((refresh_frame_flags >> dpbSlotIdx) & 0x1) != 0) {
418 m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx] = { allocationSlotIdx,
419 0, // NO temporal scalability support
420 0, // NO spatial scalability support
421 m_CurrentFramePicParams.FrameType,
422 {}, // No global_motion support
423 m_CurrentFramePicParams.OrderHint,
424 m_CurrentFramePicParams.PictureIndex };
425 }
426 }
427 }
428
429 // Number of allocations, disregarding if they are used or not, should not exceed this limit due to reuse policies
430 // on DPB items removal.
431 assert(m_PhysicalAllocationsStorage.get_number_of_tracked_allocations() <= (NUM_REF_FRAMES + 1));
432 }
433