Move Presenting, Dumping and ImGui out of Renderer

This commit is contained in:
Scott Mansell
2023-01-31 18:45:21 +13:00
parent c38c76abad
commit 0d4537d60f
29 changed files with 1766 additions and 1394 deletions
+6 -5
View File
@@ -59,6 +59,7 @@
#include "UICommon/UICommon.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoBackendBase.h"
@@ -456,8 +457,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang
if (s_surf == nullptr)
__android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "Error: Surface is null.");
if (g_renderer)
g_renderer->ChangeSurface(s_surf);
if (g_presenter)
g_presenter->ChangeSurface(s_surf);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*,
@@ -483,8 +484,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
std::lock_guard surface_guard(s_surface_lock);
if (g_renderer)
g_renderer->ChangeSurface(nullptr);
if (g_presenter)
g_presenter->ChangeSurface(nullptr);
if (s_surf)
{
@@ -503,7 +504,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_HasSurfa
JNIEXPORT jfloat JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameAspectRatio(JNIEnv*,
jclass)
{
return g_renderer->CalculateDrawAspectRatio();
return g_presenter->CalculateDrawAspectRatio();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv*, jclass)
+6 -4
View File
@@ -84,9 +84,11 @@
#include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/FrameDumper.h"
#include "VideoCommon/HiresTextures.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PerformanceMetrics.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoBackendBase.h"
@@ -540,8 +542,8 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
// Render a single frame without anything on it to clear the screen.
// This avoids the game list being displayed while the core is finishing initializing.
g_renderer->BeginUIFrame();
g_renderer->EndUIFrame();
g_presenter->BeginUIFrame();
g_presenter->EndUIFrame();
if (cpu_info.HTT)
Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4);
@@ -731,13 +733,13 @@ static std::string GenerateScreenshotName()
void SaveScreenShot()
{
Core::RunAsCPUThread([] { g_renderer->SaveScreenshot(GenerateScreenshotName()); });
Core::RunAsCPUThread([] { g_frame_dumper->SaveScreenshot(GenerateScreenshotName()); });
}
void SaveScreenShot(std::string_view name)
{
Core::RunAsCPUThread([&name] {
g_renderer->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name));
g_frame_dumper->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name));
});
}
+6
View File
@@ -639,6 +639,7 @@
<ClInclude Include="VideoCommon\FramebufferManager.h" />
<ClInclude Include="VideoCommon\FramebufferShaderGen.h" />
<ClInclude Include="VideoCommon\FrameDump.h" />
<ClInclude Include="VideoCommon\FrameDumper.h" />
<ClInclude Include="VideoCommon\FreeLookCamera.h" />
<ClInclude Include="VideoCommon\GeometryShaderGen.h" />
<ClInclude Include="VideoCommon\GeometryShaderManager.h" />
@@ -668,6 +669,7 @@
<ClInclude Include="VideoCommon\NetPlayChatUI.h" />
<ClInclude Include="VideoCommon\NetPlayGolfUI.h" />
<ClInclude Include="VideoCommon\OnScreenDisplay.h" />
<ClInclude Include="VideoCommon\OnScreenUI.h" />
<ClInclude Include="VideoCommon\OpcodeDecoding.h" />
<ClInclude Include="VideoCommon\PerfQueryBase.h" />
<ClInclude Include="VideoCommon\PerformanceMetrics.h" />
@@ -676,6 +678,7 @@
<ClInclude Include="VideoCommon\PixelShaderGen.h" />
<ClInclude Include="VideoCommon\PixelShaderManager.h" />
<ClInclude Include="VideoCommon\PostProcessing.h" />
<ClInclude Include="VideoCommon\Present.h" />
<ClInclude Include="VideoCommon\RenderBase.h" />
<ClInclude Include="VideoCommon\RenderState.h" />
<ClInclude Include="VideoCommon\ShaderCache.h" />
@@ -1232,6 +1235,7 @@
<ClCompile Include="VideoCommon\FramebufferManager.cpp" />
<ClCompile Include="VideoCommon\FramebufferShaderGen.cpp" />
<ClCompile Include="VideoCommon\FrameDump.cpp" />
<ClCompile Include="VideoCommon\FrameDumper.cpp" />
<ClCompile Include="VideoCommon\FreeLookCamera.cpp" />
<ClCompile Include="VideoCommon\GeometryShaderGen.cpp" />
<ClCompile Include="VideoCommon\GeometryShaderManager.cpp" />
@@ -1254,6 +1258,7 @@
<ClCompile Include="VideoCommon\NetPlayChatUI.cpp" />
<ClCompile Include="VideoCommon\NetPlayGolfUI.cpp" />
<ClCompile Include="VideoCommon\OnScreenDisplay.cpp" />
<ClCompile Include="VideoCommon\OnScreenUI.cpp" />
<ClCompile Include="VideoCommon\OpcodeDecoding.cpp" />
<ClCompile Include="VideoCommon\PerfQueryBase.cpp" />
<ClCompile Include="VideoCommon\PerformanceMetrics.cpp" />
@@ -1262,6 +1267,7 @@
<ClCompile Include="VideoCommon\PixelShaderGen.cpp" />
<ClCompile Include="VideoCommon\PixelShaderManager.cpp" />
<ClCompile Include="VideoCommon\PostProcessing.cpp" />
<ClCompile Include="VideoCommon\Present.cpp" />
<ClCompile Include="VideoCommon\RenderBase.cpp" />
<ClCompile Include="VideoCommon\RenderState.cpp" />
<ClCompile Include="VideoCommon\ShaderCache.cpp" />
+3 -3
View File
@@ -13,7 +13,7 @@
#include <climits>
#include <cstdio>
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/Present.h"
#include "resource.h"
namespace
@@ -181,8 +181,8 @@ LRESULT PlatformWin32::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam
case WM_SIZE:
{
if (g_renderer)
g_renderer->ResizeSurface();
if (g_presenter)
g_presenter->ResizeSurface();
}
break;
+3 -3
View File
@@ -25,7 +25,7 @@ static constexpr auto X_None = None;
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include "UICommon/X11Utils.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/Present.h"
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
@@ -263,8 +263,8 @@ void PlatformX11::ProcessEvents()
break;
case ConfigureNotify:
{
if (g_renderer)
g_renderer->ResizeSurface();
if (g_presenter)
g_presenter->ResizeSurface();
}
break;
}
@@ -21,7 +21,7 @@
#include "DolphinQt/Config/Graphics/EnhancementsWidget.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoConfig.h"
using ConfigurationOption = VideoCommon::PostProcessingConfiguration::ConfigurationOption;
@@ -31,9 +31,9 @@ PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* paren
const std::string& shader)
: QDialog(parent), m_shader(shader)
{
if (g_renderer && g_renderer->GetPostProcessor())
if (g_presenter && g_presenter->GetPostProcessor())
{
m_post_processor = g_renderer->GetPostProcessor()->GetConfig();
m_post_processor = g_presenter->GetPostProcessor()->GetConfig();
}
else
{
@@ -52,7 +52,7 @@ PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* paren
PostProcessingConfigWindow::~PostProcessingConfigWindow()
{
m_post_processor->SaveOptionsConfiguration();
if (!(g_renderer && g_renderer->GetPostProcessor()))
if (!(g_presenter && g_presenter->GetPostProcessor()))
{
delete m_post_processor;
}
+5 -4
View File
@@ -37,6 +37,7 @@
#include "UICommon/DiscordPresence.h"
#include "VideoCommon/Fifo.cpp"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoConfig.h"
@@ -76,9 +77,9 @@ void Host::SetRenderHandle(void* handle)
return;
m_render_handle = handle;
if (g_renderer)
if (g_presenter)
{
g_renderer->ChangeSurface(handle);
g_presenter->ChangeSurface(handle);
g_controller_interface.ChangeWindow(handle);
}
}
@@ -190,8 +191,8 @@ void Host::SetRenderFullscreen(bool fullscreen)
void Host::ResizeSurface(int new_width, int new_height)
{
if (g_renderer)
g_renderer->ResizeSurface();
if (g_presenter)
g_presenter->ResizeSurface();
}
std::vector<std::string> Host_GetPreferredLocales()
+16 -19
View File
@@ -32,7 +32,7 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoConfig.h"
#ifdef _WIN32
@@ -487,38 +487,40 @@ void RenderWidget::PassEventToImGui(const QEvent* event)
const QKeyEvent* key_event = static_cast<const QKeyEvent*>(event);
const bool is_down = event->type() == QEvent::KeyPress;
const u32 key = static_cast<u32>(key_event->key() & 0x1FF);
auto lock = g_renderer->GetImGuiLock();
if (key < std::size(ImGui::GetIO().KeysDown))
ImGui::GetIO().KeysDown[key] = is_down;
const char* chars = nullptr;
if (is_down)
{
auto utf8 = key_event->text().toUtf8();
ImGui::GetIO().AddInputCharactersUTF8(utf8.constData());
if (utf8.size())
chars = utf8.constData();
}
// Pass the key onto ImGui
g_presenter->SetKey(key, is_down, chars);
}
break;
case QEvent::MouseMove:
{
auto lock = g_renderer->GetImGuiLock();
// Qt multiplies all coordinates by the scaling factor in highdpi mode, giving us "scaled" mouse
// coordinates (as if the screen was standard dpi). We need to update the mouse position in
// native coordinates, as the UI (and game) is rendered at native resolution.
const float scale = devicePixelRatio();
ImGui::GetIO().MousePos.x = static_cast<const QMouseEvent*>(event)->pos().x() * scale;
ImGui::GetIO().MousePos.y = static_cast<const QMouseEvent*>(event)->pos().y() * scale;
float x = static_cast<const QMouseEvent*>(event)->pos().x() * scale;
float y = static_cast<const QMouseEvent*>(event)->pos().y() * scale;
g_presenter->SetMousePos(x, y);
}
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
{
auto lock = g_renderer->GetImGuiLock();
const u32 button_mask = static_cast<u32>(static_cast<const QMouseEvent*>(event)->buttons());
for (size_t i = 0; i < std::size(ImGui::GetIO().MouseDown); i++)
ImGui::GetIO().MouseDown[i] = (button_mask & (1u << i)) != 0;
g_presenter->SetMousePress(button_mask);
}
break;
@@ -529,7 +531,7 @@ void RenderWidget::PassEventToImGui(const QEvent* event)
void RenderWidget::SetImGuiKeyMap()
{
static constexpr std::array<std::array<int, 2>, 21> key_map{{
static std::array<std::array<int, 2>, 21> key_map{{
{ImGuiKey_Tab, Qt::Key_Tab},
{ImGuiKey_LeftArrow, Qt::Key_Left},
{ImGuiKey_RightArrow, Qt::Key_Right},
@@ -552,11 +554,6 @@ void RenderWidget::SetImGuiKeyMap()
{ImGuiKey_Y, Qt::Key_Y},
{ImGuiKey_Z, Qt::Key_Z},
}};
auto lock = g_renderer->GetImGuiLock();
if (!ImGui::GetCurrentContext())
return;
for (auto [imgui_key, qt_key] : key_map)
ImGui::GetIO().KeyMap[imgui_key] = (qt_key & 0x1FF);
g_presenter->SetKeyMap(key_map);
}
+4 -4
View File
@@ -30,6 +30,7 @@
#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/XFMemory.h"
@@ -178,16 +179,15 @@ void Renderer::OnConfigChanged(u32 bits)
void Renderer::CheckForSwapChainChanges()
{
const bool surface_changed = m_surface_changed.TestAndClear();
const bool surface_changed = g_presenter->SurfaceChangedTestAndClear();
const bool surface_resized =
m_surface_resized.TestAndClear() || m_swap_chain->CheckForFullscreenChange();
g_presenter->SurfaceResizedTestAndClear() || m_swap_chain->CheckForFullscreenChange();
if (!surface_changed && !surface_resized)
return;
if (surface_changed)
{
m_swap_chain->ChangeSurface(m_new_surface_handle);
m_new_surface_handle = nullptr;
m_swap_chain->ChangeSurface(g_presenter->GetNewSurfaceHandle());
}
else
{
@@ -15,6 +15,7 @@
#include "VideoBackends/D3D12/DX12Texture.h"
#include "VideoBackends/D3D12/DX12VertexFormat.h"
#include "VideoBackends/D3D12/DescriptorHeapManager.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
@@ -419,9 +420,9 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color)
void Renderer::CheckForSwapChainChanges()
{
const bool surface_changed = m_surface_changed.TestAndClear();
const bool surface_changed = g_presenter->SurfaceChangedTestAndClear();
const bool surface_resized =
m_surface_resized.TestAndClear() || m_swap_chain->CheckForFullscreenChange();
g_presenter->SurfaceResizedTestAndClear() || m_swap_chain->CheckForFullscreenChange();
if (!surface_changed && !surface_resized)
return;
@@ -429,8 +430,7 @@ void Renderer::CheckForSwapChainChanges()
WaitForGPUIdle();
if (surface_changed)
{
m_swap_chain->ChangeSurface(m_new_surface_handle);
m_new_surface_handle = nullptr;
m_swap_chain->ChangeSurface(g_presenter->GetNewSurfaceHandle());
}
else
{
@@ -13,6 +13,7 @@
#include "VideoBackends/Metal/MTLVertexManager.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoBackendBase.h"
Metal::Renderer::Renderer(MRCOwned<CAMetalLayer*> layer, int width, int height, float layer_scale)
@@ -480,16 +481,15 @@ std::unique_ptr<::BoundingBox> Metal::Renderer::CreateBoundingBox() const
void Metal::Renderer::CheckForSurfaceChange()
{
if (!m_surface_changed.TestAndClear())
if (!g_presenter->SurfaceChangedTestAndClear())
return;
m_layer = MRCRetain(static_cast<CAMetalLayer*>(m_new_surface_handle));
m_new_surface_handle = nullptr;
m_layer = MRCRetain(static_cast<CAMetalLayer*>(g_presenter->GetNewSurfaceHandle()));
SetupSurface();
}
void Metal::Renderer::CheckForSurfaceResize()
{
if (!m_surface_resized.TestAndClear())
if (!g_presenter->SurfaceResizedTestAndClear())
return;
SetupSurface();
}
+14 -11
View File
@@ -32,6 +32,7 @@
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
@@ -973,7 +974,7 @@ void Renderer::SelectRightBuffer()
glDrawBuffer(GL_BACK_RIGHT);
}
void Renderer::SelectMainBuffer()
void Renderer::SelectMainBuffer()
{
glDrawBuffer(GL_BACK);
}
@@ -1074,27 +1075,29 @@ void Renderer::WaitForGPUIdle()
void Renderer::CheckForSurfaceChange()
{
if (!m_surface_changed.TestAndClear())
if (!g_presenter->SurfaceChangedTestAndClear())
return;
m_main_gl_context->UpdateSurface(m_new_surface_handle);
m_new_surface_handle = nullptr;
m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle());
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
// With a surface change, the window likely has new dimensions.
m_backbuffer_width = m_main_gl_context->GetBackBufferWidth();
m_backbuffer_height = m_main_gl_context->GetBackBufferHeight();
m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height);
g_presenter->SetBackbuffer(width, height);
m_system_framebuffer->UpdateDimensions(width, height);
}
void Renderer::CheckForSurfaceResize()
{
if (!m_surface_resized.TestAndClear())
if (!g_presenter->SurfaceResizedTestAndClear())
return;
m_main_gl_context->Update();
m_backbuffer_width = m_main_gl_context->GetBackBufferWidth();
m_backbuffer_height = m_main_gl_context->GetBackBufferHeight();
m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height);
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
g_presenter->SetBackbuffer(width, height);
m_system_framebuffer->UpdateDimensions(width, height);
}
void Renderer::BeginUtilityDrawing()
@@ -23,6 +23,7 @@
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoCommon.h"
@@ -63,13 +64,12 @@ SWRenderer::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture
void SWRenderer::BindBackbuffer(const ClearColor& clear_color)
{
// Look for framebuffer resizes
if (!m_surface_resized.TestAndClear())
if (!g_presenter->SurfaceResizedTestAndClear())
return;
GLContext* context = m_window->GetContext();
context->Update();
m_backbuffer_width = context->GetBackBufferWidth();
m_backbuffer_height = context->GetBackBufferHeight();
g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight());
}
class SWShader final : public AbstractShader
@@ -33,6 +33,7 @@
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/VertexManagerBase.h"
#include "VideoCommon/VideoBackendBase.h"
@@ -379,7 +380,7 @@ void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_comple
void Renderer::CheckForSurfaceChange()
{
if (!m_surface_changed.TestAndClear() || !m_swap_chain)
if (!g_presenter->SurfaceChangedTestAndClear() || !m_swap_chain)
return;
// Submit the current draws up until rendering the XFB.
@@ -389,9 +390,8 @@ void Renderer::CheckForSurfaceChange()
g_command_buffer_mgr->CheckLastPresentFail();
// Recreate the surface. If this fails we're in trouble.
if (!m_swap_chain->RecreateSurface(m_new_surface_handle))
if (!m_swap_chain->RecreateSurface(g_presenter->GetNewSurfaceHandle()))
PanicAlertFmt("Failed to recreate Vulkan surface. Cannot continue.");
m_new_surface_handle = nullptr;
// Handle case where the dimensions are now different.
OnSwapChainResized();
@@ -399,7 +399,7 @@ void Renderer::CheckForSurfaceChange()
void Renderer::CheckForSurfaceResize()
{
if (!m_surface_resized.TestAndClear())
if (!g_presenter->SurfaceResizedTestAndClear())
return;
// If we don't have a surface, how can we resize the swap chain?
@@ -450,8 +450,7 @@ void Renderer::OnConfigChanged(u32 bits)
void Renderer::OnSwapChainResized()
{
m_backbuffer_width = m_swap_chain->GetWidth();
m_backbuffer_height = m_swap_chain->GetHeight();
g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
}
void Renderer::BindFramebuffer(VKFramebuffer* fb)
@@ -15,7 +15,7 @@
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/VKTexture.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/Present.h"
#if defined(VK_USE_PLATFORM_XLIB_KHR)
#include <X11/Xlib.h>
@@ -265,8 +265,8 @@ bool SwapChain::CreateSwapChain()
VkExtent2D size = surface_capabilities.currentExtent;
if (size.width == UINT32_MAX)
{
size.width = std::max(g_renderer->GetBackbufferWidth(), 1);
size.height = std::max(g_renderer->GetBackbufferHeight(), 1);
size.width = std::max(g_presenter->GetBackbufferWidth(), 1);
size.height = std::max(g_presenter->GetBackbufferHeight(), 1);
}
size.width = std::clamp(size.width, surface_capabilities.minImageExtent.width,
surface_capabilities.maxImageExtent.width);
+6
View File
@@ -34,6 +34,8 @@ add_library(videocommon
FramebufferManager.h
FramebufferShaderGen.cpp
FramebufferShaderGen.h
FrameDumper.cpp
FrameDumper.h
FreeLookCamera.cpp
FreeLookCamera.h
GeometryShaderGen.cpp
@@ -82,6 +84,8 @@ add_library(videocommon
NetPlayGolfUI.h
OnScreenDisplay.cpp
OnScreenDisplay.h
OnScreenUI.cpp
OnScreenUI.h
OpcodeDecoding.cpp
OpcodeDecoding.h
PerfQueryBase.cpp
@@ -98,6 +102,8 @@ add_library(videocommon
PixelShaderManager.h
PostProcessing.cpp
PostProcessing.h
Present.cpp
Present.h
RenderBase.cpp
RenderBase.h
RenderState.cpp
+15 -14
View File
@@ -37,6 +37,7 @@ extern "C" {
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "VideoCommon/FrameDumper.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
@@ -157,7 +158,7 @@ std::string AVErrorString(int error)
} // namespace
bool FrameDump::Start(int w, int h, u64 start_ticks)
bool FFMpegFrameDump::Start(int w, int h, u64 start_ticks)
{
if (IsStarted())
return true;
@@ -169,7 +170,7 @@ bool FrameDump::Start(int w, int h, u64 start_ticks)
return PrepareEncoding(w, h, start_ticks, m_savestate_index);
}
bool FrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_index)
bool FFMpegFrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_index)
{
m_context = std::make_unique<FrameDumpContext>();
@@ -189,7 +190,7 @@ bool FrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_ind
return success;
}
bool FrameDump::CreateVideoFile()
bool FFMpegFrameDump::CreateVideoFile()
{
const std::string& format = g_Config.sDumpFormat;
@@ -335,12 +336,12 @@ bool FrameDump::CreateVideoFile()
return true;
}
bool FrameDump::IsFirstFrameInCurrentFile() const
bool FFMpegFrameDump::IsFirstFrameInCurrentFile() const
{
return m_context->last_pts == AV_NOPTS_VALUE;
}
void FrameDump::AddFrame(const FrameData& frame)
void FFMpegFrameDump::AddFrame(const FrameData& frame)
{
// Are we even dumping?
if (!IsStarted())
@@ -402,7 +403,7 @@ void FrameDump::AddFrame(const FrameData& frame)
ProcessPackets();
}
void FrameDump::ProcessPackets()
void FFMpegFrameDump::ProcessPackets()
{
auto pkt = std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>(
av_packet_alloc(), [](AVPacket* packet) { av_packet_free(&packet); });
@@ -440,7 +441,7 @@ void FrameDump::ProcessPackets()
}
}
void FrameDump::Stop()
void FFMpegFrameDump::Stop()
{
if (!IsStarted())
return;
@@ -457,12 +458,12 @@ void FrameDump::Stop()
OSD::AddMessage("Stopped dumping frames");
}
bool FrameDump::IsStarted() const
bool FFMpegFrameDump::IsStarted() const
{
return m_context != nullptr;
}
void FrameDump::CloseVideoFile()
void FFMpegFrameDump::CloseVideoFile()
{
av_frame_free(&m_context->src_frame);
av_frame_free(&m_context->scaled_frame);
@@ -480,13 +481,13 @@ void FrameDump::CloseVideoFile()
m_context.reset();
}
void FrameDump::DoState(PointerWrap& p)
void FFMpegFrameDump::DoState(PointerWrap& p)
{
if (p.IsReadMode())
++m_savestate_index;
}
void FrameDump::CheckForConfigChange(const FrameData& frame)
void FFMpegFrameDump::CheckForConfigChange(const FrameData& frame)
{
bool restart_dump = false;
@@ -524,7 +525,7 @@ void FrameDump::CheckForConfigChange(const FrameData& frame)
}
}
FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const
FrameState FFMpegFrameDump::FetchState(u64 ticks, int frame_number) const
{
FrameState state;
state.ticks = ticks;
@@ -537,9 +538,9 @@ FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const
return state;
}
FrameDump::FrameDump() = default;
FFMpegFrameDump::FFMpegFrameDump() = default;
FrameDump::~FrameDump()
FFMpegFrameDump::~FFMpegFrameDump()
{
Stop();
}
+6 -24
View File
@@ -10,32 +10,14 @@
struct FrameDumpContext;
class PointerWrap;
struct FrameData;
struct FrameState;
class FrameDump
class FFMpegFrameDump
{
public:
FrameDump();
~FrameDump();
// Holds relevant emulation state during a rendered frame for
// when it is later asynchronously written.
struct FrameState
{
u64 ticks = 0;
int frame_number = 0;
u32 savestate_index = 0;
int refresh_rate_num = 0;
int refresh_rate_den = 0;
};
struct FrameData
{
const u8* data = nullptr;
int width = 0;
int height = 0;
int stride = 0;
FrameState state;
};
FFMpegFrameDump();
~FFMpegFrameDump();
bool Start(int w, int h, u64 start_ticks);
void AddFrame(const FrameData&);
@@ -68,7 +50,7 @@ private:
inline FrameDump::FrameDump() = default;
inline FrameDump::~FrameDump() = default;
inline FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const
inline FrameState FrameDump::FetchState(u64 ticks, int frame_number) const
{
return {};
}
+354
View File
@@ -0,0 +1,354 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/FrameDumper.h"
#include "Common/Assert.h"
#include "Common/FileUtil.h"
#include "Common/Image.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "VideoCommon/AbstractFramebuffer.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoConfig.h"
static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name)
{
return Common::ConvertRGBAToRGBAndSavePNG(file_name, frame.data, frame.width, frame.height,
frame.stride,
Config::Get(Config::GFX_PNG_COMPRESSION_LEVEL));
}
FrameDumper::FrameDumper()
{
}
FrameDumper::~FrameDumper()
{
ShutdownFrameDumping();
}
void FrameDumper::DumpCurrentFrame(const AbstractTexture* src_texture,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& target_rect, u64 ticks,
int frame_number)
{
int source_width = src_rect.GetWidth();
int source_height = src_rect.GetHeight();
int target_width = target_rect.GetWidth();
int target_height = target_rect.GetHeight();
// We only need to render a copy if we need to stretch/scale the XFB copy.
MathUtil::Rectangle<int> copy_rect = src_rect;
if (source_width != target_width || source_height != target_height)
{
if (!CheckFrameDumpRenderTexture(target_width, target_height))
return;
g_renderer->ScaleTexture(m_frame_dump_render_framebuffer.get(),
m_frame_dump_render_framebuffer->GetRect(), src_texture, src_rect);
src_texture = m_frame_dump_render_texture.get();
copy_rect = src_texture->GetRect();
}
if (!CheckFrameDumpReadbackTexture(target_width, target_height))
return;
m_frame_dump_readback_texture->CopyFromTexture(src_texture, copy_rect, 0, 0,
m_frame_dump_readback_texture->GetRect());
m_last_frame_state = m_ffmpeg_dump.FetchState(ticks, frame_number);
m_frame_dump_needs_flush = true;
}
bool FrameDumper::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)
{
return true;
}
// 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 = g_renderer->CreateTexture(
TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8,
AbstractTextureFlag_RenderTarget),
"Frame dump render texture");
if (!m_frame_dump_render_texture)
{
PanicAlertFmt("Failed to allocate frame dump render texture");
return false;
}
m_frame_dump_render_framebuffer =
g_renderer->CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr);
ASSERT(m_frame_dump_render_framebuffer);
return true;
}
bool FrameDumper::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height)
{
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_texture;
if (rbtex && rbtex->GetWidth() == target_width && rbtex->GetHeight() == target_height)
return true;
rbtex.reset();
rbtex = g_renderer->CreateStagingTexture(
StagingTextureType::Readback,
TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0));
if (!rbtex)
return false;
return true;
}
void FrameDumper::FlushFrameDump()
{
if (!m_frame_dump_needs_flush)
return;
// Ensure dumping thread is done with output texture before swapping.
FinishFrameData();
std::swap(m_frame_dump_output_texture, m_frame_dump_readback_texture);
// Queue encoding of the last frame dumped.
auto& output = m_frame_dump_output_texture;
output->Flush();
if (output->Map())
{
DumpFrameData(reinterpret_cast<u8*>(output->GetMappedPointer()), output->GetConfig().width,
output->GetConfig().height, static_cast<int>(output->GetMappedStride()));
}
else
{
ERROR_LOG_FMT(VIDEO, "Failed to map texture for dumping.");
}
m_frame_dump_needs_flush = false;
// Shutdown frame dumping if it is no longer active.
if (!IsFrameDumping())
ShutdownFrameDumping();
}
void FrameDumper::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();
m_frame_dump_render_framebuffer.reset();
m_frame_dump_render_texture.reset();
m_frame_dump_readback_texture.reset();
m_frame_dump_output_texture.reset();
}
void FrameDumper::DumpFrameData(const u8* data, int w, int h, int stride)
{
m_frame_dump_data = FrameData{data, w, h, stride, m_last_frame_state};
if (!m_frame_dump_thread_running.IsSet())
{
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
m_frame_dump_thread_running.Set();
m_frame_dump_thread = std::thread(&FrameDumper::FrameDumpThreadFunc, this);
}
// Wake worker thread up.
m_frame_dump_start.Set();
m_frame_dump_frame_running = true;
}
void FrameDumper::FinishFrameData()
{
if (!m_frame_dump_frame_running)
return;
m_frame_dump_done.Wait();
m_frame_dump_frame_running = false;
m_frame_dump_output_texture->Unmap();
}
void FrameDumper::FrameDumpThreadFunc()
{
Common::SetCurrentThreadName("FrameDumping");
bool dump_to_ffmpeg = !g_ActiveConfig.bDumpFramesAsImages;
bool frame_dump_started = false;
// If Dolphin was compiled without ffmpeg, we only support dumping to images.
#if !defined(HAVE_FFMPEG)
if (dump_to_ffmpeg)
{
WARN_LOG_FMT(VIDEO, "FrameDump: Dolphin was not compiled with FFmpeg, using fallback option. "
"Frames will be saved as PNG images instead.");
dump_to_ffmpeg = false;
}
#endif
while (true)
{
m_frame_dump_start.Wait();
if (!m_frame_dump_thread_running.IsSet())
break;
auto frame = m_frame_dump_data;
// Save screenshot
if (m_screenshot_request.TestAndClear())
{
std::lock_guard<std::mutex> lk(m_screenshot_lock);
if (DumpFrameToPNG(frame, m_screenshot_name))
OSD::AddMessage("Screenshot saved to " + m_screenshot_name);
// Reset settings
m_screenshot_name.clear();
m_screenshot_completed.Set();
}
if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES))
{
if (!frame_dump_started)
{
if (dump_to_ffmpeg)
frame_dump_started = StartFrameDumpToFFMPEG(frame);
else
frame_dump_started = StartFrameDumpToImage(frame);
// Stop frame dumping if we fail to start.
if (!frame_dump_started)
Config::SetCurrent(Config::MAIN_MOVIE_DUMP_FRAMES, false);
}
// If we failed to start frame dumping, don't write a frame.
if (frame_dump_started)
{
if (dump_to_ffmpeg)
DumpFrameToFFMPEG(frame);
else
DumpFrameToImage(frame);
}
}
m_frame_dump_done.Set();
}
if (frame_dump_started)
{
// No additional cleanup is needed when dumping to images.
if (dump_to_ffmpeg)
StopFrameDumpToFFMPEG();
}
}
#if defined(HAVE_FFMPEG)
bool FrameDumper::StartFrameDumpToFFMPEG(const FrameData& frame)
{
// If dumping started at boot, the start time must be set to the boot time to maintain audio sync.
// TODO: Perhaps we should care about this when starting dumping in the middle of emulation too,
// but it's less important there since the first frame to dump usually gets delivered quickly.
const u64 start_ticks = frame.state.frame_number == 0 ? 0 : frame.state.ticks;
return m_ffmpeg_dump.Start(frame.width, frame.height, start_ticks);
}
void FrameDumper::DumpFrameToFFMPEG(const FrameData& frame)
{
m_ffmpeg_dump.AddFrame(frame);
}
void FrameDumper::StopFrameDumpToFFMPEG()
{
m_ffmpeg_dump.Stop();
}
#else
bool FrameDump::StartFrameDumpToFFMPEG(const FrameData&)
{
return false;
}
void FrameDump::DumpFrameToFFMPEG(const FrameData&)
{
}
void FrameDump::StopFrameDumpToFFMPEG()
{
}
#endif // defined(HAVE_FFMPEG)
std::string FrameDumper::GetFrameDumpNextImageFileName() const
{
return fmt::format("{}framedump_{}.png", File::GetUserPath(D_DUMPFRAMES_IDX),
m_frame_dump_image_counter);
}
bool FrameDumper::StartFrameDumpToImage(const FrameData&)
{
m_frame_dump_image_counter = 1;
if (!Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES_SILENT))
{
// Only check for the presence of the first image to confirm overwriting.
// A previous run will always have at least one image, and it's safe to assume that if the user
// has allowed the first image to be overwritten, this will apply any remaining images as well.
std::string filename = GetFrameDumpNextImageFileName();
if (File::Exists(filename))
{
if (!AskYesNoFmtT("Frame dump image(s) '{0}' already exists. Overwrite?", filename))
return false;
}
}
return true;
}
void FrameDumper::DumpFrameToImage(const FrameData& frame)
{
DumpFrameToPNG(frame, GetFrameDumpNextImageFileName());
m_frame_dump_image_counter++;
}
void FrameDumper::SaveScreenshot(std::string filename)
{
std::lock_guard<std::mutex> lk(m_screenshot_lock);
m_screenshot_name = std::move(filename);
m_screenshot_request.Set();
}
bool FrameDumper::IsFrameDumping() const
{
if (m_screenshot_request.IsSet())
return true;
if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES))
return true;
return false;
}
std::unique_ptr<FrameDumper> g_frame_dumper;
+121
View File
@@ -0,0 +1,121 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Common/MathUtil.h"
#include "Common/Thread.h"
#include "VideoCommon/FrameDump.h"
class AbstractStagingTexture;
class AbstractTexture;
class AbstractFramebuffer;
// Holds relevant emulation state during a rendered frame for
// when it is later asynchronously written.
struct FrameState
{
u64 ticks = 0;
int frame_number = 0;
u32 savestate_index = 0;
int refresh_rate_num = 0;
int refresh_rate_den = 0;
};
struct FrameData
{
const u8* data = nullptr;
int width = 0;
int height = 0;
int stride = 0;
FrameState state;
};
class FrameDumper
{
public:
FrameDumper();
~FrameDumper();
// Ensures all rendered frames are queued for encoding.
void FlushFrameDump();
// Fills the frame dump staging texture with the current XFB texture.
void DumpCurrentFrame(const AbstractTexture* src_texture,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& target_rect, u64 ticks, int frame_number);
void SaveScreenshot(std::string filename);
bool IsFrameDumping() const;
void DoState(PointerWrap& p) { m_ffmpeg_dump.DoState(p); }
private:
// NOTE: The methods below are called on the framedumping thread.
void FrameDumpThreadFunc();
bool StartFrameDumpToFFMPEG(const FrameData&);
void DumpFrameToFFMPEG(const FrameData&);
void StopFrameDumpToFFMPEG();
std::string GetFrameDumpNextImageFileName() const;
bool StartFrameDumpToImage(const FrameData&);
void DumpFrameToImage(const FrameData&);
void ShutdownFrameDumping();
// Checks that the frame dump render texture exists and is the correct size.
bool CheckFrameDumpRenderTexture(u32 target_width, u32 target_height);
// Checks that the frame dump readback texture exists and is the correct size.
bool CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height);
// Asynchronously encodes the specified pointer of frame data to the frame dump.
void DumpFrameData(const u8* data, int w, int h, int stride);
// Ensures all encoded frames have been written to the output file.
void FinishFrameData();
std::thread m_frame_dump_thread;
Common::Flag m_frame_dump_thread_running;
// Used to kick frame dump thread.
Common::Event m_frame_dump_start;
// Set by frame dump thread on frame completion.
Common::Event m_frame_dump_done;
// Holds emulation state during the last swap when dumping.
FrameState m_last_frame_state;
// Communication of frame between video and dump threads.
FrameData m_frame_dump_data;
// Texture used for screenshot/frame dumping
std::unique_ptr<AbstractTexture> m_frame_dump_render_texture;
std::unique_ptr<AbstractFramebuffer> m_frame_dump_render_framebuffer;
// Double buffer:
std::unique_ptr<AbstractStagingTexture> m_frame_dump_readback_texture;
std::unique_ptr<AbstractStagingTexture> m_frame_dump_output_texture;
// Set when readback texture holds a frame that needs to be dumped.
bool m_frame_dump_needs_flush = false;
// Set when thread is processing output texture.
bool m_frame_dump_frame_running = false;
// Used to generate screenshot names.
u32 m_frame_dump_image_counter = 0;
FFMpegFrameDump m_ffmpeg_dump;
// Screenshots
Common::Flag m_screenshot_request;
Common::Event m_screenshot_completed;
std::mutex m_screenshot_lock;
std::string m_screenshot_name;
};
extern std::unique_ptr<FrameDumper> g_frame_dumper;

Some files were not shown because too many files have changed in this diff Show More