System: Add 'Task Threads'

Used for saving screenshots/GPU dumps.
This commit is contained in:
Stenzek 2024-10-28 02:38:06 +10:00
parent 0dc78e4c23
commit bfadd608fb
No known key found for this signature in database
4 changed files with 117 additions and 112 deletions

View File

@ -61,9 +61,6 @@ static TimingEvent s_frame_done_event(
"Frame Done", 1, 1, [](void* param, TickCount ticks, TickCount ticks_late) { g_gpu->FrameDoneEvent(ticks); }, "Frame Done", 1, 1, [](void* param, TickCount ticks, TickCount ticks_late) { g_gpu->FrameDoneEvent(ticks); },
nullptr); nullptr);
static std::deque<std::thread> s_screenshot_threads;
static std::mutex s_screenshot_threads_mutex;
// #define PSX_GPU_STATS // #define PSX_GPU_STATS
#ifdef PSX_GPU_STATS #ifdef PSX_GPU_STATS
static u64 s_active_gpu_cycles = 0; static u64 s_active_gpu_cycles = 0;
@ -75,9 +72,7 @@ static constexpr GPUTexture::Format DISPLAY_INTERNAL_POSTFX_FORMAT = GPUTexture:
static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp, static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
u8 quality, bool clear_alpha, bool flip_y, std::vector<u32> texture_data, u8 quality, bool clear_alpha, bool flip_y, std::vector<u32> texture_data,
u32 texture_data_stride, GPUTexture::Format texture_format, u32 texture_data_stride, GPUTexture::Format texture_format,
bool display_osd_message, bool use_thread); std::string osd_key);
static void RemoveSelfFromScreenshotThreads();
static void JoinScreenshotThreads();
GPU::GPU() GPU::GPU()
{ {
@ -92,7 +87,6 @@ GPU::~GPU()
s_frame_done_event.Deactivate(); s_frame_done_event.Deactivate();
StopRecordingGPUDump(); StopRecordingGPUDump();
JoinScreenshotThreads();
DestroyDeinterlaceTextures(); DestroyDeinterlaceTextures();
g_gpu_device->RecycleTexture(std::move(m_chroma_smoothing_texture)); g_gpu_device->RecycleTexture(std::move(m_chroma_smoothing_texture));
} }
@ -2424,23 +2418,8 @@ void GPU::CalculateDrawRect(s32 window_width, s32 window_height, bool apply_rota
bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp, bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
u8 quality, bool clear_alpha, bool flip_y, std::vector<u32> texture_data, u8 quality, bool clear_alpha, bool flip_y, std::vector<u32> texture_data,
u32 texture_data_stride, GPUTexture::Format texture_format, bool display_osd_message, u32 texture_data_stride, GPUTexture::Format texture_format, std::string osd_key)
bool use_thread)
{ {
std::string osd_key;
if (display_osd_message)
{
// Use a 60 second timeout to give it plenty of time to actually save.
osd_key = fmt::format("ScreenshotSaver_{}", filename);
Host::AddIconOSDMessage(osd_key, ICON_EMOJI_CAMERA_WITH_FLASH,
fmt::format(TRANSLATE_FS("GPU", "Saving screenshot to '{}'."), Path::GetFileName(filename)),
60.0f);
}
static constexpr auto proc = [](u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
u8 quality, bool clear_alpha, bool flip_y, std::vector<u32> texture_data,
u32 texture_data_stride, GPUTexture::Format texture_format, std::string osd_key,
bool use_thread) {
bool result; bool result;
const char* extension = std::strrchr(filename.c_str(), '.'); const char* extension = std::strrchr(filename.c_str(), '.');
@ -2490,54 +2469,10 @@ bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename,
result ? Host::OSD_INFO_DURATION : Host::OSD_ERROR_DURATION)); result ? Host::OSD_INFO_DURATION : Host::OSD_ERROR_DURATION));
} }
if (use_thread)
RemoveSelfFromScreenshotThreads();
return result; return result;
};
if (!use_thread)
{
return proc(width, height, std::move(filename), std::move(fp), quality, clear_alpha, flip_y,
std::move(texture_data), texture_data_stride, texture_format, std::move(osd_key), use_thread);
} }
std::unique_lock lock(s_screenshot_threads_mutex); bool GPU::WriteDisplayTextureToFile(std::string filename)
std::thread thread(proc, width, height, std::move(filename), std::move(fp), quality, clear_alpha, flip_y,
std::move(texture_data), texture_data_stride, texture_format, std::move(osd_key), use_thread);
s_screenshot_threads.push_back(std::move(thread));
return true;
}
void RemoveSelfFromScreenshotThreads()
{
const auto this_id = std::this_thread::get_id();
std::unique_lock lock(s_screenshot_threads_mutex);
for (auto it = s_screenshot_threads.begin(); it != s_screenshot_threads.end(); ++it)
{
if (it->get_id() == this_id)
{
it->detach();
s_screenshot_threads.erase(it);
break;
}
}
}
void JoinScreenshotThreads()
{
std::unique_lock lock(s_screenshot_threads_mutex);
while (!s_screenshot_threads.empty())
{
std::thread save_thread(std::move(s_screenshot_threads.front()));
s_screenshot_threads.pop_front();
lock.unlock();
save_thread.join();
lock.lock();
}
}
bool GPU::WriteDisplayTextureToFile(std::string filename, bool compress_on_thread /* = false */)
{ {
if (!m_display_texture) if (!m_display_texture)
return false; return false;
@ -2590,7 +2525,7 @@ bool GPU::WriteDisplayTextureToFile(std::string filename, bool compress_on_threa
return CompressAndWriteTextureToFile( return CompressAndWriteTextureToFile(
read_width, read_height, std::move(filename), std::move(fp), g_settings.display_screenshot_quality, clear_alpha, read_width, read_height, std::move(filename), std::move(fp), g_settings.display_screenshot_quality, clear_alpha,
flip_y, std::move(texture_data), texture_data_stride, m_display_texture->GetFormat(), false, compress_on_thread); flip_y, std::move(texture_data), texture_data_stride, m_display_texture->GetFormat(), std::string());
} }
bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect, bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect,
@ -2703,7 +2638,7 @@ void GPU::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* h
} }
} }
bool GPU::RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mode, u8 quality, bool compress_on_thread, bool GPU::RenderScreenshotToFile(std::string path, DisplayScreenshotMode mode, u8 quality, bool compress_on_thread,
bool show_osd_message) bool show_osd_message)
{ {
u32 width, height; u32 width, height;
@ -2725,16 +2660,41 @@ bool GPU::RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mod
} }
Error error; Error error;
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb", &error); auto fp = FileSystem::OpenManagedCFile(path.c_str(), "wb", &error);
if (!fp) if (!fp)
{ {
ERROR_LOG("Can't open file '{}': {}", Path::GetFileName(filename), error.GetDescription()); ERROR_LOG("Can't open file '{}': {}", Path::GetFileName(path), error.GetDescription());
return false; return false;
} }
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), quality, true, std::string osd_key;
if (show_osd_message)
{
// Use a 60 second timeout to give it plenty of time to actually save.
osd_key = fmt::format("ScreenshotSaver_{}", path);
Host::AddIconOSDMessage(osd_key, ICON_EMOJI_CAMERA_WITH_FLASH,
fmt::format(TRANSLATE_FS("GPU", "Saving screenshot to '{}'."), Path::GetFileName(path)),
60.0f);
}
if (compress_on_thread)
{
System::QueueTaskOnThread([width, height, path = std::move(path), fp = fp.release(), quality,
flip_y = g_gpu_device->UsesLowerLeftOrigin(), pixels = std::move(pixels), pixels_stride,
pixels_format, osd_key = std::move(osd_key)]() mutable {
CompressAndWriteTextureToFile(width, height, std::move(path), FileSystem::ManagedCFilePtr(fp), quality, true,
flip_y, std::move(pixels), pixels_stride, pixels_format, std::move(osd_key));
System::RemoveSelfFromTaskThreads();
});
return true;
}
else
{
return CompressAndWriteTextureToFile(width, height, std::move(path), std::move(fp), quality, true,
g_gpu_device->UsesLowerLeftOrigin(), std::move(pixels), pixels_stride, g_gpu_device->UsesLowerLeftOrigin(), std::move(pixels), pixels_stride,
pixels_format, show_osd_message, compress_on_thread); pixels_format, std::move(osd_key));
}
} }
bool GPU::DumpVRAMToFile(const char* filename) bool GPU::DumpVRAMToFile(const char* filename)
@ -2988,8 +2948,7 @@ void GPU::StopRecordingGPUDump()
Host::AddIconOSDMessage( Host::AddIconOSDMessage(
osd_key, ICON_EMOJI_CAMERA_WITH_FLASH, osd_key, ICON_EMOJI_CAMERA_WITH_FLASH,
fmt::format(TRANSLATE_FS("GPU", "Compressing GPU trace '{}'..."), Path::GetFileName(source_path)), 60.0f); fmt::format(TRANSLATE_FS("GPU", "Compressing GPU trace '{}'..."), Path::GetFileName(source_path)), 60.0f);
std::unique_lock screenshot_lock(s_screenshot_threads_mutex); System::QueueTaskOnThread(
s_screenshot_threads.emplace_back(
[compress_mode, source_path = std::move(source_path), osd_key = std::move(osd_key)]() mutable { [compress_mode, source_path = std::move(source_path), osd_key = std::move(osd_key)]() mutable {
Error error; Error error;
if (GPUDump::Recorder::Compress(source_path, compress_mode, &error)) if (GPUDump::Recorder::Compress(source_path, compress_mode, &error))
@ -3010,7 +2969,7 @@ void GPU::StopRecordingGPUDump()
Host::OSD_ERROR_DURATION); Host::OSD_ERROR_DURATION);
} }
RemoveSelfFromScreenshotThreads(); System::RemoveSelfFromTaskThreads();
}); });
} }

View File

@ -18,8 +18,8 @@
#include <array> #include <array>
#include <deque> #include <deque>
#include <memory> #include <memory>
#include <string>
#include <span> #include <span>
#include <string>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
@ -37,7 +37,7 @@ namespace GPUDump {
enum class PacketType : u8; enum class PacketType : u8;
class Recorder; class Recorder;
class Player; class Player;
} } // namespace GPUDump
struct Settings; struct Settings;
namespace Threading { namespace Threading {
@ -229,7 +229,7 @@ public:
GSVector4i* draw_rect) const; GSVector4i* draw_rect) const;
/// Helper function to save current display texture to PNG. /// Helper function to save current display texture to PNG.
bool WriteDisplayTextureToFile(std::string filename, bool compress_on_thread = false); bool WriteDisplayTextureToFile(std::string path);
/// Renders the display, optionally with postprocessing to the specified image. /// Renders the display, optionally with postprocessing to the specified image.
bool RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect, bool RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect,
@ -237,7 +237,7 @@ public:
GPUTexture::Format* out_format); GPUTexture::Format* out_format);
/// Helper function to save screenshot to PNG. /// Helper function to save screenshot to PNG.
bool RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mode, u8 quality, bool compress_on_thread, bool RenderScreenshotToFile(std::string path, DisplayScreenshotMode mode, u8 quality, bool compress_on_thread,
bool show_osd_message); bool show_osd_message);
/// Draws the current display texture, with any post-processing. /// Draws the current display texture, with any post-processing.

View File

@ -165,6 +165,7 @@ static bool SetBootMode(BootMode new_boot_mode, DiscRegion disc_region, Error* e
static void InternalReset(); static void InternalReset();
static void ClearRunningGame(); static void ClearRunningGame();
static void DestroySystem(); static void DestroySystem();
static void JoinTaskThreads();
static bool CreateGPU(GPURenderer renderer, bool is_switching, bool fullscreen, Error* error); static bool CreateGPU(GPURenderer renderer, bool is_switching, bool fullscreen, Error* error);
static bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true); static bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true);
@ -320,6 +321,9 @@ struct ALIGN_TO_CACHE_LINE StateVars
// Used to track play time. We use a monotonic timer here, in case of clock changes. // Used to track play time. We use a monotonic timer here, in case of clock changes.
u64 session_start_time = 0; u64 session_start_time = 0;
std::deque<std::thread> task_threads;
std::mutex task_threads_mutex;
#ifdef ENABLE_SOCKET_MULTIPLEXER #ifdef ENABLE_SOCKET_MULTIPLEXER
std::unique_ptr<SocketMultiplexer> socket_multiplexer; std::unique_ptr<SocketMultiplexer> socket_multiplexer;
#endif #endif
@ -1936,6 +1940,8 @@ void System::DestroySystem()
if (s_state.state == State::Shutdown) if (s_state.state == State::Shutdown)
return; return;
JoinTaskThreads();
if (s_state.media_capture) if (s_state.media_capture)
StopMediaCapture(); StopMediaCapture();
@ -5779,6 +5785,40 @@ u64 System::GetSessionPlayedTime()
return static_cast<u64>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_state.session_start_time))); return static_cast<u64>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_state.session_start_time)));
} }
void System::QueueTaskOnThread(std::function<void()> task)
{
const std::unique_lock lock(s_state.task_threads_mutex);
s_state.task_threads.emplace_back(std::move(task));
}
void System::RemoveSelfFromTaskThreads()
{
const auto this_id = std::this_thread::get_id();
const std::unique_lock lock(s_state.task_threads_mutex);
for (auto it = s_state.task_threads.begin(); it != s_state.task_threads.end(); ++it)
{
if (it->get_id() == this_id)
{
it->detach();
s_state.task_threads.erase(it);
break;
}
}
}
void System::JoinTaskThreads()
{
std::unique_lock lock(s_state.task_threads_mutex);
while (!s_state.task_threads.empty())
{
std::thread save_thread(std::move(s_state.task_threads.front()));
s_state.task_threads.pop_front();
lock.unlock();
save_thread.join();
lock.lock();
}
}
SocketMultiplexer* System::GetSocketMultiplexer() SocketMultiplexer* System::GetSocketMultiplexer()
{ {
#ifdef ENABLE_SOCKET_MULTIPLEXER #ifdef ENABLE_SOCKET_MULTIPLEXER

View File

@ -5,6 +5,8 @@
#include "system.h" #include "system.h"
#include <functional>
namespace System { namespace System {
/// Memory save states - only for internal use. /// Memory save states - only for internal use.
@ -55,6 +57,10 @@ const Threading::ThreadHandle& GetCPUThreadHandle();
/// Polls input, updates subsystems which are present while paused/inactive. /// Polls input, updates subsystems which are present while paused/inactive.
void IdlePollUpdate(); void IdlePollUpdate();
/// Task threads, asynchronous work which will block system shutdown.
void QueueTaskOnThread(std::function<void()> task);
void RemoveSelfFromTaskThreads();
} // namespace System } // namespace System
namespace Host { namespace Host {