blob: ce88a02631338fe7774e366f580999949d2022a0 [file] [log] [blame]
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "d3d12_common.h"
#include "d3d12_util.h"
#include "d3d12_context.h"
#include "d3d12_format.h"
#include "d3d12_resource.h"
#include "d3d12_screen.h"
#include "d3d12_surface.h"
#include "d3d12_video_enc.h"
#include "d3d12_video_enc_h264.h"
#include "d3d12_video_enc_hevc.h"
#include "d3d12_video_buffer.h"
#include "d3d12_video_texture_array_dpb_manager.h"
#include "d3d12_video_array_of_textures_dpb_manager.h"
#include "d3d12_video_encoder_references_manager_h264.h"
#include "d3d12_video_encoder_references_manager_hevc.h"
#include "d3d12_residency.h"
#include "vl/vl_video_buffer.h"
#include "util/format/u_format.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
#include "util/u_video.h"
#include <cmath>
uint64_t
d3d12_video_encoder_pool_current_index(struct d3d12_video_encoder *pD3D12Enc)
{
return pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_ASYNC_DEPTH;
}
void
d3d12_video_encoder_flush(struct pipe_video_codec *codec)
{
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
assert(pD3D12Enc);
assert(pD3D12Enc->m_spD3D12VideoDevice);
assert(pD3D12Enc->m_spEncodeCommandQueue);
// Flush buffer_subdata batch and Wait the m_spEncodeCommandQueue for GPU upload completion
// before recording EncodeFrame below.
struct pipe_fence_handle *completion_fence = NULL;
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");
pD3D12Enc->base.context->flush(pD3D12Enc->base.context, &completion_fence, PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH);
assert(completion_fence);
struct d3d12_fence *casted_completion_fence = d3d12_fence(completion_fence);
pD3D12Enc->m_spEncodeCommandQueue->Wait(casted_completion_fence->cmdqueue_fence, casted_completion_fence->value);
pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, &completion_fence, NULL);
if (!pD3D12Enc->m_bPendingWorkNotFlushed) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush started. Nothing to flush, all up to date.\n");
} else {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush started. Will flush video queue work async"
" on fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
if (hr != S_OK) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush"
" - D3D12Device was removed BEFORE commandlist "
"execution with HR %x.\n",
hr);
goto flush_fail;
}
if (pD3D12Enc->m_transitionsBeforeCloseCmdList.size() > 0) {
pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(pD3D12Enc->m_transitionsBeforeCloseCmdList.size(),
pD3D12Enc->m_transitionsBeforeCloseCmdList.data());
pD3D12Enc->m_transitionsBeforeCloseCmdList.clear();
}
hr = pD3D12Enc->m_spEncodeCommandList->Close();
if (FAILED(hr)) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - Can't close command list with HR %x\n", hr);
goto flush_fail;
}
ID3D12CommandList *ppCommandLists[1] = { pD3D12Enc->m_spEncodeCommandList.Get() };
pD3D12Enc->m_spEncodeCommandQueue->ExecuteCommandLists(1, ppCommandLists);
pD3D12Enc->m_spEncodeCommandQueue->Signal(pD3D12Enc->m_spFence.Get(), pD3D12Enc->m_fenceValue);
// Validate device was not removed
hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
if (hr != S_OK) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush"
" - D3D12Device was removed AFTER commandlist "
"execution with HR %x, but wasn't before.\n",
hr);
goto flush_fail;
}
pD3D12Enc->m_fenceValue++;
pD3D12Enc->m_bPendingWorkNotFlushed = false;
}
return;
flush_fail:
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush failed for fenceValue: %" PRIu64 "\n", pD3D12Enc->m_fenceValue);
assert(false);
}
void
d3d12_video_encoder_ensure_fence_finished(struct pipe_video_codec *codec, uint64_t fenceValueToWaitOn, uint64_t timeout_ns)
{
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
HRESULT hr = S_OK;
uint64_t completedValue = pD3D12Enc->m_spFence->GetCompletedValue();
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - Waiting for fence (with timeout_ns %" PRIu64 ") to finish with "
"fenceValue: %" PRIu64 " - Current Fence Completed Value %" PRIu64 "\n",
timeout_ns, fenceValueToWaitOn, completedValue);
if(completedValue < fenceValueToWaitOn) {
HANDLE event = { };
int event_fd = 0;
event = d3d12_fence_create_event(&event_fd);
hr = pD3D12Enc->m_spFence->SetEventOnCompletion(fenceValueToWaitOn, event);
if (FAILED(hr)) {
debug_printf(
"[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - SetEventOnCompletion for fenceValue %" PRIu64 " failed with HR %x\n",
fenceValueToWaitOn, hr);
goto ensure_fence_finished_fail;
}
d3d12_fence_wait_event(event, event_fd, timeout_ns);
d3d12_fence_close_event(event, event_fd);
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - Waiting on fence to be done with "
"fenceValue: %" PRIu64 " - current CompletedValue: %" PRIu64 "\n",
fenceValueToWaitOn,
completedValue);
} else {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_ensure_fence_finished - Fence already done with "
"fenceValue: %" PRIu64 " - current CompletedValue: %" PRIu64 "\n",
fenceValueToWaitOn,
completedValue);
}
return;
ensure_fence_finished_fail:
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion failed for fenceValue: %" PRIu64 "\n", fenceValueToWaitOn);
assert(false);
}
void
d3d12_video_encoder_sync_completion(struct pipe_video_codec *codec, uint64_t fenceValueToWaitOn, uint64_t timeout_ns)
{
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
assert(pD3D12Enc);
assert(pD3D12Enc->m_spD3D12VideoDevice);
assert(pD3D12Enc->m_spEncodeCommandQueue);
HRESULT hr = S_OK;
d3d12_video_encoder_ensure_fence_finished(codec, fenceValueToWaitOn, timeout_ns);
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion - resetting ID3D12CommandAllocator %p suceeded.\n",
pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spCommandAllocator.Get());
// Release references granted on end_frame for this inflight operations
pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spEncoder.Reset();
pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spEncoderHeap.Reset();
pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_References.reset();
// Validate device was not removed
hr = pD3D12Enc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
if (hr != S_OK) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion"
" - D3D12Device was removed AFTER d3d12_video_encoder_ensure_fence_finished "
"execution with HR %x, but wasn't before.\n",
hr);
goto sync_with_token_fail;
}
debug_printf(
"[d3d12_video_encoder] d3d12_video_encoder_sync_completion - GPU execution finalized for fenceValue: %" PRIu64 "\n",
fenceValueToWaitOn);
return;
sync_with_token_fail:
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion failed for fenceValue: %" PRIu64 "\n", fenceValueToWaitOn);
assert(false);
}
/**
* Destroys a d3d12_video_encoder
* Call destroy_XX for applicable XX nested member types before deallocating
* Destroy methods should check != nullptr on their input target argument as this method can be called as part of
* cleanup from failure on the creation method
*/
void
d3d12_video_encoder_destroy(struct pipe_video_codec *codec)
{
if (codec == nullptr) {
return;
}
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
// Flush pending work before destroying
if(pD3D12Enc->m_bPendingWorkNotFlushed){
uint64_t curBatchFence = pD3D12Enc->m_fenceValue;
d3d12_video_encoder_flush(codec);
d3d12_video_encoder_sync_completion(codec, curBatchFence, PIPE_TIMEOUT_INFINITE);
}
// Call d3d12_video_encoder dtor to make ComPtr and other member's destructors work
delete pD3D12Enc;
}
void
d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture)
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
bool bUsedAsReference = false;
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
d3d12_video_encoder_update_current_frame_pic_params_info_h264(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference);
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
d3d12_video_encoder_update_current_frame_pic_params_info_hevc(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference);
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
pD3D12Enc->m_upDPBManager->begin_frame(currentPicParams, bUsedAsReference, picture);
}
bool
d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture)
{
bool codecChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_codec) != 0);
bool profileChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_profile) != 0);
bool levelChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_level) != 0);
bool codecConfigChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_codec_config) != 0);
bool inputFormatChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_input_format) != 0);
bool resolutionChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_resolution) != 0);
bool rateControlChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_rate_control) != 0);
bool slicesChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_slices) != 0);
bool gopChanged =
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_gop) != 0);
bool motionPrecisionLimitChanged = ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags &
d3d12_video_encoder_config_dirty_flag_motion_precision_limit) != 0);
// Events that that trigger a re-creation of the reference picture manager
// Stores codec agnostic textures so only input format, resolution and gop (num dpb references) affects this
if (!pD3D12Enc->m_upDPBManager
// || codecChanged
// || profileChanged
// || levelChanged
// || codecConfigChanged
|| inputFormatChanged ||
resolutionChanged
// || rateControlChanged
// || slicesChanged
|| gopChanged
// || motionPrecisionLimitChanged
) {
if (!pD3D12Enc->m_upDPBManager) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating Reference "
"Pictures Manager for the first time\n");
} else {
debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating Reference Pictures Manager\n");
}
D3D12_RESOURCE_FLAGS resourceAllocFlags =
D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
bool fArrayOfTextures = ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) == 0);
uint32_t texturePoolSize = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc) +
1u; // adding an extra slot as we also need to count the current frame output recon
// allocation along max reference frame allocations
assert(texturePoolSize < UINT16_MAX);
pD3D12Enc->m_upDPBStorageManager.reset();
if (fArrayOfTextures) {
pD3D12Enc->m_upDPBStorageManager = std::make_unique<d3d12_array_of_textures_dpb_manager>(
static_cast<uint16_t>(texturePoolSize),
pD3D12Enc->m_pD3D12Screen->dev,
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
(D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE),
true, // setNullSubresourcesOnAllZero - D3D12 Video Encode expects nullptr pSubresources if AoT,
pD3D12Enc->m_NodeMask,
/*use underlying pool, we can't reuse upper level allocations, need D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY*/
true);
} else {
pD3D12Enc->m_upDPBStorageManager = std::make_unique<d3d12_texture_array_dpb_manager>(
static_cast<uint16_t>(texturePoolSize),
pD3D12Enc->m_pD3D12Screen->dev,
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
resourceAllocFlags,
pD3D12Enc->m_NodeMask);
}
d3d12_video_encoder_create_reference_picture_manager(pD3D12Enc);
}
bool reCreatedEncoder = false;
// Events that that trigger a re-creation of the encoder
if (!pD3D12Enc->m_spVideoEncoder || codecChanged ||
profileChanged
// || levelChanged // Only affects encoder heap
|| codecConfigChanged ||
inputFormatChanged
// || resolutionChanged // Only affects encoder heap
// Only re-create if there is NO SUPPORT for reconfiguring rateControl on the fly
|| (rateControlChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) ==
0 /*checking the flag is NOT set*/))
// Only re-create if there is NO SUPPORT for reconfiguring slices on the fly
|| (slicesChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) ==
0 /*checking the flag is NOT set*/))
// Only re-create if there is NO SUPPORT for reconfiguring gop on the fly
|| (gopChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) ==
0 /*checking the flag is NOT set*/)) ||
motionPrecisionLimitChanged) {
if (!pD3D12Enc->m_spVideoEncoder) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating "
"D3D12VideoEncoder for the first time\n");
} else {
debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating D3D12VideoEncoder\n");
reCreatedEncoder = true;
}
D3D12_VIDEO_ENCODER_DESC encoderDesc = { pD3D12Enc->m_NodeMask,
D3D12_VIDEO_ENCODER_FLAG_NONE,
pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc),
pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit };
// Create encoder
pD3D12Enc->m_spVideoEncoder.Reset();
HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CreateVideoEncoder(&encoderDesc,
IID_PPV_ARGS(pD3D12Enc->m_spVideoEncoder.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("CreateVideoEncoder failed with HR %x\n", hr);
return false;
}
}
bool reCreatedEncoderHeap = false;
// Events that that trigger a re-creation of the encoder heap
if (!pD3D12Enc->m_spVideoEncoderHeap || codecChanged || profileChanged ||
levelChanged
// || codecConfigChanged // Only affects encoder
|| inputFormatChanged // Might affect internal textures in the heap
|| resolutionChanged
// Only re-create if there is NO SUPPORT for reconfiguring rateControl on the fly
|| (rateControlChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) ==
0 /*checking the flag is NOT set*/))
// Only re-create if there is NO SUPPORT for reconfiguring slices on the fly
|| (slicesChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) ==
0 /*checking the flag is NOT set*/))
// Only re-create if there is NO SUPPORT for reconfiguring gop on the fly
|| (gopChanged && ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) ==
0 /*checking the flag is NOT set*/))
// || motionPrecisionLimitChanged // Only affects encoder
) {
if (!pD3D12Enc->m_spVideoEncoderHeap) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_reconfigure_encoder_objects - Creating "
"D3D12VideoEncoderHeap for the first time\n");
} else {
debug_printf("[d3d12_video_encoder] Reconfiguration triggered -> Re-creating D3D12VideoEncoderHeap\n");
reCreatedEncoderHeap = true;
}
D3D12_VIDEO_ENCODER_HEAP_DESC heapDesc = { pD3D12Enc->m_NodeMask,
D3D12_VIDEO_ENCODER_HEAP_FLAG_NONE,
pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
d3d12_video_encoder_get_current_level_desc(pD3D12Enc),
// resolution list count
1,
// resolution list
&pD3D12Enc->m_currentEncodeConfig.m_currentResolution };
// Create encoder heap
pD3D12Enc->m_spVideoEncoderHeap.Reset();
HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CreateVideoEncoderHeap(&heapDesc,
IID_PPV_ARGS(pD3D12Enc->m_spVideoEncoderHeap.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("CreateVideoEncoderHeap failed with HR %x\n", hr);
return false;
}
}
// If on-the-fly reconfiguration happened without object recreation, set
// D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_*_CHANGED reconfiguration flags in EncodeFrame
if (rateControlChanged &&
((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE) !=
0 /*checking if the flag it's actually set*/) &&
(pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RATE_CONTROL_CHANGE;
}
if (slicesChanged &&
((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE) !=
0 /*checking if the flag it's actually set*/) &&
(pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_SUBREGION_LAYOUT_CHANGE;
}
if (gopChanged &&
((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE) !=
0 /*checking if the flag it's actually set*/) &&
(pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_GOP_SEQUENCE_CHANGE;
}
return true;
}
void
d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder *pD3D12Enc)
{
pD3D12Enc->m_upDPBManager.reset();
pD3D12Enc->m_upBitstreamBuilder.reset();
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
bool gopHasPFrames =
(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.PPicturePeriod > 0) &&
((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.GOPLength == 0) ||
(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.PPicturePeriod <
pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures.GOPLength));
pD3D12Enc->m_upDPBManager = std::make_unique<d3d12_video_encoder_references_manager_h264>(
gopHasPFrames,
*pD3D12Enc->m_upDPBStorageManager,
// Max number of frames to be used as a reference, without counting the current recon picture
d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc)
);
pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_h264>();
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
bool gopHasPFrames =
(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.PPicturePeriod > 0) &&
((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.GOPLength == 0) ||
(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.PPicturePeriod <
pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures.GOPLength));
pD3D12Enc->m_upDPBManager = std::make_unique<d3d12_video_encoder_references_manager_hevc>(
gopHasPFrames,
*pD3D12Enc->m_upDPBStorageManager,
// Max number of frames to be used as a reference, without counting the current recon picture
d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc)
);
pD3D12Enc->m_upBitstreamBuilder = std::make_unique<d3d12_video_bitstream_builder_hevc>();
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA
d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {};
if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode !=
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) {
subregionData.pSlicesPartition_H264 =
&pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264;
subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES);
}
return subregionData;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {};
if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode !=
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) {
subregionData.pSlicesPartition_HEVC =
&pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_HEVC;
subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES);
}
return subregionData;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA
d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {};
curPicParamsData.pH264PicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_H264PicData;
curPicParamsData.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_H264PicData);
return curPicParamsData;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {};
curPicParamsData.pHEVCPicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_HEVCPicData;
curPicParamsData.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_HEVCPicData);
return curPicParamsData;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
D3D12_VIDEO_ENCODER_RATE_CONTROL
d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder *pD3D12Enc)
{
D3D12_VIDEO_ENCODER_RATE_CONTROL curRateControlDesc = {};
curRateControlDesc.Mode = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode;
curRateControlDesc.Flags = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags;
curRateControlDesc.TargetFrameRate = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate;
switch (pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode) {
case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_ABSOLUTE_QP_MAP:
{
curRateControlDesc.ConfigParams.pConfiguration_CQP = nullptr;
curRateControlDesc.ConfigParams.DataSize = 0;
} break;
case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP:
{
curRateControlDesc.ConfigParams.pConfiguration_CQP =
&pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP;
curRateControlDesc.ConfigParams.DataSize =
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP);
} break;
case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
{
curRateControlDesc.ConfigParams.pConfiguration_CBR =
&pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR;
curRateControlDesc.ConfigParams.DataSize =
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR);
} break;
case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
{
curRateControlDesc.ConfigParams.pConfiguration_VBR =
&pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR;
curRateControlDesc.ConfigParams.DataSize =
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR);
} break;
case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
{
curRateControlDesc.ConfigParams.pConfiguration_QVBR =
&pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR;
curRateControlDesc.ConfigParams.DataSize =
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR);
} break;
default:
{
unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE");
} break;
}
return curRateControlDesc;
}
D3D12_VIDEO_ENCODER_LEVEL_SETTING
d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {};
curLevelDesc.pH264LevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting;
curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting);
return curLevelDesc;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {};
curLevelDesc.pHEVCLevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_HEVCLevelSetting;
curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_HEVCLevelSetting);
return curLevelDesc;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
uint32_t
d3d12_video_encoder_build_codec_headers(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
return d3d12_video_encoder_build_codec_headers_h264(pD3D12Enc);
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
return d3d12_video_encoder_build_codec_headers_hevc(pD3D12Enc);
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
return 0u;
}
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE
d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {};
curGOPDesc.pH264GroupOfPictures =
&pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures;
curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures);
return curGOPDesc;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {};
curGOPDesc.pHEVCGroupOfPictures =
&pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures;
curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_HEVCGroupOfPictures);
return curGOPDesc;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION
d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {};
codecConfigDesc.pH264Config = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config;
codecConfigDesc.DataSize =
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config);
return codecConfigDesc;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {};
codecConfigDesc.pHEVCConfig = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_HEVCConfig;
codecConfigDesc.DataSize =
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_HEVCConfig);
return codecConfigDesc;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
D3D12_VIDEO_ENCODER_CODEC
d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
return D3D12_VIDEO_ENCODER_CODEC_H264;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
return D3D12_VIDEO_ENCODER_CODEC_HEVC;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
///
/// Call d3d12_video_encoder_query_d3d12_driver_caps and see if any optional feature requested
/// is not supported, disable it, query again until finding a negotiated cap/feature set
/// Note that with fallbacks, the upper layer will not get exactly the encoding seetings they requested
/// but for very particular settings it's better to continue with warnings than failing the whole encoding process
///
bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData) {
///
/// Check for general support
/// Check for validation errors (some drivers return general support but also validation errors anyways, work around for those unexpected cases)
///
bool configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData)
&& (((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0)
&& (capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
///
/// If rate control config is not supported, try falling back and check for caps again
///
if ((capEncoderSupportData.ValidationFlags & (D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED | D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED)) != 0) {
if (D3D12_VIDEO_ENC_FALLBACK_RATE_CONTROL_CONFIG){ // Check if fallback mode is enabled, or we should just fail without support
debug_printf("[d3d12_video_encoder] WARNING: Requested rate control is not supported, trying fallback to unsetting optional features\n");
bool isRequestingVBVSizesSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_VBV_SIZE_CONFIG_AVAILABLE) != 0);
bool isClientRequestingVBVSizes = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES) != 0);
if(isClientRequestingVBVSizes && !isRequestingVBVSizesSupported) {
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",
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity,
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity = 0;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness = 0;
}
bool isRequestingPeakFrameSizeSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0);
bool isClientRequestingPeakFrameSize = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE) != 0);
if(isClientRequestingPeakFrameSize && !isRequestingPeakFrameSizeSupported) {
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",
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxFrameBitSize);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxFrameBitSize = 0;
}
///
/// Try fallback configuration
///
configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData)
&& (((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0)
&& (capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
} else {
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");
}
}
if(!configSupported) {
debug_printf("[d3d12_video_encoder] Cap negotiation failed, see more details below:\n");
if ((capEncoderSupportData.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_NOT_SUPPORTED) != 0) {
debug_printf("[d3d12_video_encoder] Requested codec is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RESOLUTION_NOT_SUPPORTED_IN_LIST) != 0) {
debug_printf("[d3d12_video_encoder] Requested resolution is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED) != 0) {
debug_printf("[d3d12_video_encoder] Requested bitrate or rc config is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_CONFIGURATION_NOT_SUPPORTED) != 0) {
debug_printf("[d3d12_video_encoder] Requested codec config is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED) != 0) {
debug_printf("[d3d12_video_encoder] Requested rate control mode is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INTRA_REFRESH_MODE_NOT_SUPPORTED) != 0) {
debug_printf("[d3d12_video_encoder] Requested intra refresh config is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_SUBREGION_LAYOUT_MODE_NOT_SUPPORTED) != 0) {
debug_printf("[d3d12_video_encoder] Requested subregion layout mode is not supported\n");
}
if ((capEncoderSupportData.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INPUT_FORMAT_NOT_SUPPORTED) !=
0) {
debug_printf("[d3d12_video_encoder] Requested input dxgi format is not supported\n");
}
}
return configSupported;
}
bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData) {
capEncoderSupportData.NodeIndex = pD3D12Enc->m_NodeIndex;
capEncoderSupportData.Codec = d3d12_video_encoder_get_current_codec(pD3D12Enc);
capEncoderSupportData.InputFormat = pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format;
capEncoderSupportData.RateControl = d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc);
capEncoderSupportData.IntraRefresh = pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.Mode;
capEncoderSupportData.SubregionFrameEncoding = pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode;
capEncoderSupportData.ResolutionsListCount = 1;
capEncoderSupportData.pResolutionList = &pD3D12Enc->m_currentEncodeConfig.m_currentResolution;
capEncoderSupportData.CodecGopSequence = d3d12_video_encoder_get_current_gop_desc(pD3D12Enc);
capEncoderSupportData.MaxReferenceFramesInDPB = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
capEncoderSupportData.CodecConfiguration = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc);
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
capEncoderSupportData.SuggestedProfile.pH264Profile =
&pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile;
capEncoderSupportData.SuggestedProfile.DataSize =
sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile);
capEncoderSupportData.SuggestedLevel.pH264LevelSetting =
&pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting;
capEncoderSupportData.SuggestedLevel.DataSize =
sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting);
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
capEncoderSupportData.SuggestedProfile.pHEVCProfile =
&pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_HEVCProfile;
capEncoderSupportData.SuggestedProfile.DataSize =
sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_HEVCProfile);
capEncoderSupportData.SuggestedLevel.pHEVCLevelSetting =
&pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_HEVCLevelSetting;
capEncoderSupportData.SuggestedLevel.DataSize =
sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_HEVCLevelSetting);
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
// prepare inout storage for the resolution dependent result.
capEncoderSupportData.pResolutionDependentSupport =
&pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps;
HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
&capEncoderSupportData,
sizeof(capEncoderSupportData));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
return false;
}
pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags = capEncoderSupportData.SupportFlags;
pD3D12Enc->m_currentEncodeCapabilities.m_ValidationFlags = capEncoderSupportData.ValidationFlags;
return true;
}
bool d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder *pD3D12Enc,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode
)
{
D3D12_FEATURE_DATA_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE capDataSubregionLayout = { };
capDataSubregionLayout.NodeIndex = pD3D12Enc->m_NodeIndex;
capDataSubregionLayout.Codec = d3d12_video_encoder_get_current_codec(pD3D12Enc);
capDataSubregionLayout.Profile = d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
capDataSubregionLayout.Level = d3d12_video_encoder_get_current_level_desc(pD3D12Enc);
capDataSubregionLayout.SubregionMode = requestedSlicesMode;
HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE, &capDataSubregionLayout, sizeof(capDataSubregionLayout));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
return false;
}
return capDataSubregionLayout.IsSupported;
}
D3D12_VIDEO_ENCODER_PROFILE_DESC
d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder *pD3D12Enc)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {};
curProfDesc.pH264Profile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile;
curProfDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile);
return curProfDesc;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {};
curProfDesc.pHEVCProfile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_HEVCProfile;
curProfDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_HEVCProfile);
return curProfDesc;
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
uint32_t
d3d12_video_encoder_get_current_max_dpb_capacity(struct d3d12_video_encoder *pD3D12Enc)
{
return pD3D12Enc->base.max_references;
}
bool
d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture)
{
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
switch (codec) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
return d3d12_video_encoder_update_current_encoder_config_state_h264(pD3D12Enc, srcTexture, picture);
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
return d3d12_video_encoder_update_current_encoder_config_state_hevc(pD3D12Enc, srcTexture, picture);
} break;
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
bool
d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc)
{
assert(pD3D12Enc->m_spD3D12VideoDevice);
D3D12_COMMAND_QUEUE_DESC commandQueueDesc = { D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE };
HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandQueue(
&commandQueueDesc,
IID_PPV_ARGS(pD3D12Enc->m_spEncodeCommandQueue.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateCommandQueue "
"failed with HR %x\n",
hr);
return false;
}
hr = pD3D12Enc->m_pD3D12Screen->dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pD3D12Enc->m_spFence));
if (FAILED(hr)) {
debug_printf(
"[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateFence failed with HR %x\n",
hr);
return false;
}
for (auto& inputResource : pD3D12Enc->m_inflightResourcesPool)
{
// Create associated command allocator for Encode, Resolve operations
hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
IID_PPV_ARGS(inputResource.m_spCommandAllocator.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to "
"CreateCommandAllocator failed with HR %x\n",
hr);
return false;
}
}
ComPtr<ID3D12Device4> spD3D12Device4;
if (FAILED(pD3D12Enc->m_pD3D12Screen->dev->QueryInterface(
IID_PPV_ARGS(spD3D12Device4.GetAddressOf())))) {
debug_printf(
"[d3d12_video_encoder] d3d12_video_encoder_create_encoder - D3D12 Device has no Video encode support\n");
return false;
}
hr = spD3D12Device4->CreateCommandList1(0,
D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
D3D12_COMMAND_LIST_FLAG_NONE,
IID_PPV_ARGS(pD3D12Enc->m_spEncodeCommandList.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_command_objects - Call to CreateCommandList "
"failed with HR %x\n",
hr);
return false;
}
return true;
}
struct pipe_video_codec *
d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *codec)
{
///
/// Initialize d3d12_video_encoder
///
// Not using new doesn't call ctor and the initializations in the class declaration are lost
struct d3d12_video_encoder *pD3D12Enc = new d3d12_video_encoder;
pD3D12Enc->m_spEncodedFrameMetadata.resize(D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT, {nullptr, 0, 0});
pD3D12Enc->m_inflightResourcesPool.resize(D3D12_VIDEO_ENC_ASYNC_DEPTH, { 0 });
pD3D12Enc->base = *codec;
pD3D12Enc->m_screen = context->screen;
pD3D12Enc->base.context = context;
pD3D12Enc->base.width = codec->width;
pD3D12Enc->base.height = codec->height;
pD3D12Enc->base.max_references = codec->max_references;
// Only fill methods that are supported by the d3d12 encoder, leaving null the rest (ie. encode_* / encode_macroblock)
pD3D12Enc->base.destroy = d3d12_video_encoder_destroy;
pD3D12Enc->base.begin_frame = d3d12_video_encoder_begin_frame;
pD3D12Enc->base.encode_bitstream = d3d12_video_encoder_encode_bitstream;
pD3D12Enc->base.end_frame = d3d12_video_encoder_end_frame;
pD3D12Enc->base.flush = d3d12_video_encoder_flush;
pD3D12Enc->base.get_feedback = d3d12_video_encoder_get_feedback;
struct d3d12_context *pD3D12Ctx = (struct d3d12_context *) context;
pD3D12Enc->m_pD3D12Screen = d3d12_screen(pD3D12Ctx->base.screen);
if (FAILED(pD3D12Enc->m_pD3D12Screen->dev->QueryInterface(
IID_PPV_ARGS(pD3D12Enc->m_spD3D12VideoDevice.GetAddressOf())))) {
debug_printf(
"[d3d12_video_encoder] d3d12_video_encoder_create_encoder - D3D12 Device has no Video encode support\n");
goto failed;
}
if (!d3d12_video_encoder_create_command_objects(pD3D12Enc)) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_encoder - Failure on "
"d3d12_video_encoder_create_command_objects\n");
goto failed;
}
return &pD3D12Enc->base;
failed:
if (pD3D12Enc != nullptr) {
d3d12_video_encoder_destroy((struct pipe_video_codec *) pD3D12Enc);
}
return nullptr;
}
bool
d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture)
{
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.NodeIndex = pD3D12Enc->m_NodeIndex;
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.Codec =
pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc;
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.Profile =
d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.InputFormat =
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format;
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.PictureTargetResolution =
pD3D12Enc->m_currentEncodeConfig.m_currentResolution;
HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(
D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
&pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps,
sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
return false;
}
if (!pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.IsSupported) {
debug_printf("[d3d12_video_encoder] D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS arguments are not supported.\n");
return false;
}
uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
d3d12_video_encoder_calculate_metadata_resolved_buffer_size(
pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput,
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize);
D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
if ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer == nullptr) ||
(GetDesc(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get()).Width <
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize)) {
CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize);
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Reset();
HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
&Properties,
D3D12_HEAP_FLAG_NONE,
&resolvedMetadataBufferDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("CreateCommittedResource failed with HR %x\n", hr);
return false;
}
}
if ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer == nullptr) ||
(GetDesc(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get()).Width <
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize)) {
CD3DX12_RESOURCE_DESC metadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize);
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Reset();
HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
&Properties,
D3D12_HEAP_FLAG_NONE,
&metadataBufferDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.GetAddressOf()));
if (FAILED(hr)) {
debug_printf("CreateCommittedResource failed with HR %x\n", hr);
return false;
}
}
return true;
}
bool
d3d12_video_encoder_reconfigure_session(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture)
{
assert(pD3D12Enc->m_spD3D12VideoDevice);
if(!d3d12_video_encoder_update_current_encoder_config_state(pD3D12Enc, srcTexture, picture)) {
debug_printf("d3d12_video_encoder_update_current_encoder_config_state failed!\n");
return false;
}
if(!d3d12_video_encoder_reconfigure_encoder_objects(pD3D12Enc, srcTexture, picture)) {
debug_printf("d3d12_video_encoder_reconfigure_encoder_objects failed!\n");
return false;
}
d3d12_video_encoder_update_picparams_tracking(pD3D12Enc, srcTexture, picture);
if(!d3d12_video_encoder_prepare_output_buffers(pD3D12Enc, srcTexture, picture)) {
debug_printf("d3d12_video_encoder_prepare_output_buffers failed!\n");
return false;
}
return true;
}
/**
* start encoding of a new frame
*/
void
d3d12_video_encoder_begin_frame(struct pipe_video_codec * codec,
struct pipe_video_buffer *target,
struct pipe_picture_desc *picture)
{
// Do nothing here. Initialize happens on encoder creation, re-config (if any) happens in
// d3d12_video_encoder_encode_bitstream
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
assert(pD3D12Enc);
HRESULT hr = S_OK;
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame started for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
///
/// Wait here to make sure the next in flight resource set is empty before using it
///
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) ));
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",
fenceValueToWaitOn);
d3d12_video_encoder_ensure_fence_finished(codec, fenceValueToWaitOn, PIPE_TIMEOUT_INFINITE);
if (!d3d12_video_encoder_reconfigure_session(pD3D12Enc, target, picture)) {
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame - Failure on "
"d3d12_video_encoder_reconfigure_session\n");
goto fail;
}
hr = pD3D12Enc->m_spEncodeCommandList->Reset(pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spCommandAllocator.Get());
if (FAILED(hr)) {
debug_printf(
"[d3d12_video_encoder] d3d12_video_encoder_flush - resetting ID3D12GraphicsCommandList failed with HR %x\n",
hr);
goto fail;
}
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame finalized for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
return;
fail:
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame failed for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
assert(false);
}
void
d3d12_video_encoder_calculate_metadata_resolved_buffer_size(uint32_t maxSliceNumber, uint64_t &bufferSize)
{
bufferSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) +
(maxSliceNumber * sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA));
}
// Returns the number of slices that the output will contain for fixed slicing modes
// and the maximum number of slices the output might contain for dynamic slicing modes (eg. max bytes per slice)
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)
{
uint32_t pic_width_in_subregion_units =
static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Width / static_cast<double>(SubregionBlockPixelsSize)));
uint32_t pic_height_in_subregion_units =
static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Height / static_cast<double>(SubregionBlockPixelsSize)));
uint32_t total_picture_subregion_units = pic_width_in_subregion_units * pic_height_in_subregion_units;
uint32_t maxSlices = 0u;
switch (slicesMode) {
case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME:
{
maxSlices = 1u;
} break;
case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_BYTES_PER_SUBREGION:
{
maxSlices = MaxSubregionsNumberFromCaps;
} break;
case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_SQUARE_UNITS_PER_SUBREGION_ROW_UNALIGNED:
{
maxSlices = static_cast<uint32_t>(
std::ceil(total_picture_subregion_units / static_cast<double>(slicesConfig->NumberOfCodingUnitsPerSlice)));
} break;
case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION:
{
maxSlices = static_cast<uint32_t>(
std::ceil(pic_height_in_subregion_units / static_cast<double>(slicesConfig->NumberOfRowsPerSlice)));
} break;
case D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME:
{
maxSlices = slicesConfig->NumberOfSlicesPerFrame;
} break;
default:
{
unreachable("Unsupported D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE");
} break;
}
return maxSlices;
}
/**
* encode a bitstream
*/
void
d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
struct pipe_video_buffer *source,
struct pipe_resource * destination,
void ** feedback)
{
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
assert(pD3D12Enc);
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_encode_bitstream started for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
assert(pD3D12Enc->m_spD3D12VideoDevice);
assert(pD3D12Enc->m_spEncodeCommandQueue);
assert(pD3D12Enc->m_pD3D12Screen);
// Since this can be queried out of order in get_feedback, we need to pass out the actual value of the fence
// and not the pointer to it (the fence value will keep increasing in the surfaces that have a pointer to it)
*feedback = (void*) pD3D12Enc->m_fenceValue;
struct d3d12_video_buffer *pInputVideoBuffer = (struct d3d12_video_buffer *) source;
assert(pInputVideoBuffer);
ID3D12Resource *pInputVideoD3D12Res = d3d12_resource_resource(pInputVideoBuffer->texture);
uint32_t inputVideoD3D12Subresource = 0u;
struct d3d12_resource *pOutputBitstreamBuffer = (struct d3d12_resource *) destination;
assert(pOutputBitstreamBuffer);
ID3D12Resource *pOutputBufferD3D12Res = d3d12_resource_resource(pOutputBitstreamBuffer);
// Make them permanently resident for video use
d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pOutputBitstreamBuffer);
d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pInputVideoBuffer->texture);
uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
///
/// Record Encode operation
///
///
/// pInputVideoD3D12Res and pOutputBufferD3D12Res are unwrapped from pipe_resource objects that are passed externally
/// and could be tracked by pipe_context and have pending ops. Flush any work on them and transition to
/// D3D12_RESOURCE_STATE_COMMON before issuing work in Video command queue below. After the video work is done in the
/// GPU, transition back to D3D12_RESOURCE_STATE_COMMON
///
/// Note that unlike the D3D12TranslationLayer codebase, the state tracker here doesn't (yet) have any kind of
/// multi-queue support, so it wouldn't implicitly synchronize when trying to transition between a graphics op and a
/// video op.
///
d3d12_transition_resource_state(
d3d12_context(pD3D12Enc->base.context),
pInputVideoBuffer->texture, // d3d12_resource wrapper for pInputVideoD3D12Res
D3D12_RESOURCE_STATE_COMMON,
D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
d3d12_transition_resource_state(d3d12_context(pD3D12Enc->base.context),
pOutputBitstreamBuffer, // d3d12_resource wrapped for pOutputBufferD3D12Res
D3D12_RESOURCE_STATE_COMMON,
D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
d3d12_apply_resource_states(d3d12_context(pD3D12Enc->base.context), false);
d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context),
pInputVideoBuffer->texture,
false /*wantToWrite*/);
d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context), pOutputBitstreamBuffer, true /*wantToWrite*/);
std::vector<D3D12_RESOURCE_BARRIER> rgCurrentFrameStateTransitions = {
CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res,
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ),
CD3DX12_RESOURCE_BARRIER::Transition(pOutputBufferD3D12Res,
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE)
};
pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(rgCurrentFrameStateTransitions.size(),
rgCurrentFrameStateTransitions.data());
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE reconPicOutputTextureDesc =
pD3D12Enc->m_upDPBManager->get_current_frame_recon_pic_output_allocation();
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES referenceFramesDescriptor =
pD3D12Enc->m_upDPBManager->get_current_reference_frames();
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAGS picCtrlFlags = D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_NONE;
// Transition DPB reference pictures to read mode
uint32_t maxReferences = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
std::vector<D3D12_RESOURCE_BARRIER> rgReferenceTransitions(maxReferences);
if ((referenceFramesDescriptor.NumTexture2Ds > 0) ||
(pD3D12Enc->m_upDPBManager->is_current_frame_used_as_reference())) {
rgReferenceTransitions.clear();
rgReferenceTransitions.reserve(maxReferences);
if (reconPicOutputTextureDesc.pReconstructedPicture != nullptr)
picCtrlFlags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
// Check if array of textures vs texture array
if (referenceFramesDescriptor.pSubresources == nullptr) {
// Array of resources mode for reference pictures
// Transition all subresources of each reference frame independent resource allocation
for (uint32_t referenceIdx = 0; referenceIdx < referenceFramesDescriptor.NumTexture2Ds; referenceIdx++) {
rgReferenceTransitions.push_back(
CD3DX12_RESOURCE_BARRIER::Transition(referenceFramesDescriptor.ppTexture2Ds[referenceIdx],
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ));
}
// Transition all subresources the output recon pic independent resource allocation
if (reconPicOutputTextureDesc.pReconstructedPicture != nullptr) {
rgReferenceTransitions.push_back(
CD3DX12_RESOURCE_BARRIER::Transition(reconPicOutputTextureDesc.pReconstructedPicture,
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE));
}
} else if (referenceFramesDescriptor.NumTexture2Ds > 0) {
// texture array mode for reference pictures
// In Texture array mode, the dpb storage allocator uses the same texture array for all the input
// reference pics in ppTexture2Ds and also for the pReconstructedPicture output allocations, just different
// subresources.
CD3DX12_RESOURCE_DESC referencesTexArrayDesc(GetDesc(referenceFramesDescriptor.ppTexture2Ds[0]));
#if DEBUG
// the reconpic output should be all the same texarray allocation
if((reconPicOutputTextureDesc.pReconstructedPicture) && (referenceFramesDescriptor.NumTexture2Ds > 0))
assert(referenceFramesDescriptor.ppTexture2Ds[0] == reconPicOutputTextureDesc.pReconstructedPicture);
for (uint32_t refIndex = 0; refIndex < referenceFramesDescriptor.NumTexture2Ds; refIndex++) {
// all reference frames inputs should be all the same texarray allocation
assert(referenceFramesDescriptor.ppTexture2Ds[0] ==
referenceFramesDescriptor.ppTexture2Ds[refIndex]);
}
#endif
for (uint32_t referenceSubresource = 0; referenceSubresource < referencesTexArrayDesc.DepthOrArraySize;
referenceSubresource++) {
uint32_t MipLevel, PlaneSlice, ArraySlice;
D3D12DecomposeSubresource(referenceSubresource,
referencesTexArrayDesc.MipLevels,
referencesTexArrayDesc.ArraySize(),
MipLevel,
ArraySlice,
PlaneSlice);
for (PlaneSlice = 0; PlaneSlice < pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.PlaneCount;
PlaneSlice++) {
uint32_t planeOutputSubresource =
referencesTexArrayDesc.CalcSubresource(MipLevel, ArraySlice, PlaneSlice);
rgReferenceTransitions.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
// Always same allocation in texarray mode
referenceFramesDescriptor.ppTexture2Ds[0],
D3D12_RESOURCE_STATE_COMMON,
// If this is the subresource for the reconpic output allocation, transition to ENCODE_WRITE
// Otherwise, it's a subresource for an input reference picture, transition to ENCODE_READ
(referenceSubresource == reconPicOutputTextureDesc.ReconstructedPictureSubresource) ?
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE :
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
planeOutputSubresource));
}
}
}
if (rgReferenceTransitions.size() > 0) {
pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(static_cast<uint32_t>(rgReferenceTransitions.size()),
rgReferenceTransitions.data());
}
}
// Update current frame pic params state after reconfiguring above.
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
pD3D12Enc->m_upDPBManager->get_current_frame_picture_control_data(currentPicParams);
uint32_t prefixGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers(pD3D12Enc);
// If driver needs offset alignment for bitstream resource, we will pad zeroes on the codec header to this end.
if (
(pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment > 1)
&& ((prefixGeneratedHeadersByteSize % pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment) != 0)
) {
prefixGeneratedHeadersByteSize = ALIGN(prefixGeneratedHeadersByteSize, pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment);
pD3D12Enc->m_BitstreamHeadersBuffer.resize(prefixGeneratedHeadersByteSize, 0);
}
const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS inputStreamArguments = {
// D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_DESC
{ // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS
pD3D12Enc->m_currentEncodeConfig.m_seqFlags,
// D3D12_VIDEO_ENCODER_INTRA_REFRESH
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh,
d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc),
// D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC
pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
d3d12_video_encoder_get_current_slice_param_settings(pD3D12Enc),
d3d12_video_encoder_get_current_gop_desc(pD3D12Enc) },
// D3D12_VIDEO_ENCODER_PICTURE_CONTROL_DESC
{ // uint32_t IntraRefreshFrameIndex;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex,
// D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAGS Flags;
picCtrlFlags,
// D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA PictureControlCodecData;
currentPicParams,
// D3D12_VIDEO_ENCODE_REFERENCE_FRAMES ReferenceFrames;
referenceFramesDescriptor },
pInputVideoD3D12Res,
inputVideoD3D12Subresource,
prefixGeneratedHeadersByteSize // hint for driver to know header size in final bitstream for rate control internal
// budgeting. - User can also calculate headers fixed size beforehand (eg. no VUI,
// etc) and build them with final values after EncodeFrame is executed
};
const D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS outputStreamArguments = {
// D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM
{
pOutputBufferD3D12Res,
prefixGeneratedHeadersByteSize, // Start writing after the reserved interval [0,
// prefixGeneratedHeadersByteSize) for bitstream headers
},
// D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
reconPicOutputTextureDesc,
// D3D12_VIDEO_ENCODER_ENCODE_OPERATION_METADATA_BUFFER
{ pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 }
};
// Upload the CPU buffers with the bitstream headers to the compressed bitstream resource in the interval [0,
// prefixGeneratedHeadersByteSize)
assert(prefixGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size());
// Store this info for get_feedback to be able to calculate final bitstream size
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].codecHeadersSize = prefixGeneratedHeadersByteSize;
pD3D12Enc->base.context->buffer_subdata(
pD3D12Enc->base.context, // context
destination, // dst buffer - "destination" is the pipe_resource object
// wrapping pOutputBitstreamBuffer and eventually pOutputBufferD3D12Res
PIPE_MAP_WRITE, // usage PIPE_MAP_x
0, // offset
pD3D12Enc->m_BitstreamHeadersBuffer.size(),
pD3D12Enc->m_BitstreamHeadersBuffer.data());
// Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately
// Will flush and sync this batch in d3d12_video_encoder_flush with the rest of the Video Encode Queue GPU work
// Record EncodeFrame
pD3D12Enc->m_spEncodeCommandList->EncodeFrame(pD3D12Enc->m_spVideoEncoder.Get(),
pD3D12Enc->m_spVideoEncoderHeap.Get(),
&inputStreamArguments,
&outputStreamArguments);
D3D12_RESOURCE_BARRIER rgResolveMetadataStateTransitions[] = {
CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(),
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ),
CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
D3D12_RESOURCE_STATE_COMMON),
CD3DX12_RESOURCE_BARRIER::Transition(pOutputBufferD3D12Res,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
D3D12_RESOURCE_STATE_COMMON)
};
pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(_countof(rgResolveMetadataStateTransitions),
rgResolveMetadataStateTransitions);
const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS inputMetadataCmd = {
pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc,
d3d12_video_encoder_get_current_profile_desc(pD3D12Enc),
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
// D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC
pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
{ pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 }
};
const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS outputMetadataCmd = {
/*If offset were to change, has to be aligned to pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.EncoderMetadataBufferAccessAlignment*/
{ pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(), 0 }
};
pD3D12Enc->m_spEncodeCommandList->ResolveEncoderOutputMetadata(&inputMetadataCmd, &outputMetadataCmd);
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",
d3d12_video_encoder_pool_current_index(pD3D12Enc),
pD3D12Enc->m_spVideoEncoder.Get(),
pD3D12Enc->m_spVideoEncoderHeap.Get(),
inputStreamArguments.pInputFrame,
outputStreamArguments.Bitstream.pBuffer,
inputMetadataCmd.HWLayoutMetadata.pBuffer,
outputMetadataCmd.ResolvedLayoutMetadata.pBuffer,
pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spCommandAllocator.Get());
// Transition DPB reference pictures back to COMMON
if ((referenceFramesDescriptor.NumTexture2Ds > 0) ||
(pD3D12Enc->m_upDPBManager->is_current_frame_used_as_reference())) {
for (auto &BarrierDesc : rgReferenceTransitions) {
std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter);
}
if (rgReferenceTransitions.size() > 0) {
pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(static_cast<uint32_t>(rgReferenceTransitions.size()),
rgReferenceTransitions.data());
}
}
D3D12_RESOURCE_BARRIER rgRevertResolveMetadataStateTransitions[] = {
CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
D3D12_RESOURCE_STATE_COMMON),
CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(),
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
D3D12_RESOURCE_STATE_COMMON),
};
pD3D12Enc->m_spEncodeCommandList->ResourceBarrier(_countof(rgRevertResolveMetadataStateTransitions),
rgRevertResolveMetadataStateTransitions);
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_encode_bitstream finalized for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
}
void
d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, void *feedback, unsigned *size)
{
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
assert(pD3D12Enc);
uint64_t requested_metadata_fence = ((uint64_t) feedback);
d3d12_video_encoder_sync_completion(codec, requested_metadata_fence, PIPE_TIMEOUT_INFINITE);
uint64_t current_metadata_slot = (requested_metadata_fence % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
debug_printf("d3d12_video_encoder_get_feedback with feedback: %" PRIu64 ", resources slot %" PRIu64 " metadata resolved ID3D12Resource buffer %p metadata required size %" PRIu64 "\n",
requested_metadata_fence,
(requested_metadata_fence % D3D12_VIDEO_ENC_ASYNC_DEPTH),
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize);
if((pD3D12Enc->m_fenceValue - requested_metadata_fence) > D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT)
{
debug_printf("[d3d12_video_encoder_get_feedback] Requested metadata for fence %" PRIu64 " at current fence %" PRIu64
" is too far back in time for the ring buffer of size %" PRIu64 " we keep track off - "
" Please increase the D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT environment variable and try again.\n",
requested_metadata_fence,
pD3D12Enc->m_fenceValue,
D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
*size = 0;
assert(false);
return;
}
D3D12_VIDEO_ENCODER_OUTPUT_METADATA encoderMetadata;
std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> pSubregionsMetadata;
d3d12_video_encoder_extract_encode_metadata(
pD3D12Enc,
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(),
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize,
encoderMetadata,
pSubregionsMetadata);
// Read metadata from encoderMetadata
if (encoderMetadata.EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) {
debug_printf("[d3d12_video_encoder] Encode GPU command for fence %" PRIu64 " failed - EncodeErrorFlags: %" PRIu64 "\n",
requested_metadata_fence,
encoderMetadata.EncodeErrorFlags);
*size = 0;
assert(false);
return;
}
assert(encoderMetadata.EncodedBitstreamWrittenBytesCount > 0u);
*size = static_cast<unsigned int>(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].codecHeadersSize + encoderMetadata.EncodedBitstreamWrittenBytesCount);
debug_printf("[d3d12_video_encoder_get_feedback] Requested metadata for encoded frame at fence %" PRIu64 " is %d (feedback was requested at current fence %" PRIu64 ")\n",
requested_metadata_fence,
*size,
pD3D12Enc->m_fenceValue);
}
void
d3d12_video_encoder_extract_encode_metadata(
struct d3d12_video_encoder * pD3D12Enc,
ID3D12Resource * pResolvedMetadataBuffer, // input
uint64_t resourceMetadataSize, // input
D3D12_VIDEO_ENCODER_OUTPUT_METADATA & parsedMetadata, // output
std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> &pSubregionsMetadata // output
)
{
struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pD3D12Enc->m_pD3D12Screen;
assert(pD3D12Screen);
pipe_resource *pPipeResolvedMetadataBuffer =
d3d12_resource_from_resource(&pD3D12Screen->base, pResolvedMetadataBuffer);
assert(pPipeResolvedMetadataBuffer);
assert(resourceMetadataSize < INT_MAX);
struct pipe_box box = {
0, // x
0, // y
0, // z
static_cast<int>(resourceMetadataSize), // width
1, // height
1 // depth
};
struct pipe_transfer *mapTransfer;
unsigned mapUsage = PIPE_MAP_READ;
void * pMetadataBufferSrc = pD3D12Enc->base.context->buffer_map(pD3D12Enc->base.context,
pPipeResolvedMetadataBuffer,
0,
mapUsage,
&box,
&mapTransfer);
assert(mapUsage & PIPE_MAP_READ);
assert(pPipeResolvedMetadataBuffer->usage == PIPE_USAGE_DEFAULT);
// Note: As we're calling buffer_map with PIPE_MAP_READ on a pPipeResolvedMetadataBuffer which has pipe_usage_default
// buffer_map itself will do all the synchronization and waits so once the function returns control here
// the contents of mapTransfer are ready to be accessed.
// Clear output
memset(&parsedMetadata, 0, sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA));
// Calculate sizes
uint64_t encoderMetadataSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA);
// Copy buffer to the appropriate D3D12_VIDEO_ENCODER_OUTPUT_METADATA memory layout
parsedMetadata = *reinterpret_cast<D3D12_VIDEO_ENCODER_OUTPUT_METADATA *>(pMetadataBufferSrc);
// As specified in D3D12 Encode spec, the array base for metadata for the slices
// (D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA[]) is placed in memory immediately after the
// D3D12_VIDEO_ENCODER_OUTPUT_METADATA structure
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata =
reinterpret_cast<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *>(reinterpret_cast<uint8_t *>(pMetadataBufferSrc) +
encoderMetadataSize);
// Copy fields into D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA
assert(parsedMetadata.WrittenSubregionsCount < SIZE_MAX);
pSubregionsMetadata.resize(static_cast<size_t>(parsedMetadata.WrittenSubregionsCount));
for (uint32_t sliceIdx = 0; sliceIdx < parsedMetadata.WrittenSubregionsCount; sliceIdx++) {
pSubregionsMetadata[sliceIdx].bHeaderSize = pFrameSubregionMetadata[sliceIdx].bHeaderSize;
pSubregionsMetadata[sliceIdx].bSize = pFrameSubregionMetadata[sliceIdx].bSize;
pSubregionsMetadata[sliceIdx].bStartOffset = pFrameSubregionMetadata[sliceIdx].bStartOffset;
}
// Unmap the buffer tmp storage
pipe_buffer_unmap(pD3D12Enc->base.context, mapTransfer);
pipe_resource_reference(&pPipeResolvedMetadataBuffer, NULL);
}
/**
* end encoding of the current frame
*/
void
d3d12_video_encoder_end_frame(struct pipe_video_codec * codec,
struct pipe_video_buffer *target,
struct pipe_picture_desc *picture)
{
struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
assert(pD3D12Enc);
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame started for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
// Signal finish of current frame encoding to the picture management tracker
pD3D12Enc->m_upDPBManager->end_frame();
// Save extra references of Encoder, EncoderHeap and DPB allocations in case
// there's a reconfiguration that trigers the construction of new objects
pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spEncoder = pD3D12Enc->m_spVideoEncoder;
pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_spEncoderHeap = pD3D12Enc->m_spVideoEncoderHeap;
pD3D12Enc->m_inflightResourcesPool[d3d12_video_encoder_pool_current_index(pD3D12Enc)].m_References = pD3D12Enc->m_upDPBStorageManager;
debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame finalized for fenceValue: %" PRIu64 "\n",
pD3D12Enc->m_fenceValue);
pD3D12Enc->m_bPendingWorkNotFlushed = true;
}