Skip to content

Commit

Permalink
feat(win/nvenc): dynamic sdk version selection at runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
ns6089 committed Aug 19, 2024
1 parent 537e3e6 commit 2ae93f7
Show file tree
Hide file tree
Showing 33 changed files with 568 additions and 291 deletions.
16 changes: 12 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@
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
branch = sdk/12.0
[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 Expand Up @@ -58,3 +54,15 @@
path = third-party/wlr-protocols
url = https://gitlab.freedesktop.org/wlroots/wlr-protocols.git
branch = master
[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
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
108 changes: 58 additions & 50 deletions src/nvenc/nvenc_base.cpp → src/nvenc/common_impl/nvenc_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@
*/
#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

0 comments on commit 2ae93f7

Please sign in to comment.