mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-11 07:37:01 +00:00
GX2: Implement GX2SetTVGamma and GX2SetDRCGamma (#1682)
This commit is contained in:
parent
4fa0df6dcf
commit
5390f9338c
22 changed files with 239 additions and 46 deletions
|
|
@ -54,6 +54,8 @@ struct LatteGPUState_t
|
|||
// temporary (replace with proper solution later)
|
||||
bool tvBufferUsesSRGB;
|
||||
bool drcBufferUsesSRGB;
|
||||
float tvGamma = 0.0f;
|
||||
float drcGamma = 0.0f;
|
||||
// draw state
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -120,16 +120,6 @@ uint8* LatteTextureLoader_getInputLinearOptimized(LatteTextureLoaderCtx* texture
|
|||
|
||||
#define LatteTextureLoader_getInputLinearOptimized_(__textureLoader,__x,__y,__stepX,__stepY,__bpp,__sliceIndex,__numSlices,__sample,__pitch,__height) (textureLoader->inputData+((__x/__stepX) + __pitch * (__y/__stepY) + (__sliceIndex + __numSlices * __sample) * __height * __pitch)*(__bpp/8))
|
||||
|
||||
float SRGB_to_RGB(float cs)
|
||||
{
|
||||
float cl;
|
||||
if (cs <= 0.04045f)
|
||||
cl = cs / 12.92f;
|
||||
else
|
||||
cl = powf(((cs + 0.055f) / 1.055f), 2.4f);
|
||||
return cl;
|
||||
}
|
||||
|
||||
void decodeBC1Block(uint8* inputData, float* output4x4RGBA)
|
||||
{
|
||||
// read colors
|
||||
|
|
|
|||
|
|
@ -603,7 +603,7 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
|
|||
shader_unbind(RendererShader::ShaderType::kGeometry);
|
||||
shader_bind(shader->GetVertexShader());
|
||||
shader_bind(shader->GetFragmentShader());
|
||||
shader->SetUniformParameters(*texView, {imageWidth, imageHeight});
|
||||
shader->SetUniformParameters(*texView, {imageWidth, imageHeight}, padView);
|
||||
|
||||
// set viewport
|
||||
glViewportIndexedf(0, imageX, imageY, imageWidth, imageHeight);
|
||||
|
|
@ -620,14 +620,12 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, useLinearTexFilter ? GL_LINEAR : GL_NEAREST);
|
||||
texViewGL->samplerState.filterMag = 0xFFFFFFFF;
|
||||
|
||||
if ((!padView && !LatteGPUState.tvBufferUsesSRGB) || (padView && !LatteGPUState.drcBufferUsesSRGB))
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
uint16 indexData[6] = { 0,1,2,3,4,5 };
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, 5, 6, GL_UNSIGNED_SHORT, indexData);
|
||||
|
||||
if ((!padView && !LatteGPUState.tvBufferUsesSRGB) || (padView && !LatteGPUState.drcBufferUsesSRGB))
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
// unbind texture
|
||||
texture_bindAndActivate(nullptr, 0);
|
||||
|
|
|
|||
|
|
@ -227,6 +227,16 @@ sint32 RendererShaderGL::GetUniformLocation(const char* name)
|
|||
return glGetUniformLocation(m_program, name);
|
||||
}
|
||||
|
||||
void RendererShaderGL::SetUniform1i(sint32 location, sint32 value)
|
||||
{
|
||||
glProgramUniform1i(m_program, location, value);
|
||||
}
|
||||
|
||||
void RendererShaderGL::SetUniform1f(sint32 location, float value)
|
||||
{
|
||||
glProgramUniform1f(m_program, location, value);
|
||||
}
|
||||
|
||||
void RendererShaderGL::SetUniform2fv(sint32 location, void* data, sint32 count)
|
||||
{
|
||||
glProgramUniform2fv(m_program, location, count, (const GLfloat*)data);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ public:
|
|||
GLuint GetShaderObject() const { cemu_assert_debug(m_isCompiled); return m_shader_object; }
|
||||
|
||||
sint32 GetUniformLocation(const char* name) override;
|
||||
|
||||
void SetUniform1i(sint32 location, sint32 value) override;
|
||||
void SetUniform1f(sint32 location, float value) override;
|
||||
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
|
||||
void SetUniform4iv(sint32 location, void* data, sint32 count) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
const std::string RendererOutputShader::s_copy_shader_source =
|
||||
R"(
|
||||
void main()
|
||||
void outputShader()
|
||||
{
|
||||
colorOut0 = vec4(texture(textureSrc, passUV).rgb,1.0);
|
||||
}
|
||||
|
|
@ -49,7 +50,7 @@ vec4 bcFilter(vec2 uv, vec4 texelSize)
|
|||
mix(sample1, sample0, sx), sy);
|
||||
}
|
||||
|
||||
void main(){
|
||||
void outputShader(){
|
||||
vec4 texelSize = vec4( 1.0 / textureSrcResolution.xy, textureSrcResolution.xy);
|
||||
colorOut0 = vec4(bcFilter(passUV, texelSize).rgb,1.0);
|
||||
}
|
||||
|
|
@ -108,7 +109,7 @@ vec3 BicubicHermiteTexture(vec2 uv, vec4 texelSize)
|
|||
return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y);
|
||||
}
|
||||
|
||||
void main(){
|
||||
void outputShader(){
|
||||
vec4 texelSize = vec4( 1.0 / textureSrcResolution.xy, textureSrcResolution.xy);
|
||||
colorOut0 = vec4(BicubicHermiteTexture(passUV, texelSize), 1.0);
|
||||
}
|
||||
|
|
@ -135,14 +136,20 @@ RendererOutputShader::RendererOutputShader(const std::string& vertex_source, con
|
|||
m_uniformLocations[0].m_loc_textureSrcResolution = m_vertex_shader->GetUniformLocation("textureSrcResolution");
|
||||
m_uniformLocations[0].m_loc_nativeResolution = m_vertex_shader->GetUniformLocation("nativeResolution");
|
||||
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_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_nativeResolution = m_fragment_shader->GetUniformLocation("nativeResolution");
|
||||
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_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
|
||||
m_uniformLocations[1].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
|
||||
}
|
||||
}
|
||||
|
||||
void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res) const
|
||||
void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const
|
||||
{
|
||||
sint32 effectiveWidth, effectiveHeight;
|
||||
texture_view.baseTexture->GetEffectiveSize(effectiveWidth, effectiveHeight, 0);
|
||||
|
|
@ -168,6 +175,22 @@ void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_
|
|||
res[1] = (float)output_res.y;
|
||||
shader->SetUniform2fv(locations.m_loc_outputResolution, res, 1);
|
||||
}
|
||||
|
||||
if (locations.m_loc_applySRGBEncoding != -1)
|
||||
{
|
||||
shader->SetUniform1i(locations.m_loc_applySRGBEncoding, padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB);
|
||||
}
|
||||
|
||||
if (locations.m_loc_targetGamma != -1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
setUniforms(m_vertex_shader.get(), m_uniformLocations[0]);
|
||||
setUniforms(m_fragment_shader.get(), m_uniformLocations[1]);
|
||||
|
|
@ -290,16 +313,52 @@ layout(push_constant) uniform pc {
|
|||
vec2 textureSrcResolution;
|
||||
vec2 nativeResolution;
|
||||
vec2 outputResolution;
|
||||
bool applySRGBEncoding; // true = app requested sRGB encoding
|
||||
float targetGamma;
|
||||
float displayGamma;
|
||||
};
|
||||
#else
|
||||
uniform vec2 textureSrcResolution;
|
||||
uniform vec2 nativeResolution;
|
||||
uniform vec2 outputResolution;
|
||||
uniform bool applySRGBEncoding;
|
||||
uniform float targetGamma;
|
||||
uniform float displayGamma;
|
||||
#endif
|
||||
|
||||
layout(location = 0) smooth in vec2 passUV;
|
||||
layout(binding = 0) uniform sampler2D textureSrc;
|
||||
layout(location = 0) out vec4 colorOut0;
|
||||
|
||||
float sRGBEncode(float linear)
|
||||
{
|
||||
if(linear <= 0.0031308)
|
||||
return 12.92f * linear;
|
||||
else
|
||||
return 1.055f * pow(linear, 1.0f / 2.4f) - 0.055f;
|
||||
|
||||
}
|
||||
|
||||
vec3 sRGBEncode(vec3 linear)
|
||||
{
|
||||
return vec3(sRGBEncode(linear.r), sRGBEncode(linear.g), sRGBEncode(linear.b));
|
||||
}
|
||||
|
||||
// fwd. declaration
|
||||
void outputShader();
|
||||
void main()
|
||||
{
|
||||
outputShader(); // sets colorOut0
|
||||
if(applySRGBEncoding)
|
||||
colorOut0 = vec4(sRGBEncode(colorOut0.rgb), 1.0f);
|
||||
|
||||
if (displayGamma > 0.0f)
|
||||
colorOut0 = pow(colorOut0, vec4(targetGamma / displayGamma) );
|
||||
else
|
||||
colorOut0 = vec4( sRGBEncode( pow(colorOut0.rgb, vec3(targetGamma)) ), 1.0f);
|
||||
|
||||
}
|
||||
|
||||
)" + shaderSrc;
|
||||
}
|
||||
void RendererOutputShader::InitializeStatic()
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public:
|
|||
RendererOutputShader(const std::string& vertex_source, const std::string& fragment_source);
|
||||
virtual ~RendererOutputShader() = default;
|
||||
|
||||
void SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res) const;
|
||||
void SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const;
|
||||
|
||||
RendererShader* GetVertexShader() const
|
||||
{
|
||||
|
|
@ -55,6 +55,9 @@ protected:
|
|||
sint32 m_loc_textureSrcResolution = -1;
|
||||
sint32 m_loc_nativeResolution = -1;
|
||||
sint32 m_loc_outputResolution = -1;
|
||||
sint32 m_loc_applySRGBEncoding = -1;
|
||||
sint32 m_loc_targetGamma = -1;
|
||||
sint32 m_loc_displayGamma = -1;
|
||||
} m_uniformLocations[2]{};
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ public:
|
|||
|
||||
virtual sint32 GetUniformLocation(const char* name) = 0;
|
||||
|
||||
virtual void SetUniform1i(sint32 location, sint32 value) = 0;
|
||||
virtual void SetUniform1f(sint32 location, float value) = 0;
|
||||
virtual void SetUniform2fv(sint32 location, void* data, sint32 count) = 0;
|
||||
virtual void SetUniform4iv(sint32 location, void* data, sint32 count) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -232,6 +232,16 @@ sint32 RendererShaderVk::GetUniformLocation(const char* name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform1i(sint32 location, sint32 value)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform1f(sint32 location, float value)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform2fv(sint32 location, void* data, sint32 count)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ public:
|
|||
static void Shutdown();
|
||||
|
||||
sint32 GetUniformLocation(const char* name) override;
|
||||
void SetUniform1i(sint32 location, sint32 value) override;
|
||||
void SetUniform1f(sint32 location, float value) override;
|
||||
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
|
||||
void SetUniform4iv(sint32 location, void* data, sint32 count) override;
|
||||
VkShaderModule& GetShaderModule() { return m_shader_module; }
|
||||
|
|
|
|||
|
|
@ -319,18 +319,8 @@ VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector<VkSurf
|
|||
|
||||
for (const auto& format : formats)
|
||||
{
|
||||
bool useSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB;
|
||||
|
||||
if (useSRGB)
|
||||
{
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
return format;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
return format;
|
||||
}
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
return format;
|
||||
}
|
||||
|
||||
return formats[0];
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ struct SwapchainInfoVk
|
|||
bool mainWindow{};
|
||||
|
||||
bool m_shouldRecreate = false;
|
||||
bool m_usesSRGB = false;
|
||||
VSync m_vsyncState = VSync::Immediate;
|
||||
bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state
|
||||
|
||||
|
|
|
|||
|
|
@ -2590,7 +2590,6 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
|
|||
uint64 hash = 0;
|
||||
hash += (uint64)vertexRendererShader;
|
||||
hash += (uint64)fragmentRendererShader;
|
||||
hash += (uint64)(chainInfo.m_usesSRGB);
|
||||
hash += ((uint64)padView) << 1;
|
||||
|
||||
const auto it = m_backbufferBlitPipelineCache.find(hash);
|
||||
|
|
@ -2660,6 +2659,8 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
|
|||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = 0,
|
||||
.size = 3 * sizeof(float) * 2 // 3 vec2's
|
||||
+ 4 // + 1 VkBool32
|
||||
+ 4 * 2 // + 2 float
|
||||
};
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
|
|
@ -2771,10 +2772,6 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
|
|||
if(chainInfo.m_vsyncState != configValue)
|
||||
stateChanged = true;
|
||||
|
||||
const bool latteBufferUsesSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB;
|
||||
if (chainInfo.m_usesSRGB != latteBufferUsesSRGB)
|
||||
stateChanged = true;
|
||||
|
||||
int width, height;
|
||||
if (mainWindow)
|
||||
WindowSystem::GetWindowPhysSize(width, height);
|
||||
|
|
@ -2799,7 +2796,6 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
|
|||
|
||||
chainInfo.m_shouldRecreate = false;
|
||||
chainInfo.m_vsyncState = configValue;
|
||||
chainInfo.m_usesSRGB = latteBufferUsesSRGB;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3046,24 +3042,35 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
|
|||
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &descriptSet, 0, nullptr);
|
||||
|
||||
|
||||
// update push constants
|
||||
Vector2f pushData[3];
|
||||
struct
|
||||
{
|
||||
Vector2f vecs[3];
|
||||
VkBool32 applySRGBEncoding;
|
||||
float targetGamma;
|
||||
float displayGamma;
|
||||
} pushData;
|
||||
|
||||
// textureSrcResolution
|
||||
sint32 effectiveWidth, effectiveHeight;
|
||||
texView->baseTexture->GetEffectiveSize(effectiveWidth, effectiveHeight, 0);
|
||||
pushData[0] = {(float)effectiveWidth, (float)effectiveHeight};
|
||||
pushData.vecs[0] = {(float)effectiveWidth, (float)effectiveHeight};
|
||||
|
||||
// nativeResolution
|
||||
pushData[1] = {
|
||||
pushData.vecs[1] = {
|
||||
(float)texViewVk->baseTexture->width,
|
||||
(float)texViewVk->baseTexture->height,
|
||||
};
|
||||
|
||||
// outputResolution
|
||||
pushData[2] = {(float)imageWidth,(float)imageHeight};
|
||||
pushData.vecs[2] = {(float)imageWidth,(float)imageHeight};
|
||||
|
||||
vkCmdPushConstants(m_state.currentCommandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float) * 2 * 3, &pushData);
|
||||
pushData.applySRGBEncoding = padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB;
|
||||
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);
|
||||
|
||||
vkCmdDraw(m_state.currentCommandBuffer, 6, 1, 0, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -206,8 +206,12 @@ namespace GX2
|
|||
|
||||
void GX2SetTVGamma(float gamma)
|
||||
{
|
||||
if (abs(gamma - 1.0f) > 0.01f)
|
||||
cemuLog_logDebug(LogType::Force, "TV gamma set to {} which is not supported", gamma);
|
||||
LatteGPUState.tvGamma = (1.0f - gamma);
|
||||
}
|
||||
|
||||
void GX2SetDRCGamma(float gamma)
|
||||
{
|
||||
LatteGPUState.drcGamma = (1.0f - gamma);
|
||||
}
|
||||
|
||||
bool GX2GetLastFrame(uint32 deviceId, GX2Texture* textureOut)
|
||||
|
|
@ -307,6 +311,7 @@ namespace GX2
|
|||
|
||||
cafeExportRegister("gx2", GX2SetTVBuffer, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2SetTVGamma, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2SetDRCGamma, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2GetLastFrame, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2GetLastFrameGammaA, LogType::GX2);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ GLFUNC(PFNGLPROGRAMUNIFORM1IPROC, glProgramUniform1i)
|
|||
GLFUNC(PFNGLPROGRAMUNIFORM2IPROC, glProgramUniform2i)
|
||||
GLFUNC(PFNGLPROGRAMUNIFORM1IVPROC, glProgramUniform1iv)
|
||||
GLFUNC(PFNGLPROGRAMUNIFORM4IVPROC, glProgramUniform4iv)
|
||||
GLFUNC(PFNGLPROGRAMUNIFORM1IPROC, glProgramUniform1f)
|
||||
GLFUNC(PFNGLPROGRAMUNIFORM1FVPROC, glProgramUniform1fv)
|
||||
GLFUNC(PFNGLPROGRAMUNIFORM2FVPROC, glProgramUniform2fv)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "config/ActiveSettings.h"
|
||||
#include "config/LaunchSettings.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
|
||||
void ActiveSettings::SetPaths(bool isPortableMode,
|
||||
const fs::path& executablePath,
|
||||
|
|
@ -112,6 +113,18 @@ GraphicAPI ActiveSettings::GetGraphicsAPI()
|
|||
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()
|
||||
{
|
||||
return s_audio_aux_only;
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ public:
|
|||
[[nodiscard]] static bool WaitForGX2DrawDoneEnabled();
|
||||
[[nodiscard]] static GraphicAPI GetGraphicsAPI();
|
||||
|
||||
// gamma
|
||||
[[nodiscard]] static float GetTVGamma();
|
||||
[[nodiscard]] static float GetDRCGamma();
|
||||
|
||||
// audio
|
||||
[[nodiscard]] static bool AudioOutputOnlyAux();
|
||||
static void EnableAudioOnlyAux(bool state);
|
||||
|
|
|
|||
|
|
@ -129,6 +129,13 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
|
|||
graphic_api = graphic.get("api", kOpenGL);
|
||||
graphic.get("device", graphic_device_uuid);
|
||||
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);
|
||||
upscale_filter = graphic.get("UpscaleFilter", kBicubicHermiteFilter);
|
||||
downscale_filter = graphic.get("DownscaleFilter", kLinearFilter);
|
||||
|
|
@ -346,6 +353,9 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser)
|
|||
graphic.set("api", graphic_api);
|
||||
graphic.set("device", graphic_device_uuid);
|
||||
graphic.set("VSync", vsync);
|
||||
graphic.set("OverrideAppGammaPreference", overrideAppGammaPreference);
|
||||
graphic.set("OverrideGammaValue", overrideGammaValue);
|
||||
graphic.set("UserDisplayGamma", userDisplayGamma);
|
||||
graphic.set("GX2DrawdoneSync", gx2drawdone_sync);
|
||||
//graphic.set("PrecompiledShaders", precompiled_shaders.GetValue());
|
||||
graphic.set("UpscaleFilter", upscale_filter);
|
||||
|
|
|
|||
|
|
@ -389,6 +389,11 @@ struct CemuConfig
|
|||
ConfigValue<bool> render_upside_down{ false };
|
||||
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 };
|
||||
|
||||
struct
|
||||
|
|
|
|||
|
|
@ -295,6 +295,10 @@ bool CemuApp::OnInit()
|
|||
{
|
||||
MSWEnableDarkMode(DarkMode_Always);
|
||||
}
|
||||
|
||||
// extend tooltip duration to the maximum possible value
|
||||
wxToolTip::SetDelay(-1);
|
||||
wxToolTip::SetAutoPop(MAKELPARAM(std::numeric_limits<short>::max(),0));
|
||||
#endif
|
||||
|
||||
for (auto&& path : failedWriteAccess)
|
||||
|
|
|
|||
|
|
@ -377,6 +377,55 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
|
|||
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
||||
}
|
||||
|
||||
{
|
||||
auto box = new wxStaticBox(graphics_panel, wxID_ANY, _("Gamma settings"));
|
||||
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
auto row = new wxFlexGridSizer(0, 2, 0, 0);
|
||||
row->SetFlexibleDirection(wxBOTH);
|
||||
row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
||||
|
||||
auto targetGammaLabel = new wxStaticText(box, wxID_ANY, _("Target Gamma"));
|
||||
row->Add(targetGammaLabel, 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);
|
||||
auto targetGammaTooltip = _("The display gamma to reproduce\nIf you are unsure, set this to 2.2");
|
||||
targetGammaLabel->SetToolTip(targetGammaTooltip);
|
||||
m_overrideGammaValue->SetToolTip(targetGammaTooltip);
|
||||
|
||||
|
||||
auto displayGammaLabel = new wxStaticText(box, wxID_ANY, _("Display Gamma"));
|
||||
row->Add(displayGammaLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
|
||||
wxBoxSizer* srgbCheckBoxSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
row->Add(srgbCheckBoxSizer);
|
||||
m_userDisplayGamma = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f);
|
||||
|
||||
auto displayGammaTooltip = _("The gamma of your monitor\nIf you are unsure, set this to 2.2");
|
||||
m_userDisplayGamma->SetToolTip(displayGammaTooltip);
|
||||
displayGammaLabel->SetToolTip(displayGammaTooltip);
|
||||
|
||||
m_userDisplayisSRGB = new wxCheckBox(box, wxID_ANY, "sRGB", wxDefaultPosition, wxDefaultSize);
|
||||
m_userDisplayisSRGB->SetToolTip(_("Select this if Cemu is being displayed using a piecewise sRGB gamma curve.\n"
|
||||
"This is typically not the case so you can probably leave this unchecked.\n"
|
||||
"Exceptions include HDR displays (with HDR enabled), calibrated SDR displays with Windows 11's Auto Color Management enabled, "
|
||||
"or when using a display profile with a VCGT tag that targets piecewise sRGB.\n"
|
||||
"When this box is selected Cemu will compensate for the piecewise curve to approximate the pure gamma curve of a TV.\n"
|
||||
"Colors will be more accurate, especially in dark scenes, but this may result in banding or crushed shadows, "
|
||||
"so it is best if you display Cemu with pure gamma and do not use this setting."));
|
||||
m_userDisplayisSRGB->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnUserDisplaySRGBSelected, this);
|
||||
|
||||
srgbCheckBoxSizer->Add(m_userDisplayGamma, 0, wxALL, 5);
|
||||
srgbCheckBoxSizer->Add(m_userDisplayisSRGB, 0, wxALL | wxALIGN_CENTER_VERTICAL, 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 title's gamma preference"));
|
||||
row->Add(m_overrideGamma, 0, wxALL, 5);
|
||||
|
||||
box_sizer->Add(row, 0, wxEXPAND, 5);
|
||||
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
||||
}
|
||||
|
||||
{
|
||||
wxString choices[] = { _("Bilinear"), _("Bicubic"), _("Hermite"), _("Nearest Neighbor") };
|
||||
m_upscale_filter = new wxRadioBox(graphics_panel, wxID_ANY, _("Upscale filter"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS);
|
||||
|
|
@ -1104,6 +1153,9 @@ void GeneralSettings2::StoreConfig()
|
|||
|
||||
|
||||
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.async_compile = m_async_compile->IsChecked();
|
||||
|
||||
|
|
@ -1681,6 +1733,15 @@ void GeneralSettings2::ApplyConfig()
|
|||
// graphics
|
||||
m_graphic_api->SetSelection(config.graphic_api);
|
||||
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_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
|
||||
m_upscale_filter->SetSelection(config.upscale_filter);
|
||||
|
|
@ -2037,6 +2098,15 @@ void GeneralSettings2::OnGraphicAPISelected(wxCommandEvent& event)
|
|||
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)
|
||||
{
|
||||
wxDirDialog path_dialog(this, _("Select a directory containing games."), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ private:
|
|||
// Graphics
|
||||
wxChoice* m_graphic_api, * m_graphic_device;
|
||||
wxChoice* m_vsync;
|
||||
wxCheckBox* m_overrideGamma;
|
||||
wxSpinCtrlDouble* m_overrideGammaValue;
|
||||
wxSpinCtrlDouble* m_userDisplayGamma;
|
||||
wxCheckBox* m_userDisplayisSRGB;
|
||||
|
||||
wxCheckBox *m_async_compile, *m_gx2drawdone_sync;
|
||||
wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling;
|
||||
wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale;
|
||||
|
|
@ -95,6 +100,7 @@ private:
|
|||
void OnAudioDeviceSelected(wxCommandEvent& event);
|
||||
void OnAudioChannelsSelected(wxCommandEvent& event);
|
||||
void OnGraphicAPISelected(wxCommandEvent& event);
|
||||
void OnUserDisplaySRGBSelected(wxCommandEvent& event);
|
||||
void OnAddPathClicked(wxCommandEvent& event);
|
||||
void OnRemovePathClicked(wxCommandEvent& event);
|
||||
void OnActiveAccountChanged(wxCommandEvent& event);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue