diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index 625c16e77d..c6082138a7 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -104,13 +104,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.fmvAspectRatio, "EmuCore/GS", "FMVAspectRatioSwitch", Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames, FMVAspectRatioSwitchType::Off); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.interlacing, "EmuCore/GS", "deinterlace_mode", DEFAULT_INTERLACE_MODE); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.bilinearFiltering, "EmuCore/GS", "linear_present", true); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.bilinearFiltering, "EmuCore/GS", "linear_present_mode", static_cast(GSPostBilinearMode::BilinearSmooth)); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOffsets, "EmuCore/GS", "pcrtc_offsets", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOverscan, "EmuCore/GS", "pcrtc_overscan", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCAntiBlur, "EmuCore/GS", "pcrtc_antiblur", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.DisableInterlaceOffset, "EmuCore/GS", "disable_interlace_offset", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.internalResolutionScreenshots, "EmuCore/GS", "InternalResolutionScreenshots", false); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cropLeft, "EmuCore/GS", "CropLeft", 0); @@ -347,7 +346,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* dialog->registerWidgetHelp(m_ui.DisableInterlaceOffset, tr("Disable Interlace Offset"), tr("Unchecked"), tr("Disables interlacing offset which may reduce blurring in some situations.")); - dialog->registerWidgetHelp(m_ui.bilinearFiltering, tr("Bilinear Filtering"), tr("Checked"), + dialog->registerWidgetHelp(m_ui.bilinearFiltering, tr("Bilinear Filtering"), tr("Bilinear (Sharp)"), tr("Enables bilinear post processing filter. Smooths the overall picture as it is displayed on the screen. Corrects positioning between pixels.")); dialog->registerWidgetHelp(m_ui.PCRTCOffsets, tr("Screen Offsets"), tr("Unchecked"), @@ -365,9 +364,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* dialog->registerWidgetHelp(m_ui.vsync, tr("VSync"), tr("Unchecked"), tr("Enable this option to match PCSX2's refresh rate with your current monitor or screen. VSync is automatically disabled when it is not possible (eg. running at non-100% speed).")); - dialog->registerWidgetHelp(m_ui.internalResolutionScreenshots, tr("Internal Resolution Screenshots"), tr("Unchecked"), - tr("Saves screenshots at internal render resolution and without postprocessing. If this option is disabled, the screenshots will be taken at the window's resolution. Internal resolution screenshots can be very large at high rendering scales.")); - dialog->registerWidgetHelp(m_ui.integerScaling, tr("Integer Scaling"), tr("Unchecked"), tr("Adds padding to the display area to ensure that the ratio between pixels on the host to pixels in the console is an integer number. May result in a sharper image in some 2D games.")); } diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui index a35b915211..2d93744f23 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -201,14 +201,14 @@ - + Zoom: - + % @@ -221,14 +221,14 @@ - + Stretch Height: - + % @@ -241,14 +241,14 @@ - + Crop: - + @@ -320,8 +320,15 @@ - + + + + + Disable Interlace Offset + + + @@ -329,20 +336,6 @@ - - - - Bilinear Filtering - - - - - - - Internal Resolution Screenshots - - - @@ -364,14 +357,7 @@ - - - - Disable Interlace Offset - - - - + Anti-Blur @@ -383,6 +369,32 @@ + + + + Bilinear Filtering + + + + + + + + None + + + + + Bilinear (Smooth) + + + + + Bilinear (Sharp) + + + + diff --git a/pcsx2/Config.h b/pcsx2/Config.h index c79b1e069e..f6176d9901 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -144,6 +144,13 @@ enum class GSInterlaceMode : u8 Count }; +enum class GSPostBilinearMode : u8 +{ + Off, + BilinearSmooth, + BilinearSharp, +}; + // Ordering was done to keep compatibility with older ini file. enum class BiFiltering : u8 { @@ -484,7 +491,6 @@ struct Pcsx2Config PCRTCOffsets : 1, PCRTCOverscan : 1, IntegerScaling : 1, - LinearPresent : 1, SyncToHostRefreshRate : 1, UseDebugDevice : 1, UseBlitSwapChain : 1, @@ -560,6 +566,7 @@ struct Pcsx2Config AspectRatioType AspectRatio{AspectRatioType::RAuto4_3_3_2}; FMVAspectRatioSwitchType FMVAspectRatioSwitch{FMVAspectRatioSwitchType::Off}; GSInterlaceMode InterlaceMode{GSInterlaceMode::Automatic}; + GSPostBilinearMode LinearPresent{ GSPostBilinearMode::BilinearSmooth }; float Zoom{100.0f}; float StretchY{100.0f}; diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index f017659860..5d9c93e400 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -2793,6 +2793,7 @@ void FullscreenUI::DrawGraphicsSettingsPage() "11", //GSRendererType::Null }; static constexpr const char* s_vsync_values[] = {"Off", "On", "Adaptive"}; + static constexpr const char* s_bilinear_present_options[] = {"Off", "Bilinear (Smooth)", "Bilinear (Sharp)"}; static constexpr const char* s_deinterlacing_options[] = {"Automatic (Default)", "None", "Weave (Top Field First, Sawtooth)", "Weave (Bottom Field First, Sawtooth)", "Bob (Top Field First)", "Bob (Bottom Field First)", "Blend (Top Field First, Half FPS)", "Blend (Bottom Field First, Half FPS)", "Adaptive (Top Field First)", "Adaptive (Bottom Field First)"}; @@ -2877,15 +2878,13 @@ void FullscreenUI::DrawGraphicsSettingsPage() 100, 10, 300, "%d%%"); DrawIntRectSetting(bsi, "Crop", "Crops the image, while respecting aspect ratio.", "EmuCore/GS", "CropLeft", 0, "CropTop", 0, "CropRight", 0, "CropBottom", 0, 0, 720, "%dpx"); - DrawToggleSetting( - bsi, "Bilinear Upscaling", "Smooths out the image when upscaling the console to the screen.", "EmuCore/GS", "linear_present", true); + DrawIntListSetting(bsi, "Bilinear Upscaling", + "Smooths out the image when upscaling the console to the screen.", "EmuCore/GS", "linear_present_mode", + static_cast(GSPostBilinearMode::BilinearSharp), s_bilinear_present_options, std::size(s_bilinear_present_options)); DrawToggleSetting(bsi, "Integer Upscaling", "Adds padding to the display area to ensure that the ratio between pixels on the host to pixels in the console is an integer " "number. May result in a sharper image in some 2D games.", "EmuCore/GS", "IntegerScaling", false); - DrawToggleSetting(bsi, "Internal Resolution Screenshots", - "Save screenshots at the full render resolution, rather than display resolution.", "EmuCore/GS", "InternalResolutionScreenshots", - false); DrawToggleSetting(bsi, "Screen Offsets", "Enables PCRTC Offsets which position the screen as the game requests.", "EmuCore/GS", "pcrtc_offsets", false); DrawToggleSetting(bsi, "Show Overscan", diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index eafcef2424..1d3413a20c 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -1354,6 +1354,10 @@ void GSApp::Init() m_gs_texture_preloading.push_back(GSSetting(static_cast(TexturePreloadingLevel::Partial), "Partial", "")); m_gs_texture_preloading.push_back(GSSetting(static_cast(TexturePreloadingLevel::Full), "Full", "Hash Cache")); + m_gs_tex_display_list.push_back(GSSetting(static_cast(GSPostBilinearMode::Off), "None", "")); + m_gs_tex_display_list.push_back(GSSetting(static_cast(GSPostBilinearMode::BilinearSmooth), "Bilinear (Smooth)", "")); + m_gs_tex_display_list.push_back(GSSetting(static_cast(GSPostBilinearMode::BilinearSharp), "Bilinear (Sharp)", "")); + m_gs_generic_list.push_back(GSSetting(-1, "Automatic", "Default")); m_gs_generic_list.push_back(GSSetting(0, "Force-Disabled", "")); m_gs_generic_list.push_back(GSSetting(1, "Force-Enabled", "")); @@ -1455,7 +1459,7 @@ void GSApp::Init() m_default_configuration["pcrtc_overscan"] = "0"; m_default_configuration["IntegerScaling"] = "0"; m_default_configuration["deinterlace_mode"] = std::to_string(static_cast(GSInterlaceMode::Automatic)); - m_default_configuration["linear_present"] = "1"; + m_default_configuration["linear_present_mode"] = std::to_string(static_cast(GSPostBilinearMode::BilinearSmooth)); m_default_configuration["LoadTextureReplacements"] = "0"; m_default_configuration["LoadTextureReplacementsAsync"] = "1"; m_default_configuration["MaxAnisotropy"] = "0"; diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index d1fa2aa20d..8901deafde 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -137,6 +137,7 @@ public: std::vector m_gs_texture_preloading; std::vector m_gs_hack; std::vector m_gs_generic_list; + std::vector m_gs_tex_display_list; std::vector m_gs_offset_hack; std::vector m_gs_hw_mipmapping; std::vector m_gs_crc_level; diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index 4f841a7bdf..f4d50a6923 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -309,6 +309,12 @@ void GSDevice::ClearCurrent() m_mad = nullptr; m_target_tmp = nullptr; m_cas = nullptr; + m_temp_snapshot = nullptr; +} + +void GSDevice::SetSnapshot() +{ + m_temp_snapshot = m_current; } void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) @@ -442,6 +448,25 @@ void GSDevice::ShadeBoost() } } +void GSDevice::Resize(int width, int height) +{ + GSVector2i s = m_current->GetSize(); + int multiplier = 1; + + while (width > s.x || height > s.y) + { + s = m_current->GetSize() * GSVector2i(++multiplier); + } + + if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, s.x, s.y)) + { + const GSVector4 sRect(0, 0, 1, 1); + const GSVector4 dRect(0, 0, s.x, s.y); + StretchRect(m_current, sRect, m_target_tmp, dRect, ShaderConvert::COPY, false); + m_current = m_target_tmp; + } +} + bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear, bool prefer_reuse) { if (t == NULL) diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index a5b2478b4d..8c3c2d38b5 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -750,6 +750,8 @@ protected: GSTexture* m_target_tmp = nullptr; GSTexture* m_current = nullptr; GSTexture* m_cas = nullptr; + GSTexture* m_temp_snapshot = nullptr; // No need to delete this, only ever points to m_current. + struct { size_t stride, start, count, limit; @@ -852,12 +854,16 @@ public: __fi FeatureSupport Features() const { return m_features; } __fi GSTexture* GetCurrent() const { return m_current; } + __fi GSTexture* GetSnapshot() const { return m_temp_snapshot; } void ClearCurrent(); + void SetSnapshot(); void Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c); void Interlace(const GSVector2i& ds, int field, int mode, float yoffset); void FXAA(); void ShadeBoost(); + void Resize(int width, int height); + #ifndef PCSX2_CORE void ExternalFX(); #endif diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp index 1fc225becd..9bc9dd4ec0 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp +++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp @@ -403,6 +403,14 @@ bool GSRenderer::Merge(int field) if (GSConfig.FXAA) g_gs_device->FXAA(); + g_gs_device->SetSnapshot(); + + // Sharpens biinear at lower resolutions, almost nearest but with more uniform pixels. + if (GSConfig.LinearPresent == GSPostBilinearMode::BilinearSharp && (g_host_display->GetWindowWidth() > fs.x || g_host_display->GetWindowHeight() > fs.y)) + { + g_gs_device->Resize(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()); + } + if (m_scanmask_used) m_scanmask_used--; @@ -643,7 +651,7 @@ void GSRenderer::VSync(u32 field, bool registers_written) const float shader_time = static_cast(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start)); g_gs_device->PresentRect(current, src_uv, nullptr, draw_rect, - s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent); + s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off); } Host::EndPresentFrame(); @@ -708,7 +716,7 @@ void GSRenderer::VSync(u32 field, bool registers_written) Path::GetFileName(m_dump->GetPath())), Host::OSD_INFO_DURATION); } - if (GSTexture* t = g_gs_device->GetCurrent()) + if (GSTexture* t = g_gs_device->GetSnapshot()) { const std::string path(m_snapshot + ".png"); if (t->Save(path)) @@ -850,7 +858,7 @@ void GSRenderer::PresentCurrentFrame() const float shader_time = static_cast(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start)); g_gs_device->PresentRect(current, src_uv, nullptr, draw_rect, - s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent); + s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off); } Host::EndPresentFrame(); diff --git a/pcsx2/GS/Window/GSwxDialog.cpp b/pcsx2/GS/Window/GSwxDialog.cpp index 03fee54247..84470eddf3 100644 --- a/pcsx2/GS/Window/GSwxDialog.cpp +++ b/pcsx2/GS/Window/GSwxDialog.cpp @@ -465,8 +465,12 @@ PostTab::PostTab(wxWindow* parent) PaddedBoxSizer shader_box(wxVERTICAL, this, "Custom Shader"); auto not_vk_prereq = [this] { return !m_is_vk_hw; }; + + PaddedBoxSizer tex_filter_box(wxVERTICAL, this, "Debug"); + auto* tex_filter_grid_box = new wxFlexGridSizer(2, space, space); + + m_ui.addComboBoxAndLabel(tex_filter_grid_box, "Texture Filtering of Display:", "linear_present_mode", &theApp.m_gs_tex_display_list, IDC_LINEAR_PRESENT); - m_ui.addCheckBox(shader_box.inner, "Texture Filtering of Display", "linear_present", IDC_LINEAR_PRESENT); m_ui.addCheckBox(shader_box.inner, "FXAA Shader (PgUp)", "fxaa", IDC_FXAA); CheckboxPrereq shade_boost_check(m_ui.addCheckBox(shader_box.inner, "Enable Shade Boost", "ShadeBoost", IDC_SHADEBOOST)); @@ -480,6 +484,7 @@ PostTab::PostTab(wxWindow* parent) m_ui.addSliderAndLabel(shader_boost_grid, "Saturation:", "ShadeBoost_Saturation", 0, 100, 50, -1, shade_boost_check); shade_boost_box->Add(shader_boost_grid, wxSizerFlags().Expand()); + shader_box->Add(tex_filter_grid_box, wxSizerFlags().Expand()); shader_box->Add(shade_boost_box.outer, wxSizerFlags().Expand()); CheckboxPrereq ext_shader_check(m_ui.addCheckBox(shader_box.inner, "Enable External Shader", "shaderfx", IDC_SHADER_FX, not_vk_prereq)); @@ -500,7 +505,6 @@ PostTab::PostTab(wxWindow* parent) tv_box->AddGrowableCol(1); m_ui.addComboBoxAndLabel(tv_box, "TV Shader:", "TVShader", &theApp.m_gs_tv_shaders); shader_box->Add(tv_box, wxSizerFlags().Expand()); - tab_box->Add(shader_box.outer, wxSizerFlags().Expand()); SetSizerAndFit(tab_box.outer); } diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index a7b38e0537..6530ca46b0 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -303,7 +303,7 @@ Pcsx2Config::GSOptions::GSOptions() PCRTCOffsets = false; PCRTCOverscan = false; IntegerScaling = false; - LinearPresent = true; + LinearPresent = GSPostBilinearMode::BilinearSmooth; SyncToHostRefreshRate = false; UseDebugDevice = false; UseBlitSwapChain = false; @@ -381,6 +381,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const OpEqu(VsyncEnable) && OpEqu(InterlaceMode) && + OpEqu(LinearPresent) && OpEqu(Zoom) && OpEqu(StretchY) && @@ -477,7 +478,6 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap) SettingsWrapBitBool(SyncToHostRefreshRate); SettingsWrapEnumEx(AspectRatio, "AspectRatio", AspectRatioNames); SettingsWrapEnumEx(FMVAspectRatioSwitch, "FMVAspectRatioSwitch", FMVAspectRatioSwitchNames); - SettingsWrapEntry(Zoom); SettingsWrapEntry(StretchY); SettingsWrapEntryEx(Crop[0], "CropLeft"); @@ -532,7 +532,6 @@ void Pcsx2Config::GSOptions::ReloadIniSettings() GSSettingBoolEx(PCRTCOffsets, "pcrtc_offsets"); GSSettingBoolEx(PCRTCOverscan, "pcrtc_overscan"); GSSettingBool(IntegerScaling); - GSSettingBoolEx(LinearPresent, "linear_present"); GSSettingBool(UseDebugDevice); GSSettingBool(UseBlitSwapChain); GSSettingBoolEx(DisableShaderCache, "disable_shader_cache"); @@ -587,6 +586,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings() GSSettingBool(LoadTextureReplacementsAsync); GSSettingBool(PrecacheTextureReplacements); + GSSettingIntEnumEx(LinearPresent, "linear_present_mode"); GSSettingIntEnumEx(InterlaceMode, "deinterlace_mode"); GSSettingFloat(OsdScale);