mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 10:40:01 +01:00
Merge branch 'master' into TemporaryHackishBranch
This commit is contained in:
commit
5952984457
98 files changed
+1152
-527
No files matched your search
+6
-4
@@ -1194,10 +1194,6 @@ if (NOT MOBILE_DEVICE)
|
||||
Core/WaveFile.h)
|
||||
endif()
|
||||
|
||||
if(ARMV7)
|
||||
set(CORE_NEON Core/Util/AudioFormatNEON.cpp Core/Util/AudioFormatNEON.h)
|
||||
endif()
|
||||
|
||||
set(GPU_GLES
|
||||
GPU/GLES/DepalettizeShaderGLES.cpp
|
||||
GPU/GLES/DepalettizeShaderGLES.h
|
||||
@@ -1357,6 +1353,8 @@ set(GPU_SOURCES
|
||||
GPU/Common/SplineCommon.h
|
||||
GPU/Debugger/Breakpoints.cpp
|
||||
GPU/Debugger/Breakpoints.h
|
||||
GPU/Debugger/Debugger.cpp
|
||||
GPU/Debugger/Debugger.h
|
||||
GPU/Debugger/Record.cpp
|
||||
GPU/Debugger/Record.h
|
||||
GPU/Debugger/Stepping.cpp
|
||||
@@ -1430,6 +1428,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/Debugger/WebSocket/GameSubscriber.h
|
||||
Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
|
||||
Core/Debugger/WebSocket/GPUBufferSubscriber.h
|
||||
Core/Debugger/WebSocket/GPURecordSubscriber.cpp
|
||||
Core/Debugger/WebSocket/GPURecordSubscriber.h
|
||||
Core/Debugger/WebSocket/HLESubscriber.cpp
|
||||
Core/Debugger/WebSocket/HLESubscriber.h
|
||||
Core/Debugger/WebSocket/LogBroadcaster.cpp
|
||||
@@ -1709,6 +1709,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/TextureReplacer.h
|
||||
Core/Util/AudioFormat.cpp
|
||||
Core/Util/AudioFormat.h
|
||||
Core/Util/AudioFormatNEON.cpp
|
||||
Core/Util/AudioFormatNEON.h
|
||||
Core/Util/GameManager.cpp
|
||||
Core/Util/GameManager.h
|
||||
Core/Util/BlockAllocator.cpp
|
||||
|
||||
@@ -643,7 +643,7 @@ Convert16bppTo16bppFunc ConvertRGB565ToBGR565 = &ConvertRGB565ToBGR565Basic;
|
||||
#endif
|
||||
|
||||
void SetupColorConv() {
|
||||
#if PPSSPP_ARCH(ARMV7) && PPSSPP_ARCH(ARM_NEON)
|
||||
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
|
||||
if (cpu_info.bNEON) {
|
||||
ConvertRGBA4444ToABGR4444 = &ConvertRGBA4444ToABGR4444NEON;
|
||||
ConvertRGBA5551ToABGR1555 = &ConvertRGBA5551ToABGR1555NEON;
|
||||
|
||||
+10
-6
@@ -269,9 +269,11 @@ bool Delete(const std::string &filename) {
|
||||
// Returns true if successful, or path already exists.
|
||||
bool CreateDir(const std::string &path)
|
||||
{
|
||||
DEBUG_LOG(COMMON, "CreateDir('%s')", path.c_str());
|
||||
std::string fn = path;
|
||||
StripTailDirSlashes(fn);
|
||||
DEBUG_LOG(COMMON, "CreateDir('%s')", fn.c_str());
|
||||
#ifdef _WIN32
|
||||
if (::CreateDirectory(ConvertUTF8ToWString(path).c_str(), NULL))
|
||||
if (::CreateDirectory(ConvertUTF8ToWString(fn).c_str(), NULL))
|
||||
return true;
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_ALREADY_EXISTS)
|
||||
@@ -282,25 +284,27 @@ bool CreateDir(const std::string &path)
|
||||
ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error);
|
||||
return false;
|
||||
#else
|
||||
if (mkdir(path.c_str(), 0755) == 0)
|
||||
if (mkdir(fn.c_str(), 0755) == 0)
|
||||
return true;
|
||||
|
||||
int err = errno;
|
||||
|
||||
if (err == EEXIST)
|
||||
{
|
||||
WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str());
|
||||
WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", fn.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err));
|
||||
ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", fn.c_str(), strerror(err));
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Creates the full path of fullPath returns true on success
|
||||
bool CreateFullPath(const std::string &fullPath)
|
||||
bool CreateFullPath(const std::string &path)
|
||||
{
|
||||
std::string fullPath = path;
|
||||
StripTailDirSlashes(fullPath);
|
||||
int panicCounter = 100;
|
||||
VERBOSE_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
|
||||
|
||||
|
||||
@@ -1040,6 +1040,8 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
|
||||
|
||||
glslang::TProgram program;
|
||||
const char *shaderStrings[1];
|
||||
EProfile profile = ECoreProfile;
|
||||
int defaultVersion = 450;
|
||||
TBuiltInResource Resources;
|
||||
init_resources(Resources);
|
||||
|
||||
@@ -1052,7 +1054,7 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
|
||||
shaderStrings[0] = pshader;
|
||||
shader.setStrings(shaderStrings, 1);
|
||||
|
||||
if (!shader.parse(&Resources, 100, false, messages)) {
|
||||
if (!shader.parse(&Resources, defaultVersion, profile, false, true, messages)) {
|
||||
puts(shader.getInfoLog());
|
||||
puts(shader.getInfoDebugLog());
|
||||
if (errorMessage) {
|
||||
@@ -1076,7 +1078,11 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
|
||||
}
|
||||
|
||||
// Can't fail, parsing worked, "linking" worked.
|
||||
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
|
||||
glslang::SpvOptions options;
|
||||
options.disableOptimizer = false;
|
||||
options.optimizeSize = false;
|
||||
options.generateDebugInfo = false;
|
||||
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv, &options);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+49
-1
@@ -19,6 +19,7 @@
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
#include "base/display.h"
|
||||
#include "base/NativeApp.h"
|
||||
@@ -551,6 +552,49 @@ static int DefaultGPUBackend() {
|
||||
return (int)GPUBackend::OPENGL;
|
||||
}
|
||||
|
||||
int Config::NextValidBackend() {
|
||||
std::vector<std::string> split;
|
||||
std::set<int> failed;
|
||||
SplitString(sFailedGPUBackends, ',', split);
|
||||
for (const auto &str : split) {
|
||||
if (!str.empty() && str != "ALL") {
|
||||
failed.insert(atoi(str.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (failed.count(iGPUBackend)) {
|
||||
ERROR_LOG(LOADER, "Graphics backend failed for %d, trying another", iGPUBackend);
|
||||
|
||||
#if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) && !PPSSPP_PLATFORM(UWP)
|
||||
if (VulkanMayBeAvailable() && !failed.count((int)GPUBackend::VULKAN)) {
|
||||
return (int)GPUBackend::VULKAN;
|
||||
}
|
||||
#endif
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
if (DoesVersionMatchWindows(6, 1, 0, 0, true) && !failed.count((int)GPUBackend::DIRECT3D11)) {
|
||||
return (int)GPUBackend::DIRECT3D11;
|
||||
}
|
||||
#endif
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
if (!failed.count((int)GPUBackend::OPENGL)) {
|
||||
return (int)GPUBackend::OPENGL;
|
||||
}
|
||||
#endif
|
||||
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
|
||||
if (!failed.count((int)GPUBackend::DIRECT3D9)) {
|
||||
return (int)GPUBackend::DIRECT3D9;
|
||||
}
|
||||
#endif
|
||||
|
||||
// They've all failed. Let them try the default.
|
||||
sFailedGPUBackends += ",ALL";
|
||||
ERROR_LOG(LOADER, "All graphics backends failed");
|
||||
return DefaultGPUBackend();
|
||||
}
|
||||
|
||||
return iGPUBackend;
|
||||
}
|
||||
|
||||
static bool DefaultVertexCache() {
|
||||
return DefaultGPUBackend() == (int)GPUBackend::OPENGL;
|
||||
}
|
||||
@@ -562,6 +606,7 @@ static ConfigSetting graphicsSettings[] = {
|
||||
ConfigSetting("CardboardYShift", &g_Config.iCardboardXShift, 0, true, true),
|
||||
ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true),
|
||||
ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend),
|
||||
ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, ""),
|
||||
ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false),
|
||||
#ifdef _WIN32
|
||||
ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false),
|
||||
@@ -1008,7 +1053,10 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
||||
auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap();
|
||||
vPinnedPaths.clear();
|
||||
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
|
||||
vPinnedPaths.push_back(it->second);
|
||||
// Unpin paths that are deleted automatically.
|
||||
if (File::Exists(it->second)) {
|
||||
vPinnedPaths.push_back(File::ResolvePath(it->second));
|
||||
}
|
||||
}
|
||||
|
||||
// This caps the exponent 4 (so 16x.)
|
||||
|
||||
@@ -128,6 +128,7 @@ public:
|
||||
|
||||
// GFX
|
||||
int iGPUBackend;
|
||||
std::string sFailedGPUBackends;
|
||||
// We have separate device parameters for each backend so it doesn't get erased if you switch backends.
|
||||
// If not set, will use the "best" device.
|
||||
std::string sVulkanDevice;
|
||||
@@ -454,6 +455,7 @@ public:
|
||||
void GetReportingInfo(UrlEncoder &data);
|
||||
|
||||
bool IsPortrait() const;
|
||||
int NextValidBackend();
|
||||
|
||||
protected:
|
||||
void LoadStandardControllerIni();
|
||||
|
||||
@@ -188,6 +188,7 @@
|
||||
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\GameSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\GPUBufferSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\HLESubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
|
||||
@@ -549,6 +550,7 @@
|
||||
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\GPUBufferSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\HLESubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />
|
||||
|
||||
@@ -734,6 +734,9 @@
|
||||
<ClCompile Include="HLE\sceUsbAcc.cpp">
|
||||
<Filter>HLE\Libraries</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@@ -1361,6 +1364,9 @@
|
||||
<ClInclude Include="HLE\sceUsbAcc.h">
|
||||
<Filter>HLE\Libraries</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
||||
+21
-23
@@ -53,24 +53,20 @@
|
||||
#include "Core/Debugger/WebSocket/DisasmSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/GameSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/HLESubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"
|
||||
|
||||
typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map);
|
||||
typedef void (*Subscribershutdown)(void *p);
|
||||
struct SubscriberInfo {
|
||||
SubscriberInit init;
|
||||
Subscribershutdown shutdown;
|
||||
};
|
||||
|
||||
static const std::vector<SubscriberInfo> subscribers({
|
||||
{ &WebSocketBreakpointInit, nullptr },
|
||||
{ &WebSocketCPUCoreInit, nullptr },
|
||||
{ &WebSocketDisasmInit, &WebSocketDisasmShutdown },
|
||||
{ &WebSocketGameInit, nullptr },
|
||||
{ &WebSocketGPUBufferInit, nullptr },
|
||||
{ &WebSocketHLEInit, nullptr },
|
||||
{ &WebSocketSteppingInit, &WebSocketSteppingShutdown },
|
||||
typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map);
|
||||
static const std::vector<SubscriberInit> subscribers({
|
||||
&WebSocketBreakpointInit,
|
||||
&WebSocketCPUCoreInit,
|
||||
&WebSocketDisasmInit,
|
||||
&WebSocketGameInit,
|
||||
&WebSocketGPUBufferInit,
|
||||
&WebSocketGPURecordInit,
|
||||
&WebSocketHLEInit,
|
||||
&WebSocketSteppingInit,
|
||||
});
|
||||
|
||||
// To handle webserver restart, keep track of how many running.
|
||||
@@ -132,10 +128,10 @@ void HandleDebuggerRequest(const http::Request &request) {
|
||||
SteppingBroadcaster stepping;
|
||||
|
||||
std::unordered_map<std::string, DebuggerEventHandler> eventHandlers;
|
||||
std::vector<void *> subscriberData;
|
||||
for (auto info : subscribers) {
|
||||
std::vector<DebuggerSubscriber *> subscriberData;
|
||||
for (auto init : subscribers) {
|
||||
std::lock_guard<std::mutex> guard(lifecycleLock);
|
||||
subscriberData.push_back(info.init(eventHandlers));
|
||||
subscriberData.push_back(init(eventHandlers));
|
||||
}
|
||||
|
||||
// There's a tradeoff between responsiveness to incoming events, and polling for changes.
|
||||
@@ -178,6 +174,12 @@ void HandleDebuggerRequest(const http::Request &request) {
|
||||
game.Broadcast(ws);
|
||||
stepping.Broadcast(ws);
|
||||
|
||||
for (size_t i = 0; i < subscribers.size(); ++i) {
|
||||
if (subscriberData[i]) {
|
||||
subscriberData[i]->Broadcast(ws);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopRequested) {
|
||||
ws->Close(net::WebSocketClose::GOING_AWAY);
|
||||
}
|
||||
@@ -188,11 +190,7 @@ void HandleDebuggerRequest(const http::Request &request) {
|
||||
|
||||
std::lock_guard<std::mutex> guard(lifecycleLock);
|
||||
for (size_t i = 0; i < subscribers.size(); ++i) {
|
||||
if (subscribers[i].shutdown) {
|
||||
subscribers[i].shutdown(subscriberData[i]);
|
||||
} else {
|
||||
assert(!subscriberData[i]);
|
||||
}
|
||||
delete subscriberData[i];
|
||||
}
|
||||
|
||||
delete ws;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
|
||||
void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
|
||||
// No need to bind or alloc state, these are all global.
|
||||
map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd;
|
||||
map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
|
||||
DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
|
||||
|
||||
void WebSocketCPUBreakpointAdd(DebuggerRequest &req);
|
||||
void WebSocketCPUBreakpointUpdate(DebuggerRequest &req);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
|
||||
void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
|
||||
// No need to bind or alloc state, these are all global.
|
||||
map["cpu.stepping"] = &WebSocketCPUStepping;
|
||||
map["cpu.resume"] = &WebSocketCPUResume;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
|
||||
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
|
||||
|
||||
void WebSocketCPUStepping(DebuggerRequest &req);
|
||||
void WebSocketCPUResume(DebuggerRequest &req);
|
||||
|
||||
@@ -28,11 +28,12 @@
|
||||
#include "Core/MIPS/MIPSAsm.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
|
||||
struct WebSocketDisasmState {
|
||||
class WebSocketDisasmState : public DebuggerSubscriber {
|
||||
public:
|
||||
WebSocketDisasmState() {
|
||||
disasm_.setCpu(currentDebugMIPS);
|
||||
}
|
||||
~WebSocketDisasmState() {
|
||||
~WebSocketDisasmState() override {
|
||||
disasm_.clear();
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ protected:
|
||||
DisassemblyManager disasm_;
|
||||
};
|
||||
|
||||
void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
|
||||
auto p = new WebSocketDisasmState();
|
||||
map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1);
|
||||
map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1);
|
||||
@@ -59,10 +60,6 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void WebSocketDisasmShutdown(void *p) {
|
||||
delete static_cast<WebSocketDisasmState *>(p);
|
||||
}
|
||||
|
||||
static DebugInterface *CPUFromRequest(DebuggerRequest &req) {
|
||||
if (!req.HasParam("thread"))
|
||||
return currentDebugMIPS;
|
||||
|
||||
@@ -19,5 +19,4 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
|
||||
void WebSocketDisasmShutdown(void *p);
|
||||
DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "Core/Screenshot.h"
|
||||
#include "GPU/Debugger/Stepping.h"
|
||||
|
||||
void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
|
||||
// No need to bind or alloc state, these are all global.
|
||||
map["gpu.buffer.screenshot"] = &WebSocketGPUBufferScreenshot;
|
||||
map["gpu.buffer.renderColor"] = &WebSocketGPUBufferRenderColor;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
|
||||
DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
|
||||
|
||||
void WebSocketGPUBufferScreenshot(DebuggerRequest &req);
|
||||
void WebSocketGPUBufferRenderColor(DebuggerRequest &req);
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2018- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "data/base64.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
|
||||
struct WebSocketGPURecordState : public DebuggerSubscriber {
|
||||
~WebSocketGPURecordState() override;
|
||||
void Dump(DebuggerRequest &req);
|
||||
|
||||
void Broadcast(net::WebSocketServer *ws) override;
|
||||
|
||||
protected:
|
||||
bool pending_ = false;
|
||||
std::string lastTicket_;
|
||||
std::string lastFilename_;
|
||||
};
|
||||
|
||||
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) {
|
||||
auto p = new WebSocketGPURecordState();
|
||||
map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
WebSocketGPURecordState::~WebSocketGPURecordState() {
|
||||
// Clear the callback to hopefully avoid a crash.
|
||||
if (pending_)
|
||||
GPURecord::SetCallback(nullptr);
|
||||
}
|
||||
|
||||
// Begin recording (gpu.record.dump)
|
||||
//
|
||||
// No parameters.
|
||||
//
|
||||
// Response (same event name):
|
||||
// - uri: data: URI containing debug dump data.
|
||||
//
|
||||
// Note: recording may take a moment.
|
||||
void WebSocketGPURecordState::Dump(DebuggerRequest &req) {
|
||||
if (!PSP_IsInited())
|
||||
return req.Fail("CPU not started");
|
||||
|
||||
if (!GPURecord::Activate())
|
||||
return req.Fail("Recording already in progress");
|
||||
|
||||
pending_ = true;
|
||||
GPURecord::SetCallback([=](const std::string &filename) {
|
||||
lastFilename_ = filename;
|
||||
pending_ = false;
|
||||
});
|
||||
|
||||
const JsonNode *value = req.data.get("ticket");
|
||||
lastTicket_ = value ? json_stringify(value) : "";
|
||||
}
|
||||
|
||||
// This handles the asynchronous gpu.record.dump response.
|
||||
void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) {
|
||||
if (!lastFilename_.empty()) {
|
||||
FILE *fp = File::OpenCFile(lastFilename_, "rb");
|
||||
if (!fp) {
|
||||
lastFilename_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// We write directly to the stream since this is a large chunk of data.
|
||||
ws->AddFragment(false, R"({"event":"gpu.record.dump")");
|
||||
if (!lastTicket_.empty()) {
|
||||
ws->AddFragment(false, R"(,"ticket":)");
|
||||
ws->AddFragment(false, lastTicket_);
|
||||
}
|
||||
ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)");
|
||||
|
||||
// Divisible by 3 for base64 reasons.
|
||||
const size_t BUF_SIZE = 16383;
|
||||
std::vector<uint8_t> buf;
|
||||
buf.resize(BUF_SIZE);
|
||||
while (!feof(fp)) {
|
||||
size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp);
|
||||
ws->AddFragment(false, Base64Encode(&buf[0], bytes));
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
ws->AddFragment(true, R"("})");
|
||||
|
||||
lastFilename_.clear();
|
||||
lastTicket_.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2018- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map);
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "Core/ELF/ParamSFO.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
void *WebSocketGameInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) {
|
||||
map["game.status"] = &WebSocketGameStatus;
|
||||
map["version"] = &WebSocketVersion;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketGameInit(DebuggerEventHandlerMap &map);
|
||||
DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map);
|
||||
|
||||
void WebSocketGameStatus(DebuggerRequest &req);
|
||||
void WebSocketVersion(DebuggerRequest &req);
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "Core/MIPS/MIPSStackWalk.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
|
||||
void *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
|
||||
map["hle.thread.list"] = &WebSocketHLEThreadList;
|
||||
map["hle.thread.wake"] = &WebSocketHLEThreadWake;
|
||||
map["hle.thread.stop"] = &WebSocketHLEThreadStop;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketHLEInit(DebuggerEventHandlerMap &map);
|
||||
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map);
|
||||
|
||||
void WebSocketHLEThreadList(DebuggerRequest &req);
|
||||
void WebSocketHLEThreadWake(DebuggerRequest &req);
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
|
||||
using namespace MIPSAnalyst;
|
||||
|
||||
struct WebSocketSteppingState {
|
||||
struct WebSocketSteppingState : public DebuggerSubscriber {
|
||||
WebSocketSteppingState() {
|
||||
disasm_.setCpu(currentDebugMIPS);
|
||||
}
|
||||
~WebSocketSteppingState() {
|
||||
~WebSocketSteppingState() override {
|
||||
disasm_.clear();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ protected:
|
||||
DisassemblyManager disasm_;
|
||||
};
|
||||
|
||||
void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
|
||||
DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
|
||||
auto p = new WebSocketSteppingState();
|
||||
map["cpu.stepInto"] = std::bind(&WebSocketSteppingState::Into, p, std::placeholders::_1);
|
||||
map["cpu.stepOver"] = std::bind(&WebSocketSteppingState::Over, p, std::placeholders::_1);
|
||||
@@ -62,10 +62,6 @@ void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void WebSocketSteppingShutdown(void *p) {
|
||||
delete static_cast<WebSocketSteppingState *>(p);
|
||||
}
|
||||
|
||||
static DebugInterface *CPUFromRequest(DebuggerRequest &req, uint32_t *threadID = nullptr) {
|
||||
if (!req.HasParam("thread")) {
|
||||
if (threadID)
|
||||
|
||||
@@ -19,5 +19,4 @@
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
void *WebSocketSteppingInit(DebuggerEventHandlerMap &map);
|
||||
void WebSocketSteppingShutdown(void *p);
|
||||
DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map);
|
||||
@@ -98,5 +98,13 @@ private:
|
||||
bool responsePartial_ = false;
|
||||
};
|
||||
|
||||
class DebuggerSubscriber {
|
||||
public:
|
||||
virtual ~DebuggerSubscriber() {}
|
||||
|
||||
// Subscribers can also broadcast if they have simple cases to.
|
||||
virtual void Broadcast(net::WebSocketServer *ws) {}
|
||||
};
|
||||
|
||||
typedef std::function<void(DebuggerRequest &req)> DebuggerEventHandler;
|
||||
typedef std::unordered_map<std::string, DebuggerEventHandler> DebuggerEventHandlerMap;
|
||||
@@ -362,13 +362,13 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) {
|
||||
}
|
||||
}
|
||||
|
||||
void PSPSaveDialog::DisplaySaveIcon()
|
||||
void PSPSaveDialog::DisplaySaveIcon(bool checkExists)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(paramLock);
|
||||
int textureColor = CalcFadedColor(0xFFFFFFFF);
|
||||
auto curSave = param.GetFileInfo(currentSelectedSave);
|
||||
|
||||
if (curSave.size == 0)
|
||||
if (curSave.size == 0 && checkExists)
|
||||
textureColor = CalcFadedColor(0xFF777777);
|
||||
|
||||
// Calc save image position on screen
|
||||
@@ -652,7 +652,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
case DS_SAVE_CONFIRM:
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(false);
|
||||
DisplaySaveDataInfo2(true);
|
||||
|
||||
DisplayMessage(di->T("Confirm Save", "Do you want to save this data?"), true);
|
||||
@@ -673,7 +673,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
case DS_SAVE_CONFIRM_OVERWRITE:
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(di->T("Confirm Overwrite","Do you want to overwrite the data?"), true);
|
||||
@@ -702,7 +702,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2(true);
|
||||
|
||||
DisplayMessage(di->T("Saving","Saving\nPlease Wait..."));
|
||||
@@ -715,7 +715,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
JoinIOThread();
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2(true);
|
||||
|
||||
DisplayMessage(di->T("SavingFailed", "Unable to save data."));
|
||||
@@ -742,7 +742,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
}
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2(true);
|
||||
|
||||
DisplayMessage(di->T("Save completed"));
|
||||
@@ -782,7 +782,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
case DS_LOAD_CONFIRM:
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(di->T("ConfirmLoad", "Load this data?"), true);
|
||||
@@ -807,7 +807,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(di->T("Loading","Loading\nPlease Wait..."));
|
||||
@@ -820,7 +820,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
JoinIOThread();
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(di->T("LoadingFailed", "Unable to load data."));
|
||||
@@ -844,7 +844,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
JoinIOThread();
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(di->T("Load completed"));
|
||||
@@ -902,7 +902,7 @@ int PSPSaveDialog::Update(int animSpeed)
|
||||
case DS_DELETE_CONFIRM:
|
||||
StartDraw();
|
||||
|
||||
DisplaySaveIcon();
|
||||
DisplaySaveIcon(true);
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(di->T("DeleteConfirm",
|
||||
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
|
||||
void DisplayBanner(int which);
|
||||
void DisplaySaveList(bool canMove = true);
|
||||
void DisplaySaveIcon();
|
||||
void DisplaySaveIcon(bool checkExists);
|
||||
void DisplaySaveDataInfo1();
|
||||
void DisplaySaveDataInfo2(bool showNewData = false);
|
||||
void DisplayMessage(std::string text, bool hasYesNo = false);
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Common/PostShader.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
|
||||
struct FrameBufferState {
|
||||
u32 topaddr;
|
||||
@@ -737,7 +738,7 @@ void __DisplayFlip(int cyclesLate) {
|
||||
// 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
|
||||
maxFrameskip = g_Config.iFrameSkip;
|
||||
}
|
||||
if (numSkippedFrames >= maxFrameskip) {
|
||||
if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) {
|
||||
skipFrame = false;
|
||||
}
|
||||
|
||||
|
||||
+13
-12
@@ -299,7 +299,8 @@ static u32 getMpegVersion(u32 mpegRawVersion) {
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) {
|
||||
|
||||
static void AnalyzeMpeg(u8 *buffer, u32 validSize, MpegContext *ctx) {
|
||||
ctx->mpegMagic = *(u32_le*)buffer;
|
||||
ctx->mpegRawVersion = *(u32_le*)(buffer + PSMF_STREAM_VERSION_OFFSET);
|
||||
ctx->mpegVersion = getMpegVersion(ctx->mpegRawVersion);
|
||||
@@ -329,7 +330,7 @@ static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->mediaengine && (ctx->mpegStreamSize > 0) && !ctx->isAnalyzed) {
|
||||
if (!ctx->isAnalyzed && ctx->mediaengine && ctx->mpegStreamSize > 0 && validSize >= ctx->mpegOffset) {
|
||||
// init mediaEngine
|
||||
auto ringbuffer = PSPPointer<SceMpegRingBuffer>::Create(ctx->mpegRingbufferAddr);
|
||||
if (ringbuffer.IsValid()) {
|
||||
@@ -338,16 +339,16 @@ static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) {
|
||||
// TODO: Does this make any sense?
|
||||
ctx->mediaengine->loadStream(buffer, ctx->mpegOffset, 0);
|
||||
}
|
||||
|
||||
// When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset
|
||||
// and sceMpegQueryStreamSize, which forces a packet overwrite in the Media Engine and in
|
||||
// the MPEG ringbuffer.
|
||||
// Mark the current MPEG as analyzed to filter this, and restore it at sceMpegFinish.
|
||||
ctx->isAnalyzed = true;
|
||||
}
|
||||
|
||||
// When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset
|
||||
// and sceMpegQueryStreamSize, which forces a packet overwrite in the Media Engine and in
|
||||
// the MPEG ringbuffer.
|
||||
// Mark the current MPEG as analyzed to filter this, and restore it at sceMpegFinish.
|
||||
ctx->isAnalyzed = true;
|
||||
|
||||
// copy header struct to mpeg header.
|
||||
memcpy(ctx->mpegheader, buffer, 2048);
|
||||
memcpy(ctx->mpegheader, buffer, validSize >= 2048 ? 2048 : validSize);
|
||||
*(u32_le*)(ctx->mpegheader + PSMF_STREAM_OFFSET_OFFSET) = 0x80000;
|
||||
|
||||
INFO_LOG(ME, "Stream offset: %d, Stream size: 0x%X", ctx->mpegOffset, ctx->mpegStreamSize);
|
||||
@@ -601,7 +602,7 @@ static int sceMpegQueryStreamOffset(u32 mpeg, u32 bufferAddr, u32 offsetAddr)
|
||||
DEBUG_LOG(ME, "sceMpegQueryStreamOffset(%08x, %08x, %08x)", mpeg, bufferAddr, offsetAddr);
|
||||
|
||||
// Kinda destructive, no?
|
||||
AnalyzeMpeg(Memory::GetPointer(bufferAddr), ctx);
|
||||
AnalyzeMpeg(Memory::GetPointer(bufferAddr), Memory::ValidSize(bufferAddr, 32768), ctx);
|
||||
|
||||
if (ctx->mpegMagic != PSMF_MAGIC) {
|
||||
ERROR_LOG(ME, "sceMpegQueryStreamOffset: Bad PSMF magic");
|
||||
@@ -633,7 +634,7 @@ static u32 sceMpegQueryStreamSize(u32 bufferAddr, u32 sizeAddr)
|
||||
MpegContext ctx;
|
||||
ctx.mediaengine = 0;
|
||||
|
||||
AnalyzeMpeg(Memory::GetPointer(bufferAddr), &ctx);
|
||||
AnalyzeMpeg(Memory::GetPointer(bufferAddr), Memory::ValidSize(bufferAddr, 32768), &ctx);
|
||||
|
||||
if (ctx.mpegMagic != PSMF_MAGIC) {
|
||||
ERROR_LOG(ME, "sceMpegQueryStreamSize: Bad PSMF magic");
|
||||
@@ -1465,7 +1466,7 @@ void PostPutAction::run(MipsCall &call) {
|
||||
|
||||
if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAdded > 0) {
|
||||
// init mediaEngine
|
||||
AnalyzeMpeg(ctx->mpegheader, ctx);
|
||||
AnalyzeMpeg(ctx->mpegheader, 2048, ctx);
|
||||
ctx->mediaengine->loadStream(ctx->mpegheader, 2048, ringbuffer->packets * ringbuffer->packetSize);
|
||||
}
|
||||
if (packetsAdded > 0) {
|
||||
|
||||
@@ -50,13 +50,6 @@ public:
|
||||
virtual void SaveSymbolMap() {}
|
||||
virtual void SetWindowTitle(const char *message) {}
|
||||
|
||||
// While debugging is active, it's perfectly fine for these to block.
|
||||
virtual bool GPUDebuggingActive() { return false; }
|
||||
virtual void GPUNotifyCommand(u32 pc) {}
|
||||
virtual void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {}
|
||||
virtual void GPUNotifyDraw() {}
|
||||
virtual void GPUNotifyTextureAttachment(u32 addr) {}
|
||||
|
||||
virtual bool CanCreateShortcut() {return false;}
|
||||
virtual bool CreateDesktopShortcut(std::string argumentPath, std::string title) {return false;}
|
||||
|
||||
|
||||
+3
-8
@@ -411,13 +411,8 @@ namespace SaveState
|
||||
}
|
||||
fullDiscId = StringFromFormat("%s_%s", discId.c_str(), discVer.c_str());
|
||||
|
||||
std::string temp = StringFromFormat("ms0:/PSP/PPSSPP_STATE/%s_%d.%s", fullDiscId.c_str(), slot, extension);
|
||||
std::string hostPath;
|
||||
if (pspFileSystem.GetHostPath(temp, hostPath)) {
|
||||
return hostPath;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
std::string filename = StringFromFormat("%s_%d.%s", fullDiscId.c_str(), slot, extension);
|
||||
return GetSysDirectory(DIRECTORY_SAVESTATE) + filename;
|
||||
}
|
||||
|
||||
int GetCurrentSlot()
|
||||
@@ -883,7 +878,7 @@ namespace SaveState
|
||||
void Init()
|
||||
{
|
||||
// Make sure there's a directory for save slots
|
||||
pspFileSystem.MkDir("ms0:/PSP/PPSSPP_STATE");
|
||||
File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVESTATE));
|
||||
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
rewindStates.Clear();
|
||||
|
||||
+3
-3
@@ -596,9 +596,9 @@ void InitSysDirectories() {
|
||||
// expect a standard environment. Skipping THEME though, that's pointless.
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/COMMON");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/GAME");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/SAVEDATA");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/PPSSPP_STATE");
|
||||
File::CreateDir(GetSysDirectory(DIRECTORY_GAME));
|
||||
File::CreateDir(GetSysDirectory(DIRECTORY_SAVEDATA));
|
||||
File::CreateDir(GetSysDirectory(DIRECTORY_SAVESTATE));
|
||||
|
||||
if (g_Config.currentDirectory.empty()) {
|
||||
g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME);
|
||||
|
||||
+12
-15
@@ -58,6 +58,7 @@ void TextureReplacer::NotifyConfigChanged() {
|
||||
// If we're saving, auto-create the directory.
|
||||
if (g_Config.bSaveNewTextures && !File::Exists(basePath_ + NEW_TEXTURE_DIR)) {
|
||||
File::CreateFullPath(basePath_ + NEW_TEXTURE_DIR);
|
||||
File::CreateEmptyFile(basePath_ + NEW_TEXTURE_DIR + "/.nomedia");
|
||||
}
|
||||
|
||||
enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_);
|
||||
@@ -111,29 +112,24 @@ bool TextureReplacer::LoadIni() {
|
||||
ERROR_LOG(G3D, "Unsupported texture replacement version %d, trying anyway", version);
|
||||
}
|
||||
|
||||
std::vector<std::string> hashNames;
|
||||
if (ini.GetKeys("hashes", hashNames)) {
|
||||
auto hashes = ini.GetOrCreateSection("hashes");
|
||||
if (ini.HasSection("hashes")) {
|
||||
auto hashes = ini.GetOrCreateSection("hashes")->ToMap();
|
||||
// Format: hashname = filename.png
|
||||
for (std::string hashName : hashNames) {
|
||||
for (const auto &item : hashes) {
|
||||
ReplacementAliasKey key(0, 0, 0);
|
||||
if (sscanf(hashName.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &key.level) >= 1) {
|
||||
hashes->Get(hashName.c_str(), &aliases_[key], "");
|
||||
if (sscanf(item.first.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &key.level) >= 1) {
|
||||
aliases_[key] = item.second;
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Unsupported syntax under [hashes]: %s", hashName.c_str());
|
||||
ERROR_LOG(G3D, "Unsupported syntax under [hashes]: %s", item.first.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> hashrangeKeys;
|
||||
if (ini.GetKeys("hashranges", hashrangeKeys)) {
|
||||
auto hashranges = ini.GetOrCreateSection("hashranges");
|
||||
if (ini.HasSection("hashranges")) {
|
||||
auto hashranges = ini.GetOrCreateSection("hashranges")->ToMap();
|
||||
// Format: addr,w,h = newW,newH
|
||||
for (const std::string &key : hashrangeKeys) {
|
||||
std::string value;
|
||||
if (hashranges->Get(key.c_str(), &value, "")) {
|
||||
ParseHashRange(key, value);
|
||||
}
|
||||
for (const auto &item : hashranges) {
|
||||
ParseHashRange(item.first, item.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,6 +396,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
||||
const std::string saveDirectory = basePath_ + NEW_TEXTURE_DIR + hashfile.substr(0, slash);
|
||||
if (!File::Exists(saveDirectory)) {
|
||||
File::CreateFullPath(saveDirectory);
|
||||
File::CreateEmptyFile(saveDirectory + "/.nomedia");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,12 +110,12 @@ void ConvertS16ToF32(float *out, const s16 *in, size_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _M_SSE
|
||||
#if !defined(_M_SSE) && !PPSSPP_ARCH(ARM64)
|
||||
AdjustVolumeBlockFunc AdjustVolumeBlock = &AdjustVolumeBlockStandard;
|
||||
|
||||
// This has to be done after CPUDetect has done its magic.
|
||||
void SetupAudioFormats() {
|
||||
#if PPSSPP_ARCH(ARMV7) && PPSSPP_ARCH(ARM_NEON)
|
||||
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
|
||||
if (cpu_info.bNEON) {
|
||||
AdjustVolumeBlock = &AdjustVolumeBlockNEON;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ppsspp_config.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/Util/AudioFormatNEON.h"
|
||||
|
||||
#define IS_LITTLE_ENDIAN (*(const u16 *)"\0\xff" >= 0x100)
|
||||
|
||||
@@ -71,6 +72,8 @@ void ConvertS16ToF32(float *ou, const s16 *in, size_t size);
|
||||
|
||||
#ifdef _M_SSE
|
||||
#define AdjustVolumeBlock AdjustVolumeBlockStandard
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#define AdjustVolumeBlock AdjustVolumeBlockNEON
|
||||
#else
|
||||
typedef void (*AdjustVolumeBlockFunc)(s16 *out, s16 *in, size_t size, int leftVol, int rightVol);
|
||||
extern AdjustVolumeBlockFunc AdjustVolumeBlock;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "Core/Util/AudioFormat.h"
|
||||
#include "Core/Util/AudioFormatNEON.h"
|
||||
|
||||
#if !defined(ARM) && !defined(ARM64)
|
||||
#if !PPSSPP_ARCH(ARM) && !PPSSPP_ARCH(ARM64)
|
||||
#error Should not be compiled on non-ARM.
|
||||
#endif
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
@@ -29,6 +28,7 @@
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/ShaderId.h"
|
||||
#include "GPU/Common/GPUStateUtils.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
|
||||
@@ -262,6 +262,19 @@ void TextureCacheCommon::UpdateSamplingParams(TexCacheEntry &entry, SamplerCache
|
||||
void TextureCacheCommon::UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode) {
|
||||
// If the texture is >= 512 pixels tall...
|
||||
if (entry->dim >= 0x900) {
|
||||
if (entry->cluthash != 0 && entry->maxSeenV == 0) {
|
||||
const u64 cachekeyMin = (u64)(entry->addr & 0x3FFFFFFF) << 32;
|
||||
const u64 cachekeyMax = cachekeyMin + (1ULL << 32);
|
||||
for (auto it = cache_.lower_bound(cachekeyMin), end = cache_.upper_bound(cachekeyMax); it != end; ++it) {
|
||||
// They should all be the same, just make sure we take any that has already increased.
|
||||
// This is for a new texture.
|
||||
if (it->second->maxSeenV != 0) {
|
||||
entry->maxSeenV = it->second->maxSeenV;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Texture scale/offset and gen modes don't apply in through.
|
||||
// So we can optimize how much of the texture we look at.
|
||||
if (throughMode) {
|
||||
@@ -279,6 +292,16 @@ void TextureCacheCommon::UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode)
|
||||
// TODO: We could tell for texcoord UV gen, and apply scale to max?
|
||||
entry->maxSeenV = 512;
|
||||
}
|
||||
|
||||
// We need to keep all CLUT variants in sync so we detect changes properly.
|
||||
// See HandleTextureChange / STATUS_CLUT_RECHECK.
|
||||
if (entry->cluthash != 0) {
|
||||
const u64 cachekeyMin = (u64)(entry->addr & 0x3FFFFFFF) << 32;
|
||||
const u64 cachekeyMax = cachekeyMin + (1ULL << 32);
|
||||
for (auto it = cache_.lower_bound(cachekeyMin), end = cache_.upper_bound(cachekeyMax); it != end; ++it) {
|
||||
it->second->maxSeenV = entry->maxSeenV;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,7 +715,7 @@ void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFra
|
||||
entry->maxLevel = 0;
|
||||
fbTexInfo_[cachekey] = fbInfo;
|
||||
framebuffer->last_frame_attached = gpuStats.numFlips;
|
||||
host->GPUNotifyTextureAttachment(entry->addr);
|
||||
GPUDebug::NotifyTextureAttachment(entry->addr);
|
||||
} else if (entry->framebuffer == framebuffer) {
|
||||
framebuffer->last_frame_attached = gpuStats.numFlips;
|
||||
}
|
||||
@@ -711,7 +734,7 @@ void TextureCacheCommon::AttachFramebufferInvalid(TexCacheEntry *entry, VirtualF
|
||||
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
entry->maxLevel = 0;
|
||||
fbTexInfo_[cachekey] = fbInfo;
|
||||
host->GPUNotifyTextureAttachment(entry->addr);
|
||||
GPUDebug::NotifyTextureAttachment(entry->addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +747,7 @@ void TextureCacheCommon::DetachFramebuffer(TexCacheEntry *entry, u32 address, Vi
|
||||
// Otherwise we never recreate the texture.
|
||||
entry->hash ^= 1;
|
||||
fbTexInfo_.erase(cachekey);
|
||||
host->GPUNotifyTextureAttachment(entry->addr);
|
||||
GPUDebug::NotifyTextureAttachment(entry->addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+169
-117
@@ -203,51 +203,54 @@ void DoSwizzleTex16(const u32 *ysrcp, u8 *texptr, int bxc, int byc, u32 pitch) {
|
||||
// ysrcp is in 32-bits, so this is convenient.
|
||||
const u32 pitchBy32 = pitch >> 2;
|
||||
#ifdef _M_SSE
|
||||
__m128i *dest = (__m128i *)texptr;
|
||||
// The pitch parameter is in bytes, so shift down for 128-bit.
|
||||
// Note: it's always aligned to 16 bytes, so this is safe.
|
||||
const u32 pitchBy128 = pitch >> 4;
|
||||
for (int by = 0; by < byc; by++) {
|
||||
const __m128i *xsrc = (const __m128i *)ysrcp;
|
||||
for (int bx = 0; bx < bxc; bx++) {
|
||||
const __m128i *src = xsrc;
|
||||
for (int n = 0; n < 2; n++) {
|
||||
// Textures are always 16-byte aligned so this is fine.
|
||||
__m128i temp1 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
__m128i temp2 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
__m128i temp3 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
__m128i temp4 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
if (((uintptr_t)ysrcp & 0xF) == 0 && (pitch & 0xF) == 0) {
|
||||
__m128i *dest = (__m128i *)texptr;
|
||||
// The pitch parameter is in bytes, so shift down for 128-bit.
|
||||
// Note: it's always aligned to 16 bytes, so this is safe.
|
||||
const u32 pitchBy128 = pitch >> 4;
|
||||
for (int by = 0; by < byc; by++) {
|
||||
const __m128i *xsrc = (const __m128i *)ysrcp;
|
||||
for (int bx = 0; bx < bxc; bx++) {
|
||||
const __m128i *src = xsrc;
|
||||
for (int n = 0; n < 2; n++) {
|
||||
// Textures are always 16-byte aligned so this is fine.
|
||||
__m128i temp1 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
__m128i temp2 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
__m128i temp3 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
__m128i temp4 = _mm_load_si128(src);
|
||||
src += pitchBy128;
|
||||
|
||||
_mm_store_si128(dest, temp1);
|
||||
_mm_store_si128(dest + 1, temp2);
|
||||
_mm_store_si128(dest + 2, temp3);
|
||||
_mm_store_si128(dest + 3, temp4);
|
||||
dest += 4;
|
||||
_mm_store_si128(dest, temp1);
|
||||
_mm_store_si128(dest + 1, temp2);
|
||||
_mm_store_si128(dest + 2, temp3);
|
||||
_mm_store_si128(dest + 3, temp4);
|
||||
dest += 4;
|
||||
}
|
||||
xsrc++;
|
||||
}
|
||||
xsrc++;
|
||||
ysrcp += pitchBy32 * 8;
|
||||
}
|
||||
ysrcp += pitchBy32 * 8;
|
||||
}
|
||||
#else
|
||||
u32 *dest = (u32 *)texptr;
|
||||
for (int by = 0; by < byc; by++) {
|
||||
const u32 *xsrc = ysrcp;
|
||||
for (int bx = 0; bx < bxc; bx++) {
|
||||
const u32 *src = xsrc;
|
||||
for (int n = 0; n < 8; n++) {
|
||||
memcpy(dest, src, 16);
|
||||
src += pitchBy32;
|
||||
dest += 4;
|
||||
}
|
||||
xsrc += 4;
|
||||
}
|
||||
ysrcp += pitchBy32 * 8;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
u32 *dest = (u32 *)texptr;
|
||||
for (int by = 0; by < byc; by++) {
|
||||
const u32 *xsrc = ysrcp;
|
||||
for (int bx = 0; bx < bxc; bx++) {
|
||||
const u32 *src = xsrc;
|
||||
for (int n = 0; n < 8; n++) {
|
||||
memcpy(dest, src, 16);
|
||||
src += pitchBy32;
|
||||
dest += 4;
|
||||
}
|
||||
xsrc += 4;
|
||||
}
|
||||
ysrcp += pitchBy32 * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 pitch) {
|
||||
@@ -255,7 +258,7 @@ void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32
|
||||
const u32 pitchBy32 = pitch >> 2;
|
||||
|
||||
#ifdef _M_SSE
|
||||
if (((uintptr_t)ydestp & 0xF) == 0) {
|
||||
if (((uintptr_t)ydestp & 0xF) == 0 && (pitch & 0xF) == 0) {
|
||||
const __m128i *src = (const __m128i *)texptr;
|
||||
// The pitch parameter is in bytes, so shift down for 128-bit.
|
||||
// Note: it's always aligned to 16 bytes, so this is safe.
|
||||
@@ -327,16 +330,33 @@ void SetupTextureDecoder() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// S3TC / DXT Decoder
|
||||
class DXTDecoder {
|
||||
public:
|
||||
inline void DecodeColors(const DXT1Block *src, bool ignore1bitAlpha);
|
||||
inline void DecodeAlphaDXT5(const DXT5Block *src);
|
||||
inline void WriteColorsDXT1(u32 *dst, const DXT1Block *src, int pitch, int height);
|
||||
inline void WriteColorsDXT3(u32 *dst, const DXT3Block *src, int pitch, int height);
|
||||
inline void WriteColorsDXT5(u32 *dst, const DXT5Block *src, int pitch, int height);
|
||||
|
||||
protected:
|
||||
u32 colors_[4];
|
||||
u8 alpha_[8];
|
||||
};
|
||||
|
||||
static inline u32 makecol(int r, int g, int b, int a) {
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
static inline int mul_3_8(int c) {
|
||||
// This is 3/8 * c = 4/8 * c - 1/8 * c.
|
||||
return (c >> 1) - (c >> 3);
|
||||
}
|
||||
|
||||
// This could probably be done faster by decoding two or four blocks at a time with SSE/NEON.
|
||||
void DecodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch, int height, bool ignore1bitAlpha) {
|
||||
// S3TC Decoder
|
||||
// Needs more speed and debugging.
|
||||
u16 c1 = (src->color1);
|
||||
u16 c2 = (src->color2);
|
||||
void DXTDecoder::DecodeColors(const DXT1Block *src, bool ignore1bitAlpha) {
|
||||
u16 c1 = src->color1;
|
||||
u16 c2 = src->color2;
|
||||
int red1 = Convert5To8(c1 & 0x1F);
|
||||
int red2 = Convert5To8(c2 & 0x1F);
|
||||
int green1 = Convert6To8((c1 >> 5) & 0x3F);
|
||||
@@ -344,89 +364,121 @@ void DecodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch, int height, bool
|
||||
int blue1 = Convert5To8((c1 >> 11) & 0x1F);
|
||||
int blue2 = Convert5To8((c2 >> 11) & 0x1F);
|
||||
|
||||
u32 colors[4];
|
||||
colors[0] = makecol(red1, green1, blue1, 255);
|
||||
colors[1] = makecol(red2, green2, blue2, 255);
|
||||
// Keep alpha zero for non-DXT1 to skip masking the colors.
|
||||
int alpha = ignore1bitAlpha ? 0 : 255;
|
||||
|
||||
colors_[0] = makecol(red1, green1, blue1, alpha);
|
||||
colors_[1] = makecol(red2, green2, blue2, alpha);
|
||||
if (c1 > c2 || ignore1bitAlpha) {
|
||||
int blue3 = ((blue2 - blue1) >> 1) - ((blue2 - blue1) >> 3);
|
||||
int green3 = ((green2 - green1) >> 1) - ((green2 - green1) >> 3);
|
||||
int red3 = ((red2 - red1) >> 1) - ((red2 - red1) >> 3);
|
||||
colors[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, 255);
|
||||
colors[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, 255);
|
||||
int red3 = mul_3_8(red2 - red1);
|
||||
int green3 = mul_3_8(green2 - green1);
|
||||
int blue3 = mul_3_8(blue2 - blue1);
|
||||
colors_[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, alpha);
|
||||
colors_[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, alpha);
|
||||
} else {
|
||||
colors[2] = makecol((red1 + red2 + 1) / 2, // Average
|
||||
(green1 + green2 + 1) / 2,
|
||||
(blue1 + blue2 + 1) / 2, 255);
|
||||
colors[3] = makecol(red2, green2, blue2, 0); // Color2 but transparent
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
int val = src->lines[y];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
dst[x] = colors[val & 3];
|
||||
val >>= 2;
|
||||
}
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void DecodeDXT3Block(u32 *dst, const DXT3Block *src, int pitch, int height)
|
||||
{
|
||||
DecodeDXT1Block(dst, &src->color, pitch, height, true);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
u32 line = src->alphaLines[y];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
const u8 a4 = line & 0xF;
|
||||
dst[x] = (dst[x] & 0xFFFFFF) | (a4 << 24) | (a4 << 28);
|
||||
line >>= 4;
|
||||
}
|
||||
dst += pitch;
|
||||
// Average
|
||||
int red3 = (red1 + red2 + 1) / 2;
|
||||
int green3 = (green1 + green2 + 1) / 2;
|
||||
int blue3 = (blue1 + blue2 + 1) / 2;
|
||||
colors_[2] = makecol(red3, green3, blue3, 255);
|
||||
// Color2 but transparent
|
||||
colors_[3] = makecol(red2, green2, blue2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u8 lerp8(const DXT5Block *src, int n) {
|
||||
float d = n / 7.0f;
|
||||
return (u8)(src->alpha1 + (src->alpha2 - src->alpha1) * d);
|
||||
// These weights translate alpha1/alpha2 to fixed 8.8 point, pre-divided by 7.
|
||||
int weight1 = ((7 - n) << 8) / 7;
|
||||
int weight2 = (n << 8) / 7;
|
||||
return (u8)((src->alpha1 * weight1 + src->alpha2 * weight2) >> 8);
|
||||
}
|
||||
|
||||
static inline u8 lerp6(const DXT5Block *src, int n) {
|
||||
float d = n / 5.0f;
|
||||
return (u8)(src->alpha1 + (src->alpha2 - src->alpha1) * d);
|
||||
int weight1 = ((5 - n) << 8) / 5;
|
||||
int weight2 = (n << 8) / 5;
|
||||
return (u8)((src->alpha1 * weight1 + src->alpha2 * weight2) >> 8);
|
||||
}
|
||||
|
||||
void DXTDecoder::DecodeAlphaDXT5(const DXT5Block *src) {
|
||||
// TODO: Check if alpha is still not 100% correct.
|
||||
alpha_[0] = src->alpha1;
|
||||
alpha_[1] = src->alpha2;
|
||||
if (alpha_[0] > alpha_[1]) {
|
||||
alpha_[2] = lerp8(src, 1);
|
||||
alpha_[3] = lerp8(src, 2);
|
||||
alpha_[4] = lerp8(src, 3);
|
||||
alpha_[5] = lerp8(src, 4);
|
||||
alpha_[6] = lerp8(src, 5);
|
||||
alpha_[7] = lerp8(src, 6);
|
||||
} else {
|
||||
alpha_[2] = lerp6(src, 1);
|
||||
alpha_[3] = lerp6(src, 2);
|
||||
alpha_[4] = lerp6(src, 3);
|
||||
alpha_[5] = lerp6(src, 4);
|
||||
alpha_[6] = 0;
|
||||
alpha_[7] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
void DXTDecoder::WriteColorsDXT1(u32 *dst, const DXT1Block *src, int pitch, int height) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
int colordata = src->lines[y];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
dst[x] = colors_[colordata & 3];
|
||||
colordata >>= 2;
|
||||
}
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void DXTDecoder::WriteColorsDXT3(u32 *dst, const DXT3Block *src, int pitch, int height) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
int colordata = src->color.lines[y];
|
||||
u32 alphadata = src->alphaLines[y];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
const u8 a4 = alphadata & 0xF;
|
||||
dst[x] = colors_[colordata & 3] | (a4 << 24) | (a4 << 28);
|
||||
colordata >>= 2;
|
||||
alphadata >>= 4;
|
||||
}
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void DXTDecoder::WriteColorsDXT5(u32 *dst, const DXT5Block *src, int pitch, int height) {
|
||||
// 48 bits, 3 bit index per pixel, 12 bits per line.
|
||||
u64 alphadata = ((u64)(u16)src->alphadata1 << 32) | (u32)src->alphadata2;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
int colordata = src->color.lines[y];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
dst[x] = colors_[colordata & 3] | (alpha_[alphadata & 7] << 24);
|
||||
colordata >>= 2;
|
||||
alphadata >>= 3;
|
||||
}
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
// This could probably be done faster by decoding two or four blocks at a time with SSE/NEON.
|
||||
void DecodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch, int height, bool ignore1bitAlpha) {
|
||||
DXTDecoder dxt;
|
||||
dxt.DecodeColors(src, ignore1bitAlpha);
|
||||
dxt.WriteColorsDXT1(dst, src, pitch, height);
|
||||
}
|
||||
|
||||
void DecodeDXT3Block(u32 *dst, const DXT3Block *src, int pitch, int height) {
|
||||
DXTDecoder dxt;
|
||||
dxt.DecodeColors(&src->color, true);
|
||||
dxt.WriteColorsDXT3(dst, src, pitch, height);
|
||||
}
|
||||
|
||||
// The alpha channel is not 100% correct
|
||||
void DecodeDXT5Block(u32 *dst, const DXT5Block *src, int pitch, int height) {
|
||||
DecodeDXT1Block(dst, &src->color, pitch, height, true);
|
||||
u8 alpha[8];
|
||||
|
||||
alpha[0] = src->alpha1;
|
||||
alpha[1] = src->alpha2;
|
||||
if (alpha[0] > alpha[1]) {
|
||||
alpha[2] = lerp8(src, 1);
|
||||
alpha[3] = lerp8(src, 2);
|
||||
alpha[4] = lerp8(src, 3);
|
||||
alpha[5] = lerp8(src, 4);
|
||||
alpha[6] = lerp8(src, 5);
|
||||
alpha[7] = lerp8(src, 6);
|
||||
} else {
|
||||
alpha[2] = lerp6(src, 1);
|
||||
alpha[3] = lerp6(src, 2);
|
||||
alpha[4] = lerp6(src, 3);
|
||||
alpha[5] = lerp6(src, 4);
|
||||
alpha[6] = 0;
|
||||
alpha[7] = 255;
|
||||
}
|
||||
|
||||
u64 data = ((u64)(u16)src->alphadata1 << 32) | (u32)src->alphadata2;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
dst[x] = (dst[x] & 0xFFFFFF) | (alpha[data & 7] << 24);
|
||||
data >>= 3;
|
||||
}
|
||||
dst += pitch;
|
||||
}
|
||||
DXTDecoder dxt;
|
||||
dxt.DecodeColors(&src->color, true);
|
||||
dxt.DecodeAlphaDXT5(src);
|
||||
dxt.WriteColorsDXT5(dst, src, pitch, height);
|
||||
}
|
||||
|
||||
#ifdef _M_SSE
|
||||
|
||||
@@ -41,7 +41,7 @@ u32 QuickTexHashSSE2(const void *checkp, u32 size);
|
||||
#define DoQuickTexHash QuickTexHashSSE2
|
||||
#define StableQuickTexHash QuickTexHashSSE2
|
||||
|
||||
// Pitch must be aligned to 16 bits (as is the case on a PSP)
|
||||
// Pitch must be aligned to 16 bytes (as is the case on a PSP)
|
||||
void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 pitch);
|
||||
#define DoUnswizzleTex16 DoUnswizzleTex16Basic
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
@@ -34,10 +33,10 @@
|
||||
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
|
||||
#include "GPU/Common/TransformCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SoftwareTransformCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/D3D11/FramebufferManagerD3D11.h"
|
||||
#include "GPU/D3D11/TextureCacheD3D11.h"
|
||||
#include "GPU/D3D11/DrawEngineD3D11.h"
|
||||
@@ -690,10 +689,7 @@ rotateVBO:
|
||||
gstate_c.vertBounds.maxU = 0;
|
||||
gstate_c.vertBounds.maxV = 0;
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
|
||||
// We only support GPU debugging on Windows, and that's the only use case for this.
|
||||
host->GPUNotifyDraw();
|
||||
#endif
|
||||
GPUDebug::NotifyDraw();
|
||||
}
|
||||
|
||||
void DrawEngineD3D11::TessellationDataTransferD3D11::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) {
|
||||
|
||||
@@ -129,6 +129,21 @@ FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw)
|
||||
vb.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
ASSERT_SUCCESS(device_->CreateBuffer(&vb, nullptr, &postConstants_));
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
desc.CPUAccessFlags = 0;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.ArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Width = 1;
|
||||
desc.Height = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.MipLevels = 1;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &nullTexture_));
|
||||
ASSERT_SUCCESS(device_->CreateShaderResourceView(nullTexture_, nullptr, &nullTextureView_));
|
||||
uint32_t nullData[1]{};
|
||||
context_->UpdateSubresource(nullTexture_, 0, nullptr, nullData, 1, 0);
|
||||
|
||||
ShaderTranslationInit();
|
||||
|
||||
CompilePostShader();
|
||||
@@ -178,6 +193,11 @@ FramebufferManagerD3D11::~FramebufferManagerD3D11() {
|
||||
stencilUploadInputLayout_->Release();
|
||||
if (stencilValueBuffer_)
|
||||
stencilValueBuffer_->Release();
|
||||
|
||||
if (nullTextureView_)
|
||||
nullTextureView_->Release();
|
||||
if (nullTexture_)
|
||||
nullTexture_->Release();
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::SetTextureCache(TextureCacheD3D11 *tc) {
|
||||
@@ -437,31 +457,33 @@ void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, G
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
// (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.)
|
||||
// (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.)
|
||||
//
|
||||
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
|
||||
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
if (old == GE_FORMAT_565) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
|
||||
// TODO: There's no way this does anything useful :(
|
||||
context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0xFF);
|
||||
context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0], nullptr, 0xFFFFFFFF);
|
||||
context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0xFF);
|
||||
context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[D3D11_COLOR_WRITE_ENABLE_ALPHA], nullptr, 0xFFFFFFFF);
|
||||
context_->RSSetState(stockD3D11.rasterStateNoCull);
|
||||
context_->IASetInputLayout(quadInputLayout_);
|
||||
context_->PSSetShader(quadPixelShader_, nullptr, 0);
|
||||
context_->VSSetShader(quadVertexShader_, nullptr, 0);
|
||||
context_->IASetVertexBuffers(0, 1, &fsQuadBuffer_, &quadStride_, &quadOffset_);
|
||||
context_->PSSetSamplers(0, 1, &stockD3D11.samplerPoint2DClamp);
|
||||
context_->PSSetShaderResources(0, 1, &nullTextureView_);
|
||||
shaderManagerD3D11_->DirtyLastShader();
|
||||
D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)vfb->renderWidth, (float)vfb->renderHeight, 0.0f, 1.0f };
|
||||
context_->RSSetViewports(1, &vp);
|
||||
context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||
context_->Draw(4, 0);
|
||||
}
|
||||
|
||||
RebindFramebuffer();
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE);
|
||||
textureCache_->ForgetLastTexture();
|
||||
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) {
|
||||
|
||||
@@ -107,6 +107,9 @@ private:
|
||||
ID3D11Buffer *stencilValueBuffer_ = nullptr;
|
||||
ID3D11DepthStencilState *stencilMaskStates_[256]{};
|
||||
|
||||
ID3D11Texture2D *nullTexture_ = nullptr;
|
||||
ID3D11ShaderResourceView *nullTextureView_ = nullptr;
|
||||
|
||||
TextureCacheD3D11 *textureCacheD3D11_;
|
||||
ShaderManagerD3D11 *shaderManagerD3D11_;
|
||||
DrawEngineD3D11 *drawEngineD3D11_;
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
@@ -55,6 +54,7 @@
|
||||
#include "GPU/GeDisasm.h"
|
||||
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/D3D11/ShaderManagerD3D11.h"
|
||||
#include "GPU/D3D11/GPU_D3D11.h"
|
||||
#include "GPU/D3D11/FramebufferManagerD3D11.h"
|
||||
@@ -254,7 +254,7 @@ void GPU_D3D11::BeginFrame() {
|
||||
void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
||||
// TODO: Some games like Spongebob - Yellow Avenger, never change framebuffer, they blit to it.
|
||||
// So breaking on frames doesn't work. Might want to move this to sceDisplay vsync.
|
||||
host->GPUNotifyDisplay(framebuf, stride, format);
|
||||
GPUDebug::NotifyDisplay(framebuf, stride, format);
|
||||
framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
|
||||
@@ -705,7 +705,7 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &
|
||||
mapData = (u32 *)AllocateAlignedMemory(sizeof(u32) * (w * scaleFactor) * (h * scaleFactor), 16);
|
||||
mapRowPitch = w * scaleFactor * 4;
|
||||
} else {
|
||||
mapRowPitch = std::max(bufw, w) * bpp;
|
||||
mapRowPitch = std::max(w * bpp, 16);
|
||||
size_t bufSize = sizeof(u32) * (mapRowPitch / bpp) * h;
|
||||
mapData = (u32 *)AllocateAlignedMemory(bufSize, 16);
|
||||
if (!mapData) {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2018- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "GPU/GPU.h"
|
||||
#include "GPU/Debugger/Breakpoints.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Debugger/Stepping.h"
|
||||
|
||||
namespace GPUDebug {
|
||||
|
||||
static bool active = false;
|
||||
static bool inited = false;
|
||||
static BreakNext breakNext = BreakNext::NONE;
|
||||
|
||||
static void Init() {
|
||||
if (!inited) {
|
||||
GPUBreakpoints::Init();
|
||||
Core_ListenStopRequest(&GPUStepping::ForceUnpause);
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetActive(bool flag) {
|
||||
Init();
|
||||
|
||||
active = flag;
|
||||
if (!active) {
|
||||
breakNext = BreakNext::NONE;
|
||||
GPUStepping::ResumeFromStepping();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
void SetBreakNext(BreakNext next) {
|
||||
SetActive(true);
|
||||
breakNext = next;
|
||||
if (next == BreakNext::TEX) {
|
||||
GPUBreakpoints::AddTextureChangeTempBreakpoint();
|
||||
} else if (next == BreakNext::PRIM) {
|
||||
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_PRIM, true);
|
||||
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true);
|
||||
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true);
|
||||
}
|
||||
GPUStepping::ResumeFromStepping();
|
||||
}
|
||||
|
||||
void NotifyCommand(u32 pc) {
|
||||
if (!active)
|
||||
return;
|
||||
u32 op = Memory::ReadUnchecked_U32(pc);
|
||||
if (breakNext == BreakNext::OP || GPUBreakpoints::IsBreakpoint(pc, op)) {
|
||||
GPUBreakpoints::ClearTempBreakpoints();
|
||||
|
||||
auto info = gpuDebug->DissassembleOp(pc);
|
||||
NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str());
|
||||
GPUStepping::EnterStepping();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyDraw() {
|
||||
if (!active)
|
||||
return;
|
||||
if (breakNext == BreakNext::DRAW) {
|
||||
NOTICE_LOG(G3D, "Waiting at a draw");
|
||||
GPUStepping::EnterStepping();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyDisplay(u32 framebuf, u32 stride, int format) {
|
||||
if (!active)
|
||||
return;
|
||||
if (breakNext == BreakNext::FRAME) {
|
||||
// This should work fine, start stepping at the first op of the new frame.
|
||||
breakNext = BreakNext::OP;
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyTextureAttachment(u32 texaddr) {
|
||||
if (!active)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2018- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace GPUDebug {
|
||||
|
||||
enum class BreakNext {
|
||||
NONE,
|
||||
OP,
|
||||
DRAW,
|
||||
TEX,
|
||||
NONTEX,
|
||||
FRAME,
|
||||
PRIM,
|
||||
};
|
||||
|
||||
void SetActive(bool flag);
|
||||
bool IsActive();
|
||||
|
||||
void SetBreakNext(BreakNext next);
|
||||
|
||||
// While debugging is active, these may block.
|
||||
void NotifyCommand(u32 pc);
|
||||
void NotifyDraw();
|
||||
void NotifyDisplay(u32 framebuf, u32 stride, int format);
|
||||
void NotifyTextureAttachment(u32 texaddr);
|
||||
|
||||
}
|
||||
+36
-13
@@ -48,6 +48,7 @@ static const int VERSION = 2;
|
||||
static bool active = false;
|
||||
static bool nextFrame = false;
|
||||
static bool writePending = false;
|
||||
static std::function<void(const std::string &)> writeCallback;
|
||||
|
||||
enum class CommandType : u8 {
|
||||
INIT = 0,
|
||||
@@ -409,7 +410,7 @@ static void WriteCompressed(FILE *fp, const void *p, size_t sz) {
|
||||
delete [] compressed;
|
||||
}
|
||||
|
||||
static void WriteRecording() {
|
||||
static std::string WriteRecording() {
|
||||
FlushRegisters();
|
||||
EmitDisplayBuf();
|
||||
|
||||
@@ -430,6 +431,8 @@ static void WriteRecording() {
|
||||
WriteCompressed(fp, pushbuf.data(), bufsz);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
static void GetVertDataSizes(int vcount, const void *indices, u32 &vbytes, u32 &ibytes) {
|
||||
@@ -637,8 +640,35 @@ bool IsActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
void Activate() {
|
||||
nextFrame = true;
|
||||
bool IsActivePending() {
|
||||
return nextFrame || active;
|
||||
}
|
||||
|
||||
bool Activate() {
|
||||
if (!nextFrame) {
|
||||
nextFrame = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetCallback(const std::function<void(const std::string &)> callback) {
|
||||
writeCallback = callback;
|
||||
}
|
||||
|
||||
static void FinishRecording() {
|
||||
// We're done - this was just to write the result out.
|
||||
std::string filename = WriteRecording();
|
||||
commands.clear();
|
||||
pushbuf.clear();
|
||||
|
||||
NOTICE_LOG(SYSTEM, "Recording finished");
|
||||
writePending = false;
|
||||
active = false;
|
||||
|
||||
if (writeCallback)
|
||||
writeCallback(filename);
|
||||
writeCallback = nullptr;
|
||||
}
|
||||
|
||||
void NotifyCommand(u32 pc) {
|
||||
@@ -646,14 +676,7 @@ void NotifyCommand(u32 pc) {
|
||||
return;
|
||||
}
|
||||
if (writePending) {
|
||||
WriteRecording();
|
||||
commands.clear();
|
||||
pushbuf.clear();
|
||||
|
||||
writePending = false;
|
||||
// We're done - this was just to write the result out.
|
||||
NOTICE_LOG(SYSTEM, "Recording finished");
|
||||
active = false;
|
||||
FinishRecording();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -749,12 +772,12 @@ void NotifyUpload(u32 dest, u32 sz) {
|
||||
}
|
||||
|
||||
void NotifyFrame() {
|
||||
if (active && !writePending) {
|
||||
if (active && !writePending && !commands.empty()) {
|
||||
// Delay write until the first command of the next frame, so we get the right display buf.
|
||||
NOTICE_LOG(SYSTEM, "Recording complete - waiting to get display buffer");
|
||||
writePending = true;
|
||||
}
|
||||
if (nextFrame) {
|
||||
if (nextFrame && (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) {
|
||||
NOTICE_LOG(SYSTEM, "Recording starting...");
|
||||
active = true;
|
||||
nextFrame = false;
|
||||
|
||||
@@ -17,13 +17,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace GPURecord {
|
||||
|
||||
bool IsActive();
|
||||
void Activate();
|
||||
bool IsActivePending();
|
||||
bool Activate();
|
||||
// Call only if Activate() returns true.
|
||||
void SetCallback(const std::function<void(const std::string &)> callback);
|
||||
|
||||
void NotifyCommand(u32 pc);
|
||||
void NotifyMemcpy(u32 dest, u32 src, u32 sz);
|
||||
|
||||
@@ -38,6 +38,7 @@ enum PauseAction {
|
||||
};
|
||||
|
||||
static bool isStepping;
|
||||
static int stepCounter = 0;
|
||||
|
||||
static std::mutex pauseLock;
|
||||
static std::condition_variable pauseWait;
|
||||
@@ -149,7 +150,7 @@ bool SingleStep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EnterStepping(std::function<void()> callback) {
|
||||
bool EnterStepping() {
|
||||
std::unique_lock<std::mutex> guard(pauseLock);
|
||||
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) {
|
||||
// Shutting down, don't try to step.
|
||||
@@ -170,8 +171,7 @@ bool EnterStepping(std::function<void()> callback) {
|
||||
pauseAction = PAUSE_BREAK;
|
||||
}
|
||||
isStepping = true;
|
||||
|
||||
callback();
|
||||
stepCounter++;
|
||||
|
||||
do {
|
||||
RunPauseAction();
|
||||
@@ -187,6 +187,10 @@ bool IsStepping() {
|
||||
return isStepping;
|
||||
}
|
||||
|
||||
int GetSteppingCounter() {
|
||||
return stepCounter;
|
||||
}
|
||||
|
||||
static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) {
|
||||
if (!isStepping && coreState != CORE_STEPPING) {
|
||||
return false;
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
|
||||
namespace GPUStepping {
|
||||
// Should be called from the GPU thread.
|
||||
// Begins stepping and calls callback while inside a lock preparing stepping.
|
||||
// This would be a good place to deliver a message to code that stepping is ready.
|
||||
bool EnterStepping(std::function<void()> callback);
|
||||
// Should be called from the emu thread.
|
||||
// Begins stepping and increments the stepping counter while inside a lock.
|
||||
bool EnterStepping();
|
||||
bool SingleStep();
|
||||
bool IsStepping();
|
||||
int GetSteppingCounter();
|
||||
|
||||
bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer);
|
||||
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
@@ -36,6 +35,7 @@
|
||||
#include "GPU/Common/TransformCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SoftwareTransformCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Directx9/TextureCacheDX9.h"
|
||||
#include "GPU/Directx9/DrawEngineDX9.h"
|
||||
#include "GPU/Directx9/ShaderManagerDX9.h"
|
||||
@@ -621,7 +621,7 @@ rotateVBO:
|
||||
gstate_c.vertBounds.maxU = 0;
|
||||
gstate_c.vertBounds.maxV = 0;
|
||||
|
||||
host->GPUNotifyDraw();
|
||||
GPUDebug::NotifyDraw();
|
||||
}
|
||||
|
||||
void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords)
|
||||
|
||||
@@ -100,6 +100,19 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
|
||||
device_->CreateVertexDeclaration(g_FramebufferVertexElements, &pFramebufferVertexDecl);
|
||||
|
||||
|
||||
int usage = 0;
|
||||
D3DPOOL pool = D3DPOOL_MANAGED;
|
||||
if (deviceEx_) {
|
||||
pool = D3DPOOL_DEFAULT;
|
||||
usage = D3DUSAGE_DYNAMIC;
|
||||
}
|
||||
HRESULT hr = device_->CreateTexture(1, 1, 1, usage, D3DFMT_A8R8G8B8, pool, &nullTex_, nullptr);
|
||||
D3DLOCKED_RECT rect;
|
||||
nullTex_->LockRect(0, &rect, nullptr, D3DLOCK_DISCARD);
|
||||
memset(rect.pBits, 0, 4);
|
||||
nullTex_->UnlockRect(0);
|
||||
}
|
||||
|
||||
FramebufferManagerDX9::~FramebufferManagerDX9() {
|
||||
@@ -125,6 +138,8 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
if (stencilUploadVS_) {
|
||||
stencilUploadVS_->Release();
|
||||
}
|
||||
if (nullTex_)
|
||||
nullTex_->Release();
|
||||
}
|
||||
|
||||
void FramebufferManagerDX9::SetTextureCache(TextureCacheDX9 *tc) {
|
||||
@@ -307,24 +322,24 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
return;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
// (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.)
|
||||
// (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.)
|
||||
//
|
||||
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
|
||||
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
|
||||
dxstate.scissorTest.disable();
|
||||
dxstate.depthWrite.set(FALSE);
|
||||
dxstate.colorMask.set(false, false, false, true);
|
||||
dxstate.stencilFunc.set(D3DCMP_ALWAYS, 0, 0);
|
||||
dxstate.stencilMask.set(0xFF);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
|
||||
float coord[20] = {
|
||||
-1.0f,-1.0f,0, 0,0,
|
||||
@@ -338,7 +353,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
device_->SetPixelShader(pFramebufferPixelShader);
|
||||
device_->SetVertexShader(pFramebufferVertexShader);
|
||||
shaderManagerDX9_->DirtyLastShader();
|
||||
device_->SetTexture(0, nullptr);
|
||||
device_->SetTexture(0, nullTex_);
|
||||
|
||||
D3DVIEWPORT9 vp{ 0, 0, (DWORD)vfb->renderWidth, (DWORD)vfb->renderHeight, 0.0f, 1.0f };
|
||||
device_->SetViewport(&vp);
|
||||
@@ -349,9 +364,9 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
ERROR_LOG_REPORT(G3D, "ReformatFramebufferFrom() failed: %08x", hr);
|
||||
}
|
||||
dxstate.viewport.restore();
|
||||
}
|
||||
|
||||
RebindFramebuffer();
|
||||
textureCache_->ForgetLastTexture();
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) {
|
||||
|
||||
@@ -104,6 +104,8 @@ private:
|
||||
LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr;
|
||||
bool stencilUploadFailed_ = false;
|
||||
|
||||
LPDIRECT3DTEXTURE9 nullTex_ = nullptr;
|
||||
|
||||
TextureCacheDX9 *textureCacheDX9_;
|
||||
ShaderManagerDX9 *shaderManagerDX9_;
|
||||
DrawEngineDX9 *drawEngineD3D9_;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "GPU/GeDisasm.h"
|
||||
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Directx9/ShaderManagerDX9.h"
|
||||
#include "GPU/Directx9/GPU_DX9.h"
|
||||
#include "GPU/Directx9/FramebufferDX9.h"
|
||||
@@ -230,7 +231,7 @@ void GPU_DX9::BeginFrame() {
|
||||
}
|
||||
|
||||
void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
||||
host->GPUNotifyDisplay(framebuf, stride, format);
|
||||
GPUDebug::NotifyDisplay(framebuf, stride, format);
|
||||
framebufferManagerDX9_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
|
||||
@@ -307,6 +307,7 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, char* scalerC
|
||||
if (lang == HLSL_D3D11) {
|
||||
WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]);
|
||||
} else {
|
||||
// TODO: Use a texture to lookup bitwise ops?
|
||||
WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) clip(-1);\n", alphaTestFuncs[alphaTestFunc]);
|
||||
}
|
||||
} else {
|
||||
@@ -332,13 +333,17 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, char* scalerC
|
||||
} else {
|
||||
const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; // never/always don't make sense
|
||||
if (colorTestFuncs[colorTestFunc][0] != '#') {
|
||||
const char * test = colorTestFuncs[colorTestFunc];
|
||||
const char *test = colorTestFuncs[colorTestFunc];
|
||||
if (lang == HLSL_D3D11) {
|
||||
WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(v.rgb);\n");
|
||||
WRITE(p, " if ((v_scaled & u_alphacolormask.rgb) %s (u_alphacolorref.rgb & u_alphacolormask.rgb)) discard;\n", colorTestFuncs[colorTestFunc]);
|
||||
WRITE(p, " uint3 v_masked = v_scaled & u_alphacolormask.rgb;\n");
|
||||
WRITE(p, " uint3 colorTestRef = u_alphacolorref.rgb & u_alphacolormask.rgb;\n");
|
||||
// We have to test the components separately, or we get incorrect results. See #10629.
|
||||
WRITE(p, " if (v_masked.r %s colorTestRef.r && v_masked.g %s colorTestRef.g && v_masked.b %s colorTestRef.b) discard;\n", test, test, test);
|
||||
} else {
|
||||
// TODO: Use a texture to lookup bitwise ops instead?
|
||||
WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n");
|
||||
WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b )) clip(-1);\n", test, test, test);
|
||||
WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b)) clip(-1);\n", test, test, test);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -59,7 +59,7 @@ PSShader::PSShader(LPDIRECT3DDEVICE9 device, FShaderID id, const char *code) : i
|
||||
ERROR_LOG(G3D, "Error in shader compilation!");
|
||||
}
|
||||
ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str());
|
||||
ERROR_LOG(G3D, "Shader source:\n%s", code);
|
||||
ERROR_LOG(G3D, "Shader source:\n%s", LineNumberString(code).c_str());
|
||||
OutputDebugStringUTF8("Messages:\n");
|
||||
OutputDebugStringUTF8(errorMessage.c_str());
|
||||
Reporting::ReportMessage("D3D error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code);
|
||||
@@ -266,10 +266,10 @@ void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) {
|
||||
PSSetColorUniform3(CONST_PS_TEXENV, gstate.texenvcolor);
|
||||
}
|
||||
if (dirtyUniforms & DIRTY_ALPHACOLORREF) {
|
||||
PSSetColorUniform3Alpha255(CONST_PS_ALPHACOLORREF, gstate.getColorTestRef(), gstate.getAlphaTestRef());
|
||||
PSSetColorUniform3Alpha255(CONST_PS_ALPHACOLORREF, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask());
|
||||
}
|
||||
if (dirtyUniforms & DIRTY_ALPHACOLORMASK) {
|
||||
PSSetColorUniform3(CONST_PS_ALPHACOLORMASK, gstate.colortestmask);
|
||||
PSSetColorUniform3Alpha255(CONST_PS_ALPHACOLORMASK, gstate.colortestmask, gstate.getAlphaTestMask());
|
||||
}
|
||||
if (dirtyUniforms & DIRTY_FOGCOLOR) {
|
||||
PSSetColorUniform3(CONST_PS_FOGCOLOR, gstate.fogcolor);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
@@ -37,6 +36,7 @@
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SoftwareTransformCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/GLES/FragmentTestCacheGLES.h"
|
||||
#include "GPU/GLES/StateMappingGLES.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
@@ -648,9 +648,7 @@ rotateVBO:
|
||||
gstate_c.vertBounds.maxU = 0;
|
||||
gstate_c.vertBounds.maxV = 0;
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
host->GPUNotifyDraw();
|
||||
#endif
|
||||
GPUDebug::NotifyDraw();
|
||||
}
|
||||
|
||||
bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {
|
||||
|
||||
@@ -466,20 +466,17 @@ void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GE
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
// (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.)
|
||||
// (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.)
|
||||
//
|
||||
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
|
||||
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
// Clear alpha and stencil.
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
render_->Clear(0, 0.0f, 0, GL_COLOR_BUFFER_BIT, 0x8, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
RebindFramebuffer();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GeDisasm.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
#include "GPU/GLES/GPU_GLES.h"
|
||||
#include "GPU/GLES/FramebufferManagerGLES.h"
|
||||
@@ -443,7 +443,7 @@ void GPU_GLES::BeginFrame() {
|
||||
}
|
||||
|
||||
void GPU_GLES::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
||||
host->GPUNotifyDisplay(framebuf, stride, format);
|
||||
GPUDebug::NotifyDisplay(framebuf, stride, format);
|
||||
framebufferManagerGL_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
|
||||
@@ -753,11 +753,8 @@ TexCacheEntry::TexStatus TextureCacheGLES::CheckAlpha(const uint8_t *pixelData,
|
||||
void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int scaleFactor, GLenum dstFmt) {
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
bool useUnpack = false;
|
||||
uint8_t *pixelData;
|
||||
|
||||
// TODO: only do this once
|
||||
u32 texByteAlign = 1;
|
||||
int decPitch = 0;
|
||||
|
||||
gpuStats.numTexturesDecoded++;
|
||||
|
||||
@@ -765,13 +762,12 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
PROFILE_THIS_SCOPE("replacetex");
|
||||
|
||||
int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2;
|
||||
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * h * bpp, 16);
|
||||
replaced.Load(level, rearrange, bpp * w);
|
||||
decPitch = w * bpp;
|
||||
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(decPitch * h, 16);
|
||||
replaced.Load(level, rearrange, decPitch);
|
||||
pixelData = rearrange;
|
||||
|
||||
dstFmt = ToGLESFormat(replaced.Format(level));
|
||||
|
||||
texByteAlign = bpp;
|
||||
} else {
|
||||
PROFILE_THIS_SCOPE("decodetex");
|
||||
|
||||
@@ -780,14 +776,15 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
int bufw = GetTextureBufw(level, texaddr, GETextureFormat(entry.format));
|
||||
|
||||
int pixelSize = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
int decPitch = w * pixelSize;
|
||||
// We leave GL_UNPACK_ALIGNMENT at 4, so this must be at least 4.
|
||||
decPitch = std::max(w * pixelSize, 4);
|
||||
|
||||
pixelData = (uint8_t *)AllocateAlignedMemory(decPitch * h * pixelSize, 16);
|
||||
DecodeTextureLevel(pixelData, decPitch, GETextureFormat(entry.format), clutformat, texaddr, level, bufw, true, false, false);
|
||||
|
||||
// We check before scaling since scaling shouldn't invent alpha from a full alpha texture.
|
||||
if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) {
|
||||
TexCacheEntry::TexStatus alphaStatus = CheckAlpha(pixelData, dstFmt, useUnpack ? bufw : w, w, h);
|
||||
TexCacheEntry::TexStatus alphaStatus = CheckAlpha(pixelData, dstFmt, decPitch / pixelSize, w, h);
|
||||
entry.SetAlphaStatus(alphaStatus, level);
|
||||
} else {
|
||||
entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN);
|
||||
@@ -798,11 +795,9 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
scaler.ScaleAlways((u32 *)rearrange, (u32 *)pixelData, dstFmt, w, h, scaleFactor);
|
||||
FreeAlignedMemory(pixelData);
|
||||
pixelData = rearrange;
|
||||
decPitch = w * 4;
|
||||
}
|
||||
|
||||
// Textures are always aligned to 16 bytes bufw, so this could safely be 4 always.
|
||||
texByteAlign = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
|
||||
if (replacer_.Enabled()) {
|
||||
ReplacedTextureDecodeInfo replacedInfo;
|
||||
replacedInfo.cachekey = entry.CacheKey();
|
||||
@@ -813,8 +808,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
replacedInfo.scaleFactor = scaleFactor;
|
||||
replacedInfo.fmt = FromGLESFormat(dstFmt);
|
||||
|
||||
int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h);
|
||||
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@
|
||||
<ClInclude Include="D3D11\TextureScalerD3D11.h" />
|
||||
<ClInclude Include="D3D11\VertexShaderGeneratorD3D11.h" />
|
||||
<ClInclude Include="Debugger\Breakpoints.h" />
|
||||
<ClInclude Include="Debugger\Debugger.h" />
|
||||
<ClInclude Include="Debugger\Record.h" />
|
||||
<ClInclude Include="Debugger\Stepping.h" />
|
||||
<ClInclude Include="Directx9\DepalettizeShaderDX9.h" />
|
||||
@@ -320,6 +321,7 @@
|
||||
<ClCompile Include="D3D11\TextureScalerD3D11.cpp" />
|
||||
<ClCompile Include="D3D11\VertexShaderGeneratorD3D11.cpp" />
|
||||
<ClCompile Include="Debugger\Breakpoints.cpp" />
|
||||
<ClCompile Include="Debugger\Debugger.cpp" />
|
||||
<ClCompile Include="Debugger\Record.cpp" />
|
||||
<ClCompile Include="Debugger\Stepping.cpp" />
|
||||
<ClCompile Include="Directx9\DepalettizeShaderDX9.cpp" />
|
||||
|
||||
@@ -273,6 +273,9 @@
|
||||
<ClInclude Include="Vulkan\DebugVisVulkan.h">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger\Debugger.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
@@ -542,5 +545,8 @@
|
||||
<ClCompile Include="Vulkan\DebugVisVulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\Debugger.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+4
-5
@@ -28,6 +28,7 @@
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
#include "GPU/Common/TextureCacheCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
|
||||
const CommonCommandTableEntry commonCommandTable[] = {
|
||||
@@ -936,7 +937,7 @@ bool GPUCommon::InterpretList(DisplayList &list) {
|
||||
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
|
||||
|
||||
debugRecording_ = GPURecord::IsActive();
|
||||
const bool useDebugger = host->GPUDebuggingActive() || debugRecording_;
|
||||
const bool useDebugger = GPUDebug::IsActive() || debugRecording_;
|
||||
const bool useFastRunLoop = !dumpThisFrame_ && !useDebugger;
|
||||
while (gpuState == GPUSTATE_RUNNING) {
|
||||
{
|
||||
@@ -1041,7 +1042,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list)
|
||||
const bool dumpThisFrame = dumpThisFrame_;
|
||||
while (downcount > 0)
|
||||
{
|
||||
host->GPUNotifyCommand(list.pc);
|
||||
GPUDebug::NotifyCommand(list.pc);
|
||||
GPURecord::NotifyCommand(list.pc);
|
||||
u32 op = Memory::ReadUnchecked_U32(list.pc);
|
||||
u32 cmd = op >> 24;
|
||||
@@ -1593,10 +1594,8 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
|
||||
if (!g_Config.bSoftwareSkinning)
|
||||
vtypeCheckMask = 0xFFFFFFFF;
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (debugRecording_ || host->GPUDebuggingActive())
|
||||
if (debugRecording_ || GPUDebug::IsActive())
|
||||
goto bail;
|
||||
#endif
|
||||
|
||||
while (src != stall) {
|
||||
uint32_t data = *src;
|
||||
|
||||
@@ -1764,17 +1764,20 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level)
|
||||
return false;
|
||||
}
|
||||
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, false);
|
||||
|
||||
GETextureFormat texfmt = gstate.getTextureFormat();
|
||||
u32 texaddr = gstate.getTextureAddress(level);
|
||||
int texbufw = GetTextureBufw(level, texaddr, texfmt);
|
||||
u8 *texptr = Memory::GetPointer(texaddr);
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
|
||||
if (!texaddr || !Memory::IsValidRange(texaddr, (textureBitsPerPixel[texfmt] * texbufw * h) / 8))
|
||||
return false;
|
||||
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, false);
|
||||
|
||||
Sampler::Funcs sampler = Sampler::GetFuncs();
|
||||
|
||||
u8 *texptr = Memory::GetPointer(texaddr);
|
||||
u32 *row = (u32 *)buffer.GetData();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HLE/sceGe.h"
|
||||
@@ -39,6 +38,7 @@
|
||||
#include "GPU/Software/TransformUnit.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
|
||||
const int FB_WIDTH = 480;
|
||||
@@ -141,7 +141,7 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for
|
||||
displayFramebuf_ = (framebuf & 0xFF000000) == 0 ? 0x44000000 | framebuf : framebuf;
|
||||
displayStride_ = stride;
|
||||
displayFormat_ = format;
|
||||
host->GPUNotifyDisplay(framebuf, stride, format);
|
||||
GPUDebug::NotifyDisplay(framebuf, stride, format);
|
||||
}
|
||||
|
||||
// Copies RGBA8 data from RAM to the currently bound render target.
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Software/TransformUnit.h"
|
||||
#include "GPU/Software/Clipper.h"
|
||||
#include "GPU/Software/Lighting.h"
|
||||
@@ -490,7 +489,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
|
||||
break;
|
||||
}
|
||||
|
||||
host->GPUNotifyDraw();
|
||||
GPUDebug::NotifyDraw();
|
||||
}
|
||||
|
||||
// TODO: This probably is not the best interface.
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
@@ -44,6 +43,7 @@
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SoftwareTransformCommon.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Vulkan/DrawEngineVulkan.h"
|
||||
#include "GPU/Vulkan/TextureCacheVulkan.h"
|
||||
#include "GPU/Vulkan/ShaderManagerVulkan.h"
|
||||
@@ -976,7 +976,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
gstate_c.vertBounds.maxU = 0;
|
||||
gstate_c.vertBounds.maxV = 0;
|
||||
|
||||
host->GPUNotifyDraw();
|
||||
GPUDebug::NotifyDraw();
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::UpdateUBOs(FrameData *frame) {
|
||||
|
||||
@@ -364,16 +364,16 @@ void FramebufferManagerVulkan::ReformatFramebufferFrom(VirtualFramebuffer *vfb,
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
// (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.)
|
||||
// (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.)
|
||||
//
|
||||
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
|
||||
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
// TODO: To match other backends, would be ideal to clear alpha only and not color.
|
||||
// But probably doesn't matter that much...
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
@@ -36,7 +35,7 @@
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GeDisasm.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Vulkan/ShaderManagerVulkan.h"
|
||||
#include "GPU/Vulkan/GPU_Vulkan.h"
|
||||
#include "GPU/Vulkan/FramebufferVulkan.h"
|
||||
@@ -405,7 +404,7 @@ void GPU_Vulkan::UpdateVsyncInterval(bool force) {
|
||||
}
|
||||
|
||||
void GPU_Vulkan::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
||||
host->GPUNotifyDisplay(framebuf, stride, format);
|
||||
GPUDebug::NotifyDisplay(framebuf, stride, format);
|
||||
framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,9 +90,6 @@ public:
|
||||
NativeMessageReceived(message.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
bool GPUDebuggingActive() override {
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
const char* SymbolMapFilename(std::string currentFilename);
|
||||
MainWindow* mainWindow;
|
||||
|
||||
+84
-2
@@ -19,6 +19,7 @@ SDLJoystick *joystick = NULL;
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
#include <locale>
|
||||
|
||||
#include "base/display.h"
|
||||
#include "base/logging.h"
|
||||
@@ -187,8 +188,23 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
#else
|
||||
return "SDL:";
|
||||
#endif
|
||||
case SYSPROP_LANGREGION:
|
||||
case SYSPROP_LANGREGION: {
|
||||
// Get user-preferred locale from OS
|
||||
setlocale(LC_ALL, "");
|
||||
std::string locale(setlocale(LC_ALL, NULL));
|
||||
// Set c and c++ strings back to POSIX
|
||||
std::locale::global(std::locale("POSIX"));
|
||||
if (!locale.empty()) {
|
||||
if (locale.find("_", 0) != std::string::npos) {
|
||||
if (locale.find(".", 0) != std::string::npos) {
|
||||
return locale.substr(0, locale.find(".",0));
|
||||
} else {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "en_US";
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -339,6 +355,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
int set_xres = -1;
|
||||
int set_yres = -1;
|
||||
int w = 0, h = 0;
|
||||
bool portrait = false;
|
||||
bool set_ipad = false;
|
||||
float set_dpi = 1.0f;
|
||||
@@ -585,7 +602,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
while (true) {
|
||||
double startTime = time_now_d();
|
||||
SDL_Event event;
|
||||
SDL_Event event, touchEvent;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
float mx = event.motion.x * g_dpi_scale_x;
|
||||
float my = event.motion.y * g_dpi_scale_y;
|
||||
@@ -665,6 +682,71 @@ int main(int argc, char *argv[]) {
|
||||
NativeKey(key);
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERMOTION:
|
||||
{
|
||||
if (!g_Config.bFullScreen) { break; }
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
touchEvent.type = SDL_MOUSEMOTION;
|
||||
touchEvent.motion.type = SDL_MOUSEMOTION;
|
||||
touchEvent.motion.timestamp = event.tfinger.timestamp;
|
||||
touchEvent.motion.windowID = SDL_GetWindowID(window);
|
||||
touchEvent.motion.which = SDL_TOUCH_MOUSEID;
|
||||
touchEvent.motion.state = SDL_GetMouseState(NULL, NULL);
|
||||
touchEvent.motion.x = event.tfinger.x * w;
|
||||
touchEvent.motion.y = event.tfinger.y * h;
|
||||
|
||||
SDL_WarpMouseInWindow(window, event.tfinger.x * w, event.tfinger.y * h);
|
||||
|
||||
SDL_PushEvent(&touchEvent);
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERDOWN:
|
||||
{
|
||||
if (!g_Config.bFullScreen) { break; }
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
touchEvent.type = SDL_MOUSEBUTTONDOWN;
|
||||
touchEvent.button.type = SDL_MOUSEBUTTONDOWN;
|
||||
touchEvent.button.timestamp = SDL_GetTicks();
|
||||
touchEvent.button.windowID = SDL_GetWindowID(window);
|
||||
touchEvent.button.which = SDL_TOUCH_MOUSEID;
|
||||
touchEvent.button.button = SDL_BUTTON_LEFT;
|
||||
touchEvent.button.state = SDL_PRESSED;
|
||||
touchEvent.button.clicks = 1;
|
||||
touchEvent.button.x = event.tfinger.x * w;
|
||||
touchEvent.button.y = event.tfinger.y * h;
|
||||
|
||||
touchEvent.motion.type = SDL_MOUSEMOTION;
|
||||
touchEvent.motion.timestamp = SDL_GetTicks();
|
||||
touchEvent.motion.windowID = SDL_GetWindowID(window);
|
||||
touchEvent.motion.which = SDL_TOUCH_MOUSEID;
|
||||
touchEvent.motion.x = event.tfinger.x * w;
|
||||
touchEvent.motion.y = event.tfinger.y * h;
|
||||
// Any real mouse cursor should also move
|
||||
SDL_WarpMouseInWindow(window, event.tfinger.x * w, event.tfinger.y * h);
|
||||
// First finger down event also has to be a motion to that position
|
||||
SDL_PushEvent(&touchEvent);
|
||||
touchEvent.motion.type = SDL_MOUSEBUTTONDOWN;
|
||||
// Now we push the mouse button event
|
||||
SDL_PushEvent(&touchEvent);
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERUP:
|
||||
{
|
||||
if(!g_Config.bFullScreen) { break; }
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
touchEvent.type = SDL_MOUSEBUTTONUP;
|
||||
touchEvent.button.type = SDL_MOUSEBUTTONUP;
|
||||
touchEvent.button.timestamp = SDL_GetTicks();
|
||||
touchEvent.button.windowID = SDL_GetWindowID(window);
|
||||
touchEvent.button.which = SDL_TOUCH_MOUSEID;
|
||||
touchEvent.button.button = SDL_BUTTON_LEFT;
|
||||
touchEvent.button.state = SDL_RELEASED;
|
||||
touchEvent.button.clicks = 1;
|
||||
touchEvent.button.x = event.tfinger.x * w;
|
||||
touchEvent.button.y = event.tfinger.y * h;
|
||||
SDL_PushEvent(&touchEvent);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
switch (event.button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
|
||||
@@ -121,6 +121,8 @@ static std::string PostShaderTranslateName(const char *value) {
|
||||
}
|
||||
|
||||
void GameSettingsScreen::CreateViews() {
|
||||
ReloadAllPostShaderInfo();
|
||||
|
||||
if (editThenRestore_) {
|
||||
g_Config.loadGameConfig(gameID_);
|
||||
}
|
||||
@@ -1333,6 +1335,12 @@ void DeveloperToolsScreen::CreateViews() {
|
||||
cpuTests->SetEnabled(TestsAvailable());
|
||||
#endif
|
||||
|
||||
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
|
||||
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
|
||||
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
|
||||
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
|
||||
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
|
||||
|
||||
list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
|
||||
list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
|
||||
list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig);
|
||||
@@ -1349,12 +1357,6 @@ void DeveloperToolsScreen::CreateViews() {
|
||||
createTextureIni->SetEnabled(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
|
||||
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
|
||||
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
|
||||
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
|
||||
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
|
||||
}
|
||||
|
||||
void DeveloperToolsScreen::onFinish(DialogResult result) {
|
||||
|
||||
+5
-4
@@ -495,10 +495,11 @@ UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) {
|
||||
|
||||
UI::EventReturn GameBrowser::PinToggleClick(UI::EventParams &e) {
|
||||
auto &pinnedPaths = g_Config.vPinnedPaths;
|
||||
const std::string path = File::ResolvePath(path_.GetPath());
|
||||
if (IsCurrentPathPinned()) {
|
||||
pinnedPaths.erase(std::remove(pinnedPaths.begin(), pinnedPaths.end(), path_.GetPath()), pinnedPaths.end());
|
||||
pinnedPaths.erase(std::remove(pinnedPaths.begin(), pinnedPaths.end(), path), pinnedPaths.end());
|
||||
} else {
|
||||
pinnedPaths.push_back(path_.GetPath());
|
||||
pinnedPaths.push_back(path);
|
||||
}
|
||||
Refresh();
|
||||
return UI::EVENT_DONE;
|
||||
@@ -662,7 +663,7 @@ void GameBrowser::Refresh() {
|
||||
|
||||
bool GameBrowser::IsCurrentPathPinned() {
|
||||
const auto paths = g_Config.vPinnedPaths;
|
||||
return std::find(paths.begin(), paths.end(), path_.GetPath()) != paths.end();
|
||||
return std::find(paths.begin(), paths.end(), File::ResolvePath(path_.GetPath())) != paths.end();
|
||||
}
|
||||
|
||||
const std::vector<std::string> GameBrowser::GetPinnedPaths() {
|
||||
@@ -672,7 +673,7 @@ const std::vector<std::string> GameBrowser::GetPinnedPaths() {
|
||||
static const std::string sepChars = "/\\";
|
||||
#endif
|
||||
|
||||
const std::string currentPath = path_.GetPath();
|
||||
const std::string currentPath = File::ResolvePath(path_.GetPath());
|
||||
const std::vector<std::string> paths = g_Config.vPinnedPaths;
|
||||
std::vector<std::string> results;
|
||||
for (size_t i = 0; i < paths.size(); ++i) {
|
||||
|
||||
+1
-1
@@ -398,7 +398,7 @@ void NewLanguageScreen::OnCompleted(DialogResult result) {
|
||||
bool iniLoadedSuccessfully = false;
|
||||
// Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to
|
||||
// test new languages without recompiling the entire app, which is a hassle).
|
||||
const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/";
|
||||
const std::string langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) + "lang/";
|
||||
|
||||
// If we run into the unlikely case that "lang" is actually a file, just use the built-in translations.
|
||||
if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath))
|
||||
|
||||
+79
-14
@@ -29,13 +29,10 @@
|
||||
// in NativeShutdown.
|
||||
|
||||
#include <locale.h>
|
||||
// Linux doesn't like using std::find with std::vector<int> without this :/
|
||||
#if !defined(MOBILE_DEVICE)
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "Windows/DSoundStream.h"
|
||||
@@ -145,6 +142,7 @@ static bool resized = false;
|
||||
static bool restarting = false;
|
||||
|
||||
static bool askedForStoragePermission = false;
|
||||
static int renderCounter = 0;
|
||||
|
||||
struct PendingMessage {
|
||||
std::string msg;
|
||||
@@ -323,7 +321,7 @@ static void PostLoadConfig() {
|
||||
|
||||
// Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to
|
||||
// test new languages without recompiling the entire app, which is a hassle).
|
||||
const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/";
|
||||
const std::string langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) + "lang/";
|
||||
|
||||
// If we run into the unlikely case that "lang" is actually a file, just use the built-in translations.
|
||||
if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath))
|
||||
@@ -336,15 +334,75 @@ void CreateDirectoriesAndroid() {
|
||||
// On Android, create a PSP directory tree in the external_dir,
|
||||
// to hopefully reduce confusion a bit.
|
||||
ILOG("Creating %s", (g_Config.memStickDirectory + "PSP").c_str());
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/SAVEDATA");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/PPSSPP_STATE");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/GAME");
|
||||
File::CreateDir(g_Config.memStickDirectory + "PSP/SYSTEM");
|
||||
File::CreateFullPath(g_Config.memStickDirectory + "PSP");
|
||||
File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVEDATA));
|
||||
File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVESTATE));
|
||||
File::CreateFullPath(GetSysDirectory(DIRECTORY_GAME));
|
||||
File::CreateFullPath(GetSysDirectory(DIRECTORY_SYSTEM));
|
||||
|
||||
// Avoid media scanners in PPSSPP_STATE and SAVEDATA directories
|
||||
File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/PPSSPP_STATE/.nomedia");
|
||||
File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia");
|
||||
File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVESTATE) + ".nomedia");
|
||||
File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVEDATA) + ".nomedia");
|
||||
File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SYSTEM) + ".nomedia");
|
||||
}
|
||||
|
||||
static void CheckFailedGPUBackends() {
|
||||
// We only want to do this once per process run and backend, to detect process crashes.
|
||||
// If NativeShutdown is called before we finish, we might call this multiple times.
|
||||
static int lastBackend = -1;
|
||||
if (lastBackend == g_Config.iGPUBackend) {
|
||||
return;
|
||||
}
|
||||
lastBackend = g_Config.iGPUBackend;
|
||||
|
||||
std::string cache = GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt";
|
||||
|
||||
if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) {
|
||||
std::string data;
|
||||
if (readFileToString(true, cache.c_str(), data))
|
||||
g_Config.sFailedGPUBackends = data;
|
||||
}
|
||||
|
||||
// Use this if you want to debug a graphics crash...
|
||||
if (g_Config.sFailedGPUBackends == "IGNORE")
|
||||
return;
|
||||
else if (!g_Config.sFailedGPUBackends.empty())
|
||||
ERROR_LOG(LOADER, "Failed graphics backends: %s", g_Config.sFailedGPUBackends.c_str());
|
||||
|
||||
// Okay, let's not try a backend in the failed list.
|
||||
g_Config.iGPUBackend = g_Config.NextValidBackend();
|
||||
if (lastBackend != g_Config.iGPUBackend)
|
||||
WARN_LOG(LOADER, "Failed graphics backend switched from %d to %d", lastBackend, g_Config.iGPUBackend);
|
||||
// And then let's - for now - add the current to the failed list.
|
||||
if (g_Config.sFailedGPUBackends.empty()) {
|
||||
g_Config.sFailedGPUBackends = StringFromFormat("%d", g_Config.iGPUBackend);
|
||||
} else if (g_Config.sFailedGPUBackends.find("ALL") == std::string::npos) {
|
||||
g_Config.sFailedGPUBackends += StringFromFormat(",%d", g_Config.iGPUBackend);
|
||||
}
|
||||
|
||||
if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) {
|
||||
// Let's try to create, in case it doesn't exist.
|
||||
if (!File::Exists(GetSysDirectory(DIRECTORY_APP_CACHE)))
|
||||
File::CreateDir(GetSysDirectory(DIRECTORY_APP_CACHE));
|
||||
writeStringToFile(true, g_Config.sFailedGPUBackends, cache.c_str());
|
||||
} else {
|
||||
// Just save immediately, since we have storage.
|
||||
g_Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
static void ClearFailedGPUBackends() {
|
||||
if (g_Config.sFailedGPUBackends == "IGNORE")
|
||||
return;
|
||||
|
||||
// We've successfully started graphics without crashing, hurray.
|
||||
// In case they update drivers and have totally different problems much later, clear the failed list.
|
||||
g_Config.sFailedGPUBackends.clear();
|
||||
if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) {
|
||||
File::Delete(GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt");
|
||||
} else {
|
||||
g_Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir) {
|
||||
@@ -420,8 +478,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
|
||||
|
||||
#ifndef _WIN32
|
||||
g_Config.AddSearchPath(user_data_path);
|
||||
g_Config.AddSearchPath(g_Config.memStickDirectory + "PSP/SYSTEM/");
|
||||
g_Config.SetDefaultPath(g_Config.memStickDirectory + "PSP/SYSTEM/");
|
||||
g_Config.AddSearchPath(GetSysDirectory(DIRECTORY_SYSTEM));
|
||||
g_Config.SetDefaultPath(GetSysDirectory(DIRECTORY_SYSTEM));
|
||||
|
||||
// Note that if we don't have storage permission here, loading the config will
|
||||
// fail and it will be set to the default. Later, we load again when we get permission.
|
||||
@@ -630,7 +688,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
|
||||
|
||||
// We do this here, instead of in NativeInitGraphics, because the display may be reset.
|
||||
// When it's reset we don't want to forget all our managed things.
|
||||
CheckFailedGPUBackends();
|
||||
SetGPUBackend((GPUBackend) g_Config.iGPUBackend);
|
||||
renderCounter = 0;
|
||||
|
||||
// Must be done restarting by now.
|
||||
restarting = false;
|
||||
@@ -951,6 +1011,11 @@ void NativeRender(GraphicsContext *graphicsContext) {
|
||||
|
||||
ui_draw2d.PopDrawMatrix();
|
||||
ui_draw2d_front.PopDrawMatrix();
|
||||
|
||||
if (renderCounter < 10 && ++renderCounter == 10) {
|
||||
// We're rendering fine, clear out failure info.
|
||||
ClearFailedGPUBackends();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleGlobalMessage(const std::string &msg, const std::string &value) {
|
||||
|
||||
+7
-4
@@ -138,12 +138,15 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool FillVertical() const override { return false; }
|
||||
bool FillVertical() const override { return false; }
|
||||
UI::Size PopupWidth() const override { return 500; }
|
||||
bool ShowButtons() const override { return true; }
|
||||
|
||||
virtual void CreatePopupContents(UI::ViewGroup *parent) override {
|
||||
// TODO: Find an appropriate size for the image view
|
||||
parent->Add(new AsyncImageFileView(filename_, UI::IS_DEFAULT, NULL, new UI::LayoutParams(480, 272)))->SetCanBeFocused(false);
|
||||
void CreatePopupContents(UI::ViewGroup *parent) override {
|
||||
UI::LinearLayout *content = new UI::LinearLayout(UI::ORIENT_VERTICAL);
|
||||
parent->Add(content);
|
||||
UI::Margins contentMargins(10, 0);
|
||||
content->Add(new AsyncImageFileView(filename_, UI::IS_DEFAULT, nullptr, new UI::LinearLayoutParams(480, 272, contentMargins)))->SetCanBeFocused(false);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -332,6 +332,7 @@
|
||||
<ClInclude Include="..\..\GPU\D3D11\TextureScalerD3D11.h" />
|
||||
<ClInclude Include="..\..\GPU\D3D11\VertexShaderGeneratorD3D11.h" />
|
||||
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h" />
|
||||
<ClInclude Include="..\..\GPU\Debugger\Debugger.h" />
|
||||
<ClInclude Include="..\..\GPU\Debugger\Record.h" />
|
||||
<ClInclude Include="..\..\GPU\Debugger\Stepping.h" />
|
||||
<ClInclude Include="..\..\GPU\Directx9\PixelShaderGeneratorDX9.h" />
|
||||
@@ -390,6 +391,7 @@
|
||||
<ClCompile Include="..\..\GPU\D3D11\TextureScalerD3D11.cpp" />
|
||||
<ClCompile Include="..\..\GPU\D3D11\VertexShaderGeneratorD3D11.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Debugger\Record.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Debugger\Stepping.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Directx9\PixelShaderGeneratorDX9.cpp" />
|
||||
|
||||
@@ -150,13 +150,18 @@
|
||||
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\GPU\Debugger\Stepping.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\GPU\Common\ShaderCommon.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\GPU\Software\Sampler.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\Sampler.cpp">
|
||||
<Filter>Software</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\GPU\Debugger\Record.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
@@ -291,10 +296,15 @@
|
||||
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\GPU\Debugger\Debugger.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\GPU\Debugger\Stepping.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\GPU\Software\Sampler.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\Sampler.h">
|
||||
<Filter>Software</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\GPU\Debugger\Record.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -32,11 +32,6 @@ public:
|
||||
void SaveSymbolMap() override;
|
||||
void SetWindowTitle(const char *message) override;
|
||||
|
||||
bool GPUDebuggingActive() override { return false; }
|
||||
void GPUNotifyCommand(u32 pc) override {}
|
||||
void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override {}
|
||||
void GPUNotifyDraw() override {}
|
||||
void GPUNotifyTextureAttachment(u32 addr) override {}
|
||||
void ToggleDebugConsoleVisibility() override;
|
||||
|
||||
bool CanCreateShortcut() override;
|
||||
|
||||
@@ -132,6 +132,18 @@ void MainThreadFunc() {
|
||||
bool performingRestart = NativeIsRestarting();
|
||||
NativeInit(static_cast<int>(args.size()), &args[0], "1234", "1234", nullptr);
|
||||
|
||||
if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) {
|
||||
Reporting::ReportMessage("Graphics init error: %s", "ALL");
|
||||
|
||||
I18NCategory *err = GetI18NCategory("Error");
|
||||
const char *defaultErrorAll = "Failed initializing any graphics. Try upgrading your graphics drivers.";
|
||||
const char *genericError = err->T("GenericAllGraphicsError", defaultErrorAll);
|
||||
std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error"));
|
||||
MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK);
|
||||
|
||||
// Let's continue (and probably crash) just so they have a way to keep trying.
|
||||
}
|
||||
|
||||
host->UpdateUI();
|
||||
|
||||
std::string error_string;
|
||||
@@ -182,6 +194,8 @@ void MainThreadFunc() {
|
||||
if (yes) {
|
||||
// Change the config to the alternative and restart.
|
||||
g_Config.iGPUBackend = (int)nextBackend;
|
||||
// Clear this to ensure we try their selection.
|
||||
g_Config.sFailedGPUBackends.clear();
|
||||
g_Config.Save();
|
||||
|
||||
W32Util::ExitAndRestart();
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "Windows/GEDebugger/TabVertices.h"
|
||||
#include "Windows/W32Util/ShellUtil.h"
|
||||
#include "Windows/InputBox.h"
|
||||
#include "Windows/WindowsHost.h"
|
||||
#include "Windows/MainWindow.h"
|
||||
#include "Windows/main.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
@@ -39,6 +38,7 @@
|
||||
#include "GPU/Common/GPUStateUtils.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Debugger/Breakpoints.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
#include "GPU/Debugger/Stepping.h"
|
||||
#include <windowsx.h>
|
||||
@@ -47,12 +47,9 @@
|
||||
const int POPUP_SUBMENU_ID_GEDBG_PREVIEW = 10;
|
||||
|
||||
using namespace GPUBreakpoints;
|
||||
using namespace GPUDebug;
|
||||
using namespace GPUStepping;
|
||||
|
||||
static bool attached = false;
|
||||
|
||||
static BreakNextType breakNext = BREAK_NONE;
|
||||
|
||||
enum PrimaryDisplayType {
|
||||
PRIMARY_FRAMEBUF,
|
||||
PRIMARY_DEPTHBUF,
|
||||
@@ -66,9 +63,6 @@ void CGEDebugger::Init() {
|
||||
|
||||
CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
|
||||
: Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) {
|
||||
GPUBreakpoints::Init();
|
||||
Core_ListenStopRequest(ForceUnpause);
|
||||
|
||||
// minimum size = a little more than the default
|
||||
RECT windowRect;
|
||||
GetWindowRect(m_hDlg, &windowRect);
|
||||
@@ -129,6 +123,8 @@ CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
|
||||
int h = g_Config.iGEWindowH == -1 ? minHeight_ : g_Config.iGEWindowH;
|
||||
MoveWindow(m_hDlg,x,y,w,h,FALSE);
|
||||
|
||||
SetTimer(m_hDlg, 1, USER_TIMER_MINIMUM, nullptr);
|
||||
|
||||
UpdateTextureLevel(textureLevel_);
|
||||
}
|
||||
|
||||
@@ -331,6 +327,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) {
|
||||
bool bufferResult = false;
|
||||
u32 flags = SimpleGLWindow::ALPHA_IGNORE | SimpleGLWindow::RESIZE_SHRINK_CENTER;
|
||||
|
||||
SetupPreviews();
|
||||
|
||||
primaryBuffer_ = nullptr;
|
||||
if (showClut_) {
|
||||
bufferResult = GPU_GetCurrentTexture(primaryBuffer_, textureLevel_);
|
||||
@@ -375,6 +373,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) {
|
||||
void CGEDebugger::UpdateSecondPreview(const GPUgstate &state) {
|
||||
bool bufferResult = false;
|
||||
|
||||
SetupPreviews();
|
||||
|
||||
secondBuffer_ = nullptr;
|
||||
if (showClut_) {
|
||||
bufferResult = GPU_GetCurrentClut(secondBuffer_);
|
||||
@@ -417,6 +417,8 @@ void CGEDebugger::PrimaryPreviewHover(int x, int y) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetupPreviews();
|
||||
|
||||
wchar_t desc[256] = {0};
|
||||
|
||||
if (!primaryWindow->HasTex()) {
|
||||
@@ -639,14 +641,6 @@ void CGEDebugger::SavePosition()
|
||||
}
|
||||
}
|
||||
|
||||
void CGEDebugger::SetBreakNext(BreakNextType type) {
|
||||
attached = true;
|
||||
SetupPreviews();
|
||||
|
||||
breakNext = type;
|
||||
ResumeFromStepping();
|
||||
}
|
||||
|
||||
BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
switch (message) {
|
||||
case WM_INITDIALOG:
|
||||
@@ -670,9 +664,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
return TRUE;
|
||||
|
||||
case WM_CLOSE:
|
||||
attached = false;
|
||||
ResumeFromStepping();
|
||||
breakNext = BREAK_NONE;
|
||||
GPUDebug::SetActive(false);
|
||||
|
||||
Show(false);
|
||||
return TRUE;
|
||||
@@ -687,6 +679,16 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_TIMER:
|
||||
if (GPUStepping::IsStepping()) {
|
||||
static int lastCounter = 0;
|
||||
if (lastCounter != GPUStepping::GetSteppingCounter()) {
|
||||
UpdatePreviews();
|
||||
lastCounter = GPUStepping::GetSteppingCounter();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
switch (wParam)
|
||||
{
|
||||
@@ -698,7 +700,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
break;
|
||||
case IDC_GEDBG_FBTABS:
|
||||
fbTabs->HandleNotify(lParam);
|
||||
if (attached && gpuDebug != nullptr) {
|
||||
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
|
||||
UpdatePreviews();
|
||||
}
|
||||
break;
|
||||
@@ -708,32 +710,28 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case IDC_GEDBG_STEPDRAW:
|
||||
SetBreakNext(BREAK_NEXT_DRAW);
|
||||
SetBreakNext(BreakNext::DRAW);
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_STEP:
|
||||
SetBreakNext(BREAK_NEXT_OP);
|
||||
SetBreakNext(BreakNext::OP);
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_STEPTEX:
|
||||
AddTextureChangeTempBreakpoint();
|
||||
SetBreakNext(BREAK_NEXT_TEX);
|
||||
SetBreakNext(BreakNext::TEX);
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_STEPFRAME:
|
||||
SetBreakNext(BREAK_NEXT_FRAME);
|
||||
SetBreakNext(BreakNext::FRAME);
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_STEPPRIM:
|
||||
AddCmdBreakpoint(GE_CMD_PRIM, true);
|
||||
AddCmdBreakpoint(GE_CMD_BEZIER, true);
|
||||
AddCmdBreakpoint(GE_CMD_SPLINE, true);
|
||||
SetBreakNext(BREAK_NEXT_PRIM);
|
||||
SetBreakNext(BreakNext::PRIM);
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_BREAKTEX:
|
||||
{
|
||||
attached = true;
|
||||
GPUDebug::SetActive(true);
|
||||
if (!gpuDebug) {
|
||||
break;
|
||||
}
|
||||
@@ -752,7 +750,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
case IDC_GEDBG_BREAKTARGET:
|
||||
{
|
||||
attached = true;
|
||||
GPUDebug::SetActive(true);
|
||||
if (!gpuDebug) {
|
||||
break;
|
||||
}
|
||||
@@ -771,26 +769,26 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
case IDC_GEDBG_TEXLEVELDOWN:
|
||||
UpdateTextureLevel(textureLevel_ - 1);
|
||||
if (attached && gpuDebug != nullptr) {
|
||||
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
|
||||
UpdatePreviews();
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_TEXLEVELUP:
|
||||
UpdateTextureLevel(textureLevel_ + 1);
|
||||
if (attached && gpuDebug != nullptr) {
|
||||
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
|
||||
UpdatePreviews();
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_RESUME:
|
||||
SetupPreviews();
|
||||
primaryWindow->Clear();
|
||||
secondWindow->Clear();
|
||||
SetDlgItemText(m_hDlg, IDC_GEDBG_FRAMEBUFADDR, L"");
|
||||
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L"");
|
||||
|
||||
ResumeFromStepping();
|
||||
breakNext = BREAK_NONE;
|
||||
SetBreakNext(BreakNext::NONE);
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_RECORD:
|
||||
@@ -798,14 +796,14 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_FORCEOPAQUE:
|
||||
if (attached && gpuDebug != nullptr) {
|
||||
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
|
||||
forceOpaque_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_FORCEOPAQUE), BM_GETCHECK, 0, 0) != 0;
|
||||
UpdatePreviews();
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_GEDBG_SHOWCLUT:
|
||||
if (attached && gpuDebug != nullptr) {
|
||||
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
|
||||
showClut_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_SHOWCLUT), BM_GETCHECK, 0, 0) != 0;
|
||||
UpdatePreviews();
|
||||
}
|
||||
@@ -813,30 +811,13 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_GEDBG_BREAK_CMD:
|
||||
{
|
||||
u32 pc = (u32)wParam;
|
||||
ClearTempBreakpoints();
|
||||
auto info = gpuDebug->DissassembleOp(pc);
|
||||
NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str());
|
||||
UpdatePreviews();
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_GEDBG_BREAK_DRAW:
|
||||
{
|
||||
NOTICE_LOG(G3D, "Waiting at a draw");
|
||||
UpdatePreviews();
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_GEDBG_STEPDISPLAYLIST:
|
||||
SetBreakNext(BREAK_NEXT_OP);
|
||||
SetBreakNext(BreakNext::OP);
|
||||
break;
|
||||
|
||||
case WM_GEDBG_TOGGLEPCBREAKPOINT:
|
||||
{
|
||||
attached = true;
|
||||
GPUDebug::SetActive(true);
|
||||
u32 pc = (u32)wParam;
|
||||
bool temp;
|
||||
bool isBreak = IsAddressBreakpoint(pc, temp);
|
||||
@@ -850,7 +831,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
case WM_GEDBG_RUNTOWPARAM:
|
||||
{
|
||||
attached = true;
|
||||
GPUDebug::SetActive(true);
|
||||
u32 pc = (u32)wParam;
|
||||
AddAddressBreakpoint(pc, true);
|
||||
SendMessage(m_hDlg,WM_COMMAND,IDC_GEDBG_RESUME,0);
|
||||
@@ -858,9 +839,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
break;
|
||||
|
||||
case WM_GEDBG_SETCMDWPARAM:
|
||||
{
|
||||
GPU_SetCmdValue((u32)wParam);
|
||||
}
|
||||
GPU_SetCmdValue((u32)wParam);
|
||||
break;
|
||||
|
||||
case WM_GEDBG_UPDATE_WATCH:
|
||||
@@ -872,44 +851,3 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// The below WindowsHost methods are called on the GPU thread.
|
||||
|
||||
bool WindowsHost::GPUDebuggingActive() {
|
||||
return attached;
|
||||
}
|
||||
|
||||
static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam);
|
||||
}
|
||||
|
||||
static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) {
|
||||
if (attached) {
|
||||
EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam));
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsHost::GPUNotifyCommand(u32 pc) {
|
||||
u32 op = Memory::ReadUnchecked_U32(pc);
|
||||
u8 cmd = op >> 24;
|
||||
|
||||
if (breakNext == BREAK_NEXT_OP || IsBreakpoint(pc, op)) {
|
||||
PauseWithMessage(WM_GEDBG_BREAK_CMD, (WPARAM) pc);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsHost::GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {
|
||||
if (breakNext == BREAK_NEXT_FRAME) {
|
||||
// This should work fine, start stepping at the first op of the new frame.
|
||||
breakNext = BREAK_NEXT_OP;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsHost::GPUNotifyDraw() {
|
||||
if (breakNext == BREAK_NEXT_DRAW) {
|
||||
PauseWithMessage(WM_GEDBG_BREAK_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsHost::GPUNotifyTextureAttachment(u32 addr) {
|
||||
}
|
||||
@@ -19,31 +19,20 @@
|
||||
|
||||
#include "Common/CommonWindows.h"
|
||||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
#include "GPU/Debugger/Debugger.h"
|
||||
#include "Windows/resource.h"
|
||||
#include "Windows/W32Util/DialogManager.h"
|
||||
#include "Windows/W32Util/TabControl.h"
|
||||
#include "Windows/GEDebugger/SimpleGLWindow.h"
|
||||
|
||||
enum {
|
||||
WM_GEDBG_BREAK_CMD = WM_USER + 200,
|
||||
WM_GEDBG_BREAK_DRAW,
|
||||
WM_GEDBG_STEPDISPLAYLIST,
|
||||
WM_GEDBG_STEPDISPLAYLIST = WM_USER + 200,
|
||||
WM_GEDBG_TOGGLEPCBREAKPOINT,
|
||||
WM_GEDBG_RUNTOWPARAM,
|
||||
WM_GEDBG_SETCMDWPARAM,
|
||||
WM_GEDBG_UPDATE_WATCH,
|
||||
};
|
||||
|
||||
enum BreakNextType {
|
||||
BREAK_NONE,
|
||||
BREAK_NEXT_OP,
|
||||
BREAK_NEXT_DRAW,
|
||||
BREAK_NEXT_TEX,
|
||||
BREAK_NEXT_NONTEX,
|
||||
BREAK_NEXT_FRAME,
|
||||
BREAK_NEXT_PRIM,
|
||||
};
|
||||
|
||||
class CtrlDisplayListView;
|
||||
class TabDisplayLists;
|
||||
class TabStateFlags;
|
||||
@@ -76,7 +65,6 @@ private:
|
||||
void HandleRedraw(int which);
|
||||
void UpdateSize(WORD width, WORD height);
|
||||
void SavePosition();
|
||||
void SetBreakNext(BreakNextType type);
|
||||
void UpdateTextureLevel(int level);
|
||||
void DescribePrimaryPreview(const GPUgstate &state, wchar_t desc[256]);
|
||||
void DescribeSecondPreview(const GPUgstate &state, wchar_t desc[256]);
|
||||
|
||||
@@ -145,6 +145,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) {
|
||||
if (yes) {
|
||||
// Change the config to D3D and restart.
|
||||
g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D9;
|
||||
g_Config.sFailedGPUBackends.clear();
|
||||
g_Config.Save();
|
||||
|
||||
W32Util::ExitAndRestart();
|
||||
|
||||
@@ -267,6 +267,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) {
|
||||
std::wstring whichD3D9 = ConvertUTF8ToWString(err->T("D3D9or11", d3d9Or11));
|
||||
bool d3d9 = IDYES == MessageBox(hWnd_, whichD3D9.c_str(), title.c_str(), MB_YESNO);
|
||||
g_Config.iGPUBackend = d3d9 ? (int)GPUBackend::DIRECT3D9 : (int)GPUBackend::DIRECT3D11;
|
||||
g_Config.sFailedGPUBackends.clear();
|
||||
g_Config.Save();
|
||||
|
||||
W32Util::ExitAndRestart();
|
||||
|
||||
@@ -345,6 +345,18 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Qt\QtHost.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\UWP\UWPHost.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GPU\D3D11Context.cpp" />
|
||||
<ClCompile Include="GPU\D3D9Context.cpp" />
|
||||
<ClCompile Include="Debugger\BreakpointWindow.cpp" />
|
||||
@@ -439,6 +451,18 @@
|
||||
<ClInclude Include="..\android\jni\TestRunner.h" />
|
||||
<ClInclude Include="..\ios\ViewController.h" />
|
||||
<ClInclude Include="..\Qt\mainwindow.h" />
|
||||
<ClInclude Include="..\Qt\QtHost.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\UWP\UWPHost.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GPU\D3D11Context.h" />
|
||||
<ClInclude Include="GPU\D3D9Context.h" />
|
||||
<ClInclude Include="Debugger\BreakpointWindow.h" />
|
||||
|
||||
@@ -182,6 +182,12 @@
|
||||
<ClCompile Include="..\android\jni\AndroidVulkanContext.cpp">
|
||||
<Filter>Other Platforms</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Qt\QtHost.cpp">
|
||||
<Filter>Other Platforms\Qt</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\UWP\UWPHost.cpp">
|
||||
<Filter>Other Platforms</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Debugger\CtrlDisAsmView.h">
|
||||
@@ -335,6 +341,12 @@
|
||||
<ClInclude Include="..\android\jni\AndroidVulkanContext.h">
|
||||
<Filter>Other Platforms</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Qt\QtHost.h">
|
||||
<Filter>Other Platforms\Qt</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\UWP\UWPHost.h">
|
||||
<Filter>Other Platforms</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="icon1.ico">
|
||||
|
||||
@@ -55,11 +55,6 @@ public:
|
||||
void SaveSymbolMap() override;
|
||||
void SetWindowTitle(const char *message) override;
|
||||
|
||||
bool GPUDebuggingActive() override;
|
||||
void GPUNotifyCommand(u32 pc) override;
|
||||
void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override;
|
||||
void GPUNotifyDraw() override;
|
||||
void GPUNotifyTextureAttachment(u32 addr) override;
|
||||
void ToggleDebugConsoleVisibility() override;
|
||||
|
||||
bool CanCreateShortcut() override;
|
||||
|
||||
@@ -242,6 +242,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/GPU/Common/PostShader.cpp \
|
||||
$(SRC)/GPU/Common/ShaderUniforms.cpp \
|
||||
$(SRC)/GPU/Debugger/Breakpoints.cpp \
|
||||
$(SRC)/GPU/Debugger/Debugger.cpp \
|
||||
$(SRC)/GPU/Debugger/Record.cpp \
|
||||
$(SRC)/GPU/Debugger/Stepping.cpp \
|
||||
$(SRC)/GPU/GLES/FramebufferManagerGLES.cpp \
|
||||
@@ -309,6 +310,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/GPURecordSubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \
|
||||
|
||||
@@ -54,6 +54,12 @@ public class TextRenderer {
|
||||
total.x = Math.max(sz.x, total.x);
|
||||
}
|
||||
total.y = (int) (p.descent() - p.ascent()) * lines.length + 2;
|
||||
// Returning a 0 size can create problems when the caller
|
||||
// uses the measurement to create a texture.
|
||||
if (total.x < 1)
|
||||
total.x = 1;
|
||||
if (total.y < 1)
|
||||
total.y = 1;
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -67,10 +73,6 @@ public class TextRenderer {
|
||||
|
||||
int w = s.x;
|
||||
int h = s.y;
|
||||
if (w == 0)
|
||||
w = 1;
|
||||
if (h == 0)
|
||||
h = 1;
|
||||
|
||||
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
|
||||
@@ -418,10 +418,20 @@ void CheckGLExtensions() {
|
||||
} else {
|
||||
g_all_egl_extensions = "";
|
||||
}
|
||||
#elif defined(USING_GLES2) && defined(__linux__)
|
||||
const char *eglString = eglQueryString(NULL, EGL_EXTENSIONS);
|
||||
if (eglString) {
|
||||
g_all_egl_extensions = std::string(eglString);
|
||||
g_all_egl_extensions.append(" ");
|
||||
g_all_egl_extensions.append(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
|
||||
} else {
|
||||
g_all_egl_extensions = "";
|
||||
}
|
||||
#endif
|
||||
|
||||
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &gl_extensions.maxVertexTextureUnits);
|
||||
|
||||
#ifdef GL_LOW_FLOAT
|
||||
// This is probably a waste of time, implementations lie.
|
||||
if (gl_extensions.IsGLES || strstr(extString, "GL_ARB_ES2_compatibility") || gl_extensions.VersionGEThan(4, 1)) {
|
||||
const GLint precisions[6] = {
|
||||
@@ -437,6 +447,7 @@ void CheckGLExtensions() {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
gl_extensions.ARB_framebuffer_object = strstr(extString, "GL_ARB_framebuffer_object") != 0;
|
||||
gl_extensions.EXT_framebuffer_object = strstr(extString, "GL_EXT_framebuffer_object") != 0;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
void InitFastMath(int enableNEON) {
|
||||
// Every architecture has its own define. This needs to be added to.
|
||||
if (enableNEON) {
|
||||
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7S__)
|
||||
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
|
||||
fast_matrix_mul_4x4 = &fast_matrix_mul_4x4_neon;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ extern void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b)
|
||||
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
// Hard link to SSE implementations on x86/amd64
|
||||
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_sse
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_c
|
||||
#else
|
||||
extern fptr_fast_matrix_mul_4x4 fast_matrix_mul_4x4;
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "ppsspp_config.h"
|
||||
#if PPSSPP_ARCH(ARM) && PPSSPP_ARCH(ARM_NEON)
|
||||
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
|
||||
|
||||
.syntax unified // Allow both ARM and Thumb-2 instructions
|
||||
.text
|
||||
|
||||
@@ -1166,7 +1166,7 @@ void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) {
|
||||
int pixelStride = pass.readback_image.srcRect.w;
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
|
||||
GLRect2D rect = pass.readback.srcRect;
|
||||
GLRect2D rect = pass.readback_image.srcRect;
|
||||
|
||||
int size = 4 * rect.w * rect.h;
|
||||
if (size > readbackBufferSize_) {
|
||||
|
||||
@@ -176,7 +176,7 @@ public:
|
||||
data.draw.vbuffer = vbuffer;
|
||||
data.draw.voffset = voffset;
|
||||
data.draw.numUboOffsets = numUboOffsets;
|
||||
assert(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets));
|
||||
assert(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets));
|
||||
for (int i = 0; i < numUboOffsets; i++)
|
||||
data.draw.uboOffsets[i] = uboOffsets[i];
|
||||
curRenderStep_->commands.push_back(data);
|
||||
|
||||
@@ -1078,12 +1078,11 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {
|
||||
}
|
||||
|
||||
Texture *VKContext::CreateTexture(const TextureDesc &desc) {
|
||||
if (!push_) {
|
||||
if (!push_ || !renderManager_.GetInitCmd()) {
|
||||
// Too early! Fail.
|
||||
ELOG("Can't create textures before the first frame has started.");
|
||||
return nullptr;
|
||||
}
|
||||
_assert_(renderManager_.GetInitCmd());
|
||||
return new VKTexture(vulkan_, renderManager_.GetInitCmd(), push_, desc, allocator_);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,8 +168,9 @@ SOURCES_CXX += \
|
||||
$(GPUCOMMONDIR)/PostShader.cpp \
|
||||
$(COMMONDIR)/ColorConv.cpp \
|
||||
$(GPUDIR)/Debugger/Breakpoints.cpp \
|
||||
$(GPUDIR)/Debugger/Stepping.cpp \
|
||||
$(GPUDIR)/Debugger/Debugger.cpp \
|
||||
$(GPUDIR)/Debugger/Record.cpp \
|
||||
$(GPUDIR)/Debugger/Stepping.cpp \
|
||||
$(GPUDIR)/Common/TextureCacheCommon.cpp \
|
||||
$(GPUDIR)/Common/TextureScalerCommon.cpp \
|
||||
$(GPUDIR)/Common/SoftwareTransformCommon.cpp \
|
||||
|
||||
Reference in new issue
Block a user