summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/RenderBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon/RenderBase.cpp')
-rw-r--r--Source/Core/VideoCommon/RenderBase.cpp459
1 files changed, 374 insertions, 85 deletions
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index 5a5b1c5aba..132ce4bd6c 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -50,14 +50,15 @@
#include "VideoCommon/AbstractFramebuffer.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
+#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/CommandProcessor.h"
-#include "VideoCommon/Debugger.h"
#include "VideoCommon/FPSCounter.h"
-#include "VideoCommon/FramebufferManagerBase.h"
+#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/OnScreenDisplay.h"
+#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/ShaderCache.h"
@@ -68,12 +69,10 @@
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VertexManagerBase.h"
#include "VideoCommon/VertexShaderManager.h"
+#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/XFMemory.h"
-// TODO: Move these out of here.
-int frameCount;
-
std::unique_ptr<Renderer> g_renderer;
static float AspectToWidescreen(float aspect)
@@ -97,7 +96,14 @@ Renderer::~Renderer() = default;
bool Renderer::Initialize()
{
- return InitializeImGui();
+ if (!InitializeImGui())
+ return false;
+
+ m_post_processor = std::make_unique<VideoCommon::PostProcessing>();
+ if (!m_post_processor->Initialize(m_backbuffer_format))
+ return false;
+
+ return true;
}
void Renderer::Shutdown()
@@ -106,6 +112,142 @@ void Renderer::Shutdown()
// can require additional graphics sub-systems so it needs to be done first
ShutdownFrameDumping();
ShutdownImGui();
+ m_post_processor.reset();
+}
+
+void Renderer::BeginUtilityDrawing()
+{
+ g_vertex_manager->Flush();
+}
+
+void Renderer::EndUtilityDrawing()
+{
+ // Reset framebuffer/scissor/viewport. Pipeline will be reset at next draw.
+ g_framebuffer_manager->BindEFBFramebuffer();
+ BPFunctions::SetScissor();
+ BPFunctions::SetViewport();
+}
+
+void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer)
+{
+ m_current_framebuffer = framebuffer;
+}
+
+void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
+{
+ m_current_framebuffer = framebuffer;
+}
+
+void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
+ const ClearColor& color_value, float depth_value)
+{
+ m_current_framebuffer = framebuffer;
+}
+
+std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
+ const std::string& source)
+{
+ return CreateShaderFromSource(stage, source.c_str(), source.size());
+}
+
+void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
+ u32 color, u32 z)
+{
+ g_framebuffer_manager->ClearEFB(rc, colorEnable, alphaEnable, zEnable, color, z);
+}
+
+void Renderer::ReinterpretPixelData(EFBReinterpretType convtype)
+{
+ g_framebuffer_manager->ReinterpretPixelData(convtype);
+}
+
+u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
+{
+ if (type == EFBAccessType::PeekColor)
+ {
+ u32 color = g_framebuffer_manager->PeekEFBColor(x, y);
+
+ // a little-endian value is expected to be returned
+ color = ((color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000));
+
+ // check what to do with the alpha channel (GX_PokeAlphaRead)
+ PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
+
+ if (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24)
+ {
+ color = RGBA8ToRGBA6ToRGBA8(color);
+ }
+ else if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
+ {
+ color = RGBA8ToRGB565ToRGBA8(color);
+ }
+ if (bpmem.zcontrol.pixel_format != PEControl::RGBA6_Z24)
+ {
+ color |= 0xFF000000;
+ }
+
+ if (alpha_read_mode.ReadMode == 2)
+ {
+ return color; // GX_READ_NONE
+ }
+ else if (alpha_read_mode.ReadMode == 1)
+ {
+ return color | 0xFF000000; // GX_READ_FF
+ }
+ else /*if(alpha_read_mode.ReadMode == 0)*/
+ {
+ return color & 0x00FFFFFF; // GX_READ_00
+ }
+ }
+ else // if (type == EFBAccessType::PeekZ)
+ {
+ // Depth buffer is inverted for improved precision near far plane
+ float depth = g_framebuffer_manager->PeekEFBDepth(x, y);
+ if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
+ depth = 1.0f - depth;
+
+ u32 ret = 0;
+ if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
+ {
+ // if Z is in 16 bit format you must return a 16 bit integer
+ ret = MathUtil::Clamp<u32>(static_cast<u32>(depth * 65536.0f), 0, 0xFFFF);
+ }
+ else
+ {
+ ret = MathUtil::Clamp<u32>(static_cast<u32>(depth * 16777216.0f), 0, 0xFFFFFF);
+ }
+
+ return ret;
+ }
+}
+
+void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
+{
+ if (type == EFBAccessType::PokeColor)
+ {
+ for (size_t i = 0; i < num_points; i++)
+ {
+ // Convert to expected format (BGRA->RGBA)
+ // TODO: Check alpha, depending on mode?
+ const EfbPokeData& point = points[i];
+ u32 color = ((point.data & 0xFF00FF00) | ((point.data >> 16) & 0xFF) |
+ ((point.data << 16) & 0xFF0000));
+ g_framebuffer_manager->PokeEFBColor(point.x, point.y, color);
+ }
+ }
+ else // if (type == EFBAccessType::PokeZ)
+ {
+ for (size_t i = 0; i < num_points; i++)
+ {
+ // Convert to floating-point depth.
+ const EfbPokeData& point = points[i];
+ float depth = float(point.data & 0xFFFFFF) / 16777216.0f;
+ if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
+ depth = 1.0f - depth;
+
+ g_framebuffer_manager->PokeEFBDepth(point.x, point.y, depth);
+ }
+ }
}
void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight,
@@ -169,6 +311,8 @@ bool Renderer::CalculateTargetSize()
int new_efb_width = 0;
int new_efb_height = 0;
std::tie(new_efb_width, new_efb_height) = CalculateTargetScale(EFB_WIDTH, EFB_HEIGHT);
+ new_efb_width = std::max(new_efb_width, 1);
+ new_efb_height = std::max(new_efb_height, 1);
if (new_efb_width != m_target_width || new_efb_height != m_target_height)
{
@@ -251,6 +395,11 @@ void Renderer::CheckForConfigChanges()
// Update texture cache settings with any changed options.
g_texture_cache->OnConfigChanged(g_ActiveConfig);
+ // Check for post-processing shader changes. Done up here as it doesn't affect anything outside
+ // the post-processor. Note that options are applied every frame, so no need to check those.
+ if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader)
+ m_post_processor->RecompileShader();
+
// Determine which (if any) settings have changed.
ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent();
u32 changed_bits = 0;
@@ -278,13 +427,30 @@ void Renderer::CheckForConfigChanges()
// Notify the backend of the changes, if any.
OnConfigChanged(changed_bits);
+ // Framebuffer changed?
+ if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE |
+ CONFIG_CHANGE_BIT_TARGET_SIZE))
+ {
+ g_framebuffer_manager->RecreateEFBFramebuffer();
+ }
+
// Reload shaders if host config has changed.
if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES))
{
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
+ WaitForGPUIdle();
SetPipeline(nullptr);
g_vertex_manager->InvalidatePipelineObject();
- g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples);
+ g_shader_cache->SetHostConfig(new_host_config);
+ g_shader_cache->Reload();
+ g_framebuffer_manager->RecompileShaders();
+ }
+
+ // Viewport and scissor rect have to be reset since they will be scaled differently.
+ if (changed_bits & CONFIG_CHANGE_BIT_TARGET_SIZE)
+ {
+ BPFunctions::SetViewport();
+ BPFunctions::SetScissor();
}
}
@@ -389,6 +555,86 @@ void Renderer::ResizeSurface()
m_surface_resized.Set();
}
+void Renderer::SetViewportAndScissor(const MathUtil::Rectangle<int>& rect, float min_depth,
+ float max_depth)
+{
+ SetViewport(static_cast<float>(rect.left), static_cast<float>(rect.top),
+ static_cast<float>(rect.GetWidth()), static_cast<float>(rect.GetHeight()), min_depth,
+ max_depth);
+ SetScissorRect(rect);
+}
+
+void Renderer::ScaleTexture(AbstractFramebuffer* dst_framebuffer,
+ const MathUtil::Rectangle<int>& dst_rect,
+ const AbstractTexture* src_texture,
+ const MathUtil::Rectangle<int>& src_rect)
+{
+ ASSERT(dst_framebuffer->GetColorFormat() == AbstractTextureFormat::RGBA8);
+
+ BeginUtilityDrawing();
+
+ // The shader needs to know the source rectangle.
+ const auto converted_src_rect = g_renderer->ConvertFramebufferRectangle(
+ src_rect, src_texture->GetWidth(), src_texture->GetHeight());
+ const float rcp_src_width = 1.0f / src_texture->GetWidth();
+ const float rcp_src_height = 1.0f / src_texture->GetHeight();
+ const std::array<float, 4> uniforms = {{converted_src_rect.left * rcp_src_width,
+ converted_src_rect.top * rcp_src_height,
+ converted_src_rect.GetWidth() * rcp_src_width,
+ converted_src_rect.GetHeight() * rcp_src_height}};
+ g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms));
+
+ // Discard if we're overwriting the whole thing.
+ if (static_cast<u32>(dst_rect.GetWidth()) == dst_framebuffer->GetWidth() &&
+ static_cast<u32>(dst_rect.GetHeight()) == dst_framebuffer->GetHeight())
+ {
+ SetAndDiscardFramebuffer(dst_framebuffer);
+ }
+ else
+ {
+ SetFramebuffer(dst_framebuffer);
+ }
+
+ SetViewportAndScissor(ConvertFramebufferRectangle(dst_rect, dst_framebuffer));
+ SetPipeline(dst_framebuffer->GetLayers() > 1 ? g_shader_cache->GetRGBA8StereoCopyPipeline() :
+ g_shader_cache->GetRGBA8CopyPipeline());
+ SetTexture(0, src_texture);
+ SetSamplerState(0, RenderState::GetLinearSamplerState());
+ Draw(0, 3);
+ EndUtilityDrawing();
+ if (dst_framebuffer->GetColorAttachment())
+ dst_framebuffer->GetColorAttachment()->FinishedRendering();
+}
+
+MathUtil::Rectangle<int>
+Renderer::ConvertFramebufferRectangle(const MathUtil::Rectangle<int>& rect,
+ const AbstractFramebuffer* framebuffer)
+{
+ return ConvertFramebufferRectangle(rect, framebuffer->GetWidth(), framebuffer->GetHeight());
+}
+
+MathUtil::Rectangle<int> Renderer::ConvertFramebufferRectangle(const MathUtil::Rectangle<int>& rect,
+ u32 fb_width, u32 fb_height)
+{
+ MathUtil::Rectangle<int> ret = rect;
+ if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin)
+ {
+ ret.top = fb_height - rect.bottom;
+ ret.bottom = fb_height - rect.top;
+ }
+ return ret;
+}
+
+TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
+{
+ TargetRectangle result;
+ result.left = EFBToScaledX(rc.left);
+ result.top = EFBToScaledY(rc.top);
+ result.right = EFBToScaledX(rc.right);
+ result.bottom = EFBToScaledY(rc.bottom);
+ return result;
+}
+
std::tuple<float, float> Renderer::ScaleToDisplayAspectRatio(const int width,
const int height) const
{
@@ -700,7 +946,7 @@ bool Renderer::InitializeImGui()
vdecl.texcoords[0] = {VAR_FLOAT, 2, offsetof(ImDrawVert, uv), true, false};
vdecl.colors[0] = {VAR_UNSIGNED_BYTE, 4, offsetof(ImDrawVert, col), true, false};
vdecl.stride = sizeof(ImDrawVert);
- m_imgui_vertex_format = g_vertex_manager->CreateNativeVertexFormat(vdecl);
+ m_imgui_vertex_format = CreateNativeVertexFormat(vdecl);
if (!m_imgui_vertex_format)
{
PanicAlert("Failed to create imgui vertex format");
@@ -723,10 +969,10 @@ bool Renderer::InitializeImGui()
pconfig.vertex_format = m_imgui_vertex_format.get();
pconfig.vertex_shader = vertex_shader.get();
pconfig.pixel_shader = pixel_shader.get();
- pconfig.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex;
- pconfig.rasterization_state.primitive = PrimitiveType::Triangles;
- pconfig.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex;
- pconfig.blending_state.hex = RenderState::GetNoBlendingBlendState().hex;
+ pconfig.rasterization_state =
+ RenderState::GetCullBackFaceRasterizationState(PrimitiveType::Triangles);
+ pconfig.depth_state = RenderState::GetNoDepthTestingDepthState();
+ pconfig.blending_state = RenderState::GetNoBlendingBlendState();
pconfig.blending_state.blendenable = true;
pconfig.blending_state.srcfactor = BlendMode::SRCALPHA;
pconfig.blending_state.dstfactor = BlendMode::INVSRCALPHA;
@@ -752,7 +998,7 @@ bool Renderer::InitializeImGui()
io.Fonts->GetTexDataAsRGBA32(&font_tex_pixels, &font_tex_width, &font_tex_height);
TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1,
- AbstractTextureFormat::RGBA8, false);
+ AbstractTextureFormat::RGBA8, 0);
std::unique_ptr<AbstractTexture> font_tex = CreateTexture(font_tex_config);
if (!font_tex)
{
@@ -799,10 +1045,8 @@ void Renderer::BeginImGuiFrame()
ImGui::NewFrame();
}
-void Renderer::RenderImGui()
+void Renderer::DrawImGui()
{
- ImGui::Render();
-
ImDrawData* draw_data = ImGui::GetDrawData();
if (!draw_data)
return;
@@ -842,9 +1086,11 @@ void Renderer::RenderImGui()
continue;
}
- SetScissorRect(MathUtil::Rectangle<int>(
- static_cast<int>(cmd.ClipRect.x), static_cast<int>(cmd.ClipRect.y),
- static_cast<int>(cmd.ClipRect.z), static_cast<int>(cmd.ClipRect.w)));
+ SetScissorRect(ConvertFramebufferRectangle(
+ MathUtil::Rectangle<int>(
+ static_cast<int>(cmd.ClipRect.x), static_cast<int>(cmd.ClipRect.y),
+ static_cast<int>(cmd.ClipRect.z), static_cast<int>(cmd.ClipRect.w)),
+ m_current_framebuffer));
SetTexture(0, reinterpret_cast<const AbstractTexture*>(cmd.TextureId));
DrawIndexed(base_index, cmd.ElemCount, base_vertex);
base_index += cmd.ElemCount;
@@ -859,7 +1105,10 @@ std::unique_lock<std::mutex> Renderer::GetImGuiLock()
void Renderer::BeginUIFrame()
{
- ResetAPIState();
+ if (IsHeadless())
+ return;
+
+ BeginUtilityDrawing();
BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f});
}
@@ -867,16 +1116,19 @@ void Renderer::EndUIFrame()
{
{
auto lock = GetImGuiLock();
- RenderImGui();
+ ImGui::Render();
}
+ if (!IsHeadless())
{
+ DrawImGui();
+
std::lock_guard<std::mutex> guard(m_swap_mutex);
PresentBackbuffer();
+ EndUtilityDrawing();
}
BeginImGuiFrame();
- RestoreAPIState();
}
void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
@@ -946,33 +1198,35 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
// with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this.
g_vertex_manager->Flush();
- // Render the XFB to the screen.
- ResetAPIState();
- BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f});
- UpdateDrawRectangle();
- RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect);
-
- // Hold the imgui lock while we're presenting.
- // It's only to prevent races on inputs anyway, at this point.
+ // Render any UI elements to the draw list.
{
auto lock = GetImGuiLock();
DrawDebugText();
OSD::DrawMessages();
-
- RenderImGui();
+ ImGui::Render();
}
- // Present to the window system.
+ // Render the XFB to the screen.
+ BeginUtilityDrawing();
+ if (!IsHeadless())
{
- std::lock_guard<std::mutex> guard(m_swap_mutex);
- PresentBackbuffer();
+ BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}});
+ UpdateDrawRectangle();
+ RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect);
+ DrawImGui();
+
+ // Present to the window system.
+ {
+ std::lock_guard<std::mutex> guard(m_swap_mutex);
+ PresentBackbuffer();
+ }
+
+ // Update the window size based on the frame that was just rendered.
+ // Due to depending on guest state, we need to call this every frame.
+ SetWindowSize(texture_config.width, texture_config.height);
}
- // Update the window size based on the frame that was just rendered.
- // Due to depending on guest state, we need to call this every frame.
- SetWindowSize(texture_config.width, texture_config.height);
-
m_fps_counter.Update();
DolphinAnalytics::PerformanceSample perf_sample;
@@ -984,12 +1238,11 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
if (IsFrameDumping())
DumpCurrentFrame();
- frameCount++;
- GFX_DEBUGGER_PAUSE_AT(NEXT_FRAME, true);
-
// Begin new frame
+ m_frame_count++;
stats.ResetFrame();
g_shader_cache->RetrieveAsyncShaders();
+ g_vertex_manager->OnEndFrame();
BeginImGuiFrame();
// We invalidate the pipeline object at the start of the frame.
@@ -1003,13 +1256,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
g_texture_cache->FlushEFBCopies();
// Remove stale EFB/XFB copies.
- g_texture_cache->Cleanup(frameCount);
+ g_texture_cache->Cleanup(m_frame_count);
// Handle any config changes, this gets propogated to the backend.
CheckForConfigChanges();
g_Config.iSaveTargetId = 0;
- RestoreAPIState();
+ EndUtilityDrawing();
Core::Callback_VideoCopiedToXFB(true);
}
@@ -1028,6 +1281,24 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
}
}
+void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc)
+{
+ const auto target_rc = GetTargetRectangle();
+ if (g_ActiveConfig.stereo_mode == StereoMode::SBS ||
+ g_ActiveConfig.stereo_mode == StereoMode::TAB)
+ {
+ TargetRectangle left_rc, right_rc;
+ std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc);
+
+ m_post_processor->BlitFromTexture(left_rc, rc, texture, 0);
+ m_post_processor->BlitFromTexture(right_rc, rc, texture, 1);
+ }
+ else
+ {
+ m_post_processor->BlitFromTexture(target_rc, rc, texture, 0);
+ }
+}
+
bool Renderer::IsFrameDumping()
{
if (m_screenshot_request.IsSet())
@@ -1041,15 +1312,6 @@ bool Renderer::IsFrameDumping()
void Renderer::DumpCurrentFrame()
{
- // Scale/render to frame dump texture.
- RenderFrameDump();
-
- // Queue a readback for the next frame.
- QueueFrameDumpReadback();
-}
-
-void Renderer::RenderFrameDump()
-{
int target_width, target_height;
if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless())
{
@@ -1063,50 +1325,76 @@ void Renderer::RenderFrameDump()
m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().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))
+ // We only need to render a copy if we need to stretch/scale the XFB copy.
+ const AbstractTexture* source_tex = m_last_xfb_texture;
+ MathUtil::Rectangle<int> source_rect = m_last_xfb_region;
+ if (source_rect.GetWidth() != target_width || source_rect.GetHeight() != 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, 1, AbstractTextureFormat::RGBA8, true);
- m_frame_dump_render_texture.reset();
- m_frame_dump_render_texture = CreateTexture(config);
- ASSERT(m_frame_dump_render_texture);
+ if (!CheckFrameDumpRenderTexture(target_width, target_height))
+ return;
+
+ source_tex = m_frame_dump_render_texture.get();
+ source_rect = MathUtil::Rectangle<int>(0, 0, target_width, target_height);
+ ScaleTexture(m_frame_dump_render_framebuffer.get(), source_rect, m_last_xfb_texture,
+ m_last_xfb_region);
}
- // 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)
+ // 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]);
+
+ if (!CheckFrameDumpReadbackTexture(target_width, target_height))
+ return;
+
+ const auto converted_region =
+ ConvertFramebufferRectangle(source_rect, source_tex->GetWidth(), source_tex->GetHeight());
+ m_frame_dump_readback_textures[0]->CopyFromTexture(
+ source_tex, converted_region, 0, 0,
+ MathUtil::Rectangle<int>(0, 0, target_width, target_height));
+ m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks);
+ m_last_frame_exported = true;
+}
+
+bool Renderer::CheckFrameDumpRenderTexture(u32 target_width, 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->GetWidth() == target_width &&
+ m_frame_dump_render_texture->GetHeight() == target_height)
{
- m_frame_dump_render_texture->ScaleRectangleFromTexture(
- m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height});
+ return true;
}
- else
+
+ // Recreate texture, but release before creating so we don't temporarily use twice the RAM.
+ m_frame_dump_render_framebuffer.reset();
+ m_frame_dump_render_texture.reset();
+ m_frame_dump_render_texture =
+ CreateTexture(TextureConfig(target_width, target_height, 1, 1, 1,
+ AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget));
+ if (!m_frame_dump_render_texture)
{
- 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);
+ PanicAlert("Failed to allocate frame dump render texture");
+ return false;
}
+ m_frame_dump_render_framebuffer = CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr);
+ ASSERT(m_frame_dump_render_framebuffer);
+ return true;
}
-void Renderer::QueueFrameDumpReadback()
+bool Renderer::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height)
{
- // 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())
- {
- rbtex = CreateStagingTexture(StagingTextureType::Readback,
- m_frame_dump_render_texture->GetConfig());
- }
+ if (rbtex && rbtex->GetWidth() == target_width && rbtex->GetHeight() == target_height)
+ return true;
- 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);
+ rbtex.reset();
+ rbtex = CreateStagingTexture(
+ StagingTextureType::Readback,
+ TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0));
+ if (!rbtex)
+ return false;
+
+ return true;
}
void Renderer::FlushFrameDump()
@@ -1151,6 +1439,7 @@ void Renderer::ShutdownFrameDumping()
m_frame_dump_start.Set();
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
+ m_frame_dump_render_framebuffer.reset();
m_frame_dump_render_texture.reset();
for (auto& tex : m_frame_dump_readback_textures)
tex.reset();