diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 471a2d4eb..2fb93b41f 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -4142,6 +4142,7 @@ void FullscreenUI::DrawDisplaySettingsPage() SettingsInterface* bsi = GetEditingSettingsInterface(); const bool game_settings = IsEditingGameSettings(bsi); + const u32 resolution_scale = GetEffectiveUIntSetting(bsi, "GPU", "ResolutionScale", 1); BeginMenuButtons(); @@ -4298,43 +4299,46 @@ void FullscreenUI::DrawDisplaySettingsPage() "GPU", "UseSoftwareRendererForReadbacks", false); } - MenuHeading(FSUI_CSTR("Rendering")); + if (is_hardware) + { + MenuHeading(FSUI_CSTR("Rendering")); - DrawIntListSetting( - bsi, FSUI_CSTR("Internal Resolution"), - FSUI_CSTR("Scales internal VRAM resolution by the specified multiplier. Some games require 1x VRAM resolution."), - "GPU", "ResolutionScale", 1, resolution_scales.data(), resolution_scales.size(), true, 0, is_hardware); + DrawIntListSetting( + bsi, FSUI_CSTR("Internal Resolution"), + FSUI_CSTR("Scales internal VRAM resolution by the specified multiplier. Some games require 1x VRAM resolution."), + "GPU", "ResolutionScale", 1, resolution_scales.data(), resolution_scales.size(), true, 0); - DrawEnumSetting( - bsi, FSUI_CSTR("Texture Filtering"), FSUI_CSTR("Smooths out the blockiness of magnified textures on 3D objects."), - "GPU", "TextureFilter", Settings::DEFAULT_GPU_TEXTURE_FILTER, &Settings::ParseTextureFilterName, - &Settings::GetTextureFilterName, &Settings::GetTextureFilterDisplayName, GPUTextureFilter::Count, is_hardware); + DrawEnumSetting(bsi, FSUI_CSTR("Texture Filtering"), + FSUI_CSTR("Smooths out the blockiness of magnified textures on 3D objects."), "GPU", + "TextureFilter", Settings::DEFAULT_GPU_TEXTURE_FILTER, &Settings::ParseTextureFilterName, + &Settings::GetTextureFilterName, &Settings::GetTextureFilterDisplayName, GPUTextureFilter::Count); - DrawEnumSetting(bsi, FSUI_CSTR("Line Detection"), - FSUI_CSTR("Attempts to detect one pixel high/wide lines that rely on non-upscaled rasterization " - "behavior, filling in gaps introduced by upscaling."), - "GPU", "LineDetectMode", Settings::DEFAULT_GPU_LINE_DETECT_MODE, &Settings::ParseLineDetectModeName, - &Settings::GetLineDetectModeName, &Settings::GetLineDetectModeDisplayName, GPULineDetectMode::Count, - is_hardware); + DrawEnumSetting(bsi, FSUI_CSTR("Line Detection"), + FSUI_CSTR("Attempts to detect one pixel high/wide lines that rely on non-upscaled rasterization " + "behavior, filling in gaps introduced by upscaling."), + "GPU", "LineDetectMode", Settings::DEFAULT_GPU_LINE_DETECT_MODE, &Settings::ParseLineDetectModeName, + &Settings::GetLineDetectModeName, &Settings::GetLineDetectModeDisplayName, GPULineDetectMode::Count, + resolution_scale > 1); - DrawToggleSetting(bsi, FSUI_CSTR("True Color Rendering"), - 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 Rendering"), + FSUI_CSTR("Disables dithering and uses the full 8 bits per channel of color information."), "GPU", + "TrueColor", true); - 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 && bsi->GetBoolValue("GPU", "TrueColor", false)); + 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, bsi->GetBoolValue("GPU", "TrueColor", false)); - 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); + 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); - DrawToggleSetting( - bsi, FSUI_CSTR("PGXP Geometry Correction"), - FSUI_CSTR("Reduces \"wobbly\" polygons by attempting to preserve the fractional component through memory " - "transfers."), - "GPU", "PGXPEnable", false); + DrawToggleSetting( + bsi, FSUI_CSTR("PGXP Geometry Correction"), + FSUI_CSTR("Reduces \"wobbly\" polygons by attempting to preserve the fractional component through memory " + "transfers."), + "GPU", "PGXPEnable", false); + } MenuHeading(FSUI_CSTR("Screen Display")); @@ -4401,12 +4405,6 @@ void FullscreenUI::DrawDisplaySettingsPage() "ScreenshotQuality", Settings::DEFAULT_DISPLAY_SCREENSHOT_QUALITY, 1, 100, "%d%%"); MenuHeading(FSUI_CSTR("Enhancements")); - DrawToggleSetting( - bsi, FSUI_CSTR("Scaled Dithering"), - FSUI_CSTR("Scales the dithering pattern with the internal rendering resolution, making it less noticeable. " - "Usually safe to enable."), - "GPU", "ScaledDithering", true, is_hardware); - DrawToggleSetting( bsi, FSUI_CSTR("Disable Interlacing"), FSUI_CSTR("Disables interlaced rendering and display in the GPU. Some games can render in 480p this way, " @@ -4421,46 +4419,67 @@ void FullscreenUI::DrawDisplaySettingsPage() bsi, FSUI_CSTR("Force 4:3 For 24-Bit Display"), FSUI_CSTR("Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs."), "Display", "Force4_3For24Bit", false); - DrawToggleSetting(bsi, FSUI_CSTR("Chroma Smoothing For 24-Bit Display"), - FSUI_CSTR("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. Only " - "applies to the hardware renderers."), - "GPU", "ChromaSmoothing24Bit", false); - MenuHeading(FSUI_CSTR("PGXP (Precision Geometry Transform Pipeline)")); + if (is_hardware) + { + const GPUTextureFilter texture_filtering = + Settings::ParseTextureFilterName( + GetEffectiveTinyStringSetting(bsi, "GPU", "TextureFilter", + Settings::GetTextureFilterName(Settings::DEFAULT_GPU_TEXTURE_FILTER))) + .value_or(Settings::DEFAULT_GPU_TEXTURE_FILTER); - const bool pgxp_enabled = GetEffectiveBoolSetting(bsi, "GPU", "PGXPEnable", false); - const bool texture_correction_enabled = GetEffectiveBoolSetting(bsi, "GPU", "PGXPTextureCorrection", true); + DrawToggleSetting( + bsi, FSUI_CSTR("Scaled Dithering"), + FSUI_CSTR("Scales the dithering pattern with the internal rendering resolution, making it less noticeable. " + "Usually safe to enable."), + "GPU", "ScaledDithering", true, resolution_scale > 1); + DrawToggleSetting(bsi, FSUI_CSTR("Chroma Smoothing For 24-Bit Display"), + FSUI_CSTR("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs."), + "GPU", "ChromaSmoothing24Bit", false); + DrawToggleSetting( + bsi, FSUI_CSTR("Round Upscaled Texture Coordinates"), + FSUI_CSTR("Rounds texture coordinates instead of flooring when upscaling. Can fix misaligned " + "textures in some games, but break others, and is incompatible with texture filtering."), + "GPU", "ForceRoundTextureCoordinates", false, + resolution_scale > 1 && texture_filtering == GPUTextureFilter::Nearest); - DrawToggleSetting( - bsi, FSUI_CSTR("Perspective Correct Textures"), - FSUI_CSTR("Uses perspective-correct interpolation for texture coordinates, straightening out warped textures."), - "GPU", "PGXPTextureCorrection", true, pgxp_enabled); - DrawToggleSetting( - bsi, FSUI_CSTR("Perspective Correct Colors"), - FSUI_CSTR("Uses perspective-correct interpolation for colors, which can improve visuals in some games."), "GPU", - "PGXPColorCorrection", false, pgxp_enabled); - DrawToggleSetting(bsi, FSUI_CSTR("Culling Correction"), - FSUI_CSTR("Increases the precision of polygon culling, reducing the number of holes in geometry."), - "GPU", "PGXPCulling", true, pgxp_enabled); - DrawToggleSetting( - bsi, FSUI_CSTR("Preserve Projection Precision"), - FSUI_CSTR("Adds additional precision to PGXP data post-projection. May improve visuals in some games."), "GPU", - "PGXPPreserveProjFP", false, pgxp_enabled); - DrawToggleSetting(bsi, FSUI_CSTR("Depth Buffer"), - FSUI_CSTR("Reduces polygon Z-fighting through depth testing. Low compatibility with games."), "GPU", - "PGXPDepthBuffer", false, pgxp_enabled && texture_correction_enabled); - DrawToggleSetting(bsi, FSUI_CSTR("CPU Mode"), - FSUI_CSTR("Uses PGXP for all instructions, not just memory operations."), "GPU", "PGXPCPU", false, - pgxp_enabled); + MenuHeading(FSUI_CSTR("PGXP (Precision Geometry Transform Pipeline)")); - MenuHeading(FSUI_CSTR("Texture Replacements")); + const bool pgxp_enabled = GetEffectiveBoolSetting(bsi, "GPU", "PGXPEnable", false); + const bool texture_correction_enabled = GetEffectiveBoolSetting(bsi, "GPU", "PGXPTextureCorrection", true); - DrawToggleSetting(bsi, FSUI_CSTR("Enable VRAM Write Texture Replacement"), - FSUI_CSTR("Enables the replacement of background textures in supported games."), - "TextureReplacements", "EnableVRAMWriteReplacements", false); - DrawToggleSetting(bsi, FSUI_CSTR("Preload Replacement Textures"), - FSUI_CSTR("Loads all replacement texture to RAM, reducing stuttering at runtime."), - "TextureReplacements", "PreloadTextures", false); + DrawToggleSetting( + bsi, FSUI_CSTR("Perspective Correct Textures"), + FSUI_CSTR("Uses perspective-correct interpolation for texture coordinates, straightening out warped textures."), + "GPU", "PGXPTextureCorrection", true, pgxp_enabled); + DrawToggleSetting( + bsi, FSUI_CSTR("Perspective Correct Colors"), + FSUI_CSTR("Uses perspective-correct interpolation for colors, which can improve visuals in some games."), "GPU", + "PGXPColorCorrection", false, pgxp_enabled); + DrawToggleSetting( + bsi, FSUI_CSTR("Culling Correction"), + FSUI_CSTR("Increases the precision of polygon culling, reducing the number of holes in geometry."), "GPU", + "PGXPCulling", true, pgxp_enabled); + DrawToggleSetting( + bsi, FSUI_CSTR("Preserve Projection Precision"), + FSUI_CSTR("Adds additional precision to PGXP data post-projection. May improve visuals in some games."), "GPU", + "PGXPPreserveProjFP", false, pgxp_enabled); + DrawToggleSetting(bsi, FSUI_CSTR("Depth Buffer"), + FSUI_CSTR("Reduces polygon Z-fighting through depth testing. Low compatibility with games."), + "GPU", "PGXPDepthBuffer", false, pgxp_enabled && texture_correction_enabled); + DrawToggleSetting(bsi, FSUI_CSTR("CPU Mode"), + FSUI_CSTR("Uses PGXP for all instructions, not just memory operations."), "GPU", "PGXPCPU", false, + pgxp_enabled); + + MenuHeading(FSUI_CSTR("Texture Replacements")); + + DrawToggleSetting(bsi, FSUI_CSTR("Enable VRAM Write Texture Replacement"), + FSUI_CSTR("Enables the replacement of background textures in supported games."), + "TextureReplacements", "EnableVRAMWriteReplacements", false); + DrawToggleSetting(bsi, FSUI_CSTR("Preload Replacement Textures"), + FSUI_CSTR("Loads all replacement texture to RAM, reducing stuttering at runtime."), + "TextureReplacements", "PreloadTextures", false); + } EndMenuButtons(); } @@ -7591,6 +7610,8 @@ TRANSLATE_NOOP("FullscreenUI", "Rewind for {0} frames, lasting {1:.2f} seconds w TRANSLATE_NOOP("FullscreenUI", "Rewind is disabled because runahead is enabled. Runahead will significantly increase system requirements."); TRANSLATE_NOOP("FullscreenUI", "Rewind is not enabled. Please note that enabling rewind may significantly increase system requirements."); TRANSLATE_NOOP("FullscreenUI", "Rich presence inactive or unsupported."); +TRANSLATE_NOOP("FullscreenUI", "Round Upscaled Texture Coordinates"); +TRANSLATE_NOOP("FullscreenUI", "Rounds texture coordinates instead of flooring when upscaling. Can fix misaligned textures in some games, but break others."); TRANSLATE_NOOP("FullscreenUI", "Runahead"); TRANSLATE_NOOP("FullscreenUI", "Runahead/Rewind"); TRANSLATE_NOOP("FullscreenUI", "Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater performance."); @@ -7675,7 +7696,7 @@ TRANSLATE_NOOP("FullscreenUI", "Simulates the system ahead of time and rolls bac TRANSLATE_NOOP("FullscreenUI", "Skip Duplicate Frame Display"); TRANSLATE_NOOP("FullscreenUI", "Skips the presentation/display of frames that are not unique. Can result in worse frame pacing."); TRANSLATE_NOOP("FullscreenUI", "Slow Boot"); -TRANSLATE_NOOP("FullscreenUI", "Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. Only applies to the hardware renderers."); +TRANSLATE_NOOP("FullscreenUI", "Smooths out blockyness between colour transitions in 24-bit content, usually FMVs."); TRANSLATE_NOOP("FullscreenUI", "Smooths out the blockiness of magnified textures on 3D objects."); TRANSLATE_NOOP("FullscreenUI", "Sort By"); TRANSLATE_NOOP("FullscreenUI", "Sort Reversed"); diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index 342c14d27..3c8de1291 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -34,7 +34,7 @@ namespace GameDatabase { enum : u32 { GAME_DATABASE_CACHE_SIGNATURE = 0x45434C48, - GAME_DATABASE_CACHE_VERSION = 8, + GAME_DATABASE_CACHE_VERSION = 9, }; static Entry* GetMutableEntry(std::string_view serial); @@ -63,6 +63,7 @@ static constexpr const std::array(GameDatabase::Tr "ForceInterpreter", "ForceSoftwareRenderer", "ForceSoftwareRendererForReadbacks", + "ForceRoundTextureCoordinates", "ForceInterlacing", "DisableTrueColor", "DisableUpscaling", @@ -88,6 +89,7 @@ static constexpr const std::array(GameDatabase::Tr TRANSLATE_NOOP("GameDatabase", "Force Interpreter"), TRANSLATE_NOOP("GameDatabase", "Force Software Renderer"), TRANSLATE_NOOP("GameDatabase", "Force Software Renderer For Readbacks"), + TRANSLATE_NOOP("GameDatabase", "Force Round Texture Coordinates"), TRANSLATE_NOOP("GameDatabase", "Force Interlacing"), TRANSLATE_NOOP("GameDatabase", "Disable True Color"), TRANSLATE_NOOP("GameDatabase", "Disable Upscaling"), @@ -434,6 +436,11 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes settings.gpu_use_software_renderer_for_readbacks = true; } + if (HasTrait(Trait::ForceRoundUpscaledTextureCoordinates)) + { + settings.gpu_force_round_texcoords = true; + } + if (HasTrait(Trait::ForceInterlacing)) { if (display_osd_messages && settings.gpu_disable_interlacing) diff --git a/src/core/game_database.h b/src/core/game_database.h index 4e2b38c0a..06a2b0894 100644 --- a/src/core/game_database.h +++ b/src/core/game_database.h @@ -31,6 +31,7 @@ enum class Trait : u32 ForceInterpreter, ForceSoftwareRenderer, ForceSoftwareRendererForReadbacks, + ForceRoundUpscaledTextureCoordinates, ForceInterlacing, DisableTrueColor, DisableUpscaling, diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 14ebaa717..96644b534 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -198,7 +198,9 @@ bool GPU_HW::Initialize() 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_scaled_dithering = (m_resolution_scale > 1 && g_settings.gpu_scaled_dithering); + m_force_round_texcoords = (m_resolution_scale > 1 && g_settings.gpu_force_round_texcoords && + g_settings.gpu_texture_filter == GPUTextureFilter::Nearest); m_texture_filtering = g_settings.gpu_texture_filter; m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled; m_clamp_uvs = ShouldClampUVs(); @@ -336,7 +338,10 @@ void GPU_HW::UpdateSettings(const Settings& old_settings) const bool shaders_changed = (m_resolution_scale != resolution_scale || m_multisamples != multisamples || 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_per_sample_shading != per_sample_shading || + m_scaled_dithering != (resolution_scale > 1 && g_settings.gpu_scaled_dithering) || + m_force_round_texcoords != (resolution_scale > 1 && g_settings.gpu_force_round_texcoords && + g_settings.gpu_texture_filter == GPUTextureFilter::Nearest) || m_texture_filtering != g_settings.gpu_texture_filter || m_clamp_uvs != clamp_uvs || m_downsample_mode != downsample_mode || (m_downsample_mode == GPUDownsampleMode::Box && @@ -386,7 +391,9 @@ void GPU_HW::UpdateSettings(const Settings& old_settings) 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_scaled_dithering = (m_resolution_scale > 1 && g_settings.gpu_scaled_dithering); + m_force_round_texcoords = (m_resolution_scale > 1 && g_settings.gpu_force_round_texcoords && + g_settings.gpu_texture_filter == GPUTextureFilter::Nearest); m_texture_filtering = g_settings.gpu_texture_filter; m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled; m_clamp_uvs = clamp_uvs; @@ -646,12 +653,14 @@ void GPU_HW::PrintSettingsToLog() INFO_LOG("Multisampling: {}x{}", m_multisamples, m_per_sample_shading ? " (per sample shading)" : ""); INFO_LOG("Dithering: {}{}", m_true_color ? "Disabled" : "Enabled", (!m_true_color && m_scaled_dithering) ? " (Scaled)" : ((m_true_color && m_debanding) ? " (Debanding)" : "")); + INFO_LOG("Force round texture coordinates: {}", m_force_round_texcoords ? "Enabled" : "Disabled"); INFO_LOG("Texture Filtering: {}", Settings::GetTextureFilterDisplayName(m_texture_filtering)); INFO_LOG("Dual-source blending: {}", m_supports_dual_source_blend ? "Supported" : "Not supported"); INFO_LOG("Clamping UVs: {}", m_clamp_uvs ? "YES" : "NO"); INFO_LOG("Depth buffer: {}", m_pgxp_depth_buffer ? "YES" : "NO"); INFO_LOG("Downsampling: {}", Settings::GetDownsampleModeDisplayName(m_downsample_mode)); INFO_LOG("Wireframe rendering: {}", Settings::GetGPUWireframeModeDisplayName(m_wireframe_mode)); + INFO_LOG("Line detection: {}", Settings::GetLineDetectModeDisplayName(m_line_detect_mode)); INFO_LOG("Using software renderer for readbacks: {}", m_sw_renderer ? "YES" : "NO"); } @@ -858,8 +867,9 @@ bool GPU_HW::CompilePipelines() { const std::string fs = shadergen.GenerateBatchFragmentShader( static_cast(render_mode), static_cast(transparency_mode), - static_cast(texture_mode), m_texture_filtering, ConvertToBoolUnchecked(dithering), - ConvertToBoolUnchecked(interlacing), ConvertToBoolUnchecked(check_mask)); + static_cast(texture_mode), m_texture_filtering, m_force_round_texcoords, + ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing), + ConvertToBoolUnchecked(check_mask)); if (!(batch_fragment_shaders[render_mode][transparency_mode][texture_mode][check_mask][dithering] [interlacing] = g_gpu_device->CreateShader(GPUShaderStage::Fragment, @@ -936,7 +946,7 @@ bool GPU_HW::CompilePipelines() { for (u8 check_mask = 0; check_mask < 2; check_mask++) { - const bool textured = (static_cast(texture_mode) != GPUTextureMode::Disabled); + const bool textured = (static_cast(texture_mode) != BatchTextureMode::Disabled); const bool use_shader_blending = (render_mode == static_cast(BatchRenderMode::ShaderBlend) && ((textured && @@ -2498,7 +2508,7 @@ ALWAYS_INLINE_RELEASE bool GPU_HW::NeedsTwoPassRendering() const // We need two-pass rendering when using BG-FG blending and texturing, as the transparency can be enabled // on a per-pixel basis, and the opaque pixels shouldn't be blended at all. - return (m_batch.texture_mode != GPUTextureMode::Disabled && + return (m_batch.texture_mode != BatchTextureMode::Disabled && (m_batch.transparency_mode == GPUTransparencyMode::BackgroundMinusForeground || (!m_supports_dual_source_blend && m_batch.transparency_mode != GPUTransparencyMode::Disabled))); } @@ -2970,7 +2980,7 @@ void GPU_HW::DispatchRenderCommand() { const GPURenderCommand rc{m_render_command.bits}; - GPUTextureMode texture_mode = GPUTextureMode::Disabled; + BatchTextureMode texture_mode = BatchTextureMode::Disabled; if (rc.IsTexturingEnabled()) { // texture page changed - check that the new page doesn't intersect the drawing area @@ -3030,9 +3040,9 @@ void GPU_HW::DispatchRenderCommand() } } - texture_mode = (m_draw_mode.mode_reg.texture_mode == GPUTextureMode::Reserved_Direct16Bit2) ? - GPUTextureMode::Direct16Bit : - m_draw_mode.mode_reg.texture_mode; + texture_mode = (m_draw_mode.mode_reg.texture_mode == GPUTextureMode::Reserved_Direct16Bit) ? + BatchTextureMode::Direct16Bit : + static_cast(m_draw_mode.mode_reg.texture_mode.GetValue()); } // has any state changed which requires a new batch? @@ -3055,7 +3065,7 @@ void GPU_HW::DispatchRenderCommand() // transparency mode change const bool check_mask_before_draw = m_GPUSTAT.check_mask_before_draw; if (transparency_mode != GPUTransparencyMode::Disabled && - (texture_mode == GPUTextureMode::Disabled || !NeedsShaderBlending(transparency_mode, check_mask_before_draw))) + (texture_mode == BatchTextureMode::Disabled || !NeedsShaderBlending(transparency_mode, check_mask_before_draw))) { static constexpr float transparent_alpha[4][2] = {{0.5f, 0.5f}, {1.0f, 1.0f}, {1.0f, 1.0f}, {0.25f, 1.0f}}; diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 70f53d843..3d497dac2 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -33,6 +33,19 @@ public: ShaderBlend }; + enum class BatchTextureMode : u8 + { + Palette4Bit, + Palette8Bit, + Direct16Bit, + Disabled, + + MaxCount, + }; + static_assert(static_cast(BatchTextureMode::Palette4Bit) == static_cast(GPUTextureMode::Palette4Bit) && + static_cast(BatchTextureMode::Palette8Bit) == static_cast(GPUTextureMode::Palette8Bit) && + static_cast(BatchTextureMode::Direct16Bit) == static_cast(GPUTextureMode::Direct16Bit)); + GPU_HW(); ~GPU_HW() override; @@ -58,7 +71,7 @@ private: MAX_BATCH_VERTEX_COUNTER_IDS = 65536 - 2, MAX_VERTICES_FOR_RECTANGLE = 6 * (((MAX_PRIMITIVE_WIDTH + (TEXTURE_PAGE_WIDTH - 1)) / TEXTURE_PAGE_WIDTH) + 1u) * (((MAX_PRIMITIVE_HEIGHT + (TEXTURE_PAGE_HEIGHT - 1)) / TEXTURE_PAGE_HEIGHT) + 1u), - NUM_TEXTURE_MODES = 4, + NUM_TEXTURE_MODES = static_cast(BatchTextureMode::MaxCount), }; enum : u8 { @@ -88,7 +101,7 @@ private: struct BatchConfig { - GPUTextureMode texture_mode = GPUTextureMode::Disabled; + BatchTextureMode texture_mode = BatchTextureMode::Disabled; GPUTransparencyMode transparency_mode = GPUTransparencyMode::Disabled; bool dithering = false; bool interlacing = false; @@ -235,6 +248,7 @@ private: bool m_per_sample_shading : 1 = false; bool m_scaled_dithering : 1 = false; bool m_disable_color_perspective : 1 = false; + bool m_force_round_texcoords = false; GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest; GPULineDetectMode m_line_detect_mode = GPULineDetectMode::Disabled; diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index 3a0f153b1..62808f4dc 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -630,15 +630,14 @@ void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits, } } -std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMode render_mode, - GPUTransparencyMode transparency, GPUTextureMode texture_mode, - GPUTextureFilter texture_filtering, bool dithering, - bool interlacing, bool check_mask) +std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader( + GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency, GPU_HW::BatchTextureMode texture_mode, + GPUTextureFilter texture_filtering, bool force_round_texcoords, bool dithering, bool interlacing, bool check_mask) { // TODO: don't write depth for shader blend DebugAssert(transparency == GPUTransparencyMode::Disabled || render_mode == GPU_HW::BatchRenderMode::ShaderBlend); - const bool textured = (texture_mode != GPUTextureMode::Disabled); + const bool textured = (texture_mode != GPU_HW::BatchTextureMode::Disabled); const bool shader_blending = (render_mode == GPU_HW::BatchRenderMode::ShaderBlend && (transparency != GPUTransparencyMode::Disabled || check_mask)); const bool use_dual_source = (!shader_blending && m_supports_dual_source_blend && @@ -656,9 +655,10 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod DefineMacro(ss, "CHECK_MASK_BIT", check_mask); DefineMacro(ss, "TEXTURED", textured); DefineMacro(ss, "PALETTE", - texture_mode == GPUTextureMode::Palette4Bit || texture_mode == GPUTextureMode::Palette8Bit); - DefineMacro(ss, "PALETTE_4_BIT", texture_mode == GPUTextureMode::Palette4Bit); - DefineMacro(ss, "PALETTE_8_BIT", texture_mode == GPUTextureMode::Palette8Bit); + texture_mode == GPU_HW::BatchTextureMode::Palette4Bit || + texture_mode == GPU_HW::BatchTextureMode::Palette8Bit); + DefineMacro(ss, "PALETTE_4_BIT", texture_mode == GPU_HW::BatchTextureMode::Palette4Bit); + DefineMacro(ss, "PALETTE_8_BIT", texture_mode == GPU_HW::BatchTextureMode::Palette8Bit); DefineMacro(ss, "DITHERING", dithering); DefineMacro(ss, "DITHERING_SCALED", m_scaled_dithering); // Debanding requires true color to work correctly. @@ -669,6 +669,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod DefineMacro(ss, "UV_LIMITS", m_uv_limits); DefineMacro(ss, "USE_DUAL_SOURCE", use_dual_source); DefineMacro(ss, "WRITE_MASK_AS_DEPTH", m_write_mask_as_depth); + DefineMacro(ss, "FORCE_ROUND_TEXCOORDS", force_round_texcoords); WriteCommonFunctions(ss); WriteBatchUniformBuffer(ss); @@ -727,7 +728,7 @@ uint2 FloatToIntegerCoords(float2 coords) { // With the vertex offset applied at 1x resolution scale, we want to round the texture coordinates. // Floor them otherwise, as it currently breaks when upscaling as the vertex offset is not applied. - return uint2((RESOLUTION_SCALE == 1u) ? roundEven(coords) : floor(coords)); + return uint2((RESOLUTION_SCALE == 1u || FORCE_ROUND_TEXCOORDS != 0) ? roundEven(coords) : floor(coords)); } float4 SampleFromVRAM(uint4 texpage, float2 coords) diff --git a/src/core/gpu_hw_shadergen.h b/src/core/gpu_hw_shadergen.h index ad06954fe..e1e9a0041 100644 --- a/src/core/gpu_hw_shadergen.h +++ b/src/core/gpu_hw_shadergen.h @@ -16,8 +16,9 @@ public: std::string GenerateBatchVertexShader(bool textured, bool pgxp_depth); std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency, - GPUTextureMode texture_mode, GPUTextureFilter texture_filtering, - bool dithering, bool interlacing, bool check_mask); + GPU_HW::BatchTextureMode texture_mode, GPUTextureFilter texture_filtering, + bool force_round_texcoords, bool dithering, bool interlacing, + bool check_mask); std::string GenerateWireframeGeometryShader(); std::string GenerateWireframeFragmentShader(); std::string GenerateVRAMReadFragmentShader(); diff --git a/src/core/gpu_types.h b/src/core/gpu_types.h index b56091d02..5f6cfa9ae 100644 --- a/src/core/gpu_types.h +++ b/src/core/gpu_types.h @@ -53,8 +53,7 @@ enum class GPUTextureMode : u8 Palette4Bit = 0, Palette8Bit = 1, Direct16Bit = 2, - Reserved_Direct16Bit2 = 3, // Not used. - Disabled = 3 // Not a register value + Reserved_Direct16Bit = 3, // Not used. }; IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(GPUTextureMode); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 18834dfe2..7ae1b4684 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -197,6 +197,7 @@ void Settings::Load(SettingsInterface& si) gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true); gpu_debanding = si.GetBoolValue("GPU", "Debanding", false); gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", true); + gpu_force_round_texcoords = si.GetBoolValue("GPU", "ForceRoundTextureCoordinates", false); gpu_texture_filter = ParseTextureFilterName( si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str()) @@ -495,6 +496,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetBoolValue("GPU", "TrueColor", gpu_true_color); si.SetBoolValue("GPU", "Debanding", gpu_debanding); si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering); + si.SetBoolValue("GPU", "ForceRoundTextureCoordinates", gpu_force_round_texcoords); si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter)); si.SetStringValue("GPU", "LineDetectMode", GetLineDetectModeName(gpu_line_detect_mode)); si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode)); @@ -701,6 +703,7 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages) g_settings.gpu_true_color = false; g_settings.gpu_debanding = false; g_settings.gpu_scaled_dithering = false; + g_settings.gpu_force_round_texcoords = false; g_settings.gpu_texture_filter = GPUTextureFilter::Nearest; g_settings.gpu_line_detect_mode = GPULineDetectMode::Disabled; g_settings.gpu_disable_interlacing = false; diff --git a/src/core/settings.h b/src/core/settings.h index 13f5639a6..dad916242 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -117,6 +117,7 @@ struct Settings bool gpu_true_color : 1 = true; bool gpu_debanding : 1 = false; bool gpu_scaled_dithering : 1 = true; + bool gpu_force_round_texcoords : 1 = false; GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER; GPULineDetectMode gpu_line_detect_mode = DEFAULT_GPU_LINE_DETECT_MODE; GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE; diff --git a/src/core/system.cpp b/src/core/system.cpp index 3f583abb4..1fe0dc251 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -4015,6 +4015,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) 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_force_round_texcoords != old_settings.gpu_force_round_texcoords || g_settings.gpu_texture_filter != old_settings.gpu_texture_filter || g_settings.gpu_line_detect_mode != old_settings.gpu_line_detect_mode || g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing || diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index 850e83acc..0a74afc49 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -89,7 +89,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* connect(m_ui.adapter, QOverload::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onAdapterChanged); connect(m_ui.resolutionScale, QOverload::of(&QComboBox::currentIndexChanged), this, - &GraphicsSettingsWidget::onTrueColorChanged); + &GraphicsSettingsWidget::updateResolutionDependentOptions); + connect(m_ui.textureFiltering, QOverload::of(&QComboBox::currentIndexChanged), this, + &GraphicsSettingsWidget::updateResolutionDependentOptions); connect(m_ui.displayAspectRatio, QOverload::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onAspectRatioChanged); connect(m_ui.gpuDownsampleMode, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -143,6 +145,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledDithering, "GPU", "ScaledDithering", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useSoftwareRendererForReadbacks, "GPU", "UseSoftwareRendererForReadbacks", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates", + false); connect(m_ui.fullscreenMode, QOverload::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onFullscreenModeChanged); @@ -230,7 +234,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* updateRendererDependentOptions(); onAspectRatioChanged(); onDownsampleModeChanged(); - onTrueColorChanged(); + updateResolutionDependentOptions(); onEnableAnyTextureReplacementsChanged(); onEnableVRAMWriteDumpingChanged(); onShowDebugSettingsChanged(QtHost::ShouldShowDebugOptions()); @@ -373,6 +377,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* m_ui.useSoftwareRendererForReadbacks, tr("Software Renderer Readbacks"), tr("Unchecked"), tr("Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater " "performance when using graphical enhancements with the hardware renderer.")); + dialog->registerWidgetHelp( + m_ui.forceRoundedTexcoords, tr("Round Upscaled Texture Coordinates"), tr("Unchecked"), + tr("Rounds texture coordinates instead of flooring when upscaling. Can fix misaligned textures in some games, but " + "break others, and is incompatible with texture filtering.")); // PGXP Tab @@ -796,6 +804,20 @@ void GraphicsSettingsWidget::onAspectRatioChanged() m_ui.customAspectRatioSeparator->setVisible(is_custom); } +void GraphicsSettingsWidget::updateResolutionDependentOptions() +{ + const int scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1); + const GPUTextureFilter texture_filtering = + Settings::ParseTextureFilterName( + m_dialog + ->getEffectiveStringValue("GPU", "TextureFilter", + Settings::GetTextureFilterName(Settings::DEFAULT_GPU_TEXTURE_FILTER)) + .c_str()) + .value_or(Settings::DEFAULT_GPU_TEXTURE_FILTER); + m_ui.forceRoundedTexcoords->setEnabled(scale > 1 && texture_filtering == GPUTextureFilter::Nearest); + onTrueColorChanged(); +} + void GraphicsSettingsWidget::onMSAAModeChanged() { const int index = m_ui.msaaMode->currentIndex(); @@ -816,8 +838,8 @@ void GraphicsSettingsWidget::onMSAAModeChanged() void GraphicsSettingsWidget::onTrueColorChanged() { - const int resolution_scale = m_ui.resolutionScale->currentIndex(); - const bool true_color = m_ui.trueColor->isChecked(); + const int resolution_scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1); + const bool true_color = m_dialog->getEffectiveBoolValue("GPU", "TrueColor", false); const bool allow_scaled_dithering = (resolution_scale != 1 && !true_color); const bool allow_debanding = true_color; m_ui.scaledDithering->setEnabled(allow_scaled_dithering); diff --git a/src/duckstation-qt/graphicssettingswidget.h b/src/duckstation-qt/graphicssettingswidget.h index d976915c7..f3083d90d 100644 --- a/src/duckstation-qt/graphicssettingswidget.h +++ b/src/duckstation-qt/graphicssettingswidget.h @@ -29,6 +29,7 @@ private Q_SLOTS: void onAdapterChanged(); void onAspectRatioChanged(); + void updateResolutionDependentOptions(); void onMSAAModeChanged(); void onTrueColorChanged(); void onDownsampleModeChanged(); diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index b38590351..a2938e91c 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -468,10 +468,10 @@ - - + + - True Color Debanding + Threaded Rendering @@ -489,10 +489,17 @@ - - + + - Threaded Rendering + True Color Debanding + + + + + + + Round Upscaled Texture Coordinates