OGL: Support full-resolution frame dumping

This commit is contained in:
Stenzek 2016-11-10 23:20:00 +10:00
parent 93221e7f48
commit b81dee8b9a
4 changed files with 111 additions and 11 deletions

View File

@ -44,8 +44,6 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
{
ApplyShader();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(dst.left, dst.bottom, dst.GetWidth(), dst.GetHeight());
OpenGL_BindAttributelessVAO();

View File

@ -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);
// 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()
{

View File

@ -128,6 +128,17 @@ private:
void FlushFrameDump();
void DumpFrame(const TargetRectangle& flipped_trc, u64 ticks);
void 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);
// Frame dumping framebuffer, we render to this, then read it back
void PrepareFrameDumpRenderTexture(u32 width, u32 height);
void DestroyFrameDumpResources();
GLuint m_frame_dump_render_texture = 0;
GLuint m_frame_dump_render_framebuffer = 0;
u32 m_frame_dump_render_texture_width = 0;
u32 m_frame_dump_render_texture_height = 0;
// avi dumping state to delay one frame
std::array<u32, 2> m_frame_dumping_pbo = {};

View File

@ -104,7 +104,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsSSAA = true;
g_Config.backend_info.bSupportsReversedDepthRange = true;
g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = true;
// Overwritten in Render.cpp later
g_Config.backend_info.bSupportsDualSourceBlend = true;