GPU: Rewrite automatic resolution scaling

Make it play nice with rewind/runahead.
dev latest
Stenzek 1 week ago
parent d812463649
commit 3ea26cc910
No known key found for this signature in database

@ -1018,9 +1018,9 @@ void GPU::UpdateCRTCDisplayParameters()
}
if ((cs.display_vram_width != old_vram_width || cs.display_vram_height != old_vram_height) &&
g_settings.gpu_resolution_scale == 0)
g_settings.gpu_automatic_resolution_scale)
{
GPUBackend::QueueUpdateResolutionScale();
System::UpdateAutomaticResolutionScale();
}
}
@ -1980,6 +1980,38 @@ void GPU::QueuePresentCurrentFrame()
}
}
u8 GPU::CalculateAutomaticResolutionScale() const
{
// Auto scaling.
// When the system is starting and all borders crop is enabled, the registers are zero, and
// display_height therefore is also zero. Keep the existing resolution until it updates.
u32 scale = 1;
if (const WindowInfo& main_window_info = GPUThread::GetRenderWindowInfo();
!main_window_info.IsSurfaceless() && m_crtc_state.display_width > 0 && m_crtc_state.display_height > 0 &&
m_crtc_state.display_vram_width > 0 && m_crtc_state.display_vram_height > 0)
{
GSVector4i display_rect, draw_rect;
CalculateDrawRect(main_window_info.surface_width, main_window_info.surface_height, m_crtc_state.display_width,
m_crtc_state.display_height, m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, g_settings.display_rotation,
g_settings.display_alignment, g_settings.gpu_show_vram ? 1.0f : ComputePixelAspectRatio(),
g_settings.IsUsingIntegerDisplayScaling(), &display_rect, &draw_rect);
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
// anamorphic aspect ratio.
const s32 draw_width = draw_rect.width();
const s32 draw_height = draw_rect.height();
scale = static_cast<u32>(
std::ceil(std::max(static_cast<float>(draw_width) / static_cast<float>(m_crtc_state.display_vram_width),
static_cast<float>(draw_height) / static_cast<float>(m_crtc_state.display_vram_height))));
scale = std::min<u32>(scale, std::numeric_limits<decltype(g_settings.gpu_resolution_scale)>::max());
VERBOSE_LOG("Draw Size = {}x{}, VRAM Size = {}x{}, Preferred Scale = {}", draw_width, draw_height,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, scale);
}
return Truncate8(scale);
}
bool GPU::DumpVRAMToFile(const char* filename)
{
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);

@ -230,6 +230,9 @@ public:
// Queues the current frame for presentation. Should only be used with runahead.
void QueuePresentCurrentFrame();
/// Computes the effective resolution scale when it is set to automatic.
u8 CalculateAutomaticResolutionScale() const;
/// Helper function for computing the draw rectangle in a larger window.
static void CalculateDrawRect(u32 window_width, u32 window_height, u32 crtc_display_width, u32 crtc_display_height,
s32 display_origin_left, s32 display_origin_top, u32 display_vram_width,

@ -398,20 +398,6 @@ bool GPUBackend::AllocateMemorySaveStates(std::span<System::MemorySaveState> sta
return result;
}
void GPUBackend::QueueUpdateResolutionScale()
{
DebugAssert(!GPUThread::IsOnThread());
GPUThread::RunOnBackend(
[](GPUBackend* backend) {
Error error;
if (!backend->UpdateResolutionScale(&error)) [[unlikely]]
GPUThread::ReportFatalErrorAndShutdown(
fmt::format("Failed to update resolution scale: {}", error.GetDescription()));
},
false, true);
}
void GPUBackend::HandleCommand(const GPUThreadCommand* cmd)
{
switch (cmd->type)
@ -837,7 +823,6 @@ public:
bool UpdateSettings(const GPUSettings& old_settings, Error* error) override;
u32 GetResolutionScale() const override;
bool UpdateResolutionScale(Error* error) override;
void RestoreDeviceContext() override;
void FlushRender() override;
@ -891,11 +876,6 @@ u32 GPUNullBackend::GetResolutionScale() const
return 1;
}
bool GPUNullBackend::UpdateResolutionScale(Error* error)
{
return true;
}
void GPUNullBackend::RestoreDeviceContext()
{
}

@ -69,8 +69,6 @@ public:
static bool AllocateMemorySaveStates(std::span<System::MemorySaveState> states, Error* error);
static void QueueUpdateResolutionScale();
public:
GPUBackend(GPUPresenter& presenter);
virtual ~GPUBackend();
@ -86,9 +84,6 @@ public:
/// Returns the current resolution scale.
virtual u32 GetResolutionScale() const = 0;
/// Updates the resolution scale when it's set to automatic.
virtual bool UpdateResolutionScale(Error* error) = 0;
// Graphics API state reset/restore - call when drawing the UI etc.
// TODO: replace with "invalidate cached state"
virtual void RestoreDeviceContext() = 0;

@ -726,40 +726,7 @@ void GPU_HW::CheckSettings()
u32 GPU_HW::CalculateResolutionScale() const
{
u32 scale;
if (g_gpu_settings.gpu_resolution_scale != 0)
{
scale = g_gpu_settings.gpu_resolution_scale;
}
else
{
// Auto scaling.
if (m_presenter.GetDisplayWidth() == 0 || m_presenter.GetDisplayHeight() == 0 ||
m_presenter.GetDisplayVRAMWidth() == 0 || m_presenter.GetDisplayVRAMHeight() == 0 ||
!m_presenter.HasDisplayTexture() || !g_gpu_device->HasMainSwapChain())
{
// When the system is starting and all borders crop is enabled, the registers are zero, and
// display_height therefore is also zero. Keep the existing resolution until it updates.
scale = m_resolution_scale;
}
else
{
GSVector4i display_rect, draw_rect;
m_presenter.CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(),
g_gpu_device->GetMainSwapChain()->GetHeight(), true, true, &display_rect,
&draw_rect);
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
// anamorphic aspect ratio.
const s32 draw_width = draw_rect.width();
const s32 draw_height = draw_rect.height();
scale = static_cast<u32>(
std::ceil(std::max(static_cast<float>(draw_width) / static_cast<float>(m_presenter.GetDisplayVRAMWidth()),
static_cast<float>(draw_height) / static_cast<float>(m_presenter.GetDisplayVRAMHeight()))));
VERBOSE_LOG("Draw Size = {}x{}, VRAM Size = {}x{}, Preferred Scale = {}", draw_width, draw_height,
m_presenter.GetDisplayVRAMWidth(), m_presenter.GetDisplayVRAMHeight(), scale);
}
}
u32 scale = g_gpu_settings.gpu_resolution_scale;
if (g_gpu_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive && scale > 1 && !Common::IsPow2(scale))
{
@ -782,14 +749,6 @@ u32 GPU_HW::CalculateResolutionScale() const
return std::clamp<u32>(scale, 1, GetMaxResolutionScale());
}
bool GPU_HW::UpdateResolutionScale(Error* error)
{
if (CalculateResolutionScale() == m_resolution_scale)
return true;
return UpdateSettings(g_gpu_settings, error);
}
GPUDownsampleMode GPU_HW::GetDownsampleMode(u32 resolution_scale) const
{
return (resolution_scale == 1) ? GPUDownsampleMode::Disabled : g_gpu_settings.gpu_downsample_mode;

@ -74,8 +74,6 @@ public:
bool UpdateSettings(const GPUSettings& old_settings, Error* error) override;
void UpdatePostProcessingSettings(bool force_reload) override;
bool UpdateResolutionScale(Error* error) override;
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 active_line_lsb) override;
void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override;
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override;

@ -1004,8 +1004,7 @@ bool GPUPresenter::ApplyChromaSmoothing()
void GPUPresenter::CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio, bool apply_alignment,
GSVector4i* display_rect, GSVector4i* draw_rect) const
{
const bool integer_scale = (g_gpu_settings.display_scaling == DisplayScalingMode::NearestInteger ||
g_gpu_settings.display_scaling == DisplayScalingMode::BilinearInteger);
const bool integer_scale = g_gpu_settings.IsUsingIntegerDisplayScaling();
const bool show_vram = g_gpu_settings.gpu_show_vram;
const u32 display_width = show_vram ? VRAM_WIDTH : m_display_width;
const u32 display_height = show_vram ? VRAM_HEIGHT : m_display_height;

@ -65,11 +65,6 @@ void GPU_SW::ClearVRAM()
std::memset(g_gpu_clut, 0, sizeof(g_gpu_clut));
}
bool GPU_SW::UpdateResolutionScale(Error* error)
{
return true;
}
void GPU_SW::LoadState(const GPUBackendLoadStateCommand* cmd)
{
std::memcpy(g_vram, cmd->vram_data, sizeof(g_vram));

@ -46,8 +46,6 @@ public:
void ClearVRAM() override;
bool UpdateResolutionScale(Error* error) override;
void LoadState(const GPUBackendLoadStateCommand* cmd) override;
bool AllocateMemorySaveState(System::MemorySaveState& mss, Error* error) override;

@ -1287,13 +1287,6 @@ void GPUThread::DisplayWindowResizedOnThread()
Internal::PresentFrameAndRestoreContext();
Internal::PresentFrameAndRestoreContext();
}
if (g_gpu_settings.gpu_resolution_scale == 0)
{
Error error;
if (!s_state.gpu_backend->UpdateResolutionScale(&error)) [[unlikely]]
ReportFatalErrorAndShutdown(fmt::format("Failed to update resolution scale: {}", error.GetDescription()));
}
}
}

@ -199,6 +199,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
.value_or(DEFAULT_GPU_RENDERER);
gpu_adapter = si.GetStringValue("GPU", "Adapter", "");
gpu_resolution_scale = static_cast<u8>(si.GetUIntValue("GPU", "ResolutionScale", 1u));
gpu_automatic_resolution_scale = (gpu_resolution_scale == 0);
gpu_multisamples = static_cast<u8>(si.GetUIntValue("GPU", "Multisamples", 1u));
gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false);
gpu_disable_shader_cache = si.GetBoolValue("GPU", "DisableShaderCache", false);
@ -972,6 +973,7 @@ void Settings::FixIncompatibleSettings(const SettingsInterface& si, bool display
g_settings.enable_8mb_ram = false;
g_settings.gpu_resolution_scale = 1;
g_settings.gpu_multisamples = 1;
g_settings.gpu_automatic_resolution_scale = false;
g_settings.gpu_per_sample_shading = false;
g_settings.gpu_true_color = false;
g_settings.gpu_scaled_dithering = false;

@ -102,6 +102,7 @@ struct GPUSettings
bool gpu_disable_raster_order_views : 1 = false;
bool gpu_disable_compute_shaders : 1 = false;
bool gpu_disable_compressed_textures : 1 = false;
bool gpu_automatic_resolution_scale : 1 = false;
bool gpu_per_sample_shading : 1 = false;
bool gpu_true_color : 1 = true;
bool gpu_scaled_dithering : 1 = true;
@ -210,6 +211,11 @@ struct GPUSettings
ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); }
ALWAYS_INLINE bool IsUsingAccurateBlending() const { return (gpu_accurate_blending && !gpu_true_color); }
ALWAYS_INLINE bool IsUsingIntegerDisplayScaling() const
{
return (display_scaling == DisplayScalingMode::NearestInteger ||
display_scaling == DisplayScalingMode::BilinearInteger);
}
ALWAYS_INLINE bool UsingPGXPCPUMode() const { return gpu_pgxp_enable && gpu_pgxp_cpu; }
ALWAYS_INLINE bool UsingPGXPDepthBuffer() const { return gpu_pgxp_enable && gpu_pgxp_depth_buffer; }

@ -1219,6 +1219,10 @@ void System::LoadSettings(bool display_osd_messages)
(g_settings.disable_all_enhancements ||
Host::Internal::GetBaseSettingsLayer()->GetBoolValue("Main", "DisableAllEnhancements", false));
// Fix up automatic resolution scale, yuck.
if (g_settings.gpu_automatic_resolution_scale && IsValid())
g_settings.gpu_resolution_scale = g_gpu.CalculateAutomaticResolutionScale();
Settings::UpdateLogConfig(si);
Host::LoadSettings(si, lock);
InputManager::ReloadSources(controller_si, lock);
@ -1934,6 +1938,7 @@ bool System::Initialize(std::unique_ptr<CDImage> disc, DiscRegion disc_region, b
PCDrv::Initialize();
UpdateGTEAspectRatio();
UpdateAutomaticResolutionScale();
UpdateThrottlePeriod();
UpdateMemorySaveStateSettings();
@ -2592,7 +2597,7 @@ void System::ClearMemorySaveStates(bool reallocate_resources, bool recycle_textu
}
// immediately save a rewind state next frame
s_state.rewind_save_counter = (s_state.rewind_save_frequency > 0) ? 0 : -1;
s_state.rewind_save_counter = (s_state.rewind_save_frequency >= 0) ? 0 : -1;
}
void System::FreeMemoryStateStorage(bool release_memory, bool release_textures, bool recycle_textures)
@ -4220,8 +4225,8 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
UpdateRichPresence(booting);
FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial,
s_state.running_game_title, s_state.running_game_hash);
FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
s_state.running_game_hash);
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
s_state.running_game_hash);
@ -4508,7 +4513,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
// NOTE: Must come after the GPU thread settings update, otherwise it allocs the wrong size textures.
const bool use_existing_textures = (g_settings.gpu_resolution_scale == old_settings.gpu_resolution_scale);
ClearMemorySaveStates(true, use_existing_textures);
FreeMemoryStateStorage(false, true, use_existing_textures);
ClearMemorySaveStates(true, true);
if (IsPaused())
{
@ -5823,6 +5829,7 @@ void System::DisplayWindowResized()
return;
UpdateGTEAspectRatio();
UpdateAutomaticResolutionScale();
}
void System::UpdateGTEAspectRatio()
@ -5866,6 +5873,28 @@ void System::UpdateGTEAspectRatio()
GTE::SetAspectRatio(gte_ar, custom_num, custom_denom);
}
void System::UpdateAutomaticResolutionScale()
{
if (!IsValidOrInitializing() || !g_settings.gpu_automatic_resolution_scale)
return;
const u32 new_scale = g_gpu.CalculateAutomaticResolutionScale();
if (g_settings.gpu_resolution_scale == new_scale)
return;
g_settings.gpu_resolution_scale = Truncate8(new_scale);
GPUThread::UpdateSettings(true, false, false);
FreeMemoryStateStorage(false, true, false);
ClearMemorySaveStates(true, false);
if (IsPaused())
{
// resolution change needs display updated
g_gpu.UpdateDisplay(false);
GPUThread::PresentCurrentFrame();
}
}
bool System::ChangeGPUDump(std::string new_path)
{
Error error;

@ -50,6 +50,9 @@ void DisplayWindowResized();
/// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting.
void UpdateGTEAspectRatio();
/// Updates the resolution scale when it is set to automatic.
void UpdateAutomaticResolutionScale();
/// Called on card read/write, handles fast forwarding.
void OnMemoryCardAccessed();

Loading…
Cancel
Save