From 741e97168130dc06a2de3bfcd6052fad2c9f6b2b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 22 Mar 2025 18:26:14 +1000 Subject: [PATCH] GPU/HW: Add 'True Color (Full)' dithering mode This is equivalent to the old 'True Color' mode. The new 'True Color' mode truncates flat-shaded sprites/polygons to 16-bit color before drawing. Doing so fixes: - Menu background in Breath of Fire IV. - Loading background in JumpStart Wildlife Safari - Field Trip. - and other similar games. --- src/core/game_database.cpp | 8 +++++++- src/core/game_database.h | 1 + src/core/gpu_hw.cpp | 37 +++++++++++++++++++++++++++++++++---- src/core/gpu_hw.h | 2 +- src/core/settings.cpp | 3 ++- src/core/settings.h | 2 +- src/core/types.h | 1 + 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index f3e056234..7838ea8f6 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -80,6 +80,7 @@ static constexpr const std::array(Trait::MaxCou "ForceSoftwareRendererForReadbacks", "ForceRoundTextureCoordinates", "ForceShaderBlending", + "ForceFullTrueColor", "ForceDeinterlacing", "ForceFullBoot", "DisableAutoAnalogMode", @@ -110,6 +111,7 @@ static constexpr const std::array(Trait::MaxCou TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Software Renderer For Readbacks", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Round Texture Coordinates", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Shader Blending", "GameDatabase::Trait"), + TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Full True Color", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Deinterlacing", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Full Boot", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Automatic Analog Mode", "GameDatabase::Trait"), @@ -520,7 +522,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes } if (HasTrait(Trait::DisableTrueColor) || HasTrait(Trait::DisableScaledDithering) || - HasTrait(Trait::ForceShaderBlending)) + HasTrait(Trait::ForceShaderBlending) || HasTrait(Trait::ForceFullTrueColor)) { // Note: The order these are applied matters. const GPUDitheringMode old_mode = settings.gpu_dithering_mode; @@ -539,6 +541,10 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes GPUDitheringMode::ScaledShaderBlend : GPUDitheringMode::UnscaledShaderBlend; } + if (HasTrait(Trait::ForceFullTrueColor) && settings.gpu_dithering_mode == GPUDitheringMode::TrueColor) + { + settings.gpu_dithering_mode = GPUDitheringMode::TrueColorFull; + } if (display_osd_messages && settings.gpu_dithering_mode != old_mode) { diff --git a/src/core/game_database.h b/src/core/game_database.h index 95b4a702f..c4133ca4e 100644 --- a/src/core/game_database.h +++ b/src/core/game_database.h @@ -38,6 +38,7 @@ enum class Trait : u32 ForceSoftwareRendererForReadbacks, ForceRoundUpscaledTextureCoordinates, ForceShaderBlending, + ForceFullTrueColor, ForceDeinterlacing, ForceFullBoot, DisableAutoAnalogMode, diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 6a2e1d105..726382006 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -154,6 +154,21 @@ ALWAYS_INLINE_RELEASE static GSVector4i GetVRAMTransferBounds(u32 x, u32 y, u32 return ret; } +/// Returns true if the below function should be applied. +ALWAYS_INLINE static bool ShouldTruncate32To16(const GPUBackendDrawCommand* cmd) +{ + return (!cmd->texture_enable && !cmd->shading_enable && !cmd->dither_enable && + g_gpu_settings.gpu_dithering_mode == GPUDitheringMode::TrueColor); +} + +/// Truncates a 32-bit colour to 16-bit. +ALWAYS_INLINE static u32 Truncate32To16(u32 color) +{ + return GSVector4i((GSVector4(GSVector4i::zext32(color).u8to32().srl32<3>()) / GSVector4::cxpr(31.0f)) * + GSVector4::cxpr(255.0f)) + .rgba32(); +} + namespace { class ShaderCompileProgressTracker { @@ -2525,7 +2540,7 @@ void GPU_HW::DrawLine(const GPUBackendDrawLineCommand* cmd) } AddDrawnRectangle(clamped_rect); - DrawLine(GSVector4(bounds), start_color, end_color, depth); + DrawLine(cmd, GSVector4(bounds), start_color, end_color, depth); } if (ShouldDrawWithSoftwareRenderer()) @@ -2568,7 +2583,7 @@ void GPU_HW::DrawPreciseLine(const GPUBackendDrawPreciseLineCommand* cmd) } AddDrawnRectangle(clamped_rect); - DrawLine(bounds, start_color, end_color, depth); + DrawLine(cmd, bounds, start_color, end_color, depth); } if (ShouldDrawWithSoftwareRenderer()) @@ -2590,10 +2605,16 @@ void GPU_HW::DrawPreciseLine(const GPUBackendDrawPreciseLineCommand* cmd) } } -void GPU_HW::DrawLine(const GSVector4 bounds, u32 col0, u32 col1, float depth) +void GPU_HW::DrawLine(const GPUBackendDrawCommand* cmd, const GSVector4 bounds, u32 col0, u32 col1, float depth) { DebugAssert(m_batch_vertex_space >= 4 && m_batch_index_space >= 6); + if (ShouldTruncate32To16(cmd)) + { + col0 = Truncate32To16(col0); + col1 = Truncate32To16(col1); + } + const float x0 = bounds.x; const float y0 = bounds.y; const float x1 = bounds.z; @@ -2717,7 +2738,9 @@ void GPU_HW::DrawSprite(const GPUBackendDrawRectangleCommand* cmd) const s32 pos_x = cmd->x; const s32 pos_y = cmd->y; const u32 texpage = m_draw_mode.bits; - const u32 color = (cmd->texture_enable && cmd->raw_texture_enable) ? UINT32_C(0x00808080) : cmd->color; + const u32 color = (cmd->texture_enable && cmd->raw_texture_enable) ? + UINT32_C(0x00808080) : + (ShouldTruncate32To16(cmd) ? Truncate32To16(cmd->color) : cmd->color); const float depth = GetCurrentNormalizedVertexDepth(); const u32 orig_tex_left = ZeroExtend32(Truncate8(cmd->texcoord)); const u32 orig_tex_top = ZeroExtend32(cmd->texcoord) >> 8; @@ -2980,6 +3003,12 @@ ALWAYS_INLINE_RELEASE bool GPU_HW::BeginPolygonDraw(const GPUBackendDrawCommand* } } + if (ShouldTruncate32To16(cmd)) + { + for (u32 i = 0; i < 4; i++) + vertices[i].color = Truncate32To16(vertices[i].color); + } + PrepareDraw(cmd); return true; } diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 5f37478ff..cdb7d8a1c 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -242,7 +242,7 @@ private: bool BlitVRAMReplacementTexture(GPUTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height); /// Expands a line into two triangles. - void DrawLine(const GSVector4 bounds, u32 col0, u32 col1, float depth); + void DrawLine(const GPUBackendDrawCommand* cmd, const GSVector4 bounds, u32 col0, u32 col1, float depth); /// Computes partial derivatives and area for the given triangle. Needed for sprite/line detection. static void ComputeUVPartialDerivatives(const BatchVertex* vertices, float* dudx, float* dudy, float* dvdx, diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6e889af5d..279fbf0a1 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -1557,7 +1557,7 @@ const char* Settings::GetTextureFilterDisplayName(GPUTextureFilter filter) } static constexpr const std::array s_gpu_dithering_mode_names = { - "Unscaled", "UnscaledShaderBlend", "Scaled", "ScaledShaderBlend", "TrueColor", + "Unscaled", "UnscaledShaderBlend", "Scaled", "ScaledShaderBlend", "TrueColor", "TrueColorFull", }; static constexpr const std::array s_gpu_dithering_mode_display_names = { TRANSLATE_DISAMBIG_NOOP("Settings", "Unscaled", "GPUDitheringMode"), @@ -1565,6 +1565,7 @@ static constexpr const std::array s_gpu_dithering_mode_display_names = { TRANSLATE_DISAMBIG_NOOP("Settings", "Scaled", "GPUDitheringMode"), TRANSLATE_DISAMBIG_NOOP("Settings", "Scaled (Shader Blending)", "GPUDitheringMode"), TRANSLATE_DISAMBIG_NOOP("Settings", "True Color", "GPUDitheringMode"), + TRANSLATE_DISAMBIG_NOOP("Settings", "True Color (Full)", "GPUDitheringMode"), }; static_assert(s_gpu_dithering_mode_names.size() == static_cast(GPUDitheringMode::MaxCount)); static_assert(s_gpu_dithering_mode_display_names.size() == static_cast(GPUDitheringMode::MaxCount)); diff --git a/src/core/settings.h b/src/core/settings.h index 71f289fef..b68df7ebb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -212,7 +212,7 @@ struct GPUSettings void SetPGXPDepthClearThreshold(float value); ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); } - ALWAYS_INLINE bool IsUsingTrueColor() const { return (gpu_dithering_mode == GPUDitheringMode::TrueColor); } + ALWAYS_INLINE bool IsUsingTrueColor() const { return (gpu_dithering_mode >= GPUDitheringMode::TrueColor); } ALWAYS_INLINE bool IsUsingDithering() const { return (gpu_dithering_mode < GPUDitheringMode::TrueColor); } ALWAYS_INLINE bool IsUsingShaderBlending() const { diff --git a/src/core/types.h b/src/core/types.h index c20769e76..9a55e0a83 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -107,6 +107,7 @@ enum class GPUDitheringMode : u8 Scaled, ScaledShaderBlend, TrueColor, + TrueColorFull, MaxCount, };