Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(win/nvenc): dynamic sdk version selection at runtime #3040

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,18 @@
path = third-party/nanors
url = https://github.com/sleepybishop/nanors.git
branch = master
[submodule "third-party/nv-codec-headers"]
path = third-party/nv-codec-headers
url = https://github.com/FFmpeg/nv-codec-headers
[submodule "third-party/nvenc-headers/1100"]
path = third-party/nvenc-headers/1100
url = https://github.com/FFmpeg/nv-codec-headers.git
branch = sdk/11.0
[submodule "third-party/nvenc-headers/1200"]
path = third-party/nvenc-headers/1200
url = https://github.com/FFmpeg/nv-codec-headers.git
branch = sdk/12.0
[submodule "third-party/nvenc-headers/1202"]
path = third-party/nvenc-headers/1202
url = https://github.com/FFmpeg/nv-codec-headers.git
branch = master
[submodule "third-party/nvapi-open-source-sdk"]
path = third-party/nvapi-open-source-sdk
url = https://github.com/LizardByte/nvapi-open-source-sdk
Expand Down
4 changes: 0 additions & 4 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ elseif(UNIX)
endif()
endif()

include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/nv-codec-headers/include")
file(GLOB NVENC_SOURCES CONFIGURE_DEPENDS "src/nvenc/*.cpp" "src/nvenc/*.h")
list(APPEND PLATFORM_TARGET_FILES ${NVENC_SOURCES})

configure_file("${CMAKE_SOURCE_DIR}/src/version.h.in" version.h @ONLY)
include_directories("${CMAKE_CURRENT_BINARY_DIR}") # required for importing version.h

Expand Down
8 changes: 7 additions & 1 deletion cmake/compile_definitions/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ file(GLOB NVPREFS_FILES CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/src/platform/windows/nvprefs/*.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/nvprefs/*.h")

include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/nvenc-headers")
file(GLOB_RECURSE NVENC_SOURCES CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/src/nvenc/*.h"
"${CMAKE_SOURCE_DIR}/src/nvenc/*.cpp")

# vigem
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include")

Expand Down Expand Up @@ -57,7 +62,8 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/Common.h"
"${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/Util.h"
"${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/km/BusShared.h"
${NVPREFS_FILES})
${NVPREFS_FILES}
${NVENC_SOURCES})

set(OPENSSL_LIBRARIES
libssl.a
Expand Down
110 changes: 59 additions & 51 deletions src/nvenc/nvenc_base.cpp → src/nvenc/common_impl/nvenc_base.cpp
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
/**
* @file src/nvenc/nvenc_base.cpp
* @file src/nvenc/common_impl/nvenc_base.cpp
* @brief Definitions for abstract platform-agnostic base of standalone NVENC encoder.
*/
#include "nvenc_base.h"

#include "src/config.h"
#include "src/logging.h"
#include "src/utility.h"
#include "nvenc_utils.h"

#define MAKE_NVENC_VER(major, minor) ((major) | ((minor) << 24))
#include "src/utility.h"

// Make sure we check backwards compatibility when bumping the Video Codec SDK version
// Things to look out for:
// - NV_ENC_*_VER definitions where the value inside NVENCAPI_STRUCT_VERSION() was increased
// - Incompatible struct changes in nvEncodeAPI.h (fields removed, semantics changed, etc.)
// - Test both old and new drivers with all supported codecs
#if NVENCAPI_VERSION != MAKE_NVENC_VER(12U, 0U)
#error Check and update NVENC code for backwards compatibility!
#endif
#define NVENC_INT_VERSION (NVENCAPI_MAJOR_VERSION * 100 + NVENCAPI_MINOR_VERSION)

namespace {

#ifdef NVENC_NAMESPACE
using namespace NVENC_NAMESPACE;
#endif

GUID
quality_preset_guid_from_number(unsigned number) {
if (number > 7) number = 7;
Expand Down Expand Up @@ -83,7 +78,11 @@ namespace {

} // namespace

#ifdef NVENC_NAMESPACE
namespace NVENC_NAMESPACE {
#else
namespace nvenc {
#endif

nvenc_base::nvenc_base(NV_ENC_DEVICE_TYPE device_type):
device_type(device_type) {
Expand All @@ -94,25 +93,28 @@ namespace nvenc {
}

bool
nvenc_base::create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format) {
// Pick the minimum NvEncode API version required to support the specified codec
// to maximize driver compatibility. AV1 was introduced in SDK v12.0.
minimum_api_version = (client_config.videoFormat <= 1) ? MAKE_NVENC_VER(11U, 0U) : MAKE_NVENC_VER(12U, 0U);

nvenc_base::create_encoder(
const nvenc_config &config,
const video::config_t &client_config,
const video::sunshine_colorspace_t &sunshine_colorspace,
platf::pix_fmt_e sunshine_buffer_format) {
if (!nvenc && !init_library()) return false;

if (encoder) destroy_encoder();
auto fail_guard = util::fail_guard([this] { destroy_encoder(); });

auto colorspace = nvenc_colorspace_from_sunshine_colorspace(sunshine_colorspace);
auto buffer_format = nvenc_format_from_sunshine_format(sunshine_buffer_format);

encoder_params.width = client_config.width;
encoder_params.height = client_config.height;
encoder_params.buffer_format = buffer_format;
encoder_params.rfi = true;

NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { min_struct_version(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER) };
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER };
session_params.device = device;
session_params.deviceType = device_type;
session_params.apiVersion = minimum_api_version;
session_params.apiVersion = NVENCAPI_VERSION;
if (nvenc_failed(nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) {
BOOST_LOG(error) << "NvEnc: NvEncOpenEncodeSessionEx() failed: " << last_nvenc_error_string;
return false;
Expand All @@ -130,7 +132,7 @@ namespace nvenc {
return false;
}

NV_ENC_INITIALIZE_PARAMS init_params = { min_struct_version(NV_ENC_INITIALIZE_PARAMS_VER) };
NV_ENC_INITIALIZE_PARAMS init_params = { NV_ENC_INITIALIZE_PARAMS_VER };

switch (client_config.videoFormat) {
case 0:
Expand All @@ -143,10 +145,12 @@ namespace nvenc {
init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID;
break;

#if NVENC_INT_VERSION >= 1200
case 2:
// AV1
init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID;
break;
#endif

default:
BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat;
Expand All @@ -164,7 +168,8 @@ namespace nvenc {
}

auto get_encoder_cap = [&](NV_ENC_CAPS cap) {
NV_ENC_CAPS_PARAM param = { min_struct_version(NV_ENC_CAPS_PARAM_VER), cap };
NV_ENC_CAPS_PARAM param = { NV_ENC_CAPS_PARAM_VER };
param.capsToQuery = cap;
int value = 0;
nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, &param, &value);
return value;
Expand All @@ -182,7 +187,8 @@ namespace nvenc {
auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX);
auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX);
if (encoder_params.width > supported_width || encoder_params.height > supported_height) {
BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height;
BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height
<< ", requested " << encoder_params.width << "x" << encoder_params.height;
return false;
}
}
Expand Down Expand Up @@ -217,7 +223,10 @@ namespace nvenc {
init_params.frameRateNum = client_config.framerate;
init_params.frameRateDen = 1;

NV_ENC_PRESET_CONFIG preset_config = { min_struct_version(NV_ENC_PRESET_CONFIG_VER), { min_struct_version(NV_ENC_CONFIG_VER, 7, 8) } };
NV_ENC_PRESET_CONFIG preset_config = {
.version = NV_ENC_PRESET_CONFIG_VER,
.presetCfg = { .version = NV_ENC_CONFIG_VER },
};
if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) {
BOOST_LOG(error) << "NvEnc: NvEncGetEncodePresetConfigEx() failed: " << last_nvenc_error_string;
return false;
Expand Down Expand Up @@ -316,14 +325,20 @@ namespace nvenc {
auto &format_config = enc_config.encodeCodecConfig.hevcConfig;
set_h264_hevc_common_format_config(format_config);
if (buffer_is_10bit()) {
#if NVENC_INT_VERSION >= 1202
format_config.inputBitDepth = NV_ENC_BIT_DEPTH_10;
format_config.outputBitDepth = NV_ENC_BIT_DEPTH_10;
#else
format_config.pixelBitDepthMinus8 = 2;
#endif
}
set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numRefL0, 5);
set_minqp_if_enabled(config.min_qp_hevc);
fill_h264_hevc_vui(format_config.hevcVUIParameters);
break;
}

#if NVENC_INT_VERSION >= 1200
case 2: {
// AV1
auto &format_config = enc_config.encodeCodecConfig.av1Config;
Expand All @@ -334,8 +349,13 @@ namespace nvenc {
}
format_config.enableBitstreamPadding = config.insert_filler_data;
if (buffer_is_10bit()) {
#if NVENC_INT_VERSION >= 1202
format_config.inputBitDepth = NV_ENC_BIT_DEPTH_10;
format_config.outputBitDepth = NV_ENC_BIT_DEPTH_10;
#else
format_config.inputPixelBitDepthMinus8 = 2;
format_config.pixelBitDepthMinus8 = 2;
#endif
}
format_config.colorPrimaries = colorspace.primaries;
format_config.transferCharacteristics = colorspace.tranfer_function;
Expand All @@ -353,6 +373,7 @@ namespace nvenc {
}
break;
}
#endif
}

init_params.encodeConfig = &enc_config;
Expand All @@ -363,15 +384,15 @@ namespace nvenc {
}

if (async_event_handle) {
NV_ENC_EVENT_PARAMS event_params = { min_struct_version(NV_ENC_EVENT_PARAMS_VER) };
NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER };
event_params.completionEvent = async_event_handle;
if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) {
BOOST_LOG(error) << "NvEnc: NvEncRegisterAsyncEvent() failed: " << last_nvenc_error_string;
return false;
}
}

NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { min_struct_version(NV_ENC_CREATE_BITSTREAM_BUFFER_VER) };
NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER };
if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) {
BOOST_LOG(error) << "NvEnc: NvEncCreateBitstreamBuffer() failed: " << last_nvenc_error_string;
return false;
Expand All @@ -397,14 +418,17 @@ namespace nvenc {
if (buffer_is_yuv444()) extra += " yuv444";
if (buffer_is_10bit()) extra += " 10-bit";
if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) extra += " two-pass";
if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) extra += " vbv+" + std::to_string(config.vbv_percentage_increase);
if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) {
extra += " vbv+" + std::to_string(config.vbv_percentage_increase);
}
if (encoder_params.rfi) extra += " rfi";
if (init_params.enableWeightedPrediction) extra += " weighted-prediction";
if (enc_config.rcParams.enableAQ) extra += " spatial-aq";
if (enc_config.rcParams.enableMinQP) extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP);
if (config.insert_filler_data) extra += " filler-data";

BOOST_LOG(info) << "NvEnc: created encoder " << video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra;
BOOST_LOG(info) << "NvEnc: created encoder v" << NVENC_INT_VERSION << " "
<< video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra;
}

encoder_state = {};
Expand All @@ -421,7 +445,7 @@ namespace nvenc {
output_bitstream = nullptr;
}
if (encoder && async_event_handle) {
NV_ENC_EVENT_PARAMS event_params = { min_struct_version(NV_ENC_EVENT_PARAMS_VER) };
NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER };
event_params.completionEvent = async_event_handle;
if (nvenc_failed(nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params))) {
BOOST_LOG(error) << "NvEnc: NvEncUnregisterAsyncEvent() failed: " << last_nvenc_error_string;
Expand Down Expand Up @@ -458,7 +482,7 @@ namespace nvenc {
return {};
}

NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { min_struct_version(NV_ENC_MAP_INPUT_RESOURCE_VER) };
NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { NV_ENC_MAP_INPUT_RESOURCE_VER };
mapped_input_buffer.registeredResource = registered_input_buffer;

if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) {
Expand All @@ -471,7 +495,7 @@ namespace nvenc {
}
});

NV_ENC_PIC_PARAMS pic_params = { min_struct_version(NV_ENC_PIC_PARAMS_VER, 4, 6) };
NV_ENC_PIC_PARAMS pic_params = { NV_ENC_PIC_PARAMS_VER };
pic_params.inputWidth = encoder_params.width;
pic_params.inputHeight = encoder_params.height;
pic_params.encodePicFlags = force_idr ? NV_ENC_PIC_FLAG_FORCEIDR : 0;
Expand All @@ -487,7 +511,7 @@ namespace nvenc {
return {};
}

NV_ENC_LOCK_BITSTREAM lock_bitstream = { min_struct_version(NV_ENC_LOCK_BITSTREAM_VER, 1, 2) };
NV_ENC_LOCK_BITSTREAM lock_bitstream = { NV_ENC_LOCK_BITSTREAM_VER };
lock_bitstream.outputBitstream = output_bitstream;
lock_bitstream.doNotWait = 0;

Expand Down Expand Up @@ -546,7 +570,8 @@ namespace nvenc {
return false;
}

BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index;
BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame
<< " expanding to last encoded frame " << encoder_state.last_encoded_frame_index;
last_frame = encoder_state.last_encoded_frame_index;

encoder_state.last_rfi_range = { first_frame, last_frame };
Expand Down Expand Up @@ -620,21 +645,4 @@ namespace nvenc {

return false;
}

uint32_t
nvenc_base::min_struct_version(uint32_t version, uint32_t v11_struct_version, uint32_t v12_struct_version) {
assert(minimum_api_version);

// Mask off and replace the original NVENCAPI_VERSION
version &= ~NVENCAPI_VERSION;
version |= minimum_api_version;

// If there's a struct version override, apply that too
if (v11_struct_version || v12_struct_version) {
version &= ~(0xFFu << 16);
version |= (((minimum_api_version & 0xFF) >= 12) ? v12_struct_version : v11_struct_version) << 16;
}

return version;
}
} // namespace nvenc
}
Loading
Loading