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); },
|
||||
nullptr);
|
||||
|
||||
static std::deque<std::thread> s_screenshot_threads;
|
||||
static std::mutex s_screenshot_threads_mutex;
|
||||
|
||||
// #define PSX_GPU_STATS
|
||||
#ifdef PSX_GPU_STATS
|
||||
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,
|
||||
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, bool use_thread);
|
||||
static void RemoveSelfFromScreenshotThreads();
|
||||
static void JoinScreenshotThreads();
|
||||
std::string osd_key);
|
||||
|
||||
GPU::GPU()
|
||||
{
|
||||
|
@ -92,7 +87,6 @@ GPU::~GPU()
|
|||
s_frame_done_event.Deactivate();
|
||||
|
||||
StopRecordingGPUDump();
|
||||
JoinScreenshotThreads();
|
||||
DestroyDeinterlaceTextures();
|
||||
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,
|
||||
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,
|
||||
bool use_thread)
|
||||
u32 texture_data_stride, GPUTexture::Format texture_format, std::string osd_key)
|
||||
{
|
||||
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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (use_thread)
|
||||
RemoveSelfFromScreenshotThreads();
|
||||
|
||||
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);
|
||||
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 */)
|
||||
bool GPU::WriteDisplayTextureToFile(std::string filename)
|
||||
{
|
||||
if (!m_display_texture)
|
||||
return false;
|
||||
|
@ -2590,7 +2525,7 @@ bool GPU::WriteDisplayTextureToFile(std::string filename, bool compress_on_threa
|
|||
|
||||
return CompressAndWriteTextureToFile(
|
||||
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,
|
||||
|
@ -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)
|
||||
{
|
||||
u32 width, height;
|
||||
|
@ -2725,16 +2660,41 @@ bool GPU::RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mod
|
|||
}
|
||||
|
||||
Error error;
|
||||
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb", &error);
|
||||
auto fp = FileSystem::OpenManagedCFile(path.c_str(), "wb", &error);
|
||||
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 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,
|
||||
pixels_format, show_osd_message, compress_on_thread);
|
||||
pixels_format, std::move(osd_key));
|
||||
}
|
||||
}
|
||||
|
||||
bool GPU::DumpVRAMToFile(const char* filename)
|
||||
|
@ -2988,8 +2948,7 @@ void GPU::StopRecordingGPUDump()
|
|||
Host::AddIconOSDMessage(
|
||||
osd_key, ICON_EMOJI_CAMERA_WITH_FLASH,
|
||||
fmt::format(TRANSLATE_FS("GPU", "Compressing GPU trace '{}'..."), Path::GetFileName(source_path)), 60.0f);
|
||||
std::unique_lock screenshot_lock(s_screenshot_threads_mutex);
|
||||
s_screenshot_threads.emplace_back(
|
||||
System::QueueTaskOnThread(
|
||||
[compress_mode, source_path = std::move(source_path), osd_key = std::move(osd_key)]() mutable {
|
||||
Error error;
|
||||
if (GPUDump::Recorder::Compress(source_path, compress_mode, &error))
|
||||
|
@ -3010,7 +2969,7 @@ void GPU::StopRecordingGPUDump()
|
|||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
|
||||
RemoveSelfFromScreenshotThreads();
|
||||
System::RemoveSelfFromTaskThreads();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#include <array>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace GPUDump {
|
|||
enum class PacketType : u8;
|
||||
class Recorder;
|
||||
class Player;
|
||||
}
|
||||
} // namespace GPUDump
|
||||
struct Settings;
|
||||
|
||||
namespace Threading {
|
||||
|
@ -229,7 +229,7 @@ public:
|
|||
GSVector4i* draw_rect) const;
|
||||
|
||||
/// 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.
|
||||
bool RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect,
|
||||
|
@ -237,7 +237,7 @@ public:
|
|||
GPUTexture::Format* out_format);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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 ClearRunningGame();
|
||||
static void DestroySystem();
|
||||
static void JoinTaskThreads();
|
||||
|
||||
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);
|
||||
|
@ -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.
|
||||
u64 session_start_time = 0;
|
||||
|
||||
std::deque<std::thread> task_threads;
|
||||
std::mutex task_threads_mutex;
|
||||
|
||||
#ifdef ENABLE_SOCKET_MULTIPLEXER
|
||||
std::unique_ptr<SocketMultiplexer> socket_multiplexer;
|
||||
#endif
|
||||
|
@ -1936,6 +1940,8 @@ void System::DestroySystem()
|
|||
if (s_state.state == State::Shutdown)
|
||||
return;
|
||||
|
||||
JoinTaskThreads();
|
||||
|
||||
if (s_state.media_capture)
|
||||
StopMediaCapture();
|
||||
|
||||
|
@ -5779,6 +5785,40 @@ u64 System::GetSessionPlayedTime()
|
|||
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()
|
||||
{
|
||||
#ifdef ENABLE_SOCKET_MULTIPLEXER
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "system.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace System {
|
||||
|
||||
/// 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.
|
||||
void IdlePollUpdate();
|
||||
|
||||
/// Task threads, asynchronous work which will block system shutdown.
|
||||
void QueueTaskOnThread(std::function<void()> task);
|
||||
void RemoveSelfFromTaskThreads();
|
||||
|
||||
} // namespace System
|
||||
|
||||
namespace Host {
|
||||
|
|
Loading…
Reference in New Issue