summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/FramebufferManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon/FramebufferManager.cpp')
-rw-r--r--Source/Core/VideoCommon/FramebufferManager.cpp114
1 files changed, 114 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();
+}