diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 2da60166e..0e6c152d1 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -54,6 +54,7 @@ bool GPU_HW::Initialize(HostDisplay* host_display) m_scaled_dithering = g_settings.gpu_scaled_dithering; m_texture_filtering = g_settings.gpu_texture_filter; m_using_uv_limits = ShouldUseUVLimits(); + m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing; PrintSettingsToLog(); if (m_multisamples != g_settings.gpu_multisamples) @@ -111,10 +112,11 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed) const bool use_uv_limits = ShouldUseUVLimits(); *framebuffer_changed = (m_resolution_scale != resolution_scale || m_multisamples != multisamples); - *shaders_changed = (m_resolution_scale != resolution_scale || m_multisamples != multisamples || - m_true_color != g_settings.gpu_true_color || m_per_sample_shading != per_sample_shading || - m_scaled_dithering != g_settings.gpu_scaled_dithering || - m_texture_filtering != g_settings.gpu_texture_filter || m_using_uv_limits != use_uv_limits); + *shaders_changed = + (m_resolution_scale != resolution_scale || m_multisamples != multisamples || + m_true_color != g_settings.gpu_true_color || m_per_sample_shading != per_sample_shading || + m_scaled_dithering != g_settings.gpu_scaled_dithering || m_texture_filtering != g_settings.gpu_texture_filter || + m_using_uv_limits != use_uv_limits || m_chroma_smoothing != g_settings.gpu_24bit_chroma_smoothing); if (m_resolution_scale != resolution_scale) { @@ -147,6 +149,7 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed) m_scaled_dithering = g_settings.gpu_scaled_dithering; m_texture_filtering = g_settings.gpu_texture_filter; m_using_uv_limits = use_uv_limits; + m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing; PrintSettingsToLog(); } diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 55df2374f..2bd162845 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -276,6 +276,7 @@ protected: bool m_true_color = true; bool m_scaled_dithering = false; GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest; + bool m_chroma_smoothing = false; bool m_supports_per_sample_shading = false; bool m_supports_dual_source_blend = false; bool m_using_uv_limits = false; diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index f7d132c03..fedef7a33 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -525,8 +525,9 @@ bool GPU_HW_D3D11::CompileShaders() { for (u8 interlacing = 0; interlacing < 3; interlacing++) { - const std::string ps = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit), - static_cast(interlacing)); + const std::string ps = shadergen.GenerateDisplayFragmentShader( + ConvertToBoolUnchecked(depth_24bit), static_cast(interlacing), + ConvertToBoolUnchecked(depth_24bit) && m_chroma_smoothing); m_display_pixel_shaders[depth_24bit][interlacing] = shader_cache.GetPixelShader(m_device.Get(), ps); if (!m_display_pixel_shaders[depth_24bit][interlacing]) return false; @@ -669,14 +670,15 @@ void GPU_HW_D3D11::UpdateDisplay() } else { + const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale; const u32 vram_offset_x = m_crtc_state.display_vram_left; const u32 vram_offset_y = m_crtc_state.display_vram_top; - const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; - const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; + const u32 scaled_vram_offset_x = vram_offset_x * resolution_scale; + const u32 scaled_vram_offset_y = vram_offset_y * resolution_scale; const u32 display_width = m_crtc_state.display_vram_width; const u32 display_height = m_crtc_state.display_vram_height; - const u32 scaled_display_width = display_width * m_resolution_scale; - const u32 scaled_display_height = display_height * m_resolution_scale; + const u32 scaled_display_width = display_width * resolution_scale; + const u32 scaled_display_height = display_height * resolution_scale; const InterlacedRenderMode interlaced = GetInterlacedRenderMode(); if (IsDisplayDisabled()) @@ -699,8 +701,8 @@ void GPU_HW_D3D11::UpdateDisplay() m_context->PSSetShaderResources(0, 1, m_vram_texture.GetD3DSRVArray()); const u32 reinterpret_field_offset = (interlaced != InterlacedRenderMode::None) ? GetInterlacedDisplayField() : 0; - const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale; - const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * m_resolution_scale; + const u32 reinterpret_start_x = m_crtc_state.regs.X * resolution_scale; + const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * resolution_scale; const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y + reinterpret_field_offset, reinterpret_crop_left, reinterpret_field_offset}; ID3D11PixelShader* display_pixel_shader = diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index 7400c324a..6e0281a39 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -455,8 +455,8 @@ bool GPU_HW_OpenGL::CompilePrograms() for (u8 interlaced = 0; interlaced < 3; interlaced++) { const std::string vs = shadergen.GenerateScreenQuadVertexShader(); - const std::string fs = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit), - static_cast(interlaced)); + const std::string fs = shadergen.GenerateDisplayFragmentShader( + ConvertToBoolUnchecked(depth_24bit), static_cast(interlaced), m_chroma_smoothing); std::optional prog = shader_cache.GetProgram(vs, {}, fs, [this, use_binding_layout](GL::Program& prog) { @@ -673,14 +673,15 @@ void GPU_HW_OpenGL::UpdateDisplay() } else { + const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale; const u32 vram_offset_x = m_crtc_state.display_vram_left; const u32 vram_offset_y = m_crtc_state.display_vram_top; - const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; - const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; + const u32 scaled_vram_offset_x = vram_offset_x * resolution_scale; + const u32 scaled_vram_offset_y = vram_offset_y * resolution_scale; const u32 display_width = m_crtc_state.display_vram_width; const u32 display_height = m_crtc_state.display_vram_height; - const u32 scaled_display_width = display_width * m_resolution_scale; - const u32 scaled_display_height = display_height * m_resolution_scale; + const u32 scaled_display_width = display_width * resolution_scale; + const u32 scaled_display_height = display_height * resolution_scale; const InterlacedRenderMode interlaced = GetInterlacedRenderMode(); if (IsDisplayDisabled()) @@ -710,8 +711,8 @@ void GPU_HW_OpenGL::UpdateDisplay() const u32 reinterpret_field_offset = (interlaced != InterlacedRenderMode::None) ? GetInterlacedDisplayField() : 0; const u32 scaled_flipped_vram_offset_y = m_vram_texture.GetHeight() - scaled_vram_offset_y - reinterpret_field_offset - (scaled_display_height >> height_div2); - const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale; - const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * m_resolution_scale; + const u32 reinterpret_start_x = m_crtc_state.regs.X * resolution_scale; + const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * resolution_scale; const u32 uniforms[4] = {reinterpret_start_x, scaled_flipped_vram_offset_y, reinterpret_crop_left, reinterpret_field_offset}; UploadUniformBuffer(uniforms, sizeof(uniforms)); diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index 3de476be4..c6d6de5f8 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -990,19 +990,35 @@ std::string GPU_HW_ShaderGen::GenerateInterlacedFillFragmentShader() } std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, - GPU_HW::InterlacedRenderMode interlace_mode) + GPU_HW::InterlacedRenderMode interlace_mode, + bool smooth_chroma) { std::stringstream ss; WriteHeader(ss); DefineMacro(ss, "DEPTH_24BIT", depth_24bit); DefineMacro(ss, "INTERLACED", interlace_mode != GPU_HW::InterlacedRenderMode::None); DefineMacro(ss, "INTERLEAVED", interlace_mode == GPU_HW::InterlacedRenderMode::InterleavedFields); + DefineMacro(ss, "SMOOTH_CHROMA", smooth_chroma); WriteCommonFunctions(ss); DeclareUniformBuffer(ss, {"uint2 u_vram_offset", "uint u_crop_left", "uint u_field_offset"}, true); DeclareTexture(ss, "samp0", 0, UsingMSAA()); ss << R"( +float3 RGBToYUV(float3 rgb) +{ + return float3(dot(rgb.rgb, float3(0.299f, 0.587f, 0.114f)), + dot(rgb.rgb, float3(-0.14713f, -0.28886f, 0.436f)), + dot(rgb.rgb, float3(0.615f, -0.51499f, -0.10001f))); +} + +float3 YUVToRGB(float3 yuv) +{ + return float3(dot(yuv, float3(1.0f, 0.0f, 1.13983f)), + dot(yuv, float3(1.0f, -0.39465f, -0.58060f)), + dot(yuv, float3(1.0f, 2.03211f, 0.0f))); +} + float4 LoadVRAM(int2 coords) { #if MULTISAMPLING @@ -1015,12 +1031,61 @@ float4 LoadVRAM(int2 coords) return LOAD_TEXTURE(samp0, coords, 0); #endif } + +float3 SampleVRAM24(uint2 icoords) +{ + // load adjacent 16-bit texels + uint2 clamp_size = uint2(1024, 512); + + // relative to start of scanout + uint2 vram_coords = u_vram_offset + uint2((icoords.x * 3u) / 2u, icoords.y); + uint s0 = RGBA8ToRGBA5551(LoadVRAM(int2((vram_coords % clamp_size) * RESOLUTION_SCALE))); + uint s1 = RGBA8ToRGBA5551(LoadVRAM(int2(((vram_coords + uint2(1, 0)) % clamp_size) * RESOLUTION_SCALE))); + + // select which part of the combined 16-bit texels we are currently shading + uint s1s0 = ((s1 << 16) | s0) >> ((icoords.x & 1u) * 8u); + + // extract components and normalize + return float3(float(s1s0 & 0xFFu) / 255.0, float((s1s0 >> 8u) & 0xFFu) / 255.0, + float((s1s0 >> 16u) & 0xFFu) / 255.0); +} + +float3 SampleVRAMAverage2x2(uint2 icoords) +{ + float3 value = SampleVRAM24(icoords); + value += SampleVRAM24(icoords + uint2(0, 1)); + value += SampleVRAM24(icoords + uint2(1, 0)); + value += SampleVRAM24(icoords + uint2(1, 1)); + return value * 0.25; +} + +float3 SampleVRAM24Smoothed(uint2 icoords) +{ + int2 base = int2(icoords) - 1; + uint2 low = uint2(max(base & ~1, int2(0, 0))); + uint2 high = low + 2u; + float2 coeff = vec2(base & 1) * 0.5 + 0.25; + + float3 p = SampleVRAM24(icoords); + float3 p00 = SampleVRAMAverage2x2(low); + float3 p01 = SampleVRAMAverage2x2(uint2(low.x, high.y)); + float3 p10 = SampleVRAMAverage2x2(uint2(high.x, low.y)); + float3 p11 = SampleVRAMAverage2x2(high); + + float3 s = lerp(lerp(p00, p10, coeff.x), + lerp(p01, p11, coeff.x), + coeff.y); + + float y = RGBToYUV(p).x; + float2 uv = RGBToYUV(s).yz; + return YUVToRGB(float3(y, uv)); +} )"; DeclareFragmentEntryPoint(ss, 0, 1, {}, true, 1); ss << R"( { - uint2 icoords = uint2(v_pos.xy); + uint2 icoords = uint2(v_pos.xy) + uint2(u_crop_left, 0u); #if INTERLACED if ((fixYCoord(icoords.y) & 1u) != u_field_offset) @@ -1034,24 +1099,13 @@ float4 LoadVRAM(int2 coords) #endif #if DEPTH_24BIT - // relative to start of scanout - uint relative_x = (icoords.x + u_crop_left) / RESOLUTION_SCALE; - uint2 vram_coords = u_vram_offset + uint2(((relative_x * 3u) / 2u) * RESOLUTION_SCALE, icoords.y); - - // load adjacent 16-bit texels - uint s0 = RGBA8ToRGBA5551(LoadVRAM(int2(vram_coords % VRAM_SIZE))); - uint s1 = RGBA8ToRGBA5551(LoadVRAM(int2((vram_coords + uint2(RESOLUTION_SCALE, 0)) % VRAM_SIZE))); - - // select which part of the combined 16-bit texels we are currently shading - uint s1s0 = ((s1 << 16) | s0) >> ((relative_x & 1u) * 8u); - - // extract components and normalize - o_col0 = float4(float(s1s0 & 0xFFu) / 255.0, float((s1s0 >> 8u) & 0xFFu) / 255.0, - float((s1s0 >> 16u) & 0xFFu) / 255.0, 1.0); + #if SMOOTH_CHROMA + o_col0 = float4(SampleVRAM24Smoothed(icoords), 1.0); + #else + o_col0 = float4(SampleVRAM24(icoords), 1.0); + #endif #else - // load and return - uint2 vram_coords = u_vram_offset + uint2(icoords.x + u_crop_left, icoords.y); - o_col0 = LoadVRAM(int2(vram_coords % VRAM_SIZE)); + o_col0 = float4(LoadVRAM(int2((icoords + u_vram_offset) % VRAM_SIZE)).rgb, 1.0); #endif } )"; diff --git a/src/core/gpu_hw_shadergen.h b/src/core/gpu_hw_shadergen.h index 7aea8a908..998d53d96 100644 --- a/src/core/gpu_hw_shadergen.h +++ b/src/core/gpu_hw_shadergen.h @@ -14,7 +14,8 @@ public: std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency, GPU::TextureMode texture_mode, bool dithering, bool interlacing); std::string GenerateInterlacedFillFragmentShader(); - std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode); + std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode, + bool smooth_chroma); std::string GenerateVRAMReadFragmentShader(); std::string GenerateVRAMWriteFragmentShader(bool use_ssbo); std::string GenerateVRAMCopyFragmentShader(); diff --git a/src/core/gpu_hw_vulkan.cpp b/src/core/gpu_hw_vulkan.cpp index bdda18983..96025083a 100644 --- a/src/core/gpu_hw_vulkan.cpp +++ b/src/core/gpu_hw_vulkan.cpp @@ -888,7 +888,7 @@ bool GPU_HW_Vulkan::CompilePipelines() for (u8 interlace_mode = 0; interlace_mode < 3; interlace_mode++) { VkShaderModule fs = g_vulkan_shader_cache->GetFragmentShader(shadergen.GenerateDisplayFragmentShader( - ConvertToBoolUnchecked(depth_24), static_cast(interlace_mode))); + ConvertToBoolUnchecked(depth_24), static_cast(interlace_mode), m_chroma_smoothing)); if (fs == VK_NULL_HANDLE) return false; @@ -991,14 +991,15 @@ void GPU_HW_Vulkan::UpdateDisplay() } else { + const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale; const u32 vram_offset_x = m_crtc_state.display_vram_left; const u32 vram_offset_y = m_crtc_state.display_vram_top; - const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; - const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; + const u32 scaled_vram_offset_x = vram_offset_x * resolution_scale; + const u32 scaled_vram_offset_y = vram_offset_y * resolution_scale; const u32 display_width = m_crtc_state.display_vram_width; const u32 display_height = m_crtc_state.display_vram_height; - const u32 scaled_display_width = display_width * m_resolution_scale; - const u32 scaled_display_height = display_height * m_resolution_scale; + const u32 scaled_display_width = display_width * resolution_scale; + const u32 scaled_display_height = display_height * resolution_scale; const InterlacedRenderMode interlaced = GetInterlacedRenderMode(); if (IsDisplayDisabled()) @@ -1020,8 +1021,8 @@ void GPU_HW_Vulkan::UpdateDisplay() EndRenderPass(); const u32 reinterpret_field_offset = (interlaced != InterlacedRenderMode::None) ? GetInterlacedDisplayField() : 0; - const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale; - const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * m_resolution_scale; + const u32 reinterpret_start_x = m_crtc_state.regs.X * resolution_scale; + const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * resolution_scale; const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y + reinterpret_field_offset, reinterpret_crop_left, reinterpret_field_offset}; diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index c08936ce5..7331b7445 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -438,6 +438,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si) si.SetBoolValue("GPU", "DisableInterlacing", false); si.SetBoolValue("GPU", "ForceNTSCTimings", false); si.SetBoolValue("GPU", "WidescreenHack", false); + si.SetBoolValue("GPU", "ChromaSmoothing24Bit", false); si.SetBoolValue("GPU", "PGXPEnable", false); si.SetBoolValue("GPU", "PGXPCulling", true); si.SetBoolValue("GPU", "PGXPTextureCorrection", true); @@ -634,6 +635,7 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings) g_settings.gpu_texture_filter != old_settings.gpu_texture_filter || g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing || g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings || + g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing || g_settings.display_crop_mode != old_settings.display_crop_mode || g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 626b2a078..7e02f7366 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -148,6 +148,7 @@ void Settings::Load(SettingsInterface& si) gpu_disable_interlacing = si.GetBoolValue("GPU", "DisableInterlacing", false); gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false); gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false); + gpu_24bit_chroma_smoothing = si.GetBoolValue("GPU", "ChromaSmoothing24Bit", false); gpu_pgxp_enable = si.GetBoolValue("GPU", "PGXPEnable", false); gpu_pgxp_culling = si.GetBoolValue("GPU", "PGXPCulling", true); gpu_pgxp_texture_correction = si.GetBoolValue("GPU", "PGXPTextureCorrection", true); @@ -277,6 +278,7 @@ void Settings::Save(SettingsInterface& si) const si.SetBoolValue("GPU", "DisableInterlacing", gpu_disable_interlacing); si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings); si.SetBoolValue("GPU", "WidescreenHack", gpu_widescreen_hack); + si.SetBoolValue("GPU", "ChromaSmoothing24Bit", gpu_24bit_chroma_smoothing); si.SetBoolValue("GPU", "PGXPEnable", gpu_pgxp_enable); si.SetBoolValue("GPU", "PGXPCulling", gpu_pgxp_culling); si.SetBoolValue("GPU", "PGXPTextureCorrection", gpu_pgxp_texture_correction); diff --git a/src/core/settings.h b/src/core/settings.h index 3ad76714e..f5fec8764 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -113,6 +113,7 @@ struct Settings s16 display_active_end_offset = 0; DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::R4_3; bool display_force_4_3_for_24bit = false; + bool gpu_24bit_chroma_smoothing = false; bool display_linear_filtering = true; bool display_integer_scaling = false; bool display_post_processing = false; diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index 18e0699ba..c3181a226 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -457,7 +457,7 @@ void LibretroHostInterface::OnSystemDestroyed() m_using_hardware_renderer = false; } -static std::array s_option_definitions = {{ +static std::array s_option_definitions = {{ {"duckstation_Console.Region", "Console Region", "Determines which region/hardware to emulate. Auto-Detect will use the region of the disc inserted.", @@ -604,6 +604,12 @@ static std::array s_option_definitions = {{ "Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs.", {{"true", "Enabled"}, {"false", "Disabled"}}, "false"}, + {"duckstation_GPU.ChromaSmoothing24Bit", + "Chroma Smoothing For 24-Bit Display", + "Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. Only applies to the hardware " + "renderers.", + {{"true", "Enabled"}, {"false", "Disabled"}}, + "false"}, {"duckstation_GPU.TextureFilter", "Texture Filtering", "Smooths out the blockyness of magnified textures on 3D object by using bilinear filtering. Will have a " diff --git a/src/duckstation-qt/enhancementsettingswidget.cpp b/src/duckstation-qt/enhancementsettingswidget.cpp index 444203f88..822817c3a 100644 --- a/src/duckstation-qt/enhancementsettingswidget.cpp +++ b/src/duckstation-qt/enhancementsettingswidget.cpp @@ -18,6 +18,8 @@ EnhancementSettingsWidget::EnhancementSettingsWidget(QtHostInterface* host_inter SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.disableInterlacing, "GPU", "DisableInterlacing"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceNTSCTimings, "GPU", "ForceNTSCTimings"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.force43For24Bit, "Display", "Force4_3For24Bit"); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.chromaSmoothingFor24Bit, "GPU", + "ChromaSmoothing24Bit"); SettingWidgetBinder::BindWidgetToEnumSetting( m_host_interface, m_ui.textureFiltering, "GPU", "TextureFilter", &Settings::ParseTextureFilterName, &Settings::GetTextureFilterDisplayName, Settings::DEFAULT_GPU_TEXTURE_FILTER); @@ -75,6 +77,9 @@ EnhancementSettingsWidget::EnhancementSettingsWidget(QtHostInterface* host_inter dialog->registerWidgetHelp( m_ui.force43For24Bit, tr("Force 4:3 For 24-bit Display"), tr("Unchecked"), tr("Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs.")); + dialog->registerWidgetHelp(m_ui.chromaSmoothingFor24Bit, tr("Chroma Smoothing For 24-Bit Display"), tr("Unchecked"), + tr("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. " + "Only applies to the hardware renderers.")); dialog->registerWidgetHelp( m_ui.textureFiltering, tr("Texture Filtering"), qApp->translate("GPUTextureFilter", Settings::GetTextureFilterDisplayName(GPUTextureFilter::Nearest)), diff --git a/src/duckstation-qt/enhancementsettingswidget.ui b/src/duckstation-qt/enhancementsettingswidget.ui index 51d3b7132..5e3be01ed 100644 --- a/src/duckstation-qt/enhancementsettingswidget.ui +++ b/src/duckstation-qt/enhancementsettingswidget.ui @@ -113,6 +113,13 @@ + + + + Chroma Smoothing For 24-Bit Display (reduce FMV color blockyness) + + + diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index cea74d012..35e5da966 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -1017,6 +1017,7 @@ void SDLHostInterface::DrawQuickSettingsMenu() settings_changed |= ImGui::MenuItem("Disable Interlacing", nullptr, &m_settings_copy.gpu_disable_interlacing); settings_changed |= ImGui::MenuItem("Widescreen Hack", nullptr, &m_settings_copy.gpu_widescreen_hack); settings_changed |= ImGui::MenuItem("Force NTSC Timings", nullptr, &m_settings_copy.gpu_force_ntsc_timings); + settings_changed |= ImGui::MenuItem("24-Bit Chroma Smoothing", nullptr, &m_settings_copy.gpu_24bit_chroma_smoothing); ImGui::Separator(); @@ -1545,6 +1546,7 @@ void SDLHostInterface::DrawSettingsWindow() settings_changed |= ImGui::Checkbox("Widescreen Hack", &m_settings_copy.gpu_widescreen_hack); settings_changed |= ImGui::Checkbox("Force 4:3 For 24-Bit Display", &m_settings_copy.display_force_4_3_for_24bit); + settings_changed |= ImGui::Checkbox("24-Bit Chroma Smoothing", &m_settings_copy.gpu_24bit_chroma_smoothing); settings_changed |= ImGui::Checkbox("PGXP Enabled", &m_settings_copy.gpu_pgxp_enable); settings_changed |= ImGui::Checkbox("PGXP Culling", &m_settings_copy.gpu_pgxp_culling);