mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-27 08:05:41 +00:00
System: Add memory-only save states and rewind
This commit is contained in:
parent
6c6fdeb15e
commit
516d685dd0
|
@ -75,7 +75,7 @@ std::tuple<u32, u32> GPU::GetEffectiveDisplayResolution()
|
|||
return std::tie(m_crtc_state.display_vram_width, m_crtc_state.display_vram_height);
|
||||
}
|
||||
|
||||
void GPU::Reset()
|
||||
void GPU::Reset(bool clear_vram)
|
||||
{
|
||||
SoftReset();
|
||||
m_set_texture_disable_mask = false;
|
||||
|
@ -119,12 +119,12 @@ void GPU::SoftReset()
|
|||
UpdateCommandTickEvent();
|
||||
}
|
||||
|
||||
bool GPU::DoState(StateWrapper& sw, bool update_display)
|
||||
bool GPU::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
if (sw.IsReading())
|
||||
{
|
||||
// perform a reset to discard all pending draws/fb state
|
||||
Reset();
|
||||
Reset(host_texture == nullptr);
|
||||
}
|
||||
|
||||
sw.Do(&m_GPUSTAT.bits);
|
||||
|
@ -214,16 +214,27 @@ bool GPU::DoState(StateWrapper& sw, bool update_display)
|
|||
UpdateDMARequest();
|
||||
}
|
||||
|
||||
if (!sw.DoMarker("GPU-VRAM"))
|
||||
return false;
|
||||
if (!host_texture)
|
||||
{
|
||||
if (!sw.DoMarker("GPU-VRAM"))
|
||||
return false;
|
||||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
// Still need a temporary here.
|
||||
HeapArray<u16, VRAM_WIDTH* VRAM_HEIGHT> temp;
|
||||
sw.DoBytes(temp.data(), VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16));
|
||||
UpdateVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT, temp.data(), false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
sw.DoBytes(m_vram_ptr, VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16));
|
||||
}
|
||||
}
|
||||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
// Still need a temporary here.
|
||||
HeapArray<u16, VRAM_WIDTH * VRAM_HEIGHT> temp;
|
||||
sw.DoBytes(temp.data(), VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16));
|
||||
UpdateVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT, temp.data(), false, false);
|
||||
|
||||
UpdateCRTCConfig();
|
||||
if (update_display)
|
||||
UpdateDisplay();
|
||||
|
@ -231,11 +242,6 @@ bool GPU::DoState(StateWrapper& sw, bool update_display)
|
|||
UpdateCRTCTickEvent();
|
||||
UpdateCommandTickEvent();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
sw.DoBytes(m_vram_ptr, VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16));
|
||||
}
|
||||
|
||||
return !sw.HasError();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
class StateWrapper;
|
||||
|
||||
class HostDisplay;
|
||||
class HostDisplayTexture;
|
||||
|
||||
class TimingEvent;
|
||||
class Timers;
|
||||
|
@ -75,8 +76,8 @@ public:
|
|||
virtual bool IsHardwareRenderer() const = 0;
|
||||
|
||||
virtual bool Initialize(HostDisplay* host_display);
|
||||
virtual void Reset();
|
||||
virtual bool DoState(StateWrapper& sw, bool update_display);
|
||||
virtual void Reset(bool clear_vram);
|
||||
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** save_to_texture, bool update_display);
|
||||
|
||||
// Graphics API state reset/restore - call when drawing the UI etc.
|
||||
virtual void ResetGraphicsAPIState();
|
||||
|
|
|
@ -19,7 +19,7 @@ bool GPUBackend::Initialize()
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPUBackend::Reset()
|
||||
void GPUBackend::Reset(bool clear_vram)
|
||||
{
|
||||
Sync();
|
||||
m_drawing_area = {};
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
|
||||
virtual bool Initialize();
|
||||
virtual void UpdateSettings();
|
||||
virtual void Reset();
|
||||
virtual void Reset(bool clear_vram);
|
||||
virtual void Shutdown();
|
||||
|
||||
GPUBackendFillVRAMCommand* NewFillVRAMCommand();
|
||||
|
|
|
@ -91,9 +91,9 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW::Reset()
|
||||
void GPU_HW::Reset(bool clear_vram)
|
||||
{
|
||||
GPU::Reset();
|
||||
GPU::Reset(clear_vram);
|
||||
|
||||
m_batch_current_vertex_ptr = m_batch_start_vertex_ptr;
|
||||
|
||||
|
@ -107,9 +107,9 @@ void GPU_HW::Reset()
|
|||
SetFullVRAMDirtyRectangle();
|
||||
}
|
||||
|
||||
bool GPU_HW::DoState(StateWrapper& sw, bool update_display)
|
||||
bool GPU_HW::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
if (!GPU::DoState(sw, update_display))
|
||||
if (!GPU::DoState(sw, host_texture, update_display))
|
||||
return false;
|
||||
|
||||
// invalidate the whole VRAM read texture when loading state
|
||||
|
|
|
@ -32,8 +32,8 @@ public:
|
|||
virtual bool IsHardwareRenderer() const override;
|
||||
|
||||
virtual bool Initialize(HostDisplay* host_display) override;
|
||||
virtual void Reset() override;
|
||||
virtual bool DoState(StateWrapper& sw, bool update_display) override;
|
||||
virtual void Reset(bool clear_vram) override;
|
||||
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
|
||||
|
||||
void UpdateResolutionScale() override final;
|
||||
std::tuple<u32, u32> GetEffectiveDisplayResolution() override final;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/d3d11/shader_compiler.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/timer.h"
|
||||
#include "gpu_hw_shadergen.h"
|
||||
#include "host_display.h"
|
||||
|
@ -82,11 +83,49 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::Reset()
|
||||
void GPU_HW_D3D11::Reset(bool clear_vram)
|
||||
{
|
||||
GPU_HW::Reset();
|
||||
GPU_HW::Reset(clear_vram);
|
||||
|
||||
ClearFramebuffer();
|
||||
if (clear_vram)
|
||||
ClearFramebuffer();
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
if (host_texture)
|
||||
{
|
||||
const CD3D11_BOX src_box(0, 0, 0, static_cast<LONG>(m_vram_texture.GetWidth()),
|
||||
static_cast<LONG>(m_vram_texture.GetHeight()), 1);
|
||||
ComPtr<ID3D11Resource> resource;
|
||||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
HostDisplayTexture* tex = *host_texture;
|
||||
if (tex->GetWidth() != m_vram_texture.GetWidth() || tex->GetHeight() != m_vram_texture.GetHeight() ||
|
||||
tex->GetSamples() != m_vram_texture.GetSamples())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static_cast<ID3D11ShaderResourceView*>(tex->GetHandle())->GetResource(resource.GetAddressOf());
|
||||
m_context->CopySubresourceRegion(m_vram_texture.GetD3DTexture(), 0, 0, 0, 0, resource.Get(), 0, &src_box);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<HostDisplayTexture> tex =
|
||||
m_host_display->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
|
||||
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false);
|
||||
if (!tex)
|
||||
return false;
|
||||
|
||||
static_cast<ID3D11ShaderResourceView*>(tex->GetHandle())->GetResource(resource.GetAddressOf());
|
||||
m_context->CopySubresourceRegion(resource.Get(), 0, 0, 0, 0, m_vram_texture.GetD3DTexture(), 0, &src_box);
|
||||
*host_texture = tex.release();
|
||||
}
|
||||
}
|
||||
|
||||
return GPU_HW::DoState(sw, host_texture, update_display);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::ResetGraphicsAPIState()
|
||||
|
|
|
@ -21,7 +21,8 @@ public:
|
|||
~GPU_HW_D3D11() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
void Reset(bool clear_vram) override;
|
||||
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
void RestoreGraphicsAPIState() override;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "gpu_hw_opengl.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/timer.h"
|
||||
#include "gpu_hw_shadergen.h"
|
||||
#include "host_display.h"
|
||||
|
@ -95,11 +96,106 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::Reset()
|
||||
void GPU_HW_OpenGL::Reset(bool clear_vram)
|
||||
{
|
||||
GPU_HW::Reset();
|
||||
GPU_HW::Reset(clear_vram);
|
||||
|
||||
ClearFramebuffer();
|
||||
if (clear_vram)
|
||||
ClearFramebuffer();
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
if (host_texture)
|
||||
{
|
||||
if (sw.IsReading())
|
||||
{
|
||||
HostDisplayTexture* tex = *host_texture;
|
||||
if (tex->GetWidth() != m_vram_texture.GetWidth() || tex->GetHeight() != m_vram_texture.GetHeight() ||
|
||||
tex->GetSamples() != m_vram_texture.GetSamples())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CopyFramebufferForState(
|
||||
m_vram_texture.GetGLTarget(), static_cast<GLuint>(reinterpret_cast<uintptr_t>(tex->GetHandle())), 0, 0, 0,
|
||||
m_vram_texture.GetGLId(), m_vram_fbo_id, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<HostDisplayTexture> tex =
|
||||
m_host_display->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
|
||||
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false);
|
||||
if (!tex)
|
||||
return false;
|
||||
|
||||
CopyFramebufferForState(m_vram_texture.GetGLTarget(), m_vram_texture.GetGLId(), m_vram_fbo_id, 0, 0,
|
||||
static_cast<GLuint>(reinterpret_cast<uintptr_t>(tex->GetHandle())), 0, 0, 0,
|
||||
m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
*host_texture = tex.release();
|
||||
}
|
||||
}
|
||||
|
||||
return GPU_HW::DoState(sw, host_texture, update_display);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::CopyFramebufferForState(GLenum target, GLuint src_texture, u32 src_fbo, u32 src_x, u32 src_y,
|
||||
GLuint dst_texture, u32 dst_fbo, u32 dst_x, u32 dst_y, u32 width,
|
||||
u32 height)
|
||||
{
|
||||
if (target != GL_TEXTURE_2D && GLAD_GL_VERSION_4_3)
|
||||
{
|
||||
glCopyImageSubData(src_texture, target, 0, src_x, src_y, 0, dst_texture, target, 0, dst_x, dst_y, 0, width, height,
|
||||
1);
|
||||
}
|
||||
else if (target != GL_TEXTURE_2D && GLAD_GL_EXT_copy_image)
|
||||
{
|
||||
glCopyImageSubDataEXT(src_texture, target, 0, src_x, src_y, 0, dst_texture, target, 0, dst_x, dst_y, 0, width,
|
||||
height, 1);
|
||||
}
|
||||
else if (target != GL_TEXTURE_2D && GLAD_GL_OES_copy_image)
|
||||
{
|
||||
glCopyImageSubDataOES(src_texture, target, 0, src_x, src_y, 0, dst_texture, target, 0, dst_x, dst_y, 0, width,
|
||||
height, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (src_fbo == 0)
|
||||
{
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_state_copy_fbo_id);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, src_texture, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, src_fbo);
|
||||
}
|
||||
|
||||
if (dst_fbo == 0)
|
||||
{
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_state_copy_fbo_id);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, dst_texture, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_fbo);
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(src_x, src_y, src_x + width, src_y + height, dst_x, dst_y, dst_x + width, dst_y + height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
if (src_fbo == 0)
|
||||
{
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
}
|
||||
else if (dst_fbo == 0)
|
||||
{
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_vram_fbo_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::ResetGraphicsAPIState()
|
||||
|
@ -303,7 +399,9 @@ bool GPU_HW_OpenGL::CreateFramebuffer()
|
|||
return false;
|
||||
}
|
||||
|
||||
glGenFramebuffers(1, &m_vram_fbo_id);
|
||||
if (m_vram_fbo_id == 0)
|
||||
glGenFramebuffers(1, &m_vram_fbo_id);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_vram_fbo_id);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_vram_texture.GetGLTarget(),
|
||||
m_vram_texture.GetGLId(), 0);
|
||||
|
@ -320,6 +418,9 @@ bool GPU_HW_OpenGL::CreateFramebuffer()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_state_copy_fbo_id == 0)
|
||||
glGenFramebuffers(1, &m_state_copy_fbo_id);
|
||||
|
||||
SetFullVRAMDirtyRectangle();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ public:
|
|||
~GPU_HW_OpenGL() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
void Reset(bool clear_vram) override;
|
||||
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
void RestoreGraphicsAPIState() override;
|
||||
|
@ -57,6 +58,8 @@ private:
|
|||
void SetCapabilities(HostDisplay* host_display);
|
||||
bool CreateFramebuffer();
|
||||
void ClearFramebuffer();
|
||||
void CopyFramebufferForState(GLenum target, GLuint src_texture, u32 src_fbo, u32 src_x, u32 src_y, GLuint dst_texture,
|
||||
u32 dst_fbo, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
|
||||
bool CreateVertexBuffer();
|
||||
bool CreateUniformBuffer();
|
||||
|
@ -84,6 +87,7 @@ private:
|
|||
GLuint m_vram_fbo_id = 0;
|
||||
GLuint m_vao_id = 0;
|
||||
GLuint m_attributeless_vao_id = 0;
|
||||
GLuint m_state_copy_fbo_id = 0;
|
||||
|
||||
std::unique_ptr<GL::StreamBuffer> m_uniform_stream_buffer;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/scope_guard.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/vulkan/builders.h"
|
||||
#include "common/vulkan/context.h"
|
||||
|
@ -87,12 +88,75 @@ bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::Reset()
|
||||
void GPU_HW_Vulkan::Reset(bool clear_vram)
|
||||
{
|
||||
GPU_HW::Reset();
|
||||
GPU_HW::Reset(clear_vram);
|
||||
|
||||
EndRenderPass();
|
||||
ClearFramebuffer();
|
||||
|
||||
if (clear_vram)
|
||||
ClearFramebuffer();
|
||||
}
|
||||
|
||||
bool GPU_HW_Vulkan::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
if (host_texture)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
const VkImageCopy ic{{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
|
||||
{0, 0, 0},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
|
||||
{0, 0, 0},
|
||||
{m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1u}};
|
||||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
Vulkan::Texture* tex = static_cast<Vulkan::Texture*>((*host_texture)->GetHandle());
|
||||
if (tex->GetWidth() != m_vram_texture.GetWidth() || tex->GetHeight() != m_vram_texture.GetHeight() ||
|
||||
tex->GetSamples() != m_vram_texture.GetSamples())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VkCommandBuffer buf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
const VkImageLayout old_tex_layout = tex->GetLayout();
|
||||
const VkImageLayout old_vram_layout = m_vram_texture.GetLayout();
|
||||
tex->TransitionToLayout(buf, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
m_vram_texture.TransitionToLayout(buf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdCopyImage(g_vulkan_context->GetCurrentCommandBuffer(), tex->GetImage(), tex->GetLayout(),
|
||||
m_vram_texture.GetImage(), m_vram_texture.GetLayout(), 1, &ic);
|
||||
m_vram_texture.TransitionToLayout(buf, old_vram_layout);
|
||||
tex->TransitionToLayout(buf, old_tex_layout);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<HostDisplayTexture> htex =
|
||||
m_host_display->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
|
||||
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false);
|
||||
if (!htex)
|
||||
return false;
|
||||
|
||||
Vulkan::Texture* tex = static_cast<Vulkan::Texture*>(htex->GetHandle());
|
||||
if (tex->GetWidth() != m_vram_texture.GetWidth() || tex->GetHeight() != m_vram_texture.GetHeight() ||
|
||||
tex->GetSamples() != m_vram_texture.GetSamples())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VkCommandBuffer buf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
const VkImageLayout old_vram_layout = m_vram_texture.GetLayout();
|
||||
tex->TransitionToLayout(buf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
m_vram_texture.TransitionToLayout(buf, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
vkCmdCopyImage(g_vulkan_context->GetCurrentCommandBuffer(), m_vram_texture.GetImage(), m_vram_texture.GetLayout(),
|
||||
tex->GetImage(), tex->GetLayout(), 1, &ic);
|
||||
m_vram_texture.TransitionToLayout(buf, old_vram_layout);
|
||||
tex->TransitionToLayout(buf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
*host_texture = htex.release();
|
||||
}
|
||||
}
|
||||
|
||||
return GPU_HW::DoState(sw, host_texture, update_display);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::ResetGraphicsAPIState()
|
||||
|
|
|
@ -16,7 +16,8 @@ public:
|
|||
~GPU_HW_Vulkan() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
void Reset(bool clear_vram) override;
|
||||
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
void RestoreGraphicsAPIState() override;
|
||||
|
|
|
@ -75,11 +75,11 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPU_SW::Reset()
|
||||
void GPU_SW::Reset(bool clear_vram)
|
||||
{
|
||||
GPU::Reset();
|
||||
GPU::Reset(clear_vram);
|
||||
|
||||
m_backend.Reset();
|
||||
m_backend.Reset(clear_vram);
|
||||
}
|
||||
|
||||
void GPU_SW::UpdateSettings()
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
bool IsHardwareRenderer() const override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
void Reset(bool clear_vram) override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -20,11 +20,12 @@ bool GPU_SW_Backend::Initialize()
|
|||
return GPUBackend::Initialize();
|
||||
}
|
||||
|
||||
void GPU_SW_Backend::Reset()
|
||||
void GPU_SW_Backend::Reset(bool clear_vram)
|
||||
{
|
||||
GPUBackend::Reset();
|
||||
GPUBackend::Reset(clear_vram);
|
||||
|
||||
m_vram.fill(0);
|
||||
if (clear_vram)
|
||||
m_vram.fill(0);
|
||||
}
|
||||
|
||||
void GPU_SW_Backend::DrawPolygon(const GPUBackendDrawPolygonCommand* cmd)
|
||||
|
|
|
@ -11,7 +11,7 @@ public:
|
|||
~GPU_SW_Backend() override;
|
||||
|
||||
bool Initialize() override;
|
||||
void Reset() override;
|
||||
void Reset(bool clear_vram) override;
|
||||
|
||||
ALWAYS_INLINE_RELEASE u16 GetPixel(const u32 x, const u32 y) const { return m_vram[VRAM_WIDTH * y + x]; }
|
||||
ALWAYS_INLINE_RELEASE const u16* GetPixelPtr(const u32 x, const u32 y) const { return &m_vram[VRAM_WIDTH * y + x]; }
|
||||
|
|
|
@ -488,6 +488,9 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("Main", "LoadDevicesFromSaveStates", false);
|
||||
si.SetBoolValue("Main", "ApplyGameSettings", true);
|
||||
si.SetBoolValue("Main", "DisableAllEnhancements", false);
|
||||
si.SetBoolValue("Main", "RewindEnable", false);
|
||||
si.SetFloatValue("Main", "RewindFrequency", 10.0f);
|
||||
si.SetIntValue("Main", "RewindSaveSlots", 10);
|
||||
|
||||
si.SetStringValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE));
|
||||
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", false);
|
||||
|
@ -655,6 +658,13 @@ void HostInterface::FixIncompatibleSettings(bool display_osd_messages)
|
|||
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
|
||||
}
|
||||
#endif
|
||||
|
||||
// rewinding causes issues with mmap fastmem, so just use LUT
|
||||
if (g_settings.rewind_enable && g_settings.IsUsingFastmem() && g_settings.cpu_fastmem_mode == CPUFastmemMode::MMap)
|
||||
{
|
||||
Log_WarningPrintf("Disabling mmap fastmem due to rewind being enabled");
|
||||
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
|
||||
}
|
||||
}
|
||||
|
||||
void HostInterface::SaveSettings(SettingsInterface& si)
|
||||
|
@ -676,6 +686,8 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
|
||||
if (System::IsValid())
|
||||
{
|
||||
System::ClearMemorySaveStates();
|
||||
|
||||
if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
|
||||
(g_settings.cpu_overclock_active &&
|
||||
(g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator ||
|
||||
|
@ -755,7 +767,8 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.display_active_start_offset != old_settings.display_active_start_offset ||
|
||||
g_settings.display_active_end_offset != old_settings.display_active_end_offset ||
|
||||
g_settings.display_line_start_offset != old_settings.display_line_start_offset ||
|
||||
g_settings.display_line_end_offset != old_settings.display_line_end_offset)
|
||||
g_settings.display_line_end_offset != old_settings.display_line_end_offset ||
|
||||
g_settings.rewind_enable != old_settings.rewind_enable)
|
||||
{
|
||||
if (g_settings.IsUsingCodeCache())
|
||||
CPU::CodeCache::Reinitialize();
|
||||
|
@ -793,6 +806,13 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
System::UpdateMemoryCards();
|
||||
}
|
||||
|
||||
if (g_settings.rewind_enable != old_settings.rewind_enable ||
|
||||
g_settings.rewind_save_frequency != old_settings.rewind_save_frequency ||
|
||||
g_settings.rewind_save_slots != old_settings.rewind_save_slots)
|
||||
{
|
||||
System::UpdateMemorySaveStateSettings();
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.enable_vram_write_replacements !=
|
||||
old_settings.texture_replacements.enable_vram_write_replacements ||
|
||||
g_settings.texture_replacements.preload_textures != old_settings.texture_replacements.preload_textures)
|
||||
|
@ -999,6 +1019,7 @@ void HostInterface::ModifyResolutionScale(s32 increment)
|
|||
g_gpu->RestoreGraphicsAPIState();
|
||||
g_gpu->UpdateSettings();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
System::ClearMemorySaveStates();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,9 @@ void Settings::Load(SettingsInterface& si)
|
|||
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
|
||||
auto_load_cheats = si.GetBoolValue("Main", "AutoLoadCheats", false);
|
||||
disable_all_enhancements = si.GetBoolValue("Main", "DisableAllEnhancements", false);
|
||||
rewind_enable = si.GetBoolValue("Main", "RewindEnable", false);
|
||||
rewind_save_frequency = si.GetFloatValue("Main", "RewindFrequency", 10.0f);
|
||||
rewind_save_slots = static_cast<u32>(si.GetIntValue("Main", "RewindSaveSlots", 10));
|
||||
|
||||
cpu_execution_mode =
|
||||
ParseCPUExecutionMode(
|
||||
|
@ -295,6 +298,9 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
|
||||
si.SetBoolValue("Main", "AutoLoadCheats", auto_load_cheats);
|
||||
si.SetBoolValue("Main", "DisableAllEnhancements", disable_all_enhancements);
|
||||
si.SetBoolValue("Main", "RewindEnable", rewind_enable);
|
||||
si.SetFloatValue("Main", "RewindFrequency", rewind_save_frequency);
|
||||
si.SetIntValue("Main", "RewindSaveSlots", rewind_save_slots);
|
||||
|
||||
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
||||
si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable);
|
||||
|
|
|
@ -94,6 +94,10 @@ struct Settings
|
|||
bool auto_load_cheats = false;
|
||||
bool disable_all_enhancements = false;
|
||||
|
||||
bool rewind_enable = false;
|
||||
float rewind_save_frequency = 10.0f;
|
||||
u32 rewind_save_slots = 10;
|
||||
|
||||
GPURenderer gpu_renderer = GPURenderer::Software;
|
||||
std::string gpu_adapter;
|
||||
std::string display_post_process_chain;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timestamp.h"
|
||||
#include "controller.h"
|
||||
#include "cpu_code_cache.h"
|
||||
#include "cpu_core.h"
|
||||
|
@ -30,7 +31,10 @@
|
|||
#include "texture_replacements.h"
|
||||
#include "timers.h"
|
||||
#include <cctype>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
Log_SetChannel(System);
|
||||
|
@ -51,6 +55,12 @@ SystemBootParameters::~SystemBootParameters() = default;
|
|||
|
||||
namespace System {
|
||||
|
||||
struct MemorySaveState
|
||||
{
|
||||
std::unique_ptr<HostDisplayTexture> vram_texture;
|
||||
std::unique_ptr<GrowableMemoryByteStream> state_stream;
|
||||
};
|
||||
|
||||
static bool LoadEXE(const char* filename);
|
||||
static bool SetExpansionROM(const char* filename);
|
||||
|
||||
|
@ -58,7 +68,10 @@ static bool SetExpansionROM(const char* filename);
|
|||
static std::unique_ptr<CDImage> OpenCDImage(const char* path, bool force_preload);
|
||||
|
||||
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
|
||||
static bool DoState(StateWrapper& sw, bool update_display);
|
||||
static bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display);
|
||||
static void DoRunFrame();
|
||||
static void DoRewind();
|
||||
static void DoMemorySaveStates();
|
||||
static bool CreateGPU(GPURenderer renderer);
|
||||
|
||||
static bool Initialize(bool force_software_renderer);
|
||||
|
@ -104,6 +117,15 @@ static std::string s_media_playlist_filename;
|
|||
|
||||
static std::unique_ptr<CheatList> s_cheat_list;
|
||||
|
||||
static bool s_memory_saves_enabled = false;
|
||||
|
||||
static std::deque<MemorySaveState> s_rewind_states;
|
||||
static s32 s_rewind_load_frequency = -1;
|
||||
static s32 s_rewind_load_counter = -1;
|
||||
static s32 s_rewind_save_frequency = -1;
|
||||
static s32 s_rewind_save_counter = -1;
|
||||
static bool s_rewinding_first_save = false;
|
||||
|
||||
State GetState()
|
||||
{
|
||||
return s_state;
|
||||
|
@ -521,12 +543,13 @@ std::optional<DiscRegion> GetRegionForPath(const char* image_path)
|
|||
|
||||
bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/)
|
||||
{
|
||||
ClearMemorySaveStates();
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
|
||||
// save current state
|
||||
std::unique_ptr<ByteStream> state_stream = ByteStream_CreateGrowableMemoryStream();
|
||||
StateWrapper sw(state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
||||
const bool state_valid = g_gpu->DoState(sw, false) && TimingEvents::DoState(sw);
|
||||
const bool state_valid = g_gpu->DoState(sw, nullptr, false) && TimingEvents::DoState(sw);
|
||||
if (!state_valid)
|
||||
Log_ErrorPrintf("Failed to save old GPU state when switching renderers");
|
||||
|
||||
|
@ -548,7 +571,7 @@ bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/)
|
|||
state_stream->SeekAbsolute(0);
|
||||
sw.SetMode(StateWrapper::Mode::Read);
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
g_gpu->DoState(sw, update_display);
|
||||
g_gpu->DoState(sw, nullptr, update_display);
|
||||
TimingEvents::DoState(sw);
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
}
|
||||
|
@ -802,6 +825,7 @@ bool Initialize(bool force_software_renderer)
|
|||
}
|
||||
|
||||
UpdateThrottlePeriod();
|
||||
UpdateMemorySaveStateSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -810,6 +834,8 @@ void Shutdown()
|
|||
if (s_state == State::Shutdown)
|
||||
return;
|
||||
|
||||
ClearMemorySaveStates();
|
||||
|
||||
g_texture_replacements.Shutdown();
|
||||
|
||||
g_sio.Shutdown();
|
||||
|
@ -868,11 +894,11 @@ bool CreateGPU(GPURenderer renderer)
|
|||
}
|
||||
|
||||
// we put this here rather than in Initialize() because of the virtual calls
|
||||
g_gpu->Reset();
|
||||
g_gpu->Reset(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DoState(StateWrapper& sw, bool update_display)
|
||||
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
if (!sw.DoMarker("System"))
|
||||
return false;
|
||||
|
@ -897,7 +923,7 @@ bool DoState(StateWrapper& sw, bool update_display)
|
|||
return false;
|
||||
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
const bool gpu_result = sw.DoMarker("GPU") && g_gpu->DoState(sw, update_display);
|
||||
const bool gpu_result = sw.DoMarker("GPU") && g_gpu->DoState(sw, host_texture, update_display);
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
if (!gpu_result)
|
||||
return false;
|
||||
|
@ -963,7 +989,7 @@ void Reset()
|
|||
Bus::Reset();
|
||||
g_dma.Reset();
|
||||
g_interrupt_controller.Reset();
|
||||
g_gpu->Reset();
|
||||
g_gpu->Reset(true);
|
||||
g_cdrom.Reset();
|
||||
g_pad.Reset();
|
||||
g_timers.Reset();
|
||||
|
@ -1076,6 +1102,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di
|
|||
}
|
||||
|
||||
UpdateRunningGame(media_filename.c_str(), media.get());
|
||||
ClearMemorySaveStates();
|
||||
|
||||
if (s_state == State::Starting)
|
||||
{
|
||||
|
@ -1117,7 +1144,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di
|
|||
return false;
|
||||
|
||||
StateWrapper sw(state, StateWrapper::Mode::Read, header.version);
|
||||
if (!DoState(sw, update_display))
|
||||
if (!DoState(sw, nullptr, update_display))
|
||||
return false;
|
||||
|
||||
if (s_state == State::Starting)
|
||||
|
@ -1184,7 +1211,7 @@ bool SaveState(ByteStream* state, u32 screenshot_size /* = 128 */)
|
|||
g_gpu->RestoreGraphicsAPIState();
|
||||
|
||||
StateWrapper sw(state, StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
||||
const bool result = DoState(sw, false);
|
||||
const bool result = DoState(sw, nullptr, false);
|
||||
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
|
||||
|
@ -1224,10 +1251,8 @@ void SingleStepCPU()
|
|||
g_gpu->ResetGraphicsAPIState();
|
||||
}
|
||||
|
||||
void RunFrame()
|
||||
void DoRunFrame()
|
||||
{
|
||||
s_frame_timer.Reset();
|
||||
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
|
||||
if (CPU::g_state.use_debug_dispatcher)
|
||||
|
@ -1266,6 +1291,22 @@ void RunFrame()
|
|||
g_gpu->ResetGraphicsAPIState();
|
||||
}
|
||||
|
||||
void RunFrame()
|
||||
{
|
||||
s_frame_timer.Reset();
|
||||
|
||||
if (s_rewind_load_counter >= 0)
|
||||
{
|
||||
DoRewind();
|
||||
return;
|
||||
}
|
||||
|
||||
DoRunFrame();
|
||||
|
||||
if (s_memory_saves_enabled)
|
||||
DoMemorySaveStates();
|
||||
}
|
||||
|
||||
float GetTargetSpeed()
|
||||
{
|
||||
return s_target_speed;
|
||||
|
@ -1739,12 +1780,14 @@ bool InsertMedia(const char* path)
|
|||
if (g_settings.IsUsingCodeCache())
|
||||
CPU::CodeCache::Reinitialize();
|
||||
|
||||
ClearMemorySaveStates();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveMedia()
|
||||
{
|
||||
g_cdrom.RemoveMedia();
|
||||
ClearMemorySaveStates();
|
||||
}
|
||||
|
||||
void UpdateRunningGame(const char* path, CDImage* image)
|
||||
|
@ -1907,4 +1950,152 @@ void SetCheatList(std::unique_ptr<CheatList> cheats)
|
|||
s_cheat_list = std::move(cheats);
|
||||
}
|
||||
|
||||
void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage)
|
||||
{
|
||||
*ram_usage = MAX_SAVE_STATE_SIZE * static_cast<u64>(num_saves);
|
||||
*vram_usage = (VRAM_WIDTH * VRAM_HEIGHT * 4) * static_cast<u64>(std::max(g_settings.gpu_resolution_scale, 1u)) *
|
||||
static_cast<u64>(num_saves);
|
||||
}
|
||||
|
||||
void ClearMemorySaveStates()
|
||||
{
|
||||
s_rewind_states.clear();
|
||||
}
|
||||
|
||||
void UpdateMemorySaveStateSettings()
|
||||
{
|
||||
ClearMemorySaveStates();
|
||||
|
||||
s_memory_saves_enabled = g_settings.rewind_enable;
|
||||
|
||||
if (g_settings.rewind_enable)
|
||||
{
|
||||
s_rewind_save_frequency = static_cast<s32>(std::ceil(g_settings.rewind_save_frequency * s_throttle_frequency));
|
||||
s_rewind_save_counter = 0;
|
||||
|
||||
u64 ram_usage, vram_usage;
|
||||
CalculateRewindMemoryUsage(g_settings.rewind_save_slots, &ram_usage, &vram_usage);
|
||||
Log_InfoPrintf(
|
||||
"Rewind is enabled, saving every %d frames, with %u slots and %" PRIu64 "MB RAM and %" PRIu64 "MB VRAM usage",
|
||||
std::max(s_rewind_save_frequency, 1), g_settings.rewind_save_slots, ram_usage / 1048576, vram_usage / 1048576);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_rewind_save_frequency = -1;
|
||||
s_rewind_save_counter = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool SaveRewindState()
|
||||
{
|
||||
Common::Timer save_timer;
|
||||
|
||||
const u32 save_slots = g_settings.rewind_save_slots;
|
||||
while (s_rewind_states.size() >= save_slots)
|
||||
s_rewind_states.pop_front();
|
||||
|
||||
MemorySaveState mss;
|
||||
mss.state_stream = std::make_unique<GrowableMemoryByteStream>(nullptr, MAX_SAVE_STATE_SIZE);
|
||||
|
||||
HostDisplayTexture* host_texture = nullptr;
|
||||
StateWrapper sw(mss.state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
||||
if (!DoState(sw, &host_texture, false))
|
||||
{
|
||||
Log_ErrorPrint("Failed to create rewind state.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mss.vram_texture.reset(host_texture);
|
||||
s_rewind_states.push_back(std::move(mss));
|
||||
|
||||
Log_DevPrintf("Saved rewind state (%u bytes, took %.4f ms)", s_rewind_states.back().state_stream->GetSize(),
|
||||
save_timer.GetTimeMilliseconds());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */)
|
||||
{
|
||||
while (skip_saves > 0 && !s_rewind_states.empty())
|
||||
{
|
||||
s_rewind_states.pop_back();
|
||||
skip_saves--;
|
||||
}
|
||||
|
||||
if (s_rewind_states.empty())
|
||||
return false;
|
||||
|
||||
Common::Timer load_timer;
|
||||
|
||||
const MemorySaveState& mss = s_rewind_states.back();
|
||||
mss.state_stream->SeekAbsolute(0);
|
||||
|
||||
StateWrapper sw(mss.state_stream.get(), StateWrapper::Mode::Read, SAVE_STATE_VERSION);
|
||||
HostDisplayTexture* host_texture = mss.vram_texture.get();
|
||||
if (!DoState(sw, &host_texture, true))
|
||||
{
|
||||
g_host_interface->ReportError("Failed to load rewind state from memory, resetting.");
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (consume_state)
|
||||
s_rewind_states.pop_back();
|
||||
|
||||
Log_DevPrintf("Rewind load took %.4f ms", load_timer.GetTimeMilliseconds());
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetRewinding(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
// Try to rewind at the replay speed, or one per second maximum.
|
||||
const float load_frequency = std::min(g_settings.rewind_save_frequency, 1.0f);
|
||||
s_rewind_load_frequency = static_cast<s32>(std::ceil(load_frequency * s_throttle_frequency));
|
||||
s_rewind_load_counter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_rewind_load_frequency = -1;
|
||||
s_rewind_load_counter = -1;
|
||||
}
|
||||
|
||||
s_rewinding_first_save = true;
|
||||
}
|
||||
|
||||
void DoRewind()
|
||||
{
|
||||
s_frame_timer.Reset();
|
||||
|
||||
if (s_rewind_load_counter == 0)
|
||||
{
|
||||
const u32 skip_saves = BoolToUInt32(!s_rewinding_first_save);
|
||||
s_rewinding_first_save = false;
|
||||
LoadRewindState(skip_saves, false);
|
||||
ResetPerformanceCounters();
|
||||
s_rewind_load_counter = s_rewind_load_frequency;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_rewind_load_counter--;
|
||||
}
|
||||
}
|
||||
|
||||
void DoMemorySaveStates()
|
||||
{
|
||||
if (s_rewind_save_counter >= 0)
|
||||
{
|
||||
if (s_rewind_save_counter == 0)
|
||||
{
|
||||
SaveRewindState();
|
||||
s_rewind_save_counter = s_rewind_save_frequency;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_rewind_save_counter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace System
|
|
@ -228,4 +228,14 @@ void ApplyCheatCode(const CheatCode& code);
|
|||
/// Sets or clears the provided cheat list, applying every frame.
|
||||
void SetCheatList(std::unique_ptr<CheatList> cheats);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Memory Save States (Rewind and Runahead)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage);
|
||||
void ClearMemorySaveStates();
|
||||
void UpdateMemorySaveStateSettings();
|
||||
bool SaveRewindState();
|
||||
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
|
||||
void SetRewinding(bool enabled);
|
||||
|
||||
} // namespace System
|
||||
|
|
|
@ -1549,7 +1549,7 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
|||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("ToggleCheats"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Cheats")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
DoToggleCheats();
|
||||
});
|
||||
|
||||
|
@ -1584,7 +1584,7 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
|||
#else
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("TogglePatchCodes"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Patch Codes")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
DoToggleCheats();
|
||||
});
|
||||
#endif
|
||||
|
@ -1603,25 +1603,39 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
|||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("FrameStep"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Frame Step")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
DoFrameStep();
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("Rewind"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Rewind")), [this](bool pressed) {
|
||||
if (System::IsValid())
|
||||
{
|
||||
AddOSDMessage(pressed ? TranslateStdString("OSDMessage", "Rewinding...") :
|
||||
TranslateStdString("OSDMessage", "Stopped rewinding."),
|
||||
5.0f);
|
||||
System::SetRewinding(pressed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CommonHostInterface::RegisterGraphicsHotkeys()
|
||||
{
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ToggleSoftwareRendering"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Software Rendering")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
ToggleSoftwareRendering();
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePGXP"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle PGXP")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
{
|
||||
g_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable;
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
g_gpu->UpdateSettings();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
System::ClearMemorySaveStates();
|
||||
AddOSDMessage(g_settings.gpu_pgxp_enable ?
|
||||
TranslateStdString("OSDMessage", "PGXP is now enabled.") :
|
||||
TranslateStdString("OSDMessage", "PGXP is now disabled"),
|
||||
|
@ -1640,13 +1654,16 @@ void CommonHostInterface::RegisterGraphicsHotkeys()
|
|||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePGXPDepth"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle PGXP Depth Buffer")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
{
|
||||
g_settings.gpu_pgxp_depth_buffer = !g_settings.gpu_pgxp_depth_buffer;
|
||||
if (!g_settings.gpu_pgxp_enable)
|
||||
return;
|
||||
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
g_gpu->UpdateSettings();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
System::ClearMemorySaveStates();
|
||||
AddOSDMessage(g_settings.gpu_pgxp_depth_buffer ?
|
||||
TranslateStdString("OSDMessage", "PGXP Depth Buffer is now enabled.") :
|
||||
TranslateStdString("OSDMessage", "PGXP Depth Buffer is now disabled."),
|
||||
|
@ -1656,31 +1673,31 @@ void CommonHostInterface::RegisterGraphicsHotkeys()
|
|||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("IncreaseResolutionScale"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Increase Resolution Scale")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
ModifyResolutionScale(1);
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("DecreaseResolutionScale"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Decrease Resolution Scale")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
ModifyResolutionScale(-1);
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePostProcessing"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Post-Processing")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
TogglePostProcessing();
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ReloadPostProcessingShaders"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Reload Post Processing Shaders")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
ReloadPostProcessingShaders();
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ReloadTextureReplacements"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Reload Texture Replacements")), [this](bool pressed) {
|
||||
if (pressed)
|
||||
if (pressed && System::IsValid())
|
||||
{
|
||||
AddOSDMessage(TranslateStdString("OSDMessage", "Texture replacements reloaded."), 10.0f);
|
||||
g_texture_replacements.Reload();
|
||||
|
|
Loading…
Reference in a new issue