diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b874b8721..2018645094 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Common/ColorConv.cpp b/Common/ColorConv.cpp index c032fd4bc9..bf4276f1e3 100644 --- a/Common/ColorConv.cpp +++ b/Common/ColorConv.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; diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 88581332c7..145040f1ed 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -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()); diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 3b43e80c02..aeb29f20ea 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -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; } diff --git a/Core/Config.cpp b/Core/Config.cpp index 07929c3ca4..6ec47626a0 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "base/display.h" #include "base/NativeApp.h" @@ -551,6 +552,49 @@ static int DefaultGPUBackend() { return (int)GPUBackend::OPENGL; } +int Config::NextValidBackend() { + std::vector split; + std::set 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.) diff --git a/Core/Config.h b/Core/Config.h index c3cfb75ac3..5bf915b643 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -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(); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 068241f0c6..c5a39b54be 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -188,6 +188,7 @@ + @@ -549,6 +550,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index ba79121304..b459851f73 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -734,6 +734,9 @@ HLE\Libraries + + Debugger\WebSocket + @@ -1361,6 +1364,9 @@ HLE\Libraries + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 4504c827f7..6175279363 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -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 subscribers({ - { &WebSocketBreakpointInit, nullptr }, - { &WebSocketCPUCoreInit, nullptr }, - { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, - { &WebSocketGameInit, nullptr }, - { &WebSocketGPUBufferInit, nullptr }, - { &WebSocketHLEInit, nullptr }, - { &WebSocketSteppingInit, &WebSocketSteppingShutdown }, +typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map); +static const std::vector 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 eventHandlers; - std::vector subscriberData; - for (auto info : subscribers) { + std::vector subscriberData; + for (auto init : subscribers) { std::lock_guard 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 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; diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index ffe1797ce5..aab5bf8346 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -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; diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h index c9c2e1effc..d0e622cd12 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.h +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 0d2c42f66c..6f371e5b85 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -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; diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h index 3667979e96..99dc53adaa 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 78ee607fdf..6d2f76d746 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -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(p); -} - static DebugInterface *CPUFromRequest(DebuggerRequest &req) { if (!req.HasParam("thread")) return currentDebugMIPS; diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.h b/Core/Debugger/WebSocket/DisasmSubscriber.h index e6c5b0e71c..35a32fa2bb 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.h +++ b/Core/Debugger/WebSocket/DisasmSubscriber.h @@ -19,5 +19,4 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketDisasmInit(DebuggerEventHandlerMap &map); -void WebSocketDisasmShutdown(void *p); +DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map); diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp index 98f65231c2..67b13d85b2 100644 --- a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp @@ -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; diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.h b/Core/Debugger/WebSocket/GPUBufferSubscriber.h index c7062fdb82..00b93d74c0 100644 --- a/Core/Debugger/WebSocket/GPUBufferSubscriber.h +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp new file mode 100644 index 0000000000..961a4d0d04 --- /dev/null +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp @@ -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 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(); + } +} diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.h b/Core/Debugger/WebSocket/GPURecordSubscriber.h new file mode 100644 index 0000000000..31a1f6b42d --- /dev/null +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp index a17717e262..0acd4a9b3e 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.cpp +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -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; diff --git a/Core/Debugger/WebSocket/GameSubscriber.h b/Core/Debugger/WebSocket/GameSubscriber.h index 77cec9e38b..e3d08f4522 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.h +++ b/Core/Debugger/WebSocket/GameSubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/HLESubscriber.cpp b/Core/Debugger/WebSocket/HLESubscriber.cpp index 586f30199c..ae65e692fa 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.cpp +++ b/Core/Debugger/WebSocket/HLESubscriber.cpp @@ -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; diff --git a/Core/Debugger/WebSocket/HLESubscriber.h b/Core/Debugger/WebSocket/HLESubscriber.h index 9b059f4759..6d473c3563 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.h +++ b/Core/Debugger/WebSocket/HLESubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index e1347f3a14..d3422b48f9 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -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(p); -} - static DebugInterface *CPUFromRequest(DebuggerRequest &req, uint32_t *threadID = nullptr) { if (!req.HasParam("thread")) { if (threadID) diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.h b/Core/Debugger/WebSocket/SteppingSubscriber.h index 319c608caa..06983ea278 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.h +++ b/Core/Debugger/WebSocket/SteppingSubscriber.h @@ -19,5 +19,4 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketSteppingInit(DebuggerEventHandlerMap &map); -void WebSocketSteppingShutdown(void *p); +DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 70c08828b2..85dca3f4a8 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -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 DebuggerEventHandler; typedef std::unordered_map DebuggerEventHandlerMap; diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index e47f51dcf7..09b32dcc01 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -362,13 +362,13 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) { } } -void PSPSaveDialog::DisplaySaveIcon() +void PSPSaveDialog::DisplaySaveIcon(bool checkExists) { std::lock_guard 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", diff --git a/Core/Dialog/PSPSaveDialog.h b/Core/Dialog/PSPSaveDialog.h index c0d42e4955..888cc63d6e 100644 --- a/Core/Dialog/PSPSaveDialog.h +++ b/Core/Dialog/PSPSaveDialog.h @@ -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); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index f84d180ea5..f557b22a6d 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -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; } diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index ae309d7b2e..e6bd1e0680 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -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::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) { diff --git a/Core/Host.h b/Core/Host.h index b06473c4dc..2fd79e9510 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -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;} diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 6177f4a961..5613cc9258 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -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 guard(mutex); rewindStates.Clear(); diff --git a/Core/System.cpp b/Core/System.cpp index bd55510b06..5bc2208034 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -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); diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index fdb9254ec4..cc9b4788be 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -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 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 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"); } } diff --git a/Core/Util/AudioFormat.cpp b/Core/Util/AudioFormat.cpp index 2424b405d5..54610949f3 100644 --- a/Core/Util/AudioFormat.cpp +++ b/Core/Util/AudioFormat.cpp @@ -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; } diff --git a/Core/Util/AudioFormat.h b/Core/Util/AudioFormat.h index 7fc20eaf83..eb5098ad01 100644 --- a/Core/Util/AudioFormat.h +++ b/Core/Util/AudioFormat.h @@ -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; diff --git a/Core/Util/AudioFormatNEON.cpp b/Core/Util/AudioFormatNEON.cpp index ec73e93d2b..9ef6ae21b2 100644 --- a/Core/Util/AudioFormatNEON.cpp +++ b/Core/Util/AudioFormatNEON.cpp @@ -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 diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index b1fe68058b..1bf2ee344c 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -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); } } diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 116c84c1a8..ea2df36d89 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -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 diff --git a/GPU/Common/TextureDecoder.h b/GPU/Common/TextureDecoder.h index 428d148a0b..30054168bd 100644 --- a/GPU/Common/TextureDecoder.h +++ b/GPU/Common/TextureDecoder.h @@ -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 diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index fb7b001b7b..eac5cf078f 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -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) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index f2910cbec6..bb6ee6b660 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -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) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index d73b772b62..33f9bdbf36 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -107,6 +107,9 @@ private: ID3D11Buffer *stencilValueBuffer_ = nullptr; ID3D11DepthStencilState *stencilMaskStates_[256]{}; + ID3D11Texture2D *nullTexture_ = nullptr; + ID3D11ShaderResourceView *nullTextureView_ = nullptr; + TextureCacheD3D11 *textureCacheD3D11_; ShaderManagerD3D11 *shaderManagerD3D11_; DrawEngineD3D11 *drawEngineD3D11_; diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 8ad9ebd11a..a23eb2059f 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -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); } diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 44da7d3751..5c2259a183 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -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) { diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp new file mode 100644 index 0000000000..2f00c804d1 --- /dev/null +++ b/GPU/Debugger/Debugger.cpp @@ -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; +} + +} diff --git a/GPU/Debugger/Debugger.h b/GPU/Debugger/Debugger.h new file mode 100644 index 0000000000..caf1e8f37f --- /dev/null +++ b/GPU/Debugger/Debugger.h @@ -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); + +} diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index fb130ef34a..1fa237a600 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -48,6 +48,7 @@ static const int VERSION = 2; static bool active = false; static bool nextFrame = false; static bool writePending = false; +static std::function 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 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; diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index 3cfc995a80..230b361d19 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -17,13 +17,17 @@ #pragma once +#include #include #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 callback); void NotifyCommand(u32 pc); void NotifyMemcpy(u32 dest, u32 src, u32 sz); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index 644d833750..b277496556 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -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 callback) { +bool EnterStepping() { std::unique_lock guard(pauseLock); if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) { // Shutting down, don't try to step. @@ -170,8 +171,7 @@ bool EnterStepping(std::function 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; diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index fb24cf072a..85b8a1082e 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -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 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); diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index de1a85d093..e4f24703ee 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -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) diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index a66faf39d1..88bddb3bee 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -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) { diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 4c1498b786..5a67b34bf6 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -104,6 +104,8 @@ private: LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr; bool stencilUploadFailed_ = false; + LPDIRECT3DTEXTURE9 nullTex_ = nullptr; + TextureCacheDX9 *textureCacheDX9_; ShaderManagerDX9 *shaderManagerDX9_; DrawEngineDX9 *drawEngineD3D9_; diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index afd5a86d10..154a5e44d6 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -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); } diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index 609f89a792..d36c6bd5f0 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -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 { diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 077e80299b..40764cd7a1 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -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); diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index cfd10ee0f1..d9743fc461 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -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 { diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 81b2498346..6ddf06b3bd 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -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) { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 5eca0e9296..2a75ead250 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -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); } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 02bb86fb42..62dad30b5f 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -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); } } diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 296a2081b0..b531442bb8 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -217,6 +217,7 @@ + @@ -320,6 +321,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 7dc2f25cad..1710c3f790 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -273,6 +273,9 @@ Vulkan + + Debugger + @@ -542,5 +545,8 @@ Vulkan + + Debugger + \ No newline at end of file diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 5489559935..13db7056e8 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -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; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 219058c362..2a5f2aa308 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -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) { diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 381a40403a..2673ef4ab2 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -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. diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 05f1853119..1d9eef5a13 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -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. diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 11f8d376f4..2e26dfc708 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -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) { diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 9665019ffa..7851238090 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -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 }); } } diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 69cd57feb6..18aaa60b2e 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -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); } diff --git a/Qt/QtHost.h b/Qt/QtHost.h index fbad0aa7ea..846da1058f 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -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; diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 85b49825a1..14afdd040c 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -19,6 +19,7 @@ SDLJoystick *joystick = NULL; #include #include #include +#include #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: diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 476882fbd2..6ddda7c59a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -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) { diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index c31b5d8604..b5eb4401b2 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -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 GameBrowser::GetPinnedPaths() { @@ -672,7 +673,7 @@ const std::vector 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 paths = g_Config.vPinnedPaths; std::vector results; for (size_t i = 0; i < paths.size(); ++i) { diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 4dc296d49d..8f7d0af642 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -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)) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 0dbc02e4f8..1e329b2ffe 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -29,13 +29,10 @@ // in NativeShutdown. #include -// Linux doesn't like using std::find with std::vector without this :/ -#if !defined(MOBILE_DEVICE) #include -#endif #include -#include #include +#include #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) { diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 9aabb3a2f7..a7c27be645 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -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: diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 0d1bc445ea..f3cfa762bf 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -332,6 +332,7 @@ + @@ -390,6 +391,7 @@ + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index 641d4feef0..6108deb70b 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -150,13 +150,18 @@ Debugger + + Debugger + Debugger Common - + + Software + Debugger @@ -291,10 +296,15 @@ Debugger + + Debugger + Debugger - + + Software + Debugger diff --git a/UWP/UWPHost.h b/UWP/UWPHost.h index 635e37bccc..76f460a700 100644 --- a/UWP/UWPHost.h +++ b/UWP/UWPHost.h @@ -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; diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 987bb8b5e4..5a81b5ceb8 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -132,6 +132,18 @@ void MainThreadFunc() { bool performingRestart = NativeIsRestarting(); NativeInit(static_cast(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(); diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index c0454d30e1..1d35c70612 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -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 @@ -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) { -} diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index 0c95f0a7ba..29e19071df 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -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]); diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 806458b1e8..1689b51d6c 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -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(); diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index f3808583b0..527860d2ee 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -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(); diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index e7057f6f41..0d78975928 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -345,6 +345,18 @@ true true + + true + true + true + true + + + true + true + true + true + @@ -439,6 +451,18 @@ + + true + true + true + true + + + true + true + true + true + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 80851fdc34..59a100aa64 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -182,6 +182,12 @@ Other Platforms + + Other Platforms\Qt + + + Other Platforms + @@ -335,6 +341,12 @@ Other Platforms + + Other Platforms\Qt + + + Other Platforms + diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h index f05aeca316..ed3ffeefbd 100644 --- a/Windows/WindowsHost.h +++ b/Windows/WindowsHost.h @@ -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; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 171ca31310..1ed3b4eb69 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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 \ diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index 69d411e32d..7ecdeb9a7f 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -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); diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 01b8c6c570..eca5dc4869 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -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; diff --git a/ext/native/math/fast/fast_math.c b/ext/native/math/fast/fast_math.c index 67506a1823..5363a32830 100644 --- a/ext/native/math/fast/fast_math.c +++ b/ext/native/math/fast/fast_math.c @@ -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 } diff --git a/ext/native/math/fast/fast_matrix.h b/ext/native/math/fast/fast_matrix.h index 676ab1acea..41200404f6 100644 --- a/ext/native/math/fast/fast_matrix.h +++ b/ext/native/math/fast/fast_matrix.h @@ -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 diff --git a/ext/native/math/fast/fast_matrix_neon.S b/ext/native/math/fast/fast_matrix_neon.S index 813b788471..746115314c 100644 --- a/ext/native/math/fast/fast_matrix_neon.S +++ b/ext/native/math/fast/fast_matrix_neon.S @@ -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 diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index d3ad94095e..c8380d012f 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -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_) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 812943b571..a01482b69e 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -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); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 8f6906d839..43e48283ea 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -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_); } diff --git a/libretro/Makefile.common b/libretro/Makefile.common index fd6e262e35..13475121f5 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -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 \