• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © Microsoft Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "d3d12_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 #if VIDEO_CODEC_H264ENC
34 #include "d3d12_video_enc_h264.h"
35 #endif
36 #if VIDEO_CODEC_H265ENC
37 #include "d3d12_video_enc_hevc.h"
38 #endif
39 #if VIDEO_CODEC_AV1ENC
40 #include "d3d12_video_enc_av1.h"
41 #endif
42 #include "d3d12_video_buffer.h"
43 #include "d3d12_video_texture_array_dpb_manager.h"
44 #include "d3d12_video_array_of_textures_dpb_manager.h"
45 #include "d3d12_video_encoder_references_manager_h264.h"
46 #include "d3d12_video_encoder_references_manager_hevc.h"
47 #include "d3d12_video_encoder_references_manager_av1.h"
48 #include "d3d12_residency.h"
49 
50 #include "vl/vl_video_buffer.h"
51 #include "util/format/u_format.h"
52 #include "util/u_inlines.h"
53 #include "util/u_memory.h"
54 #include "util/u_video.h"
55 
56 #include <cmath>
57 
58 D3D12_VIDEO_ENCODER_CODEC
d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(enum pipe_video_profile profile)59 d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(enum pipe_video_profile profile)
60 {
61    switch (u_reduce_video_profile(profile)) {
62       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
63       {
64          return D3D12_VIDEO_ENCODER_CODEC_H264;
65       } break;
66       case PIPE_VIDEO_FORMAT_HEVC:
67       {
68          return D3D12_VIDEO_ENCODER_CODEC_HEVC;
69       } break;
70       case PIPE_VIDEO_FORMAT_AV1:
71       {
72          return D3D12_VIDEO_ENCODER_CODEC_AV1;
73       } break;
74       case PIPE_VIDEO_FORMAT_MPEG12:
75       case PIPE_VIDEO_FORMAT_MPEG4:
76       case PIPE_VIDEO_FORMAT_VC1:
77       case PIPE_VIDEO_FORMAT_JPEG:
78       case PIPE_VIDEO_FORMAT_VP9:
79       case PIPE_VIDEO_FORMAT_UNKNOWN:
80       default:
81       {
82          unreachable("Unsupported pipe_video_profile");
83       } break;
84    }
85 }
86 
87 uint64_t
d3d12_video_encoder_pool_current_index(struct d3d12_video_encoder * pD3D12Enc)88 d3d12_video_encoder_pool_current_index(struct d3d12_video_encoder *pD3D12Enc)
89 {
90    return pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_ASYNC_DEPTH;
91 }
92 
93 void
d3d12_video_encoder_flush(struct pipe_video_codec * codec)94 d3d12_video_encoder_flush(struct pipe_video_codec *codec)
95 {
96    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
97    assert(pD3D12Enc);
98    assert(pD3D12Enc->m_spD3D12VideoDevice);
99    assert(pD3D12Enc->m_spEncodeCommandQueue);
100 
101    if (pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED) {
102       debug_printf("WARNING: [d3d12_video_encoder] d3d12_video_encoder_flush - Frame submission %" PRIu64 " failed. Encoder lost, please recreate pipe_video_codec object \n", pD3D12Enc->m_fenceValue);
103       assert(false);
104       return;
105    }
106 
107    // Flush any work batched (ie. shaders blit on input texture, etc or bitstream headers buffer_subdata batched upload)
108    // and Wait the m_spEncodeCommandQueue for GPU upload completion
109    // before recording EncodeFrame below.
110    struct pipe_fence_handle *completion_fence = NULL;
111    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");
112    pD3D12Enc->base.context->flush(pD3D12Enc->base.context, &completion_fence, PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH);
113    assert(completion_fence);
114    struct d3d12_fence *casted_completion_fence = d3d12_fence(completion_fence);
115    pD3D12Enc->m_spEncodeCommandQueue->Wait(casted_completion_fence->cmdqueue_fence, casted_completion_fence->value);
116    pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, &completion_fence, NULL);
117 
118    struct d3d12_fence *input_surface_fence = pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_InputSurfaceFence;
119    if (input_surface_fence)
120       pD3D12Enc->m_spEncodeCommandQueue->Wait(input_surface_fence->cmdqueue_fence, input_surface_fence->value);
121 
122    if (!pD3D12Enc->m_bPendingWorkNotFlushed) {
123       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush started. Nothing to flush, all up to date.\n");
124    } else {
125       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush started. Will flush video queue work async"
126                     " on fenceValue: %" PRIu64 "\n",
127                     pD3D12Enc->m_fenceValue);
128 
129       HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
130       if (hr != S_OK) {
131          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush"
132                          " - D3D12Device was removed BEFORE commandlist "
133                          "execution with HR %x.\n",
134                          hr);
135          goto flush_fail;
136       }
137 
138       if (pD3D12Enc->m_transitionsBeforeCloseCmdList.size() > 0) {
139          pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(pD3D12Enc->m_transitionsBeforeCloseCmdList.size(),
140                                                            pD3D12Enc->m_transitionsBeforeCloseCmdList.data());
141          pD3D12Enc->m_transitionsBeforeCloseCmdList.clear();
142       }
143 
144       hr = pD3D12Enc->m_spEncodeCommandList->Close();
145       if (FAILED(hr)) {
146          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - Can't close command list with HR %x\n", hr);
147          goto flush_fail;
148       }
149 
150       ID3D12CommandList *ppCommandLists[1] = { pD3D12Enc->m_spEncodeCommandList.Get() };
151       pD3D12Enc->m_spEncodeCommandQueue->ExecuteCommandLists(1, ppCommandLists);
152       pD3D12Enc->m_spEncodeCommandQueue->Signal(pD3D12Enc->m_spFence.Get(), pD3D12Enc->m_fenceValue);
153 
154       // Validate device was not removed
155       hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
156       if (hr != S_OK) {
157          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush"
158                          " - D3D12Device was removed AFTER commandlist "
159                          "execution with HR %x, but wasn't before.\n",
160                          hr);
161          goto flush_fail;
162       }
163 
164       pD3D12Enc->m_fenceValue++;
165       pD3D12Enc->m_bPendingWorkNotFlushed = false;
166    }
167    return;
168 
169 flush_fail:
170    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush failed for fenceValue: %" PRIu64 "\n", pD3D12Enc->m_fenceValue);
171    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
172    pD3D12Enc->m_spEncodedFrameMetadata[pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
173    assert(false);
174 }
175 
176 void
d3d12_video_encoder_ensure_fence_finished(struct pipe_video_codec * codec,uint64_t fenceValueToWaitOn,uint64_t timeout_ns)177 d3d12_video_encoder_ensure_fence_finished(struct pipe_video_codec *codec, uint64_t fenceValueToWaitOn, uint64_t timeout_ns)
178 {
179       struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
180       HRESULT hr = S_OK;
181       uint64_t completedValue = pD3D12Enc->m_spFence->GetCompletedValue();
182 
183       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - Waiting for fence (with timeout_ns %" PRIu64 ") to finish with "
184                     "fenceValue: %" PRIu64 " - Current Fence Completed Value %" PRIu64 "\n",
185                     timeout_ns, fenceValueToWaitOn, completedValue);
186 
187       if(completedValue < fenceValueToWaitOn) {
188 
189          HANDLE              event = { };
190          int                 event_fd = 0;
191          event = d3d12_fence_create_event(&event_fd);
192 
193          hr = pD3D12Enc->m_spFence->SetEventOnCompletion(fenceValueToWaitOn, event);
194          if (FAILED(hr)) {
195             debug_printf(
196                "[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - SetEventOnCompletion for fenceValue %" PRIu64 " failed with HR %x\n",
197                fenceValueToWaitOn, hr);
198             goto ensure_fence_finished_fail;
199          }
200 
201          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - Waiting on fence to be done with "
202                "fenceValue: %" PRIu64 " - current CompletedValue: %" PRIu64 "\n",
203                fenceValueToWaitOn,
204                completedValue);
205 
206          d3d12_fence_wait_event(event, event_fd, timeout_ns);
207          d3d12_fence_close_event(event, event_fd);
208       } else {
209          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - Fence already done with "
210                "fenceValue: %" PRIu64 " - current CompletedValue: %" PRIu64 "\n",
211                fenceValueToWaitOn,
212                completedValue);
213       }
214       return;
215 
216 ensure_fence_finished_fail:
217    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion failed for fenceValue: %" PRIu64 "\n", fenceValueToWaitOn);
218    pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
219    pD3D12Enc->m_spEncodedFrameMetadata[fenceValueToWaitOn % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
220    assert(false);
221 }
222 
223 void
d3d12_video_encoder_sync_completion(struct pipe_video_codec * codec,uint64_t fenceValueToWaitOn,uint64_t timeout_ns)224 d3d12_video_encoder_sync_completion(struct pipe_video_codec *codec, uint64_t fenceValueToWaitOn, uint64_t timeout_ns)
225 {
226       struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
227       assert(pD3D12Enc);
228       assert(pD3D12Enc->m_spD3D12VideoDevice);
229       assert(pD3D12Enc->m_spEncodeCommandQueue);
230       HRESULT hr = S_OK;
231 
232       d3d12_video_encoder_ensure_fence_finished(codec, fenceValueToWaitOn, timeout_ns);
233 
234       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion - resetting ID3D12CommandAllocator %p...\n",
235          pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spCommandAllocator.Get());
236       hr = pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spCommandAllocator->Reset();
237       if(FAILED(hr)) {
238          debug_printf("failed with %x.\n", hr);
239          goto sync_with_token_fail;
240       }
241 
242       // Release references granted on end_frame for this inflight operations
243       pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spEncoder.Reset();
244       pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spEncoderHeap.Reset();
245       pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_References.reset();
246       pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_InputSurfaceFence = NULL;
247 
248       // Validate device was not removed
249       hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
250       if (hr != S_OK) {
251          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion"
252                          " - D3D12Device was removed AFTER d3d12_video_encoder_ensure_fence_finished "
253                          "execution with HR %x, but wasn't before.\n",
254                          hr);
255          goto sync_with_token_fail;
256       }
257 
258       debug_printf(
259          "[d3d12_video_encoder] d3d12_video_encoder_sync_completion - GPU execution finalized for fenceValue: %" PRIu64 "\n",
260          fenceValueToWaitOn);
261 
262       return;
263 
264 sync_with_token_fail:
265    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion failed for fenceValue: %" PRIu64 "\n", fenceValueToWaitOn);
266    pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
267    pD3D12Enc->m_spEncodedFrameMetadata[fenceValueToWaitOn % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
268    assert(false);
269 }
270 
271 /**
272  * Destroys a d3d12_video_encoder
273  * Call destroy_XX for applicable XX nested member types before deallocating
274  * Destroy methods should check != nullptr on their input target argument as this method can be called as part of
275  * cleanup from failure on the creation method
276  */
277 void
d3d12_video_encoder_destroy(struct pipe_video_codec * codec)278 d3d12_video_encoder_destroy(struct pipe_video_codec *codec)
279 {
280    if (codec == nullptr) {
281       return;
282    }
283 
284    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
285 
286       // Flush pending work before destroying
287    if(pD3D12Enc->m_bPendingWorkNotFlushed){
288       uint64_t curBatchFence = pD3D12Enc->m_fenceValue;
289       d3d12_video_encoder_flush(codec);
290       d3d12_video_encoder_sync_completion(codec, curBatchFence, OS_TIMEOUT_INFINITE);
291    }
292 
293    // Call d3d12_video_encoder dtor to make ComPtr and other member's destructors work
294    delete pD3D12Enc;
295 }
296 
297 void
d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder * pD3D12Enc,struct pipe_video_buffer * srcTexture,struct pipe_picture_desc * picture)298 d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12Enc,
299                                               struct pipe_video_buffer *  srcTexture,
300                                               struct pipe_picture_desc *  picture)
301 {
302    D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
303       d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
304 
305    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
306    bool bUsedAsReference = false;
307    switch (codec) {
308 #if VIDEO_CODEC_H264ENC
309       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
310       {
311          d3d12_video_encoder_update_current_frame_pic_params_info_h264(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference);
312       } break;
313 #endif
314 #if VIDEO_CODEC_H265ENC
315       case PIPE_VIDEO_FORMAT_HEVC:
316       {
317          d3d12_video_encoder_update_current_frame_pic_params_info_hevc(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference);
318       } break;
319 #endif
320 #if VIDEO_CODEC_AV1ENC
321       case PIPE_VIDEO_FORMAT_AV1:
322       {
323          d3d12_video_encoder_update_current_frame_pic_params_info_av1(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference);
324       } break;
325 #endif
326       default:
327       {
328          unreachable("Unsupported pipe_video_format");
329       } break;
330    }
331 
332    pD3D12Enc->m_upDPBManager->begin_frame(currentPicParams, bUsedAsReference, picture);
333 }
334 
335 bool
d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder * pD3D12Enc,struct pipe_video_buffer * srcTexture,struct pipe_picture_desc * picture)336 d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D12Enc,
337                                                 struct pipe_video_buffer *  srcTexture,
338                                                 struct pipe_picture_desc *  picture)
339 {
340    bool codecChanged =
341       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_codec) != 0);
342    bool profileChanged =
343       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_profile) != 0);
344    bool levelChanged =
345       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_level) != 0);
346    bool codecConfigChanged =
347       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_codec_config) != 0);
348    bool inputFormatChanged =
349       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_input_format) != 0);
350    bool resolutionChanged =
351       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_resolution) != 0);
352    bool rateControlChanged =
353       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_rate_control) != 0);
354    bool slicesChanged =
355       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_slices) != 0);
356    bool gopChanged =
357       ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_gop) != 0);
358    bool motionPrecisionLimitChanged = ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags &
359                                         d3d12_video_encoder_config_dirty_flag_motion_precision_limit) != 0);
360    bool irChanged = ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags &
361                                         d3d12_video_encoder_config_dirty_flag_intra_refresh) != 0);
362 
363    // Events that that trigger a re-creation of the reference picture manager
364    // Stores codec agnostic textures so only input format, resolution and gop (num dpb references) affects this
365    if (!pD3D12Enc->m_upDPBManager
366        // || codecChanged
367        // || profileChanged
368        // || levelChanged
369        // || codecConfigChanged
370        || inputFormatChanged ||
371        resolutionChanged
372        // || rateControlChanged
373        // || slicesChanged
374        || gopChanged
375        // || motionPrecisionLimitChanged
376    ) {
377       if (!pD3D12Enc->m_upDPBManager) {
378          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating Reference "
379                        "Pictures Manager for the first time\n");
380       } else {
381          debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating Reference Pictures Manager\n");
382       }
383 
384       D3D12_RESOURCE_FLAGS resourceAllocFlags =
385          D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
386       bool     fArrayOfTextures = ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
387                                 D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) == 0);
388       uint32_t texturePoolSize  = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc) +
389                                  1u;   // adding an extra slot as we also need to count the current frame output recon
390                                        // allocation along max reference frame allocations
391       assert(texturePoolSize < UINT16_MAX);
392       pD3D12Enc->m_upDPBStorageManager.reset();
393       if (fArrayOfTextures) {
394          pD3D12Enc->m_upDPBStorageManager = std::make_unique<d3d12_array_of_textures_dpb_manager>(
395             static_cast<uint16_t>(texturePoolSize),
396             pD3D12Enc->m_pD3D12Screen->dev,
397             pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
398             pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
399             (D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE),
400             true,   // setNullSubresourcesOnAllZero - D3D12 Video Encode expects nullptr pSubresources if AoT,
401             pD3D12Enc->m_NodeMask,
402             /*use underlying pool, we can't reuse upper level allocations, need D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY*/
403             true);
404       } else {
405          pD3D12Enc->m_upDPBStorageManager = std::make_unique<d3d12_texture_array_dpb_manager>(
406             static_cast<uint16_t>(texturePoolSize),
407             pD3D12Enc->m_pD3D12Screen->dev,
408             pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
409             pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
410             resourceAllocFlags,
411             pD3D12Enc->m_NodeMask);
412       }
413       d3d12_video_encoder_create_reference_picture_manager(pD3D12Enc, picture);
414    }
415 
416    bool reCreatedEncoder = false;
417    // Events that that trigger a re-creation of the encoder
418    if (!pD3D12Enc->m_spVideoEncoder || codecChanged ||
419        profileChanged
420        // || levelChanged // Only affects encoder heap
421        || codecConfigChanged ||
422        inputFormatChanged
423        // || resolutionChanged // Only affects encoder heap
424        // Only re-create if there is NO SUPPORT for reconfiguring rateControl on the fly
425        || (rateControlChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
426                                    D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) ==
427                                   0 /*checking the flag is NOT set*/))
428        // Only re-create if there is NO SUPPORT for reconfiguring slices on the fly
429        || (slicesChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
430                               D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) ==
431                              0 /*checking the flag is NOT set*/))
432        // Only re-create if there is NO SUPPORT for reconfiguring gop on the fly
433        || (gopChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
434                            D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) ==
435                           0 /*checking the flag is NOT set*/)) ||
436        motionPrecisionLimitChanged) {
437       if (!pD3D12Enc->m_spVideoEncoder) {
438          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating "
439                        "D3D12VideoEncoder for the first time\n");
440       } else {
441          debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating D3D12VideoEncoder\n");
442          reCreatedEncoder = true;
443       }
444 
445       D3D12_VIDEO_ENCODER_DESC encoderDesc = { pD3D12Enc->m_NodeMask,
446                                                D3D12_VIDEO_ENCODER_FLAG_NONE,
447                                                pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
448                                                d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
449                                                pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
450                                                d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc),
451                                                pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit };
452 
453       // Create encoder
454       pD3D12Enc->m_spVideoEncoder.Reset();
455       HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CreateVideoEncoder(&encoderDesc,
456                                                              IID_PPV_ARGS(pD3D12Enc->m_spVideoEncoder.GetAddressOf()));
457       if (FAILED(hr)) {
458          debug_printf("CreateVideoEncoder failed with HR %x\n", hr);
459          return false;
460       }
461    }
462 
463    bool reCreatedEncoderHeap = false;
464    // Events that that trigger a re-creation of the encoder heap
465    if (!pD3D12Enc->m_spVideoEncoderHeap || codecChanged || profileChanged ||
466        levelChanged
467        // || codecConfigChanged // Only affects encoder
468        || inputFormatChanged   // Might affect internal textures in the heap
469        || resolutionChanged
470        // Only re-create if there is NO SUPPORT for reconfiguring rateControl on the fly
471        || (rateControlChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
472                                    D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) ==
473                                   0 /*checking the flag is NOT set*/))
474        // Only re-create if there is NO SUPPORT for reconfiguring slices on the fly
475        || (slicesChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
476                               D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) ==
477                              0 /*checking the flag is NOT set*/))
478        // Only re-create if there is NO SUPPORT for reconfiguring gop on the fly
479        || (gopChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
480                            D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) ==
481                           0 /*checking the flag is NOT set*/))
482        // || motionPrecisionLimitChanged // Only affects encoder
483    ) {
484       if (!pD3D12Enc->m_spVideoEncoderHeap) {
485          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating "
486                        "D3D12VideoEncoderHeap for the first time\n");
487       } else {
488          debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating D3D12VideoEncoderHeap\n");
489          reCreatedEncoderHeap = true;
490       }
491 
492       D3D12_VIDEO_ENCODER_HEAP_DESC heapDesc = { pD3D12Enc->m_NodeMask,
493                                                  D3D12_VIDEO_ENCODER_HEAP_FLAG_NONE,
494                                                  pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
495                                                  d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
496                                                  d3d12_video_encoder_get_current_level_desc(pD3D12Enc),
497                                                  // resolution list count
498                                                  1,
499                                                  // resolution list
500                                                  &pD3D12Enc->m_currentEncodeConfig.m_currentResolution };
501 
502       // Create encoder heap
503       pD3D12Enc->m_spVideoEncoderHeap.Reset();
504       HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CreateVideoEncoderHeap(&heapDesc,
505                                                                            IID_PPV_ARGS(pD3D12Enc->m_spVideoEncoderHeap.GetAddressOf()));
506       if (FAILED(hr)) {
507          debug_printf("CreateVideoEncoderHeap failed with HR %x\n", hr);
508          return false;
509       }
510    }
511 
512    // If on-the-fly reconfiguration happened without object recreation, set
513    // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_*_CHANGED reconfiguration flags in EncodeFrame
514    if (rateControlChanged &&
515        ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
516          D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) !=
517         0 /*checking if the flag it's actually set*/) &&
518        (pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
519       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RATE_CONTROL_CHANGE;
520    }
521 
522    if (slicesChanged &&
523        ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
524          D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) !=
525         0 /*checking if the flag it's actually set*/) &&
526        (pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
527       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_SUBREGION_LAYOUT_CHANGE;
528    }
529 
530    if (gopChanged &&
531        ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
532          D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) !=
533         0 /*checking if the flag it's actually set*/) &&
534        (pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
535       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_GOP_SEQUENCE_CHANGE;
536    }
537 
538    if(irChanged)
539       pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_REQUEST_INTRA_REFRESH;
540 
541    return true;
542 }
543 
544 void
d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder * pD3D12Enc,struct pipe_picture_desc * picture)545 d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder *pD3D12Enc, struct pipe_picture_desc *  picture)
546 {
547    pD3D12Enc->m_upDPBManager.reset();
548    pD3D12Enc->m_upBitstreamBuilder.reset();
549    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
550    switch (codec) {
551 #if VIDEO_CODEC_H264ENC
552       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
553       {
554          bool gopHasPFrames =
555             (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.PPicturePeriod > 0) &&
556             ((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.GOPLength == 0) ||
557              (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.PPicturePeriod <
558               pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.GOPLength));
559 
560          pD3D12Enc->m_upDPBManager = std::make_unique<d3d12_video_encoder_references_manager_h264>(
561             gopHasPFrames,
562             *pD3D12Enc->m_upDPBStorageManager,
563             // Max number of frames to be used as a reference, without counting the current recon picture
564             d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc)
565          );
566 
567          struct pipe_h264_enc_picture_desc *pH264Pic = (struct pipe_h264_enc_picture_desc *) picture;
568          pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_h264>(pH264Pic->insert_aud_nalu);
569       } break;
570 #endif
571 #if VIDEO_CODEC_H265ENC
572       case PIPE_VIDEO_FORMAT_HEVC:
573       {
574          bool gopHasPFrames =
575             (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.PPicturePeriod > 0) &&
576             ((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.GOPLength == 0) ||
577              (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.PPicturePeriod <
578               pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.GOPLength));
579 
580          pD3D12Enc->m_upDPBManager = std::make_unique<d3d12_video_encoder_references_manager_hevc>(
581             gopHasPFrames,
582             *pD3D12Enc->m_upDPBStorageManager,
583             // Max number of frames to be used as a reference, without counting the current recon picture
584             d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc)
585          );
586 
587          pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_hevc>();
588       } break;
589 #endif
590 #if VIDEO_CODEC_AV1ENC
591       case PIPE_VIDEO_FORMAT_AV1:
592       {
593          bool hasInterFrames =
594             (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.InterFramePeriod > 0) &&
595             ((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.IntraDistance == 0) ||
596              (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.InterFramePeriod <
597               pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.IntraDistance));
598 
599          pD3D12Enc->m_upDPBManager = std::make_unique<d3d12_video_encoder_references_manager_av1>(
600             hasInterFrames,
601             *pD3D12Enc->m_upDPBStorageManager
602          );
603 
604          // We use packed headers and pist encode execution syntax for AV1
605          pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_av1>();
606       } break;
607 #endif
608       default:
609       {
610          unreachable("Unsupported pipe_video_format");
611       } break;
612    }
613 }
614 
615 D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA
d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder * pD3D12Enc)616 d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder *pD3D12Enc)
617 {
618    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
619    switch (codec) {
620 #if VIDEO_CODEC_H264ENC
621       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
622       {
623          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {};
624          if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode !=
625              D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) {
626             subregionData.pSlicesPartition_H264 =
627                &pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264;
628             subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES);
629          }
630          return subregionData;
631       } break;
632 #endif
633 #if VIDEO_CODEC_H265ENC
634       case PIPE_VIDEO_FORMAT_HEVC:
635       {
636          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {};
637          if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode !=
638              D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) {
639             subregionData.pSlicesPartition_HEVC =
640                &pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_HEVC;
641             subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES);
642          }
643          return subregionData;
644       } break;
645 #endif
646 #if VIDEO_CODEC_AV1ENC
647       case PIPE_VIDEO_FORMAT_AV1:
648       {
649          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {};
650          if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode !=
651              D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) {
652             subregionData.pTilesPartition_AV1 =
653                &pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition;
654             subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES);
655          }
656          return subregionData;
657       } break;
658 #endif
659       default:
660       {
661          unreachable("Unsupported pipe_video_format");
662       } break;
663    }
664 }
665 
666 D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA
d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encoder * pD3D12Enc)667 d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encoder *pD3D12Enc)
668 {
669    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
670    switch (codec) {
671 #if VIDEO_CODEC_H264ENC
672       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
673       {
674          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {};
675          curPicParamsData.pH264PicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_H264PicData;
676          curPicParamsData.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_H264PicData);
677          return curPicParamsData;
678       } break;
679 #endif
680 #if VIDEO_CODEC_H265ENC
681       case PIPE_VIDEO_FORMAT_HEVC:
682       {
683          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {};
684          curPicParamsData.pHEVCPicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_HEVCPicData;
685          curPicParamsData.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_HEVCPicData);
686          return curPicParamsData;
687       } break;
688 #endif
689 #if VIDEO_CODEC_AV1ENC
690       case PIPE_VIDEO_FORMAT_AV1:
691       {
692          D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {};
693          curPicParamsData.pAV1PicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData;
694          curPicParamsData.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData);
695          return curPicParamsData;
696       } break;
697 #endif
698       default:
699       {
700          unreachable("Unsupported pipe_video_format");
701       } break;
702    }
703 }
704 
705 D3D12_VIDEO_ENCODER_RATE_CONTROL
d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder * pD3D12Enc)706 d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder *pD3D12Enc)
707 {
708    D3D12_VIDEO_ENCODER_RATE_CONTROL curRateControlDesc = {};
709    curRateControlDesc.Mode            = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode;
710    curRateControlDesc.Flags           = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags;
711    curRateControlDesc.TargetFrameRate = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate;
712 
713    if ((curRateControlDesc.Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENSION1_SUPPORT) != 0)
714    {
715       switch (pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode) {
716          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_ABSOLUTE_QP_MAP:
717          {
718             curRateControlDesc.ConfigParams.pConfiguration_CQP1 = nullptr;
719             curRateControlDesc.ConfigParams.DataSize           = 0;
720          } break;
721          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP:
722          {
723             curRateControlDesc.ConfigParams.pConfiguration_CQP1 =
724                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP1;
725             curRateControlDesc.ConfigParams.DataSize =
726                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP1);
727          } break;
728          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
729          {
730             curRateControlDesc.ConfigParams.pConfiguration_CBR1 =
731                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR1;
732             curRateControlDesc.ConfigParams.DataSize =
733                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR1);
734          } break;
735          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
736          {
737             curRateControlDesc.ConfigParams.pConfiguration_VBR1 =
738                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR1;
739             curRateControlDesc.ConfigParams.DataSize =
740                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR1);
741          } break;
742          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
743          {
744             curRateControlDesc.ConfigParams.pConfiguration_QVBR1 =
745                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1;
746             curRateControlDesc.ConfigParams.DataSize =
747                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1);
748          } break;
749          default:
750          {
751             unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE");
752          } break;
753       }
754    }
755    else
756    {
757       switch (pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode) {
758          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_ABSOLUTE_QP_MAP:
759          {
760             curRateControlDesc.ConfigParams.pConfiguration_CQP = nullptr;
761             curRateControlDesc.ConfigParams.DataSize           = 0;
762          } break;
763          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP:
764          {
765             curRateControlDesc.ConfigParams.pConfiguration_CQP =
766                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP;
767             curRateControlDesc.ConfigParams.DataSize =
768                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP);
769          } break;
770          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
771          {
772             curRateControlDesc.ConfigParams.pConfiguration_CBR =
773                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR;
774             curRateControlDesc.ConfigParams.DataSize =
775                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR);
776          } break;
777          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
778          {
779             curRateControlDesc.ConfigParams.pConfiguration_VBR =
780                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR;
781             curRateControlDesc.ConfigParams.DataSize =
782                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR);
783          } break;
784          case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
785          {
786             curRateControlDesc.ConfigParams.pConfiguration_QVBR =
787                &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR;
788             curRateControlDesc.ConfigParams.DataSize =
789                sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR);
790          } break;
791          default:
792          {
793             unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE");
794          } break;
795       }
796    }
797 
798    return curRateControlDesc;
799 }
800 
801 D3D12_VIDEO_ENCODER_LEVEL_SETTING
d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder * pD3D12Enc)802 d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc)
803 {
804    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
805    switch (codec) {
806 #if VIDEO_CODEC_H264ENC
807       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
808       {
809          D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {};
810          curLevelDesc.pH264LevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting;
811          curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting);
812          return curLevelDesc;
813       } break;
814 #endif
815 #if VIDEO_CODEC_H265ENC
816       case PIPE_VIDEO_FORMAT_HEVC:
817       {
818          D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {};
819          curLevelDesc.pHEVCLevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_HEVCLevelSetting;
820          curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_HEVCLevelSetting);
821          return curLevelDesc;
822       } break;
823 #endif
824 #if VIDEO_CODEC_AV1ENC
825       case PIPE_VIDEO_FORMAT_AV1:
826       {
827          D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {};
828          curLevelDesc.pAV1LevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting;
829          curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting);
830          return curLevelDesc;
831       } break;
832 #endif
833       default:
834       {
835          unreachable("Unsupported pipe_video_format");
836       } break;
837    }
838 }
839 
840 void
d3d12_video_encoder_build_pre_encode_codec_headers(struct d3d12_video_encoder * pD3D12Enc,bool & postEncodeHeadersNeeded,uint64_t & preEncodeGeneratedHeadersByteSize,std::vector<uint64_t> & pWrittenCodecUnitsSizes)841 d3d12_video_encoder_build_pre_encode_codec_headers(struct d3d12_video_encoder *pD3D12Enc,
842                                                    bool &postEncodeHeadersNeeded,
843                                                    uint64_t &preEncodeGeneratedHeadersByteSize,
844                                                    std::vector<uint64_t> &pWrittenCodecUnitsSizes)
845 {
846    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
847    switch (codec) {
848 #if VIDEO_CODEC_H264ENC
849       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
850       {
851          postEncodeHeadersNeeded = false;
852          preEncodeGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers_h264(pD3D12Enc, pWrittenCodecUnitsSizes);
853       } break;
854 #endif
855 #if VIDEO_CODEC_H265ENC
856       case PIPE_VIDEO_FORMAT_HEVC:
857       {
858          postEncodeHeadersNeeded = false;
859          preEncodeGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers_hevc(pD3D12Enc, pWrittenCodecUnitsSizes);
860       } break;
861 #endif
862 #if VIDEO_CODEC_AV1ENC
863       case PIPE_VIDEO_FORMAT_AV1:
864       {
865          pD3D12Enc->m_BitstreamHeadersBuffer.resize(0);
866          postEncodeHeadersNeeded = true;
867          preEncodeGeneratedHeadersByteSize = 0;
868          pWrittenCodecUnitsSizes.clear();
869       } break;
870 #endif
871       default:
872       {
873          unreachable("Unsupported pipe_video_format");
874       } break;
875    }
876 }
877 
878 D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE
d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder * pD3D12Enc)879 d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder *pD3D12Enc)
880 {
881    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
882    switch (codec) {
883 #if VIDEO_CODEC_H264ENC
884       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
885       {
886          D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {};
887          curGOPDesc.pH264GroupOfPictures =
888             &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures;
889          curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures);
890          return curGOPDesc;
891       } break;
892 #endif
893 #if VIDEO_CODEC_H265ENC
894       case PIPE_VIDEO_FORMAT_HEVC:
895       {
896          D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {};
897          curGOPDesc.pHEVCGroupOfPictures =
898             &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures;
899          curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures);
900          return curGOPDesc;
901       } break;
902 #endif
903 #if VIDEO_CODEC_AV1ENC
904       case PIPE_VIDEO_FORMAT_AV1:
905       {
906          D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {};
907          curGOPDesc.pAV1SequenceStructure =
908             &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure;
909          curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure);
910          return curGOPDesc;
911       } break;
912 #endif
913       default:
914       {
915          unreachable("Unsupported pipe_video_format");
916       } break;
917    }
918 }
919 
920 D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION
d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder * pD3D12Enc)921 d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder *pD3D12Enc)
922 {
923    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
924    switch (codec) {
925 #if VIDEO_CODEC_H264ENC
926       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
927       {
928          D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {};
929          codecConfigDesc.pH264Config = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config;
930          codecConfigDesc.DataSize =
931             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config);
932          return codecConfigDesc;
933       } break;
934 #endif
935 #if VIDEO_CODEC_H265ENC
936       case PIPE_VIDEO_FORMAT_HEVC:
937       {
938          D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {};
939          codecConfigDesc.pHEVCConfig = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_HEVCConfig;
940          codecConfigDesc.DataSize =
941             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_HEVCConfig);
942          return codecConfigDesc;
943       } break;
944 #endif
945 #if VIDEO_CODEC_AV1ENC
946       case PIPE_VIDEO_FORMAT_AV1:
947       {
948          D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {};
949          codecConfigDesc.pAV1Config = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config;
950          codecConfigDesc.DataSize =
951             sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config);
952          return codecConfigDesc;
953       } break;
954 #endif
955       default:
956       {
957          unreachable("Unsupported pipe_video_format");
958       } break;
959    }
960 }
961 
962 D3D12_VIDEO_ENCODER_CODEC
d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder * pD3D12Enc)963 d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc)
964 {
965    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
966    switch (codec) {
967 #if VIDEO_CODEC_H264ENC
968       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
969       {
970          return D3D12_VIDEO_ENCODER_CODEC_H264;
971       } break;
972 #endif
973 #if VIDEO_CODEC_H265ENC
974       case PIPE_VIDEO_FORMAT_HEVC:
975       {
976          return D3D12_VIDEO_ENCODER_CODEC_HEVC;
977       } break;
978 #endif
979 #if VIDEO_CODEC_AV1ENC
980       case PIPE_VIDEO_FORMAT_AV1:
981       {
982          return D3D12_VIDEO_ENCODER_CODEC_AV1;
983       } break;
984 #endif
985       default:
986       {
987          unreachable("Unsupported pipe_video_format");
988       } break;
989    }
990 }
991 
992 static void
d3d12_video_encoder_disable_rc_vbv_sizes(struct D3D12EncodeRateControlState & rcState)993 d3d12_video_encoder_disable_rc_vbv_sizes(struct D3D12EncodeRateControlState & rcState)
994 {
995    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
996    switch (rcState.m_Mode) {
997       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
998       {
999          rcState.m_Config.m_Configuration_CBR.VBVCapacity = 0;
1000          rcState.m_Config.m_Configuration_CBR.InitialVBVFullness = 0;
1001       } break;
1002       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
1003       {
1004          rcState.m_Config.m_Configuration_VBR.VBVCapacity = 0;
1005          rcState.m_Config.m_Configuration_VBR.InitialVBVFullness = 0;
1006       } break;
1007       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
1008       {
1009          rcState.m_Config.m_Configuration_QVBR1.VBVCapacity = 0;
1010          rcState.m_Config.m_Configuration_QVBR1.InitialVBVFullness = 0;
1011       } break;
1012       default:
1013       {
1014          unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE for VBV Sizes");
1015       } break;
1016    }
1017 }
1018 
1019 static void
d3d12_video_encoder_disable_rc_maxframesize(struct D3D12EncodeRateControlState & rcState)1020 d3d12_video_encoder_disable_rc_maxframesize(struct D3D12EncodeRateControlState & rcState)
1021 {
1022    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
1023    rcState.max_frame_size = 0;
1024    switch (rcState.m_Mode) {
1025       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
1026       {
1027          rcState.m_Config.m_Configuration_CBR.MaxFrameBitSize = 0;
1028       } break;
1029       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
1030       {
1031          rcState.m_Config.m_Configuration_VBR.MaxFrameBitSize = 0;
1032       } break;
1033       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
1034       {
1035          rcState.m_Config.m_Configuration_QVBR.MaxFrameBitSize = 0;
1036       } break;
1037       default:
1038       {
1039          unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE for VBV Sizes");
1040       } break;
1041    }
1042 }
1043 
1044 static bool
d3d12_video_encoder_is_qualitylevel_in_range(struct D3D12EncodeRateControlState & rcState,UINT MaxQualityVsSpeed)1045 d3d12_video_encoder_is_qualitylevel_in_range(struct D3D12EncodeRateControlState & rcState, UINT MaxQualityVsSpeed)
1046 {
1047    switch (rcState.m_Mode) {
1048       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP:
1049       {
1050          return rcState.m_Config.m_Configuration_CQP1.QualityVsSpeed <= MaxQualityVsSpeed;
1051       } break;
1052       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
1053       {
1054          return rcState.m_Config.m_Configuration_CBR1.QualityVsSpeed <= MaxQualityVsSpeed;
1055       } break;
1056       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
1057       {
1058          return rcState.m_Config.m_Configuration_VBR1.QualityVsSpeed <= MaxQualityVsSpeed;
1059       } break;
1060       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
1061       {
1062          return rcState.m_Config.m_Configuration_QVBR1.QualityVsSpeed <= MaxQualityVsSpeed;
1063       } break;
1064       default:
1065       {
1066          unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE");
1067       } break;
1068    }
1069 }
1070 
1071 static void
d3d12_video_encoder_disable_rc_qualitylevels(struct D3D12EncodeRateControlState & rcState)1072 d3d12_video_encoder_disable_rc_qualitylevels(struct D3D12EncodeRateControlState & rcState)
1073 {
1074    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QUALITY_VS_SPEED;
1075    switch (rcState.m_Mode) {
1076       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
1077       {
1078          rcState.m_Config.m_Configuration_CBR1.QualityVsSpeed = 0;
1079       } break;
1080       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
1081       {
1082          rcState.m_Config.m_Configuration_VBR1.QualityVsSpeed = 0;
1083       } break;
1084       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
1085       {
1086          rcState.m_Config.m_Configuration_QVBR1.QualityVsSpeed = 0;
1087       } break;
1088       default:
1089       {
1090          unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE");
1091       } break;
1092    }
1093 }
1094 
1095 static void
d3d12_video_encoder_disable_rc_deltaqp(struct D3D12EncodeRateControlState & rcState)1096 d3d12_video_encoder_disable_rc_deltaqp(struct D3D12EncodeRateControlState & rcState)
1097 {
1098    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_DELTA_QP;
1099 }
1100 
1101 static void
d3d12_video_encoder_disable_rc_minmaxqp(struct D3D12EncodeRateControlState & rcState)1102 d3d12_video_encoder_disable_rc_minmaxqp(struct D3D12EncodeRateControlState & rcState)
1103 {
1104    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE;
1105    switch (rcState.m_Mode) {
1106       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
1107       {
1108          rcState.m_Config.m_Configuration_CBR.MinQP = 0;
1109          rcState.m_Config.m_Configuration_CBR.MaxQP = 0;
1110       } break;
1111       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
1112       {
1113          rcState.m_Config.m_Configuration_VBR.MinQP = 0;
1114          rcState.m_Config.m_Configuration_VBR.MaxQP = 0;
1115       } break;
1116       case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
1117       {
1118          rcState.m_Config.m_Configuration_QVBR.MinQP = 0;
1119          rcState.m_Config.m_Configuration_QVBR.MaxQP = 0;
1120       } break;
1121       default:
1122       {
1123          unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE for VBV Sizes");
1124       } break;
1125    }
1126 }
1127 
1128 static void
d3d12_video_encoder_disable_rc_extended1_to_legacy(struct D3D12EncodeRateControlState & rcState)1129 d3d12_video_encoder_disable_rc_extended1_to_legacy(struct D3D12EncodeRateControlState & rcState)
1130 {
1131    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENSION1_SUPPORT;
1132    // Also remove features that require extension1 enabled (eg. quality levels)
1133    rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QUALITY_VS_SPEED;
1134    // rcState.m_Configuration_XXX and m_Configuration_XXX1 are unions, can be aliased
1135    // as the m_Configuration_XXX1 extensions are binary backcompat with m_Configuration_XXX
1136 }
1137 
1138 ///
1139 /// Call d3d12_video_encoder_query_d3d12_driver_caps and see if any optional feature requested
1140 /// is not supported, disable it, query again until finding a negotiated cap/feature set
1141 /// Note that with fallbacks, the upper layer will not get exactly the encoding seetings they requested
1142 /// but for very particular settings it's better to continue with warnings than failing the whole encoding process
1143 ///
d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder * pD3D12Enc,D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 & capEncoderSupportData1)1144 bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 &capEncoderSupportData1) {
1145 
1146    ///
1147    /// Check for general support
1148    /// Check for validation errors (some drivers return general support but also validation errors anyways, work around for those unexpected cases)
1149    ///
1150 
1151    bool configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData1)
1152     && (((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0)
1153                         && (capEncoderSupportData1.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
1154 
1155    ///
1156    /// If D3D12_FEATURE_VIDEO_ENCODER_SUPPORT is not supported, try falling back to unsetting optional features and check for caps again
1157    ///
1158 
1159    if (!configSupported) {
1160       debug_printf("[d3d12_video_encoder] WARNING: D3D12_FEATURE_VIDEO_ENCODER_SUPPORT is not supported, trying fallback to unsetting optional features\n");
1161 
1162       bool isRequestingVBVSizesSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_VBV_SIZE_CONFIG_AVAILABLE) != 0);
1163       bool isClientRequestingVBVSizes = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES) != 0);
1164 
1165       if(isClientRequestingVBVSizes && !isRequestingVBVSizesSupported) {
1166          debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES with VBVCapacity and InitialVBVFullness is not supported, will continue encoding unsetting this feature as fallback.\n");
1167          d3d12_video_encoder_disable_rc_vbv_sizes(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1168       }
1169 
1170       bool isRequestingPeakFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0);
1171       bool isClientRequestingPeakFrameSize = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE) != 0);
1172 
1173       if(isClientRequestingPeakFrameSize && !isRequestingPeakFrameSizeSupported) {
1174          debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE with MaxFrameBitSize but the feature is not supported, will continue encoding unsetting this feature as fallback.\n");
1175          d3d12_video_encoder_disable_rc_maxframesize(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1176       }
1177 
1178       bool isRequestingQPRangesSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_ADJUSTABLE_QP_RANGE_AVAILABLE) != 0);
1179       bool isClientRequestingQPRanges = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE) != 0);
1180 
1181       if(isClientRequestingQPRanges && !isRequestingQPRangesSupported) {
1182          debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE with QPMin QPMax but the feature is not supported, will continue encoding unsetting this feature as fallback.\n");
1183          d3d12_video_encoder_disable_rc_minmaxqp(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1184       }
1185 
1186       bool isRequestingDeltaQPSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_DELTA_QP_AVAILABLE) != 0);
1187       bool isClientRequestingDeltaQP = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_DELTA_QP) != 0);
1188 
1189       if(isClientRequestingDeltaQP && !isRequestingDeltaQPSupported) {
1190          debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_DELTA_QP but the feature is not supported, will continue encoding unsetting this feature as fallback.\n");
1191          d3d12_video_encoder_disable_rc_deltaqp(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1192       }
1193 
1194       bool isRequestingExtended1RCSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENSION1_SUPPORT) != 0);
1195       bool isClientRequestingExtended1RC = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENSION1_SUPPORT) != 0);
1196 
1197       if(isClientRequestingExtended1RC && !isRequestingExtended1RCSupported) {
1198          debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENSION1_SUPPORT but the feature is not supported, will continue encoding unsetting this feature and dependent features as fallback.\n");
1199          d3d12_video_encoder_disable_rc_extended1_to_legacy(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1200       }
1201 
1202       /* d3d12_video_encoder_disable_rc_extended1_to_legacy may change m_Flags */
1203       if ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENSION1_SUPPORT) != 0)
1204       { // Quality levels also requires D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENSION1_SUPPORT
1205          bool isRequestingQualityLevelsSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_QUALITY_VS_SPEED_AVAILABLE) != 0);
1206          bool isClientRequestingQualityLevels = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QUALITY_VS_SPEED) != 0);
1207 
1208          if (isClientRequestingQualityLevels)
1209          {
1210             if (!isRequestingQualityLevelsSupported) {
1211                debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QUALITY_VS_SPEED but the feature is not supported, will continue encoding unsetting this feature as fallback.\n");
1212                d3d12_video_encoder_disable_rc_qualitylevels(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1213             } else if (!d3d12_video_encoder_is_qualitylevel_in_range(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc, capEncoderSupportData1.MaxQualityVsSpeed)) {
1214                debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QUALITY_VS_SPEED but the value is out of supported range, will continue encoding unsetting this feature as fallback.\n");
1215                d3d12_video_encoder_disable_rc_qualitylevels(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc);
1216             }
1217          }
1218       }
1219 
1220       /* Try fallback for multi-slice/tile not supported with single subregion mode */
1221       if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_SUBREGION_LAYOUT_MODE_NOT_SUPPORTED) != 0) {
1222          pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME;
1223          debug_printf("[d3d12_video_encoder] WARNING: Requested slice/tile mode not supported by driver, will continue encoding with single subregion encoding.\n");
1224       }
1225 
1226       ///
1227       /// Try fallback configuration
1228       ///
1229       configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData1)
1230          && (((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0)
1231                         && (capEncoderSupportData1.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
1232    }
1233 
1234    if (pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.IntraRefreshDuration >
1235       pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxIntraRefreshFrameDuration)
1236    {
1237       debug_printf("[d3d12_video_encoder] Desired duration of intrarefresh %d is not supported (higher than max "
1238                   "reported IR duration %d in query caps) for current resolution.\n",
1239                   pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.IntraRefreshDuration,
1240                   pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxIntraRefreshFrameDuration);
1241       return false;
1242    }
1243 
1244    if(!configSupported) {
1245       debug_printf("[d3d12_video_encoder] Cap negotiation failed, see more details below:\n");
1246 
1247       if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_NOT_SUPPORTED) != 0) {
1248          debug_printf("[d3d12_video_encoder] Requested codec is not supported\n");
1249       }
1250 
1251       if ((capEncoderSupportData1.ValidationFlags &
1252          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RESOLUTION_NOT_SUPPORTED_IN_LIST) != 0) {
1253          debug_printf("[d3d12_video_encoder] Requested resolution is not supported\n");
1254       }
1255 
1256       if ((capEncoderSupportData1.ValidationFlags &
1257          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED) != 0) {
1258          debug_printf("[d3d12_video_encoder] Requested bitrate or rc config is not supported\n");
1259       }
1260 
1261       if ((capEncoderSupportData1.ValidationFlags &
1262          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_CONFIGURATION_NOT_SUPPORTED) != 0) {
1263          debug_printf("[d3d12_video_encoder] Requested codec config is not supported\n");
1264       }
1265 
1266       if ((capEncoderSupportData1.ValidationFlags &
1267          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED) != 0) {
1268          debug_printf("[d3d12_video_encoder] Requested rate control mode is not supported\n");
1269       }
1270 
1271       if ((capEncoderSupportData1.ValidationFlags &
1272          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INTRA_REFRESH_MODE_NOT_SUPPORTED) != 0) {
1273          debug_printf("[d3d12_video_encoder] Requested intra refresh config is not supported\n");
1274       }
1275 
1276       if ((capEncoderSupportData1.ValidationFlags &
1277          D3D12_VIDEO_ENCODER_VALIDATION_FLAG_SUBREGION_LAYOUT_MODE_NOT_SUPPORTED) != 0) {
1278          debug_printf("[d3d12_video_encoder] Requested subregion layout mode is not supported\n");
1279       }
1280 
1281       if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INPUT_FORMAT_NOT_SUPPORTED) !=
1282          0) {
1283          debug_printf("[d3d12_video_encoder] Requested input dxgi format is not supported\n");
1284       }
1285    }
1286 
1287    if (memcmp(&pD3D12Enc->m_prevFrameEncodeConfig.m_encoderRateControlDesc,
1288               &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc,
1289               sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc)) != 0) {
1290       pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_rate_control;
1291    }
1292 
1293    return configSupported;
1294 }
1295 
d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder * pD3D12Enc,D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 & capEncoderSupportData1)1296 bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 &capEncoderSupportData1) {
1297    capEncoderSupportData1.NodeIndex                                = pD3D12Enc->m_NodeIndex;
1298    capEncoderSupportData1.Codec                                    = d3d12_video_encoder_get_current_codec(pD3D12Enc);
1299    capEncoderSupportData1.InputFormat            = pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format;
1300    capEncoderSupportData1.RateControl            = d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc);
1301    capEncoderSupportData1.IntraRefresh           = pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.Mode;
1302    capEncoderSupportData1.SubregionFrameEncoding = pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode;
1303    capEncoderSupportData1.ResolutionsListCount   = 1;
1304    capEncoderSupportData1.pResolutionList        = &pD3D12Enc->m_currentEncodeConfig.m_currentResolution;
1305    capEncoderSupportData1.CodecGopSequence       = d3d12_video_encoder_get_current_gop_desc(pD3D12Enc);
1306    capEncoderSupportData1.MaxReferenceFramesInDPB = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
1307    capEncoderSupportData1.CodecConfiguration = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc);
1308 
1309    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
1310    switch (codec) {
1311 #if VIDEO_CODEC_H264ENC
1312       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
1313       {
1314          capEncoderSupportData1.SuggestedProfile.pH264Profile =
1315             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile;
1316          capEncoderSupportData1.SuggestedProfile.DataSize =
1317             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile);
1318          capEncoderSupportData1.SuggestedLevel.pH264LevelSetting =
1319             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting;
1320          capEncoderSupportData1.SuggestedLevel.DataSize =
1321             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting);
1322       } break;
1323 #endif
1324 #if VIDEO_CODEC_H265ENC
1325       case PIPE_VIDEO_FORMAT_HEVC:
1326       {
1327          capEncoderSupportData1.SuggestedProfile.pHEVCProfile =
1328             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_HEVCProfile;
1329          capEncoderSupportData1.SuggestedProfile.DataSize =
1330             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_HEVCProfile);
1331          capEncoderSupportData1.SuggestedLevel.pHEVCLevelSetting =
1332             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_HEVCLevelSetting;
1333          capEncoderSupportData1.SuggestedLevel.DataSize =
1334             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_HEVCLevelSetting);
1335       } break;
1336 #endif
1337 #if VIDEO_CODEC_AV1ENC
1338       case PIPE_VIDEO_FORMAT_AV1:
1339       {
1340          capEncoderSupportData1.SuggestedProfile.pAV1Profile =
1341             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_AV1Profile;
1342          capEncoderSupportData1.SuggestedProfile.DataSize =
1343             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_AV1Profile);
1344          capEncoderSupportData1.SuggestedLevel.pAV1LevelSetting =
1345             &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting;
1346          capEncoderSupportData1.SuggestedLevel.DataSize =
1347             sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting);
1348       } break;
1349 #endif
1350       default:
1351       {
1352          unreachable("Unsupported pipe_video_format");
1353       } break;
1354    }
1355 
1356    // prepare inout storage for the resolution dependent result.
1357    capEncoderSupportData1.pResolutionDependentSupport =
1358       &pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps;
1359 
1360    capEncoderSupportData1.SubregionFrameEncodingData = d3d12_video_encoder_get_current_slice_param_settings(pD3D12Enc);
1361    HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1,
1362                                                                          &capEncoderSupportData1,
1363                                                                          sizeof(capEncoderSupportData1));
1364    if (FAILED(hr)) {
1365       debug_printf("CheckFeatureSupport D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1 failed with HR %x\n", hr);
1366       debug_printf("Falling back to check previous query version D3D12_FEATURE_VIDEO_ENCODER_SUPPORT...\n");
1367 
1368       // D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 extends D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT
1369       // in a binary compatible way, so just cast it and try with the older query D3D12_FEATURE_VIDEO_ENCODER_SUPPORT
1370       D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT * casted_down_cap_data = reinterpret_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT*>(&capEncoderSupportData1);
1371       hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
1372                                                                          casted_down_cap_data,
1373                                                                          sizeof(D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT));
1374       if (FAILED(hr)) {
1375          debug_printf("CheckFeatureSupport D3D12_FEATURE_VIDEO_ENCODER_SUPPORT failed with HR %x\n", hr);
1376          return false;
1377       }
1378    }
1379    pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags    = capEncoderSupportData1.SupportFlags;
1380    pD3D12Enc->m_currentEncodeCapabilities.m_ValidationFlags = capEncoderSupportData1.ValidationFlags;
1381    return true;
1382 }
1383 
d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder * pD3D12Enc,D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode)1384 bool d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder *pD3D12Enc,
1385                                     D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode
1386    )
1387 {
1388    D3D12_FEATURE_DATA_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE capDataSubregionLayout = { };
1389    capDataSubregionLayout.NodeIndex = pD3D12Enc->m_NodeIndex;
1390    capDataSubregionLayout.Codec = d3d12_video_encoder_get_current_codec(pD3D12Enc);
1391    capDataSubregionLayout.Profile = d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
1392    capDataSubregionLayout.Level = d3d12_video_encoder_get_current_level_desc(pD3D12Enc);
1393    capDataSubregionLayout.SubregionMode = requestedSlicesMode;
1394    HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE, &capDataSubregionLayout, sizeof(capDataSubregionLayout));
1395    if (FAILED(hr)) {
1396       debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
1397       return false;
1398    }
1399    return capDataSubregionLayout.IsSupported;
1400 }
1401 
1402 D3D12_VIDEO_ENCODER_PROFILE_DESC
d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder * pD3D12Enc)1403 d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder *pD3D12Enc)
1404 {
1405    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
1406    switch (codec) {
1407 #if VIDEO_CODEC_H264ENC
1408       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
1409       {
1410          D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {};
1411          curProfDesc.pH264Profile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile;
1412          curProfDesc.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile);
1413          return curProfDesc;
1414       } break;
1415 #endif
1416 #if VIDEO_CODEC_H265ENC
1417       case PIPE_VIDEO_FORMAT_HEVC:
1418       {
1419          D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {};
1420          curProfDesc.pHEVCProfile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_HEVCProfile;
1421          curProfDesc.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_HEVCProfile);
1422          return curProfDesc;
1423       } break;
1424 #endif
1425 #if VIDEO_CODEC_AV1ENC
1426       case PIPE_VIDEO_FORMAT_AV1:
1427       {
1428          D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {};
1429          curProfDesc.pAV1Profile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile;
1430          curProfDesc.DataSize     = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile);
1431          return curProfDesc;
1432       } break;
1433 #endif
1434       default:
1435       {
1436          unreachable("Unsupported pipe_video_format");
1437       } break;
1438    }
1439 }
1440 
1441 uint32_t
d3d12_video_encoder_get_current_max_dpb_capacity(struct d3d12_video_encoder * pD3D12Enc)1442 d3d12_video_encoder_get_current_max_dpb_capacity(struct d3d12_video_encoder *pD3D12Enc)
1443 {
1444    return pD3D12Enc->base.max_references;
1445 }
1446 
1447 bool
d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encoder * pD3D12Enc,D3D12_VIDEO_SAMPLE srcTextureDesc,struct pipe_picture_desc * picture)1448 d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encoder *pD3D12Enc,
1449                                                         D3D12_VIDEO_SAMPLE srcTextureDesc,
1450                                                         struct pipe_picture_desc *  picture)
1451 {
1452    pD3D12Enc->m_prevFrameEncodeConfig = pD3D12Enc->m_currentEncodeConfig;
1453 
1454    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
1455    switch (codec) {
1456 #if VIDEO_CODEC_H264ENC
1457       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
1458       {
1459          return d3d12_video_encoder_update_current_encoder_config_state_h264(pD3D12Enc, srcTextureDesc, picture);
1460       } break;
1461 #endif
1462 #if VIDEO_CODEC_H265ENC
1463       case PIPE_VIDEO_FORMAT_HEVC:
1464       {
1465          return d3d12_video_encoder_update_current_encoder_config_state_hevc(pD3D12Enc, srcTextureDesc, picture);
1466       } break;
1467 #endif
1468 #if VIDEO_CODEC_AV1ENC
1469       case PIPE_VIDEO_FORMAT_AV1:
1470       {
1471          return d3d12_video_encoder_update_current_encoder_config_state_av1(pD3D12Enc, srcTextureDesc, picture);
1472       } break;
1473 #endif
1474       default:
1475       {
1476          unreachable("Unsupported pipe_video_format");
1477       } break;
1478    }
1479 }
1480 
1481 bool
d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder * pD3D12Enc)1482 d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc)
1483 {
1484    assert(pD3D12Enc->m_spD3D12VideoDevice);
1485 
1486    D3D12_COMMAND_QUEUE_DESC commandQueueDesc = { D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE };
1487    HRESULT                  hr               = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandQueue(
1488       &commandQueueDesc,
1489       IID_PPV_ARGS(pD3D12Enc->m_spEncodeCommandQueue.GetAddressOf()));
1490    if (FAILED(hr)) {
1491       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateCommandQueue "
1492                       "failed with HR %x\n",
1493                       hr);
1494       return false;
1495    }
1496 
1497    hr = pD3D12Enc->m_pD3D12Screen->dev->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&pD3D12Enc->m_spFence));
1498    if (FAILED(hr)) {
1499       debug_printf(
1500          "[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateFence failed with HR %x\n",
1501          hr);
1502       return false;
1503    }
1504 
1505    for (auto& inputResource : pD3D12Enc->m_inflightResourcesPool)
1506    {
1507       // Create associated command allocator for Encode, Resolve operations
1508       hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandAllocator(
1509          D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
1510          IID_PPV_ARGS(inputResource.m_spCommandAllocator.GetAddressOf()));
1511       if (FAILED(hr)) {
1512          debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to "
1513                         "CreateCommandAllocator failed with HR %x\n",
1514                         hr);
1515          return false;
1516       }
1517    }
1518 
1519    ComPtr<ID3D12Device4> spD3D12Device4;
1520    if (FAILED(pD3D12Enc->m_pD3D12Screen->dev->QueryInterface(
1521           IID_PPV_ARGS(spD3D12Device4.GetAddressOf())))) {
1522       debug_printf(
1523          "[d3d12_video_encoder] d3d12_video_encoder_create_encoder - D3D12 Device has no Video encode support\n");
1524       return false;
1525    }
1526 
1527    hr = spD3D12Device4->CreateCommandList1(0,
1528                         D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
1529                         D3D12_COMMAND_LIST_FLAG_NONE,
1530                         IID_PPV_ARGS(pD3D12Enc->m_spEncodeCommandList.GetAddressOf()));
1531 
1532    if (FAILED(hr)) {
1533       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateCommandList "
1534                       "failed with HR %x\n",
1535                       hr);
1536       return false;
1537    }
1538 
1539    return true;
1540 }
1541 
1542 struct pipe_video_codec *
d3d12_video_encoder_create_encoder(struct pipe_context * context,const struct pipe_video_codec * codec)1543 d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *codec)
1544 {
1545    ///
1546    /// Initialize d3d12_video_encoder
1547    ///
1548 
1549    // Not using new doesn't call ctor and the initializations in the class declaration are lost
1550    struct d3d12_video_encoder *pD3D12Enc = new d3d12_video_encoder;
1551 
1552    pD3D12Enc->m_spEncodedFrameMetadata.resize(D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT, {nullptr, 0, 0});
1553    pD3D12Enc->m_inflightResourcesPool.resize(D3D12_VIDEO_ENC_ASYNC_DEPTH, { 0 });
1554 
1555    pD3D12Enc->base         = *codec;
1556    pD3D12Enc->m_screen     = context->screen;
1557    pD3D12Enc->base.context = context;
1558    pD3D12Enc->base.width   = codec->width;
1559    pD3D12Enc->base.height  = codec->height;
1560    pD3D12Enc->base.max_references  = codec->max_references;
1561    // Only fill methods that are supported by the d3d12 encoder, leaving null the rest (ie. encode_* / encode_macroblock)
1562    pD3D12Enc->base.destroy          = d3d12_video_encoder_destroy;
1563    pD3D12Enc->base.begin_frame      = d3d12_video_encoder_begin_frame;
1564    pD3D12Enc->base.encode_bitstream = d3d12_video_encoder_encode_bitstream;
1565    pD3D12Enc->base.end_frame        = d3d12_video_encoder_end_frame;
1566    pD3D12Enc->base.flush            = d3d12_video_encoder_flush;
1567    pD3D12Enc->base.get_encode_headers = d3d12_video_encoder_get_encode_headers;
1568    pD3D12Enc->base.get_feedback_fence = d3d12_video_encoder_get_feedback_fence;
1569    pD3D12Enc->base.get_feedback     = d3d12_video_encoder_get_feedback;
1570 
1571    struct d3d12_context *pD3D12Ctx = (struct d3d12_context *) context;
1572    pD3D12Enc->m_pD3D12Screen       = d3d12_screen(pD3D12Ctx->base.screen);
1573 
1574    if (FAILED(pD3D12Enc->m_pD3D12Screen->dev->QueryInterface(
1575           IID_PPV_ARGS(pD3D12Enc->m_spD3D12VideoDevice.GetAddressOf())))) {
1576       debug_printf(
1577          "[d3d12_video_encoder] d3d12_video_encoder_create_encoder - D3D12 Device has no Video encode support\n");
1578       goto failed;
1579    }
1580 
1581    if (!d3d12_video_encoder_create_command_objects(pD3D12Enc)) {
1582       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_encoder - Failure on "
1583                       "d3d12_video_encoder_create_command_objects\n");
1584       goto failed;
1585    }
1586 
1587    // Cache quality levels cap
1588    pD3D12Enc->max_quality_levels = context->screen->get_video_param(context->screen, codec->profile,
1589                                     codec->entrypoint,
1590                                     PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL);
1591 
1592    return &pD3D12Enc->base;
1593 
1594 failed:
1595    if (pD3D12Enc != nullptr) {
1596       d3d12_video_encoder_destroy((struct pipe_video_codec *) pD3D12Enc);
1597    }
1598 
1599    return nullptr;
1600 }
1601 
1602 bool
d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder * pD3D12Enc,struct pipe_video_buffer * srcTexture,struct pipe_picture_desc * picture)1603 d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder *pD3D12Enc,
1604                                            struct pipe_video_buffer *  srcTexture,
1605                                            struct pipe_picture_desc *  picture)
1606 {
1607    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.NodeIndex = pD3D12Enc->m_NodeIndex;
1608    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.Codec =
1609       pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc;
1610    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.Profile =
1611       d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
1612    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.InputFormat =
1613       pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format;
1614    pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.PictureTargetResolution =
1615       pD3D12Enc->m_currentEncodeConfig.m_currentResolution;
1616 
1617    HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(
1618       D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
1619       &pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps,
1620       sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps));
1621 
1622    if (FAILED(hr)) {
1623       debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
1624       return false;
1625    }
1626 
1627    if (!pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.IsSupported) {
1628       debug_printf("[d3d12_video_encoder] D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS arguments are not supported.\n");
1629       return false;
1630    }
1631 
1632    uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
1633 
1634    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
1635    d3d12_video_encoder_calculate_metadata_resolved_buffer_size(
1636       codec,
1637       pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput,
1638       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize);
1639 
1640    D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
1641    if ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer == nullptr) ||
1642        (GetDesc(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get()).Width <
1643         pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize)) {
1644       CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
1645          pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize);
1646 
1647       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Reset();
1648       HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
1649          &Properties,
1650          D3D12_HEAP_FLAG_NONE,
1651          &resolvedMetadataBufferDesc,
1652          D3D12_RESOURCE_STATE_COMMON,
1653          nullptr,
1654          IID_PPV_ARGS(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.GetAddressOf()));
1655 
1656       if (FAILED(hr)) {
1657          debug_printf("CreateCommittedResource failed with HR %x\n", hr);
1658          return false;
1659       }
1660    }
1661 
1662    if ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer == nullptr) ||
1663        (GetDesc(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get()).Width <
1664         pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize)) {
1665       CD3DX12_RESOURCE_DESC metadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
1666          pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize);
1667 
1668       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Reset();
1669       HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
1670          &Properties,
1671          D3D12_HEAP_FLAG_NONE,
1672          &metadataBufferDesc,
1673          D3D12_RESOURCE_STATE_COMMON,
1674          nullptr,
1675          IID_PPV_ARGS(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.GetAddressOf()));
1676 
1677       if (FAILED(hr)) {
1678          debug_printf("CreateCommittedResource failed with HR %x\n", hr);
1679          return false;
1680       }
1681    }
1682    return true;
1683 }
1684 
1685 bool
d3d12_video_encoder_reconfigure_session(struct d3d12_video_encoder * pD3D12Enc,struct pipe_video_buffer * srcTexture,struct pipe_picture_desc * picture)1686 d3d12_video_encoder_reconfigure_session(struct d3d12_video_encoder *pD3D12Enc,
1687                                         struct pipe_video_buffer *  srcTexture,
1688                                         struct pipe_picture_desc *  picture)
1689 {
1690    assert(pD3D12Enc->m_spD3D12VideoDevice);
1691    D3D12_VIDEO_SAMPLE srcTextureDesc = {};
1692    srcTextureDesc.Width = srcTexture->width;
1693    srcTextureDesc.Height = srcTexture->height;
1694    srcTextureDesc.Format.Format = d3d12_get_format(srcTexture->buffer_format);
1695    if(!d3d12_video_encoder_update_current_encoder_config_state(pD3D12Enc, srcTextureDesc, picture)) {
1696       debug_printf("d3d12_video_encoder_update_current_encoder_config_state failed!\n");
1697       return false;
1698    }
1699    if(!d3d12_video_encoder_reconfigure_encoder_objects(pD3D12Enc, srcTexture, picture)) {
1700       debug_printf("d3d12_video_encoder_reconfigure_encoder_objects failed!\n");
1701       return false;
1702    }
1703    d3d12_video_encoder_update_picparams_tracking(pD3D12Enc, srcTexture, picture);
1704    if(!d3d12_video_encoder_prepare_output_buffers(pD3D12Enc, srcTexture, picture)) {
1705       debug_printf("d3d12_video_encoder_prepare_output_buffers failed!\n");
1706       return false;
1707    }
1708 
1709    // Save frame size expectation snapshot from record time to resolve at get_feedback time (after execution)
1710    uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
1711    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].expected_max_frame_size =
1712       pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.max_frame_size;
1713 
1714    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].expected_max_slice_size =
1715       (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode == D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_BYTES_PER_SUBREGION) ?
1716       pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264.MaxBytesPerSlice : 0;
1717 
1718    return true;
1719 }
1720 
1721 /**
1722  * start encoding of a new frame
1723  */
1724 void
d3d12_video_encoder_begin_frame(struct pipe_video_codec * codec,struct pipe_video_buffer * target,struct pipe_picture_desc * picture)1725 d3d12_video_encoder_begin_frame(struct pipe_video_codec * codec,
1726                                 struct pipe_video_buffer *target,
1727                                 struct pipe_picture_desc *picture)
1728 {
1729    // Do nothing here. Initialize happens on encoder creation, re-config (if any) happens in
1730    // d3d12_video_encoder_encode_bitstream
1731    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
1732    assert(pD3D12Enc);
1733    HRESULT hr = S_OK;
1734    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame started for fenceValue: %" PRIu64 "\n",
1735                  pD3D12Enc->m_fenceValue);
1736 
1737    ///
1738    /// Wait here to make sure the next in flight resource set is empty before using it
1739    ///
1740    uint64_t fenceValueToWaitOn = static_cast<uint64_t>(std::max(static_cast<int64_t>(0l), static_cast<int64_t>(pD3D12Enc->m_fenceValue) - static_cast<int64_t>(D3D12_VIDEO_ENC_ASYNC_DEPTH) ));
1741 
1742    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame Waiting for completion of in flight resource sets with previous work with fenceValue: %" PRIu64 "\n",
1743                  fenceValueToWaitOn);
1744 
1745    d3d12_video_encoder_ensure_fence_finished(codec, fenceValueToWaitOn, OS_TIMEOUT_INFINITE);
1746 
1747    if (!d3d12_video_encoder_reconfigure_session(pD3D12Enc, target, picture)) {
1748       debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame - Failure on "
1749                       "d3d12_video_encoder_reconfigure_session\n");
1750       goto fail;
1751    }
1752 
1753    hr = pD3D12Enc->m_spEncodeCommandList->Reset(pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spCommandAllocator.Get());
1754    if (FAILED(hr)) {
1755       debug_printf(
1756          "[d3d12_video_encoder] d3d12_video_encoder_flush - resetting ID3D12GraphicsCommandList failed with HR %x\n",
1757          hr);
1758       goto fail;
1759    }
1760 
1761    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_InputSurfaceFence = (struct d3d12_fence*) *picture->fence;
1762    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_OK;
1763    pD3D12Enc->m_spEncodedFrameMetadata[pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_OK;
1764 
1765    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame finalized for fenceValue: %" PRIu64 "\n",
1766                  pD3D12Enc->m_fenceValue);
1767    return;
1768 
1769 fail:
1770    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame failed for fenceValue: %" PRIu64 "\n",
1771                 pD3D12Enc->m_fenceValue);
1772    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
1773    pD3D12Enc->m_spEncodedFrameMetadata[pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
1774    assert(false);
1775 }
1776 
1777 void
d3d12_video_encoder_calculate_metadata_resolved_buffer_size(enum pipe_video_format codec,uint32_t maxSliceNumber,uint64_t & bufferSize)1778 d3d12_video_encoder_calculate_metadata_resolved_buffer_size(enum pipe_video_format codec, uint32_t maxSliceNumber, uint64_t &bufferSize)
1779 {
1780    bufferSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) +
1781                 (maxSliceNumber * sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA));
1782 
1783    switch (codec) {
1784 #if VIDEO_CODEC_H264ENC
1785       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
1786          break;
1787 #endif
1788 #if VIDEO_CODEC_H265ENC
1789       case PIPE_VIDEO_FORMAT_HEVC:
1790          break;
1791 #endif
1792 #if VIDEO_CODEC_AV1ENC
1793       case PIPE_VIDEO_FORMAT_AV1:
1794       {
1795          size_t extra_av1_size = d3d12_video_encoder_calculate_metadata_resolved_buffer_size_av1(maxSliceNumber);
1796          bufferSize += extra_av1_size;
1797       } break;
1798 #endif
1799       default:
1800       {
1801          unreachable("Unsupported pipe_video_format");
1802       } break;
1803    }
1804 }
1805 
1806 // Returns the number of slices that the output will contain for fixed slicing modes
1807 // and the maximum number of slices the output might contain for dynamic slicing modes (eg. max bytes per slice)
1808 uint32_t
d3d12_video_encoder_calculate_max_slices_count_in_output(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE slicesMode,const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES * slicesConfig,uint32_t MaxSubregionsNumberFromCaps,D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC sequenceTargetResolution,uint32_t SubregionBlockPixelsSize)1809 d3d12_video_encoder_calculate_max_slices_count_in_output(
1810    D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE                          slicesMode,
1811    const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES *slicesConfig,
1812    uint32_t                                                                 MaxSubregionsNumberFromCaps,
1813    D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC                              sequenceTargetResolution,
1814    uint32_t                                                                 SubregionBlockPixelsSize)
1815 {
1816    uint32_t pic_width_in_subregion_units =
1817       static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Width / static_cast<double>(SubregionBlockPixelsSize)));
1818    uint32_t pic_height_in_subregion_units =
1819       static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Height / static_cast<double>(SubregionBlockPixelsSize)));
1820    uint32_t total_picture_subregion_units = pic_width_in_subregion_units * pic_height_in_subregion_units;
1821    uint32_t maxSlices                     = 0u;
1822    switch (slicesMode) {
1823       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME:
1824       {
1825          maxSlices = 1u;
1826       } break;
1827       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_BYTES_PER_SUBREGION:
1828       {
1829          maxSlices = MaxSubregionsNumberFromCaps;
1830       } break;
1831       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_SQUARE_UNITS_PER_SUBREGION_ROW_UNALIGNED:
1832       {
1833          maxSlices = static_cast<uint32_t>(
1834             std::ceil(total_picture_subregion_units / static_cast<double>(slicesConfig->NumberOfCodingUnitsPerSlice)));
1835       } break;
1836       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION:
1837       {
1838          maxSlices = static_cast<uint32_t>(
1839             std::ceil(pic_height_in_subregion_units / static_cast<double>(slicesConfig->NumberOfRowsPerSlice)));
1840       } break;
1841       case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME:
1842       {
1843          maxSlices = slicesConfig->NumberOfSlicesPerFrame;
1844       } break;
1845       default:
1846       {
1847          unreachable("Unsupported D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE");
1848       } break;
1849    }
1850 
1851    return maxSlices;
1852 }
1853 
1854 /**
1855  * encode a bitstream
1856  */
1857 void
d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,struct pipe_video_buffer * source,struct pipe_resource * destination,void ** feedback)1858 d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
1859                                      struct pipe_video_buffer *source,
1860                                      struct pipe_resource *    destination,
1861                                      void **                   feedback)
1862 {
1863    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
1864    assert(pD3D12Enc);
1865    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_encode_bitstream started for fenceValue: %" PRIu64 "\n",
1866                  pD3D12Enc->m_fenceValue);
1867    assert(pD3D12Enc->m_spD3D12VideoDevice);
1868    assert(pD3D12Enc->m_spEncodeCommandQueue);
1869    assert(pD3D12Enc->m_pD3D12Screen);
1870 
1871    if (pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED) {
1872       debug_printf("WARNING: [d3d12_video_encoder] d3d12_video_encoder_encode_bitstream - Frame submission %" PRIu64 " failed. Encoder lost, please recreate pipe_video_codec object\n", pD3D12Enc->m_fenceValue);
1873       assert(false);
1874       return;
1875    }
1876 
1877    struct d3d12_video_buffer *pInputVideoBuffer = (struct d3d12_video_buffer *) source;
1878    assert(pInputVideoBuffer);
1879    ID3D12Resource *pInputVideoD3D12Res        = d3d12_resource_resource(pInputVideoBuffer->texture);
1880    uint32_t        inputVideoD3D12Subresource = 0u;
1881 
1882    struct d3d12_resource *pOutputBitstreamBuffer = (struct d3d12_resource *) destination;
1883 
1884    // Make them permanently resident for video use
1885    d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pOutputBitstreamBuffer);
1886    d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pInputVideoBuffer->texture);
1887 
1888    uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
1889 
1890    /* Warning if the previous finished async execution stored was read not by get_feedback()
1891       before overwriting. This should be handled correctly by the app by calling vaSyncBuffer/vaSyncSurface
1892       without having the async depth going beyond D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT frames without syncing */
1893    if(!pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bRead) {
1894       debug_printf("WARNING: [d3d12_video_encoder] d3d12_video_encoder_encode_bitstream - overwriting metadata slot %" PRIu64 " before calling get_feedback", current_metadata_slot);
1895       assert(false);
1896    }
1897    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bRead = false;
1898 
1899    ///
1900    /// Record Encode operation
1901    ///
1902 
1903    ///
1904    /// pInputVideoBuffer and pOutputBitstreamBuffer are passed externally
1905    /// and could be tracked by pipe_context and have pending ops. Flush any work on them and transition to
1906    /// D3D12_RESOURCE_STATE_COMMON before issuing work in Video command queue below. After the video work is done in the
1907    /// GPU, transition back to D3D12_RESOURCE_STATE_COMMON
1908    ///
1909    /// Note that unlike the D3D12TranslationLayer codebase, the state tracker here doesn't (yet) have any kind of
1910    /// multi-queue support, so it wouldn't implicitly synchronize when trying to transition between a graphics op and a
1911    /// video op.
1912    ///
1913 
1914    d3d12_transition_resource_state(
1915       d3d12_context(pD3D12Enc->base.context),
1916       pInputVideoBuffer->texture,
1917       D3D12_RESOURCE_STATE_COMMON,
1918       D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
1919    d3d12_transition_resource_state(d3d12_context(pD3D12Enc->base.context),
1920                                    pOutputBitstreamBuffer,
1921                                    D3D12_RESOURCE_STATE_COMMON,
1922                                    D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
1923    d3d12_apply_resource_states(d3d12_context(pD3D12Enc->base.context), false);
1924 
1925    d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context),
1926                             pInputVideoBuffer->texture,
1927                             false /*wantToWrite*/);
1928    d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context), pOutputBitstreamBuffer, true /*wantToWrite*/);
1929 
1930    ///
1931    /// Process pre-encode bitstream headers
1932    ///
1933 
1934    // Decide the D3D12 buffer EncodeFrame will write to based on pre-post encode headers generation policy
1935    ID3D12Resource *pOutputBufferD3D12Res = nullptr;
1936 
1937    d3d12_video_encoder_build_pre_encode_codec_headers(pD3D12Enc,
1938                                                       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded,
1939                                                       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize,
1940                                                       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes);
1941    assert(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size());
1942    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersBytePadding = 0;
1943 
1944    // Only upload headers now and leave prefix offset space gap in compressed bitstream if the codec builds headers before execution.
1945    if (!pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded)
1946    {
1947 
1948       // Headers are written before encode execution, have EncodeFrame write directly into the pipe destination buffer
1949       pOutputBufferD3D12Res = d3d12_resource_resource(pOutputBitstreamBuffer);
1950 
1951       // It can happen that codecs like H264/HEVC don't write pre-headers for all frames (ie. reuse previous PPS)
1952       if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize > 0)
1953       {
1954          // If driver needs offset alignment for bitstream resource, we will pad zeroes on the codec header to this end.
1955          if (
1956             (pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment > 1)
1957             && ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize % pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment) != 0)
1958          ) {
1959             uint64_t new_size = align64(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize, pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment);
1960             pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersBytePadding = new_size - pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize;
1961             pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize = new_size;
1962             pD3D12Enc->m_BitstreamHeadersBuffer.resize(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize, 0);
1963          }
1964 
1965          // Upload the CPU buffers with the bitstream headers to the compressed bitstream resource in the interval
1966          // [0..pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize)
1967          // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately
1968          // Will flush and sync this batch in d3d12_video_encoder_flush with the rest of the Video Encode Queue GPU work
1969 
1970          pD3D12Enc->base.context->buffer_subdata(
1971             pD3D12Enc->base.context,         // context
1972             &pOutputBitstreamBuffer->base.b, // dst buffer
1973             PIPE_MAP_WRITE,                  // usage PIPE_MAP_x
1974             0,                               // offset
1975             pD3D12Enc->m_BitstreamHeadersBuffer.size(),
1976             pD3D12Enc->m_BitstreamHeadersBuffer.data());
1977       }
1978    }
1979    else
1980    {
1981       assert(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize == 0);
1982       if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream == nullptr) {
1983          D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
1984          CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_DEFAULT_COMPBIT_STAGING_SIZE);
1985          HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
1986             &Properties,
1987             D3D12_HEAP_FLAG_NONE,
1988             &resolvedMetadataBufferDesc,
1989             D3D12_RESOURCE_STATE_COMMON,
1990             nullptr,
1991             IID_PPV_ARGS(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream.GetAddressOf()));
1992 
1993          if (FAILED(hr)) {
1994             debug_printf("CreateCommittedResource failed with HR %x\n", hr);
1995             pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
1996             pD3D12Enc->m_spEncodedFrameMetadata[pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
1997             assert(false);
1998             return;
1999          }
2000       }
2001 
2002       // Headers are written after execution, have EncodeFrame write into a staging buffer
2003       // and then get_feedback will pack the finalized bitstream and copy into comp_bit_destination
2004       pOutputBufferD3D12Res = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream.Get();
2005 
2006       // Save the pipe destination buffer the headers need to be written to in get_feedback
2007       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination = &pOutputBitstreamBuffer->base.b;
2008    }
2009 
2010    memset(&pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData,
2011             0,
2012             sizeof(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData));
2013    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData.value = pD3D12Enc->m_fenceValue;
2014    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData.cmdqueue_fence = pD3D12Enc->m_spFence.Get();
2015    *feedback = (void*) &pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData;
2016 
2017    std::vector<D3D12_RESOURCE_BARRIER> rgCurrentFrameStateTransitions = {
2018       CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res,
2019                                            D3D12_RESOURCE_STATE_COMMON,
2020                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ),
2021       CD3DX12_RESOURCE_BARRIER::Transition(pOutputBufferD3D12Res,
2022                                            D3D12_RESOURCE_STATE_COMMON,
2023                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
2024       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(),
2025                                            D3D12_RESOURCE_STATE_COMMON,
2026                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE)
2027    };
2028 
2029    pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(rgCurrentFrameStateTransitions.size(),
2030                                                      rgCurrentFrameStateTransitions.data());
2031 
2032    D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE reconPicOutputTextureDesc =
2033       pD3D12Enc->m_upDPBManager->get_current_frame_recon_pic_output_allocation();
2034    D3D12_VIDEO_ENCODE_REFERENCE_FRAMES referenceFramesDescriptor =
2035       pD3D12Enc->m_upDPBManager->get_current_reference_frames();
2036    D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAGS picCtrlFlags = D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_NONE;
2037 
2038    // Transition DPB reference pictures to read mode
2039    uint32_t                            maxReferences = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
2040    std::vector<D3D12_RESOURCE_BARRIER> rgReferenceTransitions(maxReferences);
2041    if ((referenceFramesDescriptor.NumTexture2Ds > 0) ||
2042        (pD3D12Enc->m_upDPBManager->is_current_frame_used_as_reference())) {
2043       rgReferenceTransitions.clear();
2044       rgReferenceTransitions.reserve(maxReferences);
2045 
2046       if (reconPicOutputTextureDesc.pReconstructedPicture != nullptr)
2047          picCtrlFlags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
2048 
2049       // Check if array of textures vs texture array
2050 
2051       if (referenceFramesDescriptor.pSubresources == nullptr) {
2052 
2053          // Array of resources mode for reference pictures
2054 
2055          // Transition all subresources of each reference frame independent resource allocation
2056          for (uint32_t referenceIdx = 0; referenceIdx < referenceFramesDescriptor.NumTexture2Ds; referenceIdx++) {
2057             rgReferenceTransitions.push_back(
2058                CD3DX12_RESOURCE_BARRIER::Transition(referenceFramesDescriptor.ppTexture2Ds[referenceIdx],
2059                                                     D3D12_RESOURCE_STATE_COMMON,
2060                                                     D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ));
2061          }
2062 
2063          // Transition all subresources the output recon pic independent resource allocation
2064          if (reconPicOutputTextureDesc.pReconstructedPicture != nullptr) {
2065             rgReferenceTransitions.push_back(
2066                CD3DX12_RESOURCE_BARRIER::Transition(reconPicOutputTextureDesc.pReconstructedPicture,
2067                                                     D3D12_RESOURCE_STATE_COMMON,
2068                                                     D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE));
2069          }
2070       } else if (referenceFramesDescriptor.NumTexture2Ds > 0) {
2071 
2072          // texture array mode for reference pictures
2073 
2074          // In Texture array mode, the dpb storage allocator uses the same texture array for all the input
2075          // reference pics in ppTexture2Ds and also for the pReconstructedPicture output allocations, just different
2076          // subresources.
2077 
2078          CD3DX12_RESOURCE_DESC referencesTexArrayDesc(GetDesc(referenceFramesDescriptor.ppTexture2Ds[0]));
2079 
2080 #if DEBUG
2081    // the reconpic output should be all the same texarray allocation
2082    if((reconPicOutputTextureDesc.pReconstructedPicture) && (referenceFramesDescriptor.NumTexture2Ds > 0))
2083       assert(referenceFramesDescriptor.ppTexture2Ds[0] == reconPicOutputTextureDesc.pReconstructedPicture);
2084 
2085    for (uint32_t refIndex = 0; refIndex < referenceFramesDescriptor.NumTexture2Ds; refIndex++) {
2086             // all reference frames inputs should be all the same texarray allocation
2087             assert(referenceFramesDescriptor.ppTexture2Ds[0] ==
2088                    referenceFramesDescriptor.ppTexture2Ds[refIndex]);
2089    }
2090 #endif
2091 
2092          for (uint32_t referenceSubresource = 0; referenceSubresource < referencesTexArrayDesc.DepthOrArraySize;
2093               referenceSubresource++) {
2094 
2095             uint32_t MipLevel, PlaneSlice, ArraySlice;
2096             D3D12DecomposeSubresource(referenceSubresource,
2097                                       referencesTexArrayDesc.MipLevels,
2098                                       referencesTexArrayDesc.ArraySize(),
2099                                       MipLevel,
2100                                       ArraySlice,
2101                                       PlaneSlice);
2102 
2103             for (PlaneSlice = 0; PlaneSlice < pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.PlaneCount;
2104                  PlaneSlice++) {
2105 
2106                uint32_t planeOutputSubresource =
2107                   referencesTexArrayDesc.CalcSubresource(MipLevel, ArraySlice, PlaneSlice);
2108 
2109                rgReferenceTransitions.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
2110                   // Always same allocation in texarray mode
2111                   referenceFramesDescriptor.ppTexture2Ds[0],
2112                   D3D12_RESOURCE_STATE_COMMON,
2113                   // If this is the subresource for the reconpic output allocation, transition to ENCODE_WRITE
2114                   // Otherwise, it's a subresource for an input reference picture, transition to ENCODE_READ
2115                   (referenceSubresource == reconPicOutputTextureDesc.ReconstructedPictureSubresource) ?
2116                      D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE :
2117                      D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
2118                   planeOutputSubresource));
2119             }
2120          }
2121       }
2122 
2123       if (rgReferenceTransitions.size() > 0) {
2124          pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(static_cast<uint32_t>(rgReferenceTransitions.size()),
2125                                                            rgReferenceTransitions.data());
2126       }
2127    }
2128 
2129    // Update current frame pic params state after reconfiguring above.
2130    D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
2131       d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
2132 
2133    if (!pD3D12Enc->m_upDPBManager->get_current_frame_picture_control_data(currentPicParams)) {
2134       debug_printf("[d3d12_video_encoder_encode_bitstream] get_current_frame_picture_control_data failed!\n");
2135       pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
2136       pD3D12Enc->m_spEncodedFrameMetadata[pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT].encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
2137       assert(false);
2138       return;
2139    }
2140 
2141    // Stores D3D12_VIDEO_ENCODER_AV1_REFERENCE_PICTURE_DESCRIPTOR in the associated metadata
2142    // for header generation after execution (if applicable)
2143    if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded) {
2144       d3d12_video_encoder_store_current_picture_references(pD3D12Enc, current_metadata_slot);
2145    }
2146 
2147    const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS inputStreamArguments = {
2148       // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_DESC
2149       { // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS
2150         pD3D12Enc->m_currentEncodeConfig.m_seqFlags,
2151         // D3D12_VIDEO_ENCODER_INTRA_REFRESH
2152         pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh,
2153         d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc),
2154         // D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC
2155         pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
2156         pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
2157         d3d12_video_encoder_get_current_slice_param_settings(pD3D12Enc),
2158         d3d12_video_encoder_get_current_gop_desc(pD3D12Enc) },
2159       // D3D12_VIDEO_ENCODER_PICTURE_CONTROL_DESC
2160       { // uint32_t IntraRefreshFrameIndex;
2161         pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex,
2162         // D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAGS Flags;
2163         picCtrlFlags,
2164         // D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA PictureControlCodecData;
2165         currentPicParams,
2166         // D3D12_VIDEO_ENCODE_REFERENCE_FRAMES ReferenceFrames;
2167         referenceFramesDescriptor },
2168       pInputVideoD3D12Res,
2169       inputVideoD3D12Subresource,
2170       static_cast<UINT>(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize)
2171       // budgeting. - User can also calculate headers fixed size beforehand (eg. no VUI,
2172       // etc) and build them with final values after EncodeFrame is executed
2173    };
2174 
2175    const D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS outputStreamArguments = {
2176       // D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM
2177       {
2178          pOutputBufferD3D12Res,
2179          pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize,
2180       },
2181       // D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
2182       reconPicOutputTextureDesc,
2183       // D3D12_VIDEO_ENCODER_ENCODE_OPERATION_METADATA_BUFFER
2184       { pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 }
2185    };
2186 
2187    // Record EncodeFrame
2188    pD3D12Enc->m_spEncodeCommandList->EncodeFrame(pD3D12Enc->m_spVideoEncoder.Get(),
2189                                                  pD3D12Enc->m_spVideoEncoderHeap.Get(),
2190                                                  &inputStreamArguments,
2191                                                  &outputStreamArguments);
2192 
2193    D3D12_RESOURCE_BARRIER rgResolveMetadataStateTransitions[] = {
2194       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
2195                                            D3D12_RESOURCE_STATE_COMMON,
2196                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
2197       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(),
2198                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
2199                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ),
2200       CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res,
2201                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
2202                                            D3D12_RESOURCE_STATE_COMMON),
2203       CD3DX12_RESOURCE_BARRIER::Transition(pOutputBufferD3D12Res,
2204                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
2205                                            D3D12_RESOURCE_STATE_COMMON)
2206    };
2207 
2208    pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(_countof(rgResolveMetadataStateTransitions),
2209                                                      rgResolveMetadataStateTransitions);
2210 
2211    const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS inputMetadataCmd = {
2212       pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
2213       d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
2214       pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
2215       // D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC
2216       pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
2217       { pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 }
2218    };
2219 
2220    const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS outputMetadataCmd = {
2221       /*If offset were to change, has to be aligned to pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.EncoderMetadataBufferAccessAlignment*/
2222       { pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(), 0 }
2223    };
2224    pD3D12Enc->m_spEncodeCommandList->ResolveEncoderOutputMetadata(&inputMetadataCmd, &outputMetadataCmd);
2225 
2226    debug_printf("[d3d12_video_encoder_encode_bitstream] EncodeFrame slot %" PRIu64 " encoder %p encoderheap %p input tex %p output bitstream %p raw metadata buf %p resolved metadata buf %p Command allocator %p\n",
2227                d3d12_video_encoder_pool_current_index(pD3D12Enc),
2228                pD3D12Enc->m_spVideoEncoder.Get(),
2229                pD3D12Enc->m_spVideoEncoderHeap.Get(),
2230                inputStreamArguments.pInputFrame,
2231                outputStreamArguments.Bitstream.pBuffer,
2232                inputMetadataCmd.HWLayoutMetadata.pBuffer,
2233                outputMetadataCmd.ResolvedLayoutMetadata.pBuffer,
2234                pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spCommandAllocator.Get());
2235 
2236    // Transition DPB reference pictures back to COMMON
2237    if ((referenceFramesDescriptor.NumTexture2Ds > 0) ||
2238        (pD3D12Enc->m_upDPBManager->is_current_frame_used_as_reference())) {
2239       for (auto &BarrierDesc : rgReferenceTransitions) {
2240          std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter);
2241       }
2242 
2243       if (rgReferenceTransitions.size() > 0) {
2244          pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(static_cast<uint32_t>(rgReferenceTransitions.size()),
2245                                                            rgReferenceTransitions.data());
2246       }
2247    }
2248 
2249    D3D12_RESOURCE_BARRIER rgRevertResolveMetadataStateTransitions[] = {
2250       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
2251                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
2252                                            D3D12_RESOURCE_STATE_COMMON),
2253       CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(),
2254                                            D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
2255                                            D3D12_RESOURCE_STATE_COMMON),
2256    };
2257 
2258    pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(_countof(rgRevertResolveMetadataStateTransitions),
2259                                                      rgRevertResolveMetadataStateTransitions);
2260 
2261    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_encode_bitstream finalized for fenceValue: %" PRIu64 "\n",
2262                  pD3D12Enc->m_fenceValue);
2263 }
2264 
2265 void
d3d12_video_encoder_get_feedback(struct pipe_video_codec * codec,void * feedback,unsigned * output_buffer_size,struct pipe_enc_feedback_metadata * pMetadata)2266 d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec,
2267                                   void *feedback,
2268                                   unsigned *output_buffer_size,
2269                                   struct pipe_enc_feedback_metadata* pMetadata)
2270 {
2271    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
2272    assert(pD3D12Enc);
2273 
2274    struct d3d12_fence *feedback_fence = (struct d3d12_fence *) feedback;
2275    uint64_t requested_metadata_fence = feedback_fence->value;
2276 
2277    struct pipe_enc_feedback_metadata opt_metadata;
2278    memset(&opt_metadata, 0, sizeof(opt_metadata));
2279 
2280    HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
2281    if (hr != S_OK) {
2282       opt_metadata.encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
2283       debug_printf("Error: d3d12_video_encoder_get_feedback for Encode GPU command for fence %" PRIu64 " failed with GetDeviceRemovedReason: %x\n",
2284                      requested_metadata_fence,
2285                      hr);
2286       assert(false);
2287       if(pMetadata)
2288          *pMetadata = opt_metadata;
2289       return;
2290    }
2291 
2292    uint64_t current_metadata_slot = (requested_metadata_fence % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
2293    opt_metadata.encode_result = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].encode_result;
2294    if (opt_metadata.encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED) {
2295       debug_printf("Error: d3d12_video_encoder_get_feedback for Encode GPU command for fence %" PRIu64 " failed on submission with encode_result: %x\n",
2296                      requested_metadata_fence,
2297                      opt_metadata.encode_result);
2298       assert(false);
2299       if(pMetadata)
2300          *pMetadata = opt_metadata;
2301       return;
2302    }
2303 
2304    d3d12_video_encoder_sync_completion(codec, requested_metadata_fence, OS_TIMEOUT_INFINITE);
2305 
2306    opt_metadata.encode_result = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].encode_result;
2307    if (opt_metadata.encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED) {
2308       debug_printf("Error: d3d12_video_encoder_get_feedback for Encode GPU command for fence %" PRIu64 " failed on GPU fence wait with encode_result: %x\n",
2309                      requested_metadata_fence,
2310                      opt_metadata.encode_result);
2311       assert(false);
2312       if(pMetadata)
2313          *pMetadata = opt_metadata;
2314       return;
2315    }
2316 
2317    debug_printf("d3d12_video_encoder_get_feedback with feedback: %" PRIu64 ", resources slot %" PRIu64 " metadata resolved ID3D12Resource buffer %p metadata required size %" PRIu64 "\n",
2318       requested_metadata_fence,
2319       (requested_metadata_fence % D3D12_VIDEO_ENC_ASYNC_DEPTH),
2320       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
2321       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize);
2322 
2323    if((pD3D12Enc->m_fenceValue - requested_metadata_fence) > D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT)
2324    {
2325       debug_printf("[d3d12_video_encoder_get_feedback] Requested metadata for fence %" PRIu64 " at current fence %" PRIu64
2326          " is too far back in time for the ring buffer of size %" PRIu64 " we keep track off - "
2327          " Please increase the D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT environment variable and try again.\n",
2328          requested_metadata_fence,
2329          pD3D12Enc->m_fenceValue,
2330          D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
2331       opt_metadata.encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
2332       assert(false);
2333       if(pMetadata)
2334          *pMetadata = opt_metadata;
2335       return;
2336    }
2337 
2338    // Extract encode metadata
2339    D3D12_VIDEO_ENCODER_OUTPUT_METADATA                       encoderMetadata;
2340    std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> pSubregionsMetadata;
2341    d3d12_video_encoder_extract_encode_metadata(
2342       pD3D12Enc,
2343       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
2344       pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize,
2345       encoderMetadata,
2346       pSubregionsMetadata);
2347 
2348    // Validate encoder output metadata
2349    if ((encoderMetadata.EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) || (encoderMetadata.EncodedBitstreamWrittenBytesCount == 0)) {
2350       opt_metadata.encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
2351       debug_printf("[d3d12_video_encoder] Encode GPU command for fence %" PRIu64 " failed - EncodeErrorFlags: %" PRIu64 "\n",
2352                      requested_metadata_fence,
2353                      encoderMetadata.EncodeErrorFlags);
2354       assert(false);
2355       if(pMetadata)
2356          *pMetadata = opt_metadata;
2357       return;
2358    }
2359 
2360    uint64_t unpadded_frame_size = 0;
2361    if(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded)
2362    {
2363       *output_buffer_size = d3d12_video_encoder_build_post_encode_codec_bitstream(
2364          pD3D12Enc,
2365          requested_metadata_fence,
2366          pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot]
2367       );
2368       for (uint32_t i = 0; i < pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes.size(); i++)
2369       {
2370          opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].size = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes[i];
2371          opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].offset = unpadded_frame_size;
2372          unpadded_frame_size += opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].size;
2373          opt_metadata.codec_unit_metadata_count++;
2374       }
2375    }
2376    else
2377    {
2378       *output_buffer_size = 0;
2379       for (uint32_t i = 0; i < pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes.size() ; i++) {
2380          unpadded_frame_size += pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes[i];
2381          opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].size = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes[i];
2382          opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].offset = *output_buffer_size;
2383          *output_buffer_size += pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes[i];
2384          opt_metadata.codec_unit_metadata_count++;
2385       }
2386 
2387       // Add padding between pre encode headers (e.g EncodeFrame driver offset alignment) and the first slice
2388       *output_buffer_size += pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersBytePadding;
2389 
2390       for (uint32_t i = 0; i < pSubregionsMetadata.size(); i++)
2391       {
2392          uint64_t unpadded_slice_size = pSubregionsMetadata[i].bSize - pSubregionsMetadata[i].bStartOffset;
2393          unpadded_frame_size += unpadded_slice_size;
2394          opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].size = unpadded_slice_size;
2395          opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].offset = *output_buffer_size;
2396          *output_buffer_size += pSubregionsMetadata[i].bSize;
2397          if ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].expected_max_slice_size > 0) &&
2398              (unpadded_slice_size > pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].expected_max_slice_size))
2399             opt_metadata.codec_unit_metadata[opt_metadata.codec_unit_metadata_count].flags |= PIPE_VIDEO_CODEC_UNIT_LOCATION_FLAG_MAX_SLICE_SIZE_OVERFLOW;
2400          opt_metadata.codec_unit_metadata_count++;
2401       }
2402    }
2403 
2404    if ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].expected_max_frame_size > 0) &&
2405       (unpadded_frame_size > pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].expected_max_frame_size))
2406       opt_metadata.encode_result |= PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_MAX_FRAME_SIZE_OVERFLOW;
2407 
2408    opt_metadata.average_frame_qp = static_cast<unsigned int>(encoderMetadata.EncodeStats.AverageQP);
2409 
2410    opt_metadata.present_metadata = (PIPE_VIDEO_FEEDBACK_METADATA_TYPE_BITSTREAM_SIZE |
2411                                     PIPE_VIDEO_FEEDBACK_METADATA_TYPE_ENCODE_RESULT |
2412                                     PIPE_VIDEO_FEEDBACK_METADATA_TYPE_CODEC_UNIT_LOCATION |
2413                                     PIPE_VIDEO_FEEDBACK_METADATA_TYPE_MAX_FRAME_SIZE_OVERFLOW |
2414                                     PIPE_VIDEO_FEEDBACK_METADATA_TYPE_MAX_SLICE_SIZE_OVERFLOW |
2415                                     PIPE_VIDEO_FEEDBACK_METADATA_TYPE_AVERAGE_FRAME_QP);
2416 
2417    if (pMetadata)
2418       *pMetadata = opt_metadata;
2419 
2420    debug_printf("[d3d12_video_encoder_get_feedback] Requested metadata for encoded frame at fence %" PRIu64 " is:\n"
2421                 "\tfeedback was requested at current fence: %" PRIu64 "\n"
2422                 "\toutput_buffer_size (including padding): %d\n"
2423                 "\tunpadded_frame_size: %" PRIu64 "\n"
2424                 "\ttotal padding: %" PRIu64 "\n"
2425                 "\tcodec_unit_metadata_count: %d\n",
2426                 pD3D12Enc->m_fenceValue,
2427                 requested_metadata_fence,
2428                 *output_buffer_size,
2429                 unpadded_frame_size,
2430                 static_cast<uint64_t>(static_cast<uint64_t>(*output_buffer_size) - unpadded_frame_size),
2431                 opt_metadata.codec_unit_metadata_count);
2432 
2433    for (uint32_t i = 0; i < opt_metadata.codec_unit_metadata_count; i++) {
2434       debug_printf("\tcodec_unit_metadata[%d].offset: %" PRIu64" - codec_unit_metadata[%d].size: %" PRIu64" \n",
2435          i,
2436          opt_metadata.codec_unit_metadata[i].offset,
2437          i,
2438          opt_metadata.codec_unit_metadata[i].size);
2439    }
2440 
2441    pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bRead = true;
2442 }
2443 
2444 unsigned
d3d12_video_encoder_build_post_encode_codec_bitstream(struct d3d12_video_encoder * pD3D12Enc,uint64_t associated_fence_value,EncodedBitstreamResolvedMetadata & associatedMetadata)2445 d3d12_video_encoder_build_post_encode_codec_bitstream(struct d3d12_video_encoder * pD3D12Enc,
2446                                              uint64_t associated_fence_value,
2447                                              EncodedBitstreamResolvedMetadata& associatedMetadata)
2448 {
2449    enum pipe_video_format codec_format = u_reduce_video_profile(pD3D12Enc->base.profile);
2450    switch (codec_format) {
2451 #if VIDEO_CODEC_H264ENC
2452       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
2453       {
2454          return 0;
2455       } break;
2456 #endif
2457 #if VIDEO_CODEC_H265ENC
2458       case PIPE_VIDEO_FORMAT_HEVC:
2459       {
2460          return 0;
2461       } break; // Do not need post encode values in headers
2462 #endif
2463 #if VIDEO_CODEC_AV1ENC
2464       case PIPE_VIDEO_FORMAT_AV1:
2465       {
2466          return d3d12_video_encoder_build_post_encode_codec_bitstream_av1(
2467             // Current encoder
2468             pD3D12Enc,
2469             // associated fence value
2470             associated_fence_value,
2471             // Metadata desc
2472             associatedMetadata
2473          );
2474       } break;
2475 #endif
2476       default:
2477          unreachable("Unsupported pipe_video_format");
2478    }
2479 }
2480 
2481 void
d3d12_video_encoder_extract_encode_metadata(struct d3d12_video_encoder * pD3D12Enc,ID3D12Resource * pResolvedMetadataBuffer,uint64_t resourceMetadataSize,D3D12_VIDEO_ENCODER_OUTPUT_METADATA & parsedMetadata,std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> & pSubregionsMetadata)2482 d3d12_video_encoder_extract_encode_metadata(
2483    struct d3d12_video_encoder *                               pD3D12Enc,
2484    ID3D12Resource *                                           pResolvedMetadataBuffer,   // input
2485    uint64_t                                                   resourceMetadataSize,      // input
2486    D3D12_VIDEO_ENCODER_OUTPUT_METADATA &                      parsedMetadata,            // output
2487    std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> &pSubregionsMetadata        // output
2488 )
2489 {
2490    struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pD3D12Enc->m_pD3D12Screen;
2491    assert(pD3D12Screen);
2492    pipe_resource *pPipeResolvedMetadataBuffer =
2493       d3d12_resource_from_resource(&pD3D12Screen->base, pResolvedMetadataBuffer);
2494    assert(pPipeResolvedMetadataBuffer);
2495    assert(resourceMetadataSize < INT_MAX);
2496    struct pipe_box box = {
2497       0,                                        // x
2498       0,                                        // y
2499       0,                                        // z
2500       static_cast<int>(resourceMetadataSize),   // width
2501       1,                                        // height
2502       1                                         // depth
2503    };
2504    struct pipe_transfer *mapTransfer;
2505    unsigned mapUsage = PIPE_MAP_READ;
2506    void *                pMetadataBufferSrc = pD3D12Enc->base.context->buffer_map(pD3D12Enc->base.context,
2507                                                                   pPipeResolvedMetadataBuffer,
2508                                                                   0,
2509                                                                   mapUsage,
2510                                                                   &box,
2511                                                                   &mapTransfer);
2512 
2513    assert(mapUsage & PIPE_MAP_READ);
2514    assert(pPipeResolvedMetadataBuffer->usage == PIPE_USAGE_DEFAULT);
2515    // Note: As we're calling buffer_map with PIPE_MAP_READ on a pPipeResolvedMetadataBuffer which has pipe_usage_default
2516    // buffer_map itself will do all the synchronization and waits so once the function returns control here
2517    // the contents of mapTransfer are ready to be accessed.
2518 
2519    // Clear output
2520    memset(&parsedMetadata, 0, sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA));
2521 
2522    // Calculate sizes
2523    uint64_t encoderMetadataSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA);
2524 
2525    // Copy buffer to the appropriate D3D12_VIDEO_ENCODER_OUTPUT_METADATA memory layout
2526    parsedMetadata = *reinterpret_cast<D3D12_VIDEO_ENCODER_OUTPUT_METADATA *>(pMetadataBufferSrc);
2527 
2528    // As specified in D3D12 Encode spec, the array base for metadata for the slices
2529    // (D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA[]) is placed in memory immediately after the
2530    // D3D12_VIDEO_ENCODER_OUTPUT_METADATA structure
2531    D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata =
2532       reinterpret_cast<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *>(reinterpret_cast<uint8_t *>(pMetadataBufferSrc) +
2533                                                                        encoderMetadataSize);
2534 
2535    // Copy fields into D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA
2536    assert(parsedMetadata.WrittenSubregionsCount < SIZE_MAX);
2537    pSubregionsMetadata.resize(static_cast<size_t>(parsedMetadata.WrittenSubregionsCount));
2538    for (uint32_t sliceIdx = 0; sliceIdx < parsedMetadata.WrittenSubregionsCount; sliceIdx++) {
2539       pSubregionsMetadata[sliceIdx].bHeaderSize  = pFrameSubregionMetadata[sliceIdx].bHeaderSize;
2540       pSubregionsMetadata[sliceIdx].bSize        = pFrameSubregionMetadata[sliceIdx].bSize;
2541       pSubregionsMetadata[sliceIdx].bStartOffset = pFrameSubregionMetadata[sliceIdx].bStartOffset;
2542    }
2543 
2544    // Unmap the buffer tmp storage
2545    pipe_buffer_unmap(pD3D12Enc->base.context, mapTransfer);
2546    pipe_resource_reference(&pPipeResolvedMetadataBuffer, NULL);
2547 }
2548 
2549 /**
2550  * end encoding of the current frame
2551  */
2552 void
d3d12_video_encoder_end_frame(struct pipe_video_codec * codec,struct pipe_video_buffer * target,struct pipe_picture_desc * picture)2553 d3d12_video_encoder_end_frame(struct pipe_video_codec * codec,
2554                               struct pipe_video_buffer *target,
2555                               struct pipe_picture_desc *picture)
2556 {
2557    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
2558    assert(pD3D12Enc);
2559    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame started for fenceValue: %" PRIu64 "\n",
2560                  pD3D12Enc->m_fenceValue);
2561 
2562    if (pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].encode_result != PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_OK) {
2563       debug_printf("WARNING: [d3d12_video_encoder] d3d12_video_encoder_end_frame - Frame submission %" PRIu64 " failed. Encoder lost, please recreate pipe_video_codec object\n", pD3D12Enc->m_fenceValue);
2564       assert(false);
2565       return;
2566    }
2567 
2568    // Signal finish of current frame encoding to the picture management tracker
2569    pD3D12Enc->m_upDPBManager->end_frame();
2570 
2571    // Save extra references of Encoder, EncoderHeap and DPB allocations in case
2572    // there's a reconfiguration that trigers the construction of new objects
2573    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spEncoder = pD3D12Enc->m_spVideoEncoder;
2574    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spEncoderHeap = pD3D12Enc->m_spVideoEncoderHeap;
2575    pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_References = pD3D12Enc->m_upDPBStorageManager;
2576 
2577    debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame finalized for fenceValue: %" PRIu64 "\n",
2578                  pD3D12Enc->m_fenceValue);
2579 
2580    pD3D12Enc->m_bPendingWorkNotFlushed = true;
2581 }
2582 
2583 void
d3d12_video_encoder_store_current_picture_references(d3d12_video_encoder * pD3D12Enc,uint64_t current_metadata_slot)2584 d3d12_video_encoder_store_current_picture_references(d3d12_video_encoder *pD3D12Enc,
2585                                                      uint64_t current_metadata_slot)
2586 {
2587    enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
2588    switch (codec) {
2589 #if VIDEO_CODEC_H264ENC
2590       case PIPE_VIDEO_FORMAT_MPEG4_AVC:
2591       {
2592          // Not needed (not post encode headers)
2593       } break;
2594 #endif
2595 #if VIDEO_CODEC_H265ENC
2596       case PIPE_VIDEO_FORMAT_HEVC:
2597       {
2598          // Not needed (not post encode headers)
2599       } break;
2600 #endif
2601 #if VIDEO_CODEC_AV1ENC
2602       case PIPE_VIDEO_FORMAT_AV1:
2603       {
2604          d3d12_video_encoder_store_current_picture_references_av1(pD3D12Enc, current_metadata_slot);
2605       } break;
2606 #endif
2607       default:
2608       {
2609          unreachable("Unsupported pipe_video_format");
2610       } break;
2611    }
2612 }
2613 
2614 struct pipe_fence_handle*
d3d12_video_encoder_get_feedback_fence(struct pipe_video_codec * codec,void * feedback)2615 d3d12_video_encoder_get_feedback_fence(struct pipe_video_codec *codec, void *feedback)
2616 {
2617    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
2618    struct d3d12_fence *feedback_fence = (struct d3d12_fence *) feedback;
2619    uint64_t requested_metadata_fence = feedback_fence->value;
2620    uint64_t current_metadata_slot = (requested_metadata_fence % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
2621    if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED) {
2622       debug_printf("Error: d3d12_video_encoder_get_feedback_fence failed for Encode GPU command for fence %" PRIu64 " failed on submission with encode_result: %x\n",
2623                      requested_metadata_fence,
2624                      pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].encode_result);
2625       assert(false);
2626       return NULL;
2627    }
2628 
2629    return (pipe_fence_handle*) feedback;
2630 }
2631 
d3d12_video_encoder_get_encode_headers(struct pipe_video_codec * codec,struct pipe_picture_desc * picture,void * bitstream_buf,unsigned * bitstream_buf_size)2632 int d3d12_video_encoder_get_encode_headers([[maybe_unused]] struct pipe_video_codec *codec,
2633                                            [[maybe_unused]] struct pipe_picture_desc *picture,
2634                                            [[maybe_unused]] void* bitstream_buf,
2635                                            [[maybe_unused]] unsigned *bitstream_buf_size)
2636 {
2637 #if (VIDEO_CODEC_H264ENC || VIDEO_CODEC_H265ENC)
2638    struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
2639    D3D12_VIDEO_SAMPLE srcTextureDesc = {};
2640    srcTextureDesc.Width = pD3D12Enc->base.width;
2641    srcTextureDesc.Height = pD3D12Enc->base.height;
2642    srcTextureDesc.Format.Format = d3d12_get_format(picture->input_format);
2643    if(!d3d12_video_encoder_update_current_encoder_config_state(pD3D12Enc, srcTextureDesc, picture))
2644       return EINVAL;
2645 
2646    if (!pD3D12Enc->m_upBitstreamBuilder) {
2647 #if VIDEO_CODEC_H264ENC
2648       if (u_reduce_video_profile(pD3D12Enc->base.profile) == PIPE_VIDEO_FORMAT_MPEG4_AVC)
2649          pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_h264>();
2650 #endif
2651 #if VIDEO_CODEC_H265ENC
2652       if (u_reduce_video_profile(pD3D12Enc->base.profile) == PIPE_VIDEO_FORMAT_HEVC)
2653          pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_hevc>();
2654 #endif
2655    }
2656    bool postEncodeHeadersNeeded = false;
2657    uint64_t preEncodeGeneratedHeadersByteSize = 0;
2658    std::vector<uint64_t> pWrittenCodecUnitsSizes;
2659    pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_sequence_info;
2660    d3d12_video_encoder_build_pre_encode_codec_headers(pD3D12Enc,
2661                                                       postEncodeHeadersNeeded,
2662                                                       preEncodeGeneratedHeadersByteSize,
2663                                                       pWrittenCodecUnitsSizes);
2664    if (preEncodeGeneratedHeadersByteSize > *bitstream_buf_size)
2665       return ENOMEM;
2666 
2667    *bitstream_buf_size = pD3D12Enc->m_BitstreamHeadersBuffer.size();
2668    memcpy(bitstream_buf,
2669           pD3D12Enc->m_BitstreamHeadersBuffer.data(),
2670           *bitstream_buf_size);
2671    return 0;
2672 #else
2673    return ENOTSUP;
2674 #endif
2675 }
2676 
2677 template void
2678 d3d12_video_encoder_update_picparams_region_of_interest_qpmap(struct d3d12_video_encoder *pD3D12Enc,
2679                                                               const struct pipe_enc_roi *roi_config,
2680                                                               int32_t min_delta_qp,
2681                                                               int32_t max_delta_qp,
2682                                                               std::vector<int16_t>& pQPMap);
2683 
2684 template void
2685 d3d12_video_encoder_update_picparams_region_of_interest_qpmap(struct d3d12_video_encoder *pD3D12Enc,
2686                                                               const struct pipe_enc_roi *roi_config,
2687                                                               int32_t min_delta_qp,
2688                                                               int32_t max_delta_qp,
2689                                                               std::vector<int8_t>& pQPMap);
2690 
2691 template<typename T>
2692 void
d3d12_video_encoder_update_picparams_region_of_interest_qpmap(struct d3d12_video_encoder * pD3D12Enc,const struct pipe_enc_roi * roi_config,int32_t min_delta_qp,int32_t max_delta_qp,std::vector<T> & pQPMap)2693 d3d12_video_encoder_update_picparams_region_of_interest_qpmap(struct d3d12_video_encoder *pD3D12Enc,
2694                                                               const struct pipe_enc_roi *roi_config,
2695                                                               int32_t min_delta_qp,
2696                                                               int32_t max_delta_qp,
2697                                                               std::vector<T>& pQPMap)
2698 {
2699    static_assert(ARRAY_SIZE(roi_config->region) == PIPE_ENC_ROI_REGION_NUM_MAX);
2700    assert(roi_config->num > 0);
2701    assert(roi_config->num <= PIPE_ENC_ROI_REGION_NUM_MAX);
2702    assert(min_delta_qp < 0);
2703    assert(max_delta_qp > 0);
2704 
2705    // Set all the QP blocks with zero QP Delta, then only fill in the regions that have a non-zero delta value
2706    uint32_t QPMapRegionPixelsSize = pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.QPMapRegionPixelsSize;
2707    uint64_t pic_width_in_qpmap_block_units = static_cast<uint64_t>(std::ceil(pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width /
2708       static_cast<double>(QPMapRegionPixelsSize)));
2709    uint64_t pic_height_in_qpmap_block_units = static_cast<uint64_t>(std::ceil(pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height /
2710       static_cast<double>(QPMapRegionPixelsSize)));
2711    uint64_t total_picture_qpmap_block_units = pic_width_in_qpmap_block_units * pic_height_in_qpmap_block_units;
2712    pQPMap.resize(total_picture_qpmap_block_units, 0u);
2713 
2714    // Loop in reverse for priority of overlapping regions as per p_video_state roi parameter docs
2715    for (int32_t i = (roi_config->num - 1); i >= 0 ; i--)
2716    {
2717       auto& cur_region = roi_config->region[i];
2718       if (cur_region.valid)
2719       {
2720          uint32_t bucket_start_block_x = cur_region.x / QPMapRegionPixelsSize;
2721          uint32_t bucket_start_block_y = cur_region.y / QPMapRegionPixelsSize;
2722          uint32_t bucket_end_block_x = std::ceil((cur_region.x + cur_region.width) / static_cast<double>(QPMapRegionPixelsSize)) - 1;
2723          uint32_t bucket_end_block_y = std::ceil((cur_region.y + cur_region.height) / static_cast<double>(QPMapRegionPixelsSize)) - 1;
2724          for (uint32_t i = bucket_start_block_x; i <= bucket_end_block_x; i++)
2725             for (uint32_t j = bucket_start_block_y; j <= bucket_end_block_y; j++)
2726                pQPMap[(j * pic_width_in_qpmap_block_units) + i] = CLAMP(cur_region.qp_value, min_delta_qp, max_delta_qp);
2727       }
2728    }
2729 }
2730 
2731