fix(windows): disable dynamic HDR peak detection on Qualcomm GPUs

Playing HDR content tone-mapped to an SDR display on Windows-on-ARM stutters heavily and flickers in brightness: dynamic peak detection moves the tone-mapping parameters every frame, libplacebo regenerates its tone-map and gamut-map shader LUTs each time, and Qualcomm's Adreno D3D11 driver takes tens of milliseconds per regeneration - roughly 100 ms of work against a 42 ms frame budget at 24 fps.

Scan the DXGI adapter list for a Qualcomm vendor id and set hdr-compute-peak=no when one is present, keeping auto elsewhere; static metadata-driven tone mapping generates its LUTs once.

close #2191
This commit is contained in:
edde746
2026-09-01 07:37:10 +02:00
parent 5fb4c78385
commit efb0e885ae
2 changed files with 45 additions and 4 deletions
+2 -2
View File
@@ -32,7 +32,7 @@ target_compile_features(simdutf PUBLIC cxx_std_17)
target_compile_options(simdutf PRIVATE /W0)
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app flutter_wrapper_plugin)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib" "comctl32.lib")
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib" "comctl32.lib" "dxgi.lib")
target_link_libraries(${BINARY_NAME} PRIVATE "${MPV_LIB_DIR}/libmpv.dll.a")
target_link_libraries(${BINARY_NAME} PRIVATE simdutf)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
@@ -63,7 +63,7 @@ if(PLEZY_BUILD_MPV_PROPERTY_CONTRACT_TESTS)
target_compile_definitions(mpv_player_property_contract_test PRIVATE "NOMINMAX")
target_link_libraries(
mpv_player_property_contract_test
PRIVATE flutter_wrapper_plugin "${MPV_LIB_DIR}/libmpv.dll.a" simdutf "comctl32.lib" "user32.lib"
PRIVATE flutter_wrapper_plugin "${MPV_LIB_DIR}/libmpv.dll.a" simdutf "comctl32.lib" "user32.lib" "dxgi.lib"
)
target_include_directories(
mpv_player_property_contract_test
+43 -2
View File
@@ -1,6 +1,7 @@
#include "mpv_player.h"
#include <commctrl.h>
#include <dxgi.h>
#include <windowsx.h>
#include <unordered_map>
@@ -26,6 +27,45 @@ struct InnerWindowSubclassState {
namespace {
// Whether any GPU on this system is a Qualcomm Adreno.
//
// Dynamic HDR peak detection (hdr-compute-peak) moves the tone-mapping
// parameters every frame, and libplacebo regenerates its tone-map and
// gamut-map shader LUTs whenever they move. Qualcomm's D3D11 driver has no
// host-visible upload path (its libplacebo caps report buf_transfer and
// max_mapped_size as zero), so each regeneration costs tens of milliseconds:
// 4K HDR→SDR playback starves to single-digit fps and the swinging peak
// reads as brightness flicker (#2191). Static metadata-driven tone mapping
// generates its LUTs once, so peak detection is disabled when an Adreno may
// be doing the rendering.
//
// The whole adapter list is scanned rather than predicting mpv's choice: mpv
// takes the DXGI default adapter, and no supported machine pairs an Adreno
// with another GPU, so presence is equivalent to use. This also covers the
// x64 build running emulated on Windows-on-ARM, which an architecture check
// would miss.
bool SystemHasQualcommGpu() {
// Qualcomm's Windows driver reports the FourCC 'QCOM' as its DXGI vendor
// id (seen in the wild on the Adreno X1-85); 0x5143 is Qualcomm's PCI-SIG
// id, matched in case a driver reports that instead.
constexpr UINT kQualcommFourCc = 0x4D4F4351;
constexpr UINT kQualcommPci = 0x5143;
IDXGIFactory1* factory = nullptr;
if (FAILED(::CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) return false;
bool found = false;
IDXGIAdapter1* adapter = nullptr;
for (UINT i = 0; !found && SUCCEEDED(factory->EnumAdapters1(i, &adapter)); ++i) {
DXGI_ADAPTER_DESC1 desc;
if (SUCCEEDED(adapter->GetDesc1(&desc))) {
found = desc.VendorId == kQualcommFourCc || desc.VendorId == kQualcommPci;
}
adapter->Release();
adapter = nullptr;
}
factory->Release();
return found;
}
// Adapts the shared, bounded mpv_node walk onto Flutter's encodable values.
struct EncodableNodeBuilder {
using Value = flutter::EncodableValue;
@@ -581,9 +621,10 @@ bool MpvPlayer::Initialize(HWND view) {
// Let mpv use display/context detection instead of forcing HDR signaling.
mpv_set_option_string(mpv_, "target-colorspace-hint", plezy::mpv_common::TargetColorspaceHint(hdr_enabled_));
// Fallback tone mapping when display doesn't support HDR
// Fallback tone mapping when display doesn't support HDR. Dynamic peak
// detection is pathological on Adreno — see SystemHasQualcommGpu.
mpv_set_option_string(mpv_, "tone-mapping", "auto");
mpv_set_option_string(mpv_, "hdr-compute-peak", "auto");
mpv_set_option_string(mpv_, "hdr-compute-peak", SystemHasQualcommGpu() ? "no" : "auto");
}
// When WASAPI becomes unavailable (sleep, device unplug), fall back to null