OGL: Support full-resolution frame dumping
This commit is contained in:
parent
93221e7f48
commit
b81dee8b9a
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue