Merge pull request #20137 from hrydgard/screenshot-speedup

Screenshot performance improvement
This commit is contained in:
Henrik Rydgård 2025-03-20 21:46:54 +01:00 committed by GitHub
commit a68e4292f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 159 additions and 123 deletions

View file

@ -2411,8 +2411,6 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Screenshot.h
Core/System.cpp
Core/System.h
Core/ThreadPools.cpp
Core/ThreadPools.h
Core/Util/AtracTrack.cpp
Core/Util/AtracTrack.h
Core/Util/AudioFormat.cpp

View file

@ -7,6 +7,22 @@
#include "Common/Thread/Channel.h"
#include "Common/Thread/ThreadManager.h"
// Nobody needs to wait for this (except threadpool shutdown).
template<class T>
class IndependentTask : public Task {
public:
IndependentTask(TaskType type, TaskPriority prio, T func) : func_(std::move(func)), type_(type), prio_(prio) {}
TaskType Type() const override { return type_; }
TaskPriority Priority() const override { return prio_; }
void Run() override {
func_();
}
private:
T func_;
TaskType type_;
TaskPriority prio_;
};
template<class T>
class PromiseTask : public Task {
public:

View file

@ -24,6 +24,8 @@ const int MAX_CORES_TO_USE = 16;
const int MIN_IO_BLOCKING_THREADS = 4;
static constexpr size_t TASK_PRIORITY_COUNT = (size_t)TaskPriority::COUNT;
ThreadManager g_threadManager;
struct GlobalThreadContext {
std::mutex mutex;
std::deque<Task *> compute_queue[TASK_PRIORITY_COUNT];

View file

@ -1084,7 +1084,6 @@
<ClCompile Include="MIPS\MIPSStackWalk.cpp" />
<ClCompile Include="Screenshot.cpp" />
<ClCompile Include="System.cpp" />
<ClCompile Include="ThreadPools.cpp" />
<ClCompile Include="TiltEventProcessor.cpp" />
<ClCompile Include="Util\AtracTrack.cpp" />
<ClCompile Include="Util\AudioFormat.cpp" />
@ -1470,7 +1469,6 @@
<ClInclude Include="Screenshot.h" />
<ClInclude Include="System.h" />
<ClInclude Include="ThreadEventQueue.h" />
<ClInclude Include="ThreadPools.h" />
<ClInclude Include="TiltEventProcessor.h" />
<ClInclude Include="Util\AtracTrack.h" />
<ClInclude Include="Util\AudioFormat.h" />

View file

@ -952,9 +952,6 @@
<ClCompile Include="KeyMap.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="ThreadPools.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\InputSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
@ -2034,9 +2031,6 @@
<ClInclude Include="KeyMap.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="ThreadPools.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\InputSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>

View file

@ -22,6 +22,7 @@
#include <sstream>
#include "Common/Thread/ParallelLoop.h"
#include "Common/Thread/ThreadManager.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/HLE/HLE.h"
@ -30,7 +31,6 @@
#include "Core/MemMapHelpers.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "Core/ThreadPools.h"
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Serialize/SerializeMap.h"

View file

@ -483,7 +483,7 @@ void PSPModule::GetLongInfo(char *ptr, int bufSize) const {
StringWriter w(ptr, bufSize);
w.F("%s: Version %d.%d. %d segments", nm.name, nm.version[1], nm.version[0], nm.nsegment).endl();
w.F("Memory block: %08x (%08x/%d bytes)", memoryBlockAddr, memoryBlockSize, memoryBlockSize).endl();
for (int i = 0; i < nm.nsegment; i++) {
for (int i = 0; i < (int)nm.nsegment; i++) {
w.F(" %08x (%08x bytes)\n", nm.segmentaddr[i], nm.segmentsize[i]);
}
w.F("Text: %08x (%08x bytes)\n", nm.text_addr, nm.text_size);

View file

@ -466,13 +466,12 @@ double g_lastSaveTime = -1.0;
Enqueue(Operation(SAVESTATE_REWIND, Path(), -1, callback, cbUserData));
}
void SaveScreenshot(const Path &filename, Callback callback, void *cbUserData) {
static void SaveScreenshot(const Path &filename) {
screenshotFailures = 0;
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, callback, cbUserData));
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, nullptr, nullptr));
}
bool CanRewind()
{
bool CanRewind() {
return !rewindStates.Empty();
}
@ -690,7 +689,7 @@ double g_lastSaveTime = -1.0;
DeleteIfExists(shotUndo);
RenameIfExists(shot, shotUndo);
}
SaveScreenshot(shot, Callback(), 0);
SaveScreenshot(shot);
Save(fn.WithExtraExtension(".tmp"), slot, renameCallback, cbUserData);
} else {
if (callback) {
@ -966,11 +965,9 @@ double g_lastSaveTime = -1.0;
bool readbackImage = false;
for (size_t i = 0, n = operations.size(); i < n; ++i) {
Operation &op = operations[i];
for (const auto &op : operations) {
CChunkFileReader::Error result;
Status callbackResult;
bool tempResult;
std::string callbackMessage;
std::string title;
@ -1056,7 +1053,8 @@ double g_lastSaveTime = -1.0;
break;
case SAVESTATE_VERIFY:
tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
{
int tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
if (tempResult) {
INFO_LOG(Log::SaveState, "Verified save state system");
@ -1064,6 +1062,7 @@ double g_lastSaveTime = -1.0;
ERROR_LOG(Log::SaveState, "Save state system verification failed");
}
break;
}
case SAVESTATE_REWIND:
INFO_LOG(Log::SaveState, "Rewinding to recent savestate snapshot");
@ -1093,18 +1092,32 @@ double g_lastSaveTime = -1.0;
case SAVESTATE_SAVE_SCREENSHOT:
{
_dbg_assert_(!op.callback);
int maxResMultiplier = 2;
tempResult = TakeGameScreenshot(nullptr, op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, maxResMultiplier);
callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
if (!tempResult) {
ScreenshotResult tempResult = TakeGameScreenshot(nullptr, op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, maxResMultiplier, [](bool success) {
if (success) {
screenshotFailures = 0;
}
});
switch (tempResult) {
case ScreenshotResult::ScreenshotNotPossible:
// Try again soon, for a short while.
callbackResult = Status::FAILURE;
WARN_LOG(Log::SaveState, "Failed to take a screenshot for the savestate! (%s) The savestate will lack an icon.", op.filename.c_str());
if (coreState != CORE_STEPPING_CPU && screenshotFailures++ < SCREENSHOT_FAILURE_RETRIES) {
// Requeue for next frame (if we were stepping, no point, will just spam errors quickly).
SaveScreenshot(op.filename, op.callback, op.cbUserData);
SaveScreenshot(op.filename);
}
} else {
screenshotFailures = 0;
break;
case ScreenshotResult::DelayedResult:
case ScreenshotResult::Success:
// We might not know if the file write succeeded yet though.
callbackResult = Status::SUCCESS;
break;
}
readbackImage = true;
break;
}

View file

@ -26,7 +26,10 @@
#include "Common/File/FileUtil.h"
#include "Common/File/Path.h"
#include "Common/Log.h"
#include "Common/System/System.h"
#include "Common/System/Display.h"
#include "Common/System/NativeApp.h"
#include "Common/Thread/Promise.h"
#include "Core/Screenshot.h"
#include "Core/System.h"
#include "GPU/Common/GPUDebugInterface.h"
@ -328,56 +331,54 @@ static GPUDebugBuffer ApplyRotation(const GPUDebugBuffer &buf, DisplayRotation r
return rotated;
}
bool TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width, int *height, int maxRes) {
ScreenshotResult TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int maxRes, std::function<void(bool success)> callback) {
GPUDebugBuffer buf;
bool success = false;
u32 w = (u32)-1;
u32 h = (u32)-1;
if (type == SCREENSHOT_DISPLAY || type == SCREENSHOT_RENDER) {
if (!gpuDebug) {
ERROR_LOG(Log::System, "Can't take screenshots when GPU not running");
return false;
return ScreenshotResult::ScreenshotNotPossible;
}
if (!gpuDebug->GetCurrentFramebuffer(buf, type == SCREENSHOT_RENDER ? GPU_DBG_FRAMEBUF_RENDER : GPU_DBG_FRAMEBUF_DISPLAY, maxRes)) {
return ScreenshotResult::ScreenshotNotPossible;
}
success = gpuDebug->GetCurrentFramebuffer(buf, type == SCREENSHOT_RENDER ? GPU_DBG_FRAMEBUF_RENDER : GPU_DBG_FRAMEBUF_DISPLAY, maxRes);
w = maxRes > 0 ? 480 * maxRes : PSP_CoreParameter().renderWidth;
h = maxRes > 0 ? 272 * maxRes : PSP_CoreParameter().renderHeight;
} else if (g_display.rotation != DisplayRotation::ROTATE_0) {
_dbg_assert_(draw);
GPUDebugBuffer temp;
success = ::GetOutputFramebuffer(draw, temp);
if (success) {
buf = ApplyRotation(temp, g_display.rotation);
if (!::GetOutputFramebuffer(draw, temp)) {
return ScreenshotResult::ScreenshotNotPossible;
}
buf = ApplyRotation(temp, g_display.rotation);
} else {
_dbg_assert_(draw);
success = ::GetOutputFramebuffer(draw, buf);
}
if (!success) {
return false;
}
if (success) {
u8 *flipbuffer = nullptr;
const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h);
success = buffer != nullptr;
if (success) {
if (width)
*width = w;
if (height)
*height = h;
success = Save888RGBScreenshot(filename, fmt, buffer, w, h);
if (!GetOutputFramebuffer(draw, buf)) {
return ScreenshotResult::ScreenshotNotPossible;
}
delete[] flipbuffer;
}
if (!success) {
ERROR_LOG(Log::IO, "Failed to write screenshot.");
if (callback) {
g_threadManager.EnqueueTask(new IndependentTask(TaskType::CPU_COMPUTE, TaskPriority::LOW,
[buf = std::move(buf), callback = std::move(callback), filename, fmt, w, h]() {
u8 *flipbuffer = nullptr;
u32 width = w, height = h;
const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, width, height);
bool success = Save888RGBScreenshot(filename, fmt, buffer, width, height);
delete[] flipbuffer;
System_RunOnMainThread([success, callback = std::move(callback)]() {
callback(success);
});
}));
return ScreenshotResult::DelayedResult;
}
return success;
u8 *flipbuffer = nullptr;
const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h);
bool success = Save888RGBScreenshot(filename, fmt, buffer, w, h);
delete[] flipbuffer;
return success ? ScreenshotResult::Success : ScreenshotResult::FailedToWriteFile;
}
bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h) {

View file

@ -19,6 +19,8 @@
#include "Common/File/Path.h"
#include <functional>
struct GPUDebugBuffer;
namespace Draw {
class DrawContext;
@ -29,6 +31,8 @@ enum class ScreenshotFormat {
JPG,
};
// NOTE: The first two may need rotation, depending on the backend and screen orientation.
// This is handled internally in TakeGameScreenshot().
enum ScreenshotType {
// What's being show on screen (e.g. including FPS, etc.)
SCREENSHOT_OUTPUT,
@ -38,10 +42,19 @@ enum ScreenshotType {
SCREENSHOT_RENDER,
};
enum class ScreenshotResult {
ScreenshotNotPossible,
DelayedResult, // This specifies that the actual result is one of the two below and will arrive in the callback.
// These result can be delayed and arrive in the callback, if one is specified.
FailedToWriteFile,
Success,
};
const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h);
// Can only be used while in game.
bool TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1);
// If the callback is passed in, the saving action happens on a background thread.
ScreenshotResult TakeGameScreenshot(Draw::DrawContext *draw, const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int maxRes = -1, std::function<void(bool success)> callback = nullptr);
bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h);
bool Save8888RGBAScreenshot(const Path &filename, const u8 *bufferRGBA8888, int w, int h);

View file

@ -1,3 +0,0 @@
#include "ThreadPools.h"
ThreadManager g_threadManager;

View file

@ -1,5 +0,0 @@
#pragma once
#include "Common/Thread/ThreadManager.h"
extern ThreadManager g_threadManager;

View file

@ -153,13 +153,13 @@ struct GPUDebugBuffer {
void ZeroBytes();
u32 GetRawPixel(int x, int y) const;
void SetRawPixel(int x, int y, u32 c);
u8 *GetData() {
return data_;
}
u32 GetRawPixel(int x, int y) const;
void SetRawPixel(int x, int y, u32 c);
const u8 *GetData() const {
return data_;
}

View file

@ -26,6 +26,7 @@
#include "Common/CommonTypes.h"
#include "Common/File/FileUtil.h"
#include "Common/Thread/ThreadManager.h"
#include "Common/Thread/ParallelLoop.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
@ -36,7 +37,6 @@
#include "Core/HLE/sceDisplay.h"
#include "Core/MemMap.h"
#include "Core/System.h"
#include "Core/ThreadPools.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/GPUCommon.h"
#include "GPU/GPUState.h"

View file

@ -1445,8 +1445,7 @@ bool SoftGPU::GetCurrentTexture(GPUDebugBuffer &buffer, int level, bool *isFrame
return Rasterizer::GetCurrentTexture(buffer, level);
}
bool SoftGPU::GetCurrentClut(GPUDebugBuffer &buffer)
{
bool SoftGPU::GetCurrentClut(GPUDebugBuffer &buffer) {
const u32 bpp = gstate.getClutPaletteFormat() == GE_CMODE_32BIT_ABGR8888 ? 4 : 2;
const u32 pixels = 1024 / bpp;

View file

@ -84,6 +84,7 @@
#include "Common/OSVersion.h"
#include "Common/GPU/ShaderTranslation.h"
#include "Common/VR/PPSSPPVR.h"
#include "Common/Thread/ThreadManager.h"
#include "Core/ControlMapper.h"
#include "Core/Config.h"
@ -109,7 +110,6 @@
#include "Core/Util/AudioFormat.h"
#include "Core/WebServer.h"
#include "Core/TiltEventProcessor.h"
#include "Core/ThreadPools.h"
#include "GPU/GPUCommon.h"
#include "GPU/Common/PresentationCommon.h"
@ -949,45 +949,60 @@ static void TakeScreenshot(Draw::DrawContext *draw) {
}
// First, find a free filename.
int i = 0;
//
// NOTE: On Android, the old approach of checking filenames one by one doesn't scale.
// So let's just grab the full file listing, and then find a name that's not in it.
//
// TODO: Also, we could do this on a thread too. Not sure if worth it.
std::string gameId = g_paramSFO.GetDiscID();
const std::string gameId = g_paramSFO.GetDiscID();
// TODO: Make something like IterateFileInDir instead.
std::vector<File::FileInfo> files;
const std::string prefix = gameId + "_";
File::GetFilesInDir(path, &files, nullptr, 0, prefix);
std::set<std::string> existingNames;
for (auto &file : files) {
existingNames.insert(file.name);
}
Path filename;
while (i < 10000){
if (g_Config.bScreenshotsAsPNG)
filename = path / StringFromFormat("%s_%05d.png", gameId.c_str(), i);
else
filename = path / StringFromFormat("%s_%05d.jpg", gameId.c_str(), i);
File::FileInfo info;
if (!File::Exists(filename))
int i = 0;
for (int i = 0; i < 20000; i++) {
const std::string pngName = prefix + StringFromFormat("%05d.png", i);
const std::string jpgName = prefix + StringFromFormat("%05d.jpg", i);
if (existingNames.find(pngName) == existingNames.end() && existingNames.find(jpgName) == existingNames.end()) {
filename = path / (g_Config.bScreenshotsAsPNG ? pngName : jpgName);
break;
i++;
}
ScreenshotType type = SCREENSHOT_OUTPUT;
if (g_Config.iScreenshotMode == (int)ScreenshotMode::GameImage) {
type = SCREENSHOT_DISPLAY;
}
bool success = TakeGameScreenshot(draw, filename, g_Config.bScreenshotsAsPNG ? ScreenshotFormat::PNG : ScreenshotFormat::JPG, type);
if (success) {
g_OSD.Show(OSDType::MESSAGE_FILE_LINK, filename.ToVisualString(), 0.0f, "screenshot_link");
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
g_OSD.SetClickCallback("screenshot_link", [](bool clicked, void *data) -> void {
Path *path = reinterpret_cast<Path *>(data);
if (clicked) {
System_ShowFileInFolder(*path);
} else {
delete path;
}
}, new Path(filename));
}
} else {
auto err = GetI18NCategory(I18NCat::ERRORS);
g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Could not save screenshot file"));
WARN_LOG(Log::System, "Failed to take screenshot.");
}
if (filename.empty()) {
// Overwrite this one over and over.
filename = path / (prefix + (g_Config.bScreenshotsAsPNG ? "20000.png" : "20000.jpg"));
}
const ScreenshotType type = g_Config.iScreenshotMode == (int)ScreenshotMode::GameImage ? SCREENSHOT_DISPLAY : SCREENSHOT_OUTPUT;
const ScreenshotResult result = TakeGameScreenshot(draw, filename, g_Config.bScreenshotsAsPNG ? ScreenshotFormat::PNG : ScreenshotFormat::JPG, type, -1, [filename](bool success) {
if (success) {
g_OSD.Show(OSDType::MESSAGE_FILE_LINK, filename.ToVisualString(), 0.0f, "screenshot_link");
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
g_OSD.SetClickCallback("screenshot_link", [](bool clicked, void *data) -> void {
Path *path = reinterpret_cast<Path *>(data);
if (clicked) {
System_ShowFileInFolder(*path);
} else {
delete path;
}
}, new Path(filename));
}
} else {
auto err = GetI18NCategory(I18NCat::ERRORS);
g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Could not save screenshot file"));
WARN_LOG(Log::System, "Failed to take screenshot.");
}
});
}
void CallbackPostRender(UIContext *dc, void *userdata) {

View file

@ -182,21 +182,22 @@ ScreenRenderFlags ReportScreen::render(ScreenRenderMode mode) {
File::CreateDir(path);
}
screenshotFilename_ = path / ".reporting.jpg";
if (TakeGameScreenshot(screenManager()->getDrawContext(), screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) {
// Redo the views already, now with a screenshot included.
RecreateViews();
} else {
// Good news (?), the views are good as-is without a screenshot.
screenshotFilename_.clear();
}
ScreenshotResult ignored = TakeGameScreenshot(screenManager()->getDrawContext(), screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_RENDER, 4, [this](bool success) {
if (success) {
// Redo the views already, now with a screenshot included.
RecreateViews();
} else {
// Good news (?), the views are good as-is without a screenshot.
screenshotFilename_.clear();
}
});
tookScreenshot_ = true;
}
}
// We take the screenshot first, then we start rendering.
// We are the only screen visible so this avoid starting and then trying to resume a backbuffer render pass.
ScreenRenderFlags flags = UIScreen::render(mode);
const ScreenRenderFlags flags = UIScreen::render(mode);
return flags;
}

View file

@ -331,7 +331,6 @@
<ClInclude Include="..\..\Core\Screenshot.h" />
<ClInclude Include="..\..\Core\System.h" />
<ClInclude Include="..\..\Core\ThreadEventQueue.h" />
<ClInclude Include="..\..\Core\ThreadPools.h" />
<ClInclude Include="..\..\Core\TiltEventProcessor.h" />
<ClInclude Include="..\..\Core\Util\AtracTrack.h" />
<ClInclude Include="..\..\Core\Util\GameDB.h" />
@ -631,7 +630,6 @@
<ClCompile Include="..\..\Core\SaveState.cpp" />
<ClCompile Include="..\..\Core\Screenshot.cpp" />
<ClCompile Include="..\..\Core\System.cpp" />
<ClCompile Include="..\..\Core\ThreadPools.cpp" />
<ClCompile Include="..\..\Core\TiltEventProcessor.cpp" />
<ClCompile Include="..\..\Core\Util\AtracTrack.cpp" />
<ClCompile Include="..\..\Core\Util\GameDB.cpp" />
@ -1042,4 +1040,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -1191,7 +1191,6 @@
<Filter>Ext\libzip</Filter>
</ClCompile>
<ClCompile Include="..\..\Core\KeyMap.cpp" />
<ClCompile Include="..\..\Core\ThreadPools.cpp" />
<ClCompile Include="..\..\Core\ControlMapper.cpp" />
<ClCompile Include="..\..\Core\KeyMapDefaults.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNp2.cpp">
@ -1912,7 +1911,6 @@
<Filter>Ext\jpge</Filter>
</ClInclude>
<ClInclude Include="..\..\Core\KeyMap.h" />
<ClInclude Include="..\..\Core\ThreadPools.h" />
<ClInclude Include="..\..\Core\ControlMapper.h" />
<ClInclude Include="..\..\Core\KeyMapDefaults.h" />
<ClInclude Include="..\..\Core\HLE\sceNp2.h">
@ -1966,4 +1964,4 @@
<Filter>Ext\gason</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View file

@ -619,7 +619,6 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Screenshot.cpp \
$(SRC)/Core/System.cpp \
$(SRC)/Core/TiltEventProcessor.cpp \
$(SRC)/Core/ThreadPools.cpp \
$(SRC)/Core/WebServer.cpp \
$(SRC)/Core/Debugger/Breakpoints.cpp \
$(SRC)/Core/Debugger/DisassemblyManager.cpp \

View file

@ -825,7 +825,6 @@ SOURCES_CXX += \
$(COREDIR)/SaveState.cpp \
$(COREDIR)/Screenshot.cpp \
$(COREDIR)/System.cpp \
$(COREDIR)/ThreadPools.cpp \
$(COREDIR)/Util/AtracTrack.cpp \
$(COREDIR)/Util/BlockAllocator.cpp \
$(COREDIR)/Util/MemStick.cpp \