Compare commits
12 Commits
85dee300b5
...
2d5286d2e2
Author | SHA1 | Date |
---|---|---|
![]() |
2d5286d2e2 | |
![]() |
03f2b99ffe | |
![]() |
b19cf1f962 | |
![]() |
7f1e7314de | |
![]() |
f922129255 | |
![]() |
76a74daac2 | |
![]() |
647eba36f3 | |
![]() |
11b628f250 | |
![]() |
ac5c2d9bf2 | |
![]() |
fba333dde5 | |
![]() |
153d0201a8 | |
![]() |
f3d1cda672 |
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
[configuration]
|
||||||
|
|
||||||
|
[OptionRangeFloat]
|
||||||
|
GUIName = Amplificiation
|
||||||
|
OptionName = AMPLIFICATION
|
||||||
|
MinValue = 1.0
|
||||||
|
MaxValue = 6.0
|
||||||
|
StepAmount = 0.25
|
||||||
|
DefaultValue = 2.5
|
||||||
|
|
||||||
|
[/configuration]
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ICtCP Colorspace as defined by Dolby here:
|
||||||
|
// https://professional.dolby.com/siteassets/pdfs/ictcp_dolbywhitepaper_v071.pdf
|
||||||
|
|
||||||
|
/***** Transfer Function *****/
|
||||||
|
|
||||||
|
const float4 m_1 = float4(2610.0 / 16384.0);
|
||||||
|
const float4 m_2 = float4(128.0 * 2523.0 / 4096.0);
|
||||||
|
const float4 m_1_inv = float4(16384.0 / 2610.0);
|
||||||
|
const float4 m_2_inv = float4(4096.0 / (128.0 * 2523.0));
|
||||||
|
|
||||||
|
const float4 c_1 = float4(3424.0 / 4096.0);
|
||||||
|
const float4 c_2 = float4(2413.0 / 4096.0 * 32.0);
|
||||||
|
const float4 c_3 = float4(2392.0 / 4096.0 * 32.0);
|
||||||
|
|
||||||
|
float4 EOTF_inv(float4 lms) {
|
||||||
|
float4 y = pow(lms, m_1);
|
||||||
|
return pow((c_1 + c_2 * y) / (1.0 + c_3 * y), m_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 EOTF(float4 lms) {
|
||||||
|
float4 x = pow(lms, m_2_inv);
|
||||||
|
return pow(-(x - c_1) / (c_3 * x - c_2), m_1_inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is required as scaling in EOTF space is not linear.
|
||||||
|
float EOTF_AMPLIFICATION = EOTF_inv(float4(AMPLIFICATION)).x;
|
||||||
|
|
||||||
|
/***** Linear <--> ICtCp *****/
|
||||||
|
|
||||||
|
const mat4 RGBtoLMS = mat4(
|
||||||
|
1688.0, 683.0, 99.0, 0.0,
|
||||||
|
2146.0, 2951.0, 309.0, 0.0,
|
||||||
|
262.0, 462.0, 3688.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 4096.0) / 4096.0;
|
||||||
|
|
||||||
|
const mat4 LMStoICtCp = mat4(
|
||||||
|
+2048.0, +6610.0, +17933.0, 0.0,
|
||||||
|
+2048.0, -13613.0, -17390.0, 0.0,
|
||||||
|
+0.0, +7003.0, -543.0, 0.0,
|
||||||
|
+0.0, +0.0, +0.0, 4096.0) / 4096.0;
|
||||||
|
|
||||||
|
float4 LinearRGBToICtCP(float4 c)
|
||||||
|
{
|
||||||
|
return LMStoICtCp * EOTF_inv(RGBtoLMS * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** ICtCp <--> Linear *****/
|
||||||
|
|
||||||
|
mat4 ICtCptoLMS = inverse(LMStoICtCp);
|
||||||
|
mat4 LMStoRGB = inverse(RGBtoLMS);
|
||||||
|
|
||||||
|
float4 ICtCpToLinearRGB(float4 c)
|
||||||
|
{
|
||||||
|
return LMStoRGB * EOTF(ICtCptoLMS * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float4 color = Sample();
|
||||||
|
|
||||||
|
// Nothing to do here, we are in SDR
|
||||||
|
if (!OptionEnabled(hdr_output) || !OptionEnabled(linear_space_output)) {
|
||||||
|
SetOutput(color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renormalize Color to be in [0.0 - 1.0] SDR Space. We will revert this later.
|
||||||
|
const float hdr_paper_white = hdr_paper_white_nits / hdr_sdr_white_nits;
|
||||||
|
color.rgb /= hdr_paper_white;
|
||||||
|
|
||||||
|
// Convert Color to Perceptual Color Space. This will allow us to do perceptual
|
||||||
|
// scaling while also being able to use the luminance channel.
|
||||||
|
float4 ictcp_color = LinearRGBToICtCP(color);
|
||||||
|
|
||||||
|
// Scale the color in perceptual space depending on the percieved luminance.
|
||||||
|
//
|
||||||
|
// At low luminances, ~0.0, pow(EOTF_AMPLIFICATION, ~0.0) ~= 1.0, so the
|
||||||
|
// color will appear to be unchanged. This is important as we don't want to
|
||||||
|
// over expose dark colors which would not have otherwise been seen.
|
||||||
|
//
|
||||||
|
// At high luminances, ~1.0, pow(EOTF_AMPLIFICATION, ~1.0) ~= EOTF_AMPLIFICATION,
|
||||||
|
// which is equivilant to scaling the color by EOTF_AMPLIFICATION. This is
|
||||||
|
// important as we want to get the most out of the display, and we want to
|
||||||
|
// get bright colors to hit their target brightness.
|
||||||
|
//
|
||||||
|
// For more information, see this desmos demonstrating this scaling process:
|
||||||
|
// https://www.desmos.com/calculator/syjyrjsj5c
|
||||||
|
const float luminance = ictcp_color.x;
|
||||||
|
ictcp_color *= pow(EOTF_AMPLIFICATION, luminance);
|
||||||
|
|
||||||
|
// Convert back to Linear RGB and output the color to the display.
|
||||||
|
// We use hdr_paper_white to renormalize the color to the comfortable
|
||||||
|
// SDR viewing range.
|
||||||
|
SetOutput(hdr_paper_white * ICtCpToLinearRGB(ictcp_color));
|
||||||
|
}
|
|
@ -210,11 +210,6 @@ bool IsRunningAndStarted()
|
||||||
return s_is_started && !s_is_stopping;
|
return s_is_started && !s_is_stopping;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRunningInCurrentThread()
|
|
||||||
{
|
|
||||||
return IsRunning() && IsCPUThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsCPUThread()
|
bool IsCPUThread()
|
||||||
{
|
{
|
||||||
return tls_is_cpu_thread;
|
return tls_is_cpu_thread;
|
||||||
|
|
|
@ -138,9 +138,8 @@ void UndeclareAsHostThread();
|
||||||
std::string StopMessage(bool main_thread, std::string_view message);
|
std::string StopMessage(bool main_thread, std::string_view message);
|
||||||
|
|
||||||
bool IsRunning();
|
bool IsRunning();
|
||||||
bool IsRunningAndStarted(); // is running and the CPU loop has been entered
|
bool IsRunningAndStarted(); // is running and the CPU loop has been entered
|
||||||
bool IsRunningInCurrentThread(); // this tells us whether we are running in the CPU thread.
|
bool IsCPUThread(); // this tells us whether we are the CPU thread.
|
||||||
bool IsCPUThread(); // this tells us whether we are the CPU thread.
|
|
||||||
bool IsGPUThread();
|
bool IsGPUThread();
|
||||||
bool IsHostThread();
|
bool IsHostThread();
|
||||||
|
|
||||||
|
|
|
@ -402,7 +402,7 @@ static void CompressAndDumpState(CompressAndDumpState_args& save_args)
|
||||||
File::IOFile f(temp_filename, "wb");
|
File::IOFile f(temp_filename, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
{
|
{
|
||||||
Core::DisplayMessage("Could not save state", 2000);
|
Core::DisplayMessage("Failed to create state file", 2000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +413,9 @@ static void CompressAndDumpState(CompressAndDumpState_args& save_args)
|
||||||
else
|
else
|
||||||
f.WriteBytes(buffer_data, buffer_size);
|
f.WriteBytes(buffer_data, buffer_size);
|
||||||
|
|
||||||
|
if (!f.IsGood())
|
||||||
|
Core::DisplayMessage("Failed to write state file", 2000);
|
||||||
|
|
||||||
const std::string last_state_filename = File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav";
|
const std::string last_state_filename = File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav";
|
||||||
const std::string last_state_dtmname = last_state_filename + ".dtm";
|
const std::string last_state_dtmname = last_state_filename + ".dtm";
|
||||||
const std::string dtmname = filename + ".dtm";
|
const std::string dtmname = filename + ".dtm";
|
||||||
|
@ -448,12 +451,20 @@ static void CompressAndDumpState(CompressAndDumpState_args& save_args)
|
||||||
// Move written state to final location.
|
// Move written state to final location.
|
||||||
// TODO: This should also be atomic. This is possible on all systems, but needs a special
|
// TODO: This should also be atomic. This is possible on all systems, but needs a special
|
||||||
// implementation of IOFile on Windows.
|
// implementation of IOFile on Windows.
|
||||||
f.Close();
|
if (!f.Close())
|
||||||
File::Rename(temp_filename, filename);
|
Core::DisplayMessage("Failed to close state file", 2000);
|
||||||
|
|
||||||
|
if (!File::Rename(temp_filename, filename))
|
||||||
|
{
|
||||||
|
Core::DisplayMessage("Failed to rename state file", 2000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::filesystem::path temp_path(filename);
|
||||||
|
Core::DisplayMessage(fmt::format("Saved State to {}", temp_path.filename().string()), 2000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path tempfilename(filename);
|
|
||||||
Core::DisplayMessage(fmt::format("Saved State to {}", tempfilename.filename().string()), 2000);
|
|
||||||
Host_UpdateMainFrame();
|
Host_UpdateMainFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -315,10 +315,12 @@ void BalloonTip::UpdateBoundsAndRedraw(const QPoint& target_arrow_tip_position,
|
||||||
// Place the arrow tip at the target position whether the arrow tip is drawn or not
|
// Place the arrow tip at the target position whether the arrow tip is drawn or not
|
||||||
const int target_balloontip_global_x =
|
const int target_balloontip_global_x =
|
||||||
target_arrow_tip_position.x() - static_cast<int>(arrow_tip_x);
|
target_arrow_tip_position.x() - static_cast<int>(arrow_tip_x);
|
||||||
const int rightmost_valid_balloontip_global_x = screen_rect.width() - size_hint.width();
|
const int rightmost_valid_balloontip_global_x =
|
||||||
|
screen_rect.left() + screen_rect.width() - size_hint.width();
|
||||||
// If the balloon would extend off the screen, push it left or right until it's not
|
// If the balloon would extend off the screen, push it left or right until it's not
|
||||||
const int actual_balloontip_global_x =
|
const int actual_balloontip_global_x =
|
||||||
std::max(0, std::min(rightmost_valid_balloontip_global_x, target_balloontip_global_x));
|
std::max(screen_rect.left(),
|
||||||
|
std::min(rightmost_valid_balloontip_global_x, target_balloontip_global_x));
|
||||||
// The tip pixel should be in the middle of the control, and arrow_tip_exterior_y is at the bottom
|
// The tip pixel should be in the middle of the control, and arrow_tip_exterior_y is at the bottom
|
||||||
// of that pixel. When arrow_at_bottom is true the arrow is above arrow_tip_exterior_y and so the
|
// of that pixel. When arrow_at_bottom is true the arrow is above arrow_tip_exterior_y and so the
|
||||||
// tip pixel is in the right place, but when it's false the arrow is below arrow_tip_exterior_y
|
// tip pixel is in the right place, but when it's false the arrow is below arrow_tip_exterior_y
|
||||||
|
|
|
@ -164,8 +164,23 @@ void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
|
||||||
return;
|
return;
|
||||||
NSView* view = static_cast<NSView*>(wsi.render_surface);
|
NSView* view = static_cast<NSView*>(wsi.render_surface);
|
||||||
CAMetalLayer* layer = [CAMetalLayer layer];
|
CAMetalLayer* layer = [CAMetalLayer layer];
|
||||||
|
|
||||||
|
Util::PopulateBackendInfo(&g_Config);
|
||||||
|
|
||||||
|
if (g_Config.backend_info.bSupportsHDROutput && g_Config.bHDR)
|
||||||
|
{
|
||||||
|
[layer setWantsExtendedDynamicRangeContent:YES];
|
||||||
|
[layer setPixelFormat:MTLPixelFormatRGBA16Float];
|
||||||
|
|
||||||
|
const CFStringRef name = kCGColorSpaceExtendedLinearSRGB;
|
||||||
|
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(name);
|
||||||
|
[layer setColorspace:colorspace];
|
||||||
|
CGColorSpaceRelease(colorspace);
|
||||||
|
}
|
||||||
|
|
||||||
[view setWantsLayer:YES];
|
[view setWantsLayer:YES];
|
||||||
[view setLayer:layer];
|
[view setLayer:layer];
|
||||||
|
|
||||||
wsi.render_surface = layer;
|
wsi.render_surface = layer;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AppKit/NSScreen.h>
|
||||||
#include <Metal/Metal.h>
|
#include <Metal/Metal.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "VideoCommon/AbstractShader.h"
|
#include "VideoCommon/AbstractShader.h"
|
||||||
|
|
|
@ -77,6 +77,8 @@ void Metal::Util::PopulateBackendInfo(VideoConfig* config)
|
||||||
config->backend_info.bSupportsPartialMultisampleResolve = false;
|
config->backend_info.bSupportsPartialMultisampleResolve = false;
|
||||||
config->backend_info.bSupportsDynamicVertexLoader = true;
|
config->backend_info.bSupportsDynamicVertexLoader = true;
|
||||||
config->backend_info.bSupportsVSLinePointExpand = true;
|
config->backend_info.bSupportsVSLinePointExpand = true;
|
||||||
|
config->backend_info.bSupportsHDROutput =
|
||||||
|
1.0 < [[NSScreen deepestScreen] maximumPotentialExtendedDynamicRangeColorComponentValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Metal::Util::PopulateBackendInfoAdapters(VideoConfig* config,
|
void Metal::Util::PopulateBackendInfoAdapters(VideoConfig* config,
|
||||||
|
|
|
@ -232,18 +232,17 @@ const std::vector<std::unique_ptr<VideoBackendBase>>& VideoBackendBase::GetAvail
|
||||||
static auto s_available_backends = [] {
|
static auto s_available_backends = [] {
|
||||||
std::vector<std::unique_ptr<VideoBackendBase>> backends;
|
std::vector<std::unique_ptr<VideoBackendBase>> backends;
|
||||||
|
|
||||||
// OGL > D3D11 > D3D12 > Vulkan > SW > Null
|
|
||||||
// On macOS, we prefer Vulkan over OpenGL due to OpenGL support being deprecated by Apple.
|
|
||||||
#ifdef HAS_OPENGL
|
|
||||||
backends.push_back(std::make_unique<OGL::VideoBackend>());
|
|
||||||
#endif
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
backends.push_back(std::make_unique<DX11::VideoBackend>());
|
backends.push_back(std::make_unique<DX11::VideoBackend>());
|
||||||
backends.push_back(std::make_unique<DX12::VideoBackend>());
|
backends.push_back(std::make_unique<DX12::VideoBackend>());
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAS_OPENGL
|
||||||
|
backends.push_back(std::make_unique<OGL::VideoBackend>());
|
||||||
|
#endif
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// Emplace the Vulkan backend at the beginning so it takes precedence over OpenGL.
|
// Emplace the Vulkan backend at the beginning so it takes precedence over OpenGL.
|
||||||
|
// On macOS, we prefer Vulkan over OpenGL due to OpenGL support being deprecated by Apple.
|
||||||
backends.emplace(backends.begin(), std::make_unique<Vulkan::VideoBackend>());
|
backends.emplace(backends.begin(), std::make_unique<Vulkan::VideoBackend>());
|
||||||
#else
|
#else
|
||||||
backends.push_back(std::make_unique<Vulkan::VideoBackend>());
|
backends.push_back(std::make_unique<Vulkan::VideoBackend>());
|
||||||
|
|
Loading…
Reference in New Issue