mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-30 09:35:40 +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);
|
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();
|
SoftReset();
|
||||||
m_set_texture_disable_mask = false;
|
m_set_texture_disable_mask = false;
|
||||||
|
@ -119,12 +119,12 @@ void GPU::SoftReset()
|
||||||
UpdateCommandTickEvent();
|
UpdateCommandTickEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::DoState(StateWrapper& sw, bool update_display)
|
bool GPU::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||||
{
|
{
|
||||||
if (sw.IsReading())
|
if (sw.IsReading())
|
||||||
{
|
{
|
||||||
// perform a reset to discard all pending draws/fb state
|
// perform a reset to discard all pending draws/fb state
|
||||||
Reset();
|
Reset(host_texture == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Do(&m_GPUSTAT.bits);
|
sw.Do(&m_GPUSTAT.bits);
|
||||||
|
@ -214,16 +214,27 @@ bool GPU::DoState(StateWrapper& sw, bool update_display)
|
||||||
UpdateDMARequest();
|
UpdateDMARequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sw.DoMarker("GPU-VRAM"))
|
if (!host_texture)
|
||||||
return false;
|
{
|
||||||
|
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())
|
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();
|
UpdateCRTCConfig();
|
||||||
if (update_display)
|
if (update_display)
|
||||||
UpdateDisplay();
|
UpdateDisplay();
|
||||||
|
@ -231,11 +242,6 @@ bool GPU::DoState(StateWrapper& sw, bool update_display)
|
||||||
UpdateCRTCTickEvent();
|
UpdateCRTCTickEvent();
|
||||||
UpdateCommandTickEvent();
|
UpdateCommandTickEvent();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
|
||||||
sw.DoBytes(m_vram_ptr, VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16));
|
|
||||||
}
|
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
class HostDisplay;
|
class HostDisplay;
|
||||||
|
class HostDisplayTexture;
|
||||||
|
|
||||||
class TimingEvent;
|
class TimingEvent;
|
||||||
class Timers;
|
class Timers;
|
||||||
|
@ -75,8 +76,8 @@ public:
|
||||||
virtual bool IsHardwareRenderer() const = 0;
|
virtual bool IsHardwareRenderer() const = 0;
|
||||||
|
|
||||||
virtual bool Initialize(HostDisplay* host_display);
|
virtual bool Initialize(HostDisplay* host_display);
|
||||||
virtual void Reset();
|
virtual void Reset(bool clear_vram);
|
||||||
virtual bool DoState(StateWrapper& sw, bool update_display);
|
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** save_to_texture, bool update_display);
|
||||||
|
|
||||||
// Graphics API state reset/restore - call when drawing the UI etc.
|
// Graphics API state reset/restore - call when drawing the UI etc.
|
||||||
virtual void ResetGraphicsAPIState();
|
virtual void ResetGraphicsAPIState();
|
||||||
|
|
|
@ -19,7 +19,7 @@ bool GPUBackend::Initialize()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUBackend::Reset()
|
void GPUBackend::Reset(bool clear_vram)
|
||||||
{
|
{
|
||||||
Sync();
|
Sync();
|
||||||
m_drawing_area = {};
|
m_drawing_area = {};
|
||||||
|
|
|
@ -23,7 +23,7 @@ public:
|
||||||
|
|
||||||
virtual bool Initialize();
|
virtual bool Initialize();
|
||||||
virtual void UpdateSettings();
|
virtual void UpdateSettings();
|
||||||
virtual void Reset();
|
virtual void Reset(bool clear_vram);
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
|
||||||
GPUBackendFillVRAMCommand* NewFillVRAMCommand();
|
GPUBackendFillVRAMCommand* NewFillVRAMCommand();
|
||||||
|
|
|
@ -91,9 +91,9 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
|
||||||
return true;
|
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;
|
m_batch_current_vertex_ptr = m_batch_start_vertex_ptr;
|
||||||
|
|
||||||
|
@ -107,9 +107,9 @@ void GPU_HW::Reset()
|
||||||
SetFullVRAMDirtyRectangle();
|
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;
|
return false;
|
||||||
|
|
||||||
// invalidate the whole VRAM read texture when loading state
|
// invalidate the whole VRAM read texture when loading state
|
||||||
|
|
|
@ -32,8 +32,8 @@ public:
|
||||||
virtual bool IsHardwareRenderer() const override;
|
virtual bool IsHardwareRenderer() const override;
|
||||||
|
|
||||||
virtual bool Initialize(HostDisplay* host_display) override;
|
virtual bool Initialize(HostDisplay* host_display) override;
|
||||||
virtual void Reset() override;
|
virtual void Reset(bool clear_vram) override;
|
||||||
virtual bool DoState(StateWrapper& sw, bool update_display) override;
|
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
|
||||||
|
|
||||||
void UpdateResolutionScale() override final;
|
void UpdateResolutionScale() override final;
|
||||||
std::tuple<u32, u32> GetEffectiveDisplayResolution() override final;
|
std::tuple<u32, u32> GetEffectiveDisplayResolution() override final;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/d3d11/shader_compiler.h"
|
#include "common/d3d11/shader_compiler.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
#include "common/state_wrapper.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "gpu_hw_shadergen.h"
|
#include "gpu_hw_shadergen.h"
|
||||||
#include "host_display.h"
|
#include "host_display.h"
|
||||||
|
@ -82,11 +83,49 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
|
||||||
return true;
|
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()
|
void GPU_HW_D3D11::ResetGraphicsAPIState()
|
||||||
|
|
|
@ -21,7 +21,8 @@ public:
|
||||||
~GPU_HW_D3D11() override;
|
~GPU_HW_D3D11() override;
|
||||||
|
|
||||||
bool Initialize(HostDisplay* host_display) 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 ResetGraphicsAPIState() override;
|
||||||
void RestoreGraphicsAPIState() override;
|
void RestoreGraphicsAPIState() override;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "gpu_hw_opengl.h"
|
#include "gpu_hw_opengl.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
#include "common/state_wrapper.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "gpu_hw_shadergen.h"
|
#include "gpu_hw_shadergen.h"
|
||||||
#include "host_display.h"
|
#include "host_display.h"
|
||||||
|
@ -95,11 +96,106 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
|
||||||
return true;
|
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()
|
void GPU_HW_OpenGL::ResetGraphicsAPIState()
|
||||||
|
@ -303,7 +399,9 @@ bool GPU_HW_OpenGL::CreateFramebuffer()
|
||||||
return false;
|
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);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_vram_fbo_id);
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_vram_texture.GetGLTarget(),
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_vram_texture.GetGLTarget(),
|
||||||
m_vram_texture.GetGLId(), 0);
|
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();
|
SetFullVRAMDirtyRectangle();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ public:
|
||||||
~GPU_HW_OpenGL() override;
|
~GPU_HW_OpenGL() override;
|
||||||
|
|
||||||
bool Initialize(HostDisplay* host_display) 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 ResetGraphicsAPIState() override;
|
||||||
void RestoreGraphicsAPIState() override;
|
void RestoreGraphicsAPIState() override;
|
||||||
|
@ -57,6 +58,8 @@ private:
|
||||||
void SetCapabilities(HostDisplay* host_display);
|
void SetCapabilities(HostDisplay* host_display);
|
||||||
bool CreateFramebuffer();
|
bool CreateFramebuffer();
|
||||||
void ClearFramebuffer();
|
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 CreateVertexBuffer();
|
||||||
bool CreateUniformBuffer();
|
bool CreateUniformBuffer();
|
||||||
|
@ -84,6 +87,7 @@ private:
|
||||||
GLuint m_vram_fbo_id = 0;
|
GLuint m_vram_fbo_id = 0;
|
||||||
GLuint m_vao_id = 0;
|
GLuint m_vao_id = 0;
|
||||||
GLuint m_attributeless_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;
|
std::unique_ptr<GL::StreamBuffer> m_uniform_stream_buffer;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/scope_guard.h"
|
#include "common/scope_guard.h"
|
||||||
|
#include "common/state_wrapper.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "common/vulkan/builders.h"
|
#include "common/vulkan/builders.h"
|
||||||
#include "common/vulkan/context.h"
|
#include "common/vulkan/context.h"
|
||||||
|
@ -87,12 +88,75 @@ bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU_HW_Vulkan::Reset()
|
void GPU_HW_Vulkan::Reset(bool clear_vram)
|
||||||
{
|
{
|
||||||
GPU_HW::Reset();
|
GPU_HW::Reset(clear_vram);
|
||||||
|
|
||||||
EndRenderPass();
|
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()
|
void GPU_HW_Vulkan::ResetGraphicsAPIState()
|
||||||
|
|
|
@ -16,7 +16,8 @@ public:
|
||||||
~GPU_HW_Vulkan() override;
|
~GPU_HW_Vulkan() override;
|
||||||
|
|
||||||
bool Initialize(HostDisplay* host_display) 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 ResetGraphicsAPIState() override;
|
||||||
void RestoreGraphicsAPIState() override;
|
void RestoreGraphicsAPIState() override;
|
||||||
|
|
|
@ -75,11 +75,11 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
|
||||||
return true;
|
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()
|
void GPU_SW::UpdateSettings()
|
||||||
|
|
|
@ -18,7 +18,7 @@ public:
|
||||||
bool IsHardwareRenderer() const override;
|
bool IsHardwareRenderer() const override;
|
||||||
|
|
||||||
bool Initialize(HostDisplay* host_display) override;
|
bool Initialize(HostDisplay* host_display) override;
|
||||||
void Reset() override;
|
void Reset(bool clear_vram) override;
|
||||||
void UpdateSettings() override;
|
void UpdateSettings() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -20,11 +20,12 @@ bool GPU_SW_Backend::Initialize()
|
||||||
return GPUBackend::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)
|
void GPU_SW_Backend::DrawPolygon(const GPUBackendDrawPolygonCommand* cmd)
|
||||||
|
|
|
@ -11,7 +11,7 @@ public:
|
||||||
~GPU_SW_Backend() override;
|
~GPU_SW_Backend() override;
|
||||||
|
|
||||||
bool Initialize() 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 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]; }
|
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", "LoadDevicesFromSaveStates", false);
|
||||||
si.SetBoolValue("Main", "ApplyGameSettings", true);
|
si.SetBoolValue("Main", "ApplyGameSettings", true);
|
||||||
si.SetBoolValue("Main", "DisableAllEnhancements", false);
|
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.SetStringValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE));
|
||||||
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", false);
|
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", false);
|
||||||
|
@ -655,6 +658,13 @@ void HostInterface::FixIncompatibleSettings(bool display_osd_messages)
|
||||||
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
|
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
void HostInterface::SaveSettings(SettingsInterface& si)
|
||||||
|
@ -676,6 +686,8 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
|
|
||||||
if (System::IsValid())
|
if (System::IsValid())
|
||||||
{
|
{
|
||||||
|
System::ClearMemorySaveStates();
|
||||||
|
|
||||||
if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
|
if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
|
||||||
(g_settings.cpu_overclock_active &&
|
(g_settings.cpu_overclock_active &&
|
||||||
(g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator ||
|
(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_start_offset != old_settings.display_active_start_offset ||
|
||||||
g_settings.display_active_end_offset != old_settings.display_active_end_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_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())
|
if (g_settings.IsUsingCodeCache())
|
||||||
CPU::CodeCache::Reinitialize();
|
CPU::CodeCache::Reinitialize();
|
||||||
|
@ -793,6 +806,13 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
System::UpdateMemoryCards();
|
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 !=
|
if (g_settings.texture_replacements.enable_vram_write_replacements !=
|
||||||
old_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)
|
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->RestoreGraphicsAPIState();
|
||||||
g_gpu->UpdateSettings();
|
g_gpu->UpdateSettings();
|
||||||
g_gpu->ResetGraphicsAPIState();
|
g_gpu->ResetGraphicsAPIState();
|
||||||
|
System::ClearMemorySaveStates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,9 @@ void Settings::Load(SettingsInterface& si)
|
||||||
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
|
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
|
||||||
auto_load_cheats = si.GetBoolValue("Main", "AutoLoadCheats", false);
|
auto_load_cheats = si.GetBoolValue("Main", "AutoLoadCheats", false);
|
||||||
disable_all_enhancements = si.GetBoolValue("Main", "DisableAllEnhancements", 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 =
|
cpu_execution_mode =
|
||||||
ParseCPUExecutionMode(
|
ParseCPUExecutionMode(
|
||||||
|
@ -295,6 +298,9 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
|
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
|
||||||
si.SetBoolValue("Main", "AutoLoadCheats", auto_load_cheats);
|
si.SetBoolValue("Main", "AutoLoadCheats", auto_load_cheats);
|
||||||
si.SetBoolValue("Main", "DisableAllEnhancements", disable_all_enhancements);
|
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.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
||||||
si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable);
|
si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable);
|
||||||
|
|
|
@ -94,6 +94,10 @@ struct Settings
|
||||||
bool auto_load_cheats = false;
|
bool auto_load_cheats = false;
|
||||||
bool disable_all_enhancements = 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;
|
GPURenderer gpu_renderer = GPURenderer::Software;
|
||||||
std::string gpu_adapter;
|
std::string gpu_adapter;
|
||||||
std::string display_post_process_chain;
|
std::string display_post_process_chain;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
#include "common/timestamp.h"
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "cpu_code_cache.h"
|
#include "cpu_code_cache.h"
|
||||||
#include "cpu_core.h"
|
#include "cpu_core.h"
|
||||||
|
@ -30,7 +31,10 @@
|
||||||
#include "texture_replacements.h"
|
#include "texture_replacements.h"
|
||||||
#include "timers.h"
|
#include "timers.h"
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <deque>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
Log_SetChannel(System);
|
Log_SetChannel(System);
|
||||||
|
@ -51,6 +55,12 @@ SystemBootParameters::~SystemBootParameters() = default;
|
||||||
|
|
||||||
namespace System {
|
namespace System {
|
||||||
|
|
||||||
|
struct MemorySaveState
|
||||||
|
{
|
||||||
|
std::unique_ptr<HostDisplayTexture> vram_texture;
|
||||||
|
std::unique_ptr<GrowableMemoryByteStream> state_stream;
|
||||||
|
};
|
||||||
|
|
||||||
static bool LoadEXE(const char* filename);
|
static bool LoadEXE(const char* filename);
|
||||||
static bool SetExpansionROM(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 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 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 CreateGPU(GPURenderer renderer);
|
||||||
|
|
||||||
static bool Initialize(bool force_software_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 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()
|
State GetState()
|
||||||
{
|
{
|
||||||
return s_state;
|
return s_state;
|
||||||
|
@ -521,12 +543,13 @@ std::optional<DiscRegion> GetRegionForPath(const char* image_path)
|
||||||
|
|
||||||
bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/)
|
bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/)
|
||||||
{
|
{
|
||||||
|
ClearMemorySaveStates();
|
||||||
g_gpu->RestoreGraphicsAPIState();
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
|
|
||||||
// save current state
|
// save current state
|
||||||
std::unique_ptr<ByteStream> state_stream = ByteStream_CreateGrowableMemoryStream();
|
std::unique_ptr<ByteStream> state_stream = ByteStream_CreateGrowableMemoryStream();
|
||||||
StateWrapper sw(state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
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)
|
if (!state_valid)
|
||||||
Log_ErrorPrintf("Failed to save old GPU state when switching renderers");
|
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);
|
state_stream->SeekAbsolute(0);
|
||||||
sw.SetMode(StateWrapper::Mode::Read);
|
sw.SetMode(StateWrapper::Mode::Read);
|
||||||
g_gpu->RestoreGraphicsAPIState();
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
g_gpu->DoState(sw, update_display);
|
g_gpu->DoState(sw, nullptr, update_display);
|
||||||
TimingEvents::DoState(sw);
|
TimingEvents::DoState(sw);
|
||||||
g_gpu->ResetGraphicsAPIState();
|
g_gpu->ResetGraphicsAPIState();
|
||||||
}
|
}
|
||||||
|
@ -802,6 +825,7 @@ bool Initialize(bool force_software_renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateThrottlePeriod();
|
UpdateThrottlePeriod();
|
||||||
|
UpdateMemorySaveStateSettings();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,6 +834,8 @@ void Shutdown()
|
||||||
if (s_state == State::Shutdown)
|
if (s_state == State::Shutdown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ClearMemorySaveStates();
|
||||||
|
|
||||||
g_texture_replacements.Shutdown();
|
g_texture_replacements.Shutdown();
|
||||||
|
|
||||||
g_sio.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
|
// we put this here rather than in Initialize() because of the virtual calls
|
||||||
g_gpu->Reset();
|
g_gpu->Reset(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DoState(StateWrapper& sw, bool update_display)
|
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||||
{
|
{
|
||||||
if (!sw.DoMarker("System"))
|
if (!sw.DoMarker("System"))
|
||||||
return false;
|
return false;
|
||||||
|
@ -897,7 +923,7 @@ bool DoState(StateWrapper& sw, bool update_display)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
g_gpu->RestoreGraphicsAPIState();
|
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();
|
g_gpu->ResetGraphicsAPIState();
|
||||||
if (!gpu_result)
|
if (!gpu_result)
|
||||||
return false;
|
return false;
|
||||||
|
@ -963,7 +989,7 @@ void Reset()
|
||||||
Bus::Reset();
|
Bus::Reset();
|
||||||
g_dma.Reset();
|
g_dma.Reset();
|
||||||
g_interrupt_controller.Reset();
|
g_interrupt_controller.Reset();
|
||||||
g_gpu->Reset();
|
g_gpu->Reset(true);
|
||||||
g_cdrom.Reset();
|
g_cdrom.Reset();
|
||||||
g_pad.Reset();
|
g_pad.Reset();
|
||||||
g_timers.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());
|
UpdateRunningGame(media_filename.c_str(), media.get());
|
||||||
|
ClearMemorySaveStates();
|
||||||
|
|
||||||
if (s_state == State::Starting)
|
if (s_state == State::Starting)
|
||||||
{
|
{
|
||||||
|
@ -1117,7 +1144,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
StateWrapper sw(state, StateWrapper::Mode::Read, header.version);
|
StateWrapper sw(state, StateWrapper::Mode::Read, header.version);
|
||||||
if (!DoState(sw, update_display))
|
if (!DoState(sw, nullptr, update_display))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (s_state == State::Starting)
|
if (s_state == State::Starting)
|
||||||
|
@ -1184,7 +1211,7 @@ bool SaveState(ByteStream* state, u32 screenshot_size /* = 128 */)
|
||||||
g_gpu->RestoreGraphicsAPIState();
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
|
|
||||||
StateWrapper sw(state, StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
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();
|
g_gpu->ResetGraphicsAPIState();
|
||||||
|
|
||||||
|
@ -1224,10 +1251,8 @@ void SingleStepCPU()
|
||||||
g_gpu->ResetGraphicsAPIState();
|
g_gpu->ResetGraphicsAPIState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunFrame()
|
void DoRunFrame()
|
||||||
{
|
{
|
||||||
s_frame_timer.Reset();
|
|
||||||
|
|
||||||
g_gpu->RestoreGraphicsAPIState();
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
|
|
||||||
if (CPU::g_state.use_debug_dispatcher)
|
if (CPU::g_state.use_debug_dispatcher)
|
||||||
|
@ -1266,6 +1291,22 @@ void RunFrame()
|
||||||
g_gpu->ResetGraphicsAPIState();
|
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()
|
float GetTargetSpeed()
|
||||||
{
|
{
|
||||||
return s_target_speed;
|
return s_target_speed;
|
||||||
|
@ -1739,12 +1780,14 @@ bool InsertMedia(const char* path)
|
||||||
if (g_settings.IsUsingCodeCache())
|
if (g_settings.IsUsingCodeCache())
|
||||||
CPU::CodeCache::Reinitialize();
|
CPU::CodeCache::Reinitialize();
|
||||||
|
|
||||||
|
ClearMemorySaveStates();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveMedia()
|
void RemoveMedia()
|
||||||
{
|
{
|
||||||
g_cdrom.RemoveMedia();
|
g_cdrom.RemoveMedia();
|
||||||
|
ClearMemorySaveStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateRunningGame(const char* path, CDImage* image)
|
void UpdateRunningGame(const char* path, CDImage* image)
|
||||||
|
@ -1907,4 +1950,152 @@ void SetCheatList(std::unique_ptr<CheatList> cheats)
|
||||||
s_cheat_list = std::move(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
|
} // namespace System
|
|
@ -228,4 +228,14 @@ void ApplyCheatCode(const CheatCode& code);
|
||||||
/// Sets or clears the provided cheat list, applying every frame.
|
/// Sets or clears the provided cheat list, applying every frame.
|
||||||
void SetCheatList(std::unique_ptr<CheatList> cheats);
|
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
|
} // namespace System
|
||||||
|
|
|
@ -1549,7 +1549,7 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("ToggleCheats"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("ToggleCheats"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Cheats")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Toggle Cheats")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
DoToggleCheats();
|
DoToggleCheats();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1584,7 +1584,7 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
||||||
#else
|
#else
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("TogglePatchCodes"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("TogglePatchCodes"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Patch Codes")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Toggle Patch Codes")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
DoToggleCheats();
|
DoToggleCheats();
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
@ -1603,25 +1603,39 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("FrameStep"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "General")), StaticString("FrameStep"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Frame Step")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Frame Step")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
DoFrameStep();
|
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()
|
void CommonHostInterface::RegisterGraphicsHotkeys()
|
||||||
{
|
{
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ToggleSoftwareRendering"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ToggleSoftwareRendering"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Software Rendering")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Toggle Software Rendering")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
ToggleSoftwareRendering();
|
ToggleSoftwareRendering();
|
||||||
});
|
});
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePGXP"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePGXP"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle PGXP")), [this](bool pressed) {
|
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_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable;
|
||||||
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
g_gpu->UpdateSettings();
|
g_gpu->UpdateSettings();
|
||||||
|
g_gpu->ResetGraphicsAPIState();
|
||||||
|
System::ClearMemorySaveStates();
|
||||||
AddOSDMessage(g_settings.gpu_pgxp_enable ?
|
AddOSDMessage(g_settings.gpu_pgxp_enable ?
|
||||||
TranslateStdString("OSDMessage", "PGXP is now enabled.") :
|
TranslateStdString("OSDMessage", "PGXP is now enabled.") :
|
||||||
TranslateStdString("OSDMessage", "PGXP is now disabled"),
|
TranslateStdString("OSDMessage", "PGXP is now disabled"),
|
||||||
|
@ -1640,13 +1654,16 @@ void CommonHostInterface::RegisterGraphicsHotkeys()
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePGXPDepth"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePGXPDepth"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle PGXP Depth Buffer")), [this](bool pressed) {
|
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;
|
g_settings.gpu_pgxp_depth_buffer = !g_settings.gpu_pgxp_depth_buffer;
|
||||||
if (!g_settings.gpu_pgxp_enable)
|
if (!g_settings.gpu_pgxp_enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
g_gpu->UpdateSettings();
|
g_gpu->UpdateSettings();
|
||||||
|
g_gpu->ResetGraphicsAPIState();
|
||||||
|
System::ClearMemorySaveStates();
|
||||||
AddOSDMessage(g_settings.gpu_pgxp_depth_buffer ?
|
AddOSDMessage(g_settings.gpu_pgxp_depth_buffer ?
|
||||||
TranslateStdString("OSDMessage", "PGXP Depth Buffer is now enabled.") :
|
TranslateStdString("OSDMessage", "PGXP Depth Buffer is now enabled.") :
|
||||||
TranslateStdString("OSDMessage", "PGXP Depth Buffer is now disabled."),
|
TranslateStdString("OSDMessage", "PGXP Depth Buffer is now disabled."),
|
||||||
|
@ -1656,31 +1673,31 @@ void CommonHostInterface::RegisterGraphicsHotkeys()
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("IncreaseResolutionScale"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("IncreaseResolutionScale"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Increase Resolution Scale")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Increase Resolution Scale")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
ModifyResolutionScale(1);
|
ModifyResolutionScale(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("DecreaseResolutionScale"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("DecreaseResolutionScale"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Decrease Resolution Scale")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Decrease Resolution Scale")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
ModifyResolutionScale(-1);
|
ModifyResolutionScale(-1);
|
||||||
});
|
});
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePostProcessing"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("TogglePostProcessing"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Post-Processing")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Toggle Post-Processing")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
TogglePostProcessing();
|
TogglePostProcessing();
|
||||||
});
|
});
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ReloadPostProcessingShaders"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ReloadPostProcessingShaders"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Reload Post Processing Shaders")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Reload Post Processing Shaders")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
ReloadPostProcessingShaders();
|
ReloadPostProcessingShaders();
|
||||||
});
|
});
|
||||||
|
|
||||||
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ReloadTextureReplacements"),
|
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Graphics")), StaticString("ReloadTextureReplacements"),
|
||||||
StaticString(TRANSLATABLE("Hotkeys", "Reload Texture Replacements")), [this](bool pressed) {
|
StaticString(TRANSLATABLE("Hotkeys", "Reload Texture Replacements")), [this](bool pressed) {
|
||||||
if (pressed)
|
if (pressed && System::IsValid())
|
||||||
{
|
{
|
||||||
AddOSDMessage(TranslateStdString("OSDMessage", "Texture replacements reloaded."), 10.0f);
|
AddOSDMessage(TranslateStdString("OSDMessage", "Texture replacements reloaded."), 10.0f);
|
||||||
g_texture_replacements.Reload();
|
g_texture_replacements.Reload();
|
||||||
|
|
Loading…
Reference in a new issue