mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
Add debanding option to improve gradient smoothness with truecolor
This option only has an effect if true color is also enabled.
This commit is contained in:
parent
572ea80e50
commit
cc94b76037
|
@ -3997,6 +3997,10 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
|||
FSUI_CSTR("Disables dithering and uses the full 8 bits per channel of color information."), "GPU",
|
||||
"TrueColor", true, is_hardware);
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_CSTR("True Color Debanding"),
|
||||
FSUI_CSTR("Applies modern dithering techniques to further smooth out gradients when true color is enabled."), "GPU",
|
||||
"Debanding", false, is_hardware);
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_CSTR("Widescreen Hack"),
|
||||
FSUI_CSTR("Increases the field of view from 4:3 to the chosen display aspect ratio in 3D games."),
|
||||
"GPU", "WidescreenHack", false, is_hardware);
|
||||
|
|
|
@ -212,6 +212,7 @@ bool GPU_HW::Initialize()
|
|||
m_supports_framebuffer_fetch = features.framebuffer_fetch;
|
||||
m_per_sample_shading = g_settings.gpu_per_sample_shading && features.per_sample_shading;
|
||||
m_true_color = g_settings.gpu_true_color;
|
||||
m_debanding = g_settings.gpu_debanding;
|
||||
m_scaled_dithering = g_settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_clamp_uvs = ShouldClampUVs();
|
||||
|
@ -345,7 +346,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
|
|||
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale));
|
||||
const bool 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_true_color != g_settings.gpu_true_color || m_debanding != g_settings.gpu_debanding || m_per_sample_shading != per_sample_shading ||
|
||||
m_scaled_dithering != g_settings.gpu_scaled_dithering || m_texture_filtering != g_settings.gpu_texture_filter ||
|
||||
m_clamp_uvs != clamp_uvs || m_chroma_smoothing != g_settings.gpu_24bit_chroma_smoothing ||
|
||||
m_downsample_mode != downsample_mode ||
|
||||
|
@ -395,6 +396,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
|
|||
m_multisamples = multisamples;
|
||||
m_per_sample_shading = per_sample_shading;
|
||||
m_true_color = g_settings.gpu_true_color;
|
||||
m_debanding = g_settings.gpu_debanding;
|
||||
m_scaled_dithering = g_settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_clamp_uvs = clamp_uvs;
|
||||
|
@ -604,7 +606,7 @@ void GPU_HW::PrintSettingsToLog()
|
|||
VRAM_HEIGHT * m_resolution_scale, GetMaxResolutionScale());
|
||||
Log_InfoFmt("Multisampling: {}x{}", m_multisamples, m_per_sample_shading ? " (per sample shading)" : "");
|
||||
Log_InfoFmt("Dithering: {}{}", m_true_color ? "Disabled" : "Enabled",
|
||||
(!m_true_color && m_scaled_dithering) ? " (Scaled)" : "");
|
||||
(!m_true_color && m_scaled_dithering) ? " (Scaled)" : ((m_true_color && m_debanding) ? " (Debanding)" : ""));
|
||||
Log_InfoFmt("Texture Filtering: {}", Settings::GetTextureFilterDisplayName(m_texture_filtering));
|
||||
Log_InfoFmt("Dual-source blending: {}", m_supports_dual_source_blend ? "Supported" : "Not supported");
|
||||
Log_InfoFmt("Clamping UVs: {}", m_clamp_uvs ? "YES" : "NO");
|
||||
|
@ -698,7 +700,7 @@ bool GPU_HW::CompilePipelines()
|
|||
const GPUDevice::Features features = g_gpu_device->GetFeatures();
|
||||
GPU_HW_ShaderGen shadergen(g_gpu_device->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
|
||||
m_true_color, m_scaled_dithering, m_texture_filtering, m_clamp_uvs, m_pgxp_depth_buffer,
|
||||
m_disable_color_perspective, m_supports_dual_source_blend, m_supports_framebuffer_fetch);
|
||||
m_disable_color_perspective, m_supports_dual_source_blend, m_supports_framebuffer_fetch, m_debanding);
|
||||
|
||||
ShaderCompileProgressTracker progress("Compiling Pipelines", 2 + (4 * 5 * 9 * 2 * 2) + (3 * 4 * 5 * 9 * 2 * 2) + 1 +
|
||||
2 + (2 * 2) + 2 + 1 + 1 + (2 * 3) + 1);
|
||||
|
@ -3126,6 +3128,11 @@ void GPU_HW::DrawRendererStats(bool is_idle_frame)
|
|||
ImGui::TextColored(m_true_color ? active_color : inactive_color, m_true_color ? "Enabled" : "Disabled");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("Debanding:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(m_debanding ? active_color : inactive_color, m_debanding ? "Enabled" : "Disabled");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("Scaled Dithering:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(m_scaled_dithering ? active_color : inactive_color, m_scaled_dithering ? "Enabled" : "Disabled");
|
||||
|
|
|
@ -252,6 +252,7 @@ private:
|
|||
GPUDownsampleMode m_downsample_mode = GPUDownsampleMode::Disabled;
|
||||
GPUWireframeMode m_wireframe_mode = GPUWireframeMode::Disabled;
|
||||
bool m_true_color = true;
|
||||
bool m_debanding = false;
|
||||
bool m_clamp_uvs = false;
|
||||
bool m_compute_uv_range = false;
|
||||
bool m_pgxp_depth_buffer = false;
|
||||
|
|
|
@ -9,11 +9,11 @@ GPU_HW_ShaderGen::GPU_HW_ShaderGen(RenderAPI render_api, u32 resolution_scale, u
|
|||
bool per_sample_shading, bool true_color, bool scaled_dithering,
|
||||
GPUTextureFilter texture_filtering, bool uv_limits, bool pgxp_depth,
|
||||
bool disable_color_perspective, bool supports_dual_source_blend,
|
||||
bool supports_framebuffer_fetch)
|
||||
bool supports_framebuffer_fetch, bool debanding)
|
||||
: ShaderGen(render_api, supports_dual_source_blend, supports_framebuffer_fetch), m_resolution_scale(resolution_scale),
|
||||
m_multisamples(multisamples), m_per_sample_shading(per_sample_shading), m_true_color(true_color),
|
||||
m_scaled_dithering(scaled_dithering), m_texture_filter(texture_filtering), m_uv_limits(uv_limits),
|
||||
m_pgxp_depth(pgxp_depth), m_disable_color_perspective(disable_color_perspective)
|
||||
m_pgxp_depth(pgxp_depth), m_disable_color_perspective(disable_color_perspective), m_debanding(debanding)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -615,7 +615,7 @@ void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
|||
|
||||
ialpha = res.w;
|
||||
texcol = float4(res.xyz, resW);
|
||||
|
||||
|
||||
// Compensate for partially transparent sampling.
|
||||
if (ialpha > 0.0)
|
||||
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
||||
|
@ -662,6 +662,8 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod
|
|||
DefineMacro(ss, "RAW_TEXTURE", raw_texture);
|
||||
DefineMacro(ss, "DITHERING", dithering);
|
||||
DefineMacro(ss, "DITHERING_SCALED", m_scaled_dithering);
|
||||
// Debanding requires true color to work correctly.
|
||||
DefineMacro(ss, "DEBANDING", m_true_color && m_debanding);
|
||||
DefineMacro(ss, "INTERLACING", interlacing);
|
||||
DefineMacro(ss, "TRUE_COLOR", m_true_color);
|
||||
DefineMacro(ss, "TEXTURE_FILTERING", m_texture_filter != GPUTextureFilter::Nearest);
|
||||
|
@ -761,13 +763,29 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
return SAMPLE_TEXTURE(samp0, float2(palette_icoord) * RCP_VRAM_SIZE);
|
||||
#else
|
||||
// Direct texturing. Render-to-texture effects. Use upscaled coordinates.
|
||||
uint2 icoord = ApplyUpscaledTextureWindow(FloatToIntegerCoords(coords));
|
||||
uint2 icoord = ApplyUpscaledTextureWindow(FloatToIntegerCoords(coords));
|
||||
uint2 direct_icoord = texpage.xy + icoord;
|
||||
return SAMPLE_TEXTURE(samp0, float2(direct_icoord) * RCP_VRAM_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
|
||||
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
|
||||
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
|
||||
float3 ApplyDebanding(float2 frag_coord) {
|
||||
#if DEBANDING
|
||||
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
|
||||
float3 dither = float3(dot(vec2(171.0, 231.0), frag_coord));
|
||||
dither.rgb = fract(dither.rgb / float3(103.0, 71.0, 97.0));
|
||||
|
||||
// Subtract 0.5 to avoid slightly brightening the whole viewport.
|
||||
return (dither.rgb - 0.5) / 255.0;
|
||||
#else
|
||||
return float3(0.0);
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
if (textured)
|
||||
|
@ -797,7 +815,7 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
|
||||
ss << R"(
|
||||
{
|
||||
uint3 vertcol = uint3(v_col0.rgb * float3(255.0, 255.0, 255.0));
|
||||
uint3 vertcol = uint3(v_col0.rgb * float3(255.0, 255.0, 255.0) + ApplyDebanding(v_pos.xy));
|
||||
|
||||
bool semitransparent;
|
||||
uint3 icolor;
|
||||
|
@ -821,7 +839,7 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
#if UV_LIMITS
|
||||
float4 uv_limits = v_uv_limits;
|
||||
#if !PALETTE
|
||||
// Extend the UV range to all "upscaled" pixels. This means 1-pixel-high polygon-based
|
||||
// Extend the UV range to all "upscaled" pixels. This means 1-pixel-high polygon-based
|
||||
// framebuffer effects won't be downsampled. (e.g. Mega Man Legends 2 haze effect)
|
||||
uv_limits *= float(RESOLUTION_SCALE);
|
||||
uv_limits.zw += float(RESOLUTION_SCALE - 1u);
|
||||
|
@ -859,7 +877,7 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
#endif
|
||||
#endif
|
||||
#else
|
||||
icolor = uint3(texcol.rgb * float3(255.0, 255.0, 255.0));
|
||||
icolor = uint3(texcol.rgb * float3(255.0, 255.0, 255.0) + ApplyDebanding(v_pos.xy));
|
||||
#if !RAW_TEXTURE
|
||||
icolor = (icolor * vertcol) >> 7;
|
||||
#if DITHERING
|
||||
|
@ -1051,10 +1069,10 @@ float3 SampleVRAM24(uint2 icoords)
|
|||
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);
|
||||
|
@ -1113,7 +1131,7 @@ float3 SampleVRAM24Smoothed(uint2 icoords)
|
|||
o_col0 = float4(SampleVRAM24Smoothed(icoords), 1.0);
|
||||
#else
|
||||
o_col0 = float4(SampleVRAM24(icoords), 1.0);
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
o_col0 = float4(LoadVRAM(int2((icoords + u_vram_offset) % VRAM_SIZE)).rgb, 1.0);
|
||||
#endif
|
||||
|
@ -1333,7 +1351,7 @@ std::string GPU_HW_ShaderGen::GenerateVRAMWriteFragmentShader(bool use_buffer, b
|
|||
uint buffer_offset = u_buffer_base_offset + (offset.y * u_size.x) + offset.x;
|
||||
uint value = GET_VALUE(buffer_offset) | u_mask_or_bits;
|
||||
#endif
|
||||
|
||||
|
||||
o_col0 = RGBA5551ToRGBA8(value);
|
||||
#if !PGXP_DEPTH
|
||||
o_depth = (o_col0.a == 1.0) ? u_depth_value : 0.0;
|
||||
|
|
|
@ -11,7 +11,7 @@ public:
|
|||
GPU_HW_ShaderGen(RenderAPI render_api, u32 resolution_scale, u32 multisamples, bool per_sample_shading,
|
||||
bool true_color, bool scaled_dithering, GPUTextureFilter texture_filtering, bool uv_limits,
|
||||
bool pgxp_depth, bool disable_color_perspective, bool supports_dual_source_blend,
|
||||
bool supports_framebuffer_fetch);
|
||||
bool supports_framebuffer_fetch, bool debanding);
|
||||
~GPU_HW_ShaderGen();
|
||||
|
||||
std::string GenerateBatchVertexShader(bool textured);
|
||||
|
@ -51,4 +51,5 @@ private:
|
|||
bool m_uv_limits;
|
||||
bool m_pgxp_depth;
|
||||
bool m_disable_color_perspective;
|
||||
bool m_debanding;
|
||||
};
|
||||
|
|
|
@ -525,8 +525,13 @@ void ImGuiManager::DrawEnhancementsOverlay()
|
|||
{
|
||||
text.append_format(" {}x{}", g_settings.gpu_multisamples, g_settings.gpu_per_sample_shading ? "SSAA" : "MSAA");
|
||||
}
|
||||
if (g_settings.gpu_true_color)
|
||||
text.append(" TrueCol");
|
||||
if (g_settings.gpu_true_color) {
|
||||
if (g_settings.gpu_debanding) {
|
||||
text.append(" TrueColDeband");
|
||||
} else {
|
||||
text.append(" TrueCol");
|
||||
}
|
||||
}
|
||||
if (g_settings.gpu_disable_interlacing)
|
||||
text.append(" ForceProg");
|
||||
if (g_settings.gpu_force_ntsc_timings && System::GetRegion() == ConsoleRegion::PAL)
|
||||
|
|
|
@ -190,6 +190,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
gpu_use_software_renderer_for_readbacks = si.GetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false);
|
||||
gpu_threaded_presentation = si.GetBoolValue("GPU", "ThreadedPresentation", true);
|
||||
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true);
|
||||
gpu_debanding = si.GetBoolValue("GPU", "Debanding", false);
|
||||
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", true);
|
||||
gpu_texture_filter =
|
||||
ParseTextureFilterName(
|
||||
|
@ -456,6 +457,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("GPU", "ThreadedPresentation", gpu_threaded_presentation);
|
||||
si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", gpu_use_software_renderer_for_readbacks);
|
||||
si.SetBoolValue("GPU", "TrueColor", gpu_true_color);
|
||||
si.SetBoolValue("GPU", "Debanding", gpu_debanding);
|
||||
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
|
||||
si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
|
||||
si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode));
|
||||
|
@ -614,6 +616,7 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
|
|||
g_settings.gpu_multisamples = 1;
|
||||
g_settings.gpu_per_sample_shading = false;
|
||||
g_settings.gpu_true_color = false;
|
||||
g_settings.gpu_debanding = false;
|
||||
g_settings.gpu_scaled_dithering = false;
|
||||
g_settings.gpu_texture_filter = GPUTextureFilter::Nearest;
|
||||
g_settings.gpu_disable_interlacing = false;
|
||||
|
|
|
@ -111,6 +111,7 @@ struct Settings
|
|||
bool gpu_disable_texture_copy_to_self = false;
|
||||
bool gpu_per_sample_shading = false;
|
||||
bool gpu_true_color = true;
|
||||
bool gpu_debanding = false;
|
||||
bool gpu_scaled_dithering = true;
|
||||
GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
|
||||
GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE;
|
||||
|
|
|
@ -3626,6 +3626,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.gpu_fifo_size != old_settings.gpu_fifo_size ||
|
||||
g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
|
||||
g_settings.gpu_true_color != old_settings.gpu_true_color ||
|
||||
g_settings.gpu_debanding != old_settings.gpu_debanding ||
|
||||
g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
|
||||
g_settings.gpu_texture_filter != old_settings.gpu_texture_filter ||
|
||||
g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
|
||||
|
|
|
@ -80,6 +80,11 @@ EnhancementSettingsWidget::EnhancementSettingsWidget(SettingsWindow* dialog, QWi
|
|||
"Disabling the option also enables dithering, which makes the transition between colours less sharp by applying "
|
||||
"a pattern around those pixels. Most games are compatible with this option, but there is a number which aren't "
|
||||
"and will have broken effects with it enabled. Only applies to the hardware renderers."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.debanding, tr("True Color Debanding"), tr("Unchecked"),
|
||||
tr("Applies modern dithering techniques to further smooth out gradients when true color is enabled. "
|
||||
"This debanding is performed during rendering (as opposed to a post-processing step), which allows it to be fast while preserving detail. "
|
||||
"Debanding increases the file size of screenshots due to the subtle dithering pattern present in screenshots."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.scaledDithering, tr("Scaled Dithering (scale dither pattern to resolution)"), tr("Checked"),
|
||||
tr("Scales the dither pattern to the resolution scale of the emulated GPU. This makes the dither pattern much less "
|
||||
|
|
|
@ -49,13 +49,20 @@
|
|||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="textureFiltering"/>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="trueColor">
|
||||
<property name="text">
|
||||
<string>True Color Rendering (24-bit, disables dithering)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="debanding">
|
||||
<property name="text">
|
||||
<string>True Color Debanding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="scaledDithering">
|
||||
<property name="text">
|
||||
|
|
Loading…
Reference in a new issue