This commit is contained in:
jasaaved 2026-03-21 01:27:48 -07:00 committed by GitHub
commit ade2e6f97b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 142 additions and 6 deletions

View File

@ -3,6 +3,11 @@
/*
[configuration]
[OptionBool]
GUIName = Use Display Peak Luminance (DX11+)
OptionName = USE_DISPLAY_PEAK_LUMINANCE
DefaultValue = 0
[OptionRangeFloat]
GUIName = HDR Display Max Nits
OptionName = HDR_DISPLAY_MAX_NITS
@ -54,7 +59,13 @@ void main()
// Find the color luminance (it works better than average)
float sdr_ratio = luminance(color.rgb);
const float auto_hdr_max_white = max(HDR_DISPLAY_MAX_NITS / (hdr_paper_white_nits / hdr_sdr_white_nits), hdr_sdr_white_nits) / hdr_sdr_white_nits;
// When "Use Display Peak Luminance" is enabled, use the peak luminance reported by the display
// (only available on DirectX 11/12). Falls back to the manual slider on other APIs or if
// no display data is available.
const float display_max_nits = (OptionEnabled(USE_DISPLAY_PEAK_LUMINANCE) && hdr_max_luminance_nits > 0.0)
? hdr_max_luminance_nits
: HDR_DISPLAY_MAX_NITS;
const float auto_hdr_max_white = max(display_max_nits / (hdr_paper_white_nits / hdr_sdr_white_nits), hdr_sdr_white_nits) / hdr_sdr_white_nits;
if (sdr_ratio > AUTO_HDR_SHOULDER_START_ALPHA && AUTO_HDR_SHOULDER_START_ALPHA < 1.0)
{
const float auto_hdr_shoulder_ratio = 1.0 - (max(1.0 - sdr_ratio, 0.0) / (1.0 - AUTO_HDR_SHOULDER_START_ALPHA));

View File

@ -22,6 +22,7 @@
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoConfig.h"
using ConfigurationOption = VideoCommon::PostProcessingConfiguration::ConfigurationOption;
using OptionType = ConfigurationOption::OptionType;
@ -43,6 +44,21 @@ PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* paren
setWindowTitle(tr("Post-Processing Shader Configuration"));
PopulateGroups();
// If the display was queried since the shader was last loaded (e.g. window was resized or
// moved to another monitor), sync the slider to the latest queried peak luminance before
// building the widgets so the displayed value is always current.
if (g_backend_info.hdr_max_luminance_nits > 0.f)
{
const auto use_peak_it = m_config_map.find("USE_DISPLAY_PEAK_LUMINANCE");
const auto max_nits_it = m_config_map.find("HDR_DISPLAY_MAX_NITS");
if (use_peak_it != m_config_map.end() && max_nits_it != m_config_map.end() &&
use_peak_it->second->GetConfigurationOption()->m_bool_value)
{
m_post_processor->SetOptionf("HDR_DISPLAY_MAX_NITS", 0, g_backend_info.hdr_max_luminance_nits);
}
}
Create();
ConnectWidgets();
@ -120,6 +136,12 @@ void PostProcessingConfigWindow::Create()
m_tabs->insertTab(0, general, tr("General"));
}
// Apply initial enabled state for options controlled by USE_DISPLAY_PEAK_LUMINANCE
const auto use_peak_it = m_config_map.find("USE_DISPLAY_PEAK_LUMINANCE");
const auto max_nits_it = m_config_map.find("HDR_DISPLAY_MAX_NITS");
if (use_peak_it != m_config_map.end() && max_nits_it != m_config_map.end())
max_nits_it->second->SetEnabled(!use_peak_it->second->GetCheckboxValue());
m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
auto* layout = new QVBoxLayout(this);
@ -152,6 +174,14 @@ void PostProcessingConfigWindow::UpdateBool(ConfigGroup* const config_group, con
m_post_processor->SetOptionb(config_group->GetOptionName(), state);
config_group->EnableSuboptions(state);
if (config_group->GetOptionName() == "USE_DISPLAY_PEAK_LUMINANCE")
{
m_post_processor->SaveOptionsConfiguration();
const auto it = m_config_map.find("HDR_DISPLAY_MAX_NITS");
if (it != m_config_map.end())
it->second->SetEnabled(!state);
}
}
void PostProcessingConfigWindow::UpdateInteger(ConfigGroup* const config_group, const int value)
@ -346,6 +376,14 @@ u32 PostProcessingConfigWindow::ConfigGroup::AddFloat(PostProcessingConfigWindow
return row + 1;
}
void PostProcessingConfigWindow::ConfigGroup::SetEnabled(const bool state)
{
for (auto& slider : m_sliders)
slider->setEnabled(state);
for (auto& value_box : m_value_boxes)
value_box->setEnabled(state);
}
void PostProcessingConfigWindow::ConfigGroup::EnableSuboptions(const bool state)
{
for (auto& it : m_subgroups)

View File

@ -46,6 +46,7 @@ private:
const std::vector<std::unique_ptr<ConfigGroup>>& GetSubGroups() const noexcept;
u32 AddWidgets(PostProcessingConfigWindow* parent, QGridLayout* grid, u32 row);
void EnableSuboptions(bool state);
void SetEnabled(bool state);
int GetCheckboxValue() const;
int GetSliderValue(size_t index) const;
void SetSliderText(size_t index, const QString& text);

View File

@ -5,12 +5,15 @@
#include <algorithm>
#include <cstdint>
#include <winrt/Windows.Graphics.Display.h>
#include <windows.graphics.display.interop.h>
#include "Common/Assert.h"
#include "Common/CommonFuncs.h"
#include "Common/HRWrap.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/ScopeGuard.h"
#include "VideoCommon/VideoConfig.h"
@ -188,9 +191,52 @@ bool SwapChain::CreateSwapChain(bool stereo, bool hdr)
return false;
}
QueryDisplayHDRCapabilities();
return true;
}
void SwapChain::QueryDisplayHDRCapabilities()
{
g_backend_info.hdr_max_luminance_nits = 0.0f;
g_backend_info.hdr_min_luminance_nits = 0.0f;
g_backend_info.hdr_sdr_white_nits = 80.f;
if (!m_hdr)
return;
Microsoft::WRL::ComPtr<IDXGIOutput> output;
Microsoft::WRL::ComPtr<IDXGIOutput6> output6;
DXGI_OUTPUT_DESC1 desc{};
if (FAILED(m_swap_chain->GetContainingOutput(&output)) || FAILED(output.As(&output6)) ||
FAILED(output6->GetDesc1(&desc)))
{
return;
}
g_backend_info.hdr_max_luminance_nits = desc.MaxLuminance;
g_backend_info.hdr_min_luminance_nits = desc.MinLuminance;
try
{
winrt::init_apartment();
Common::ScopeGuard uninit_guard([] { winrt::uninit_apartment(); });
auto statics =
winrt::get_activation_factory<winrt::Windows::Graphics::Display::DisplayInformation,
IDisplayInformationStaticsInterop>();
winrt::Windows::Graphics::Display::DisplayInformation display_info{nullptr};
winrt::check_hresult(statics->GetForMonitor(
desc.Monitor, winrt::guid_of<winrt::Windows::Graphics::Display::IDisplayInformation>(),
winrt::put_abi(display_info)));
g_backend_info.hdr_sdr_white_nits = display_info.GetAdvancedColorInfo().SdrWhiteLevelInNits();
}
catch (...)
{
g_backend_info.hdr_sdr_white_nits = 80.f;
}
}
void SwapChain::DestroySwapChain()
{
DestroySwapChainBuffers();
@ -225,7 +271,7 @@ bool SwapChain::ResizeSwapChain()
m_width = desc.BufferDesc.Width;
m_height = desc.BufferDesc.Height;
}
QueryDisplayHDRCapabilities();
return CreateSwapChainBuffers();
}

View File

@ -3,7 +3,7 @@
#pragma once
#include <dxgi1_5.h>
#include <dxgi1_6.h>
#include <wrl/client.h>
#include "Common/CommonTypes.h"
@ -54,6 +54,7 @@ protected:
u32 GetSwapChainFlags() const;
bool CreateSwapChain(bool stereo = false, bool hdr = false);
void DestroySwapChain();
void QueryDisplayHDRCapabilities();
virtual bool CreateSwapChainBuffers() = 0;
virtual void DestroySwapChainBuffers() = 0;

View File

@ -295,10 +295,32 @@ void PostProcessingConfiguration::LoadOptionsConfiguration()
it.second.m_float_values = float_values;
}
}
else if (it.second.m_option_name == "HDR_DISPLAY_MAX_NITS" &&
g_backend_info.hdr_max_luminance_nits > 0.f)
{
it.second.m_float_values[0] = g_backend_info.hdr_max_luminance_nits;
it.second.m_dirty = true;
std::ostringstream queried_value;
queried_value.imbue(std::locale("C"));
queried_value << g_backend_info.hdr_max_luminance_nits;
ini.GetOrCreateSection(section)->Set(it.second.m_option_name, queried_value.str());
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
}
}
break;
}
}
// If USE_DISPLAY_PEAK_LUMINANCE is checked and we have queried data, sync the slider to the
// queried value so it reflects what the shader is actually using.
const auto use_display_peak = m_options.find("USE_DISPLAY_PEAK_LUMINANCE");
const auto max_nits_option = m_options.find("HDR_DISPLAY_MAX_NITS");
if (use_display_peak != m_options.end() && max_nits_option != m_options.end() &&
use_display_peak->second.m_bool_value && g_backend_info.hdr_max_luminance_nits > 0.f)
{
max_nits_option->second.m_float_values[0] = g_backend_info.hdr_max_luminance_nits;
max_nits_option->second.m_dirty = true;
}
}
void PostProcessingConfiguration::SaveOptionsConfiguration()
@ -637,6 +659,8 @@ std::string PostProcessing::GetUniformBufferHeader(bool user_post_process) const
ss << " int hdr_output;\n";
ss << " float hdr_paper_white_nits;\n";
ss << " float hdr_sdr_white_nits;\n";
ss << " float hdr_max_luminance_nits;\n";
ss << " float hdr_min_luminance_nits;\n";
if (user_post_process)
{
@ -856,6 +880,8 @@ struct BuiltinUniforms
s32 hdr_output;
float hdr_paper_white_nits;
float hdr_sdr_white_nits;
float hdr_max_luminance_nits;
float hdr_min_luminance_nits;
};
size_t PostProcessing::CalculateUniformsSize(bool user_post_process) const
@ -909,9 +935,19 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
builtin_uniforms.linear_space_output = m_framebuffer_format == AbstractTextureFormat::RGBA16F;
// Implies output values can be beyond the 0-1 range
builtin_uniforms.hdr_output = m_framebuffer_format == AbstractTextureFormat::RGBA16F;
builtin_uniforms.hdr_paper_white_nits = g_ActiveConfig.color_correction.fHDRPaperWhiteNits;
// A value of 1 1 1 usually matches 80 nits in HDR
builtin_uniforms.hdr_sdr_white_nits = 80.f;
// Clamp paper white to at least the display's SDR white level when display data is available.
// This prevents hdr_paper_white (= paper_white_nits / sdr_white_nits) from dropping below 1.0,
// which would scale colors up before tone mapping and risk clipping highlights.
const float effective_paper_white =
g_backend_info.hdr_max_luminance_nits > 0.f ?
std::max(g_ActiveConfig.color_correction.fHDRPaperWhiteNits,
g_backend_info.hdr_sdr_white_nits) :
g_ActiveConfig.color_correction.fHDRPaperWhiteNits;
builtin_uniforms.hdr_paper_white_nits = effective_paper_white;
// Queried from the display on DX backends; falls back to 80 (scRGB spec value)
builtin_uniforms.hdr_sdr_white_nits = g_backend_info.hdr_sdr_white_nits;
builtin_uniforms.hdr_max_luminance_nits = g_backend_info.hdr_max_luminance_nits;
builtin_uniforms.hdr_min_luminance_nits = g_backend_info.hdr_min_luminance_nits;
std::memcpy(buffer, &builtin_uniforms, sizeof(builtin_uniforms));
buffer += sizeof(builtin_uniforms);

View File

@ -180,6 +180,9 @@ struct BackendInfo
bool bSupportsVSLinePointExpand = false;
bool bSupportsGLLayerInFS = true;
bool bSupportsHDROutput = false;
float hdr_max_luminance_nits = 0.f;
float hdr_min_luminance_nits = 0.f;
float hdr_sdr_white_nits = 80.f;
bool bSupportsUnrestrictedDepthRange = false;
};