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_common.h"
25 
26 #include "d3d12_util.h"
27 #include "d3d12_context.h"
28 #include "d3d12_format.h"
29 #include "d3d12_resource.h"
30 #include "d3d12_screen.h"
31 #include "d3d12_surface.h"
32 #include "d3d12_video_enc.h"
33 #include "d3d12_video_enc_h264.h"
34 #include "d3d12_video_buffer.h"
35 #include "d3d12_video_texture_array_dpb_manager.h"
36 #include "d3d12_video_array_of_textures_dpb_manager.h"
37 #include "d3d12_video_encoder_references_manager_h264.h"
38 #include "d3d12_residency.h"
39 
40 #include "vl/vl_video_buffer.h"
41 #include "util/format/u_format.h"
42 #include "util/u_inlines.h"
43 #include "util/u_memory.h"
44 #include "util/u_video.h"
45 
46 #include <cmath>
47 
48 /**
49  * flush any outstanding command buffers to the hardware
50  * should be called before a video_buffer is acessed by the gallium frontend again
51  */
52 void
d3d12_video_encoder_flush(struct pipe_video_codec *codec)53 d3d12_video_encoder_flush(struct pipe_video_codec *codec)
54 {
55    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
56    assert(pD3D12Enc);
57    assert(pD3D12Enc->m_spD3D12VideoDevice);
58    assert(pD3D12Enc->m_spEncodeCommandQueue);
59 
60    // Flush buffer_subdata batch and Wait the m_spEncodeCommandQueue for GPU upload completion
61    // before recording EncodeFrame below.
62    struct pipe_fence_handle *completion_fence = NULL;
63    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - Flushing pD3D12Enc->base.context and GPU sync between Video/Context queues before flushing Video Encode Queue.\n");
64    pD3D12Enc->base.context->flush(pD3D12Enc->base.context, &completion_fence, PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH);
65    assert(completion_fence);
66    struct d3d12_fence *casted_completion_fence = d3d12_fence(completion_fence);
67    pD3D12Enc->m_spEncodeCommandQueue->Wait(casted_completion_fence->cmdqueue_fence, casted_completion_fence->value);
68    pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, &completion_fence, NULL);
69 
70    if (!pD3D12Enc->m_needsGPUFlush) {
71       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush started. Nothing to flush, all up to date.\n");
72    } else {
73       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush started. Will flush video queue work and CPU wait "
74                     "on fenceValue: %d\n",
75                     pD3D12Enc->m_fenceValue);
76 
77       HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
78       if (hr != S_OK) {
79          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush"
80                          " - D3D12Device was removed BEFORE commandlist "
81                          "execution with HR %x.\n",
82                          hr);
83          goto flush_fail;
84       }
85 
86       // Close and execute command list and wait for idle on CPU blocking
87       // this method before resetting list and allocator for next submission.
88 
89       if (pD3D12Enc->m_transitionsBeforeCloseCmdList.size() > 0) {
90          pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(pD3D12Enc->m_transitionsBeforeCloseCmdList.size(),
91                                                            pD3D12Enc->m_transitionsBeforeCloseCmdList.data());
92          pD3D12Enc->m_transitionsBeforeCloseCmdList.clear();
93       }
94 
95       hr = pD3D12Enc->m_spEncodeCommandList->Close();
96       if (FAILED(hr)) {
97          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - Can't close command list with HR %x\n", hr);
98          goto flush_fail;
99       }
100 
101       ID3D12CommandList *ppCommandLists[1] = { pD3D12Enc->m_spEncodeCommandList.Get() };
102       pD3D12Enc->m_spEncodeCommandQueue->ExecuteCommandLists(1, ppCommandLists);
103       pD3D12Enc->m_spEncodeCommandQueue->Signal(pD3D12Enc->m_spFence.Get(), pD3D12Enc->m_fenceValue);
104       pD3D12Enc->m_spFence->SetEventOnCompletion(pD3D12Enc->m_fenceValue, nullptr);
105       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - ExecuteCommandLists finished on signal with "
106                     "fenceValue: %d\n",
107                     pD3D12Enc->m_fenceValue);
108 
109       hr = pD3D12Enc->m_spCommandAllocator->Reset();
110       if (FAILED(hr)) {
111          debug_printf(
112             "[d3d12_video_encoder] d3d12_video_encoder_flush - resetting ID3D12CommandAllocator failed with HR %x\n",
113             hr);
114          goto flush_fail;
115       }
116 
117       hr = pD3D12Enc->m_spEncodeCommandList->Reset(pD3D12Enc->m_spCommandAllocator.Get());
118       if (FAILED(hr)) {
119          debug_printf(
120             "[d3d12_video_encoder] d3d12_video_encoder_flush - resetting ID3D12GraphicsCommandList failed with HR %x\n",
121             hr);
122          goto flush_fail;
123       }
124 
125       // Validate device was not removed
126       hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
127       if (hr != S_OK) {
128          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush"
129                          " - D3D12Device was removed AFTER commandlist "
130                          "execution with HR %x, but wasn't before.\n",
131                          hr);
132          goto flush_fail;
133       }
134 
135       debug_printf(
136          "[d3d12_video_encoder] d3d12_video_encoder_flush - GPU signaled execution finalized for fenceValue: %d\n",
137          pD3D12Enc->m_fenceValue);
138 
139       pD3D12Enc->m_fenceValue++;
140       pD3D12Enc->m_needsGPUFlush = false;
141    }
142    return;
143 
144 flush_fail:
145    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush failed for fenceValue: %d\n", pD3D12Enc->m_fenceValue);
146    assert(false);
147 }
148 
149 /**
150  * Destroys a d3d12_video_encoder
151  * Call destroy_XX for applicable XX nested member types before deallocating
152  * Destroy methods should check != nullptr on their input target argument as this method can be called as part of
153  * cleanup from failure on the creation method
154  */
155 void
d3d12_video_encoder_destroy(struct pipe_video_codec *codec)156 d3d12_video_encoder_destroy(struct pipe_video_codec *codec)
157 {
158    if (codec == nullptr) {
159       return;
160    }
161 
162    d3d12_video_encoder_flush(codec);   // Flush pending work before destroying.
163 
164    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
165 
166    // Call d3d12_video_encoder dtor to make ComPtr and other member's destructors work
167    delete pD3D12Enc;
168 }
169 
170 void
d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12Enc, struct pipe_video_buffer * srcTexture, struct pipe_picture_desc * picture)171 d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12Enc,
172                                               struct pipe_video_buffer *  srcTexture,
173                                               struct pipe_picture_desc *  picture)
174 {
175    D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
176       d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
177 
178    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
179    bool bUsedAsReference = false;
180    switch (codec) {
181       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
182       {
183          d3d12_video_encoder_update_current_frame_pic_params_info_h264(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference);
184       } break;
185 
186       default:
187       {
188          unreachable("Unsupported pipe_video_format");
189       } break;
190    }
191 
192    pD3D12Enc->m_upDPBManager->begin_frame(currentPicParams, bUsedAsReference);
193 }
194 
195 bool
d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D12Enc, struct pipe_video_buffer * srcTexture, struct pipe_picture_desc * picture)196 d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D12Enc,
197                                                 struct pipe_video_buffer *  srcTexture,
198                                                 struct pipe_picture_desc *  picture)
199 {
200    bool codecChanged =
201       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_codec) != 0);
202    bool profileChanged =
203       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_profile) != 0);
204    bool levelChanged =
205       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_level) != 0);
206    bool codecConfigChanged =
207       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_codec_config) != 0);
208    bool inputFormatChanged =
209       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_input_format) != 0);
210    bool resolutionChanged =
211       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_resolution) != 0);
212    bool rateControlChanged =
213       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_rate_control) != 0);
214    bool slicesChanged =
215       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_slices) != 0);
216    bool gopChanged =
217       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_gop) != 0);
218    bool motionPrecisionLimitChanged = ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags &
219                                         d3d12_video_encoder_config_dirty_flag_motion_precision_limit) != 0);
220 
221    // Events that that trigger a re-creation of the reference picture manager
222    // Stores codec agnostic textures so only input format, resolution and gop (num dpb references) affects this
223    if (!pD3D12Enc->m_upDPBManager
224        // || codecChanged
225        // || profileChanged
226        // || levelChanged
227        // || codecConfigChanged
228        || inputFormatChanged ||
229        resolutionChanged
230        // || rateControlChanged
231        // || slicesChanged
232        || gopChanged
233        // || motionPrecisionLimitChanged
234    ) {
235       if (!pD3D12Enc->m_upDPBManager) {
236          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating Reference "
237                        "Pictures Manager for the first time\n");
238       } else {
239          debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating Reference Pictures Manager\n");
240       }
241 
242       D3D12_RESOURCE_FLAGS resourceAllocFlags =
243          D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
244       bool     fArrayOfTextures = ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
245                                 D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) == 0);
246       uint32_t texturePoolSize  = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc) +
247                                  1u;   // adding an extra slot as we also need to count the current frame output recon
248                                        // allocation along max reference frame allocations
249       assert(texturePoolSize < UINT16_MAX);
250       if (fArrayOfTextures) {
251          pD3D12Enc->m_upDPBStorageManager = std::make_unique<d3d12_array_of_textures_dpb_manager>(
252             static_cast<uint16_t>(texturePoolSize),
253             pD3D12Enc->m_pD3D12Screen->dev,
254             pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
255             pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
256             (D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE),
257             true,   // setNullSubresourcesOnAllZero - D3D12 Video Encode expects nullptr pSubresources if AoT,
258             pD3D12Enc->m_NodeMask,
259             /*use underlying pool, we can't reuse upper level allocations, need D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY*/
260             true);
261       } else {
262          pD3D12Enc->m_upDPBStorageManager = std::make_unique<d3d12_texture_array_dpb_manager>(
263             static_cast<uint16_t>(texturePoolSize),
264             pD3D12Enc->m_pD3D12Screen->dev,
265             pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
266             pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
267             resourceAllocFlags,
268             pD3D12Enc->m_NodeMask);
269       }
270       d3d12_video_encoder_create_reference_picture_manager(pD3D12Enc);
271    }
272 
273    bool reCreatedEncoder = false;
274    // Events that that trigger a re-creation of the encoder
275    if (!pD3D12Enc->m_spVideoEncoder || codecChanged ||
276        profileChanged
277        // || levelChanged // Only affects encoder heap
278        || codecConfigChanged ||
279        inputFormatChanged
280        // || resolutionChanged // Only affects encoder heap
281        // Only re-create if there is NO SUPPORT for reconfiguring rateControl on the fly
282        || (rateControlChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
283                                    D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) ==
284                                   0 /*checking the flag is NOT set*/))
285        // Only re-create if there is NO SUPPORT for reconfiguring slices on the fly
286        || (slicesChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
287                               D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) ==
288                              0 /*checking the flag is NOT set*/))
289        // Only re-create if there is NO SUPPORT for reconfiguring gop on the fly
290        || (gopChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
291                            D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) ==
292                           0 /*checking the flag is NOT set*/)) ||
293        motionPrecisionLimitChanged) {
294       if (!pD3D12Enc->m_spVideoEncoder) {
295          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating "
296                        "D3D12VideoEncoder for the first time\n");
297       } else {
298          debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating D3D12VideoEncoder\n");
299          reCreatedEncoder = true;
300       }
301 
302       D3D12_VIDEO_ENCODER_DESC encoderDesc = { pD3D12Enc->m_NodeMask,
303                                                D3D12_VIDEO_ENCODER_FLAG_NONE,
304                                                pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
305                                                d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
306                                                pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
307                                                d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc),
308                                                pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit };
309 
310       // Create encoder
311       HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CreateVideoEncoder(&encoderDesc,
312                                                              IID_PPV_ARGS(pD3D12Enc->m_spVideoEncoder.GetAddressOf()));
313       if (FAILED(hr)) {
314          debug_printf("CreateVideoEncoder failed with HR %x\n", hr);
315          return false;
316       }
317    }
318 
319    bool reCreatedEncoderHeap = false;
320    // Events that that trigger a re-creation of the encoder heap
321    if (!pD3D12Enc->m_spVideoEncoderHeap || codecChanged || profileChanged ||
322        levelChanged
323        // || codecConfigChanged // Only affects encoder
324        || inputFormatChanged   // Might affect internal textures in the heap
325        || resolutionChanged
326        // Only re-create if there is NO SUPPORT for reconfiguring rateControl on the fly
327        || (rateControlChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
328                                    D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) ==
329                                   0 /*checking the flag is NOT set*/))
330        // Only re-create if there is NO SUPPORT for reconfiguring slices on the fly
331        || (slicesChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
332                               D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) ==
333                              0 /*checking the flag is NOT set*/))
334        // Only re-create if there is NO SUPPORT for reconfiguring gop on the fly
335        || (gopChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
336                            D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) ==
337                           0 /*checking the flag is NOT set*/))
338        // || motionPrecisionLimitChanged // Only affects encoder
339    ) {
340       if (!pD3D12Enc->m_spVideoEncoderHeap) {
341          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating "
342                        "D3D12VideoEncoderHeap for the first time\n");
343       } else {
344          debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating D3D12VideoEncoderHeap\n");
345          reCreatedEncoderHeap = true;
346       }
347 
348       D3D12_VIDEO_ENCODER_HEAP_DESC heapDesc = { pD3D12Enc->m_NodeMask,
349                                                  D3D12_VIDEO_ENCODER_HEAP_FLAG_NONE,
350                                                  pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
351                                                  d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
352                                                  d3d12_video_encoder_get_current_level_desc(pD3D12Enc),
353                                                  // resolution list count
354                                                  1,
355                                                  // resolution list
356                                                  &pD3D12Enc->m_currentEncodeConfig.m_currentResolution };
357 
358       // Create encoder heap
359       HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CreateVideoEncoderHeap(&heapDesc,
360                                                                            IID_PPV_ARGS(pD3D12Enc->m_spVideoEncoderHeap.GetAddressOf()));
361       if (FAILED(hr)) {
362          debug_printf("CreateVideoEncoderHeap failed with HR %x\n", hr);
363          return false;
364       }
365    }
366 
367    // If on-the-fly reconfiguration happened without object recreation, set
368    // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_*_CHANGED reconfiguration flags in EncodeFrame
369    if (rateControlChanged &&
370        ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
371          D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) !=
372         0 /*checking if the flag it's actually set*/) &&
373        (pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
374       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RATE_CONTROL_CHANGE;
375    }
376 
377    if (slicesChanged &&
378        ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
379          D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) !=
380         0 /*checking if the flag it's actually set*/) &&
381        (pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
382       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_SUBREGION_LAYOUT_CHANGE;
383    }
384 
385    if (gopChanged &&
386        ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
387          D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) !=
388         0 /*checking if the flag it's actually set*/) &&
389        (pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
390       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_GOP_SEQUENCE_CHANGE;
391    }
392    return true;
393 }
394 
395 void
396 d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder *pD3D12Enc)
397 {
398    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
399    switch (codec) {
400       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
401       {
402          bool gopHasPFrames =
403             (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.PPicturePeriod > 0) &&
404             ((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.GOPLength == 0) ||
405              (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.PPicturePeriod <
406               pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.GOPLength));
407 
408          pD3D12Enc->m_upDPBManager = std::make_unique<d3d12_video_encoder_references_manager_h264>(
409             gopHasPFrames,
410             *pD3D12Enc->m_upDPBStorageManager,
411             // Max number of frames to be used as a reference, without counting the current recon picture
412             d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc)
413          );
414 
415          pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_h264>();
416       } break;
417 
418       default:
419       {
420          unreachable("Unsupported pipe_video_format");
421       } break;
422    }
423 }
424 
425 D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA
426 d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder *pD3D12Enc)
427 {
428    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
429    switch (codec) {
430       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
431       {
432          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {};
433          if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode !=
434              D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) {
435             subregionData.pSlicesPartition_H264 =
436                &pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264;
437             subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES);
438          }
439          return subregionData;
440       } break;
441 
442       default:
443       {
444          unreachable("Unsupported pipe_video_format");
445       } break;
446    }
447 }
448 
449 D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA
450 d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encoder *pD3D12Enc)
451 {
452    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
453    switch (codec) {
454       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
455       {
456          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {};
457          curPicParamsData.pH264PicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_H264PicData;
458          curPicParamsData.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_H264PicData);
459          return curPicParamsData;
460       } break;
461 
462       default:
463       {
464          unreachable("Unsupported pipe_video_format");
465       } break;
466    }
467 }
468 
469 D3D12_VIDEO_ENCODER_RATE_CONTROL
470 d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder *pD3D12Enc)
471 {
472    D3D12_VIDEO_ENCODER_RATE_CONTROL curRateControlDesc = {};
473    curRateControlDesc.Mode            = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode;
474    curRateControlDesc.Flags           = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags;
475    curRateControlDesc.TargetFrameRate = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate;
476 
477    switch (pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode) {
478       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_ABSOLUTE_QP_MAP:
479       {
480          curRateControlDesc.ConfigParams.pConfiguration_CQP = nullptr;
481          curRateControlDesc.ConfigParams.DataSize           = 0;
482       } break;
483       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP:
484       {
485          curRateControlDesc.ConfigParams.pConfiguration_CQP =
486             &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP;
487          curRateControlDesc.ConfigParams.DataSize =
488             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP);
489       } break;
490       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
491       {
492          curRateControlDesc.ConfigParams.pConfiguration_CBR =
493             &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR;
494          curRateControlDesc.ConfigParams.DataSize =
495             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR);
496       } break;
497       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
498       {
499          curRateControlDesc.ConfigParams.pConfiguration_VBR =
500             &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR;
501          curRateControlDesc.ConfigParams.DataSize =
502             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR);
503       } break;
504       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
505       {
506          curRateControlDesc.ConfigParams.pConfiguration_QVBR =
507             &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR;
508          curRateControlDesc.ConfigParams.DataSize =
509             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR);
510       } break;
511       default:
512       {
513          unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE");
514       } break;
515    }
516 
517    return curRateControlDesc;
518 }
519 
520 D3D12_VIDEO_ENCODER_LEVEL_SETTING
521 d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc)
522 {
523    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
524    switch (codec) {
525       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
526       {
527          D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {};
528          curLevelDesc.pH264LevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting;
529          curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting);
530          return curLevelDesc;
531       } break;
532 
533       default:
534       {
535          unreachable("Unsupported pipe_video_format");
536       } break;
537    }
538 }
539 
540 uint32_t
541 d3d12_video_encoder_build_codec_headers(struct d3d12_video_encoder *pD3D12Enc)
542 {
543    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
544    switch (codec) {
545       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
546       {
547          return d3d12_video_encoder_build_codec_headers_h264(pD3D12Enc);
548 
549       } break;
550 
551       default:
552       {
553          unreachable("Unsupported pipe_video_format");
554       } break;
555    }
556    return 0u;
557 }
558 
559 D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE
560 d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder *pD3D12Enc)
561 {
562    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
563    switch (codec) {
564       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
565       {
566          D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {};
567          curGOPDesc.pH264GroupOfPictures =
568             &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures;
569          curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures);
570          return curGOPDesc;
571       } break;
572 
573       default:
574       {
575          unreachable("Unsupported pipe_video_format");
576       } break;
577    }
578 }
579 
580 D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION
581 d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder *pD3D12Enc)
582 {
583    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
584    switch (codec) {
585       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
586       {
587          D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {};
588          codecConfigDesc.pH264Config = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config;
589          codecConfigDesc.DataSize =
590             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config);
591          return codecConfigDesc;
592       } break;
593 
594       default:
595       {
596          unreachable("Unsupported pipe_video_format");
597       } break;
598    }
599 }
600 
601 D3D12_VIDEO_ENCODER_CODEC
602 d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc)
603 {
604    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
605    switch (codec) {
606       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
607       {
608          return D3D12_VIDEO_ENCODER_CODEC_H264;
609       } break;
610       default:
611       {
612          unreachable("Unsupported pipe_video_format");
613       } break;
614    }
615 }
616 
617 ///
618 /// Call d3d12_video_encoder_query_d3d12_driver_caps and see if any optional feature requested
619 /// is not supported, disable it, query again until finding a negotiated cap/feature set
620 /// Note that with fallbacks, the upper layer will not get exactly the encoding seetings they requested
621 /// but for very particular settings it's better to continue with warnings than failing the whole encoding process
622 ///
623 bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData) {
624 
625    ///
626    /// Check for general support
627    /// Check for validation errors (some drivers return general support but also validation errors anyways, work around for those unexpected cases)
628    ///
629 
630    bool configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData)
631     && (((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0)
632                         && (capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
633 
634    ///
635    /// If rate control config is not supported, try falling back and check for caps again
636    ///
637 
638    if ((capEncoderSupportData.ValidationFlags & (D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED | D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED)) != 0) {
639 
640       if (D3D12_VIDEO_ENC_FALLBACK_RATE_CONTROL_CONFIG){ // Check if fallback mode is enabled, or we should just fail without support
641 
642          debug_printf("[d3d12_video_encoder] WARNING: Requested rate control is not supported, trying fallback to unsetting optional features\n");
643 
644          bool isRequestingVBVSizesSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_VBV_SIZE_CONFIG_AVAILABLE) != 0);
645          bool isClientRequestingVBVSizes = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES) != 0);
646 
647          if(isClientRequestingVBVSizes && !isRequestingVBVSizesSupported) {
648             debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES with VBVCapacity (bits): %" PRIu64 " and InitialVBVFullness (bits) %" PRIu64 " is not supported, will continue encoding unsetting this feature as fallback.\n",
649                   pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity,
650                   pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness);
651 
652             pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
653             pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity = 0;
654             pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness = 0;
655          }
656 
657          bool isRequestingPeakFrameSizeSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0);
658          bool isClientRequestingPeakFrameSize = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE) != 0);
659 
660          if(isClientRequestingPeakFrameSize && !isRequestingPeakFrameSizeSupported) {
661             debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE with MaxFrameBitSize %" PRIu64 " but the feature is not supported, will continue encoding unsetting this feature as fallback.\n",
662                pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxFrameBitSize);
663 
664             pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
665             pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxFrameBitSize = 0;
666          }
667 
668          ///
669          /// Try fallback configuration
670          ///
671          configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData)
672           && (((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0)
673                            && (capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
674 
675       } else {
676          debug_printf("[d3d12_video_encoder] WARNING: Requested rate control is not supported. To continue with a fallback, must enable the OS environment variable D3D12_VIDEO_ENC_FALLBACK_RATE_CONTROL_CONFIG\n");
677       }
678    }
679 
680    if(!configSupported) {
681       debug_printf("[d3d12_video_encoder] Cap negotiation failed, see more details below:\n");
682 
683       if ((capEncoderSupportData.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_NOT_SUPPORTED) != 0) {
684          debug_printf("[d3d12_video_encoder] Requested codec is not supported\n");
685       }
686 
687       if ((capEncoderSupportData.ValidationFlags &
688          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RESOLUTION_NOT_SUPPORTED_IN_LIST) != 0) {
689          debug_printf("[d3d12_video_encoder] Requested resolution is not supported\n");
690       }
691 
692       if ((capEncoderSupportData.ValidationFlags &
693          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED) != 0) {
694          debug_printf("[d3d12_video_encoder] Requested bitrate or rc config is not supported\n");
695       }
696 
697       if ((capEncoderSupportData.ValidationFlags &
698          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_CONFIGURATION_NOT_SUPPORTED) != 0) {
699          debug_printf("[d3d12_video_encoder] Requested codec config is not supported\n");
700       }
701 
702       if ((capEncoderSupportData.ValidationFlags &
703          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED) != 0) {
704          debug_printf("[d3d12_video_encoder] Requested rate control mode is not supported\n");
705       }
706 
707       if ((capEncoderSupportData.ValidationFlags &
708          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INTRA_REFRESH_MODE_NOT_SUPPORTED) != 0) {
709          debug_printf("[d3d12_video_encoder] Requested intra refresh config is not supported\n");
710       }
711 
712       if ((capEncoderSupportData.ValidationFlags &
713          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_SUBREGION_LAYOUT_MODE_NOT_SUPPORTED) != 0) {
714          debug_printf("[d3d12_video_encoder] Requested subregion layout mode is not supported\n");
715       }
716 
717       if ((capEncoderSupportData.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INPUT_FORMAT_NOT_SUPPORTED) !=
718          0) {
719          debug_printf("[d3d12_video_encoder] Requested input dxgi format is not supported\n");
720       }
721    }
722 
723    return configSupported;
724 }
725 
726 bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData) {
727    capEncoderSupportData.NodeIndex                                = pD3D12Enc->m_NodeIndex;
728    capEncoderSupportData.Codec                                    = d3d12_video_encoder_get_current_codec(pD3D12Enc);
729    capEncoderSupportData.InputFormat            = pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format;
730    capEncoderSupportData.RateControl            = d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc);
731    capEncoderSupportData.IntraRefresh           = pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.Mode;
732    capEncoderSupportData.SubregionFrameEncoding = pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode;
733    capEncoderSupportData.ResolutionsListCount   = 1;
734    capEncoderSupportData.pResolutionList        = &pD3D12Enc->m_currentEncodeConfig.m_currentResolution;
735    capEncoderSupportData.CodecGopSequence       = d3d12_video_encoder_get_current_gop_desc(pD3D12Enc);
736    capEncoderSupportData.MaxReferenceFramesInDPB = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
737    capEncoderSupportData.CodecConfiguration = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc);
738 
739    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
740    switch (codec) {
741       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
742       {
743          capEncoderSupportData.SuggestedProfile.pH264Profile =
744             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile;
745          capEncoderSupportData.SuggestedProfile.DataSize =
746             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile);
747          capEncoderSupportData.SuggestedLevel.pH264LevelSetting =
748             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting;
749          capEncoderSupportData.SuggestedLevel.DataSize =
750             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting);
751       } break;
752 
753       default:
754       {
755          unreachable("Unsupported pipe_video_format");
756       } break;
757    }
758 
759    // prepare inout storage for the resolution dependent result.
760    capEncoderSupportData.pResolutionDependentSupport =
761       &pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps;
762 
763    HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
764                                                                          &capEncoderSupportData,
765                                                                          sizeof(capEncoderSupportData));
766    if (FAILED(hr)) {
767       debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
768       return false;
769    }
770    pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags    = capEncoderSupportData.SupportFlags;
771    pD3D12Enc->m_currentEncodeCapabilities.m_ValidationFlags = capEncoderSupportData.ValidationFlags;
772    return true;
773 }
774 
775 bool d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder *pD3D12Enc,
776                                     D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode
777    )
778 {
779    D3D12_FEATURE_DATA_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE capDataSubregionLayout = { };
780    capDataSubregionLayout.NodeIndex = pD3D12Enc->m_NodeIndex;
781    capDataSubregionLayout.Codec = d3d12_video_encoder_get_current_codec(pD3D12Enc);
782    capDataSubregionLayout.Profile = d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
783    capDataSubregionLayout.Level = d3d12_video_encoder_get_current_level_desc(pD3D12Enc);
784    capDataSubregionLayout.SubregionMode = requestedSlicesMode;
785    HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE, &capDataSubregionLayout, sizeof(capDataSubregionLayout));
786    if (FAILED(hr)) {
787       debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
788       return false;
789    }
790    return capDataSubregionLayout.IsSupported;
791 }
792 
793 D3D12_VIDEO_ENCODER_PROFILE_DESC
794 d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder *pD3D12Enc)
795 {
796    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
797    switch (codec) {
798       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
799       {
800          D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {};
801          curProfDesc.pH264Profile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile;
802          curProfDesc.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile);
803          return curProfDesc;
804       } break;
805 
806       default:
807       {
808          unreachable("Unsupported pipe_video_format");
809       } break;
810    }
811 }
812 
813 uint32_t
814 d3d12_video_encoder_get_current_max_dpb_capacity(struct d3d12_video_encoder *pD3D12Enc)
815 {
816    return pD3D12Enc->base.max_references;
817 }
818 
819 bool
820 d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encoder *pD3D12Enc,
821                                                         struct pipe_video_buffer *  srcTexture,
822                                                         struct pipe_picture_desc *  picture)
823 {
824    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
825    switch (codec) {
826       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
827       {
828          return d3d12_video_encoder_update_current_encoder_config_state_h264(pD3D12Enc, srcTexture, picture);
829       } break;
830 
831       default:
832       {
833          unreachable("Unsupported pipe_video_format");
834       } break;
835    }
836 }
837 
838 bool
839 d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc)
840 {
841    assert(pD3D12Enc->m_spD3D12VideoDevice);
842 
843    D3D12_COMMAND_QUEUE_DESC commandQueueDesc = { D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE };
844    HRESULT                  hr               = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandQueue(
845       &commandQueueDesc,
846       IID_PPV_ARGS(pD3D12Enc->m_spEncodeCommandQueue.GetAddressOf()));
847    if (FAILED(hr)) {
848       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateCommandQueue "
849                       "failed with HR %x\n",
850                       hr);
851       return false;
852    }
853 
854    hr = pD3D12Enc->m_pD3D12Screen->dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pD3D12Enc->m_spFence));
855    if (FAILED(hr)) {
856       debug_printf(
857          "[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateFence failed with HR %x\n",
858          hr);
859       return false;
860    }
861 
862    hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandAllocator(
863       D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
864       IID_PPV_ARGS(pD3D12Enc->m_spCommandAllocator.GetAddressOf()));
865    if (FAILED(hr)) {
866       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to "
867                       "CreateCommandAllocator failed with HR %x\n",
868                       hr);
869       return false;
870    }
871 
872    hr =
873       pD3D12Enc->m_pD3D12Screen->dev->CreateCommandList(0,
874                                                         D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
875                                                         pD3D12Enc->m_spCommandAllocator.Get(),
876                                                         nullptr,
877                                                         IID_PPV_ARGS(pD3D12Enc->m_spEncodeCommandList.GetAddressOf()));
878 
879    if (FAILED(hr)) {
880       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateCommandList "
881                       "failed with HR %x\n",
882                       hr);
883       return false;
884    }
885 
886    return true;
887 }
888 
889 struct pipe_video_codec *
890 d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *codec)
891 {
892    ///
893    /// Initialize d3d12_video_encoder
894    ///
895 
896    // Not using new doesn't call ctor and the initializations in the class declaration are lost
897    struct d3d12_video_encoder *pD3D12Enc = new d3d12_video_encoder;
898 
899    pD3D12Enc->base         = *codec;
900    pD3D12Enc->m_screen     = context->screen;
901    pD3D12Enc->base.context = context;
902    pD3D12Enc->base.width   = codec->width;
903    pD3D12Enc->base.height  = codec->height;
904    pD3D12Enc->base.max_references  = codec->max_references;
905    // Only fill methods that are supported by the d3d12 encoder, leaving null the rest (ie. encode_* / encode_macroblock)
906    pD3D12Enc->base.destroy          = d3d12_video_encoder_destroy;
907    pD3D12Enc->base.begin_frame      = d3d12_video_encoder_begin_frame;
908    pD3D12Enc->base.encode_bitstream = d3d12_video_encoder_encode_bitstream;
909    pD3D12Enc->base.end_frame        = d3d12_video_encoder_end_frame;
910    pD3D12Enc->base.flush            = d3d12_video_encoder_flush;
911    pD3D12Enc->base.get_feedback     = d3d12_video_encoder_get_feedback;
912 
913    struct d3d12_context *pD3D12Ctx = (struct d3d12_context *) context;
914    pD3D12Enc->m_pD3D12Screen       = d3d12_screen(pD3D12Ctx->base.screen);
915 
916    if (FAILED(pD3D12Enc->m_pD3D12Screen->dev->QueryInterface(
917           IID_PPV_ARGS(pD3D12Enc->m_spD3D12VideoDevice.GetAddressOf())))) {
918       debug_printf(
919          "[d3d12_video_encoder] d3d12_video_encoder_create_encoder - D3D12 Device has no Video encode support\n");
920       goto failed;
921    }
922 
923    if (!d3d12_video_encoder_create_command_objects(pD3D12Enc)) {
924       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_encoder - Failure on "
925                       "d3d12_video_encoder_create_command_objects\n");
926       goto failed;
927    }
928 
929    return &pD3D12Enc->base;
930 
931 failed:
932    if (pD3D12Enc != nullptr) {
933       d3d12_video_encoder_destroy((struct pipe_video_codec *) pD3D12Enc);
934    }
935 
936    return nullptr;
937 }
938 
939 bool
940 d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder *pD3D12Enc,
941                                            struct pipe_video_buffer *  srcTexture,
942                                            struct pipe_picture_desc *  picture)
943 {
944    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.NodeIndex = pD3D12Enc->m_NodeIndex;
945    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.Codec =
946       pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc;
947    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.Profile =
948       d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
949    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.InputFormat =
950       pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format;
951    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.PictureTargetResolution =
952       pD3D12Enc->m_currentEncodeConfig.m_currentResolution;
953 
954    HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(
955       D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
956       &pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps,
957       sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps));
958 
959    if (FAILED(hr)) {
960       debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
961       return false;
962    }
963 
964    if (!pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.IsSupported) {
965       debug_printf("[d3d12_video_encoder] D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS arguments are not supported.\n");
966       return false;
967    }
968 
969    d3d12_video_encoder_calculate_metadata_resolved_buffer_size(
970       pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput,
971       pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize);
972 
973    D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
974    if ((pD3D12Enc->m_spResolvedMetadataBuffer == nullptr) ||
975        (GetDesc(pD3D12Enc->m_spResolvedMetadataBuffer.Get()).Width <
976         pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize)) {
977       CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
978          pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize);
979 
980       HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
981          &Properties,
982          D3D12_HEAP_FLAG_NONE,
983          &resolvedMetadataBufferDesc,
984          D3D12_RESOURCE_STATE_COMMON,
985          nullptr,
986          IID_PPV_ARGS(pD3D12Enc->m_spResolvedMetadataBuffer.GetAddressOf()));
987 
988       if (FAILED(hr)) {
989          debug_printf("CreateCommittedResource failed with HR %x\n", hr);
990          return false;
991       }
992    }
993 
994    if ((pD3D12Enc->m_spMetadataOutputBuffer == nullptr) ||
995        (GetDesc(pD3D12Enc->m_spMetadataOutputBuffer.Get()).Width <
996         pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize)) {
997       CD3DX12_RESOURCE_DESC metadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
998          pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize);
999 
1000       HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
1001          &Properties,
1002          D3D12_HEAP_FLAG_NONE,
1003          &metadataBufferDesc,
1004          D3D12_RESOURCE_STATE_COMMON,
1005          nullptr,
1006          IID_PPV_ARGS(pD3D12Enc->m_spMetadataOutputBuffer.GetAddressOf()));
1007 
1008       if (FAILED(hr)) {
1009          debug_printf("CreateCommittedResource failed with HR %x\n", hr);
1010          return false;
1011       }
1012    }
1013    return true;
1014 }
1015 
1016 bool
1017 d3d12_video_encoder_reconfigure_session(struct d3d12_video_encoder *pD3D12Enc,
1018                                         struct pipe_video_buffer *  srcTexture,
1019                                         struct pipe_picture_desc *  picture)
1020 {
1021    assert(pD3D12Enc->m_spD3D12VideoDevice);
1022    if(!d3d12_video_encoder_update_current_encoder_config_state(pD3D12Enc, srcTexture, picture)) {
1023       debug_printf("d3d12_video_encoder_update_current_encoder_config_state failed!\n");
1024       return false;
1025    }
1026    if(!d3d12_video_encoder_reconfigure_encoder_objects(pD3D12Enc, srcTexture, picture)) {
1027       debug_printf("d3d12_video_encoder_reconfigure_encoder_objects failed!\n");
1028       return false;
1029    }
1030    d3d12_video_encoder_update_picparams_tracking(pD3D12Enc, srcTexture, picture);
1031    if(!d3d12_video_encoder_prepare_output_buffers(pD3D12Enc, srcTexture, picture)) {
1032       debug_printf("d3d12_video_encoder_prepare_output_buffers failed!\n");
1033       return false;
1034    }
1035    return true;
1036 }
1037 
1038 /**
1039  * start encoding of a new frame
1040  */
1041 void
1042 d3d12_video_encoder_begin_frame(struct pipe_video_codec * codec,
1043                                 struct pipe_video_buffer *target,
1044                                 struct pipe_picture_desc *picture)
1045 {
1046    // Do nothing here. Initialize happens on encoder creation, re-config (if any) happens in
1047    // d3d12_video_encoder_encode_bitstream
1048    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
1049    assert(pD3D12Enc);
1050    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame started for fenceValue: %d\n",
1051                  pD3D12Enc->m_fenceValue);
1052 
1053    if (!d3d12_video_encoder_reconfigure_session(pD3D12Enc, target, picture)) {
1054       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame - Failure on "
1055                       "d3d12_video_encoder_reconfigure_session\n");
1056       goto fail;
1057    }
1058 
1059    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame finalized for fenceValue: %d\n",
1060                  pD3D12Enc->m_fenceValue);
1061    return;
1062 
1063 fail:
1064    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame failed for fenceValue: %d\n",
1065                 pD3D12Enc->m_fenceValue);
1066    assert(false);
1067 }
1068 
1069 void
1070 d3d12_video_encoder_calculate_metadata_resolved_buffer_size(uint32_t maxSliceNumber, size_t &bufferSize)
1071 {
1072    bufferSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) +
1073                 (maxSliceNumber * sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA));
1074 }
1075 
1076 // Returns the number of slices that the output will contain for fixed slicing modes
1077 // and the maximum number of slices the output might contain for dynamic slicing modes (eg. max bytes per slice)
1078 uint32_t
1079 d3d12_video_encoder_calculate_max_slices_count_in_output(
1080    D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE                          slicesMode,
1081    const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES *slicesConfig,
1082    uint32_t                                                                 MaxSubregionsNumberFromCaps,
1083    D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC                              sequenceTargetResolution,
1084    uint32_t                                                                 SubregionBlockPixelsSize)
1085 {
1086    uint32_t pic_width_in_subregion_units =
1087       static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Width / static_cast<double>(SubregionBlockPixelsSize)));
1088    uint32_t pic_height_in_subregion_units =
1089       static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Height / static_cast<double>(SubregionBlockPixelsSize)));
1090    uint32_t total_picture_subregion_units = pic_width_in_subregion_units * pic_height_in_subregion_units;
1091    uint32_t maxSlices                     = 0u;
1092    switch (slicesMode) {
1093       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME:
1094       {
1095          maxSlices = 1u;
1096       } break;
1097       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_BYTES_PER_SUBREGION:
1098       {
1099          maxSlices = MaxSubregionsNumberFromCaps;
1100       } break;
1101       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_SQUARE_UNITS_PER_SUBREGION_ROW_UNALIGNED:
1102       {
1103          maxSlices = static_cast<uint32_t>(
1104             std::ceil(total_picture_subregion_units / static_cast<double>(slicesConfig->NumberOfCodingUnitsPerSlice)));
1105       } break;
1106       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION:
1107       {
1108          maxSlices = static_cast<uint32_t>(
1109             std::ceil(pic_height_in_subregion_units / static_cast<double>(slicesConfig->NumberOfRowsPerSlice)));
1110       } break;
1111       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME:
1112       {
1113          maxSlices = slicesConfig->NumberOfSlicesPerFrame;
1114       } break;
1115       default:
1116       {
1117          unreachable("Unsupported D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE");
1118       } break;
1119    }
1120 
1121    return maxSlices;
1122 }
1123 
1124 /**
1125  * encode a bitstream
1126  */
1127 void
1128 d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
1129                                      struct pipe_video_buffer *source,
1130                                      struct pipe_resource *    destination,
1131                                      void **                   feedback)
1132 {
1133    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
1134    assert(pD3D12Enc);
1135    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_encode_bitstream started for fenceValue: %d\n",
1136                  pD3D12Enc->m_fenceValue);
1137    assert(pD3D12Enc->m_spD3D12VideoDevice);
1138    assert(pD3D12Enc->m_spEncodeCommandQueue);
1139    assert(pD3D12Enc->m_pD3D12Screen);
1140 
1141    struct d3d12_video_buffer *pInputVideoBuffer = (struct d3d12_video_buffer *) source;
1142    assert(pInputVideoBuffer);
1143    ID3D12Resource *pInputVideoD3D12Res        = d3d12_resource_resource(pInputVideoBuffer->texture);
1144    uint32_t        inputVideoD3D12Subresource = 0u;
1145 
1146    struct d3d12_resource *pOutputBitstreamBuffer = (struct d3d12_resource *) destination;
1147    assert(pOutputBitstreamBuffer);
1148    ID3D12Resource *pOutputBufferD3D12Res = d3d12_resource_resource(pOutputBitstreamBuffer);
1149 
1150    // Make them permanently resident for video use
1151    d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pOutputBitstreamBuffer);
1152    d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pInputVideoBuffer->texture);
1153 
1154    ///
1155    /// Record Encode operation
1156    ///
1157 
1158    ///
1159    /// pInputVideoD3D12Res and pOutputBufferD3D12Res are unwrapped from pipe_resource objects that are passed externally
1160    /// and could be tracked by pipe_context and have pending ops. Flush any work on them and transition to
1161    /// D3D12_RESOURCE_STATE_COMMON before issuing work in Video command queue below. After the video work is done in the
1162    /// GPU, transition back to D3D12_RESOURCE_STATE_COMMON
1163    ///
1164    /// Note that unlike the D3D12TranslationLayer codebase, the state tracker here doesn't (yet) have any kind of
1165    /// multi-queue support, so it wouldn't implicitly synchronize when trying to transition between a graphics op and a
1166    /// video op.
1167    ///
1168 
1169    d3d12_transition_resource_state(
1170       d3d12_context(pD3D12Enc->base.context),
1171       pInputVideoBuffer->texture,   // d3d12_resource wrapper for pInputVideoD3D12Res
1172       D3D12_RESOURCE_STATE_COMMON,
1173       D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
1174    d3d12_transition_resource_state(d3d12_context(pD3D12Enc->base.context),
1175                                    pOutputBitstreamBuffer,   // d3d12_resource wrapped for pOutputBufferD3D12Res
1176                                    D3D12_RESOURCE_STATE_COMMON,
1177                                    D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
1178    d3d12_apply_resource_states(d3d12_context(pD3D12Enc->base.context), false);
1179 
1180    d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context),
1181                             pInputVideoBuffer->texture,
1182                             false /*wantToWrite*/);
1183    d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context), pOutputBitstreamBuffer, true /*wantToWrite*/);
1184 
1185    std::vector<D3D12_RESOURCE_BARRIER> rgCurrentFrameStateTransitions = {
1186       CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res,
1187                                            D3D12_RESOURCE_STATE_COMMON,
1188                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ),
1189       CD3DX12_RESOURCE_BARRIER::Transition(pOutputBufferD3D12Res,
1190                                            D3D12_RESOURCE_STATE_COMMON,
1191                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
1192       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spMetadataOutputBuffer.Get(),
1193                                            D3D12_RESOURCE_STATE_COMMON,
1194                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE)
1195    };
1196 
1197    pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(rgCurrentFrameStateTransitions.size(),
1198                                                      rgCurrentFrameStateTransitions.data());
1199 
1200    D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE reconPicOutputTextureDesc =
1201       pD3D12Enc->m_upDPBManager->get_current_frame_recon_pic_output_allocation();
1202    D3D12_VIDEO_ENCODE_REFERENCE_FRAMES referenceFramesDescriptor =
1203       pD3D12Enc->m_upDPBManager->get_current_reference_frames();
1204    D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAGS picCtrlFlags = D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_NONE;
1205 
1206    // Transition DPB reference pictures to read mode
1207    uint32_t                            maxReferences = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
1208    std::vector<D3D12_RESOURCE_BARRIER> rgReferenceTransitions(maxReferences);
1209    if ((referenceFramesDescriptor.NumTexture2Ds > 0) ||
1210        (pD3D12Enc->m_upDPBManager->is_current_frame_used_as_reference())) {
1211       rgReferenceTransitions.clear();
1212       rgReferenceTransitions.reserve(maxReferences);
1213 
1214       // Check if array of textures vs texture array
1215 
1216       if (referenceFramesDescriptor.pSubresources == nullptr) {
1217 
1218          // Array of resources mode for reference pictures
1219 
1220          // Transition all subresources of each reference frame independent resource allocation
1221          for (uint32_t referenceIdx = 0; referenceIdx < referenceFramesDescriptor.NumTexture2Ds; referenceIdx++) {
1222             rgReferenceTransitions.push_back(
1223                CD3DX12_RESOURCE_BARRIER::Transition(referenceFramesDescriptor.ppTexture2Ds[referenceIdx],
1224                                                     D3D12_RESOURCE_STATE_COMMON,
1225                                                     D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ));
1226          }
1227 
1228          // Transition all subresources the output recon pic independent resource allocation
1229          if (reconPicOutputTextureDesc.pReconstructedPicture != nullptr) {
1230             picCtrlFlags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
1231 
1232             rgReferenceTransitions.push_back(
1233                CD3DX12_RESOURCE_BARRIER::Transition(reconPicOutputTextureDesc.pReconstructedPicture,
1234                                                     D3D12_RESOURCE_STATE_COMMON,
1235                                                     D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE));
1236          }
1237       } else if (referenceFramesDescriptor.NumTexture2Ds > 0) {
1238 
1239          // texture array mode for reference pictures
1240 
1241          // In Texture array mode, the dpb storage allocator uses the same texture array for all the input
1242          // reference pics in ppTexture2Ds and also for the pReconstructedPicture output allocations, just different
1243          // subresources.
1244 
1245          CD3DX12_RESOURCE_DESC referencesTexArrayDesc(GetDesc(referenceFramesDescriptor.ppTexture2Ds[0]));
1246 
1247          for (uint32_t referenceSubresource = 0; referenceSubresource < referencesTexArrayDesc.DepthOrArraySize;
1248               referenceSubresource++) {
1249 
1250             // all reference frames inputs should be all the same texarray allocation
1251             assert(referenceFramesDescriptor.ppTexture2Ds[0] ==
1252                    referenceFramesDescriptor.ppTexture2Ds[referenceSubresource]);
1253 
1254             // the reconpic output should be all the same texarray allocation
1255             assert(referenceFramesDescriptor.ppTexture2Ds[0] == reconPicOutputTextureDesc.pReconstructedPicture);
1256 
1257             uint32_t MipLevel, PlaneSlice, ArraySlice;
1258             D3D12DecomposeSubresource(referenceSubresource,
1259                                       referencesTexArrayDesc.MipLevels,
1260                                       referencesTexArrayDesc.ArraySize(),
1261                                       MipLevel,
1262                                       ArraySlice,
1263                                       PlaneSlice);
1264 
1265             for (PlaneSlice = 0; PlaneSlice < pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.PlaneCount;
1266                  PlaneSlice++) {
1267 
1268                uint32_t planeOutputSubresource =
1269                   referencesTexArrayDesc.CalcSubresource(MipLevel, ArraySlice, PlaneSlice);
1270 
1271                rgReferenceTransitions.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
1272                   // Always same allocation in texarray mode
1273                   referenceFramesDescriptor.ppTexture2Ds[0],
1274                   D3D12_RESOURCE_STATE_COMMON,
1275                   // If this is the subresource for the reconpic output allocation, transition to ENCODE_WRITE
1276                   // Otherwise, it's a subresource for an input reference picture, transition to ENCODE_READ
1277                   (referenceSubresource == reconPicOutputTextureDesc.ReconstructedPictureSubresource) ?
1278                      D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE :
1279                      D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
1280                   planeOutputSubresource));
1281             }
1282          }
1283       }
1284 
1285       if (rgReferenceTransitions.size() > 0) {
1286          pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(static_cast<uint32_t>(rgReferenceTransitions.size()),
1287                                                            rgReferenceTransitions.data());
1288       }
1289    }
1290 
1291    // Update current frame pic params state after reconfiguring above.
1292    D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
1293       d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
1294    pD3D12Enc->m_upDPBManager->get_current_frame_picture_control_data(currentPicParams);
1295 
1296    uint32_t prefixGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers(pD3D12Enc);
1297 
1298    // If driver needs offset alignment for bitstream resource, we will pad zeroes on the codec header to this end.
1299    if (
1300       (pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment > 1)
1301       && ((prefixGeneratedHeadersByteSize % pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment) != 0)
1302    ) {
1303       prefixGeneratedHeadersByteSize = ALIGN(prefixGeneratedHeadersByteSize, pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment);
1304       pD3D12Enc->m_BitstreamHeadersBuffer.resize(prefixGeneratedHeadersByteSize, 0);
1305    }
1306 
1307    const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS inputStreamArguments = {
1308       // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_DESC
1309       { // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS
1310         pD3D12Enc->m_currentEncodeConfig.m_seqFlags,
1311         // D3D12_VIDEO_ENCODER_INTRA_REFRESH
1312         pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh,
1313         d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc),
1314         // D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC
1315         pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
1316         pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
1317         d3d12_video_encoder_get_current_slice_param_settings(pD3D12Enc),
1318         d3d12_video_encoder_get_current_gop_desc(pD3D12Enc) },
1319       // D3D12_VIDEO_ENCODER_PICTURE_CONTROL_DESC
1320       { // uint32_t IntraRefreshFrameIndex;
1321         pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex,
1322         // D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAGS Flags;
1323         picCtrlFlags,
1324         // D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA PictureControlCodecData;
1325         currentPicParams,
1326         // D3D12_VIDEO_ENCODE_REFERENCE_FRAMES ReferenceFrames;
1327         referenceFramesDescriptor },
1328       pInputVideoD3D12Res,
1329       inputVideoD3D12Subresource,
1330       prefixGeneratedHeadersByteSize   // hint for driver to know header size in final bitstream for rate control internal
1331       // budgeting. - User can also calculate headers fixed size beforehand (eg. no VUI,
1332       // etc) and build them with final values after EncodeFrame is executed
1333    };
1334 
1335    const D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS outputStreamArguments = {
1336       // D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM
1337       {
1338          pOutputBufferD3D12Res,
1339          prefixGeneratedHeadersByteSize,   // Start writing after the reserved interval [0,
1340                                            // prefixGeneratedHeadersByteSize) for bitstream headers
1341       },
1342       // D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
1343       reconPicOutputTextureDesc,
1344       // D3D12_VIDEO_ENCODER_ENCODE_OPERATION_METADATA_BUFFER
1345       { pD3D12Enc->m_spMetadataOutputBuffer.Get(), 0 }
1346    };
1347 
1348    // Upload the CPU buffers with the bitstream headers to the compressed bitstream resource in the interval [0,
1349    // prefixGeneratedHeadersByteSize)
1350    assert(prefixGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size());
1351 
1352    pD3D12Enc->base.context->buffer_subdata(
1353       pD3D12Enc->base.context,   // context
1354       destination,               // dst buffer - "destination" is the pipe_resource object
1355                                  // wrapping pOutputBitstreamBuffer and eventually pOutputBufferD3D12Res
1356       PIPE_MAP_WRITE,            // usage PIPE_MAP_x
1357       0,                         // offset
1358       pD3D12Enc->m_BitstreamHeadersBuffer.size(),
1359       pD3D12Enc->m_BitstreamHeadersBuffer.data());
1360 
1361    // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately
1362    // Will flush and sync this batch in d3d12_video_encoder_flush with the rest of the Video Encode Queue GPU work
1363 
1364    // Record EncodeFrame
1365    pD3D12Enc->m_spEncodeCommandList->EncodeFrame(pD3D12Enc->m_spVideoEncoder.Get(),
1366                                                  pD3D12Enc->m_spVideoEncoderHeap.Get(),
1367                                                  &inputStreamArguments,
1368                                                  &outputStreamArguments);
1369 
1370    D3D12_RESOURCE_BARRIER rgResolveMetadataStateTransitions[] = {
1371       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spResolvedMetadataBuffer.Get(),
1372                                            D3D12_RESOURCE_STATE_COMMON,
1373                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
1374       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spMetadataOutputBuffer.Get(),
1375                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
1376                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ),
1377       CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res,
1378                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
1379                                            D3D12_RESOURCE_STATE_COMMON),
1380       CD3DX12_RESOURCE_BARRIER::Transition(pOutputBufferD3D12Res,
1381                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
1382                                            D3D12_RESOURCE_STATE_COMMON)
1383    };
1384 
1385    pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(_countof(rgResolveMetadataStateTransitions),
1386                                                      rgResolveMetadataStateTransitions);
1387 
1388    const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS inputMetadataCmd = {
1389       pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
1390       d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
1391       pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
1392       // D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC
1393       pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
1394       { pD3D12Enc->m_spMetadataOutputBuffer.Get(), 0 }
1395    };
1396 
1397    const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS outputMetadataCmd = {
1398       /*If offset were to change, has to be aligned to pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.EncoderMetadataBufferAccessAlignment*/
1399       { pD3D12Enc->m_spResolvedMetadataBuffer.Get(), 0 }
1400    };
1401    pD3D12Enc->m_spEncodeCommandList->ResolveEncoderOutputMetadata(&inputMetadataCmd, &outputMetadataCmd);
1402 
1403    // Transition DPB reference pictures back to COMMON
1404    if ((referenceFramesDescriptor.NumTexture2Ds > 0) ||
1405        (pD3D12Enc->m_upDPBManager->is_current_frame_used_as_reference())) {
1406       for (auto &BarrierDesc : rgReferenceTransitions) {
1407          std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter);
1408       }
1409 
1410       if (rgReferenceTransitions.size() > 0) {
1411          pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(static_cast<uint32_t>(rgReferenceTransitions.size()),
1412                                                            rgReferenceTransitions.data());
1413       }
1414    }
1415 
1416    D3D12_RESOURCE_BARRIER rgRevertResolveMetadataStateTransitions[] = {
1417       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spResolvedMetadataBuffer.Get(),
1418                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
1419                                            D3D12_RESOURCE_STATE_COMMON),
1420       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spMetadataOutputBuffer.Get(),
1421                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
1422                                            D3D12_RESOURCE_STATE_COMMON),
1423    };
1424 
1425    pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(_countof(rgRevertResolveMetadataStateTransitions),
1426                                                      rgRevertResolveMetadataStateTransitions);
1427 
1428    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_encode_bitstream finalized for fenceValue: %d\n",
1429                  pD3D12Enc->m_fenceValue);
1430 }
1431 
1432 void
1433 d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, void *feedback, unsigned *size)
1434 {
1435    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
1436    assert(pD3D12Enc);
1437 
1438    if (pD3D12Enc->m_needsGPUFlush) {
1439       d3d12_video_encoder_flush(codec);
1440    }
1441 
1442    D3D12_VIDEO_ENCODER_OUTPUT_METADATA                       encoderMetadata;
1443    std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> pSubregionsMetadata;
1444    d3d12_video_encoder_extract_encode_metadata(
1445       pD3D12Enc,
1446       pD3D12Enc->m_spResolvedMetadataBuffer.Get(),
1447       pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize,
1448       encoderMetadata,
1449       pSubregionsMetadata);
1450 
1451    // Read metadata from encoderMetadata
1452    if (encoderMetadata.EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) {
1453       debug_printf("[d3d12_video_encoder] Encode GPU command failed - EncodeErrorFlags: %" PRIu64 "\n",
1454                       encoderMetadata.EncodeErrorFlags);
1455       *size = 0;
1456    }
1457 
1458    assert(encoderMetadata.EncodedBitstreamWrittenBytesCount > 0u);
1459    *size = (pD3D12Enc->m_BitstreamHeadersBuffer.size() + encoderMetadata.EncodedBitstreamWrittenBytesCount);
1460 }
1461 
1462 void
1463 d3d12_video_encoder_extract_encode_metadata(
1464    struct d3d12_video_encoder *                               pD3D12Enc,
1465    ID3D12Resource *                                           pResolvedMetadataBuffer,   // input
1466    size_t                                                     resourceMetadataSize,      // input
1467    D3D12_VIDEO_ENCODER_OUTPUT_METADATA &                      parsedMetadata,            // output
1468    std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> &pSubregionsMetadata        // output
1469 )
1470 {
1471    struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pD3D12Enc->m_pD3D12Screen;
1472    assert(pD3D12Screen);
1473    pipe_resource *pPipeResolvedMetadataBuffer =
1474       d3d12_resource_from_resource(&pD3D12Screen->base, pResolvedMetadataBuffer);
1475    assert(pPipeResolvedMetadataBuffer);
1476    assert(resourceMetadataSize < INT_MAX);
1477    struct pipe_box box = {
1478       0,                                        // x
1479       0,                                        // y
1480       0,                                        // z
1481       static_cast<int>(resourceMetadataSize),   // width
1482       1,                                        // height
1483       1                                         // depth
1484    };
1485    struct pipe_transfer *mapTransfer;
1486    unsigned mapUsage = PIPE_MAP_READ;
1487    void *                pMetadataBufferSrc = pD3D12Enc->base.context->buffer_map(pD3D12Enc->base.context,
1488                                                                   pPipeResolvedMetadataBuffer,
1489                                                                   0,
1490                                                                   mapUsage,
1491                                                                   &box,
1492                                                                   &mapTransfer);
1493 
1494    assert(mapUsage & PIPE_MAP_READ);
1495    assert(pPipeResolvedMetadataBuffer->usage == PIPE_USAGE_DEFAULT);
1496    // Note: As we're calling buffer_map with PIPE_MAP_READ on a pPipeResolvedMetadataBuffer which has pipe_usage_default
1497    // buffer_map itself will do all the synchronization and waits so once the function returns control here
1498    // the contents of mapTransfer are ready to be accessed.
1499 
1500    // Clear output
1501    memset(&parsedMetadata, 0, sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA));
1502 
1503    // Calculate sizes
1504    size_t encoderMetadataSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA);
1505 
1506    // Copy buffer to the appropriate D3D12_VIDEO_ENCODER_OUTPUT_METADATA memory layout
1507    parsedMetadata = *reinterpret_cast<D3D12_VIDEO_ENCODER_OUTPUT_METADATA *>(pMetadataBufferSrc);
1508 
1509    // As specified in D3D12 Encode spec, the array base for metadata for the slices
1510    // (D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA[]) is placed in memory immediately after the
1511    // D3D12_VIDEO_ENCODER_OUTPUT_METADATA structure
1512    D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata =
1513       reinterpret_cast<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *>(reinterpret_cast<uint8_t *>(pMetadataBufferSrc) +
1514                                                                        encoderMetadataSize);
1515 
1516    // Copy fields into D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA
1517    assert(parsedMetadata.WrittenSubregionsCount < SIZE_MAX);
1518    pSubregionsMetadata.resize(static_cast<size_t>(parsedMetadata.WrittenSubregionsCount));
1519    for (uint32_t sliceIdx = 0; sliceIdx < parsedMetadata.WrittenSubregionsCount; sliceIdx++) {
1520       pSubregionsMetadata[sliceIdx].bHeaderSize  = pFrameSubregionMetadata[sliceIdx].bHeaderSize;
1521       pSubregionsMetadata[sliceIdx].bSize        = pFrameSubregionMetadata[sliceIdx].bSize;
1522       pSubregionsMetadata[sliceIdx].bStartOffset = pFrameSubregionMetadata[sliceIdx].bStartOffset;
1523    }
1524 
1525    // Unmap the buffer tmp storage
1526    pipe_buffer_unmap(pD3D12Enc->base.context, mapTransfer);
1527 }
1528 
1529 /**
1530  * end encoding of the current frame
1531  */
1532 void
1533 d3d12_video_encoder_end_frame(struct pipe_video_codec * codec,
1534                               struct pipe_video_buffer *target,
1535                               struct pipe_picture_desc *picture)
1536 {
1537    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
1538    assert(pD3D12Enc);
1539    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame started for fenceValue: %d\n",
1540                  pD3D12Enc->m_fenceValue);
1541 
1542    // Signal finish of current frame encoding to the picture management tracker
1543    pD3D12Enc->m_upDPBManager->end_frame();
1544 
1545    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame finalized for fenceValue: %d\n",
1546                  pD3D12Enc->m_fenceValue);
1547 
1548    ///
1549    /// Flush work to the GPU and blocking wait until encode finishes
1550    ///
1551    pD3D12Enc->m_needsGPUFlush = true;
1552    d3d12_video_encoder_flush(codec);
1553 }
1554