summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/OGL/Render.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2016-11-10 23:20:00 +1000
committerStenzek <stenzek@gmail.com>2016-11-28 21:54:56 +1000
commitb81dee8b9afec63c9c7bf23a1e91afb7567bab49 (patch)
tree7acb4d60faf54afa1b6e4b1ded994adbc8b60d2d /Source/Core/VideoBackends/OGL/Render.cpp
parent93221e7f48cf907df12d7d9e11e051db2f252a39 (diff)
OGL: Support full-resolution frame dumping
Diffstat (limited to 'Source/Core/VideoBackends/OGL/Render.cpp')
-rw-r--r--Source/Core/VideoBackends/OGL/Render.cpp107
1 files changed, 99 insertions, 8 deletions
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index bcfd98a937..8c83e92f89 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -768,8 +768,7 @@ Renderer::~Renderer()
{
FlushFrameDump();
FinishFrameData();
- if (m_frame_dumping_pbo[0])
- glDeleteBuffers(2, m_frame_dumping_pbo.data());
+ DestroyFrameDumpResources();
}
void Renderer::Shutdown()
@@ -1380,11 +1379,30 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
std::swap(flipped_trc.top, flipped_trc.bottom);
// Copy the framebuffer to screen.
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
DrawFrame(flipped_trc, rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
-
- DumpFrame(flipped_trc, ticks);
+ // The FlushFrameDump call here is necessary even after frame dumping is stopped.
+ // If left out, screenshots are "one frame" behind, as an extra frame is dumped and buffered.
+ FlushFrameDump();
+ if (IsFrameDumping())
+ {
+ // Currently, we only use the off-screen buffer as a frame dump source if full-resolution
+ // frame dumping is enabled, saving the need for an extra copy. In the future, this could
+ // be extended to be used for surfaceless contexts as well.
+ bool use_offscreen_buffer = g_ActiveConfig.bInternalResolutionFrameDumps;
+ if (use_offscreen_buffer)
+ {
+ // DumpFrameUsingFBO resets GL_FRAMEBUFFER, so change back to the window for drawing OSD.
+ DumpFrameUsingFBO(rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight, ticks);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ }
+ else
+ {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ DumpFrame(flipped_trc, ticks);
+ }
+ }
// Finish up the current frame, print some stats
@@ -1630,9 +1648,6 @@ void Renderer::FlushFrameDump()
void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
{
- if (!IsFrameDumping())
- return;
-
if (!m_frame_dumping_pbo[0])
{
glGenBuffers(2, m_frame_dumping_pbo.data());
@@ -1669,6 +1684,82 @@ void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
+void Renderer::DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count,
+ u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
+{
+ // This needs to be converted to the GL bottom-up window coordinate system.
+ TargetRectangle render_rc = CalculateFrameDumpDrawRectangle();
+ std::swap(render_rc.top, render_rc.bottom);
+
+ // Ensure the render texture meets the size requirements of the draw area.
+ u32 render_width = static_cast<u32>(render_rc.GetWidth());
+ u32 render_height = static_cast<u32>(render_rc.GetHeight());
+ PrepareFrameDumpRenderTexture(render_width, render_height);
+
+ // Ensure the alpha channel of the render texture is blank. The frame dump backend expects
+ // that the alpha is set to 1.0 for all pixels.
+ FramebufferManager::SetFramebuffer(m_frame_dump_render_framebuffer);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Render the frame into the frame dump render texture. Disable alpha writes in case the
+ // post-processing shader writes a non-1.0 value.
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ DrawFrame(render_rc, source_rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
+
+ // Copy frame to output buffer. This assumes that GL_FRAMEBUFFER has been set.
+ DumpFrame(render_rc, ticks);
+
+ // Restore state after drawing. This isn't the game state, it's the state set by ResetAPIState.
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ FramebufferManager::SetFramebuffer(0);
+}
+
+void Renderer::PrepareFrameDumpRenderTexture(u32 width, u32 height)
+{
+ // Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used).
+ // Or, resize texture if it isn't large enough to accommodate the current frame.
+ if (m_frame_dump_render_texture != 0 && m_frame_dump_render_framebuffer != 0 &&
+ m_frame_dump_render_texture_width >= width && m_frame_dump_render_texture_height >= height)
+ {
+ return;
+ }
+
+ // Recreate texture objects.
+ if (m_frame_dump_render_texture != 0)
+ glDeleteTextures(1, &m_frame_dump_render_texture);
+ if (m_frame_dump_render_framebuffer != 0)
+ glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
+
+ glGenTextures(1, &m_frame_dump_render_texture);
+ glActiveTexture(GL_TEXTURE9);
+ glBindTexture(GL_TEXTURE_2D, m_frame_dump_render_texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ glGenFramebuffers(1, &m_frame_dump_render_framebuffer);
+ FramebufferManager::SetFramebuffer(m_frame_dump_render_framebuffer);
+ FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ m_frame_dump_render_texture, 0);
+
+ m_frame_dump_render_texture_width = width;
+ m_frame_dump_render_texture_height = height;
+ TextureCache::SetStage();
+}
+
+void Renderer::DestroyFrameDumpResources()
+{
+ if (m_frame_dump_render_framebuffer)
+ glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
+ if (m_frame_dump_render_texture)
+ glDeleteTextures(1, &m_frame_dump_render_texture);
+ if (m_frame_dumping_pbo[0])
+ glDeleteBuffers(2, m_frame_dumping_pbo.data());
+}
+
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
void Renderer::ResetAPIState()
{