--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _WIN32
+#include <wsl/winadapter.h>
+#endif
+
+#define D3D12_IGNORE_SDK_LAYERS
+#include <directx/d3d12.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_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_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"
+
+/**
+ * flush any outstanding command buffers to the hardware
+ * should be called before a video_buffer is acessed by the gallium frontend again
+ */
+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_needsGPUFlush) {
+ 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 and CPU wait "
+ "on fenceValue: %d\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;
+ }
+
+ // Close and execute command list and wait for idle on CPU blocking
+ // this method before resetting list and allocator for next submission.
+
+ 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);
+ pD3D12Enc->m_spFence->SetEventOnCompletion(pD3D12Enc->m_fenceValue, nullptr);
+ debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - ExecuteCommandLists finished on signal with "
+ "fenceValue: %d\n",
+ pD3D12Enc->m_fenceValue);
+
+ hr = pD3D12Enc->m_spCommandAllocator->Reset();
+ if (FAILED(hr)) {
+ debug_printf(
+ "[d3d12_video_encoder] d3d12_video_encoder_flush - resetting ID3D12CommandAllocator failed with HR %x\n",
+ hr);
+ goto flush_fail;
+ }
+
+ hr = pD3D12Enc->m_spEncodeCommandList->Reset(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 flush_fail;
+ }
+
+ // 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;
+ }
+
+ debug_printf(
+ "[d3d12_video_encoder] d3d12_video_encoder_flush - GPU signaled execution finalized for fenceValue: %d\n",
+ pD3D12Enc->m_fenceValue);
+
+ pD3D12Enc->m_fenceValue++;
+ pD3D12Enc->m_needsGPUFlush = false;
+ }
+ return;
+
+flush_fail:
+ debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush failed for fenceValue: %d\n", pD3D12Enc->m_fenceValue);
+ 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;
+ }
+
+ d3d12_video_encoder_flush(codec); // Flush pending work before destroying.
+
+ struct d3d12_video_encoder *pD3D12Enc = (struct d3d12_video_encoder *) codec;
+
+ // 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;
+
+ default:
+ {
+ unreachable("Unsupported pipe_video_format");
+ } break;
+ }
+
+ pD3D12Enc->m_upDPBManager->begin_frame(currentPicParams, bUsedAsReference);
+}
+
+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);
+ 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
+ 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
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+ 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): %ld and InitialVBVFullness (bits) %ld 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 %ld 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;
+
+ 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;
+
+ 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;
+
+ 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;
+ }
+
+ hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandAllocator(
+ D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
+ IID_PPV_ARGS(pD3D12Enc->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;
+ }
+
+ hr =
+ pD3D12Enc->m_pD3D12Screen->dev->CreateCommandList(0,
+ D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
+ pD3D12Enc->m_spCommandAllocator.Get(),
+ nullptr,
+ 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;
+ }
+
+ D3D12_COMMAND_QUEUE_DESC copyQueueDesc = { D3D12_COMMAND_LIST_TYPE_COPY };
+ hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommandQueue(©QueueDesc,
+ IID_PPV_ARGS(pD3D12Enc->m_spCopyQueue.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;
+ }
+
+ 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->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;
+ }
+
+ d3d12_video_encoder_calculate_metadata_resolved_buffer_size(
+ pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput,
+ pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize);
+
+ D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
+ if ((pD3D12Enc->m_spResolvedMetadataBuffer == nullptr) ||
+ (pD3D12Enc->m_spResolvedMetadataBuffer->GetDesc().Width <
+ pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize)) {
+ CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
+ pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize);
+
+ HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
+ &Properties,
+ D3D12_HEAP_FLAG_NONE,
+ &resolvedMetadataBufferDesc,
+ D3D12_RESOURCE_STATE_COMMON,
+ nullptr,
+ IID_PPV_ARGS(pD3D12Enc->m_spResolvedMetadataBuffer.GetAddressOf()));
+
+ if (FAILED(hr)) {
+ debug_printf("CreateCommittedResource failed with HR %x\n", hr);
+ return false;
+ }
+ }
+
+ if ((pD3D12Enc->m_spMetadataOutputBuffer == nullptr) ||
+ (pD3D12Enc->m_spMetadataOutputBuffer->GetDesc().Width <
+ pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize)) {
+ CD3DX12_RESOURCE_DESC metadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(
+ pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.MaxEncoderOutputMetadataBufferSize);
+
+ HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource(
+ &Properties,
+ D3D12_HEAP_FLAG_NONE,
+ &metadataBufferDesc,
+ D3D12_RESOURCE_STATE_COMMON,
+ nullptr,
+ IID_PPV_ARGS(pD3D12Enc->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);
+ debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame started for fenceValue: %d\n",
+ pD3D12Enc->m_fenceValue);
+
+ 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;
+ }
+
+ debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame finalized for fenceValue: %d\n",
+ pD3D12Enc->m_fenceValue);
+ return;
+
+fail:
+ debug_printf("[d3d12_video_encoder] d3d12_video_encoder_begin_frame failed for fenceValue: %d\n",
+ pD3D12Enc->m_fenceValue);
+ assert(false);
+}
+
+void
+d3d12_video_encoder_calculate_metadata_resolved_buffer_size(uint32_t maxSliceNumber, size_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: %d\n",
+ pD3D12Enc->m_fenceValue);
+ assert(pD3D12Enc->m_spD3D12VideoDevice);
+ assert(pD3D12Enc->m_spEncodeCommandQueue);
+ assert(pD3D12Enc->m_pD3D12Screen);
+
+ 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);
+
+ ///
+ /// 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_BIND_INVALIDATE_FULL);
+ d3d12_transition_resource_state(d3d12_context(pD3D12Enc->base.context),
+ pOutputBitstreamBuffer, // d3d12_resource wrapped for pOutputBufferD3D12Res
+ D3D12_RESOURCE_STATE_COMMON,
+ D3D12_BIND_INVALIDATE_FULL);
+ 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_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);
+
+ // 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) {
+ picCtrlFlags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
+
+ 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(referenceFramesDescriptor.ppTexture2Ds[0]->GetDesc());
+
+ for (uint32_t referenceSubresource = 0; referenceSubresource < referencesTexArrayDesc.DepthOrArraySize;
+ referenceSubresource++) {
+
+ // all reference frames inputs should be all the same texarray allocation
+ assert(referenceFramesDescriptor.ppTexture2Ds[0] ==
+ referenceFramesDescriptor.ppTexture2Ds[referenceSubresource]);
+
+ // the reconpic output should be all the same texarray allocation
+ assert(referenceFramesDescriptor.ppTexture2Ds[0] == reconPicOutputTextureDesc.pReconstructedPicture);
+
+ 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);
+
+ 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_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());
+
+ 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_spResolvedMetadataBuffer.Get(),
+ D3D12_RESOURCE_STATE_COMMON,
+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE),
+ CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->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_spMetadataOutputBuffer.Get(), 0 }
+ };
+
+ const D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS outputMetadataCmd = {
+ { pD3D12Enc->m_spResolvedMetadataBuffer.Get(), 0 }
+ };
+ pD3D12Enc->m_spEncodeCommandList->ResolveEncoderOutputMetadata(&inputMetadataCmd, &outputMetadataCmd);
+
+ // 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_spResolvedMetadataBuffer.Get(),
+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
+ D3D12_RESOURCE_STATE_COMMON),
+ CD3DX12_RESOURCE_BARRIER::Transition(pD3D12Enc->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: %d\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);
+
+ if (pD3D12Enc->m_needsGPUFlush) {
+ d3d12_video_encoder_flush(codec);
+ }
+
+ D3D12_VIDEO_ENCODER_OUTPUT_METADATA encoderMetadata;
+ std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> pSubregionsMetadata;
+ d3d12_video_encoder_extract_encode_metadata(
+ pD3D12Enc,
+ pD3D12Enc->m_spResolvedMetadataBuffer.Get(),
+ pD3D12Enc->m_currentEncodeCapabilities.m_resolvedLayoutMetadataBufferRequiredSize,
+ 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 failed - EncodeErrorFlags: %ld\n",
+ encoderMetadata.EncodeErrorFlags);
+ *size = 0;
+ }
+
+ assert(encoderMetadata.EncodedBitstreamWrittenBytesCount > 0u);
+ *size = (pD3D12Enc->m_BitstreamHeadersBuffer.size() + encoderMetadata.EncodedBitstreamWrittenBytesCount);
+}
+
+void
+d3d12_video_encoder_extract_encode_metadata(
+ struct d3d12_video_encoder * pD3D12Enc,
+ ID3D12Resource * pResolvedMetadataBuffer, // input
+ size_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
+ size_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);
+}
+
+/**
+ * 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: %d\n",
+ pD3D12Enc->m_fenceValue);
+
+ // Signal finish of current frame encoding to the picture management tracker
+ pD3D12Enc->m_upDPBManager->end_frame();
+
+ debug_printf("[d3d12_video_encoder] d3d12_video_encoder_end_frame finalized for fenceValue: %d\n",
+ pD3D12Enc->m_fenceValue);
+
+ ///
+ /// Flush work to the GPU and blocking wait until encode finishes
+ ///
+ pD3D12Enc->m_needsGPUFlush = true;
+ d3d12_video_encoder_flush(codec);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef D3D12_VIDEO_ENC_H
+#define D3D12_VIDEO_ENC_H
+
+#include "d3d12_video_types.h"
+#include "d3d12_video_encoder_references_manager.h"
+#include "d3d12_video_dpb_storage_manager.h"
+#include "d3d12_video_encoder_bitstream_builder_h264.h"
+
+///
+/// Pipe video interface starts
+///
+
+/**
+ * creates a video encoder
+ */
+struct pipe_video_codec *
+d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *templ);
+
+/**
+ * destroy this video encoder
+ */
+void
+d3d12_video_encoder_destroy(struct pipe_video_codec *codec);
+
+/**
+ * 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);
+
+/**
+ * encode to a bitstream
+ */
+void
+d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
+ struct pipe_video_buffer *source,
+ struct pipe_resource * destination,
+ void ** feedback);
+
+/**
+ * get encoder feedback
+ */
+void
+d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, void *feedback, unsigned *size);
+
+/**
+ * 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);
+
+/**
+ * flush any outstanding command buffers to the hardware
+ * should be called before a video_buffer is acessed by the gallium frontend again
+ */
+void
+d3d12_video_encoder_flush(struct pipe_video_codec *codec);
+
+///
+/// Pipe video interface ends
+///
+
+enum d3d12_video_encoder_config_dirty_flags
+{
+ d3d12_video_encoder_config_dirty_flag_none = 0x0,
+ d3d12_video_encoder_config_dirty_flag_codec = 0x1,
+ d3d12_video_encoder_config_dirty_flag_profile = 0x2,
+ d3d12_video_encoder_config_dirty_flag_level = 0x4,
+ d3d12_video_encoder_config_dirty_flag_codec_config = 0x8,
+ d3d12_video_encoder_config_dirty_flag_input_format = 0x10,
+ d3d12_video_encoder_config_dirty_flag_resolution = 0x20,
+ d3d12_video_encoder_config_dirty_flag_rate_control = 0x40,
+ d3d12_video_encoder_config_dirty_flag_slices = 0x80,
+ d3d12_video_encoder_config_dirty_flag_gop = 0x100,
+ d3d12_video_encoder_config_dirty_flag_motion_precision_limit = 0x200,
+};
+DEFINE_ENUM_FLAG_OPERATORS(d3d12_video_encoder_config_dirty_flags);
+
+///
+/// d3d12_video_encoder functions starts
+///
+
+struct d3d12_video_encoder
+{
+ struct pipe_video_codec base;
+ struct pipe_screen * m_screen;
+ struct d3d12_screen * m_pD3D12Screen;
+
+ ///
+ /// D3D12 objects and context info
+ ///
+
+ const uint m_NodeMask = 0u;
+ const uint m_NodeIndex = 0u;
+
+ ComPtr<ID3D12Fence> m_spFence;
+ uint m_fenceValue = 1u;
+
+ ComPtr<ID3D12VideoDevice3> m_spD3D12VideoDevice;
+ ComPtr<ID3D12VideoEncoder> m_spVideoEncoder;
+ ComPtr<ID3D12VideoEncoderHeap> m_spVideoEncoderHeap;
+ ComPtr<ID3D12CommandQueue> m_spEncodeCommandQueue;
+ ComPtr<ID3D12CommandAllocator> m_spCommandAllocator;
+ ComPtr<ID3D12VideoEncodeCommandList2> m_spEncodeCommandList;
+ ComPtr<ID3D12CommandQueue> m_spCopyQueue;
+ std::vector<D3D12_RESOURCE_BARRIER> m_transitionsBeforeCloseCmdList;
+
+ std::unique_ptr<d3d12_video_encoder_references_manager_interface> m_upDPBManager;
+ std::unique_ptr<d3d12_video_dpb_storage_manager_interface> m_upDPBStorageManager;
+ std::unique_ptr<d3d12_video_bitstream_builder_interface> m_upBitstreamBuilder;
+
+ bool m_needsGPUFlush = false;
+
+ ComPtr<ID3D12Resource> m_spResolvedMetadataBuffer;
+ ComPtr<ID3D12Resource> m_spMetadataOutputBuffer;
+
+ std::vector<uint8_t> m_BitstreamHeadersBuffer;
+
+ struct
+ {
+ bool m_fArrayOfTexturesDpb;
+
+ D3D12_VIDEO_ENCODER_SUPPORT_FLAGS m_SupportFlags;
+ D3D12_VIDEO_ENCODER_VALIDATION_FLAGS m_ValidationFlags;
+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS m_currentResolutionSupportCaps;
+ union
+ {
+ D3D12_VIDEO_ENCODER_PROFILE_H264 m_H264Profile;
+ D3D12_VIDEO_ENCODER_PROFILE_HEVC m_HEVCProfile;
+ } m_encoderSuggestedProfileDesc = {};
+
+ union
+ {
+ D3D12_VIDEO_ENCODER_LEVELS_H264 m_H264LevelSetting;
+ D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC m_HEVCLevelSetting;
+ } m_encoderLevelSuggestedDesc = {};
+
+ // Required size for the layout-resolved metadata buffer of current frame to be encoded
+ size_t m_resolvedLayoutMetadataBufferRequiredSize;
+
+ // The maximum number of slices that the output of the current frame to be encoded will contain
+ uint32_t m_MaxSlicesInOutput;
+
+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS m_ResourceRequirementsCaps;
+
+ } m_currentEncodeCapabilities;
+
+ struct
+ {
+ d3d12_video_encoder_config_dirty_flags m_ConfigDirtyFlags = d3d12_video_encoder_config_dirty_flag_none;
+
+ D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC m_currentResolution = {};
+ D3D12_BOX m_FrameCroppingCodecConfig = {};
+
+ D3D12_FEATURE_DATA_FORMAT_INFO m_encodeFormatInfo = {};
+
+ D3D12_VIDEO_ENCODER_CODEC m_encoderCodecDesc = {};
+
+ D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS m_seqFlags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE;
+
+ /// As the following D3D12 Encode types have pointers in their structures, we need to keep a deep copy of them
+
+ union
+ {
+ D3D12_VIDEO_ENCODER_PROFILE_H264 m_H264Profile;
+ D3D12_VIDEO_ENCODER_PROFILE_HEVC m_HEVCProfile;
+ } m_encoderProfileDesc = {};
+
+ union
+ {
+ D3D12_VIDEO_ENCODER_LEVELS_H264 m_H264LevelSetting;
+ D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC m_HEVCLevelSetting;
+ } m_encoderLevelDesc = {};
+
+ struct
+ {
+ D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE m_Mode;
+ D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAGS m_Flags;
+ DXGI_RATIONAL m_FrameRate;
+ union
+ {
+ D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP m_Configuration_CQP;
+ D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR m_Configuration_CBR;
+ D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR m_Configuration_VBR;
+ D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR m_Configuration_QVBR;
+ } m_Config;
+ } m_encoderRateControlDesc = {};
+
+ union
+ {
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 m_H264Config;
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC m_HEVCConfig;
+ } m_encoderCodecSpecificConfigDesc = {};
+
+
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE m_encoderSliceConfigMode;
+ union
+ {
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES m_SlicesPartition_H264;
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES m_SlicesPartition_HEVC;
+ } m_encoderSliceConfigDesc = {};
+
+ union
+ {
+ D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 m_H264GroupOfPictures;
+ D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC m_HEVCGroupOfPictures;
+ } m_encoderGOPConfigDesc = {};
+
+ union
+ {
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 m_H264PicData;
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC m_HEVCPicData;
+ } m_encoderPicParamsDesc = {};
+
+ D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE m_encoderMotionPrecisionLimit =
+ D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM;
+
+ D3D12_VIDEO_ENCODER_INTRA_REFRESH m_IntraRefresh = { D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE, 0 };
+ uint32_t m_IntraRefreshCurrentFrameIndex = 0;
+
+ } m_currentEncodeConfig;
+};
+
+bool
+d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc);
+bool
+d3d12_video_encoder_reconfigure_session(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture);
+bool
+d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture);
+bool
+d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture);
+D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA
+d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encoder *pD3D12Enc);
+D3D12_VIDEO_ENCODER_LEVEL_SETTING
+d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc);
+D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION
+d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder *pD3D12Enc);
+D3D12_VIDEO_ENCODER_PROFILE_DESC
+d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder *pD3D12Enc);
+D3D12_VIDEO_ENCODER_RATE_CONTROL
+d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder *pD3D12Enc);
+D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA
+d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder *pD3D12Enc);
+D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE
+d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder *pD3D12Enc);
+uint32_t
+d3d12_video_encoder_get_current_max_dpb_capacity(struct d3d12_video_encoder *pD3D12Enc);
+void
+d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder *pD3D12Enc);
+void
+d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture);
+void
+d3d12_video_encoder_calculate_metadata_resolved_buffer_size(uint32_t maxSliceNumber, size_t &bufferSize);
+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);
+bool
+d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture);
+uint32_t
+d3d12_video_encoder_build_codec_headers(struct d3d12_video_encoder *pD3D12Enc);
+void
+d3d12_video_encoder_extract_encode_metadata(
+ struct d3d12_video_encoder * pD3D12Dec,
+ ID3D12Resource * pResolvedMetadataBuffer,
+ size_t resourceMetadataSize,
+ D3D12_VIDEO_ENCODER_OUTPUT_METADATA & encoderMetadata,
+ std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> &pSubregionsMetadata);
+
+D3D12_VIDEO_ENCODER_CODEC
+d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc);
+
+bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData);
+bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData);
+bool d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder *pD3D12Enc, D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode);
+
+///
+/// d3d12_video_encoder functions ends
+///
+
+#endif
--- /dev/null
+/*
+ * 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_video_enc.h"
+#include "d3d12_video_enc_h264.h"
+#include "util/u_video.h"
+#include "d3d12_screen.h"
+#include "d3d12_format.h"
+
+void
+d3d12_video_encoder_update_current_rate_control_h264(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture)
+{
+ auto previousConfig = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc;
+
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc = {};
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate.Numerator =
+ picture->rate_ctrl[0].frame_rate_num;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate.Denominator =
+ picture->rate_ctrl[0].frame_rate_den;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags = D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE;
+
+ switch (picture->rate_ctrl[0].rate_ctrl_method) {
+ case PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE_SKIP:
+ case PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE:
+ {
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.TargetAvgBitRate =
+ picture->rate_ctrl[0].target_bitrate;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.PeakBitRate =
+ picture->rate_ctrl[0].peak_bitrate;
+ } break;
+ case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT_SKIP:
+ case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT:
+ {
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate =
+ picture->rate_ctrl[0].target_bitrate;
+
+ /* For CBR mode, to guarantee bitrate of generated stream complies with
+ * target bitrate (e.g. no over +/-10%), vbv_buffer_size should be same
+ * as target bitrate. Controlled by OS env var D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE
+ */
+ if (D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE) {
+ debug_printf("[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE environment variable is set, "
+ ", forcing VBV Size = Target Bitrate = %ld (bits)\n", pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate);
+ 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 =
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate;
+ }
+
+ } break;
+ case PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE:
+ {
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
+ .ConstantQP_FullIntracodedFrame = picture->quant_i_frames;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
+ .ConstantQP_InterPredictedFrame_PrevRefOnly = picture->quant_p_frames;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
+ .ConstantQP_InterPredictedFrame_BiDirectionalRef = picture->quant_b_frames;
+ } break;
+ default:
+ {
+ debug_printf("[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 invalid RC "
+ "config, using default RC CQP mode\n");
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
+ .ConstantQP_FullIntracodedFrame = 30;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
+ .ConstantQP_InterPredictedFrame_PrevRefOnly = 30;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
+ .ConstantQP_InterPredictedFrame_BiDirectionalRef = 30;
+ } break;
+ }
+
+ if (memcmp(&previousConfig,
+ &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc,
+ sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc)) != 0) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_rate_control;
+ }
+}
+
+void
+d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer *srcTexture,
+ struct pipe_picture_desc *picture,
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &picParams,
+ bool &bUsedAsReference)
+{
+ struct pipe_h264_enc_picture_desc *h264Pic = (struct pipe_h264_enc_picture_desc *) picture;
+ d3d12_video_bitstream_builder_h264 *pH264BitstreamBuilder =
+ dynamic_cast<d3d12_video_bitstream_builder_h264 *>(pD3D12Enc->m_upBitstreamBuilder.get());
+ assert(pH264BitstreamBuilder != nullptr);
+
+ bUsedAsReference = !h264Pic->not_referenced;
+
+ picParams.pH264PicData->pic_parameter_set_id = pH264BitstreamBuilder->get_active_pps_id();
+ picParams.pH264PicData->idr_pic_id = h264Pic->idr_pic_id;
+ picParams.pH264PicData->FrameType = d3d12_video_encoder_convert_frame_type(h264Pic->picture_type);
+ picParams.pH264PicData->PictureOrderCountNumber = h264Pic->pic_order_cnt;
+ picParams.pH264PicData->FrameDecodingOrderNumber = h264Pic->frame_num;
+
+ picParams.pH264PicData->List0ReferenceFramesCount = 0;
+ picParams.pH264PicData->pList0ReferenceFrames = nullptr;
+ picParams.pH264PicData->List1ReferenceFramesCount = 0;
+ picParams.pH264PicData->pList1ReferenceFrames = nullptr;
+
+ if (picParams.pH264PicData->FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME) {
+ picParams.pH264PicData->List0ReferenceFramesCount = h264Pic->num_ref_idx_l0_active_minus1 + 1;
+ picParams.pH264PicData->pList0ReferenceFrames = h264Pic->ref_idx_l0_list;
+ } else if (picParams.pH264PicData->FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME) {
+ picParams.pH264PicData->List0ReferenceFramesCount = h264Pic->num_ref_idx_l0_active_minus1 + 1;
+ picParams.pH264PicData->pList0ReferenceFrames = h264Pic->ref_idx_l0_list;
+ picParams.pH264PicData->List1ReferenceFramesCount = h264Pic->num_ref_idx_l1_active_minus1 + 1;
+ picParams.pH264PicData->pList1ReferenceFrames = h264Pic->ref_idx_l1_list;
+ }
+}
+
+D3D12_VIDEO_ENCODER_FRAME_TYPE_H264
+d3d12_video_encoder_convert_frame_type(enum pipe_h2645_enc_picture_type picType)
+{
+ switch (picType) {
+ case PIPE_H2645_ENC_PICTURE_TYPE_P:
+ {
+ return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME;
+ } break;
+ case PIPE_H2645_ENC_PICTURE_TYPE_B:
+ {
+ return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME;
+ } break;
+ case PIPE_H2645_ENC_PICTURE_TYPE_I:
+ {
+ return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_I_FRAME;
+ } break;
+ case PIPE_H2645_ENC_PICTURE_TYPE_IDR:
+ {
+ return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME;
+ } break;
+ default:
+ {
+ unreachable("Unsupported pipe_h2645_enc_picture_type");
+ } break;
+ }
+}
+
+///
+/// Tries to configurate the encoder using the requested slice configuration
+/// or falls back to single slice encoding.
+///
+bool
+d3d12_video_encoder_negotiate_current_h264_slices_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture)
+{
+ ///
+ /// Initialize single slice by default
+ ///
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode =
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME;
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES requestedSlicesConfig = {};
+ requestedSlicesConfig.NumberOfSlicesPerFrame = 1;
+
+ ///
+ /// Try to see if can accomodate for multi-slice request by user
+ ///
+ if (picture->num_slice_descriptors > 1) {
+ /* Last slice can be less for rounding frame size and leave some error for mb rounding */
+ bool bUniformSizeSlices = true;
+ const double rounding_delta = 1.0;
+ for (uint32_t sliceIdx = 1; (sliceIdx < picture->num_slice_descriptors - 1) && bUniformSizeSlices; sliceIdx++) {
+ int64_t curSlice = picture->slices_descriptors[sliceIdx].num_macroblocks;
+ int64_t prevSlice = picture->slices_descriptors[sliceIdx - 1].num_macroblocks;
+ bUniformSizeSlices = bUniformSizeSlices && (std::abs(curSlice - prevSlice) <= rounding_delta);
+ }
+
+ uint32_t mbPerScanline =
+ pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width / D3D12_VIDEO_H264_MB_IN_PIXELS;
+ bool bSliceAligned = ((picture->slices_descriptors[0].num_macroblocks % mbPerScanline) == 0);
+
+ if (!bUniformSizeSlices &&
+ d3d12_video_encoder_check_subregion_mode_support(
+ pD3D12Enc,
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME)) {
+
+ if (D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG) { // Check if fallback mode is enabled, or we should just fail
+ // without support
+ // Not supported to have custom slice sizes in D3D12 Video Encode fallback to uniform multi-slice
+ debug_printf(
+ "[d3d12_video_encoder_h264] WARNING: Requested slice control mode is not supported: All slices must "
+ "have the same number of macroblocks. Falling back to encoding uniform %d slices per frame.\n",
+ picture->num_slice_descriptors);
+ requestedSlicesMode =
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME;
+ requestedSlicesConfig.NumberOfSlicesPerFrame = picture->num_slice_descriptors;
+ debug_printf("[d3d12_video_encoder_h264] Using multi slice encoding mode: "
+ "D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME "
+ "with %d slices per frame.\n",
+ requestedSlicesConfig.NumberOfSlicesPerFrame);
+ } else {
+ debug_printf("[d3d12_video_encoder_h264] Requested slice control mode is not supported: All slices must "
+ "have the same number of macroblocks. To continue with uniform slices as a fallback, must "
+ "enable the OS environment variable D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG");
+ return false;
+ }
+ } else if (bUniformSizeSlices && bSliceAligned &&
+ d3d12_video_encoder_check_subregion_mode_support(
+ pD3D12Enc,
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION)) {
+
+ // Number of macroblocks per slice is aligned to a scanline width, in which case we can
+ // use D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION
+ requestedSlicesMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION;
+ requestedSlicesConfig.NumberOfRowsPerSlice = (picture->slices_descriptors[0].num_macroblocks / mbPerScanline);
+ debug_printf("[d3d12_video_encoder_h264] Using multi slice encoding mode: "
+ "D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION with "
+ "%d macroblocks rows per slice.\n",
+ requestedSlicesConfig.NumberOfRowsPerSlice);
+ } else if (bUniformSizeSlices &&
+ d3d12_video_encoder_check_subregion_mode_support(
+ pD3D12Enc,
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME)) {
+ requestedSlicesMode =
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME;
+ requestedSlicesConfig.NumberOfSlicesPerFrame = picture->num_slice_descriptors;
+ debug_printf("[d3d12_video_encoder_h264] Using multi slice encoding mode: "
+ "D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME "
+ "with %d slices per frame.\n",
+ requestedSlicesConfig.NumberOfSlicesPerFrame);
+ } else if (D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG) { // Check if fallback mode is enabled, or we should just fail
+ // without support
+ // Fallback to single slice encoding (assigned by default when initializing variables requestedSlicesMode,
+ // requestedSlicesConfig)
+ debug_printf(
+ "[d3d12_video_encoder_h264] WARNING: Slice mode for %d slices with bUniformSizeSlices: %d - bSliceAligned "
+ "%d not supported by the D3D12 driver, falling back to encoding a single slice per frame.\n",
+ picture->num_slice_descriptors,
+ bUniformSizeSlices,
+ bSliceAligned);
+ } else {
+ debug_printf("[d3d12_video_encoder_h264] Requested slice control mode is not supported: All slices must "
+ "have the same number of macroblocks. To continue with uniform slices as a fallback, must "
+ "enable the OS environment variable D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG");
+ return false;
+ }
+ }
+
+ if (!d3d12_video_encoder_compare_slice_config_h264_hevc(
+ pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
+ pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264,
+ requestedSlicesMode,
+ requestedSlicesConfig)) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_slices;
+ }
+
+ pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264 = requestedSlicesConfig;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode = requestedSlicesMode;
+
+ return true;
+}
+
+D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE
+d3d12_video_encoder_convert_h264_motion_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture)
+{
+ return D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM;
+}
+
+D3D12_VIDEO_ENCODER_LEVELS_H264
+d3d12_video_encoder_convert_level_h264(uint32_t h264SpecLevel)
+{
+ switch (h264SpecLevel) {
+ case 10:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_1;
+ } break;
+ case 11:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_11;
+ } break;
+ case 12:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_12;
+ } break;
+ case 13:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_13;
+ } break;
+ case 20:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_2;
+ } break;
+ case 21:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_21;
+ } break;
+ case 22:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_22;
+ } break;
+ case 30:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_3;
+ } break;
+ case 31:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_31;
+ } break;
+ case 32:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_32;
+ } break;
+ case 40:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_4;
+ } break;
+ case 41:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_41;
+ } break;
+ case 42:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_42;
+ } break;
+ case 50:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_5;
+ } break;
+ case 51:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_51;
+ } break;
+ case 52:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_52;
+ } break;
+ case 60:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_6;
+ } break;
+ case 61:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_61;
+ } break;
+ case 62:
+ {
+ return D3D12_VIDEO_ENCODER_LEVELS_H264_62;
+ } break;
+ default:
+ {
+ unreachable("Unsupported H264 level");
+ } break;
+ }
+}
+
+void
+d3d12_video_encoder_convert_from_d3d12_level_h264(D3D12_VIDEO_ENCODER_LEVELS_H264 level12,
+ uint32_t &specLevel,
+ uint32_t &constraint_set3_flag)
+{
+ specLevel = 0;
+ constraint_set3_flag = 0;
+
+ switch (level12) {
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_1:
+ {
+ specLevel = 10;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_1b:
+ {
+ specLevel = 11;
+ constraint_set3_flag = 1;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_11:
+ {
+ specLevel = 11;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_12:
+ {
+ specLevel = 12;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_13:
+ {
+ specLevel = 13;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_2:
+ {
+ specLevel = 20;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_21:
+ {
+ specLevel = 21;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_22:
+ {
+ specLevel = 22;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_3:
+ {
+ specLevel = 30;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_31:
+ {
+ specLevel = 31;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_32:
+ {
+ specLevel = 32;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_4:
+ {
+ specLevel = 40;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_41:
+ {
+ specLevel = 41;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_42:
+ {
+ specLevel = 42;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_5:
+ {
+ specLevel = 50;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_51:
+ {
+ specLevel = 51;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_52:
+ {
+ specLevel = 52;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_6:
+ {
+ specLevel = 60;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_61:
+ {
+ specLevel = 61;
+ } break;
+ case D3D12_VIDEO_ENCODER_LEVELS_H264_62:
+ {
+ specLevel = 62;
+ } break;
+ default:
+ {
+ unreachable("Unsupported D3D12_VIDEO_ENCODER_LEVELS_H264 value");
+ } break;
+ }
+}
+
+bool
+d3d12_video_encoder_update_h264_gop_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture)
+{
+ // Only update GOP when it begins
+ if (picture->gop_cnt == 1) {
+ uint32_t GOPCoeff = picture->i_remain;
+ uint32_t GOPLength = picture->gop_size / GOPCoeff;
+ uint32_t PPicturePeriod = std::ceil(GOPLength / (double) (picture->p_remain / GOPCoeff)) - 1;
+
+ if (picture->pic_order_cnt_type == 1u) {
+ debug_printf("[d3d12_video_encoder_h264] Upper layer is requesting pic_order_cnt_type %d but D3D12 Video "
+ "only supports pic_order_cnt_type = 0 or pic_order_cnt_type = 2\n",
+ picture->pic_order_cnt_type);
+ return false;
+ }
+
+ const uint32_t max_pic_order_cnt_lsb = 2 * GOPLength;
+ const uint32_t max_max_frame_num = GOPLength;
+ double log2_max_frame_num_minus4 = std::max(0.0, std::ceil(std::log2(max_max_frame_num)) - 4);
+ double log2_max_pic_order_cnt_lsb_minus4 = std::max(0.0, std::ceil(std::log2(max_pic_order_cnt_lsb)) - 4);
+ assert(log2_max_frame_num_minus4 < UCHAR_MAX);
+ assert(log2_max_pic_order_cnt_lsb_minus4 < UCHAR_MAX);
+ assert(picture->pic_order_cnt_type < UCHAR_MAX);
+
+ // Set dirty flag if m_H264GroupOfPictures changed
+ auto previousGOPConfig = pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures;
+ pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures = {
+ GOPLength,
+ PPicturePeriod,
+ static_cast<uint8_t>(picture->pic_order_cnt_type),
+ static_cast<uint8_t>(log2_max_frame_num_minus4),
+ static_cast<uint8_t>(log2_max_pic_order_cnt_lsb_minus4)
+ };
+
+ if (memcmp(&previousGOPConfig,
+ &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures,
+ sizeof(D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264)) != 0) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_gop;
+ }
+ }
+ return true;
+}
+
+D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264
+d3d12_video_encoder_convert_h264_codec_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture)
+{
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 config = {
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_NONE,
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_DIRECT_MODES_DISABLED,
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_0_ALL_LUMA_CHROMA_SLICE_BLOCK_EDGES_ALWAYS_FILTERED,
+ };
+
+ if (picture->pic_ctrl.enc_cabac_enable) {
+ config.ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_ENABLE_CABAC_ENCODING;
+ }
+
+ return config;
+}
+
+bool
+d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer *srcTexture,
+ struct pipe_picture_desc *picture)
+{
+ struct pipe_h264_enc_picture_desc *h264Pic = (struct pipe_h264_enc_picture_desc *) picture;
+
+ // Reset reconfig dirty flags
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags = d3d12_video_encoder_config_dirty_flag_none;
+ // Reset sequence changes flags
+ pD3D12Enc->m_currentEncodeConfig.m_seqFlags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE;
+
+ // Set codec
+ if (pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc != D3D12_VIDEO_ENCODER_CODEC_H264) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_codec;
+ }
+ pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc = D3D12_VIDEO_ENCODER_CODEC_H264;
+
+ // Set input format
+ DXGI_FORMAT targetFmt = d3d12_convert_pipe_video_profile_to_dxgi_format(pD3D12Enc->base.profile);
+ if (pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format != targetFmt) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_input_format;
+ }
+
+ pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo = {};
+ pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format = targetFmt;
+ HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO,
+ &pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo,
+ sizeof(pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo));
+ if (FAILED(hr)) {
+ debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
+ return false;
+ }
+
+ // Set resolution
+ if ((pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width != srcTexture->width) ||
+ (pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height != srcTexture->height)) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_resolution;
+ }
+ pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width = srcTexture->width;
+ pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height = srcTexture->height;
+
+ // Set resolution codec dimensions (ie. cropping)
+ if (h264Pic->pic_ctrl.enc_frame_cropping_flag) {
+ pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.left = h264Pic->pic_ctrl.enc_frame_crop_left_offset;
+ pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.right = h264Pic->pic_ctrl.enc_frame_crop_right_offset;
+ pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.top = h264Pic->pic_ctrl.enc_frame_crop_top_offset;
+ pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.bottom =
+ h264Pic->pic_ctrl.enc_frame_crop_bottom_offset;
+ } else {
+ memset(&pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig,
+ 0,
+ sizeof(pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig));
+ }
+
+ // Set profile
+ auto targetProfile = d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_h264(pD3D12Enc->base.profile);
+ if (pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile != targetProfile) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_profile;
+ }
+ pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile = targetProfile;
+
+ // Set level
+ auto targetLevel = d3d12_video_encoder_convert_level_h264(pD3D12Enc->base.level);
+ if (pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting != targetLevel) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_level;
+ }
+ pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting = targetLevel;
+
+ // Set codec config
+ auto targetCodecConfig = d3d12_video_encoder_convert_h264_codec_configuration(pD3D12Enc, h264Pic);
+ if (memcmp(&pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config,
+ &targetCodecConfig,
+ sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264)) != 0) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_codec_config;
+ }
+ pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config = targetCodecConfig;
+
+ // Set rate control
+ d3d12_video_encoder_update_current_rate_control_h264(pD3D12Enc, h264Pic);
+
+ // Set slices config
+ if(!d3d12_video_encoder_negotiate_current_h264_slices_configuration(pD3D12Enc, h264Pic)) {
+ debug_printf("d3d12_video_encoder_negotiate_current_h264_slices_configuration failed!\n");
+ return false;
+ }
+
+ // Set GOP config
+ if(!d3d12_video_encoder_update_h264_gop_configuration(pD3D12Enc, h264Pic)) {
+ debug_printf("d3d12_video_encoder_update_h264_gop_configuration failed!\n");
+ return false;
+ }
+
+ // m_currentEncodeConfig.m_encoderPicParamsDesc pic params are set in d3d12_video_encoder_reconfigure_encoder_objects
+ // after re-allocating objects if needed
+
+ // Set motion estimation config
+ auto targetMotionLimit = d3d12_video_encoder_convert_h264_motion_configuration(pD3D12Enc, h264Pic);
+ if (pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit != targetMotionLimit) {
+ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |=
+ d3d12_video_encoder_config_dirty_flag_motion_precision_limit;
+ }
+ pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit = targetMotionLimit;
+
+ ///
+ /// Check for video encode support detailed capabilities
+ ///
+
+ // Will call for d3d12 driver support based on the initial requested features, then
+ // try to fallback if any of them is not supported and return the negotiated d3d12 settings
+ D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT capEncoderSupportData = {};
+ if (!d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(pD3D12Enc, capEncoderSupportData)) {
+ debug_printf("[d3d12_video_encoder_h264] After negotiating caps, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT "
+ "arguments are not supported - "
+ "ValidationFlags: 0x%x - SupportFlags: 0x%x\n",
+ capEncoderSupportData.ValidationFlags,
+ capEncoderSupportData.SupportFlags);
+ return false;
+ }
+
+ ///
+ // Calculate current settings based on the returned values from the caps query
+ //
+ pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput =
+ d3d12_video_encoder_calculate_max_slices_count_in_output(
+ pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
+ &pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264,
+ pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxSubregionsNumber,
+ pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
+ pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.SubregionBlockPixelsSize);
+
+ //
+ // Validate caps support returned values against current settings
+ //
+ if (pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile !=
+ pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile) {
+ debug_printf("[d3d12_video_encoder_h264] Warning: Requested D3D12_VIDEO_ENCODER_PROFILE_H264 by upper layer: %d "
+ "mismatches UMD suggested D3D12_VIDEO_ENCODER_PROFILE_H264: %d\n",
+ pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile,
+ pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile);
+ }
+
+ if (pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting !=
+ pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting) {
+ debug_printf("[d3d12_video_encoder_h264] Warning: Requested D3D12_VIDEO_ENCODER_LEVELS_H264 by upper layer: %d "
+ "mismatches UMD suggested D3D12_VIDEO_ENCODER_LEVELS_H264: %d\n",
+ pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting,
+ pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting);
+ }
+
+ if (pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput >
+ pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxSubregionsNumber) {
+ debug_printf("[d3d12_video_encoder_h264] Desired number of subregions is not supported (higher than max "
+ "reported slice number in query caps)\n.");
+ return false;
+ }
+ return true;
+}
+
+D3D12_VIDEO_ENCODER_PROFILE_H264
+d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_h264(enum pipe_video_profile profile)
+{
+ switch (profile) {
+ case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
+ case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
+ case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
+ {
+ return D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN;
+
+ } break;
+ case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
+ {
+ return D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH;
+ } break;
+ case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
+ {
+ return D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10;
+ } break;
+ default:
+ {
+ unreachable("Unsupported pipe_video_profile");
+ } break;
+ }
+}
+
+D3D12_VIDEO_ENCODER_CODEC
+d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(enum pipe_video_profile profile)
+{
+ switch (u_reduce_video_profile(profile)) {
+ 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;
+ case PIPE_VIDEO_FORMAT_MPEG12:
+ case PIPE_VIDEO_FORMAT_MPEG4:
+ case PIPE_VIDEO_FORMAT_VC1:
+ case PIPE_VIDEO_FORMAT_JPEG:
+ case PIPE_VIDEO_FORMAT_VP9:
+ case PIPE_VIDEO_FORMAT_UNKNOWN:
+ default:
+ {
+ unreachable("Unsupported pipe_video_profile");
+ } break;
+ }
+}
+
+bool
+d3d12_video_encoder_compare_slice_config_h264_hevc(
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE targetMode,
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES targetConfig,
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE otherMode,
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES otherConfig)
+{
+ return (targetMode == otherMode) &&
+ (memcmp(&targetConfig,
+ &otherConfig,
+ sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES)) == 0);
+}
+
+uint32_t
+d3d12_video_encoder_build_codec_headers_h264(struct d3d12_video_encoder *pD3D12Enc)
+{
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
+ d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
+
+ auto profDesc = d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
+ auto levelDesc = d3d12_video_encoder_get_current_level_desc(pD3D12Enc);
+ auto codecConfigDesc = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc);
+ auto MaxDPBCapacity = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
+
+ size_t writtenSPSBytesCount = 0;
+ bool isFirstFrame = (pD3D12Enc->m_fenceValue == 1);
+ bool writeNewSPS = isFirstFrame // on first frame
+ || ((pD3D12Enc->m_currentEncodeConfig.m_seqFlags & // also on resolution change
+ D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RESOLUTION_CHANGE) != 0);
+
+ d3d12_video_bitstream_builder_h264 *pH264BitstreamBuilder =
+ dynamic_cast<d3d12_video_bitstream_builder_h264 *>(pD3D12Enc->m_upBitstreamBuilder.get());
+ assert(pH264BitstreamBuilder);
+
+ uint32_t active_seq_parameter_set_id = pH264BitstreamBuilder->get_active_sps_id();
+
+ if (writeNewSPS) {
+ // For every new SPS for reconfiguration, increase the active_sps_id
+ if (!isFirstFrame) {
+ active_seq_parameter_set_id++;
+ pH264BitstreamBuilder->set_active_sps_id(active_seq_parameter_set_id);
+ }
+ pH264BitstreamBuilder->build_sps(*profDesc.pH264Profile,
+ *levelDesc.pH264LevelSetting,
+ pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
+ *codecConfigDesc.pH264Config,
+ pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures,
+ active_seq_parameter_set_id,
+ MaxDPBCapacity, // max_num_ref_frames
+ pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
+ pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig,
+ pD3D12Enc->m_BitstreamHeadersBuffer,
+ pD3D12Enc->m_BitstreamHeadersBuffer.begin(),
+ writtenSPSBytesCount);
+ }
+
+ size_t writtenPPSBytesCount = 0;
+ pH264BitstreamBuilder->build_pps(*profDesc.pH264Profile,
+ *codecConfigDesc.pH264Config,
+ *currentPicParams.pH264PicData,
+ currentPicParams.pH264PicData->pic_parameter_set_id,
+ active_seq_parameter_set_id,
+ pD3D12Enc->m_BitstreamHeadersBuffer,
+ pD3D12Enc->m_BitstreamHeadersBuffer.begin() + writtenSPSBytesCount,
+ writtenPPSBytesCount);
+
+ // Shrink buffer to fit the headers
+ if (pD3D12Enc->m_BitstreamHeadersBuffer.size() > (writtenPPSBytesCount + writtenSPSBytesCount)) {
+ pD3D12Enc->m_BitstreamHeadersBuffer.resize(writtenPPSBytesCount + writtenSPSBytesCount);
+ }
+
+ return pD3D12Enc->m_BitstreamHeadersBuffer.size();
+}
--- /dev/null
+
+/*
+ * 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.
+ */
+
+#ifndef D3D12_VIDEO_ENC_H264_H
+#define D3D12_VIDEO_ENC_H264_H
+#include "d3d12_video_types.h"
+
+bool
+d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture);
+void
+d3d12_video_encoder_update_current_rate_control_h264(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture);
+bool
+d3d12_video_encoder_negotiate_current_h264_slices_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture);
+bool
+d3d12_video_encoder_update_h264_gop_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture);
+D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE
+d3d12_video_encoder_convert_h264_motion_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture);
+D3D12_VIDEO_ENCODER_LEVELS_H264
+d3d12_video_encoder_convert_level_h264(uint32_t h264SpecLevel);
+D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264
+d3d12_video_encoder_convert_h264_codec_configuration(struct d3d12_video_encoder *pD3D12Enc,
+ pipe_h264_enc_picture_desc *picture);
+void
+d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video_encoder *pD3D12Enc,
+ struct pipe_video_buffer * srcTexture,
+ struct pipe_picture_desc * picture,
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &picParams,
+ bool &bUsedAsReference);
+D3D12_VIDEO_ENCODER_FRAME_TYPE_H264
+d3d12_video_encoder_convert_frame_type(enum pipe_h2645_enc_picture_type picType);
+uint32_t
+d3d12_video_encoder_build_codec_headers_h264(struct d3d12_video_encoder *pD3D12Enc);
+bool
+d3d12_video_encoder_compare_slice_config_h264_hevc(
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE targetMode,
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES targetConfig,
+ D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE otherMode,
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES otherConfig);
+
+#endif
--- /dev/null
+/*
+ * 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 <climits>
+#include "d3d12_video_encoder_bitstream.h"
+
+d3d12_video_encoder_bitstream::d3d12_video_encoder_bitstream()
+{
+ m_pBitsBuffer = nullptr;
+ m_uiBitsBufferSize = 0;
+ m_iBitsToGo = 32;
+ m_uintEncBuffer = 0;
+ m_bExternalBuffer = false;
+ m_bBufferOverflow = false;
+ m_bPreventStartCode = false;
+ m_bAllowReallocate = false;
+}
+
+d3d12_video_encoder_bitstream::~d3d12_video_encoder_bitstream()
+{
+ if (!m_bExternalBuffer) {
+ if (m_pBitsBuffer) {
+ delete[](m_pBitsBuffer);
+ (m_pBitsBuffer) = NULL;
+ }
+ }
+}
+
+int32_t
+d3d12_video_encoder_bitstream::get_exp_golomb0_code_len(uint32_t uiVal)
+{
+ int32_t iLen = 0;
+ uiVal++;
+
+ if (uiVal >= 0x10000) {
+ uiVal >>= 16;
+ iLen += 16;
+ }
+ if (uiVal >= 0x100) {
+ uiVal >>= 8;
+ iLen += 8;
+ }
+
+ assert(uiVal < 256);
+
+ return iLen + m_iLog_2_N[uiVal];
+}
+
+void
+d3d12_video_encoder_bitstream::exp_Golomb_ue(uint32_t uiVal)
+{
+ if (uiVal != UINT32_MAX) {
+ int32_t iLen = get_exp_golomb0_code_len(uiVal);
+ put_bits((iLen << 1) + 1, uiVal + 1);
+ } else {
+ put_bits(32, 0);
+ put_bits(1, 1);
+ put_bits(32, 1);
+ }
+}
+
+void
+d3d12_video_encoder_bitstream::exp_Golomb_se(int32_t iVal)
+{
+ if (iVal > 0) {
+ exp_Golomb_ue((iVal << 1) - 1);
+ } else {
+ exp_Golomb_ue(((-iVal) << 1) - (iVal == INT_MIN));
+ }
+}
+
+void
+d3d12_video_encoder_bitstream::setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer)
+{
+ m_pBitsBuffer = pBuffer;
+ m_uiBitsBufferSize = uiInitBufferSize;
+ m_uiOffset = 0;
+ memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
+ m_bExternalBuffer = true;
+ m_bAllowReallocate = false;
+}
+
+bool
+d3d12_video_encoder_bitstream::create_bitstream(uint32_t uiInitBufferSize)
+{
+ assert((uiInitBufferSize) >= 4 && !(uiInitBufferSize & 3));
+
+ m_pBitsBuffer = (uint8_t *) new uint8_t[uiInitBufferSize];
+
+ if (nullptr == m_pBitsBuffer) {
+ return false;
+ }
+
+ m_uiBitsBufferSize = uiInitBufferSize;
+ m_uiOffset = 0;
+ memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
+ m_bExternalBuffer = false;
+
+ return true;
+}
+
+bool
+d3d12_video_encoder_bitstream::reallocate_buffer()
+{
+ uint32_t uiBufferSize = m_uiBitsBufferSize * 3 / 2;
+ uint8_t *pNewBuffer = (uint8_t *) new uint8_t[uiBufferSize];
+
+ if (nullptr == pNewBuffer) {
+ return false;
+ }
+
+ memcpy(pNewBuffer, m_pBitsBuffer, m_uiOffset * sizeof(uint8_t));
+ if (m_pBitsBuffer) {
+ delete[](m_pBitsBuffer);
+ (m_pBitsBuffer) = NULL;
+ }
+ m_pBitsBuffer = pNewBuffer;
+ m_uiBitsBufferSize = uiBufferSize;
+ return true;
+}
+
+bool
+d3d12_video_encoder_bitstream::verify_buffer(uint32_t uiBytesToWrite)
+{
+ if (!m_bBufferOverflow) {
+ if (m_uiOffset + uiBytesToWrite > m_uiBitsBufferSize) {
+ if (!m_bAllowReallocate || !reallocate_buffer()) {
+ m_bBufferOverflow = true;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void
+d3d12_video_encoder_bitstream::inc_current_offset(int32_t dwOffset)
+{
+ assert(32 == m_iBitsToGo && m_uiOffset < m_uiBitsBufferSize);
+ m_uiOffset += dwOffset;
+}
+
+void
+d3d12_video_encoder_bitstream::get_current_buffer_position_and_size(uint8_t **ppCurrBufPos, int32_t *pdwLeftBufSize)
+{
+ assert(32 == m_iBitsToGo && m_uiOffset < m_uiBitsBufferSize);
+ *ppCurrBufPos = m_pBitsBuffer + m_uiOffset;
+ *pdwLeftBufSize = m_uiBitsBufferSize - m_uiOffset;
+}
+
+void
+d3d12_video_encoder_bitstream::attach(uint8_t *pBitsBuffer, uint32_t uiBufferSize)
+{
+ m_pBitsBuffer = pBitsBuffer;
+ m_uiBitsBufferSize = uiBufferSize;
+ m_bExternalBuffer = true;
+ m_bBufferOverflow = false;
+ m_bAllowReallocate = false;
+
+ clear();
+}
+
+void
+d3d12_video_encoder_bitstream::write_byte_start_code_prevention(uint8_t u8Val)
+{
+ int32_t iOffset = m_uiOffset;
+ uint8_t *pBuffer = m_pBitsBuffer + iOffset;
+
+ if (m_bPreventStartCode && iOffset > 1) {
+ if (((u8Val & 0xfc) | pBuffer[-2] | pBuffer[-1]) == 0) {
+ *pBuffer++ = 3;
+ iOffset++;
+ }
+ }
+
+ *pBuffer = u8Val;
+ iOffset++;
+
+ m_uiOffset = iOffset;
+}
+
+#define WRITE_BYTE(byte) write_byte_start_code_prevention(byte)
+
+void
+d3d12_video_encoder_bitstream::put_bits(int32_t uiBitsCount, uint32_t iBitsVal)
+{
+ assert(uiBitsCount <= 32);
+
+ if (uiBitsCount < m_iBitsToGo) {
+ m_uintEncBuffer |= (iBitsVal << (m_iBitsToGo - uiBitsCount));
+ m_iBitsToGo -= uiBitsCount;
+ } else if (verify_buffer(4)) {
+ int32_t iLeftOverBits = uiBitsCount - m_iBitsToGo;
+ m_uintEncBuffer |= (iBitsVal >> iLeftOverBits);
+
+ uint8_t *temp = (uint8_t *) (&m_uintEncBuffer);
+ WRITE_BYTE(*(temp + 3));
+ WRITE_BYTE(*(temp + 2));
+ WRITE_BYTE(*(temp + 1));
+ WRITE_BYTE(*temp);
+
+ m_uintEncBuffer = 0;
+ m_iBitsToGo = 32 - iLeftOverBits;
+
+ if (iLeftOverBits > 0) {
+ m_uintEncBuffer = (iBitsVal << (32 - iLeftOverBits));
+ }
+ }
+}
+
+void
+d3d12_video_encoder_bitstream::flush()
+{
+ bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
+ assert(isAligned);
+
+ uint32_t temp = (uint32_t)(32 - m_iBitsToGo);
+
+ if (!verify_buffer(temp >> 3)) {
+ return;
+ }
+
+ while (temp > 0) {
+ WRITE_BYTE((uint8_t)(m_uintEncBuffer >> 24));
+ m_uintEncBuffer <<= 8;
+ temp -= 8;
+ }
+
+ m_iBitsToGo = 32;
+ m_uintEncBuffer = 0;
+}
+
+void
+d3d12_video_encoder_bitstream::append_byte_stream(d3d12_video_encoder_bitstream *pStream)
+{
+ bool isStreamAligned =
+ pStream->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
+ assert(isStreamAligned);
+ bool isThisAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
+ assert(isThisAligned);
+ assert(m_iBitsToGo == 32);
+
+ uint8_t *pDst = m_pBitsBuffer + m_uiOffset;
+ uint8_t *pSrc = pStream->get_bitstream_buffer();
+ uint32_t uiLen = (uint32_t) pStream->get_byte_count();
+
+ if (!verify_buffer(uiLen)) {
+ return;
+ }
+
+ memcpy(pDst, pSrc, uiLen);
+ m_uiOffset += uiLen;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef D3D12_VIDEO_ENC_BITSTREAM_H
+#define D3D12_VIDEO_ENC_BITSTREAM_H
+
+#include "d3d12_video_types.h"
+
+class d3d12_video_encoder_bitstream
+{
+ public:
+ d3d12_video_encoder_bitstream();
+ ~d3d12_video_encoder_bitstream();
+
+ public:
+ void get_current_buffer_position_and_size(uint8_t **ppCurrBufPos, int32_t *pdwLeftBufSize);
+ void inc_current_offset(int32_t dwOffset);
+ bool create_bitstream(uint32_t uiInitBufferSize);
+ void setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer);
+ void attach(uint8_t *pBitsBuffer, uint32_t uiBufferSize);
+ void put_bits(int32_t uiBitsCount, uint32_t iBitsVal);
+ void flush();
+ void exp_Golomb_ue(uint32_t uiVal);
+ void exp_Golomb_se(int32_t iVal);
+
+ inline void clear()
+ {
+ m_iBitsToGo = 32;
+ m_uiOffset = 0;
+ m_uintEncBuffer = 0;
+ };
+
+ void append_byte_stream(d3d12_video_encoder_bitstream *pStream);
+
+ void set_start_code_prevention(bool bSCP)
+ {
+ m_bPreventStartCode = bSCP;
+ }
+ int32_t get_bits_count()
+ {
+ return m_uiOffset * 8 + (32 - m_iBitsToGo);
+ }
+ int32_t get_byte_count()
+ {
+ return m_uiOffset + ((32 - m_iBitsToGo) >> 3);
+ }
+ uint8_t *get_bitstream_buffer()
+ {
+ return m_pBitsBuffer;
+ }
+ bool is_byte_aligned()
+ {
+ if (m_bBufferOverflow) {
+ m_iBitsToGo = 32;
+ }
+ return !(m_iBitsToGo & 7);
+ }
+ int32_t get_num_bits_for_byte_align()
+ {
+ return (m_iBitsToGo & 7);
+ }
+ bool get_start_code_prevention_status()
+ {
+ return m_bPreventStartCode;
+ }
+ bool verify_buffer(uint32_t uiBytesToWrite);
+
+ public:
+ bool m_bBufferOverflow;
+ bool m_bAllowReallocate;
+
+ private:
+ void write_byte_start_code_prevention(uint8_t u8Val);
+ bool reallocate_buffer();
+ int32_t get_exp_golomb0_code_len(uint32_t uiVal);
+
+ const uint8_t m_iLog_2_N[256] = {
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+ };
+
+ private:
+ uint8_t *m_pBitsBuffer;
+ uint32_t m_uiBitsBufferSize;
+ uint32_t m_uiOffset;
+
+ bool m_bExternalBuffer;
+ uint32_t m_uintEncBuffer;
+ int32_t m_iBitsToGo;
+
+ bool m_bPreventStartCode;
+};
+
+#endif
--- /dev/null
+/*
+ * 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.
+ */
+
+
+#ifndef D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H
+#define D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H
+
+#include "d3d12_video_types.h"
+
+class d3d12_video_bitstream_builder_interface
+{
+ public:
+ virtual ~d3d12_video_bitstream_builder_interface()
+ { }
+};
+
+#endif
--- /dev/null
+/*
+ * 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_video_encoder_bitstream_builder_h264.h"
+
+inline H264_SPEC_PROFILES
+Convert12ToSpecH264Profiles(D3D12_VIDEO_ENCODER_PROFILE_H264 profile12)
+{
+ switch (profile12) {
+ case D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN:
+ {
+ return H264_PROFILE_MAIN;
+ } break;
+ case D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH:
+ {
+ return H264_PROFILE_HIGH;
+ } break;
+ case D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10:
+ {
+ return H264_PROFILE_HIGH10;
+ } break;
+ default:
+ {
+ unreachable("Unsupported D3D12_VIDEO_ENCODER_PROFILE_H264");
+ } break;
+ }
+}
+
+void
+d3d12_video_bitstream_builder_h264::build_sps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
+ const D3D12_VIDEO_ENCODER_LEVELS_H264 & level,
+ const DXGI_FORMAT & inputFmt,
+ const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
+ const D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 &gopConfig,
+ uint32_t seq_parameter_set_id,
+ uint32_t max_num_ref_frames,
+ D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC sequenceTargetResolution,
+ D3D12_BOX frame_cropping_codec_config,
+ std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ H264_SPEC_PROFILES profile_idc = Convert12ToSpecH264Profiles(profile);
+ uint32_t constraint_set3_flag = 0;
+ uint32_t level_idc = 0;
+ d3d12_video_encoder_convert_from_d3d12_level_h264(
+ level,
+ level_idc,
+ constraint_set3_flag /*Always 0 except if level is 11 or 1b in which case 0 means 11, 1 means 1b*/);
+
+ // constraint_set3_flag is for Main profile only and levels 11 or 1b: levels 11 if off, level 1b if on. Always 0 for
+ // HIGH/HIGH10 profiles
+ if ((profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH) || (profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10)) {
+ // Force 0 for high profiles
+ constraint_set3_flag = 0;
+ }
+
+ assert((inputFmt == DXGI_FORMAT_NV12) || (inputFmt == DXGI_FORMAT_P010));
+
+ // Assume NV12 YUV 420 8 bits
+ uint32_t bit_depth_luma_minus8 = 0;
+ uint32_t bit_depth_chroma_minus8 = 0;
+
+ // In case is 420 10 bits fix it
+ if (inputFmt == DXGI_FORMAT_P010) {
+ bit_depth_luma_minus8 = 2;
+ bit_depth_chroma_minus8 = 2;
+ }
+
+ // Calculate sequence resolution sizes in MBs
+ // Always in MBs since we don't support interlace in D3D12 Encode
+ uint32_t pic_width_in_mbs_minus1 = static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Width / 16.0)) - 1;
+ uint32_t pic_height_in_map_units_minus1 =
+ static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Height / 16.0)) - 1;
+
+ uint32_t frame_cropping_flag = 0;
+ if (frame_cropping_codec_config.left
+ || frame_cropping_codec_config.right
+ || frame_cropping_codec_config.top
+ || frame_cropping_codec_config.bottom
+ ) {
+ frame_cropping_flag = 1;
+ }
+
+ H264_SPS spsStructure = { static_cast<uint32_t>(profile_idc),
+ constraint_set3_flag,
+ level_idc,
+ seq_parameter_set_id,
+ bit_depth_luma_minus8,
+ bit_depth_chroma_minus8,
+ gopConfig.log2_max_frame_num_minus4,
+ gopConfig.pic_order_cnt_type,
+ gopConfig.log2_max_pic_order_cnt_lsb_minus4,
+ max_num_ref_frames,
+ 0, // gaps_in_frame_num_value_allowed_flag
+ pic_width_in_mbs_minus1,
+ pic_height_in_map_units_minus1,
+ ((codecConfig.ConfigurationFlags &
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_USE_ADAPTIVE_8x8_TRANSFORM) != 0) ?
+ 1u :
+ 0u, // direct_8x8_inference_flag
+ frame_cropping_flag,
+ frame_cropping_codec_config.left,
+ frame_cropping_codec_config.right,
+ frame_cropping_codec_config.top,
+ frame_cropping_codec_config.bottom };
+
+ // Print built PPS structure
+ debug_printf(
+ "[D3D12 d3d12_video_bitstream_builder_h264] H264_SPS Structure generated before writing to bitstream:\n");
+ print_sps(spsStructure);
+
+ // Convert the H264 SPS structure into bytes
+ m_h264Encoder.sps_to_nalu_bytes(&spsStructure, headerBitstream, placingPositionStart, writtenBytes);
+}
+
+void
+d3d12_video_bitstream_builder_h264::write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ m_h264Encoder.write_end_of_stream_nalu(headerBitstream, placingPositionStart, writtenBytes);
+}
+
+void
+d3d12_video_bitstream_builder_h264::write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ m_h264Encoder.write_end_of_sequence_nalu(headerBitstream, placingPositionStart, writtenBytes);
+}
+
+void
+d3d12_video_bitstream_builder_h264::build_pps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
+ const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
+ const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 &pictureControl,
+ uint32_t pic_parameter_set_id,
+ uint32_t seq_parameter_set_id,
+ std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ BOOL bIsHighProfile =
+ ((profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH) || (profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10));
+
+ H264_PPS ppsStructure = {
+ pic_parameter_set_id,
+ seq_parameter_set_id,
+ ((codecConfig.ConfigurationFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_ENABLE_CABAC_ENCODING) != 0) ?
+ 1u :
+ 0u, // entropy_coding_mode_flag
+ 0, // pic_order_present_flag (bottom_field_pic_order_in_frame_present_flag) - will use pic_cnt 0 or 2, always
+ // off ; used with pic_cnt_type 1 and deltas.
+ static_cast<uint32_t>(std::max(static_cast<int32_t>(pictureControl.List0ReferenceFramesCount) - 1,
+ 0)), // num_ref_idx_l0_active_minus1
+ static_cast<uint32_t>(std::max(static_cast<int32_t>(pictureControl.List1ReferenceFramesCount) - 1,
+ 0)), // num_ref_idx_l1_active_minus1
+ ((codecConfig.ConfigurationFlags &
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_USE_CONSTRAINED_INTRAPREDICTION) != 0) ?
+ 1u :
+ 0u, // constrained_intra_pred_flag
+ ((codecConfig.ConfigurationFlags &
+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_USE_ADAPTIVE_8x8_TRANSFORM) != 0) ?
+ 1u :
+ 0u // transform_8x8_mode_flag
+ };
+
+ // Print built PPS structure
+ debug_printf(
+ "[D3D12 d3d12_video_bitstream_builder_h264] H264_PPS Structure generated before writing to bitstream:\n");
+ print_pps(ppsStructure);
+
+ // Convert the H264 SPS structure into bytes
+ m_h264Encoder.pps_to_nalu_bytes(&ppsStructure, headerBitstream, bIsHighProfile, placingPositionStart, writtenBytes);
+}
+
+void
+d3d12_video_bitstream_builder_h264::print_pps(const H264_PPS &pps)
+{
+ // Be careful that build_pps also wraps some other NALU bytes in pps_to_nalu_bytes so bitstream returned by build_pps
+ // won't be exactly the bytes from the H264_PPS struct
+
+ static_assert(sizeof(H264_PPS) ==
+ (sizeof(uint32_t) *
+ 8), "Update the number of uint32_t in struct in assert and add case below if structure changes");
+
+ // Declared fields from definition in d3d12_video_encoder_bitstream_builder_h264.h
+
+ debug_printf("[D3D12 d3d12_video_bitstream_builder_h264] H264_PPS values below:\n");
+ debug_printf("pic_parameter_set_id: %d\n", pps.pic_parameter_set_id);
+ debug_printf("seq_parameter_set_id: %d\n", pps.seq_parameter_set_id);
+ debug_printf("entropy_coding_mode_flag: %d\n", pps.entropy_coding_mode_flag);
+ debug_printf("pic_order_present_flag: %d\n", pps.pic_order_present_flag);
+ debug_printf("num_ref_idx_l0_active_minus1: %d\n", pps.num_ref_idx_l0_active_minus1);
+ debug_printf("num_ref_idx_l1_active_minus1: %d\n", pps.num_ref_idx_l1_active_minus1);
+ debug_printf("constrained_intra_pred_flag: %d\n", pps.constrained_intra_pred_flag);
+ debug_printf("transform_8x8_mode_flag: %d\n", pps.transform_8x8_mode_flag);
+ debug_printf(
+ "[D3D12 d3d12_video_bitstream_builder_h264] H264_PPS values end\n--------------------------------------\n");
+}
+
+void
+d3d12_video_bitstream_builder_h264::print_sps(const H264_SPS &sps)
+{
+ // Be careful when calling this method that build_sps also wraps some other NALU bytes in sps_to_nalu_bytes so
+ // bitstream returned by build_sps won't be exactly the bytes from the H264_SPS struct From definition in
+ // d3d12_video_encoder_bitstream_builder_h264.h
+
+ static_assert(sizeof(H264_SPS) ==
+ (sizeof(uint32_t) *
+ 19), "Update the number of uint32_t in struct in assert and add case below if structure changes");
+
+ // Declared fields from definition in d3d12_video_encoder_bitstream_builder_h264.h
+
+ debug_printf("[D3D12 d3d12_video_bitstream_builder_h264] H264_SPS values below:\n");
+ debug_printf("profile_idc: %d\n", sps.profile_idc);
+ debug_printf("constraint_set3_flag: %d\n", sps.constraint_set3_flag);
+ debug_printf("level_idc: %d\n", sps.level_idc);
+ debug_printf("seq_parameter_set_id: %d\n", sps.seq_parameter_set_id);
+ debug_printf("bit_depth_luma_minus8: %d\n", sps.bit_depth_luma_minus8);
+ debug_printf("bit_depth_chroma_minus8: %d\n", sps.bit_depth_chroma_minus8);
+ debug_printf("log2_max_frame_num_minus4: %d\n", sps.log2_max_frame_num_minus4);
+ debug_printf("pic_order_cnt_type: %d\n", sps.pic_order_cnt_type);
+ debug_printf("log2_max_pic_order_cnt_lsb_minus4: %d\n", sps.log2_max_pic_order_cnt_lsb_minus4);
+ debug_printf("max_num_ref_frames: %d\n", sps.max_num_ref_frames);
+ debug_printf("gaps_in_frame_num_value_allowed_flag: %d\n", sps.gaps_in_frame_num_value_allowed_flag);
+ debug_printf("pic_width_in_mbs_minus1: %d\n", sps.pic_width_in_mbs_minus1);
+ debug_printf("pic_height_in_map_units_minus1: %d\n", sps.pic_height_in_map_units_minus1);
+ debug_printf("direct_8x8_inference_flag: %d\n", sps.direct_8x8_inference_flag);
+ debug_printf("frame_cropping_flag: %d\n", sps.frame_cropping_flag);
+ debug_printf("frame_cropping_rect_left_offset: %d\n", sps.frame_cropping_rect_left_offset);
+ debug_printf("frame_cropping_rect_right_offset: %d\n", sps.frame_cropping_rect_right_offset);
+ debug_printf("frame_cropping_rect_top_offset: %d\n", sps.frame_cropping_rect_top_offset);
+ debug_printf("frame_cropping_rect_bottom_offset: %d\n", sps.frame_cropping_rect_bottom_offset);
+ debug_printf(
+ "[D3D12 d3d12_video_bitstream_builder_h264] H264_SPS values end\n--------------------------------------\n");
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H264_H
+#define D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H264_H
+
+#include "d3d12_video_encoder_nalu_writer_h264.h"
+#include "d3d12_video_encoder_bitstream_builder.h"
+
+class d3d12_video_bitstream_builder_h264 : public d3d12_video_bitstream_builder_interface
+{
+
+ public:
+ d3d12_video_bitstream_builder_h264() {};
+ ~d3d12_video_bitstream_builder_h264() {};
+
+ void build_sps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
+ const D3D12_VIDEO_ENCODER_LEVELS_H264 & level,
+ const DXGI_FORMAT & inputFmt,
+ const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
+ const D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 &gopConfig,
+ uint32_t seq_parameter_set_id,
+ uint32_t max_num_ref_frames,
+ D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC sequenceTargetResolution,
+ D3D12_BOX frame_cropping_codec_config,
+ std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+
+ void build_pps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
+ const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
+ const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 &pictureControl,
+ uint32_t pic_parameter_set_id,
+ uint32_t seq_parameter_set_id,
+ std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+
+ void write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+ void write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+
+ void print_pps(const H264_PPS &pps);
+ void print_sps(const H264_SPS &sps);
+
+ uint32_t m_activeSPSIndex = 0;
+ uint32_t m_activePPSIndex = 0;
+
+ uint32_t get_active_sps_id()
+ {
+ return m_activeSPSIndex;
+ };
+ uint32_t get_active_pps_id()
+ {
+ return m_activePPSIndex;
+ };
+
+ void set_active_sps_id(uint32_t active_sps_id)
+ {
+ m_activeSPSIndex = active_sps_id;
+ debug_printf("[d3d12_video_bitstream_builder_h264] Setting new active SPS ID: %d ", m_activeSPSIndex);
+ };
+ void set_active_pps_id(uint32_t active_pps_id)
+ {
+ m_activePPSIndex = active_pps_id;
+ debug_printf("[d3d12_video_bitstream_builder_h264] Setting new active PPS ID: %d ", m_activePPSIndex);
+ };
+
+ private:
+ d3d12_video_nalu_writer_h264 m_h264Encoder;
+};
+
+#endif
--- /dev/null
+/*
+ * 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_video_encoder_nalu_writer_h264.h"
+#include <algorithm>
+
+void
+d3d12_video_nalu_writer_h264::rbsp_trailing(d3d12_video_encoder_bitstream *pBitstream)
+{
+ pBitstream->put_bits(1, 1);
+ int32_t iLeft = pBitstream->get_num_bits_for_byte_align();
+
+ if (iLeft) {
+ pBitstream->put_bits(iLeft, 0);
+ }
+
+ bool isAligned = pBitstream->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
+ assert(isAligned);
+}
+
+uint32_t
+d3d12_video_nalu_writer_h264::write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS)
+{
+ int32_t iBytesWritten = pBitstream->get_byte_count();
+
+ // Standard constraint to be between 0 and 31 inclusive
+ assert(pSPS->seq_parameter_set_id >= 0);
+ assert(pSPS->seq_parameter_set_id < 32);
+
+ pBitstream->put_bits(8, pSPS->profile_idc);
+ pBitstream->put_bits(1, 0); // constraint_set0_flag
+ pBitstream->put_bits(1, 0); // constraint_set1_flag
+ pBitstream->put_bits(1, 0); // constraint_set2_flag
+ pBitstream->put_bits(1, pSPS->constraint_set3_flag);
+ pBitstream->put_bits(1, 0); // constraint_set4_flag
+ pBitstream->put_bits(1, 0); // constraint_set5_flag
+ pBitstream->put_bits(2, 0);
+ pBitstream->put_bits(8, pSPS->level_idc);
+ pBitstream->exp_Golomb_ue(pSPS->seq_parameter_set_id);
+
+ // Only support profiles defined in D3D12 Video Encode
+ // If adding new profile support, check that the chroma_format_idc and bit depth are set correctly below
+ // for the new additions
+ assert((pSPS->profile_idc == H264_PROFILE_MAIN) || (pSPS->profile_idc == H264_PROFILE_HIGH) ||
+ (pSPS->profile_idc == H264_PROFILE_HIGH10));
+
+ if ((pSPS->profile_idc == H264_PROFILE_HIGH) || (pSPS->profile_idc == H264_PROFILE_HIGH10)) {
+ // chroma_format_idc always 4.2.0
+ pBitstream->exp_Golomb_ue(1);
+ // Assume no separate_colour_plane_flag given chroma_format_idc = 1
+ pBitstream->exp_Golomb_ue(pSPS->bit_depth_luma_minus8);
+ pBitstream->exp_Golomb_ue(pSPS->bit_depth_chroma_minus8);
+ // qpprime_y_zero_transform_bypass_flag
+ pBitstream->put_bits(1, 0);
+ // seq_scaling_matrix_present_flag)
+ pBitstream->put_bits(1, 0);
+ }
+
+ pBitstream->exp_Golomb_ue(pSPS->log2_max_frame_num_minus4);
+
+ pBitstream->exp_Golomb_ue(pSPS->pic_order_cnt_type);
+ if (pSPS->pic_order_cnt_type == 0) {
+ pBitstream->exp_Golomb_ue(pSPS->log2_max_pic_order_cnt_lsb_minus4);
+ }
+ pBitstream->exp_Golomb_ue(pSPS->max_num_ref_frames);
+ pBitstream->put_bits(1, pSPS->gaps_in_frame_num_value_allowed_flag);
+ pBitstream->exp_Golomb_ue(pSPS->pic_width_in_mbs_minus1);
+ pBitstream->exp_Golomb_ue(pSPS->pic_height_in_map_units_minus1);
+
+ // No support for interlace in D3D12 Video Encode
+ // frame_mbs_only_flag coded as 1
+ pBitstream->put_bits(1, 1); // frame_mbs_only_flag
+ pBitstream->put_bits(1, pSPS->direct_8x8_inference_flag);
+
+ // no cropping
+ pBitstream->put_bits(1, pSPS->frame_cropping_flag); // frame_cropping_flag
+ if (pSPS->frame_cropping_flag) {
+ pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_left_offset);
+ pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_right_offset);
+ pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_top_offset);
+ pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_bottom_offset);
+ }
+
+ // We're not including the VUI so this better be zero.
+ pBitstream->put_bits(1, 0); // vui_paramenters_present_flag
+
+ rbsp_trailing(pBitstream);
+ pBitstream->flush();
+
+ iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
+ return (uint32_t) iBytesWritten;
+}
+
+uint32_t
+d3d12_video_nalu_writer_h264::write_pps_bytes(d3d12_video_encoder_bitstream *pBitstream,
+ H264_PPS * pPPS,
+ BOOL bIsHighProfile)
+{
+ int32_t iBytesWritten = pBitstream->get_byte_count();
+
+ // Standard constraint to be between 0 and 31 inclusive
+ assert(pPPS->seq_parameter_set_id >= 0);
+ assert(pPPS->seq_parameter_set_id < 32);
+
+ // Standard constraint to be between 0 and 255 inclusive
+ assert(pPPS->pic_parameter_set_id >= 0);
+ assert(pPPS->pic_parameter_set_id < 256);
+
+ pBitstream->exp_Golomb_ue(pPPS->pic_parameter_set_id);
+ pBitstream->exp_Golomb_ue(pPPS->seq_parameter_set_id);
+ pBitstream->put_bits(1, pPPS->entropy_coding_mode_flag);
+ pBitstream->put_bits(1, pPPS->pic_order_present_flag); // bottom_field_pic_order_in_frame_present_flag
+ pBitstream->exp_Golomb_ue(0); // num_slice_groups_minus1
+
+
+ pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l0_active_minus1);
+ pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l1_active_minus1);
+ pBitstream->put_bits(1, 0); // weighted_pred_flag
+ pBitstream->put_bits(2, 0); // weighted_bipred_idc
+ pBitstream->exp_Golomb_se(0); // pic_init_qp_minus26
+ pBitstream->exp_Golomb_se(0); // pic_init_qs_minus26
+ pBitstream->exp_Golomb_se(0); // chroma_qp_index_offset
+ pBitstream->put_bits(1, 1); // deblocking_filter_control_present_flag
+ pBitstream->put_bits(1, pPPS->constrained_intra_pred_flag);
+ pBitstream->put_bits(1, 0); // redundant_pic_cnt_present_flag
+
+ if (bIsHighProfile) {
+ pBitstream->put_bits(1, pPPS->transform_8x8_mode_flag);
+ pBitstream->put_bits(1, 0); // pic_scaling_matrix_present_flag
+ pBitstream->exp_Golomb_se(0); // second_chroma_qp_index_offset
+ }
+
+ rbsp_trailing(pBitstream);
+ pBitstream->flush();
+
+ iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
+ return (uint32_t) iBytesWritten;
+}
+
+uint32_t
+d3d12_video_nalu_writer_h264::wrap_sps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
+{
+ return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_SPS);
+}
+
+uint32_t
+d3d12_video_nalu_writer_h264::wrap_pps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
+{
+ return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_PPS);
+}
+
+void
+d3d12_video_nalu_writer_h264::write_nalu_end(d3d12_video_encoder_bitstream *pNALU)
+{
+ pNALU->flush();
+ pNALU->set_start_code_prevention(FALSE);
+ int32_t iNALUnitLen = pNALU->get_byte_count();
+
+ if (FALSE == pNALU->m_bBufferOverflow && 0x00 == pNALU->get_bitstream_buffer()[iNALUnitLen - 1]) {
+ pNALU->put_bits(8, 0x03);
+ pNALU->flush();
+ }
+}
+
+uint32_t
+d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
+ d3d12_video_encoder_bitstream *pRBSP,
+ uint32_t iNaluIdc,
+ uint32_t iNaluType)
+{
+ bool isAligned = pRBSP->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
+ assert(isAligned);
+
+ int32_t iBytesWritten = pNALU->get_byte_count();
+
+ pNALU->set_start_code_prevention(FALSE);
+
+ // NAL start code
+ pNALU->put_bits(24, 0);
+ pNALU->put_bits(8, 1);
+
+ // NAL header
+ pNALU->put_bits(1, 0);
+ pNALU->put_bits(2, iNaluIdc);
+ pNALU->put_bits(5, iNaluType);
+ pNALU->flush();
+
+ // NAL body
+ pRBSP->flush();
+
+ if (pRBSP->get_start_code_prevention_status()) {
+ // Direct copying.
+ pNALU->append_byte_stream(pRBSP);
+ } else {
+ // Copy with start code prevention.
+ pNALU->set_start_code_prevention(TRUE);
+ int32_t iLength = pRBSP->get_byte_count();
+ uint8_t *pBuffer = pRBSP->get_bitstream_buffer();
+
+ for (int32_t i = 0; i < iLength; i++) {
+ pNALU->put_bits(8, pBuffer[i]);
+ }
+ }
+
+ isAligned = pNALU->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
+ assert(isAligned);
+ write_nalu_end(pNALU);
+
+ pNALU->flush();
+
+ iBytesWritten = pNALU->get_byte_count() - iBytesWritten;
+ return (uint32_t) iBytesWritten;
+}
+
+void
+d3d12_video_nalu_writer_h264::sps_to_nalu_bytes(H264_SPS * pSPS,
+ std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ // Wrap SPS into NALU and copy full NALU into output byte array
+ d3d12_video_encoder_bitstream rbsp, nalu;
+
+ if (!rbsp.create_bitstream(MAX_COMPRESSED_SPS)) {
+ debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_SPS) failed\n");
+ assert(false);
+ }
+
+ if (!nalu.create_bitstream(2 * MAX_COMPRESSED_SPS)) {
+ debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_SPS) failed\n");
+ assert(false);
+ }
+
+ rbsp.set_start_code_prevention(TRUE);
+ if (write_sps_bytes(&rbsp, pSPS) <= 0u) {
+ debug_printf("write_sps_bytes(&rbsp, pSPS) didn't write any bytes.\n");
+ assert(false);
+ }
+
+ if (wrap_sps_nalu(&nalu, &rbsp) <= 0u) {
+ debug_printf("wrap_sps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
+ assert(false);
+ }
+
+ // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
+ // memory.
+ uint8_t *naluBytes = nalu.get_bitstream_buffer();
+ size_t naluByteSize = nalu.get_byte_count();
+
+ auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
+ if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
+ headerBitstream.resize(startDstIndex + naluByteSize);
+ }
+
+ std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
+
+ writtenBytes = naluByteSize;
+}
+
+void
+d3d12_video_nalu_writer_h264::pps_to_nalu_bytes(H264_PPS * pPPS,
+ std::vector<uint8_t> & headerBitstream,
+ BOOL bIsHighProfile,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ // Wrap PPS into NALU and copy full NALU into output byte array
+ d3d12_video_encoder_bitstream rbsp, nalu;
+ if (!rbsp.create_bitstream(MAX_COMPRESSED_PPS)) {
+ debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_PPS) failed\n");
+ assert(false);
+ }
+
+ if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
+ debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
+ assert(false);
+ }
+
+ rbsp.set_start_code_prevention(TRUE);
+
+ if (write_pps_bytes(&rbsp, pPPS, bIsHighProfile) <= 0u) {
+ debug_printf("write_pps_bytes(&rbsp, pPPS, bIsHighProfile) didn't write any bytes.\n");
+ assert(false);
+ }
+
+ if (wrap_pps_nalu(&nalu, &rbsp) <= 0u) {
+ debug_printf("wrap_pps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
+ assert(false);
+ }
+
+ // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
+ // memory.
+ uint8_t *naluBytes = nalu.get_bitstream_buffer();
+ size_t naluByteSize = nalu.get_byte_count();
+
+ auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
+ if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
+ headerBitstream.resize(startDstIndex + naluByteSize);
+ }
+
+ std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
+
+ writtenBytes = naluByteSize;
+}
+
+void
+d3d12_video_nalu_writer_h264::write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ d3d12_video_encoder_bitstream rbsp, nalu;
+ if (!rbsp.create_bitstream(8)) {
+ debug_printf("rbsp.create_bitstream(8) failed\n");
+ assert(false);
+ }
+ if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
+ debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
+ assert(false);
+ }
+
+ rbsp.set_start_code_prevention(TRUE);
+ if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) <= 0u) {
+ debug_printf(
+ "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) didn't write any bytes.\n");;
+ assert(false);
+ }
+
+ // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
+ // memory.
+ uint8_t *naluBytes = nalu.get_bitstream_buffer();
+ size_t naluByteSize = nalu.get_byte_count();
+
+ auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
+ if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
+ headerBitstream.resize(startDstIndex + naluByteSize);
+ }
+
+ std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
+
+ writtenBytes = naluByteSize;
+}
+
+void
+d3d12_video_nalu_writer_h264::write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes)
+{
+ d3d12_video_encoder_bitstream rbsp, nalu;
+ if (!rbsp.create_bitstream(8)) {
+ debug_printf("rbsp.create_bitstream(8) failed.\n");
+ assert(false);
+ }
+
+ if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
+ debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
+ assert(false);
+ }
+
+ rbsp.set_start_code_prevention(TRUE);
+ if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) <= 0u) {
+
+ debug_printf(
+ "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) didn't write any bytes.\n");
+ assert(false);
+ }
+
+ // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
+ // memory.
+ uint8_t *naluBytes = nalu.get_bitstream_buffer();
+ size_t naluByteSize = nalu.get_byte_count();
+
+ auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
+ if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
+ headerBitstream.resize(startDstIndex + naluByteSize);
+ }
+
+ std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
+
+ writtenBytes = naluByteSize;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef D3D12_VIDEO_ENC_NALU_WRITER_H264_H
+#define D3D12_VIDEO_ENC_NALU_WRITER_H264_H
+
+#include "d3d12_video_encoder_bitstream.h"
+
+enum H264_NALREF_IDC
+{
+ NAL_REFIDC_REF = 3,
+ NAL_REFIDC_NONREF = 0
+};
+
+enum H264_NALU_TYPE
+{
+ NAL_TYPE_UNSPECIFIED = 0,
+ NAL_TYPE_SLICE = 1,
+ NAL_TYPE_SLICEDATA_A = 2,
+ NAL_TYPE_SLICEDATA_B = 3,
+ NAL_TYPE_SLICEDATA_C = 4,
+ NAL_TYPE_IDR = 5,
+ NAL_TYPE_SEI = 6,
+ NAL_TYPE_SPS = 7,
+ NAL_TYPE_PPS = 8,
+ NAL_TYPE_ACCESS_UNIT_DEMILITER = 9,
+ NAL_TYPE_END_OF_SEQUENCE = 10,
+ NAL_TYPE_END_OF_STREAM = 11,
+ NAL_TYPE_FILLER_DATA = 12,
+ NAL_TYPE_SPS_EXTENSION = 13,
+ NAL_TYPE_PREFIX = 14,
+ /* 15...18 RESERVED */
+ NAL_TYPE_AUXILIARY_SLICE = 19,
+ /* 20...23 RESERVED */
+ /* 24...31 UNSPECIFIED */
+};
+
+struct H264_SPS
+{
+ uint32_t profile_idc;
+ uint32_t constraint_set3_flag;
+ uint32_t level_idc;
+ uint32_t seq_parameter_set_id;
+ uint32_t bit_depth_luma_minus8;
+ uint32_t bit_depth_chroma_minus8;
+ uint32_t log2_max_frame_num_minus4;
+ uint32_t pic_order_cnt_type;
+ uint32_t log2_max_pic_order_cnt_lsb_minus4;
+ uint32_t max_num_ref_frames;
+ uint32_t gaps_in_frame_num_value_allowed_flag;
+ uint32_t pic_width_in_mbs_minus1;
+ uint32_t pic_height_in_map_units_minus1;
+ uint32_t direct_8x8_inference_flag;
+ uint32_t frame_cropping_flag;
+ uint32_t frame_cropping_rect_left_offset;
+ uint32_t frame_cropping_rect_right_offset;
+ uint32_t frame_cropping_rect_top_offset;
+ uint32_t frame_cropping_rect_bottom_offset;
+};
+
+struct H264_PPS
+{
+ uint32_t pic_parameter_set_id;
+ uint32_t seq_parameter_set_id;
+ uint32_t entropy_coding_mode_flag;
+ uint32_t pic_order_present_flag;
+ uint32_t num_ref_idx_l0_active_minus1;
+ uint32_t num_ref_idx_l1_active_minus1;
+ uint32_t constrained_intra_pred_flag;
+ uint32_t transform_8x8_mode_flag;
+};
+
+enum H264_SPEC_PROFILES
+{
+ H264_PROFILE_MAIN = 77,
+ H264_PROFILE_HIGH = 100,
+ H264_PROFILE_HIGH10 = 110,
+};
+
+#define MAX_COMPRESSED_PPS 256
+#define MAX_COMPRESSED_SPS 256
+
+class d3d12_video_nalu_writer_h264
+{
+ public:
+ d3d12_video_nalu_writer_h264()
+ { }
+ ~d3d12_video_nalu_writer_h264()
+ { }
+
+ // Writes the H264 SPS structure into a bitstream passed in headerBitstream
+ // Function resizes bitstream accordingly and puts result in byte vector
+ void sps_to_nalu_bytes(H264_SPS * pSPS,
+ std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+
+ // Writes the H264 PPS structure into a bitstream passed in headerBitstream
+ // Function resizes bitstream accordingly and puts result in byte vector
+ void pps_to_nalu_bytes(H264_PPS * pPPS,
+ std::vector<uint8_t> & headerBitstream,
+ BOOL bIsFREXTProfile,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+
+ void write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+ void write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
+ std::vector<uint8_t>::iterator placingPositionStart,
+ size_t & writtenBytes);
+
+ private:
+ // Writes from structure into bitstream with RBSP trailing but WITHOUT NAL unit wrap (eg. nal_idc_type, etc)
+ uint32_t write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS);
+ uint32_t write_pps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_PPS *pPPS, BOOL bIsFREXTProfile);
+
+ // Adds NALU wrapping into structures and ending NALU control bits
+ uint32_t wrap_sps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP);
+ uint32_t wrap_pps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP);
+
+ // Helpers
+ void write_nalu_end(d3d12_video_encoder_bitstream *pNALU);
+ void rbsp_trailing(d3d12_video_encoder_bitstream *pBitstream);
+ uint32_t wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
+ d3d12_video_encoder_bitstream *pRBSP,
+ uint32_t iNaluIdc,
+ uint32_t iNaluType);
+};
+
+#endif
--- /dev/null
+/*
+ * 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.
+ */
+
+
+#ifndef D3D12_VIDEO_ENCODE_REFERENCES_MANAGER_INTERFACE_H
+#define D3D12_VIDEO_ENCODE_REFERENCES_MANAGER_INTERFACE_H
+
+#include "d3d12_video_types.h"
+
+class d3d12_video_encoder_references_manager_interface
+{
+ public:
+ virtual void begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA, bool bUsedAsReference) = 0;
+ virtual void end_frame() = 0;
+ virtual D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE get_current_frame_recon_pic_output_allocation() = 0;
+ virtual void
+ get_current_frame_picture_control_data(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation) = 0;
+ virtual bool is_current_frame_used_as_reference() = 0;
+ virtual D3D12_VIDEO_ENCODE_REFERENCE_FRAMES get_current_reference_frames() = 0;
+ virtual ~d3d12_video_encoder_references_manager_interface()
+ { }
+};
+
+#endif
--- /dev/null
+/*
+ * 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_video_encoder_references_manager_h264.h"
+#include <algorithm>
+#include <string>
+#include "d3d12_screen.h"
+
+using namespace std;
+
+d3d12_video_encoder_references_manager_h264::d3d12_video_encoder_references_manager_h264(
+ bool gopHasIorPFrames, d3d12_video_dpb_storage_manager_interface &rDpbStorageManager, uint32_t MaxDPBCapacity)
+ : m_MaxDPBCapacity(MaxDPBCapacity),
+ m_rDPBStorageManager(rDpbStorageManager),
+ m_CurrentFrameReferencesData({}),
+ m_gopHasInterFrames(gopHasIorPFrames)
+{
+ assert((m_MaxDPBCapacity + 1 /*extra for cur frame output recon pic*/) ==
+ m_rDPBStorageManager.get_number_of_tracked_allocations());
+
+ debug_printf("[D3D12 Video Encoder Picture Manager H264] Completed construction of "
+ "d3d12_video_encoder_references_manager_h264 instance, settings are\n");
+ debug_printf("[D3D12 Video Encoder Picture Manager H264] m_MaxDPBCapacity: %d\n", m_MaxDPBCapacity);
+}
+
+void
+d3d12_video_encoder_references_manager_h264::reset_gop_tracking_and_dpb()
+{
+ // Reset m_CurrentFrameReferencesData tracking
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.clear();
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.reserve(m_MaxDPBCapacity);
+ m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
+
+ // Reset DPB storage
+ uint32_t numPicsBeforeClearInDPB = m_rDPBStorageManager.get_number_of_pics_in_dpb();
+ uint32_t cFreedResources = m_rDPBStorageManager.clear_decode_picture_buffer();
+ assert(numPicsBeforeClearInDPB == cFreedResources);
+
+ // Initialize if needed the reconstructed picture allocation for the first IDR picture in the GOP
+ // This needs to be done after initializing the GOP tracking state above since it makes decisions based on the
+ // current picture type.
+ prepare_current_frame_recon_pic_allocation();
+
+ // After clearing the DPB, outstanding used allocations should be 1u only for the first allocation for the
+ // reconstructed picture of the initial IDR in the GOP
+ assert(m_rDPBStorageManager.get_number_of_in_use_allocations() == m_gopHasInterFrames ? 1u : 0u);
+ assert(m_rDPBStorageManager.get_number_of_tracked_allocations() <=
+ (m_MaxDPBCapacity + 1)); // pool is not extended beyond maximum expected usage
+}
+
+// Calculates the picture control structure for the current frame
+void
+d3d12_video_encoder_references_manager_h264::get_current_frame_picture_control_data(
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation)
+{
+ // Update reference picture control structures (L0/L1 and DPB descriptors lists based on current frame and next frame
+ // in GOP) for next frame
+
+ debug_printf("[D3D12 Video Encoder Picture Manager H264] %d resources IN USE out of a total of %d ALLOCATED "
+ "resources at frame with POC: %d\n",
+ m_rDPBStorageManager.get_number_of_in_use_allocations(),
+ m_rDPBStorageManager.get_number_of_tracked_allocations(),
+ m_curFrameState.PictureOrderCountNumber);
+
+ // See casts below
+ assert(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size() < UINT32_MAX);
+
+ bool needsL0List = (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME) ||
+ (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME);
+ bool needsL1List = (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME);
+
+ assert(codecAllocation.DataSize == sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264));
+
+ // See D3D12 Encode spec below
+ // pList0ReferenceFrames
+ // List of past frame reference frames to be used for this frame. Each integer value in this array indices into
+ // pReferenceFramesReconPictureDescriptors to reference pictures kept in the DPB.
+ // pList1ReferenceFrames
+ // List of future frame reference frames to be used for this frame. Each integer value in this array indices into
+ // pReferenceFramesReconPictureDescriptors to reference pictures kept in the DPB.
+
+ // Need to map from frame_num in the receiving ref_idx_l0_list/ref_idx_l1_list to the position with that
+ // FrameDecodingOrderNumber in the DPB descriptor
+
+ if (needsL0List && (m_curFrameState.List0ReferenceFramesCount > 0)) {
+ std::vector<uint32_t> tmpL0(m_curFrameState.List0ReferenceFramesCount, 0);
+ memcpy(tmpL0.data(),
+ m_curFrameState.pList0ReferenceFrames,
+ m_curFrameState.List0ReferenceFramesCount * sizeof(m_curFrameState.pList0ReferenceFrames[0]));
+
+ for (size_t l0Idx = 0; l0Idx < m_curFrameState.List0ReferenceFramesCount; l0Idx++) {
+ // tmpL0[l0Idx] has frame_num's (FrameDecodingOrderNumber)
+ // m_curFrameState.pList0ReferenceFrames[l0Idx] needs to have the index j of
+ // pReferenceFramesReconPictureDescriptors where
+ // pReferenceFramesReconPictureDescriptors[j].FrameDecodingOrderNumber == tmpL0[l0Idx]
+
+ auto value = tmpL0[l0Idx];
+ auto foundItemIt = std::find_if(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(),
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end(),
+ [&value](const D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 &p) {
+ return p.FrameDecodingOrderNumber == value;
+ });
+
+ assert(foundItemIt != m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end());
+ m_curFrameState.pList0ReferenceFrames[l0Idx] =
+ std::distance(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(), foundItemIt);
+ }
+ }
+
+ if (needsL1List && (m_curFrameState.List1ReferenceFramesCount > 0)) {
+ std::vector<uint32_t> tmpL1(m_curFrameState.List1ReferenceFramesCount, 0);
+ memcpy(tmpL1.data(),
+ m_curFrameState.pList1ReferenceFrames,
+ m_curFrameState.List1ReferenceFramesCount * sizeof(m_curFrameState.pList1ReferenceFrames[0]));
+
+ for (size_t l1Idx = 0; l1Idx < m_curFrameState.List1ReferenceFramesCount; l1Idx++) {
+ // tmpL1[l1Idx] has frame_num's (FrameDecodingOrderNumber)
+ // m_curFrameState.pList1ReferenceFrames[l1Idx] needs to have the index j of
+ // pReferenceFramesReconPictureDescriptors where
+ // pReferenceFramesReconPictureDescriptors[j].FrameDecodingOrderNumber == tmpL1[l1Idx]
+
+ auto value = tmpL1[l1Idx];
+ auto foundItemIt = std::find_if(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(),
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end(),
+ [&value](const D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 &p) {
+ return p.FrameDecodingOrderNumber == value;
+ });
+
+ assert(foundItemIt != m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end());
+ m_curFrameState.pList1ReferenceFrames[l1Idx] =
+ std::distance(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(), foundItemIt);
+ }
+ }
+
+ m_curFrameState.List0ReferenceFramesCount = needsL0List ? m_curFrameState.List0ReferenceFramesCount : 0;
+ m_curFrameState.pList0ReferenceFrames = needsL0List ? m_curFrameState.pList0ReferenceFrames : nullptr,
+ m_curFrameState.List1ReferenceFramesCount = needsL1List ? m_curFrameState.List1ReferenceFramesCount : 0,
+ m_curFrameState.pList1ReferenceFrames = needsL1List ? m_curFrameState.pList1ReferenceFrames : nullptr,
+ m_curFrameState.ReferenceFramesReconPictureDescriptorsCount =
+ needsL0List ? static_cast<uint32_t>(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size()) :
+ 0,
+ m_curFrameState.pReferenceFramesReconPictureDescriptors =
+ needsL0List ? m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.data() : nullptr,
+
+ *codecAllocation.pH264PicData = m_curFrameState;
+
+ print_l0_l1_lists();
+ print_dpb();
+}
+
+// Returns the resource allocation for a reconstructed picture output for the current frame
+D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
+d3d12_video_encoder_references_manager_h264::get_current_frame_recon_pic_output_allocation()
+{
+ return m_CurrentFrameReferencesData.ReconstructedPicTexture;
+}
+
+D3D12_VIDEO_ENCODE_REFERENCE_FRAMES
+d3d12_video_encoder_references_manager_h264::get_current_reference_frames()
+{
+ D3D12_VIDEO_ENCODE_REFERENCE_FRAMES retVal = { 0,
+ // ppTexture2Ds
+ nullptr,
+ // pSubresources
+ nullptr };
+
+ // Return nullptr for fully intra frames (eg IDR)
+ // and return references information for inter frames (eg.P/B) and I frame that doesn't flush DPB
+
+ if ((m_curFrameState.FrameType != D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME) &&
+ (m_curFrameState.FrameType != D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_I_FRAME) && m_gopHasInterFrames) {
+ auto curRef = m_rDPBStorageManager.get_current_reference_frames();
+ retVal.NumTexture2Ds = curRef.NumTexture2Ds;
+ retVal.ppTexture2Ds = curRef.ppTexture2Ds;
+ retVal.pSubresources = curRef.pSubresources;
+ }
+
+ return retVal;
+}
+
+void
+d3d12_video_encoder_references_manager_h264::prepare_current_frame_recon_pic_allocation()
+{
+ m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
+
+ // If all GOP are intra frames, no point in doing reference pic allocations
+ if (is_current_frame_used_as_reference() && m_gopHasInterFrames) {
+ auto reconPic = m_rDPBStorageManager.get_new_tracked_picture_allocation();
+ m_CurrentFrameReferencesData.ReconstructedPicTexture.pReconstructedPicture = reconPic.pReconstructedPicture;
+ m_CurrentFrameReferencesData.ReconstructedPicTexture.ReconstructedPictureSubresource =
+ reconPic.ReconstructedPictureSubresource;
+ }
+}
+
+void
+d3d12_video_encoder_references_manager_h264::update_fifo_dpb_push_front_cur_recon_pic()
+{
+ // Keep the order of the dpb storage and dpb descriptors in a circular buffer
+ // order such that the DPB array consists of a sequence of frames in DECREASING encoding order
+ // eg. last frame encoded at first, followed by one to last frames encoded, and at the end
+ // the most distant frame encoded (currentFrameEncodeOrderNumber - MaxDPBSize)
+
+ // If current pic was not used as reference, current reconstructed picture resource is empty,
+ // No need to to anything in that case.
+ // Otherwise extract the reconstructed picture result and add it to the DPB
+
+ // If GOP are all intra frames, do nothing also.
+ if (is_current_frame_used_as_reference() && m_gopHasInterFrames) {
+ debug_printf("[D3D12 Video Encoder Picture Manager H264] MaxDPBCapacity is %d - Number of pics in DPB is %d "
+ "when trying to put frame with POC %d at front of the DPB\n",
+ m_MaxDPBCapacity,
+ m_rDPBStorageManager.get_number_of_pics_in_dpb(),
+ m_curFrameState.PictureOrderCountNumber);
+
+ // Release least recently used in DPB if we filled the m_MaxDPBCapacity allowed
+ if (m_rDPBStorageManager.get_number_of_pics_in_dpb() == m_MaxDPBCapacity) {
+ bool untrackedRes = false;
+ m_rDPBStorageManager.remove_reference_frame(m_rDPBStorageManager.get_number_of_pics_in_dpb() - 1,
+ &untrackedRes); // Remove last entry
+ // Verify that resource was untracked since this class is using the pool completely for allocations
+ assert(untrackedRes);
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.pop_back(); // Remove last entry
+ }
+
+ // Add new dpb to front of DPB
+ D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE recAlloc = get_current_frame_recon_pic_output_allocation();
+ d3d12_video_reconstructed_picture refFrameDesc = {};
+ refFrameDesc.pReconstructedPicture = recAlloc.pReconstructedPicture;
+ refFrameDesc.ReconstructedPictureSubresource = recAlloc.ReconstructedPictureSubresource;
+ refFrameDesc.pVideoHeap = nullptr; // D3D12 Video Encode does not need the D3D12VideoEncoderHeap struct for H264
+ // (used for no-key-frame resolution change in VC1, AV1, etc)
+ m_rDPBStorageManager.insert_reference_frame(refFrameDesc, 0);
+
+ // Prepare D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 for added DPB member
+ D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 newDPBDescriptor = {
+ // uint32_t ReconstructedPictureResourceIndex;
+ 0, // the associated reconstructed picture is also being pushed_front in m_rDPBStorageManager
+ // BOOL IsLongTermReference;
+ false,
+ // uint32_t LongTermPictureIdx;
+ 0,
+ // uint32_t PictureOrderCountNumber;
+ m_curFrameState.PictureOrderCountNumber,
+ // uint32_t FrameDecodingOrderNumber;
+ m_curFrameState.FrameDecodingOrderNumber,
+ // uint32_t TemporalLayerIndex;
+ 0 // NO B-hierarchy in this impl of the picture manager
+ };
+
+ // Add DPB entry
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.insert(
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(),
+ newDPBDescriptor);
+
+ // Update the indices for ReconstructedPictureResourceIndex in pReferenceFramesReconPictureDescriptors
+ // to be in identity mapping with m_rDPBStorageManager indices
+ // after pushing the elements to the right in the push_front operation
+ for (uint32_t dpbResIdx = 1;
+ dpbResIdx < m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size();
+ dpbResIdx++) {
+ auto &dpbDesc = m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[dpbResIdx];
+ dpbDesc.ReconstructedPictureResourceIndex = dpbResIdx;
+ }
+ }
+
+ // Number of allocations, disregarding if they are used or not, should not exceed this limit due to reuse policies on
+ // DPB items removal.
+ assert(m_rDPBStorageManager.get_number_of_tracked_allocations() <= (m_MaxDPBCapacity + 1));
+}
+
+void
+d3d12_video_encoder_references_manager_h264::print_l0_l1_lists()
+{
+ if ((D3D12_DEBUG_VERBOSE & d3d12_debug) &&
+ ((m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME) ||
+ (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME))) {
+ std::string list0ContentsString;
+ for (uint32_t idx = 0; idx < m_curFrameState.List0ReferenceFramesCount; idx++) {
+ uint32_t value = m_curFrameState.pList0ReferenceFrames[idx];
+ list0ContentsString += "{ DPBidx: ";
+ list0ContentsString += std::to_string(value);
+ list0ContentsString += " - POC: ";
+ list0ContentsString += std::to_string(
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].PictureOrderCountNumber);
+ list0ContentsString += " - FrameDecodingOrderNumber: ";
+ list0ContentsString += std::to_string(
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].FrameDecodingOrderNumber);
+ list0ContentsString += "}\n";
+ }
+
+ debug_printf(
+ "[D3D12 Video Encoder Picture Manager H264] L0 list for frame with POC %d - frame_num (%d) is: \n %s \n",
+ m_curFrameState.PictureOrderCountNumber,
+ m_curFrameState.FrameDecodingOrderNumber,
+ list0ContentsString.c_str());
+
+ std::string list1ContentsString;
+ for (uint32_t idx = 0; idx < m_curFrameState.List1ReferenceFramesCount; idx++) {
+ uint32_t value = m_curFrameState.pList1ReferenceFrames[idx];
+ list1ContentsString += "{ DPBidx: ";
+ list1ContentsString += std::to_string(value);
+ list1ContentsString += " - POC: ";
+ list1ContentsString += std::to_string(
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].PictureOrderCountNumber);
+ list1ContentsString += " - FrameDecodingOrderNumber: ";
+ list1ContentsString += std::to_string(
+ m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].FrameDecodingOrderNumber);
+ list1ContentsString += "}\n";
+ }
+
+ debug_printf(
+ "[D3D12 Video Encoder Picture Manager H264] L1 list for frame with POC %d - frame_num (%d) is: \n %s \n",
+ m_curFrameState.PictureOrderCountNumber,
+ m_curFrameState.FrameDecodingOrderNumber,
+ list1ContentsString.c_str());
+ }
+}
+
+void
+d3d12_video_encoder_references_manager_h264::print_dpb()
+{
+ if (D3D12_DEBUG_VERBOSE & d3d12_debug) {
+ std::string dpbContents;
+ for (uint32_t dpbResIdx = 0;
+ dpbResIdx < m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size();
+ dpbResIdx++) {
+ auto &dpbDesc = m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[dpbResIdx];
+ auto dpbEntry = m_rDPBStorageManager.get_reference_frame(dpbDesc.ReconstructedPictureResourceIndex);
+
+ dpbContents += "{ DPBidx: ";
+ dpbContents += std::to_string(dpbResIdx);
+ dpbContents += " - POC: ";
+ dpbContents += std::to_string(dpbDesc.PictureOrderCountNumber);
+ dpbContents += " - FrameDecodingOrderNumber: ";
+ dpbContents += std::to_string(dpbDesc.FrameDecodingOrderNumber);
+ dpbContents += " - DPBStorageIdx: ";
+ dpbContents += std::to_string(dpbDesc.ReconstructedPictureResourceIndex);
+ dpbContents += " - DPBStorageResourcePtr: ";
+ char strBuf[256];
+ memset(&strBuf, '\0', 256);
+ sprintf(strBuf, "%p", dpbEntry.pReconstructedPicture);
+ dpbContents += std::string(strBuf);
+ dpbContents += " - DPBStorageSubresource: ";
+ dpbContents += std::to_string(dpbEntry.ReconstructedPictureSubresource);
+ dpbContents += "}\n";
+ }
+
+ debug_printf("[D3D12 Video Encoder Picture Manager H264] DPB has %d frames - DPB references for frame with POC "
+ "%d (frame_num: %d) are: \n %s \n",
+ m_rDPBStorageManager.get_number_of_pics_in_dpb(),
+ m_curFrameState.PictureOrderCountNumber,
+ m_curFrameState.FrameDecodingOrderNumber,
+ dpbContents.c_str());
+ }
+}
+
+// Advances state to next frame in GOP; subsequent calls to GetCurrentFrame* point to the advanced frame status
+void
+d3d12_video_encoder_references_manager_h264::end_frame()
+{
+ debug_printf("[D3D12 Video Encoder Picture Manager H264] %d resources IN USE out of a total of %d ALLOCATED "
+ "resources at end_frame for frame with POC: %d\n",
+ m_rDPBStorageManager.get_number_of_in_use_allocations(),
+ m_rDPBStorageManager.get_number_of_tracked_allocations(),
+ m_curFrameState.PictureOrderCountNumber);
+
+ // Adds last used (if not null) get_current_frame_recon_pic_output_allocation to DPB for next EncodeFrame if
+ // necessary updates pReferenceFramesReconPictureDescriptors and updates the dpb storage
+
+ update_fifo_dpb_push_front_cur_recon_pic();
+}
+
+bool
+d3d12_video_encoder_references_manager_h264::is_current_frame_used_as_reference()
+{
+ return m_isCurrentFrameUsedAsReference;
+}
+
+void
+d3d12_video_encoder_references_manager_h264::begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData,
+ bool bUsedAsReference)
+{
+ m_curFrameState = *curFrameData.pH264PicData;
+ m_isCurrentFrameUsedAsReference = bUsedAsReference;
+ debug_printf("Marking frame_num %d (POC %d) as reference ? %d\n",
+ curFrameData.pH264PicData->FrameDecodingOrderNumber,
+ curFrameData.pH264PicData->PictureOrderCountNumber,
+ bUsedAsReference);
+
+ // Advance the GOP tracking state
+ bool isDPBFlushNeeded = (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME);
+ if (isDPBFlushNeeded) {
+ reset_gop_tracking_and_dpb();
+ } else {
+ // Get new allocation from DPB storage for reconstructed picture
+ // This is only necessary for the frames that come after an IDR
+ // since in the initial state already has this initialized
+ // and re-initialized by reset_gop_tracking_and_dpb above
+
+ prepare_current_frame_recon_pic_allocation();
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef D3D12_VIDEO_ENCODE_FIFO_REFERENCES_MANAGER_H264_H
+#define D3D12_VIDEO_ENCODE_FIFO_REFERENCES_MANAGER_H264_H
+
+#include "d3d12_video_types.h"
+#include "d3d12_video_encoder_references_manager.h"
+#include "d3d12_video_dpb_storage_manager.h"
+
+class d3d12_video_encoder_references_manager_h264 : public d3d12_video_encoder_references_manager_interface
+{
+ public:
+ void end_frame();
+ void begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData, bool bUsedAsReference);
+ D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE get_current_frame_recon_pic_output_allocation();
+ void get_current_frame_picture_control_data(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation);
+ bool is_current_frame_used_as_reference();
+ D3D12_VIDEO_ENCODE_REFERENCE_FRAMES get_current_reference_frames();
+
+ d3d12_video_encoder_references_manager_h264(bool gopHasInterCodedFrames,
+ d3d12_video_dpb_storage_manager_interface &rDpbStorageManager,
+ uint32_t MaxDPBCapacity);
+
+ ~d3d12_video_encoder_references_manager_h264()
+ { }
+
+ private:
+ // Class helpers
+ void prepare_current_frame_recon_pic_allocation();
+ void reset_gop_tracking_and_dpb();
+ void update_fifo_dpb_push_front_cur_recon_pic();
+ void print_dpb();
+ void print_l0_l1_lists();
+
+ // Class members
+
+ uint32_t m_MaxDPBCapacity = 0;
+
+ struct current_frame_references_data
+ {
+ std::vector<D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264> pReferenceFramesReconPictureDescriptors;
+ D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE ReconstructedPicTexture;
+ };
+
+ d3d12_video_dpb_storage_manager_interface &m_rDPBStorageManager;
+
+ current_frame_references_data m_CurrentFrameReferencesData;
+
+ bool m_gopHasInterFrames = false;
+
+ bool m_isCurrentFrameUsedAsReference = false;
+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 m_curFrameState = {};
+};
+
+#endif
'd3d12_video_dec_references_mgr.cpp',
'd3d12_video_dec_h264.cpp',
'd3d12_video_buffer.cpp',
+ 'd3d12_video_enc.cpp',
+ 'd3d12_video_enc_h264.cpp',
+ 'd3d12_video_encoder_references_manager_h264.cpp',
+ 'd3d12_video_encoder_nalu_writer_h264.cpp',
+ 'd3d12_video_encoder_bitstream_builder_h264.cpp',
+ 'd3d12_video_encoder_bitstream.cpp',
'd3d12_video_texture_array_dpb_manager.cpp',
'd3d12_video_array_of_textures_dpb_manager.cpp',
)