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