FramebufferManager: Support saving EFB to save state
This commit is contained in:
parent
1082468133
commit
137009affe
|
@ -7,6 +7,7 @@
|
||||||
#include "VideoCommon/FramebufferShaderGen.h"
|
#include "VideoCommon/FramebufferShaderGen.h"
|
||||||
#include "VideoCommon/VertexManagerBase.h"
|
#include "VideoCommon/VertexManagerBase.h"
|
||||||
|
|
||||||
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "VideoCommon/AbstractFramebuffer.h"
|
#include "VideoCommon/AbstractFramebuffer.h"
|
||||||
|
@ -464,6 +465,20 @@ bool FramebufferManager::CompileReadbackPipelines()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EFB restore pipeline
|
||||||
|
auto restore_shader = g_renderer->CreateShaderFromSource(
|
||||||
|
ShaderStage::Pixel, FramebufferShaderGen::GenerateEFBRestorePixelShader());
|
||||||
|
if (!restore_shader)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
config.framebuffer_state = GetEFBFramebufferState();
|
||||||
|
config.framebuffer_state.per_sample_shading = false;
|
||||||
|
config.vertex_shader = g_shader_cache->GetScreenQuadVertexShader();
|
||||||
|
config.pixel_shader = restore_shader.get();
|
||||||
|
m_efb_restore_pipeline = g_renderer->CreatePipeline(config);
|
||||||
|
if (!m_efb_restore_pipeline)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,3 +857,102 @@ void FramebufferManager::DestroyPokePipelines()
|
||||||
m_color_poke_pipeline.reset();
|
m_color_poke_pipeline.reset();
|
||||||
m_poke_vertex_format.reset();
|
m_poke_vertex_format.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FramebufferManager::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
FlushEFBPokes();
|
||||||
|
|
||||||
|
if (p.GetMode() == PointerWrap::MODE_WRITE || p.GetMode() == PointerWrap::MODE_MEASURE)
|
||||||
|
DoSaveState(p);
|
||||||
|
else
|
||||||
|
DoLoadState(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramebufferManager::DoSaveState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
// For multisampling, we need to resolve first before we can save.
|
||||||
|
// This won't be bit-exact when loading, which could cause interesting rendering side-effects for
|
||||||
|
// a frame. But whatever, MSAA doesn't exactly behave that well anyway.
|
||||||
|
AbstractTexture* color_texture = ResolveEFBColorTexture(m_efb_color_texture->GetRect());
|
||||||
|
AbstractTexture* depth_texture = ResolveEFBDepthTexture(m_efb_depth_texture->GetRect());
|
||||||
|
|
||||||
|
// We don't want to save these as rendertarget textures, just the data itself when deserializing.
|
||||||
|
const TextureConfig color_texture_config(color_texture->GetWidth(), color_texture->GetHeight(),
|
||||||
|
color_texture->GetLevels(), color_texture->GetLayers(),
|
||||||
|
1, GetEFBColorFormat(), 0);
|
||||||
|
g_texture_cache->SerializeTexture(color_texture, color_texture_config, p);
|
||||||
|
|
||||||
|
if (GetEFBDepthFormat() == AbstractTextureFormat::D32F)
|
||||||
|
{
|
||||||
|
const TextureConfig depth_texture_config(
|
||||||
|
depth_texture->GetWidth(), depth_texture->GetHeight(), depth_texture->GetLevels(),
|
||||||
|
depth_texture->GetLayers(), 1,
|
||||||
|
AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()), 0);
|
||||||
|
g_texture_cache->SerializeTexture(depth_texture, depth_texture_config, p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the EFB is backed by a D24S8 texture, we first have to convert it to R32F.
|
||||||
|
const TextureConfig temp_texture_config(depth_texture->GetWidth(), depth_texture->GetHeight(),
|
||||||
|
depth_texture->GetLevels(), depth_texture->GetLayers(),
|
||||||
|
1, AbstractTextureFormat::R32F,
|
||||||
|
AbstractTextureFlag_RenderTarget);
|
||||||
|
std::unique_ptr<AbstractTexture> temp_texture = g_renderer->CreateTexture(temp_texture_config);
|
||||||
|
std::unique_ptr<AbstractFramebuffer> temp_fb =
|
||||||
|
g_renderer->CreateFramebuffer(temp_texture.get(), nullptr);
|
||||||
|
if (temp_texture && temp_fb)
|
||||||
|
{
|
||||||
|
g_renderer->ScaleTexture(temp_fb.get(), temp_texture->GetRect(), depth_texture,
|
||||||
|
depth_texture->GetRect());
|
||||||
|
|
||||||
|
const TextureConfig depth_texture_config(
|
||||||
|
depth_texture->GetWidth(), depth_texture->GetHeight(), depth_texture->GetLevels(),
|
||||||
|
depth_texture->GetLayers(), 1, temp_texture->GetFormat(), 0);
|
||||||
|
g_texture_cache->SerializeTexture(depth_texture, depth_texture_config, p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to create temp texture for depth saving");
|
||||||
|
g_texture_cache->SerializeTexture(color_texture, color_texture_config, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramebufferManager::DoLoadState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
// Invalidate any peek cache tiles.
|
||||||
|
InvalidatePeekCache(true);
|
||||||
|
|
||||||
|
// Deserialize the color and depth textures. This could fail.
|
||||||
|
auto color_tex = g_texture_cache->DeserializeTexture(p);
|
||||||
|
auto depth_tex = g_texture_cache->DeserializeTexture(p);
|
||||||
|
|
||||||
|
// If the stereo mode is different in the save state, throw it away.
|
||||||
|
if (!color_tex || !depth_tex ||
|
||||||
|
color_tex->texture->GetLayers() != m_efb_color_texture->GetLayers())
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "Failed to deserialize EFB contents. Clearing instead.");
|
||||||
|
g_renderer->SetAndClearFramebuffer(
|
||||||
|
m_efb_framebuffer.get(), {{0.0f, 0.0f, 0.0f, 0.0f}},
|
||||||
|
g_ActiveConfig.backend_info.bSupportsReversedDepthRange ? 1.0f : 0.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size differences are okay here, since the linear filtering will downscale/upscale it.
|
||||||
|
// Depth buffer is always point sampled, since we don't want to interpolate depth values.
|
||||||
|
const bool rescale = color_tex->texture->GetWidth() != m_efb_color_texture->GetWidth() ||
|
||||||
|
color_tex->texture->GetHeight() != m_efb_color_texture->GetHeight();
|
||||||
|
|
||||||
|
// Draw the deserialized textures over the EFB.
|
||||||
|
g_renderer->BeginUtilityDrawing();
|
||||||
|
g_renderer->SetAndDiscardFramebuffer(m_efb_framebuffer.get());
|
||||||
|
g_renderer->SetViewportAndScissor(m_efb_framebuffer->GetRect());
|
||||||
|
g_renderer->SetPipeline(m_efb_restore_pipeline.get());
|
||||||
|
g_renderer->SetTexture(0, color_tex->texture.get());
|
||||||
|
g_renderer->SetTexture(1, depth_tex->texture.get());
|
||||||
|
g_renderer->SetSamplerState(0, rescale ? RenderState::GetLinearSamplerState() :
|
||||||
|
RenderState::GetPointSamplerState());
|
||||||
|
g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState());
|
||||||
|
g_renderer->Draw(0, 3);
|
||||||
|
g_renderer->EndUtilityDrawing();
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "VideoCommon/TextureConfig.h"
|
#include "VideoCommon/TextureConfig.h"
|
||||||
|
|
||||||
class NativeVertexFormat;
|
class NativeVertexFormat;
|
||||||
|
class PointerWrap;
|
||||||
|
|
||||||
enum class EFBReinterpretType
|
enum class EFBReinterpretType
|
||||||
{
|
{
|
||||||
|
@ -95,6 +96,9 @@ public:
|
||||||
void PokeEFBDepth(u32 x, u32 y, float depth);
|
void PokeEFBDepth(u32 x, u32 y, float depth);
|
||||||
void FlushEFBPokes();
|
void FlushEFBPokes();
|
||||||
|
|
||||||
|
// Save state load/save.
|
||||||
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct EFBPokeVertex
|
struct EFBPokeVertex
|
||||||
{
|
{
|
||||||
|
@ -145,6 +149,9 @@ protected:
|
||||||
void DrawPokeVertices(const EFBPokeVertex* vertices, u32 vertex_count,
|
void DrawPokeVertices(const EFBPokeVertex* vertices, u32 vertex_count,
|
||||||
const AbstractPipeline* pipeline);
|
const AbstractPipeline* pipeline);
|
||||||
|
|
||||||
|
void DoLoadState(PointerWrap& p);
|
||||||
|
void DoSaveState(PointerWrap& p);
|
||||||
|
|
||||||
std::unique_ptr<AbstractTexture> m_efb_color_texture;
|
std::unique_ptr<AbstractTexture> m_efb_color_texture;
|
||||||
std::unique_ptr<AbstractTexture> m_efb_convert_color_texture;
|
std::unique_ptr<AbstractTexture> m_efb_convert_color_texture;
|
||||||
std::unique_ptr<AbstractTexture> m_efb_depth_texture;
|
std::unique_ptr<AbstractTexture> m_efb_depth_texture;
|
||||||
|
@ -156,6 +163,9 @@ protected:
|
||||||
std::unique_ptr<AbstractFramebuffer> m_efb_depth_resolve_framebuffer;
|
std::unique_ptr<AbstractFramebuffer> m_efb_depth_resolve_framebuffer;
|
||||||
std::unique_ptr<AbstractPipeline> m_efb_depth_resolve_pipeline;
|
std::unique_ptr<AbstractPipeline> m_efb_depth_resolve_pipeline;
|
||||||
|
|
||||||
|
// Pipeline for restoring the contents of the EFB from a save state
|
||||||
|
std::unique_ptr<AbstractPipeline> m_efb_restore_pipeline;
|
||||||
|
|
||||||
// Format conversion shaders
|
// Format conversion shaders
|
||||||
std::array<std::unique_ptr<AbstractPipeline>, 6> m_format_conversion_pipelines;
|
std::array<std::unique_ptr<AbstractPipeline>, 6> m_format_conversion_pipelines;
|
||||||
|
|
||||||
|
|
|
@ -644,4 +644,24 @@ std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureF
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GenerateEFBRestorePixelShader()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
EmitSamplerDeclarations(ss, 0, 2, false);
|
||||||
|
EmitPixelMainDeclaration(ss, 1, 0, "float4",
|
||||||
|
GetAPIType() == APIType::D3D ? "out float depth : SV_Depth, " : "");
|
||||||
|
ss << "{\n";
|
||||||
|
ss << " float3 coords = float3(v_tex0.x, "
|
||||||
|
<< (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin ? "1.0 - " : "")
|
||||||
|
<< "v_tex0.y, v_tex0.z);\n";
|
||||||
|
ss << " ocol0 = ";
|
||||||
|
EmitSampleTexture(ss, 0, "coords");
|
||||||
|
ss << ";\n";
|
||||||
|
ss << " " << (GetAPIType() == APIType::D3D ? "depth" : "gl_FragDepth") << " = ";
|
||||||
|
EmitSampleTexture(ss, 1, "coords");
|
||||||
|
ss << ".r;\n";
|
||||||
|
ss << "}\n";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FramebufferShaderGen
|
} // namespace FramebufferShaderGen
|
||||||
|
|
|
@ -30,5 +30,6 @@ std::string GenerateEFBPokeVertexShader();
|
||||||
std::string GenerateColorPixelShader();
|
std::string GenerateColorPixelShader();
|
||||||
std::string GenerateFormatConversionShader(EFBReinterpretType convtype, u32 samples);
|
std::string GenerateFormatConversionShader(EFBReinterpretType convtype, u32 samples);
|
||||||
std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureFormat to_format);
|
std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureFormat to_format);
|
||||||
|
std::string GenerateEFBRestorePixelShader();
|
||||||
|
|
||||||
} // namespace FramebufferShaderGen
|
} // namespace FramebufferShaderGen
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "VideoCommon/CPMemory.h"
|
#include "VideoCommon/CPMemory.h"
|
||||||
#include "VideoCommon/CommandProcessor.h"
|
#include "VideoCommon/CommandProcessor.h"
|
||||||
#include "VideoCommon/Fifo.h"
|
#include "VideoCommon/Fifo.h"
|
||||||
|
#include "VideoCommon/FramebufferManager.h"
|
||||||
#include "VideoCommon/GeometryShaderManager.h"
|
#include "VideoCommon/GeometryShaderManager.h"
|
||||||
#include "VideoCommon/PixelEngine.h"
|
#include "VideoCommon/PixelEngine.h"
|
||||||
#include "VideoCommon/PixelShaderManager.h"
|
#include "VideoCommon/PixelShaderManager.h"
|
||||||
|
@ -74,6 +75,9 @@ void VideoCommon_DoState(PointerWrap& p)
|
||||||
BoundingBox::DoState(p);
|
BoundingBox::DoState(p);
|
||||||
p.DoMarker("BoundingBox");
|
p.DoMarker("BoundingBox");
|
||||||
|
|
||||||
|
g_framebuffer_manager->DoState(p);
|
||||||
|
p.DoMarker("FramebufferManager");
|
||||||
|
|
||||||
g_texture_cache->DoState(p);
|
g_texture_cache->DoState(p);
|
||||||
p.DoMarker("TextureCache");
|
p.DoMarker("TextureCache");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue