diff options
| author | Stenzek <stenzek@gmail.com> | 2019-02-15 11:59:50 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2019-02-19 16:57:54 +1000 |
| commit | f039149198657c1891e1c6462ed30c31ed4b8486 (patch) | |
| tree | b914fd7b0ef9c937e9334a880a49a26a26f17c8f /Source/Core/VideoCommon/RenderBase.cpp | |
| parent | 933f3ba008942cba96ed623c25e61d6ea86582fe (diff) | |
Move most backend functionality to VideoCommon
Diffstat (limited to 'Source/Core/VideoCommon/RenderBase.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/RenderBase.cpp | 459 |
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(); |
