implement sRGB output gamma, and user settings

This commit is contained in:
goeiecool9999 2025-09-05 11:30:27 +02:00
parent 181176b046
commit 4f08db0b23
11 changed files with 114 additions and 16 deletions

View file

@ -54,6 +54,8 @@ struct LatteGPUState_t
// temporary (replace with proper solution later) // temporary (replace with proper solution later)
bool tvBufferUsesSRGB; bool tvBufferUsesSRGB;
bool drcBufferUsesSRGB; bool drcBufferUsesSRGB;
float tvGamma = 0.0f;
float drcGamma = 0.0f;
// draw state // draw state
bool activeShaderHasError; // if try, at least one currently bound shader stage has an error and cannot be used for drawing bool activeShaderHasError; // if try, at least one currently bound shader stage has an error and cannot be used for drawing
bool repeatTextureInitialization; // if set during rendertarget or texture initialization, repeat the process (textures likely have been invalidated) bool repeatTextureInitialization; // if set during rendertarget or texture initialization, repeat the process (textures likely have been invalidated)

View file

@ -1,5 +1,6 @@
#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h" #include "Cafe/HW/Latte/Renderer/RendererOuputShader.h"
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
#include "config/ActiveSettings.h"
const std::string RendererOutputShader::s_copy_shader_source = const std::string RendererOutputShader::s_copy_shader_source =
R"( R"(
@ -137,12 +138,14 @@ RendererOutputShader::RendererOutputShader(const std::string& vertex_source, con
m_uniformLocations[0].m_loc_outputResolution = m_vertex_shader->GetUniformLocation("outputResolution"); m_uniformLocations[0].m_loc_outputResolution = m_vertex_shader->GetUniformLocation("outputResolution");
m_uniformLocations[0].m_loc_applySRGBEncoding = m_vertex_shader->GetUniformLocation("applySRGBEncoding"); m_uniformLocations[0].m_loc_applySRGBEncoding = m_vertex_shader->GetUniformLocation("applySRGBEncoding");
m_uniformLocations[0].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma"); m_uniformLocations[0].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
m_uniformLocations[0].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
m_uniformLocations[1].m_loc_textureSrcResolution = m_fragment_shader->GetUniformLocation("textureSrcResolution"); m_uniformLocations[1].m_loc_textureSrcResolution = m_fragment_shader->GetUniformLocation("textureSrcResolution");
m_uniformLocations[1].m_loc_nativeResolution = m_fragment_shader->GetUniformLocation("nativeResolution"); m_uniformLocations[1].m_loc_nativeResolution = m_fragment_shader->GetUniformLocation("nativeResolution");
m_uniformLocations[1].m_loc_outputResolution = m_fragment_shader->GetUniformLocation("outputResolution"); m_uniformLocations[1].m_loc_outputResolution = m_fragment_shader->GetUniformLocation("outputResolution");
m_uniformLocations[1].m_loc_applySRGBEncoding = m_fragment_shader->GetUniformLocation("applySRGBEncoding"); m_uniformLocations[1].m_loc_applySRGBEncoding = m_fragment_shader->GetUniformLocation("applySRGBEncoding");
m_uniformLocations[1].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma"); m_uniformLocations[1].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
m_uniformLocations[1].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
} }
} }
@ -180,7 +183,12 @@ void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_
if (locations.m_loc_targetGamma != -1) if (locations.m_loc_targetGamma != -1)
{ {
shader->SetUniform1f(locations.m_loc_targetGamma, GetTargetGamma(padView)); // TODO: hook up to user-pref override or GX calls shader->SetUniform1f(locations.m_loc_targetGamma, padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma());
}
if (locations.m_loc_displayGamma != -1)
{
shader->SetUniform1f(locations.m_loc_displayGamma, GetConfig().userDisplayGamma);
} }
}; };
@ -307,6 +315,7 @@ layout(push_constant) uniform pc {
vec2 outputResolution; vec2 outputResolution;
bool applySRGBEncoding; // true = app requested sRGB encoding bool applySRGBEncoding; // true = app requested sRGB encoding
float targetGamma; float targetGamma;
float displayGamma;
}; };
#else #else
uniform vec2 textureSrcResolution; uniform vec2 textureSrcResolution;
@ -314,6 +323,7 @@ uniform vec2 nativeResolution;
uniform vec2 outputResolution; uniform vec2 outputResolution;
uniform bool applySRGBEncoding; uniform bool applySRGBEncoding;
uniform float targetGamma; uniform float targetGamma;
uniform float displayGamma;
#endif #endif
layout(location = 0) smooth in vec2 passUV; layout(location = 0) smooth in vec2 passUV;
@ -340,11 +350,13 @@ void main()
{ {
outputShader(); // sets colorOut0 outputShader(); // sets colorOut0
if(applySRGBEncoding) if(applySRGBEncoding)
{ colorOut0 = vec4(sRGBEncode(colorOut0.rgb), 1.0f);
colorOut0 = vec4(sRGBEncode(colorOut0.xyz), 1.0f);
} if (displayGamma > 0.0f)
colorOut0 = pow(colorOut0, vec4(targetGamma / displayGamma) );
else
colorOut0 = vec4( sRGBEncode( pow(colorOut0.rgb, vec3(targetGamma)) ), 1.0f);
colorOut0 = pow(colorOut0, vec4(targetGamma / 2.2f) );
} }
)" + shaderSrc; )" + shaderSrc;
@ -384,8 +396,3 @@ void RendererOutputShader::ShutdownStatic()
delete s_hermit_shader; delete s_hermit_shader;
delete s_hermit_shader_ud; delete s_hermit_shader_ud;
} }
float RendererOutputShader::GetTargetGamma(const bool padView)
{
return 2.4f;
}

View file

@ -46,8 +46,6 @@ public:
static std::string PrependFragmentPreamble(const std::string& shaderSrc); static std::string PrependFragmentPreamble(const std::string& shaderSrc);
static float GetTargetGamma(const bool padView);
protected: protected:
std::unique_ptr<RendererShader> m_vertex_shader; std::unique_ptr<RendererShader> m_vertex_shader;
std::unique_ptr<RendererShader> m_fragment_shader; std::unique_ptr<RendererShader> m_fragment_shader;
@ -59,6 +57,7 @@ protected:
sint32 m_loc_outputResolution = -1; sint32 m_loc_outputResolution = -1;
sint32 m_loc_applySRGBEncoding = -1; sint32 m_loc_applySRGBEncoding = -1;
sint32 m_loc_targetGamma = -1; sint32 m_loc_targetGamma = -1;
sint32 m_loc_displayGamma = -1;
} m_uniformLocations[2]{}; } m_uniformLocations[2]{};
private: private:

View file

@ -2661,7 +2661,7 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
.offset = 0, .offset = 0,
.size = 3 * sizeof(float) * 2 // 3 vec2's .size = 3 * sizeof(float) * 2 // 3 vec2's
+ 4 // + 1 VkBool32 + 4 // + 1 VkBool32
+ 4 // + 1 float + 4 * 2 // + 2 float
}; };
VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
@ -3055,6 +3055,7 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
Vector2f vecs[3]; Vector2f vecs[3];
VkBool32 applySRGBEncoding; VkBool32 applySRGBEncoding;
float targetGamma; float targetGamma;
float displayGamma;
} pushData; } pushData;
// textureSrcResolution // textureSrcResolution
@ -3072,7 +3073,8 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
pushData.vecs[2] = {(float)imageWidth,(float)imageHeight}; pushData.vecs[2] = {(float)imageWidth,(float)imageHeight};
pushData.applySRGBEncoding = padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB; pushData.applySRGBEncoding = padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB;
pushData.targetGamma = RendererOutputShader::GetTargetGamma(padView); pushData.targetGamma = padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma();
pushData.displayGamma = GetConfig().userDisplayGamma;
vkCmdPushConstants(m_state.currentCommandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushData), &pushData); vkCmdPushConstants(m_state.currentCommandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushData), &pushData);

View file

@ -206,8 +206,12 @@ namespace GX2
void GX2SetTVGamma(float gamma) void GX2SetTVGamma(float gamma)
{ {
if (abs(gamma - 1.0f) > 0.01f) LatteGPUState.tvGamma = (1.0f - gamma);
cemuLog_logDebug(LogType::Force, "TV gamma set to {} which is not supported", gamma); }
void GX2SetDRCGamma(float gamma)
{
LatteGPUState.drcGamma = (1.0f - gamma);
} }
bool GX2GetLastFrame(uint32 deviceId, GX2Texture* textureOut) bool GX2GetLastFrame(uint32 deviceId, GX2Texture* textureOut)
@ -307,6 +311,7 @@ namespace GX2
cafeExportRegister("gx2", GX2SetTVBuffer, LogType::GX2); cafeExportRegister("gx2", GX2SetTVBuffer, LogType::GX2);
cafeExportRegister("gx2", GX2SetTVGamma, LogType::GX2); cafeExportRegister("gx2", GX2SetTVGamma, LogType::GX2);
cafeExportRegister("gx2", GX2SetDRCGamma, LogType::GX2);
cafeExportRegister("gx2", GX2GetLastFrame, LogType::GX2); cafeExportRegister("gx2", GX2GetLastFrame, LogType::GX2);
cafeExportRegister("gx2", GX2GetLastFrameGammaA, LogType::GX2); cafeExportRegister("gx2", GX2GetLastFrameGammaA, LogType::GX2);

View file

@ -6,6 +6,7 @@
#include "config/ActiveSettings.h" #include "config/ActiveSettings.h"
#include "config/LaunchSettings.h" #include "config/LaunchSettings.h"
#include "util/helpers/helpers.h" #include "util/helpers/helpers.h"
#include "Cafe/HW/Latte/Core/Latte.h"
void ActiveSettings::SetPaths(bool isPortableMode, void ActiveSettings::SetPaths(bool isPortableMode,
const fs::path& executablePath, const fs::path& executablePath,
@ -112,6 +113,18 @@ GraphicAPI ActiveSettings::GetGraphicsAPI()
return api; return api;
} }
float ActiveSettings::GetTVGamma()
{
const auto& config = GetConfig();
return config.overrideGammaValue.GetValue() + LatteGPUState.tvGamma * !config.overrideAppGammaPreference.GetValue();
}
float ActiveSettings::GetDRCGamma()
{
const auto& config = GetConfig();
return config.overrideGammaValue.GetValue() + LatteGPUState.drcGamma * !config.overrideAppGammaPreference.GetValue();
}
bool ActiveSettings::AudioOutputOnlyAux() bool ActiveSettings::AudioOutputOnlyAux()
{ {
return s_audio_aux_only; return s_audio_aux_only;

View file

@ -96,6 +96,11 @@ public:
[[nodiscard]] static bool WaitForGX2DrawDoneEnabled(); [[nodiscard]] static bool WaitForGX2DrawDoneEnabled();
[[nodiscard]] static GraphicAPI GetGraphicsAPI(); [[nodiscard]] static GraphicAPI GetGraphicsAPI();
// gamma
[[nodiscard]] static float GetTVGamma();
[[nodiscard]] static float GetDRCGamma();
[[nodiscard]] static float GetPCDisplayGamma();
// audio // audio
[[nodiscard]] static bool AudioOutputOnlyAux(); [[nodiscard]] static bool AudioOutputOnlyAux();
static void EnableAudioOnlyAux(bool state); static void EnableAudioOnlyAux(bool state);

View file

@ -129,6 +129,13 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
graphic_api = graphic.get("api", kOpenGL); graphic_api = graphic.get("api", kOpenGL);
graphic.get("device", graphic_device_uuid); graphic.get("device", graphic_device_uuid);
vsync = graphic.get("VSync", 0); vsync = graphic.get("VSync", 0);
overrideAppGammaPreference = graphic.get("OverrideAppGammaPreference", false);
overrideGammaValue = graphic.get("OverrideGammaValue", 2.2f);
if(overrideGammaValue < 0)
overrideGammaValue = 2.2f;
userDisplayGamma = graphic.get("UserDisplayGamma", 2.2f);
if(userDisplayGamma < 0)
userDisplayGamma = 2.2f;
gx2drawdone_sync = graphic.get("GX2DrawdoneSync", true); gx2drawdone_sync = graphic.get("GX2DrawdoneSync", true);
upscale_filter = graphic.get("UpscaleFilter", kBicubicHermiteFilter); upscale_filter = graphic.get("UpscaleFilter", kBicubicHermiteFilter);
downscale_filter = graphic.get("DownscaleFilter", kLinearFilter); downscale_filter = graphic.get("DownscaleFilter", kLinearFilter);
@ -346,6 +353,9 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser)
graphic.set("api", graphic_api); graphic.set("api", graphic_api);
graphic.set("device", graphic_device_uuid); graphic.set("device", graphic_device_uuid);
graphic.set("VSync", vsync); graphic.set("VSync", vsync);
graphic.set("OverrideAppGammaPreference", overrideAppGammaPreference);
graphic.set("OverrideGammaValue", overrideGammaValue);
graphic.set("UserDisplayGamma", userDisplayGamma);
graphic.set("GX2DrawdoneSync", gx2drawdone_sync); graphic.set("GX2DrawdoneSync", gx2drawdone_sync);
//graphic.set("PrecompiledShaders", precompiled_shaders.GetValue()); //graphic.set("PrecompiledShaders", precompiled_shaders.GetValue());
graphic.set("UpscaleFilter", upscale_filter); graphic.set("UpscaleFilter", upscale_filter);

View file

@ -389,6 +389,11 @@ struct CemuConfig
ConfigValue<bool> render_upside_down{ false }; ConfigValue<bool> render_upside_down{ false };
ConfigValue<bool> async_compile{ true }; ConfigValue<bool> async_compile{ true };
// Gamma
ConfigValue<bool> overrideAppGammaPreference{ false };
ConfigValue<float> overrideGammaValue{ 2.2f };
ConfigValue<float> userDisplayGamma { 2.2f }; // 0 = sRGB, >0 gamma
ConfigValue<bool> vk_accurate_barriers{ true }; ConfigValue<bool> vk_accurate_barriers{ true };
struct struct

View file

@ -361,6 +361,28 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
m_vsync->SetToolTip(_("Controls the vsync state")); m_vsync->SetToolTip(_("Controls the vsync state"));
row->Add(m_vsync, 0, wxALL, 5); row->Add(m_vsync, 0, wxALL, 5);
row->Add(new wxStaticText(box, wxID_ANY, _("Override gamma")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_overrideGamma = new wxCheckBox(box, wxID_ANY, "", wxDefaultPosition, {230, -1});
m_overrideGamma->SetToolTip(_("Ignore app gamma preference"));
row->Add(m_overrideGamma, 0, wxALL, 5);
row->Add(new wxStaticText(box, wxID_ANY, _("Gamma")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_overrideGammaValue = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f);
row->Add(m_overrideGammaValue, 0, wxALL, 5);
row->Add(new wxStaticText(box, wxID_ANY, _("Display Gamma")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxBoxSizer* test = new wxBoxSizer(wxHORIZONTAL);
row->Add(test);
m_userDisplayGamma = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f);
m_userDisplayisSRGB = new wxCheckBox(box, wxID_ANY, "sRGB", wxDefaultPosition, wxDefaultSize);
m_userDisplayisSRGB->SetToolTip(_("Select this if your screen is standard compliant sRGB.\nMore accurate but may result in banding and/or crushed shadows."));
m_userDisplayisSRGB->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnUserDisplaySRGBSelected, this);
test->Add(m_userDisplayGamma, 0, wxALL, 5);
test->Add(m_userDisplayisSRGB, 0, wxALL, 5);
box_sizer->Add(row, 0, wxEXPAND, 5); box_sizer->Add(row, 0, wxEXPAND, 5);
auto* graphic_misc_row = new wxFlexGridSizer(0, 2, 0, 0); auto* graphic_misc_row = new wxFlexGridSizer(0, 2, 0, 0);
@ -1104,6 +1126,9 @@ void GeneralSettings2::StoreConfig()
config.vsync = m_vsync->GetSelection(); config.vsync = m_vsync->GetSelection();
config.overrideAppGammaPreference = m_overrideGamma->IsChecked();
config.overrideGammaValue = m_overrideGammaValue->GetValue();
config.userDisplayGamma = m_userDisplayGamma->GetValue() * !m_userDisplayisSRGB->GetValue();
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked(); config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
config.async_compile = m_async_compile->IsChecked(); config.async_compile = m_async_compile->IsChecked();
@ -1685,6 +1710,15 @@ void GeneralSettings2::ApplyConfig()
// graphics // graphics
m_graphic_api->SetSelection(config.graphic_api); m_graphic_api->SetSelection(config.graphic_api);
m_vsync->SetSelection(config.vsync); m_vsync->SetSelection(config.vsync);
m_overrideGamma->SetValue(config.overrideAppGammaPreference);
m_overrideGammaValue->SetValue(config.overrideGammaValue);
m_userDisplayisSRGB->SetValue(config.userDisplayGamma == 0.0f);
m_userDisplayGamma->SetValue(config.userDisplayGamma);
if(m_userDisplayisSRGB->GetValue())
{
m_userDisplayGamma->Disable();
m_userDisplayGamma->SetValue(2.2f);
}
m_async_compile->SetValue(config.async_compile); m_async_compile->SetValue(config.async_compile);
m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync); m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
m_upscale_filter->SetSelection(config.upscale_filter); m_upscale_filter->SetSelection(config.upscale_filter);
@ -2040,6 +2074,15 @@ void GeneralSettings2::OnGraphicAPISelected(wxCommandEvent& event)
HandleGraphicsApiSelection(); HandleGraphicsApiSelection();
} }
void GeneralSettings2::OnUserDisplaySRGBSelected(wxCommandEvent& event)
{
m_userDisplayGamma->SetValue(2.2f);
if(event.GetInt())
m_userDisplayGamma->Disable();
else
m_userDisplayGamma->Enable();
}
void GeneralSettings2::OnAddPathClicked(wxCommandEvent& event) void GeneralSettings2::OnAddPathClicked(wxCommandEvent& event)
{ {
wxDirDialog path_dialog(this, _("Select a directory containing games."), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); wxDirDialog path_dialog(this, _("Select a directory containing games."), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);

View file

@ -56,6 +56,11 @@ private:
// Graphics // Graphics
wxChoice* m_graphic_api, * m_graphic_device; wxChoice* m_graphic_api, * m_graphic_device;
wxChoice* m_vsync; wxChoice* m_vsync;
wxCheckBox* m_overrideGamma;
wxSpinCtrlDouble* m_overrideGammaValue;
wxSpinCtrlDouble* m_userDisplayGamma;
wxCheckBox* m_userDisplayisSRGB;
wxCheckBox *m_async_compile, *m_gx2drawdone_sync; wxCheckBox *m_async_compile, *m_gx2drawdone_sync;
wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling; wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling;
wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale; wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale;
@ -95,6 +100,7 @@ private:
void OnAudioDeviceSelected(wxCommandEvent& event); void OnAudioDeviceSelected(wxCommandEvent& event);
void OnAudioChannelsSelected(wxCommandEvent& event); void OnAudioChannelsSelected(wxCommandEvent& event);
void OnGraphicAPISelected(wxCommandEvent& event); void OnGraphicAPISelected(wxCommandEvent& event);
void OnUserDisplaySRGBSelected(wxCommandEvent& event);
void OnAddPathClicked(wxCommandEvent& event); void OnAddPathClicked(wxCommandEvent& event);
void OnRemovePathClicked(wxCommandEvent& event); void OnRemovePathClicked(wxCommandEvent& event);
void OnActiveAccountChanged(wxCommandEvent& event); void OnActiveAccountChanged(wxCommandEvent& event);
@ -115,6 +121,7 @@ private:
void UpdateAccountInformation(); void UpdateAccountInformation();
void UpdateOnlineAccounts(); void UpdateOnlineAccounts();
void HandleGraphicsApiSelection(); void HandleGraphicsApiSelection();
void HandleGammaSettings();
void ApplyConfig(); void ApplyConfig();
}; };