Skip to content

Commit 0c84206

Browse files
andygrundmanReenigneArcher
authored andcommitted
feat(nvenc): support for split frame encoding on GPUs with 2+ nvenc blocks. Ported from #3061
1 parent d44979e commit 0c84206

File tree

10 files changed

+112
-18
lines changed

10 files changed

+112
-18
lines changed

docs/configuration.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,6 +2347,46 @@ editing the `conf` file in a text editor. Use the examples as reference.
23472347
</tr>
23482348
</table>
23492349

2350+
### nvenc_split_encode
2351+
2352+
<table>
2353+
<tr>
2354+
<td>Description</td>
2355+
<td colspan="2">
2356+
Split the encoding of each video frame over multiple NVENC hardware units.
2357+
Significantly reduces encoding latency with a marginal compression efficiency penalty.
2358+
This option is ignored if your GPU has a singular NVENC unit.
2359+
@note{This option only applies when using NVENC [encoder](#encoderhttpslocalhost47990configencoder) with HEVC or AV1.}
2360+
@note{Applies to Windows only.}
2361+
</td>
2362+
</tr>
2363+
<tr>
2364+
<td>Default</td>
2365+
<td colspan="2">@code{}
2366+
driver_decides
2367+
@endcode</td>
2368+
</tr>
2369+
<tr>
2370+
<td>Example</td>
2371+
<td colspan="2">@code{}
2372+
nvenc_split_encode = driver_decides
2373+
@endcode</td>
2374+
</tr>
2375+
<tr>
2376+
<td rowspan="3">Choices</td>
2377+
<td>disabled</td>
2378+
<td>Disabled</td>
2379+
</tr>
2380+
<tr>
2381+
<td>driver_decides</td>
2382+
<td>The NVIDIA driver will automatically enable split frame encoding when the following conditions are met: 2+ NVENC units, resolution is at least 4K, and the preset is P1-P4.</td>
2383+
</tr>
2384+
<tr>
2385+
<td>enabled</td>
2386+
<td>Enabled</td>
2387+
</tr>
2388+
</table>
2389+
23502390
### nvenc_latency_over_power
23512391

23522392
<table>

src/config.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ namespace config {
6464
return nvenc::nvenc_two_pass::quarter_resolution;
6565
}
6666

67+
nvenc::nvenc_split_frame_encoding split_encode_from_view(const std::string_view &preset) {
68+
using enum nvenc::nvenc_split_frame_encoding;
69+
if (preset == "disabled") {
70+
return disabled;
71+
}
72+
if (preset == "driver_decides") {
73+
return driver_decides;
74+
}
75+
if (preset == "enabled") {
76+
return force_enabled;
77+
}
78+
BOOST_LOG(warning) << "config: unknown nvenc_split_encode value: " << preset;
79+
return driver_decides;
80+
}
81+
6782
} // namespace nv
6883

6984
namespace amd {
@@ -1083,6 +1098,7 @@ namespace config {
10831098
bool_f(vars, "nvenc_spatial_aq", video.nv.adaptive_quantization);
10841099
generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view);
10851100
bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc);
1101+
generic_f(vars, "nvenc_split_encode", video.nv.split_frame_encoding, nv::split_encode_from_view);
10861102
bool_f(vars, "nvenc_realtime_hags", video.nv_realtime_hags);
10871103
bool_f(vars, "nvenc_opengl_vulkan_on_dxgi", video.nv_opengl_vulkan_on_dxgi);
10881104
bool_f(vars, "nvenc_latency_over_power", video.nv_sunshine_high_power_mode);

src/nvenc/nvenc_base.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ namespace nvenc {
9898
}
9999

100100
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) {
101-
minimum_api_version = NVENCAPI_VERSION;
102101
if (!nvenc && !init_library()) {
103102
return false;
104103
}
@@ -115,7 +114,7 @@ namespace nvenc {
115114
encoder_params.buffer_format = buffer_format;
116115
encoder_params.rfi = true;
117116

118-
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER };
117+
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = {NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER};
119118
session_params.device = device;
120119
session_params.deviceType = device_type;
121120
session_params.apiVersion = NVENCAPI_VERSION;
@@ -136,7 +135,7 @@ namespace nvenc {
136135
return false;
137136
}
138137

139-
NV_ENC_INITIALIZE_PARAMS init_params = { NV_ENC_INITIALIZE_PARAMS_VER };
138+
NV_ENC_INITIALIZE_PARAMS init_params = {NV_ENC_INITIALIZE_PARAMS_VER};
140139

141140
switch (client_config.videoFormat) {
142141
case 0:
@@ -170,11 +169,14 @@ namespace nvenc {
170169
}
171170

172171
auto get_encoder_cap = [&](NV_ENC_CAPS cap) {
173-
NV_ENC_CAPS_PARAM param = { NV_ENC_CAPS_PARAM_VER };
172+
NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER};
174173
param.capsToQuery = cap;
175174
int value = 0;
176-
nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, &param, &value);
177-
return value;
175+
int ret = nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, &param, &value);
176+
if (ret == NV_ENC_SUCCESS) {
177+
return value;
178+
}
179+
return 0;
178180
};
179181

180182
auto buffer_is_10bit = [&]() {
@@ -229,10 +231,17 @@ namespace nvenc {
229231
init_params.frameRateDen = fps.den;
230232
}
231233

234+
if (client_config.videoFormat > 0 && get_encoder_cap(NV_ENC_CAPS_NUM_ENCODER_ENGINES) > 1) {
235+
// SFE supports HEVC/AV1 if you have more than 1 nvenc block
236+
using enum nvenc_split_frame_encoding;
237+
init_params.splitEncodeMode = config.split_frame_encoding == disabled ? NV_ENC_SPLIT_DISABLE_MODE :
238+
config.split_frame_encoding == force_enabled ? NV_ENC_SPLIT_AUTO_FORCED_MODE :
239+
NV_ENC_SPLIT_AUTO_MODE;
240+
}
232241

233242
NV_ENC_PRESET_CONFIG preset_config = {
234243
.version = NV_ENC_PRESET_CONFIG_VER,
235-
.presetCfg = { .version = NV_ENC_CONFIG_VER },
244+
.presetCfg = {.version = NV_ENC_CONFIG_VER},
236245
};
237246
if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) {
238247
BOOST_LOG(error) << "NvEnc: NvEncGetEncodePresetConfigEx() failed: " << last_nvenc_error_string;
@@ -398,15 +407,15 @@ namespace nvenc {
398407
}
399408

400409
if (async_event_handle) {
401-
NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER };
410+
NV_ENC_EVENT_PARAMS event_params = {NV_ENC_EVENT_PARAMS_VER};
402411
event_params.completionEvent = async_event_handle;
403412
if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) {
404413
BOOST_LOG(error) << "NvEnc: NvEncRegisterAsyncEvent() failed: " << last_nvenc_error_string;
405414
return false;
406415
}
407416
}
408417

409-
NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER };
418+
NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = {NV_ENC_CREATE_BITSTREAM_BUFFER_VER};
410419
if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) {
411420
BOOST_LOG(error) << "NvEnc: NvEncCreateBitstreamBuffer() failed: " << last_nvenc_error_string;
412421
return false;
@@ -458,6 +467,13 @@ namespace nvenc {
458467
if (config.insert_filler_data) {
459468
extra += " filler-data";
460469
}
470+
if (client_config.videoFormat > 0 && get_encoder_cap(NV_ENC_CAPS_NUM_ENCODER_ENGINES) > 1) {
471+
if (init_params.splitEncodeMode == NV_ENC_SPLIT_AUTO_MODE) {
472+
extra += " sfe-auto";
473+
} else if (init_params.splitEncodeMode == NV_ENC_SPLIT_AUTO_FORCED_MODE) {
474+
extra += " sfe";
475+
}
476+
}
461477

462478
BOOST_LOG(info) << "NvEnc: created encoder " << video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra;
463479
}
@@ -475,7 +491,7 @@ namespace nvenc {
475491
output_bitstream = nullptr;
476492
}
477493
if (encoder && async_event_handle) {
478-
NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER };
494+
NV_ENC_EVENT_PARAMS event_params = {NV_ENC_EVENT_PARAMS_VER};
479495
event_params.completionEvent = async_event_handle;
480496
if (nvenc_failed(nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params))) {
481497
BOOST_LOG(error) << "NvEnc: NvEncUnregisterAsyncEvent() failed: " << last_nvenc_error_string;
@@ -511,7 +527,7 @@ namespace nvenc {
511527
return {};
512528
}
513529

514-
NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { NV_ENC_MAP_INPUT_RESOURCE_VER };
530+
NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = {NV_ENC_MAP_INPUT_RESOURCE_VER};
515531
mapped_input_buffer.registeredResource = registered_input_buffer;
516532

517533
if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) {
@@ -524,7 +540,7 @@ namespace nvenc {
524540
}
525541
});
526542

527-
NV_ENC_PIC_PARAMS pic_params = { NV_ENC_PIC_PARAMS_VER };
543+
NV_ENC_PIC_PARAMS pic_params = {NV_ENC_PIC_PARAMS_VER};
528544
pic_params.inputWidth = encoder_params.width;
529545
pic_params.inputHeight = encoder_params.height;
530546
pic_params.encodePicFlags = force_idr ? NV_ENC_PIC_FLAG_FORCEIDR : 0;
@@ -540,7 +556,7 @@ namespace nvenc {
540556
return {};
541557
}
542558

543-
NV_ENC_LOCK_BITSTREAM lock_bitstream = { NV_ENC_LOCK_BITSTREAM_VER };
559+
NV_ENC_LOCK_BITSTREAM lock_bitstream = {NV_ENC_LOCK_BITSTREAM_VER};
544560
lock_bitstream.outputBitstream = output_bitstream;
545561
lock_bitstream.doNotWait = async_event_handle ? 1 : 0;
546562

@@ -587,8 +603,7 @@ namespace nvenc {
587603
return false;
588604
}
589605

590-
if (first_frame >= encoder_state.last_rfi_range.first &&
591-
last_frame <= encoder_state.last_rfi_range.second) {
606+
if (first_frame >= encoder_state.last_rfi_range.first && last_frame <= encoder_state.last_rfi_range.second) {
592607
BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " already done";
593608
return true;
594609
}

src/nvenc/nvenc_base.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ namespace nvenc {
132132

133133
private:
134134
NV_ENC_OUTPUT_PTR output_bitstream = nullptr;
135-
uint32_t minimum_api_version = 0;
136135

137136
struct {
138137
uint64_t last_encoded_frame_index = 0;

src/nvenc/nvenc_config.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ namespace nvenc {
1212
full_resolution, ///< Better overall statistics, slower and uses more extra vram
1313
};
1414

15+
enum class nvenc_split_frame_encoding {
16+
disabled, ///< Disable
17+
driver_decides, ///< Let driver decide
18+
force_enabled, ///< Force-enable
19+
};
20+
1521
/**
1622
* @brief NVENC encoder configuration.
1723
*/
@@ -48,6 +54,9 @@ namespace nvenc {
4854

4955
// Add filler data to encoded frames to stay at target bitrate, mainly for testing
5056
bool insert_filler_data = false;
57+
58+
// Enable split-frame encoding if the gpu has multiple NVENC hardware clusters
59+
nvenc_split_frame_encoding split_frame_encoding = nvenc_split_frame_encoding::driver_decides;
5160
};
5261

5362
} // namespace nvenc

src/nvenc/nvenc_d3d11_native.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace nvenc {
5151
}
5252

5353
if (!registered_input_buffer) {
54-
NV_ENC_REGISTER_RESOURCE register_resource = { NV_ENC_REGISTER_RESOURCE_VER };
54+
NV_ENC_REGISTER_RESOURCE register_resource = {NV_ENC_REGISTER_RESOURCE_VER};
5555
register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX;
5656
register_resource.width = encoder_params.width;
5757
register_resource.height = encoder_params.height;

src/nvenc/nvenc_d3d11_on_cuda.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ namespace nvenc {
169169
}
170170

171171
if (!registered_input_buffer) {
172-
NV_ENC_REGISTER_RESOURCE register_resource = { NV_ENC_REGISTER_RESOURCE_VER };
172+
NV_ENC_REGISTER_RESOURCE register_resource = {NV_ENC_REGISTER_RESOURCE_VER};
173173
register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR;
174174
register_resource.width = encoder_params.width;
175175
register_resource.height = encoder_params.height;

src_assets/common/assets/web/config.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ <h1 class="my-4">{{ $t('config.configuration') }}</h1>
289289
"nvenc_spatial_aq": "disabled",
290290
"nvenc_vbv_increase": 0,
291291
"nvenc_realtime_hags": "enabled",
292+
"nvenc_split_encode": "driver_decides",
292293
"nvenc_latency_over_power": "enabled",
293294
"nvenc_opengl_vulkan_on_dxgi": "enabled",
294295
"nvenc_h264_cavlc": "disabled",

src_assets/common/assets/web/configs/tabs/encoders/NvidiaNvencEncoder.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ const config = ref(props.config)
2727
<div class="form-text">{{ $t('config.nvenc_preset_desc') }}</div>
2828
</div>
2929

30+
<!-- Split frame encoding -->
31+
<div class="mb-3" v-if="platform === 'windows'">
32+
<label for="nvenc_split_encode" class="form-label">{{ $t('config.nvenc_split_encode') }}</label>
33+
<select id="nvenc_split_encode" class="form-select" v-model="config.nvenc_split_encode">
34+
<option value="disabled">{{ $t('_common.disabled') }}</option>
35+
<option value="driver_decides">{{ $t('config.nvenc_split_encode_driver_decides_def') }}</option>
36+
<option value="enabled">{{ $t('_common.enabled') }}</option>
37+
</select>
38+
<div class="form-text">{{ $t('config.nvenc_split_encode_desc') }}</div>
39+
</div>
40+
3041
<!-- Two-pass mode -->
3142
<div class="mb-3">
3243
<label for="nvenc_twopass" class="form-label">{{ $t('config.nvenc_twopass') }}</label>

src_assets/common/assets/web/public/assets/locale/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@
301301
"nvenc_realtime_hags_desc": "Currently NVIDIA drivers may freeze in encoder when HAGS is enabled, realtime priority is used and VRAM utilization is close to maximum. Disabling this option lowers the priority to high, sidestepping the freeze at the cost of reduced capture performance when the GPU is heavily loaded.",
302302
"nvenc_spatial_aq": "Spatial AQ",
303303
"nvenc_spatial_aq_desc": "Assign higher QP values to flat regions of the video. Recommended to enable when streaming at lower bitrates.",
304+
"nvenc_split_encode": "Split frame encoding",
305+
"nvenc_split_encode_desc": "Split the encoding of each video frame over multiple NVENC hardware units. Significantly reduces host processing latency with a marginal compression efficiency penalty. The default option enables SFE if the following conditions are met: there are 2+ NVENC units, the stream is 4K resolution or higher, and the preset is P1-P4. Set this to Enabled to use SFE at lower resolutions or higher presets.",
306+
"nvenc_split_encode_driver_decides_def": "Driver decides (default)",
304307
"nvenc_twopass": "Two-pass mode",
305308
"nvenc_twopass_desc": "Adds preliminary encoding pass. This allows to detect more motion vectors, better distribute bitrate across the frame and more strictly adhere to bitrate limits. Disabling it is not recommended since this can lead to occasional bitrate overshoot and subsequent packet loss.",
306309
"nvenc_twopass_disabled": "Disabled (fastest, not recommended)",

0 commit comments

Comments
 (0)