Skip to content

Commit

Permalink
win: amf: HRD blacklist, expand UI docs and various fixes.
Browse files Browse the repository at this point in the history
* Add 'auto' default setting to 'rc_mode' and 'enforce_hrd' which defaults
  to 'cbr' and 'true', respectively.

* Implement HRD blacklist: if a device ID matches, 'rc_mode' and 'enforce_hrd'
  are set to 'vbr_latency' and 'false' when set to 'auto'. The current
  blacklist applies to most RX 400 and RX 500 series, but can be expanded.

* Document all new and existing AMF options in the docs and UI.

* Various cleanups & fixes to AMF option parsing, including proper setting of
  defaults if any unrecognized configuration exists.
  • Loading branch information
psyke83 committed Apr 9, 2024
1 parent 7e26d2f commit ab28277
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 62 deletions.
32 changes: 27 additions & 5 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,8 @@ keybindings

.. note:: This option only applies when using amdvce `encoder`_.

.. warning:: the default auto rate control varies depending on the `amd_enforce_hrd`_ blacklist.

**Choices**

.. table::
Expand All @@ -1516,19 +1518,20 @@ keybindings
=========== ===========
Value Description
=========== ===========
auto cbr, or vbr_latency if HRD is blacklisted
cqp constant qp mode
cbr constant bitrate
vbr_latency variable bitrate, latency constrained
vbr_peak variable bitrate, peak constrained
=========== ===========

**Default**
``cbr``
``auto``

**Example**
.. code-block:: text
amd_rc = cbr
amd_rc = auto
`amd_usage <https://localhost:47990/config/#amd_usage>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -1599,15 +1602,34 @@ keybindings
**Description**
Enable Hypothetical Reference Decoder (HRD) enforcement to help constrain the target bitrate.

.. note:: This option only applies when using amdvce `encoder`_.
.. note:: This option only applies when using amdvce `encoder`_, and will not work
correctly on VCE 3.x cards.

.. warning:: 'auto' will disable HRD if Sunshine detects a VCE 3.x card that is
known to exhibit artifacts. If you experience encoding artifacts on 'auto' that can be
resolved by disabling HRD explicitly, please file an issue so that your card's
Device ID can be added to the blacklist in future releases.

**Choices**

.. table::
:widths: auto

======== ===========
Value Description
======== ===========
auto enable HRD if card is not blacklisted
enabled enable HRD
disabled disable HRD
======== ===========

**Default**
``enabled``
``auto``

**Example**
.. code-block:: text
amd_enforce_hrd = enabled
amd_enforce_hrd = auto
`amd_coder <https://localhost:47990/config/#amd_coder>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
94 changes: 62 additions & 32 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,27 @@ namespace config {
};

enum class rc_av1_e : int {
_auto = -1,
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR,
cqp = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP,
vbr_latency = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
};

enum class rc_hevc_e : int {
_auto = -1,
cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR,
cqp = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP,
vbr_latency = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR
vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
};

enum class rc_h264_e : int {
_auto = -1,
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR,
cqp = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP,
vbr_latency = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR
vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
};

enum class usage_av1_e : int {
Expand All @@ -168,6 +171,12 @@ namespace config {
ultralowlatency = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY
};

enum class enforce_hrd_e : int {
disabled = 0,
enabled,
_auto
};

enum coder_e : int {
_auto = AMF_VIDEO_ENCODER_UNDEFINED,
cabac = AMF_VIDEO_ENCODER_CABAC,
Expand All @@ -176,41 +185,54 @@ namespace config {

template <class T>
std::optional<int>
quality_from_view(const std::string_view &quality_type) {
quality_from_view(const std::string_view &quality_type, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (quality_type == #x##sv) return (int) T::x
_CONVERT_(balanced);
_CONVERT_(quality);
_CONVERT_(speed);
_CONVERT_(balanced);
#undef _CONVERT_
return std::nullopt;
return original;
}

template <class T>
std::optional<int>
rc_from_view(const std::string_view &rc) {
rc_from_view(const std::string_view &rc, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (rc == #x##sv) return (int) T::x
_CONVERT_(_auto);
_CONVERT_(cbr);
_CONVERT_(cqp);
_CONVERT_(vbr_latency);
_CONVERT_(vbr_peak);
_CONVERT_(cbr);
#undef _CONVERT_
return std::nullopt;
return original;
}

template <class T>
std::optional<int>
usage_from_view(const std::string_view &usage) {
usage_from_view(const std::string_view &usage, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (usage == #x##sv) return (int) T::x
_CONVERT_(transcoding);
_CONVERT_(webcam);
_CONVERT_(lowlatency);
_CONVERT_(lowlatency_high_quality);
_CONVERT_(transcoding);
_CONVERT_(ultralowlatency);
_CONVERT_(webcam);
#undef _CONVERT_
return std::nullopt;
return original;
}

template <class T>
std::optional<int>
enforce_hrd_from_view(const std::string_view &enforce_hrd, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (enforce_hrd == #x##sv) return (int) T::x
_CONVERT_(_auto);
_CONVERT_(disabled);
_CONVERT_(enabled);
#undef _CONVERT_
return original;
}

int
Expand All @@ -219,7 +241,7 @@ namespace config {
if (coder == "cabac"sv || coder == "ac"sv) return cabac;
if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc;

return -1;
return _auto;
}
} // namespace amd

Expand Down Expand Up @@ -353,15 +375,18 @@ namespace config {
(int) amd::quality_h264_e::balanced, // quality (h264)
(int) amd::quality_hevc_e::balanced, // quality (hevc)
(int) amd::quality_av1_e::balanced, // quality (av1)
(int) amd::rc_h264_e::cbr, // rate control (h264)
(int) amd::rc_hevc_e::cbr, // rate control (hevc)
(int) amd::rc_av1_e::cbr, // rate control (av1)
(int) amd::rc_h264_e::_auto, // rate control (h264)
(int) amd::rc_hevc_e::_auto, // rate control (hevc)
(int) amd::rc_av1_e::_auto, // rate control (av1)
(int) amd::rc_h264_e::vbr_latency, // fallback rate control (h264)
(int) amd::rc_hevc_e::vbr_latency, // fallback rate control (hevc)
(int) amd::rc_av1_e::vbr_latency, // fallback rate control (av1)
(int) amd::usage_h264_e::ultralowlatency, // usage (h264)
(int) amd::usage_hevc_e::ultralowlatency, // usage (hevc)
(int) amd::usage_av1_e::ultralowlatency, // usage (av1)
0, // preanalysis
1, // vbaq
1, // enforce_hrd
(int) amd::enforce_hrd_e::_auto, // enforce_hrd
(int) amd::coder_e::_auto, // coder
}, // amd

Expand Down Expand Up @@ -982,31 +1007,36 @@ namespace config {
std::string quality;
string_f(vars, "amd_quality", quality);
if (!quality.empty()) {
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality);
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality);
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality);
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality, video.amd.amd_quality_h264);
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality, video.amd.amd_quality_hevc);
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality, video.amd.amd_quality_av1);
}

std::string rc;
string_f(vars, "amd_rc", rc);
int_f(vars, "amd_coder", video.amd.amd_coder, amd::coder_from_view);
if (!rc.empty()) {
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc);
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc);
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc);
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc, video.amd.amd_rc_h264);
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc, video.amd.amd_rc_hevc);
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc, video.amd.amd_rc_av1);
}

std::string usage;
string_f(vars, "amd_usage", usage);
if (!usage.empty()) {
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(usage);
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(usage);
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(usage);
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(usage, video.amd.amd_usage_h264);
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(usage, video.amd.amd_usage_hevc);
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(usage, video.amd.amd_usage_av1);
}

std::string enforce_hrd;
string_f(vars, "amd_enforce_hrd", enforce_hrd);
if (!enforce_hrd.empty()) {
video.amd.amd_enforce_hrd = amd::enforce_hrd_from_view<amd::enforce_hrd_e>(enforce_hrd, video.amd.amd_enforce_hrd);
}

bool_f(vars, "amd_preanalysis", (bool &) video.amd.amd_preanalysis);
bool_f(vars, "amd_vbaq", (bool &) video.amd.amd_vbaq);
bool_f(vars, "amd_enforce_hrd", (bool &) video.amd.amd_enforce_hrd);
int_f(vars, "amd_coder", video.amd.amd_coder, amd::coder_from_view);

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
3 changes: 3 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ namespace config {
std::optional<int> amd_rc_h264;
std::optional<int> amd_rc_hevc;
std::optional<int> amd_rc_av1;
std::optional<int> amd_rc_h264_fallback;
std::optional<int> amd_rc_hevc_fallback;
std::optional<int> amd_rc_av1_fallback;
std::optional<int> amd_usage_h264;
std::optional<int> amd_usage_hevc;
std::optional<int> amd_usage_av1;
Expand Down
14 changes: 14 additions & 0 deletions src/platform/windows/display_vram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,20 @@ namespace platf::dxgi {
return false;
}

// Cards with VCE3.x encoder revision cause artifacts when HRD enforcement is enabled.
std::vector<uint32_t> hrd_blacklist_ids = {
0x67df, // Radeon RX 470/480/570/570X/580/580X/590
0x67ef // Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X
};

for (uint32_t id : hrd_blacklist_ids) {
if (adapter_desc.DeviceId == id) {
BOOST_LOG(warning) << "AMF HRD Enforcement: Device ID " << util::hex(id).to_string_view() << " found on blacklist.";
::video::amd_hrd_blacklist = true;
break;
}
}

// Perform AMF version checks if we're using an AMD GPU. This check is placed in display_vram_t
// to avoid hitting the display_ram_t path which uses software encoding and doesn't touch AMF.
HMODULE amfrt = LoadLibraryW(AMF_DLL_NAME);
Expand Down
13 changes: 7 additions & 6 deletions src/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,9 @@ namespace video {
{ "log_to_dbg"s, []() { return config::sunshine.min_log_level < 2 ? 1 : 0; } },
{ "preencode"s, &config::video.amd.amd_preanalysis },
{ "quality"s, &config::video.amd.amd_quality_av1 },
{ "rc"s, &config::video.amd.amd_rc_av1 },
{ "rc"s, []() { int ret = config::video.amd.amd_rc_av1.value(); return ret == -1 && amd_hrd_blacklist ? config::video.amd.amd_rc_av1_fallback.value() : ret == -1 ? 3 /* CBR */ : ret; } },
{ "usage"s, &config::video.amd.amd_usage_av1 },
{ "enforce_hrd"s, &config::video.amd.amd_enforce_hrd },
{ "enforce_hrd"s, []() { int ret = config::video.amd.amd_enforce_hrd.value(); return ret < 2 ? ret : !amd_hrd_blacklist; } },
},
{}, // SDR-specific options
{}, // HDR-specific options
Expand All @@ -691,10 +691,10 @@ namespace video {
{ "qmax"s, 51 },
{ "qmin"s, 0 },
{ "quality"s, &config::video.amd.amd_quality_hevc },
{ "rc"s, &config::video.amd.amd_rc_hevc },
{ "rc"s, []() { int ret = config::video.amd.amd_rc_hevc.value(); return ret == -1 && amd_hrd_blacklist ? config::video.amd.amd_rc_hevc_fallback.value() : ret == -1 ? 3 /* CBR */ : ret; } },
{ "usage"s, &config::video.amd.amd_usage_hevc },
{ "vbaq"s, &config::video.amd.amd_vbaq },
{ "enforce_hrd"s, &config::video.amd.amd_enforce_hrd },
{ "enforce_hrd"s, []() { int ret = config::video.amd.amd_enforce_hrd.value(); return ret < 2 ? ret : !amd_hrd_blacklist; } },
},
{}, // SDR-specific options
{}, // HDR-specific options
Expand All @@ -711,10 +711,10 @@ namespace video {
{ "qmax"s, 51 },
{ "qmin"s, 0 },
{ "quality"s, &config::video.amd.amd_quality_h264 },
{ "rc"s, &config::video.amd.amd_rc_h264 },
{ "rc"s, []() { int ret = config::video.amd.amd_rc_h264.value(); return ret == -1 && amd_hrd_blacklist ? config::video.amd.amd_rc_h264_fallback.value() : ret == -1 ? 1 /* CBR */ : ret; } },
{ "usage"s, &config::video.amd.amd_usage_h264 },
{ "vbaq"s, &config::video.amd.amd_vbaq },
{ "enforce_hrd"s, &config::video.amd.amd_enforce_hrd },
{ "enforce_hrd"s, []() { int ret = config::video.amd.amd_enforce_hrd.value(); return ret < 2 ? ret : !amd_hrd_blacklist; } },
},
// SDR-specific options
{},
Expand Down Expand Up @@ -937,6 +937,7 @@ namespace video {
static encoder_t *chosen_encoder;
int active_hevc_mode;
int active_av1_mode;
bool amd_hrd_blacklist = false;
bool last_encoder_probe_supported_ref_frames_invalidation = false;

void
Expand Down
1 change: 1 addition & 0 deletions src/video.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ namespace video {

extern int active_hevc_mode;
extern int active_av1_mode;
extern bool amd_hrd_blacklist;
extern bool last_encoder_probe_supported_ref_frames_invalidation;

void
Expand Down
Loading

0 comments on commit ab28277

Please sign in to comment.