Skip to content
Merged
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
19 changes: 19 additions & 0 deletions src/amf/amf_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,26 @@ namespace amf {
bool enable_ssim_feedback = false;

// --- High Motion Quality Boost (encoder-level, separate from PA) ---
// Default nullopt = do not set the property, let the AMD driver decide
// (FFmpeg amfenc.c never sets this property either). Some AMD driver
// releases (e.g. Adrenalin 26.5.x on RDNA4) appear to expose latent VCN
// bugs when this is enabled, leading to encoder freezes after ~minutes.
std::optional<bool> high_motion_quality_boost_enable;

// --- Low Latency Mode (encoder-level) ---
// Default nullopt = do not set the property, let the driver default decide.
// Matches FFmpeg amfenc behavior (only set when user opts in or Smart
// Access Video is enabled). Streaming workloads usually want this true,
// but exposing it lets users disable it as a workaround for driver bugs.
std::optional<bool> lowlatency_mode;

// --- Input Queue Size (async_depth) ---
// Default nullopt = do not set the property; AMD driver default ~16,
// matches FFmpeg amfenc default. Sunshine historically forced 1 for
// minimum latency, but that is the most fragile code path inside the
// driver. Users can opt-in to 1 for absolute lowest latency or larger
// values (4/8/16) as a workaround for driver freezes.
std::optional<int> input_queue_size;
};

} // namespace amf
23 changes: 15 additions & 8 deletions src/amf/amf_d3d11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,12 @@ namespace amf {
if (config.preanalysis) encoder->SetProperty(AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE, !!(*config.preanalysis));
if (config.vbaq) encoder->SetProperty(AMF_VIDEO_ENCODER_ENABLE_VBAQ, !!(*config.vbaq));
encoder->SetProperty(AMF_VIDEO_ENCODER_B_PIC_PATTERN, (amf_int64) 0);
encoder->SetProperty(AMF_VIDEO_ENCODER_LOWLATENCY_MODE, true);
encoder->SetProperty(AMF_VIDEO_ENCODER_INPUT_QUEUE_SIZE, (amf_int64) 1);
// LOWLATENCY_MODE and INPUT_QUEUE_SIZE: only set when user opts in.
// Matches FFmpeg amfenc behavior (FFmpeg never forces these properties).
// Forcing them to true/1 has been observed to expose latent AMD driver
// bugs (see AlkaidLab/foundation-sunshine#666 freeze on RDNA4 26.5.x).
if (config.lowlatency_mode) encoder->SetProperty(AMF_VIDEO_ENCODER_LOWLATENCY_MODE, !!(*config.lowlatency_mode));
if (config.input_queue_size) encoder->SetProperty(AMF_VIDEO_ENCODER_INPUT_QUEUE_SIZE, (amf_int64) *config.input_queue_size);
encoder->SetProperty(AMF_VIDEO_ENCODER_QUERY_TIMEOUT, (amf_int64) 1);

// LTR for RFI (Reference Frame Invalidation, weak-network recovery).
Expand Down Expand Up @@ -222,8 +226,10 @@ namespace amf {
encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE, (amf_int64) AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED);
if (config.preanalysis) encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, !!(*config.preanalysis));
if (config.vbaq) encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, !!(*config.vbaq));
encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_LOWLATENCY_MODE, true);
encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_INPUT_QUEUE_SIZE, (amf_int64) 1);
// LOWLATENCY_MODE and INPUT_QUEUE_SIZE: only set when user opts in.
// See H.264 block above for rationale (FFmpeg-aligned default behavior).
if (config.lowlatency_mode) encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_LOWLATENCY_MODE, !!(*config.lowlatency_mode));
if (config.input_queue_size) encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_INPUT_QUEUE_SIZE, (amf_int64) *config.input_queue_size);
encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, (amf_int64) 1);

if (colorspace.bit_depth == 10) {
Expand Down Expand Up @@ -286,14 +292,15 @@ namespace amf {
encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE, (amf_int64) AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS);
encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_GOP_SIZE, (amf_int64) 0);
if (config.preanalysis) encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE, !!(*config.preanalysis));
encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_INPUT_QUEUE_SIZE, (amf_int64) 1);
// INPUT_QUEUE_SIZE / ENCODING_LATENCY_MODE: only set when user opts in.
// Matches FFmpeg amfenc behavior (never auto-forces LOWEST_LATENCY).
// See AlkaidLab/foundation-sunshine#666 for the RDNA4 freeze that
// motivated stopping aggressive defaults.
if (config.input_queue_size) encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_INPUT_QUEUE_SIZE, (amf_int64) *config.input_queue_size);
encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_QUERY_TIMEOUT, (amf_int64) 1);
if (config.av1_encoding_latency_mode) {
encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE, (amf_int64) *config.av1_encoding_latency_mode);
}
else {
encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE, (amf_int64) AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_LOWEST_LATENCY);
}

// AV1 Screen Content Tools
if (config.av1_screen_content_tools) {
Expand Down
36 changes: 36 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,33 @@ namespace config {
input = to_bool(tmp);
}

void
bool_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<bool> &input) {
std::string tmp;
string_f(vars, name, tmp);

if (tmp.empty()) {
return;
}

input = to_bool(tmp);
}

void
int_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &input, const std::pair<int, int> &range) {
std::optional<int> temp;
int_f(vars, name, temp);

if (!temp) {
return;
}

TUPLE_2D_REF(lower, upper, range);
if (*temp >= lower && *temp <= upper) {
input = *temp;
}
}

void
double_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input) {
std::string tmp;
Expand Down Expand Up @@ -1188,6 +1215,15 @@ namespace config {
int_between_f(vars, "amd_qvbr_quality", video.amd.amd_qvbr_quality, { 1, 51 });
int_between_f(vars, "amd_ltr_frames", video.amd.amd_ltr_frames, { 0, 4 });
int_between_f(vars, "amd_slices_per_frame", video.amd.amd_slices_per_frame, { 0, 4 });
// FFmpeg-aligned opt-in toggles (default nullopt = let AMD driver decide,
// matches FFmpeg amfenc.c behavior of never setting the property unless
// the user explicitly opts in). See AlkaidLab/foundation-sunshine#666 for
// the RDNA4 freeze that motivated removing aggressive defaults.
bool_f(vars, "amd_high_motion_qb", video.amd.amd_high_motion_qb);
bool_f(vars, "amd_lowlatency_mode", video.amd.amd_lowlatency_mode);
int_between_f(vars, "amd_input_queue_size", video.amd.amd_input_queue_size, { 1, 16 });
// AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_* enum: 0=NONE, 1=POWER_SAVING_REAL_TIME, 2=REAL_TIME, 3=LOWEST_LATENCY
int_between_f(vars, "amd_av1_latency_mode", video.amd.amd_av1_latency_mode, { 0, 3 });

int_f(vars, "vt_coder", video.vt.vt_coder, vt::coder_from_view);
int_f(vars, "vt_software", video.vt.vt_allow_sw, vt::allow_software_from_view);
Expand Down
11 changes: 11 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ namespace config {
int amd_qvbr_quality = 23; // QVBR quality level 1-51 (lower=better, default=23)
int amd_ltr_frames = 0; // LTR frames for RFI (0=disabled by default; matches FFmpeg amfenc behavior to avoid static-region color blocks)
int amd_slices_per_frame = 0; // Slices/tiles per frame (0=client decides, 1-4=minimum)
// The properties below historically had aggressive hardcoded defaults that
// forced AMF code paths FFmpeg never touches (HIGH_MOTION_QUALITY_BOOST=on,
// INPUT_QUEUE_SIZE=1, LOWLATENCY_MODE=on, AV1 LOWEST_LATENCY). Those paths
// expose latent AMD driver bugs (e.g. RDNA4 Adrenalin 26.5.x freeze after
// ~minutes, AlkaidLab/foundation-sunshine#666). Default is nullopt =
// "do not call SetProperty" so the driver picks its own default, matching
// FFmpeg amfenc behavior. Users can still opt in via the WebUI.
std::optional<bool> amd_high_motion_qb;
std::optional<bool> amd_lowlatency_mode;
std::optional<int> amd_input_queue_size; // 1-16
std::optional<int> amd_av1_latency_mode; // AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_*
} amd;

struct {
Expand Down
Loading
Loading