summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2019-06-29 21:05:30 +1000
committerStenzek <stenzek@gmail.com>2019-07-24 05:19:22 +1000
commit137009affef20bfaa91ff32ea1bb6693c09e03c2 (patch)
treeab6ac090a91ffdc2aa9efd6a1ca01fba1c0c363b /Source/Core/VideoCommon
parent1082468133342586ae2a5986408d163ce5aa14ca (diff)
FramebufferManager: Support saving EFB to save state
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/FramebufferManager.cpp114
-rw-r--r--Source/Core/VideoCommon/FramebufferManager.h10
-rw-r--r--Source/Core/VideoCommon/FramebufferShaderGen.cpp20
-rw-r--r--Source/Core/VideoCommon/FramebufferShaderGen.h1
-rw-r--r--Source/Core/VideoCommon/VideoState.cpp4
5 files changed, 149 insertions, 0 deletions
diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp
index 6f4e132c06..e28cf0b5cc 100644
--- a/Source/Core/VideoCommon/FramebufferManager.cpp
+++ b/Source/Core/VideoCommon/FramebufferManager.cpp
@@ -7,6 +7,7 @@
#include "VideoCommon/FramebufferShaderGen.h"
#include "VideoCommon/VertexManagerBase.h"
+#include "Common/ChunkFile.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractFramebuffer.h"
@@ -464,6 +465,20 @@ bool FramebufferManager::CompileReadbackPipelines()
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;
}
@@ -842,3 +857,102 @@ void FramebufferManager::DestroyPokePipelines()
m_color_poke_pipeline.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();
+}
diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h
index b97d45b31e..b4ae99361d 100644
--- a/Source/Core/VideoCommon/FramebufferManager.h
+++ b/Source/Core/VideoCommon/FramebufferManager.h
@@ -17,6 +17,7 @@
#include "VideoCommon/TextureConfig.h"
class NativeVertexFormat;
+class PointerWrap;
enum class EFBReinterpretType
{
@@ -95,6 +96,9 @@ public:
void PokeEFBDepth(u32 x, u32 y, float depth);
void FlushEFBPokes();
+ // Save state load/save.
+ void DoState(PointerWrap& p);
+
protected:
struct EFBPokeVertex
{
@@ -145,6 +149,9 @@ protected:
void DrawPokeVertices(const EFBPokeVertex* vertices, u32 vertex_count,
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_convert_color_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<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
std::array<std::unique_ptr<AbstractPipeline>, 6> m_format_conversion_pipelines;
diff --git a/Source/Core/VideoCommon/FramebufferShaderGen.cpp b/Source/Core/VideoCommon/FramebufferShaderGen.cpp
index 5789c0a0d2..00ff753d92 100644
--- a/Source/Core/VideoCommon/FramebufferShaderGen.cpp
+++ b/Source/Core/VideoCommon/FramebufferShaderGen.cpp
@@ -644,4 +644,24 @@ std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureF
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
diff --git a/Source/Core/VideoCommon/FramebufferShaderGen.h b/Source/Core/VideoCommon/FramebufferShaderGen.h
index b0134b5897..2ec50b4d76 100644
--- a/Source/Core/VideoCommon/FramebufferShaderGen.h
+++ b/Source/Core/VideoCommon/FramebufferShaderGen.h
@@ -30,5 +30,6 @@ std::string GenerateEFBPokeVertexShader();
std::string GenerateColorPixelShader();
std::string GenerateFormatConversionShader(EFBReinterpretType convtype, u32 samples);
std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureFormat to_format);
+std::string GenerateEFBRestorePixelShader();
} // namespace FramebufferShaderGen
diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp
index 0fdeebcfa1..9b6418f98f 100644
--- a/Source/Core/VideoCommon/VideoState.cpp
+++ b/Source/Core/VideoCommon/VideoState.cpp
@@ -10,6 +10,7 @@
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/Fifo.h"
+#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/GeometryShaderManager.h"
#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/PixelShaderManager.h"
@@ -74,6 +75,9 @@ void VideoCommon_DoState(PointerWrap& p)
BoundingBox::DoState(p);
p.DoMarker("BoundingBox");
+ g_framebuffer_manager->DoState(p);
+ p.DoMarker("FramebufferManager");
+
g_texture_cache->DoState(p);
p.DoMarker("TextureCache");