summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/RenderBase.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2017-12-01 14:24:06 +1000
committerGitHub <noreply@github.com>2017-12-01 14:24:06 +1000
commitcd68b3606cea8a7041751bda482170533bae3220 (patch)
tree4edef80bda42a82a50ff9f9e86d64aac38c8aff3 /Source/Core/VideoCommon/RenderBase.cpp
parent0773a48c6c990c488e70ff7c1f46713593440ddc (diff)
parent7f217a8bb24da323042081a02b65f84f9ce9761f (diff)
Merge pull request #6193 from stenzek/readbacks
Abstract Staging Textures - VideoCommon interface for texture readbacks/uploads
Diffstat (limited to 'Source/Core/VideoCommon/RenderBase.cpp')
-rw-r--r--Source/Core/VideoCommon/RenderBase.cpp133
1 files changed, 91 insertions, 42 deletions
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index 2edd6dcfd5..2145c57eeb 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -43,6 +43,7 @@
#include "Core/Movie.h"
#include "VideoCommon/AVIDump.h"
+#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/CPMemory.h"
@@ -100,15 +101,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)
Renderer::~Renderer() = default;
-void Renderer::ExitFramedumping()
-{
- ShutdownFrameDumping();
- if (m_frame_dump_thread.joinable())
- m_frame_dump_thread.join();
-
- m_dump_texture.reset();
-}
-
void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight,
float Gamma)
{
@@ -635,14 +627,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
}
- if (IsFrameDumping() && m_last_xfb_texture)
- {
- FinishFrameData();
- }
- else
- {
- ShutdownFrameDumping();
- }
+ // Ensure the last frame was written to the dump.
+ // This is required even if frame dumping has stopped, since the frame dump is one frame
+ // behind the renderer.
+ FlushFrameDump();
bool update_frame_count = false;
if (xfbAddr && fbWidth && fbStride && fbHeight)
@@ -668,10 +656,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
m_fps_counter.Update();
update_frame_count = true;
+
if (IsFrameDumping())
- {
- DoDumpFrame();
- }
+ DumpCurrentFrame();
}
// Update our last xfb values
@@ -701,20 +688,16 @@ bool Renderer::IsFrameDumping()
return false;
}
-void Renderer::DoDumpFrame()
+void Renderer::DumpCurrentFrame()
{
- UpdateFrameDumpTexture();
+ // Scale/render to frame dump texture.
+ RenderFrameDump();
- auto result = m_dump_texture->Map();
- if (result.has_value())
- {
- auto raw_data = result.value();
- DumpFrameData(raw_data.data, raw_data.width, raw_data.height, raw_data.stride,
- AVIDump::FetchState(m_last_xfb_ticks));
- }
+ // Queue a readback for the next frame.
+ QueueFrameDumpReadback();
}
-void Renderer::UpdateFrameDumpTexture()
+void Renderer::RenderFrameDump()
{
int target_width, target_height;
if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless())
@@ -729,33 +712,99 @@ void Renderer::UpdateFrameDumpTexture()
m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().height);
}
- if (m_dump_texture == nullptr ||
- m_dump_texture->GetConfig().width != static_cast<u32>(target_width) ||
- m_dump_texture->GetConfig().height != static_cast<u32>(target_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 ||
+ m_frame_dump_render_texture->GetConfig().width != static_cast<u32>(target_width) ||
+ m_frame_dump_render_texture->GetConfig().height == static_cast<u32>(target_height))
+ {
+ // Recreate texture objects. Release before creating so we don't temporarily use twice the RAM.
+ TextureConfig config(target_width, target_height, 1, 1, AbstractTextureFormat::RGBA8, true);
+ m_frame_dump_render_texture.reset();
+ m_frame_dump_render_texture = CreateTexture(config);
+ _assert_(m_frame_dump_render_texture);
+ }
+
+ // Scaling is likely to occur here, but if possible, do a bit-for-bit copy.
+ if (m_last_xfb_region.GetWidth() != target_width ||
+ m_last_xfb_region.GetHeight() != target_height)
+ {
+ m_frame_dump_render_texture->ScaleRectangleFromTexture(
+ m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height});
+ }
+ else
+ {
+ m_frame_dump_render_texture->CopyRectangleFromTexture(
+ m_last_xfb_texture, m_last_xfb_region, 0, 0,
+ EFBRectangle{0, 0, target_width, target_height}, 0, 0);
+ }
+}
+
+void Renderer::QueueFrameDumpReadback()
+{
+ // Index 0 was just sent to AVI dump. Swap with the second texture.
+ if (m_frame_dump_readback_textures[0])
+ std::swap(m_frame_dump_readback_textures[0], m_frame_dump_readback_textures[1]);
+
+ std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
+ if (!rbtex || rbtex->GetConfig() != m_frame_dump_render_texture->GetConfig())
{
- TextureConfig config;
- config.width = target_width;
- config.height = target_height;
- config.rendertarget = true;
- m_dump_texture = g_texture_cache->CreateTexture(config);
+ rbtex = CreateStagingTexture(StagingTextureType::Readback,
+ m_frame_dump_render_texture->GetConfig());
}
- m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region,
- EFBRectangle{0, 0, target_width, target_height});
+
+ m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks);
+ m_last_frame_exported = true;
+ rbtex->CopyFromTexture(m_frame_dump_render_texture.get(), 0, 0);
+}
+
+void Renderer::FlushFrameDump()
+{
+ if (!m_last_frame_exported)
+ return;
+
+ // Ensure the previously-queued frame was encoded.
+ FinishFrameData();
+
+ // Queue encoding of the last frame dumped.
+ std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
+ rbtex->Flush();
+ if (rbtex->Map())
+ {
+ DumpFrameData(reinterpret_cast<u8*>(rbtex->GetMappedPointer()), rbtex->GetConfig().width,
+ rbtex->GetConfig().height, static_cast<int>(rbtex->GetMappedStride()),
+ m_last_frame_state);
+ rbtex->Unmap();
+ }
+
+ m_last_frame_exported = false;
+
+ // Shutdown frame dumping if it is no longer active.
+ if (!IsFrameDumping())
+ ShutdownFrameDumping();
}
void Renderer::ShutdownFrameDumping()
{
+ // Ensure the last queued readback has been sent to the encoder.
+ FlushFrameDump();
+
if (!m_frame_dump_thread_running.IsSet())
return;
+ // Ensure previous frame has been encoded.
FinishFrameData();
+
+ // Wake thread up, and wait for it to exit.
m_frame_dump_thread_running.Clear();
m_frame_dump_start.Set();
+ if (m_frame_dump_thread.joinable())
+ m_frame_dump_thread.join();
}
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state)
{
- m_frame_dump_config = FrameDumpConfig{m_last_xfb_texture, data, w, h, stride, state};
+ m_frame_dump_config = FrameDumpConfig{data, w, h, stride, state};
if (!m_frame_dump_thread_running.IsSet())
{
@@ -765,6 +814,7 @@ void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVI
m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this);
}
+ // Wake worker thread up.
m_frame_dump_start.Set();
m_frame_dump_frame_running = true;
}
@@ -776,7 +826,6 @@ void Renderer::FinishFrameData()
m_frame_dump_done.Wait();
m_frame_dump_frame_running = false;
- m_frame_dump_config.texture->Unmap();
}
void Renderer::RunFrameDumps()