summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/OGL/Render.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2016-11-28 22:05:16 +1000
committerGitHub <noreply@github.com>2016-11-28 22:05:16 +1000
commit0212741574548fa95593e0e9d9451feca45ab83d (patch)
tree048acf7bcc5cb7b5eb3e7c92915ed4bf9f96af7c /Source/Core/VideoBackends/OGL/Render.cpp
parent49e807b71f5f2af09359295ecd2f1645c86d7c2c (diff)
parentb81dee8b9afec63c9c7bf23a1e91afb7567bab49 (diff)
Merge pull request #4436 from stenzek/vulkan-full-ir-framedump
VideoBackends: Internal resolution frame dumping
Diffstat (limited to 'Source/Core/VideoBackends/OGL/Render.cpp')
-rw-r--r--Source/Core/VideoBackends/OGL/Render.cpp275
1 files changed, 199 insertions, 76 deletions
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index fae6d687bb..8c83e92f89 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -700,10 +700,10 @@ Renderer::Renderer()
FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
- UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
+ UpdateDrawRectangle();
s_last_efb_scale = g_ActiveConfig.iEFBScale;
- CalculateTargetSize(s_backbuffer_width, s_backbuffer_height);
+ CalculateTargetSize();
PixelShaderManager::SetEfbScaleChanged();
@@ -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()
@@ -1373,85 +1372,38 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
ResetAPIState();
- UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
+ UpdateDrawRectangle();
TargetRectangle flipped_trc = GetTargetRectangle();
// Flip top and bottom for some reason; TODO: Fix the code to suck less?
std::swap(flipped_trc.top, flipped_trc.bottom);
// Copy the framebuffer to screen.
- const XFBSource* xfbSource = nullptr;
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ DrawFrame(flipped_trc, rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight);
- if (g_ActiveConfig.bUseXFB)
+ // 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())
{
- // draw each xfb source
- for (u32 i = 0; i < xfbCount; ++i)
+ // 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)
{
- xfbSource = (const XFBSource*)xfbSourceList[i];
-
- TargetRectangle drawRc;
- TargetRectangle sourceRc;
- sourceRc.left = xfbSource->sourceRc.left;
- sourceRc.right = xfbSource->sourceRc.right;
- sourceRc.top = xfbSource->sourceRc.top;
- sourceRc.bottom = xfbSource->sourceRc.bottom;
-
- if (g_ActiveConfig.bUseRealXFB)
- {
- drawRc = flipped_trc;
- sourceRc.right -= fbStride - fbWidth;
-
- // RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a
- // top-left origin.
- // The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
- sourceRc.top = sourceRc.bottom;
- sourceRc.bottom = 0;
- }
- else
- {
- // use virtual xfb with offset
- int xfbHeight = xfbSource->srcHeight;
- int xfbWidth = xfbSource->srcWidth;
- int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbStride * 2);
-
- drawRc.top = flipped_trc.top - hOffset * flipped_trc.GetHeight() / (s32)fbHeight;
- drawRc.bottom =
- flipped_trc.top - (hOffset + xfbHeight) * flipped_trc.GetHeight() / (s32)fbHeight;
- drawRc.left =
- flipped_trc.left +
- (flipped_trc.GetWidth() - xfbWidth * flipped_trc.GetWidth() / (s32)fbStride) / 2;
- drawRc.right =
- flipped_trc.left +
- (flipped_trc.GetWidth() + xfbWidth * flipped_trc.GetWidth() / (s32)fbStride) / 2;
-
- // The following code disables auto stretch. Kept for reference.
- // scale draw area for a 1 to 1 pixel mapping with the draw target
- // float vScale = (float)fbHeight / (float)flipped_trc.GetHeight();
- // float hScale = (float)fbWidth / (float)flipped_trc.GetWidth();
- // drawRc.top *= vScale;
- // drawRc.bottom *= vScale;
- // drawRc.left *= hScale;
- // drawRc.right *= hScale;
-
- sourceRc.right -= Renderer::EFBToScaledX(fbStride - fbWidth);
- }
-
- BlitScreen(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
+ // 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);
}
- }
- else
- {
- TargetRectangle targetRc = ConvertEFBRectangle(rc);
-
- // for msaa mode, we must resolve the efb content to non-msaa
- GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
- BlitScreen(targetRc, flipped_trc, tex, s_target_width, s_target_height);
}
- glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
-
- DumpFrame(flipped_trc, ticks);
-
// Finish up the current frame, print some stats
SetWindowSize(fbStride, fbHeight);
@@ -1484,7 +1436,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
s_last_efb_scale = g_ActiveConfig.iEFBScale;
}
bool TargetSizeChanged = false;
- if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height))
+ if (CalculateTargetSize())
{
TargetSizeChanged = true;
}
@@ -1494,7 +1446,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
{
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
- UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
+ UpdateDrawRectangle();
if (TargetSizeChanged || s_last_multisamples != g_ActiveConfig.iMultisamples ||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
@@ -1580,6 +1532,104 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
ClearEFBCache();
}
+void Renderer::DrawFrame(const TargetRectangle& target_rc, const EFBRectangle& source_rc,
+ u32 xfb_addr, const XFBSourceBase* const* xfb_sources, u32 xfb_count,
+ u32 fb_width, u32 fb_stride, u32 fb_height)
+{
+ if (g_ActiveConfig.bUseXFB)
+ {
+ if (g_ActiveConfig.bUseRealXFB)
+ DrawRealXFB(target_rc, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
+ else
+ DrawVirtualXFB(target_rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
+ }
+ else
+ {
+ DrawEFB(target_rc, source_rc);
+ }
+}
+
+void Renderer::DrawEFB(const TargetRectangle& target_rc, const EFBRectangle& source_rc)
+{
+ TargetRectangle scaled_source_rc = ConvertEFBRectangle(source_rc);
+
+ // for msaa mode, we must resolve the efb content to non-msaa
+ GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc);
+ BlitScreen(scaled_source_rc, target_rc, tex, s_target_width, s_target_height);
+}
+
+void Renderer::DrawVirtualXFB(const TargetRectangle& target_rc, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height)
+{
+ for (u32 i = 0; i < xfb_count; ++i)
+ {
+ const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
+
+ TargetRectangle draw_rc;
+ TargetRectangle source_rc;
+ source_rc.left = xfbSource->sourceRc.left;
+ source_rc.right = xfbSource->sourceRc.right;
+ source_rc.top = xfbSource->sourceRc.top;
+ source_rc.bottom = xfbSource->sourceRc.bottom;
+
+ // use virtual xfb with offset
+ int xfbHeight = xfbSource->srcHeight;
+ int xfbWidth = xfbSource->srcWidth;
+ int hOffset = (static_cast<s32>(xfbSource->srcAddr) - static_cast<s32>(xfb_addr)) /
+ (static_cast<s32>(fb_stride) * 2);
+
+ draw_rc.top = target_rc.top - hOffset * target_rc.GetHeight() / static_cast<s32>(fb_height);
+ draw_rc.bottom =
+ target_rc.top - (hOffset + xfbHeight) * target_rc.GetHeight() / static_cast<s32>(fb_height);
+ draw_rc.left =
+ target_rc.left +
+ (target_rc.GetWidth() - xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
+ draw_rc.right =
+ target_rc.left +
+ (target_rc.GetWidth() + xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
+
+ // The following code disables auto stretch. Kept for reference.
+ // scale draw area for a 1 to 1 pixel mapping with the draw target
+ // float h_scale = static_cast<float>(fb_width) / static_cast<float>(target_rc.GetWidth());
+ // float v_scale = static_cast<float>(fb_height) / static_cast<float>(target_rc.GetHeight());
+ // draw_rc.top *= v_scale;
+ // draw_rc.bottom *= v_scale;
+ // draw_rc.left *= h_scale;
+ // draw_rc.right *= h_scale;
+
+ source_rc.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
+
+ BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
+ }
+}
+
+void Renderer::DrawRealXFB(const TargetRectangle& target_rc,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height)
+{
+ for (u32 i = 0; i < xfb_count; ++i)
+ {
+ const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
+
+ TargetRectangle source_rc;
+ source_rc.left = xfbSource->sourceRc.left;
+ source_rc.right = xfbSource->sourceRc.right;
+ source_rc.top = xfbSource->sourceRc.top;
+ source_rc.bottom = xfbSource->sourceRc.bottom;
+
+ source_rc.right -= fb_stride - fb_width;
+
+ // RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a top-
+ // left origin. The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
+ source_rc.top = source_rc.bottom;
+ source_rc.bottom = 0;
+
+ TargetRectangle draw_rc = target_rc;
+ BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
+ }
+}
+
void Renderer::FlushFrameDump()
{
if (!m_last_frame_exported)
@@ -1598,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());
@@ -1637,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()
{