System: Add 'Task Threads'
Used for saving screenshots/GPU dumps.
This commit is contained in:
parent
0dc78e4c23
commit
bfadd608fb
113
src/core/gpu.cpp
113
src/core/gpu.cpp
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue