Skip to content

Commit a68e429

Browse files
authored
Merge pull request #20137 from hrydgard/screenshot-speedup
Screenshot performance improvement
2 parents 79945d6 + 6a71cbe commit a68e429

21 files changed

+156
-120
lines changed

CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,8 +2411,6 @@ add_library(${CoreLibName} ${CoreLinkType}
24112411
Core/Screenshot.h
24122412
Core/System.cpp
24132413
Core/System.h
2414-
Core/ThreadPools.cpp
2415-
Core/ThreadPools.h
24162414
Core/Util/AtracTrack.cpp
24172415
Core/Util/AtracTrack.h
24182416
Core/Util/AudioFormat.cpp

Common/Thread/Promise.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@
77
#include "Common/Thread/Channel.h"
88
#include "Common/Thread/ThreadManager.h"
99

10+
// Nobody needs to wait for this (except threadpool shutdown).
11+
template<class T>
12+
class IndependentTask : public Task {
13+
public:
14+
IndependentTask(TaskType type, TaskPriority prio, T func) : func_(std::move(func)), type_(type), prio_(prio) {}
15+
TaskType Type() const override { return type_; }
16+
TaskPriority Priority() const override { return prio_; }
17+
void Run() override {
18+
func_();
19+
}
20+
private:
21+
T func_;
22+
TaskType type_;
23+
TaskPriority prio_;
24+
};
25+
1026
template<class T>
1127
class PromiseTask : public Task {
1228
public:

Common/Thread/ThreadManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const int MAX_CORES_TO_USE = 16;
2424
const int MIN_IO_BLOCKING_THREADS = 4;
2525
static constexpr size_t TASK_PRIORITY_COUNT = (size_t)TaskPriority::COUNT;
2626

27+
ThreadManager g_threadManager;
28+
2729
struct GlobalThreadContext {
2830
std::mutex mutex;
2931
std::deque<Task *> compute_queue[TASK_PRIORITY_COUNT];

Core/Core.vcxproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,6 @@
10841084
<ClCompile Include="MIPS\MIPSStackWalk.cpp" />
10851085
<ClCompile Include="Screenshot.cpp" />
10861086
<ClCompile Include="System.cpp" />
1087-
<ClCompile Include="ThreadPools.cpp" />
10881087
<ClCompile Include="TiltEventProcessor.cpp" />
10891088
<ClCompile Include="Util\AtracTrack.cpp" />
10901089
<ClCompile Include="Util\AudioFormat.cpp" />
@@ -1470,7 +1469,6 @@
14701469
<ClInclude Include="Screenshot.h" />
14711470
<ClInclude Include="System.h" />
14721471
<ClInclude Include="ThreadEventQueue.h" />
1473-
<ClInclude Include="ThreadPools.h" />
14741472
<ClInclude Include="TiltEventProcessor.h" />
14751473
<ClInclude Include="Util\AtracTrack.h" />
14761474
<ClInclude Include="Util\AudioFormat.h" />

Core/Core.vcxproj.filters

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -952,9 +952,6 @@
952952
<ClCompile Include="KeyMap.cpp">
953953
<Filter>Core</Filter>
954954
</ClCompile>
955-
<ClCompile Include="ThreadPools.cpp">
956-
<Filter>Core</Filter>
957-
</ClCompile>
958955
<ClCompile Include="Debugger\WebSocket\InputSubscriber.cpp">
959956
<Filter>Debugger\WebSocket</Filter>
960957
</ClCompile>
@@ -2034,9 +2031,6 @@
20342031
<ClInclude Include="KeyMap.h">
20352032
<Filter>Core</Filter>
20362033
</ClInclude>
2037-
<ClInclude Include="ThreadPools.h">
2038-
<Filter>Core</Filter>
2039-
</ClInclude>
20402034
<ClInclude Include="Debugger\WebSocket\InputSubscriber.h">
20412035
<Filter>Debugger\WebSocket</Filter>
20422036
</ClInclude>

Core/HLE/sceKernelMemory.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <sstream>
2323

2424
#include "Common/Thread/ParallelLoop.h"
25+
#include "Common/Thread/ThreadManager.h"
2526
#include "Core/CoreTiming.h"
2627
#include "Core/Debugger/MemBlockInfo.h"
2728
#include "Core/HLE/HLE.h"
@@ -30,7 +31,6 @@
3031
#include "Core/MemMapHelpers.h"
3132
#include "Core/Reporting.h"
3233
#include "Core/System.h"
33-
#include "Core/ThreadPools.h"
3434
#include "Common/Serialize/Serializer.h"
3535
#include "Common/Serialize/SerializeFuncs.h"
3636
#include "Common/Serialize/SerializeMap.h"

Core/HLE/sceKernelModule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ void PSPModule::GetLongInfo(char *ptr, int bufSize) const {
483483
StringWriter w(ptr, bufSize);
484484
w.F("%s: Version %d.%d. %d segments", nm.name, nm.version[1], nm.version[0], nm.nsegment).endl();
485485
w.F("Memory block: %08x (%08x/%d bytes)", memoryBlockAddr, memoryBlockSize, memoryBlockSize).endl();
486-
for (int i = 0; i < nm.nsegment; i++) {
486+
for (int i = 0; i < (int)nm.nsegment; i++) {
487487
w.F(" %08x (%08x bytes)\n", nm.segmentaddr[i], nm.segmentsize[i]);
488488
}
489489
w.F("Text: %08x (%08x bytes)\n", nm.text_addr, nm.text_size);

Core/SaveState.cpp

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -466,13 +466,12 @@ double g_lastSaveTime = -1.0;
466466
Enqueue(Operation(SAVESTATE_REWIND, Path(), -1, callback, cbUserData));
467467
}
468468

469-
void SaveScreenshot(const Path &filename, Callback callback, void *cbUserData) {
469+
static void SaveScreenshot(const Path &filename) {
470470
screenshotFailures = 0;
471-
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, callback, cbUserData));
471+
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, nullptr, nullptr));
472472
}
473473

474-
bool CanRewind()
475-
{
474+
bool CanRewind() {
476475
return !rewindStates.Empty();
477476
}
478477

@@ -690,7 +689,7 @@ double g_lastSaveTime = -1.0;
690689
DeleteIfExists(shotUndo);
691690
RenameIfExists(shot, shotUndo);
692691
}
693-
SaveScreenshot(shot, Callback(), 0);
692+
SaveScreenshot(shot);
694693
Save(fn.WithExtraExtension(".tmp"), slot, renameCallback, cbUserData);
695694
} else {
696695
if (callback) {
@@ -966,11 +965,9 @@ double g_lastSaveTime = -1.0;
966965

967966
bool readbackImage = false;
968967

969-
for (size_t i = 0, n = operations.size(); i < n; ++i) {
970-
Operation &op = operations[i];
968+
for (const auto &op : operations) {
971969
CChunkFileReader::Error result;
972970
Status callbackResult;
973-
bool tempResult;
974971
std::string callbackMessage;
975972
std::string title;
976973

@@ -1056,14 +1053,16 @@ double g_lastSaveTime = -1.0;
10561053
break;
10571054

10581055
case SAVESTATE_VERIFY:
1059-
tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
1056+
{
1057+
int tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
10601058
callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
10611059
if (tempResult) {
10621060
INFO_LOG(Log::SaveState, "Verified save state system");
10631061
} else {
10641062
ERROR_LOG(Log::SaveState, "Save state system verification failed");
10651063
}
10661064
break;
1065+
}
10671066

10681067
case SAVESTATE_REWIND:
10691068
INFO_LOG(Log::SaveState, "Rewinding to recent savestate snapshot");
@@ -1093,18 +1092,32 @@ double g_lastSaveTime = -1.0;
10931092

10941093
case SAVESTATE_SAVE_SCREENSHOT:
10951094
{
1095+
_dbg_assert_(!op.callback);
1096+
10961097
int maxResMultiplier = 2;
1097-
tempResult = TakeGameScreenshot(nullptr, op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, maxResMultiplier);
1098-
callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
1099-
if (!tempResult) {
1098+
ScreenshotResult tempResult = TakeGameScreenshot(nullptr, op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, maxResMultiplier, [](bool success) {
1099+
if (success) {
1100+
screenshotFailures = 0;
1101+
}
1102+
});
1103+
1104+
switch (tempResult) {
1105+
case ScreenshotResult::ScreenshotNotPossible:
1106+
// Try again soon, for a short while.
1107+
callbackResult = Status::FAILURE;
11001108
WARN_LOG(Log::SaveState, "Failed to take a screenshot for the savestate! (%s) The savestate will lack an icon.", op.filename.c_str());
11011109
if (coreState != CORE_STEPPING_CPU && screenshotFailures++ < SCREENSHOT_FAILURE_RETRIES) {
11021110
// Requeue for next frame (if we were stepping, no point, will just spam errors quickly).
1103-
SaveScreenshot(op.filename, op.callback, op.cbUserData);
1111+
SaveScreenshot(op.filename);
11041112
}
1105-
} else {
1106-
screenshotFailures = 0;
1113+
break;
1114+
case ScreenshotResult::DelayedResult:
1115+
case ScreenshotResult::Success:
1116+
// We might not know if the file write succeeded yet though.
1117+
callbackResult = Status::SUCCESS;
1118+
break;
11071119
}
1120+
11081121
readbackImage = true;
11091122
break;
11101123
}

Core/Screenshot.cpp

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
#include "Common/File/FileUtil.h"
2727
#include "Common/File/Path.h"
2828
#include "Common/Log.h"
29+
#include "Common/System/System.h"
2930
#include "Common/System/Display.h"
31+
#include "Common/System/NativeApp.h"
32+
#include "Common/Thread/Promise.h"
3033
#include "Core/Screenshot.h"
3134
#include "Core/System.h"
3235
#include "GPU/Common/GPUDebugInterface.h"
@@ -328,56 +331,54 @@ static GPUDebugBuffer ApplyRotation(const GPUDebugBuffer &buf, DisplayRotation r
328331
return rotated;
329332
}
330333

331-
bool TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width, int *height, int maxRes) {
334+
ScreenshotResult TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int maxRes, std::function<void(bool success)> callback) {
332335
GPUDebugBuffer buf;
333-
bool success = false;
334336
u32 w = (u32)-1;
335337
u32 h = (u32)-1;
336338

337339
if (type == SCREENSHOT_DISPLAY || type == SCREENSHOT_RENDER) {
338340
if (!gpuDebug) {
339341
ERROR_LOG(Log::System, "Can't take screenshots when GPU not running");
340-
return false;
342+
return ScreenshotResult::ScreenshotNotPossible;
343+
}
344+
if (!gpuDebug->GetCurrentFramebuffer(buf, type == SCREENSHOT_RENDER ? GPU_DBG_FRAMEBUF_RENDER : GPU_DBG_FRAMEBUF_DISPLAY, maxRes)) {
345+
return ScreenshotResult::ScreenshotNotPossible;
341346
}
342-
success = gpuDebug->GetCurrentFramebuffer(buf, type == SCREENSHOT_RENDER ? GPU_DBG_FRAMEBUF_RENDER : GPU_DBG_FRAMEBUF_DISPLAY, maxRes);
343347
w = maxRes > 0 ? 480 * maxRes : PSP_CoreParameter().renderWidth;
344348
h = maxRes > 0 ? 272 * maxRes : PSP_CoreParameter().renderHeight;
345349
} else if (g_display.rotation != DisplayRotation::ROTATE_0) {
346350
_dbg_assert_(draw);
347351
GPUDebugBuffer temp;
348-
success = ::GetOutputFramebuffer(draw, temp);
349-
if (success) {
350-
buf = ApplyRotation(temp, g_display.rotation);
352+
if (!::GetOutputFramebuffer(draw, temp)) {
353+
return ScreenshotResult::ScreenshotNotPossible;
351354
}
355+
buf = ApplyRotation(temp, g_display.rotation);
352356
} else {
353357
_dbg_assert_(draw);
354-
success = ::GetOutputFramebuffer(draw, buf);
355-
}
356-
357-
if (!success) {
358-
return false;
359-
}
360-
361-
if (success) {
362-
u8 *flipbuffer = nullptr;
363-
const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h);
364-
success = buffer != nullptr;
365-
if (success) {
366-
if (width)
367-
*width = w;
368-
if (height)
369-
*height = h;
370-
371-
success = Save888RGBScreenshot(filename, fmt, buffer, w, h);
358+
if (!GetOutputFramebuffer(draw, buf)) {
359+
return ScreenshotResult::ScreenshotNotPossible;
372360
}
373-
delete[] flipbuffer;
374361
}
375362

376-
if (!success) {
377-
ERROR_LOG(Log::IO, "Failed to write screenshot.");
363+
if (callback) {
364+
g_threadManager.EnqueueTask(new IndependentTask(TaskType::CPU_COMPUTE, TaskPriority::LOW,
365+
[buf = std::move(buf), callback = std::move(callback), filename, fmt, w, h]() {
366+
u8 *flipbuffer = nullptr;
367+
u32 width = w, height = h;
368+
const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, width, height);
369+
bool success = Save888RGBScreenshot(filename, fmt, buffer, width, height);
370+
delete[] flipbuffer;
371+
System_RunOnMainThread([success, callback = std::move(callback)]() {
372+
callback(success);
373+
});
374+
}));
375+
return ScreenshotResult::DelayedResult;
378376
}
379-
380-
return success;
377+
u8 *flipbuffer = nullptr;
378+
const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h);
379+
bool success = Save888RGBScreenshot(filename, fmt, buffer, w, h);
380+
delete[] flipbuffer;
381+
return success ? ScreenshotResult::Success : ScreenshotResult::FailedToWriteFile;
381382
}
382383

383384
bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h) {

Core/Screenshot.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include "Common/File/Path.h"
2121

22+
#include <functional>
23+
2224
struct GPUDebugBuffer;
2325
namespace Draw {
2426
class DrawContext;
@@ -29,6 +31,8 @@ enum class ScreenshotFormat {
2931
JPG,
3032
};
3133

34+
// NOTE: The first two may need rotation, depending on the backend and screen orientation.
35+
// This is handled internally in TakeGameScreenshot().
3236
enum ScreenshotType {
3337
// What's being show on screen (e.g. including FPS, etc.)
3438
SCREENSHOT_OUTPUT,
@@ -38,10 +42,19 @@ enum ScreenshotType {
3842
SCREENSHOT_RENDER,
3943
};
4044

45+
enum class ScreenshotResult {
46+
ScreenshotNotPossible,
47+
DelayedResult, // This specifies that the actual result is one of the two below and will arrive in the callback.
48+
// These result can be delayed and arrive in the callback, if one is specified.
49+
FailedToWriteFile,
50+
Success,
51+
};
52+
4153
const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h);
4254

4355
// Can only be used while in game.
44-
bool TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1);
56+
// If the callback is passed in, the saving action happens on a background thread.
57+
ScreenshotResult TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int maxRes = -1, std::function<void(bool success)> callback = nullptr);
4558

4659
bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h);
4760
bool Save8888RGBAScreenshot(const Path &filename, const u8 *bufferRGBA8888, int w, int h);

0 commit comments

Comments
 (0)