New Atlas

Merge branch 'master' into TemporaryHackishBranch

# Conflicts:
#	UI/MainScreen.cpp
#	android/assets/ui_atlas.zim
#	android/assets/ui_atlas_luna.meta
#	assets/ui_atlas.zim
#	assets/ui_atlas_luna.meta
This commit is contained in:
LunaMoo committed 2021-01-20 01:52:27 +01:00
commit 4b6855cd6e
134 files changed
+2120 -499

No files matched your search

+2 -2
View File
@@ -9,6 +9,6 @@ You can remove this stuff - just don't forget to answer the questions. They're
### What *should* happen?
### What hardware, operating system, and PPSSPP version? On desktop, GPU matters for graphical issues.
### What hardware, operating system, and PPSSPP version? On desktop and mobile phone, GPU matters for graphical issues.
### Please test newest version in https://buildbot.orphis.net/ppsspp/
+5 -4
View File
@@ -121,22 +121,22 @@ jobs:
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a UNITTEST=1 HEADLESS=1
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=armeabi-v7a
args: cd android && ./ab.sh -j2 APP_ABI=armeabi-v7a UNITTEST=1 HEADLESS=1
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=x86
args: cd android && ./ab.sh -j2 APP_ABI=x86 UNITTEST=1 HEADLESS=1
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=x86_64
args: cd android && ./ab.sh -j2 APP_ABI=x86_64 UNITTEST=1 HEADLESS=1
- os: ubuntu-latest
extra: android
cc: clang
@@ -215,6 +215,7 @@ jobs:
cp -r build*/PPSSPPSDL.app ppsspp/
elif [ -e build*/PPSSPPSDL ]; then
cp build*/PPSSPPSDL ppsspp/
cp -r assets ppsspp/assets
fi
if [ -e build*/$BUILD_CONFIGURATION/PPSSPPHeadless ]; then
cp build*/$BUILD_CONFIGURATION/PPSSPPHeadless ppsspp/
+18 -14
View File
@@ -91,7 +91,7 @@ if(ANDROID OR WIN32 OR (UNIX AND NOT ARM_NO_VULKAN))
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
if(NOT (ARM64 AND MACOSX))
if(NOT ((ARM64 AND MACOSX) OR IOS))
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/sdl)
endif()
@@ -198,7 +198,7 @@ if(USING_EGL)
set(OPENGL_LIBRARIES ${OPENGL_LIBRARIES} ${EGL_LIBRARIES})
endif()
if(NOT LIBRETRO)
if(NOT LIBRETRO AND NOT IOS)
find_package(SDL2)
endif()
include(FindThreads)
@@ -281,11 +281,11 @@ if(NOT MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -D_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -Os -D_NDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -D_NDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -D_NDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O2 -g -D_NDEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -D_DEBUG")
set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -Os -D_NDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -D_NDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -D_NDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O2 -g -D_NDEBUG")
#TODO: Remove this and include the file properly everywhere it makes sense
# First step is too use the macros everywhere
@@ -767,15 +767,15 @@ include_directories(ext/cityhash)
if(NOT MSVC)
# These can be fast even for debug.
set_target_properties(udis86 PROPERTIES COMPILE_FLAGS "-O3")
set_target_properties(cityhash PROPERTIES COMPILE_FLAGS "-O3")
set_target_properties(udis86 PROPERTIES COMPILE_FLAGS "-O2")
set_target_properties(cityhash PROPERTIES COMPILE_FLAGS "-O2")
if(NOT ZLIB_FOUND)
set_target_properties(zlib PROPERTIES COMPILE_FLAGS "-O3")
set_target_properties(zlib PROPERTIES COMPILE_FLAGS "-O2")
endif()
endif()
find_package(LibZip)
find_package(LIBZIP)
if(LIBZIP_FOUND AND USE_SYSTEM_LIBZIP)
add_definitions(-DSHARED_LIBZIP)
else()
@@ -1037,7 +1037,7 @@ if(WIN32)
target_link_libraries(Common winmm d3d9 dsound)
endif()
if(TARGET SDL2::SDL2)
if(TARGET SDL2::SDL2 AND NOT IOS)
target_link_libraries(Common SDL2::SDL2)
endif()
@@ -1492,6 +1492,10 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/GPURecordSubscriber.h
Core/Debugger/WebSocket/HLESubscriber.cpp
Core/Debugger/WebSocket/HLESubscriber.h
Core/Debugger/WebSocket/InputBroadcaster.cpp
Core/Debugger/WebSocket/InputBroadcaster.h
Core/Debugger/WebSocket/InputSubscriber.cpp
Core/Debugger/WebSocket/InputSubscriber.h
Core/Debugger/WebSocket/LogBroadcaster.cpp
Core/Debugger/WebSocket/LogBroadcaster.h
Core/Debugger/WebSocket/MemorySubscriber.cpp
@@ -2168,11 +2172,12 @@ if(IOS)
set_source_files_properties(${RSRC_XIB_FILES}
PROPERTIES MACOSX_PACKAGE_LOCATION Resources
)
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(APP_DIR_NAME "$(TARGET_BUILD_DIR)/$(FULL_PRODUCT_NAME)")
else()
#This breaks in modern XCode. Not sure when it worked...
#if(CMAKE_GENERATOR STREQUAL "Xcode")
# set(APP_DIR_NAME "$(TARGET_BUILD_DIR)/$(FULL_PRODUCT_NAME)")
#else()
set(APP_DIR_NAME "$<TARGET_FILE_DIR:PPSSPP>")
endif()
#endif()
add_custom_command(TARGET PPSSPP POST_BUILD
COMMAND mkdir -p \"${APP_DIR_NAME}\"
COMMAND tar -c -C ${CMAKE_CURRENT_BINARY_DIR} --exclude .DS_Store --exclude .git assets *.png | tar -x -C \"${APP_DIR_NAME}\"
@@ -2185,7 +2190,6 @@ if(IOS)
RESOURCE "ios/Settings.bundle"
RESOURCE "MoltenVK/iOS/Frameworks"
XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET}
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "iPhone/iPad"
XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
XCODE_ATTRIBUTE_ENABLE_BITCODE NO
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-"
+1 -1
View File
@@ -273,7 +273,7 @@ bool PathBrowser::CanNavigateUp() {
}
#endif
*/
if (path_ == "/") {
if (path_ == "/" || path_ == "") {
return false;
}
return true;
+2
View File
@@ -594,6 +594,8 @@ std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage) {
if (!gl_extensions.IsGLES && gl_extensions.IsCoreContext) {
// We need to add a corresponding #version. Apple drivers fail without an exact match.
version = StringFromFormat("#version %d\n", gl_extensions.GLSLVersion());
} else if (gl_extensions.IsGLES && gl_extensions.GLES3) {
version = StringFromFormat("#version %d es\n", gl_extensions.GLSLVersion());
}
if (stage == GL_FRAGMENT_SHADER) {
temp = version + glsl_fragment_prelude + source;
+10
View File
@@ -17,6 +17,16 @@ const char *ShaderLanguageAsString(ShaderLanguage lang) {
}
}
const char *ShaderStageAsString(ShaderStage stage) {
switch (stage) {
case ShaderStage::Fragment: return "Fragment";
case ShaderStage::Vertex: return "Vertex";
case ShaderStage::Geometry: return "Geometry";
case ShaderStage::Compute: return "Compute";
default: return "(unknown)";
}
}
ShaderLanguageDesc::ShaderLanguageDesc(ShaderLanguage lang) {
Init(lang);
}
+3
View File
@@ -28,6 +28,9 @@ enum class ShaderStage {
Compute,
};
const char *ShaderStageAsString(ShaderStage lang);
struct ShaderLanguageDesc {
ShaderLanguageDesc() {}
explicit ShaderLanguageDesc(ShaderLanguage lang);
+7 -6
View File
@@ -53,7 +53,7 @@
extern void init_resources(TBuiltInResource &Resources);
static EShLanguage GetLanguage(const ShaderStage stage) {
static EShLanguage GetShLanguageFromStage(const ShaderStage stage) {
switch (stage) {
case ShaderStage::Vertex: return EShLangVertex;
case ShaderStage::Geometry: return EShLangGeometry;
@@ -234,15 +234,16 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
*errorMessage = "";
glslang::TProgram program;
const char *shaderStrings[1];
const char *shaderStrings[1]{};
TBuiltInResource Resources;
TBuiltInResource Resources{};
init_resources(Resources);
// Don't enable SPIR-V and Vulkan rules when parsing GLSL. Our postshaders are written in oldschool GLES 2.0.
EShMessages messages = EShMessages::EShMsgDefault;
EShLanguage shaderStage = GetLanguage(stage);
EShLanguage shaderStage = GetShLanguageFromStage(stage);
glslang::TShader shader(shaderStage);
shaderStrings[0] = src.c_str();
@@ -250,7 +251,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
// TODO: Should set settings here based on srcLang.
if (!shader.parse(&Resources, 100, EProfile::ECompatibilityProfile, false, false, messages)) {
*errorMessage = std::string("GLSL parser failure: ") + shader.getInfoLog() + shader.getInfoDebugLog();
*errorMessage = StringFromFormat("%s parser failure: %s\n%s", ShaderStageAsString(stage), shader.getInfoLog(), shader.getInfoDebugLog());
return false; // something didn't work
}
@@ -258,7 +259,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
program.addShader(&shader);
if (!program.link(messages)) {
*errorMessage = std::string("Linker failure: ") + shader.getInfoLog() + shader.getInfoDebugLog();
*errorMessage = StringFromFormat("%s linker failure: %s\n%s", ShaderStageAsString(stage), shader.getInfoLog(), shader.getInfoDebugLog());
return false;
}
+2 -2
View File
@@ -346,8 +346,8 @@ void VulkanRenderManager::DestroyBackbuffers() {
StopThread();
vulkan_->WaitUntilQueueIdle();
for (uint32_t i = 0; i < swapchainImageCount_; i++) {
vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view);
for (auto &image : swapchainImages_) {
vulkan_->Delete().QueueDeleteImageView(image.view);
}
swapchainImages_.clear();
+5
View File
@@ -4,13 +4,18 @@
#include "Common/StringUtils.h"
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
#define closesocket close
#else
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
-13
View File
@@ -7,19 +7,6 @@
#include "Common/Net/Resolve.h"
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
#include "Common/Buffer.h"
namespace net {
+1 -1
View File
@@ -126,7 +126,7 @@ std::string UriDecode(const std::string & sSrc)
const unsigned char * const SRC_END = pSrc + SRC_LEN;
const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
char * const pStart = new char[SRC_LEN];
char * const pStart = new char[SRC_LEN]; // Output will be shorter.
char * pEnd = pStart;
while (pSrc < SRC_LAST_DEC)
+12
View File
@@ -236,6 +236,18 @@ void DrawBuffer::DrawImageStretch(ImageID atlas_image, float x1, float y1, float
V(x1, y2, color, image->u1, image->v2);
}
void DrawBuffer::DrawImageStretchVGradient(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color1, Color color2) {
const AtlasImage *image = atlas->getImage(atlas_image);
if (!image)
return;
V(x1, y1, color1, image->u1, image->v1);
V(x2, y1, color1, image->u2, image->v1);
V(x2, y2, color2, image->u2, image->v2);
V(x1, y1, color1, image->u1, image->v1);
V(x2, y2, color2, image->u2, image->v2);
V(x1, y2, color2, image->u1, image->v2);
}
inline void rot(float *v, float angle, float xc, float yc) {
const float x = v[0] - xc;
const float y = v[1] - yc;
+1
View File
@@ -108,6 +108,7 @@ public:
bool MeasureImage(ImageID atlas_image, float *w, float *h);
void DrawImage(ImageID atlas_image, float x, float y, float scale, Color color = COLOR(0xFFFFFF), int align = ALIGN_TOPLEFT);
void DrawImageStretch(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF));
void DrawImageStretchVGradient(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color1, Color color2);
void DrawImageStretch(ImageID atlas_image, const Bounds &bounds, Color color = COLOR(0xFFFFFF)) {
DrawImageStretch(atlas_image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), color);
}
+7
View File
@@ -55,6 +55,12 @@ enum SystemProperty {
SYSPROP_CLIPBOARD_TEXT,
SYSPROP_GPUDRIVER_VERSION,
// Separate SD cards or similar.
// Need hacky solutions to get at this.
SYSPROP_HAS_ADDITIONAL_STORAGE,
SYSPROP_ADDITIONAL_STORAGE_DIRS,
SYSPROP_TEMP_DIRS,
SYSPROP_HAS_FILE_BROWSER,
SYSPROP_HAS_FOLDER_BROWSER,
SYSPROP_HAS_IMAGE_BROWSER,
@@ -95,6 +101,7 @@ enum SystemProperty {
};
std::string System_GetProperty(SystemProperty prop);
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop);
int System_GetPropertyInt(SystemProperty prop);
float System_GetPropertyFloat(SystemProperty prop);
bool System_GetPropertyBool(SystemProperty prop);
+5
View File
@@ -55,6 +55,7 @@ void UIContext::BeginNoTex() {
}
void UIContext::BeginPipeline(Draw::Pipeline *pipeline, Draw::SamplerState *samplerState) {
_assert_(pipeline != nullptr);
draw_->BindSamplerStates(0, 1, &samplerState);
RebindTexture();
UIBegin(pipeline);
@@ -236,6 +237,10 @@ void UIContext::FillRect(const UI::Drawable &drawable, const Bounds &bounds) {
}
}
void UIContext::DrawImageVGradient(ImageID image, uint32_t color1, uint32_t color2, const Bounds &bounds) {
uidrawbuffer_->DrawImageStretchVGradient(image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), color1, color2);
}
void UIContext::PushTransform(const UITransform &transform) {
Flush();
+1
View File
@@ -85,6 +85,7 @@ public:
void DrawTextShadow(const char *str, float x, float y, uint32_t color, int align = 0);
void DrawTextRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0);
void FillRect(const UI::Drawable &drawable, const Bounds &bounds);
void DrawImageVGradient(ImageID image, uint32_t color1, uint32_t color2, const Bounds &bounds);
// in dps, like dp_xres and dp_yres
void SetBounds(const Bounds &b) { bounds_ = b; }
+4
View File
@@ -803,6 +803,10 @@ void TextView::Draw(UIContext &dc) {
dc.DrawTextRect(text_.c_str(), bounds_.Offset(1.0f, 1.0f), shadowColor, textAlign_);
}
dc.DrawTextRect(text_.c_str(), bounds_, textColor, textAlign_);
if (small_) {
// If we changed font style, reset it.
dc.SetFontStyle(dc.theme->uiFont);
}
if (clip) {
dc.PopScissor();
}
+3
View File
@@ -527,6 +527,9 @@ public:
paddingW_ = w;
paddingH_ = h;
}
void SetImageID(ImageID imageID) {
imageID_ = imageID;
}
// Needed an extra small button...
void SetScale(float f) {
+4
View File
@@ -437,6 +437,8 @@
<ClCompile Include="Debugger\WebSocket\GPUBufferSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\HLESubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\InputBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\InputSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\MemorySubscriber.cpp" />
@@ -981,6 +983,8 @@
<ClInclude Include="Debugger\WebSocket\GPUBufferSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\HLESubscriber.h" />
<ClInclude Include="Debugger\WebSocket\InputBroadcaster.h" />
<ClInclude Include="Debugger\WebSocket\InputSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />
<ClInclude Include="Debugger\WebSocket\CPUCoreSubscriber.h" />
+12
View File
@@ -965,6 +965,12 @@
<ClCompile Include="ThreadPools.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\InputSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\InputBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@@ -1649,6 +1655,12 @@
<ClInclude Include="ThreadPools.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\InputSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\InputBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
+6 -1
View File
@@ -45,6 +45,7 @@
// For other events, look inside Core/Debugger/WebSocket/ for details on each event.
#include "Core/Debugger/WebSocket/GameBroadcaster.h"
#include "Core/Debugger/WebSocket/InputBroadcaster.h"
#include "Core/Debugger/WebSocket/LogBroadcaster.h"
#include "Core/Debugger/WebSocket/SteppingBroadcaster.h"
@@ -55,6 +56,7 @@
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
#include "Core/Debugger/WebSocket/HLESubscriber.h"
#include "Core/Debugger/WebSocket/InputSubscriber.h"
#include "Core/Debugger/WebSocket/MemorySubscriber.h"
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"
@@ -67,6 +69,7 @@ static const std::vector<SubscriberInit> subscribers({
&WebSocketGPUBufferInit,
&WebSocketGPURecordInit,
&WebSocketHLEInit,
&WebSocketInputInit,
&WebSocketMemoryInit,
&WebSocketSteppingInit,
});
@@ -125,8 +128,9 @@ void HandleDebuggerRequest(const http::Request &request) {
UpdateConnected(1);
SetupDebuggerLock();
LogBroadcaster logger;
GameBroadcaster game;
LogBroadcaster logger;
InputBroadcaster input;
SteppingBroadcaster stepping;
std::unordered_map<std::string, DebuggerEventHandler> eventHandlers;
@@ -175,6 +179,7 @@ void HandleDebuggerRequest(const http::Request &request) {
logger.Broadcast(ws);
game.Broadcast(ws);
stepping.Broadcast(ws);
input.Broadcast(ws);
for (size_t i = 0; i < subscribers.size(); ++i) {
if (subscriberData[i]) {
@@ -0,0 +1,102 @@
// 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 <unordered_map>
#include "Core/Debugger/WebSocket/InputBroadcaster.h"
#include "Core/Debugger/WebSocket/InputSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/HLE/sceDisplay.h"
// Button press state change (input.buttons)
//
// Sent unexpectedly with these properties:
// - buttons: an object with button names as keys and bool press state as values.
// - changed: same as buttons, but only including changed states.
//
// See input.buttons.send in InputSubscriber for button names.
// Analog position change (input.analog)
//
// Sent unexpectedly with these properties:
// - stick: "left" or "right".
// - x: number between -1.0 and 1.0, representing horizontal position in a square.
// - y: number between -1.0 and 1.0, representing vertical position in a square.
std::string InputBroadcaster::Analog::Event(const char *stick) {
JsonWriter j;
j.begin();
j.writeString("event", "input.analog");
j.writeString("stick", stick);
j.writeFloat("x", x);
j.writeFloat("y", y);
j.end();
return j.str();
}
static std::string ButtonsEvent(uint32_t lastButtons, uint32_t newButtons) {
uint32_t pressed = newButtons & ~lastButtons;
uint32_t released = ~newButtons & lastButtons;
JsonWriter j;
j.begin();
j.writeString("event", "input.buttons");
j.pushDict("buttons");
for (auto it : WebSocketInputButtonLookup()) {
j.writeBool(it.first, (newButtons & it.second) != 0);
}
j.pop();
j.pushDict("changed");
for (auto it : WebSocketInputButtonLookup()) {
if (pressed & it.second) {
j.writeBool(it.first, true);
} else if (released & it.second) {
j.writeBool(it.first, false);
}
}
j.pop();
j.end();
return j.str();
}
void InputBroadcaster::Broadcast(net::WebSocketServer *ws) {
int counter = __DisplayGetNumVblanks();
if (lastCounter_ == counter)
return;
lastCounter_ = counter;
uint32_t newButtons = __CtrlPeekButtons();
if (newButtons != lastButtons_) {
ws->Send(ButtonsEvent(lastButtons_, newButtons));
lastButtons_ = newButtons;
}
Analog newAnalog;
__CtrlPeekAnalog(CTRL_STICK_LEFT, &newAnalog.x, &newAnalog.y);
if (!lastAnalog_[0].Equals(newAnalog)) {
ws->Send(newAnalog.Event("left"));
lastAnalog_[0].x = newAnalog.x;
lastAnalog_[0].y = newAnalog.y;
}
__CtrlPeekAnalog(CTRL_STICK_RIGHT, &newAnalog.x, &newAnalog.y);
if (!lastAnalog_[1].Equals(newAnalog)) {
ws->Send(newAnalog.Event("right"));
lastAnalog_[1].x = newAnalog.x;
lastAnalog_[1].y = newAnalog.y;
}
}
@@ -0,0 +1,48 @@
// 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 <cstdint>
#include <string>
namespace net {
class WebSocketServer;
}
struct InputBroadcaster {
public:
InputBroadcaster() {
}
void Broadcast(net::WebSocketServer *ws);
private:
struct Analog {
float x = 0.0f;
float y = 0.0f;
bool Equals(const Analog &other) const {
return x == other.x && y == other.y;
}
std::string Event(const char *stick);
};
int lastCounter_ = -1;
uint32_t lastButtons_ = 0;
Analog lastAnalog_[2];
};
+261
View File
@@ -0,0 +1,261 @@
// Copyright (c) 2021- 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 <algorithm>
#include <unordered_map>
#include "Common/StringUtils.h"
#include "Core/Debugger/WebSocket/InputSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/HLE/sceDisplay.h"
// This is also used in InputBroadcaster.
const std::unordered_map<std::string, uint32_t> buttonLookup = {
{ "cross", CTRL_CROSS },
{ "circle", CTRL_CIRCLE },
{ "triangle", CTRL_TRIANGLE },
{ "square", CTRL_SQUARE },
{ "up", CTRL_UP },
{ "down", CTRL_DOWN },
{ "left", CTRL_LEFT },
{ "right", CTRL_RIGHT },
{ "start", CTRL_START },
{ "select", CTRL_SELECT },
{ "home", CTRL_HOME },
{ "screen", CTRL_SCREEN },
{ "note", CTRL_NOTE },
{ "ltrigger", CTRL_LTRIGGER },
{ "rtrigger", CTRL_RTRIGGER },
{ "hold", CTRL_HOLD },
{ "wlan", CTRL_WLAN },
{ "remote_hold", CTRL_REMOTE_HOLD },
{ "vol_up", CTRL_VOL_UP },
{ "vol_down", CTRL_VOL_DOWN },
{ "disc", CTRL_DISC },
{ "memstick", CTRL_MEMSTICK },
{ "forward", CTRL_FORWARD },
{ "back", CTRL_BACK },
{ "playpause", CTRL_PLAYPAUSE },
};
struct WebSocketInputState : public DebuggerSubscriber {
void ButtonsSend(DebuggerRequest &req);
void ButtonsPress(DebuggerRequest &req);
void AnalogSend(DebuggerRequest &req);
void Broadcast(net::WebSocketServer *ws) override;
protected:
struct PressInfo {
std::string ticket;
uint32_t button;
uint32_t duration;
std::string Event();
};
std::vector<PressInfo> pressTickets_;
int lastCounter_ = -1;
};
std::string WebSocketInputState::PressInfo::Event() {
JsonWriter j;
j.begin();
j.writeString("event", "input.buttons.press");
if (!ticket.empty()) {
j.writeRaw("ticket", ticket);
}
j.end();
return j.str();
}
const std::unordered_map<std::string, uint32_t> &WebSocketInputButtonLookup() {
return buttonLookup;
}
DebuggerSubscriber *WebSocketInputInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketInputState();
map["input.buttons.send"] = std::bind(&WebSocketInputState::ButtonsSend, p, std::placeholders::_1);
map["input.buttons.press"] = std::bind(&WebSocketInputState::ButtonsPress, p, std::placeholders::_1);
map["input.analog.send"] = std::bind(&WebSocketInputState::AnalogSend, p, std::placeholders::_1);
return p;
}
// Alter PSP button press flags (input.buttons.send)
//
// Parameters:
// - buttons: object containing button names as string keys, boolean press state as value.
//
// Button names (some are not respected by PPSSPP):
// - cross: button on bottom side of right pad.
// - circle: button on right side of right pad.
// - triangle: button on top side of right pad.
// - square: button on left side of right pad.
// - up: d-pad up button.
// - down: d-pad down button.
// - left: d-pad left button.
// - right: d-pad right button.
// - start: rightmost button at bottom of device.
// - select: second to the right at bottom of device.
// - home: leftmost button at bottom of device.
// - screen: brightness control button at bottom of device.
// - note: mute control button at bottom of device.
// - ltrigger: left shoulder trigger button.
// - rtrigger: right shoulder trigger button.
// - hold: hold setting of power switch.
// - wlan: wireless networking switch.
// - remote_hold: hold switch on headset.
// - vol_up: volume up button next to home at bottom of device.
// - vol_down: volume down button next to home at bottom of device.
// - disc: UMD disc sensor.
// - memstick: memory stick sensor.
// - forward: forward button on headset.
// - back: back button on headset.
// - playpause: play/pause button on headset.
//
// Empty response.
void WebSocketInputState::ButtonsSend(DebuggerRequest &req) {
const JsonNode *jsonButtons = req.data.get("buttons");
if (!jsonButtons) {
return req.Fail("Missing 'buttons' parameter");
}
if (jsonButtons->value.getTag() != JSON_OBJECT) {
return req.Fail("Invalid 'buttons' parameter type");
}
uint32_t downFlags = 0;
uint32_t upFlags = 0;
for (const JsonNode *button : jsonButtons->value) {
auto info = buttonLookup.find(button->key);
if (info == buttonLookup.end()) {
return req.Fail(StringFromFormat("Unsupported 'buttons' object key '%s'", button->key));
}
if (button->value.getTag() == JSON_TRUE) {
downFlags |= info->second;
} else if (button->value.getTag() == JSON_FALSE) {
upFlags |= info->second;
} else if (button->value.getTag() != JSON_NULL) {
return req.Fail(StringFromFormat("Unsupported 'buttons' object type for key '%s'", button->key));
}
}
if (downFlags) {
__CtrlButtonDown(downFlags);
}
if (upFlags) {
__CtrlButtonUp(upFlags);
}
req.Respond();
}
// Press and release a button (input.buttons.press)
//
// Parameters:
// - button: required string indicating button name (see input.buttons.send.)
// - duration: optional integer indicating frames to press for, defaults to 1.
//
// Empty response once released.
void WebSocketInputState::ButtonsPress(DebuggerRequest &req) {
std::string button;
if (!req.ParamString("button", &button))
return;
PressInfo press;
press.duration = 1;
if (!req.ParamU32("duration", &press.duration, false, DebuggerParamType::OPTIONAL))
return;
if (press.duration < 0)
return req.Fail("Parameter 'duration' must not be negative");
const JsonNode *value = req.data.get("ticket");
press.ticket = value ? json_stringify(value) : "";
auto info = buttonLookup.find(button);
if (info == buttonLookup.end()) {
return req.Fail(StringFromFormat("Unsupported button value '%s'", button.c_str()));
}
press.button = info->second;
__CtrlButtonDown(press.button);
pressTickets_.push_back(press);
}
void WebSocketInputState::Broadcast(net::WebSocketServer *ws) {
int counter = __DisplayGetNumVblanks();
if (pressTickets_.empty() || lastCounter_ == counter)
return;
lastCounter_ = counter;
for (PressInfo &press : pressTickets_) {
press.duration--;
if (press.duration == -1) {
__CtrlButtonUp(press.button);
ws->Send(press.Event());
}
}
auto negative = [](const PressInfo &press) -> bool {
return press.duration < 0;
};
pressTickets_.erase(std::remove_if(pressTickets_.begin(), pressTickets_.end(), negative), pressTickets_.end());
}
static bool AnalogValue(DebuggerRequest &req, float *value, const char *name) {
const JsonNode *node = req.data.get(name);
if (!node) {
req.Fail(StringFromFormat("Missing '%s' parameter", name));
return false;
}
if (node->value.getTag() != JSON_NUMBER) {
req.Fail(StringFromFormat("Invalid '%s' parameter type", name));
return false;
}
double val = node->value.toNumber();
if (val < 1.0 || val > 1.0) {
req.Fail(StringFromFormat("Parameter '%s' must be between -1.0 and 1.0", name));
return false;
}
*value = (float)val;
return true;
}
// Set coordinates of analog stick (input.analog.send)
//
// Parameters:
// - x: required number from -1.0 to 1.0.
// - y: required number from -1.0 to 1.0.
// - stick: optional string, either "left" (default) or "right".
//
// Empty response.
void WebSocketInputState::AnalogSend(DebuggerRequest &req) {
std::string stick = "left";
if (!req.ParamString("stick", &stick, DebuggerParamType::OPTIONAL))
return;
if (stick != "left" && stick != "right")
return req.Fail(StringFromFormat("Parameter 'stick' must be 'left' or 'right', not '%s'", stick.c_str()));
float x, y;
if (!AnalogValue(req, &x, "x") || !AnalogValue(req, &y, "y"))
return;
__CtrlSetAnalogX(x, stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT);
__CtrlSetAnalogY(y, stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT);
req.Respond();
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2021- 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 <cstdint>
#include <unordered_map>
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
DebuggerSubscriber *WebSocketInputInit(DebuggerEventHandlerMap &map);
const std::unordered_map<std::string, uint32_t> &WebSocketInputButtonLookup();
+6 -6
View File
@@ -149,11 +149,11 @@ int PSPMsgDialog::Init(unsigned int paramAddr) {
void PSPMsgDialog::FormatErrorCode(uint32_t code) {
auto di = GetI18NCategory("Dialog");
auto err = GetI18NCategory("Dialog");
switch (code) {
case SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN:
snprintf(msgText, 512, "%s (%08x)", di->T("MsgErrorSavedataDataBroken", "Save data was corrupt."), code);
snprintf(msgText, 512, "%s (%08x)", err->T("MsgErrorSavedataDataBroken", "Save data was corrupt."), code);
break;
case SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_MS:
@@ -161,23 +161,23 @@ void PSPMsgDialog::FormatErrorCode(uint32_t code) {
case SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_MS:
case SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_MS:
case SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_MS:
snprintf(msgText, 512, "%s (%08x)", di->T("MsgErrorSavedataNoMS", "Memory stick not inserted."), code);
snprintf(msgText, 512, "%s (%08x)", err->T("MsgErrorSavedataNoMS", "Memory stick not inserted."), code);
break;
case SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA:
case SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA:
case SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA:
case SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA:
snprintf(msgText, 512, "%s (%08x)", di->T("MsgErrorSavedataNoData", "Warning: no save data was found."), code);
snprintf(msgText, 512, "%s (%08x)", err->T("MsgErrorSavedataNoData", "Warning: no save data was found."), code);
break;
case SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL:
case SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE:
snprintf(msgText, 512, "%s (%08x)", di->T("MsgErrorSavedataMSFull", "Memory stick full. Check your storage space."), code);
snprintf(msgText, 512, "%s (%08x)", err->T("MsgErrorSavedataMSFull", "Memory stick full. Check your storage space."), code);
break;
default:
snprintf(msgText, 512, "%s %08x", di->T("MsgErrorCode", "Error code:"), code);
snprintf(msgText, 512, "%s %08x", err->T("MsgErrorCode", "Error code:"), code);
}
}
+1 -1
View File
@@ -347,7 +347,7 @@ int PSPNetconfDialog::Update(int animSpeed) {
if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC)
channel = "Automatic";
DisplayMessage(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), di->T("Channel") + std::string(" ") + di->T(channel));
DisplayMessage(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), di->T("Channel:") + std::string(" ") + di->T(channel));
// Only Join mode is showing Cancel button on KHBBS and the button will fade out before the dialog is fading out, probably because it's already connected thus can't be canceled anymore
if (request.netAction == NETCONF_JOIN_ADHOC)
+8 -8
View File
@@ -1485,19 +1485,20 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param)
}
const std::string thisSaveName = FixedToString(saveNameListData[i], ARRAY_SIZE(saveNameListData[i]));
DEBUG_LOG(SCEUTILITY, "Name : %s", thisSaveName.c_str());
std::string fileDataDir = savePath + GetGameName(param) + thisSaveName;
PSPFileInfo info = GetSaveInfo(fileDataDir);
if (info.exists) {
SetFileInfo(realCount, info, thisSaveName);
DEBUG_LOG(SCEUTILITY,"%s Exist",fileDataDir.c_str());
DEBUG_LOG(SCEUTILITY, "Save data exists: %s = %s", thisSaveName.c_str(), fileDataDir.c_str());
realCount++;
} else {
if (listEmptyFile) {
ClearFileInfo(saveDataList[realCount], thisSaveName);
DEBUG_LOG(SCEUTILITY,"Don't Exist");
DEBUG_LOG(SCEUTILITY, "Listing missing save data: %s = %s", thisSaveName.c_str(), fileDataDir.c_str());
realCount++;
} else {
DEBUG_LOG(SCEUTILITY, "Save data not found: %s = %s", thisSaveName.c_str(), fileDataDir.c_str());
}
}
}
@@ -1513,18 +1514,18 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param)
saveDataListCount = 1;
// get and stock file info for each file
DEBUG_LOG(SCEUTILITY,"Name : %s",GetSaveName(param).c_str());
std::string fileDataDir = savePath + GetGameName(param) + GetSaveName(param);
PSPFileInfo info = GetSaveInfo(fileDataDir);
if (info.exists) {
SetFileInfo(0, info, GetSaveName(param));
ERROR_LOG(SCEUTILITY,"%s Exist",fileDataDir.c_str());
DEBUG_LOG(SCEUTILITY, "Save data exists: %s = %s", GetSaveName(param).c_str(), fileDataDir.c_str());
saveNameListDataCount = 1;
} else {
if (listEmptyFile) {
ClearFileInfo(saveDataList[0], GetSaveName(param));
DEBUG_LOG(SCEUTILITY,"Don't Exist");
DEBUG_LOG(SCEUTILITY, "Listing missing save data: %s = %s", GetSaveName(param).c_str(), fileDataDir.c_str());
} else {
DEBUG_LOG(SCEUTILITY, "Save data not found: %s = %s", GetSaveName(param).c_str(), fileDataDir.c_str());
}
saveNameListDataCount = 0;
return 0;
@@ -1631,7 +1632,6 @@ PSPFileInfo SavedataParam::GetSaveInfo(std::string saveDir) {
}
}
}
ERROR_LOG(HLE, "size = %d", info.size);
return info;
}
+15 -9
View File
@@ -1203,18 +1203,24 @@ void notifyAdhocctlHandlers(u32 flag, u32 error) {
// Note: Must not lock peerlock within this function to prevent race-condition with other thread whos owning peerlock and trying to lock context->eventlock owned by this thread
void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32_le &bufAddr, u32_le &bufLen, u32_le * args) {
// Don't share buffer address space with other mipscall in the queue since mipscalls aren't immediately executed
MatchingArgs argsNew;
MatchingArgs argsNew = { 0 };
u32_le dataBufLen = msg->optlen + 8; //max(bufLen, msg->optlen + 8);
u32_le dataBufAddr = userMemory.Alloc(dataBufLen); // We will free this memory after returning from mipscall
uint8_t * dataPtr = Memory::GetPointer(dataBufAddr);
memcpy(dataPtr, &msg->mac, sizeof(msg->mac));
if (msg->optlen > 0)
memcpy(dataPtr + 8, opt, msg->optlen);
argsNew.data[0] = context->id;
argsNew.data[1] = msg->opcode;
argsNew.data[2] = dataBufAddr;
argsNew.data[3] = msg->optlen;
argsNew.data[4] = dataBufAddr + 8; // OptData Addr
if (dataPtr) {
memcpy(dataPtr, &msg->mac, sizeof(msg->mac));
if (msg->optlen > 0)
memcpy(dataPtr + 8, opt, msg->optlen);
argsNew.data[1] = msg->opcode;
argsNew.data[2] = dataBufAddr;
argsNew.data[3] = msg->optlen;
argsNew.data[4] = dataBufAddr + 8; // OptData Addr
}
else {
argsNew.data[1] = PSP_ADHOC_MATCHING_EVENT_ERROR; // not sure where to put the error code for EVENT_ERROR tho
}
argsNew.data[0] = context->id;
argsNew.data[5] = context->handler.entryPoint; //not part of callback argument, just borrowing a space to store callback address so i don't need to search the context first later
// ScheduleEvent_Threadsafe_Immediate seems to get mixed up with interrupt (returning from mipscall inside an interrupt) and getting invalid address before returning from interrupt
+54 -41
View File
@@ -33,6 +33,7 @@
#include "Core/HLE/HLETables.h"
#include "Core/HLE/Plugins.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/HLE/sceDisplay.h"
#include "Core/Reporting.h"
#include "Core/Host.h"
#include "Core/Loaders.h"
@@ -117,6 +118,8 @@ static const char * const blacklistedModules[] = {
"sceMD5_Library",
};
struct WriteVarSymbolState;
struct VarSymbolImport {
char moduleName[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 nid;
@@ -150,7 +153,7 @@ struct FuncSymbolExport {
u32 nid;
};
void ImportVarSymbol(const VarSymbolImport &var);
void ImportVarSymbol(WriteVarSymbolState &state, const VarSymbolImport &var);
void ExportVarSymbol(const VarSymbolExport &var);
void UnexportVarSymbol(const VarSymbolExport &var);
@@ -378,11 +381,11 @@ public:
ImportFuncSymbol(func, reimporting, GetName());
}
void ImportVar(const VarSymbolImport &var) {
void ImportVar(WriteVarSymbolState &state, const VarSymbolImport &var) {
// Keep track and actually hook it up if possible.
importedVars.push_back(var);
impExpModuleNames.insert(var.moduleName);
ImportVarSymbol(var);
ImportVarSymbol(state, var);
}
void ExportFunc(const FuncSymbolExport &func) {
@@ -557,22 +560,22 @@ void __KernelModuleShutdown()
// Sometimes there are multiple LO16's or HI16's per pair, even though the ABI says nothing of this.
// For multiple LO16's, we need the original (unrelocated) instruction data of the HI16.
// For multiple HI16's, we just need to set each one.
struct HI16RelocInfo
{
struct HI16RelocInfo {
u32 addr;
u32 data;
};
static void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type, bool reverse = false)
{
// We have to post-process the HI16 part, since it might be +1 or not depending on the LO16 value.
static u32 lastHI16ExportAddress = 0;
static std::vector<HI16RelocInfo> lastHI16Relocs;
static bool lastHI16Processed = true;
// We have to post-process the HI16 part, since it might be +1 or not depending on the LO16 value.
// For that purpose, we use this state to track HI16s to adjust.
struct WriteVarSymbolState {
u32 lastHI16ExportAddress = 0;
std::vector<HI16RelocInfo> lastHI16Relocs;
bool lastHI16Processed = true;
};
static void WriteVarSymbol(WriteVarSymbolState &state, u32 exportAddress, u32 relocAddress, u8 type, bool reverse = false) {
u32 relocData = Memory::Read_Instruction(relocAddress, true).encoding;
switch (type)
{
switch (type) {
case R_MIPS_NONE:
WARN_LOG_REPORT(LOADER, "Var relocation type NONE - %08x => %08x", exportAddress, relocAddress);
break;
@@ -601,13 +604,13 @@ static void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type, bool re
*/
case R_MIPS_HI16:
if (lastHI16ExportAddress != exportAddress) {
if (!lastHI16Processed && lastHI16Relocs.size() >= 1) {
WARN_LOG_REPORT(LOADER, "Unsafe unpaired HI16 variable relocation @ %08x / %08x", lastHI16Relocs[lastHI16Relocs.size() - 1].addr, relocAddress);
if (state.lastHI16ExportAddress != exportAddress) {
if (!state.lastHI16Processed && !state.lastHI16Relocs.empty()) {
WARN_LOG_REPORT(LOADER, "Unsafe unpaired HI16 variable relocation @ %08x / %08x", state.lastHI16Relocs[state.lastHI16Relocs.size() - 1].addr, relocAddress);
}
lastHI16ExportAddress = exportAddress;
lastHI16Relocs.clear();
state.lastHI16ExportAddress = exportAddress;
state.lastHI16Relocs.clear();
}
// After this will be an R_MIPS_LO16. If that addition overflows, we need to account for it in HI16.
@@ -615,8 +618,8 @@ static void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type, bool re
HI16RelocInfo reloc;
reloc.addr = relocAddress;
reloc.data = Memory::Read_Instruction(relocAddress, true).encoding;
lastHI16Relocs.push_back(reloc);
lastHI16Processed = false;
state.lastHI16Relocs.push_back(reloc);
state.lastHI16Processed = false;
break;
case R_MIPS_LO16:
@@ -632,27 +635,26 @@ static void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type, bool re
}
// The ABI requires that these come in pairs, at least.
if (lastHI16Relocs.empty()) {
if (state.lastHI16Relocs.empty()) {
ERROR_LOG_REPORT(LOADER, "LO16 without any HI16 variable import at %08x for %08x", relocAddress, exportAddress);
// Try to process at least the low relocation...
} else if (lastHI16ExportAddress != exportAddress) {
ERROR_LOG_REPORT(LOADER, "HI16 and LO16 imports do not match at %08x for %08x (should be %08x)", relocAddress, lastHI16ExportAddress, exportAddress);
} else if (state.lastHI16ExportAddress != exportAddress) {
ERROR_LOG_REPORT(LOADER, "HI16 and LO16 imports do not match at %08x for %08x (should be %08x)", relocAddress, state.lastHI16ExportAddress, exportAddress);
} else {
// Process each of the HI16. Usually there's only one.
for (auto it = lastHI16Relocs.begin(), end = lastHI16Relocs.end(); it != end; ++it)
{
for (auto &reloc : state.lastHI16Relocs) {
if (!reverse) {
full = (it->data << 16) + offsetLo + exportAddress;
full = (reloc.data << 16) + offsetLo + exportAddress;
} else {
full = (it->data << 16) + offsetLo - exportAddress;
full = (reloc.data << 16) + offsetLo - exportAddress;
}
// The low instruction will be a signed add, which means (full & 0x8000) will subtract.
// We add 1 in that case so that it ends up the right value.
u16 high = (full >> 16) + ((full & 0x8000) ? 1 : 0);
Memory::Write_U32((it->data & ~0xFFFF) | high, it->addr);
currentMIPS->InvalidateICache(it->addr, 4);
Memory::Write_U32((reloc.data & ~0xFFFF) | high, reloc.addr);
currentMIPS->InvalidateICache(reloc.addr, 4);
}
lastHI16Processed = true;
state.lastHI16Processed = true;
}
// With full set above (hopefully), now we just need to correct the low instruction.
@@ -668,7 +670,7 @@ static void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type, bool re
currentMIPS->InvalidateICache(relocAddress, 4);
}
void ImportVarSymbol(const VarSymbolImport &var) {
void ImportVarSymbol(WriteVarSymbolState &state, const VarSymbolImport &var) {
if (var.nid == 0) {
// TODO: What's the right thing for this?
ERROR_LOG_REPORT(LOADER, "Var import with nid = 0, type = %d", var.type);
@@ -688,9 +690,9 @@ void ImportVarSymbol(const VarSymbolImport &var) {
}
// Look for exports currently loaded modules already have. Maybe it's available?
for (auto it = module->exportedVars.begin(), end = module->exportedVars.end(); it != end; ++it) {
if (it->Matches(var)) {
WriteVarSymbol(it->symAddr, var.stubAddr, var.type);
for (const auto &exported : module->exportedVars) {
if (exported.Matches(var)) {
WriteVarSymbol(state, exported.symAddr, var.stubAddr, var.type);
return;
}
}
@@ -709,10 +711,11 @@ void ExportVarSymbol(const VarSymbolExport &var) {
}
// Look for imports currently loaded modules already have, hook it up right away.
for (auto it = module->importedVars.begin(), end = module->importedVars.end(); it != end; ++it) {
if (var.Matches(*it)) {
WriteVarSymbolState state;
for (auto &imported : module->importedVars) {
if (var.Matches(imported)) {
INFO_LOG(LOADER, "Resolving var %s/%08x", var.moduleName, var.nid);
WriteVarSymbol(var.symAddr, it->stubAddr, it->type);
WriteVarSymbol(state, var.symAddr, imported.stubAddr, imported.type);
}
}
}
@@ -727,10 +730,11 @@ void UnexportVarSymbol(const VarSymbolExport &var) {
}
// Look for imports modules that are *still* loaded have, and reverse them.
for (auto it = module->importedVars.begin(), end = module->importedVars.end(); it != end; ++it) {
if (var.Matches(*it)) {
WriteVarSymbolState state;
for (auto &imported : module->importedVars) {
if (var.Matches(imported)) {
INFO_LOG(LOADER, "Unresolving var %s/%08x", var.moduleName, var.nid);
WriteVarSymbol(var.symAddr, it->stubAddr, it->type, true);
WriteVarSymbol(state, var.symAddr, imported.stubAddr, imported.type, true);
}
}
}
@@ -1051,12 +1055,13 @@ static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr,
continue;
}
WriteVarSymbolState state;
u32_le *varRef = (u32_le *)Memory::GetPointer(varRefsPtr);
for (; *varRef != 0; ++varRef) {
var.nid = nid;
var.stubAddr = (*varRef & 0x03FFFFFF) << 2;
var.type = *varRef >> 26;
module->ImportVar(var);
module->ImportVar(state, var);
}
}
} else if (entry->numVars > 0 && !reimporting) {
@@ -1876,6 +1881,14 @@ void __KernelGPUReplay() {
if (!GPURecord::RunMountedReplay(filename)) {
Core_Stop();
}
if (PSP_CoreParameter().headLess && !PSP_CoreParameter().startBreak) {
PSPPointer<u8> topaddr;
u32 linesize = 512;
__DisplayGetFramebuf(&topaddr, &linesize, nullptr, 0);
host->SendDebugScreenshot(topaddr, linesize, 272);
Core_Stop();
}
}
int sceKernelLoadExec(const char *filename, u32 paramPtr)
+9 -20
View File
@@ -1337,32 +1337,21 @@ u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr)
int __KernelGetThreadExitStatus(SceUID threadID) {
u32 error;
PSPThread *t = kernelObjects.Get<PSPThread>(threadID, error);
if (t)
{
if (t->nt.status == THREADSTATUS_DORMANT) // TODO: can be dormant before starting, too, need to avoid that
{
DEBUG_LOG(SCEKERNEL, "sceKernelGetThreadExitStatus(%d)", threadID);
return t->nt.exitStatus;
}
else
{
DEBUG_LOG(SCEKERNEL, "sceKernelGetThreadExitStatus(%d): not dormant", threadID);
return SCE_KERNEL_ERROR_NOT_DORMANT;
}
if (!t) {
return hleLogError(SCEKERNEL, error);
}
else
{
ERROR_LOG(SCEKERNEL, "sceKernelGetThreadExitStatus Error %08x", error);
return SCE_KERNEL_ERROR_UNKNOWN_THID;
// __KernelResetThread and __KernelCreateThread set exitStatus in case it's DORMANT.
if (t->nt.status == THREADSTATUS_DORMANT) {
return hleLogSuccessI(SCEKERNEL, t->nt.exitStatus);
}
return hleLogDebug(SCEKERNEL, SCE_KERNEL_ERROR_NOT_DORMANT, "not dormant");
}
int sceKernelGetThreadExitStatus(SceUID threadID)
{
int sceKernelGetThreadExitStatus(SceUID threadID) {
u32 status = __KernelGetThreadExitStatus(threadID);
// Seems this is called in a tight-ish loop, maybe awaiting an interrupt - issue #13698
// Guess based on sceKernelGetThreadId.
hleEatCycles(180);
hleEatCycles(330);
return status;
}
+6 -4
View File
@@ -103,6 +103,7 @@ u32_le matchingThreadCode[3];
int matchingEventThread(int matchingId);
int matchingInputThread(int matchingId);
void sendBulkDataPacket(SceNetAdhocMatchingContext* context, SceNetEtherAddr* mac, int datalen, void* data);
int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port);
int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock);
int FlushPtpSocket(int socketId);
@@ -299,15 +300,16 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, adhocctlNotifyEvent, userdata);
return;
}
else
else if (req.opcode != OPCODE_LOGIN)
result = ERROR_NET_ADHOCCTL_BUSY;
}
}
else
result = ERROR_NET_ADHOCCTL_WLAN_SWITCH_OFF;
u32 waitVal = __KernelGetWaitValue(threadID, error);
__KernelResumeThreadFromWait(threadID, result);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - State: %d", waitID, error, (int)result, adhocctlState);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Opcode: %d, State: %d", waitID, error, (int)result, waitVal, adhocctlState);
// We are done with this request
adhocctlRequests.erase(uid);
@@ -5279,7 +5281,7 @@ int sceNetAdhocMatchingSendData(int matchingId, const char *mac, int dataLen, u3
peer->sending = 1;
// Send Data to Peer
sendBulkData(context, peer, dataLen, data);
sendBulkDataPacket(context, &peer->mac, dataLen, data);
// Return Success
return 0;
@@ -5518,7 +5520,7 @@ void __NetMatchingCallbacks() //(int matchingId)
after->SetData(args[0], args[1], args[2]);
hleEnqueueCall(args[5], 5, args, after);
matchingEvents.pop_front();
delayus = (adhocMatchingEventDelay + adhocExtraDelay); // Added an extra delay to prevent I/O Timing method from causing disconnection
delayus = adhocMatchingEventDelay; // Add extra delay to prevent I/O Timing method from causing disconnection, but delaying too long may cause matchingEvents to pile up
}
// Must be delayed long enough whenever there is a pending callback. Should it be 10-100ms for Matching Events? or Not Less than the delays on sceNetAdhocMatching HLE?
+8 -2
View File
@@ -40,8 +40,14 @@ void RegisterFileLoaderFactory(std::string prefix, std::unique_ptr<FileLoaderFac
}
FileLoader *ConstructFileLoader(const std::string &filename) {
if (filename.find("http://") == 0 || filename.find("https://") == 0)
return new CachingFileLoader(new DiskCachingFileLoader(new RetryingFileLoader(new HTTPFileLoader(filename))));
if (filename.find("http://") == 0 || filename.find("https://") == 0) {
FileLoader *baseLoader = new RetryingFileLoader(new HTTPFileLoader(filename));
// For headless, avoid disk caching since it's usually used for tests that might mutate.
if (!PSP_CoreParameter().headLess) {
baseLoader = new DiskCachingFileLoader(baseLoader);
}
return new CachingFileLoader(baseLoader);
}
for (auto &iter : factories) {
if (startsWith(iter.first, filename)) {
+1 -1
View File
@@ -44,7 +44,7 @@ using namespace MIPSAnalyst;
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
+1 -1
View File
@@ -43,7 +43,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
+1 -1
View File
@@ -41,7 +41,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
+1 -1
View File
@@ -37,7 +37,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (opts.disableFlags & (uint32_t)JitDisable::flag) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
#define INVALIDOP { Comp_Generic(op); return; }
+1 -1
View File
@@ -48,7 +48,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (opts.disableFlags & (uint32_t)JitDisable::flag) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
#define INVALIDOP { Comp_Generic(op); return; }
+1 -1
View File
@@ -38,7 +38,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (opts.disableFlags & (uint32_t)JitDisable::flag) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
#define INVALIDOP { Comp_Generic(op); return; }
+2 -2
View File
@@ -36,7 +36,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (opts.disableFlags & (uint32_t)JitDisable::flag) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
#define INVALIDOP { Comp_Generic(op); return; }
@@ -993,7 +993,7 @@ namespace MIPSComp {
ir.Write(IROp::AndConst, IRTEMP_0, rt, ir.AddConstant(mask));
ir.Write(IROp::SetCtrlVFPUReg, imm - 128, IRTEMP_0);
} else {
ir.Write(IROp::SetCtrlVFPU, imm - 128, rt);
ir.Write(IROp::SetCtrlVFPUReg, imm - 128, rt);
}
}
+2 -2
View File
@@ -114,11 +114,11 @@ static const IRMeta irMeta[] = {
{ IROp::FpCondToReg, "FpCondToReg", "G" },
{ IROp::VfpuCtrlToReg, "VfpuCtrlToReg", "GI" },
{ IROp::SetCtrlVFPU, "SetCtrlVFPU", "TC" },
{ IROp::SetCtrlVFPUReg, "SetCtrlVFPUReg", "TC" },
{ IROp::SetCtrlVFPUReg, "SetCtrlVFPUReg", "TG" },
{ IROp::SetCtrlVFPUFReg, "SetCtrlVFPUFReg", "TF" },
{ IROp::FCmovVfpuCC, "FCmovVfpuCC", "FFI" },
{ IROp::FCmpVfpuBit, "FCmpVfpuBit", "IFF" },
{ IROp::FCmpVfpuAggregate, "FCmpVfpuAggregate", "" },
{ IROp::FCmpVfpuAggregate, "FCmpVfpuAggregate", "I" },
{ IROp::Vec4Init, "Vec4Init", "Vv" },
{ IROp::Vec4Shuffle, "Vec4Shuffle", "VVs" },
{ IROp::Vec4Mov, "Vec4Mov", "VV" },
+20
View File
@@ -8,6 +8,10 @@
#include "Core/MIPS/IR/IRPassSimplify.h"
#include "Core/MIPS/IR/IRRegCache.h"
// #define CONDITIONAL_DISABLE { for (IRInst inst : in.GetInstructions()) { out.Write(inst); } return false; }
#define CONDITIONAL_DISABLE
#define DISABLE { for (IRInst inst : in.GetInstructions()) { out.Write(inst); } return false; }
u32 Evaluate(u32 a, u32 b, IROp op) {
switch (op) {
case IROp::Add: case IROp::AddConst: return a + b;
@@ -101,8 +105,11 @@ bool IRApplyPasses(const IRPassFunc *passes, size_t c, const IRWriter &in, IRWri
}
bool OptimizeFPMoves(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
bool logBlocks = false;
IRInst prev{ IROp::Nop };
for (int i = 0; i < (int)in.GetInstructions().size(); i++) {
IRInst inst = in.GetInstructions()[i];
switch (inst.op) {
@@ -151,6 +158,8 @@ bool OptimizeFPMoves(const IRWriter &in, IRWriter &out, const IROptions &opts) {
// Might be useful later on x86.
bool ThreeOpToTwoOp(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
bool logBlocks = false;
for (int i = 0; i < (int)in.GetInstructions().size(); i++) {
IRInst inst = in.GetInstructions()[i];
@@ -201,6 +210,8 @@ bool ThreeOpToTwoOp(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}
bool RemoveLoadStoreLeftRight(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
bool logBlocks = false;
for (int i = 0, n = (int)in.GetInstructions().size(); i < n; ++i) {
const IRInst &inst = in.GetInstructions()[i];
@@ -366,6 +377,7 @@ bool RemoveLoadStoreLeftRight(const IRWriter &in, IRWriter &out, const IROptions
}
bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
IRRegCache gpr(&out);
bool logBlocks = false;
@@ -784,6 +796,7 @@ bool IRMutatesDestGPR(const IRInst &inst, int reg) {
}
bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
std::vector<IRInst> insts;
insts.reserve(in.GetInstructions().size());
@@ -822,6 +835,8 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
insts[check.index] = IRReplaceDestGPR(insts[check.index], check.reg, inst.dest);
lastWrittenTo[inst.dest] = check.index;
check.reg = inst.dest;
// Update the read by exit flag to match the new reg.
check.readByExit = inst.dest < IRTEMP_0 || inst.dest > IRTEMP_LR_SHIFT;
// And swap the args for this mov, since we changed the other dest. We'll optimize this out later.
std::swap(inst.dest, inst.src1);
} else {
@@ -903,6 +918,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}
bool ReduceLoads(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
// This tells us to skip an AND op that has been optimized out.
// Maybe we could skip multiple, but that'd slow things down and is pretty uncommon.
int nextSkip = -1;
@@ -1039,6 +1055,8 @@ static std::vector<IRInst> ReorderLoadStoreOps(std::vector<IRInst> &ops) {
}
bool ReorderLoadStore(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
bool logBlocks = false;
enum class RegState : u8 {
@@ -1234,6 +1252,8 @@ bool ReorderLoadStore(const IRWriter &in, IRWriter &out, const IROptions &opts)
}
bool MergeLoadStore(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
bool logBlocks = false;
auto opsCompatible = [&](const IRInst &a, const IRInst &b, int dist) {
+1 -1
View File
@@ -41,7 +41,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
+1 -1
View File
@@ -41,7 +41,7 @@
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; }
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
#define DISABLE { Comp_Generic(op); return; }
+1 -1
View File
@@ -493,7 +493,7 @@ void PSP_Shutdown() {
// Make sure things know right away that PSP memory, etc. is going away.
pspIsQuitting = true;
if (coreState == CORE_RUNNING)
Core_UpdateState(CORE_POWERDOWN);
Core_Stop();
#ifndef MOBILE_DEVICE
if (g_Config.bFuncHashMap) {
+3
View File
@@ -28,6 +28,9 @@
#else
#include "ext/libzip/zip.h"
#endif
#ifdef _WIN32
#include "Common/CommonWindows.h"
#endif
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Data/Format/IniFile.h"
#include "Common/Log.h"
+15 -4
View File
@@ -394,7 +394,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
if (!strcmp(compat.fragColor0, "fragColor0")) {
const char *qualifierColor0 = "out";
if (compat.lastFragData && !strcmp(compat.lastFragData, compat.fragColor0)) {
if (readFramebuffer && compat.lastFragData && !strcmp(compat.lastFragData, compat.fragColor0)) {
qualifierColor0 = "inout";
}
// Output the output color definitions.
@@ -778,10 +778,21 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
// When testing against 0 (common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if (colorTestFunc == GE_COMP_NOTEQUAL) {
WRITE(p, " if (v.r < 0.002 && v.g < 0.002 && v.b < 0.002) %s\n", discardStatement);
if (compat.shaderLanguage == GLSL_VULKAN) {
// Old workaround for Adreno driver bug. We could make this the main path actually
// since the math is roughly equivalent given the non-negative inputs.
WRITE(p, " if (v.r + v.g + v.b < 0.002) %s\n", discardStatement);
} else {
WRITE(p, " if (v.r < 0.002 && v.g < 0.002 && v.b < 0.002) %s\n", discardStatement);
}
} else if (colorTestFunc != GE_COMP_NEVER) {
// Anything else is a test for == 0.
WRITE(p, " if (v.r > 0.002 || v.g > 0.002 || v.b > 0.002) %s\n", discardStatement);
if (compat.shaderLanguage == GLSL_VULKAN) {
// See the GE_COMP_NOTEQUAL case.
WRITE(p, " if (v.r + v.g + v.b > 0.002) %s\n", discardStatement);
} else {
// Anything else is a test for == 0.
WRITE(p, " if (v.r > 0.002 || v.g > 0.002 || v.b > 0.002) %s\n", discardStatement);
}
} else {
// NEVER has been logged as used by games, although it makes little sense - statically failing.
// Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here.
+8 -2
View File
@@ -575,6 +575,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
renderHeightFactor = renderHeight / 272.0f;
}
_assert_(renderWidthFactor > 0.0);
_assert_(renderHeightFactor > 0.0);
renderX = gstate_c.curRTOffsetX;
// Scissor
@@ -694,8 +697,11 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
out.viewportX = left * renderWidthFactor + displayOffsetX;
out.viewportY = top * renderHeightFactor + displayOffsetY;
out.viewportW = (right - left) * renderWidthFactor;
out.viewportH = (bottom - top) * renderHeightFactor;
// The calculations should end up with zero or positive values, but let's protect against any
// precision issues. See #13921.
out.viewportW = std::max(0.0f, (right - left) * renderWidthFactor);
out.viewportH = std::max(0.0f, (bottom - top) * renderHeightFactor);
// The depth viewport parameters are the same, but we handle it a bit differently.
// When clipping is enabled, depth is clamped to [0, 65535]. And minz/maxz discard.
+5 -3
View File
@@ -239,7 +239,8 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha
return false;
}
std::string vsError, fsError;
std::string vsError;
std::string fsError;
// All post shaders are written in GLSL 1.0 so that's what we pass in here as a "from" language.
Draw::ShaderModule *vs = CompileShaderModule(ShaderStage::Vertex, GLSL_1xx, vsSourceGLSL, &vsError);
@@ -248,7 +249,7 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha
// Don't worry, CompileShaderModule makes sure they get freed if one succeeded.
if (!fs || !vs) {
std::string errorString = vsError + "\n" + fsError;
// DO NOT turn this into a report, as it will pollute our logs with all kinds of
// DO NOT turn this into an ERROR_LOG_REPORT, as it will pollute our logs with all kinds of
// user shader experiments.
ERROR_LOG(FRAMEBUF, "Failed to build post-processing program from %s and %s!\n%s", shaderInfo->vertexShaderFile.c_str(), shaderInfo->fragmentShaderFile.c_str(), errorString.c_str());
ShowPostShaderError(errorString);
@@ -263,6 +264,7 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha
{ "u_setting", 4, 4, UniformType::FLOAT4, offsetof(PostShaderUniforms, setting) },
{ "u_video", 5, 5, UniformType::FLOAT1, offsetof(PostShaderUniforms, video) },
} };
Draw::Pipeline *pipeline = CreatePipeline({ vs, fs }, true, &postShaderDesc);
if (!pipeline)
return false;
@@ -353,7 +355,7 @@ void PresentationCommon::ShowPostShaderError(const std::string &errorString) {
}
}
if (!firstLine.empty()) {
host->NotifyUserMessage("Post-shader error: " + firstLine + "...", 10.0f, 0xFF3090FF);
host->NotifyUserMessage("Post-shader error: " + firstLine + "...:\n" + errorString, 10.0f, 0xFF3090FF);
} else {
host->NotifyUserMessage("Post-shader error, see log for details", 10.0f, 0xFF3090FF);
}
+2 -2
View File
@@ -61,7 +61,7 @@ std::string VertexShaderDesc(const VShaderID &id) {
return desc.str();
}
void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform, bool useHWTessellation) {
void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
bool isModeThrough = (vertType & GE_VTYPE_THROUGH) != 0;
bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
bool doShadeMapping = doTexture && (gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP);
@@ -112,7 +112,7 @@ void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform,
id.SetBits(VS_BIT_BONES, 3, TranslateNumBones(vertTypeGetNumBoneWeights(vertType)) - 1);
// 2 bits. We should probably send in the weight scalefactor as a uniform instead,
// or simply preconvert all weights to floats.
id.SetBits(VS_BIT_WEIGHT_FMTSCALE, 2, (vertType & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT);
id.SetBits(VS_BIT_WEIGHT_FMTSCALE, 2, weightsAsFloat ? 0 : (vertType & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT);
}
if (gstate.isLightingEnabled()) {
+1 -1
View File
@@ -225,7 +225,7 @@ class Bugs;
}
void ComputeVertexShaderID(VShaderID *id, uint32_t vertexType, bool useHWTransform, bool useHWTessellation);
void ComputeVertexShaderID(VShaderID *id, uint32_t vertexType, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
// Generates a compact string that describes the shader. Useful in a list to get an overview
// of the current flora of shaders.
std::string VertexShaderDesc(const VShaderID &id);
+2 -2
View File
@@ -471,8 +471,8 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt
if (!result->setSafeSize && prim == GE_PRIM_RECTANGLES && maxIndex == 2) {
bool clearingColor = gstate.isModeClear() && (gstate.isClearModeColorMask() || gstate.isClearModeAlphaMask());
bool writingColor = gstate.getColorMask() != 0xFFFFFFFF;
bool startsZeroX = transformed[0].x <= 0.0f && transformed[1].x > transformed[0].x;
bool startsZeroY = transformed[0].y <= 0.0f && transformed[1].y > transformed[0].y;
bool startsZeroX = transformed[0].x <= 0.0f && transformed[1].x > 0.0f && transformed[1].x > transformed[0].x;
bool startsZeroY = transformed[0].y <= 0.0f && transformed[1].y > 0.0f && transformed[1].y > transformed[0].y;
if (startsZeroX && startsZeroY && (clearingColor || writingColor)) {
int scissorX2 = gstate.getScissorX2() + 1;
+3 -3
View File
@@ -822,9 +822,9 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
// Uncomment this to screw up bone shaders to check the vertex shader software fallback
// WRITE(p, "THIS SHOULD ERROR! #error");
if (numBoneWeights == 1 && ShaderLanguageIsOpenGL(compat.shaderLanguage))
WRITE(p, " %s skinMatrix = w1 * u_bone0", boneMatrix);
WRITE(p, " %s skinMatrix = mul(w1, u_bone0)", boneMatrix);
else
WRITE(p, " %s skinMatrix = w1.x * u_bone0", boneMatrix);
WRITE(p, " %s skinMatrix = mul(w1.x, u_bone0)", boneMatrix);
for (int i = 1; i < numBoneWeights; i++) {
const char *weightAttr = boneWeightAttr[i];
// workaround for "cant do .x of scalar" issue.
@@ -832,7 +832,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
if (numBoneWeights == 1 && i == 0) weightAttr = "w1";
if (numBoneWeights == 5 && i == 4) weightAttr = "w2";
}
WRITE(p, " + %s * u_bone%i", weightAttr, i);
WRITE(p, " + mul(%s, u_bone%i)", weightAttr, i);
}
WRITE(p, ";\n");
+2 -2
View File
@@ -523,7 +523,7 @@ rotateVBO:
D3D11VertexShader *vshader;
D3D11FragmentShader *fshader;
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform, useHWTessellation_);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat);
ID3D11InputLayout *inputLayout = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType());
context_->PSSetShader(fshader->GetShader(), nullptr, 0);
context_->VSSetShader(vshader->GetShader(), nullptr, 0);
@@ -609,7 +609,7 @@ rotateVBO:
D3D11VertexShader *vshader;
D3D11FragmentShader *fshader;
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat);
context_->PSSetShader(fshader->GetShader(), nullptr, 0);
context_->VSSetShader(vshader->GetShader(), nullptr, 0);
shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering());
-5
View File
@@ -36,7 +36,6 @@
#include "GPU/Debugger/Stepping.h"
#include "GPU/Common/FramebufferManagerCommon.h"
#include "GPU/Common/PresentationCommon.h"
#include "Common/GPU/ShaderTranslation.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/D3D11/FramebufferManagerD3D11.h"
#include "GPU/D3D11/ShaderManagerD3D11.h"
@@ -125,15 +124,11 @@ FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw)
uint32_t nullData[1]{};
context_->UpdateSubresource(nullTexture_, 0, nullptr, nullData, 1, 0);
ShaderTranslationInit();
presentation_->SetLanguage(HLSL_D3D11);
preferredPixelsFormat_ = Draw::DataFormat::B8G8R8A8_UNORM;
}
FramebufferManagerD3D11::~FramebufferManagerD3D11() {
ShaderTranslationShutdown();
// Drawing cleanup
if (quadVertexShader_)
quadVertexShader_->Release();
+2 -2
View File
@@ -177,13 +177,13 @@ void ShaderManagerD3D11::BindUniforms() {
context_->PSSetConstantBuffers(0, 1, ps_cbs);
}
void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation) {
void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
VShaderID VSID;
FShaderID FSID;
if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) {
gstate_c.Clean(DIRTY_VERTEXSHADER_STATE);
ComputeVertexShaderID(&VSID, vertType, useHWTransform, useHWTessellation);
ComputeVertexShaderID(&VSID, vertType, useHWTransform, useHWTessellation, weightsAsFloat);
} else {
VSID = lastVSID_;
}
+1 -1
View File
@@ -85,7 +85,7 @@ public:
ShaderManagerD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context, D3D_FEATURE_LEVEL featureLevel);
~ShaderManagerD3D11();
void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation);
void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
void ClearShaders();
void DirtyLastShader() override;
+3 -3
View File
@@ -324,7 +324,7 @@ void DrawEngineDX9::DoFlush() {
GEPrimitiveType prim = prevPrim_;
ApplyDrawState(prim);
VSShader *vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_);
VSShader *vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat);
if (vshader->UseHWTransform()) {
LPDIRECT3DVERTEXBUFFER9 vb_ = NULL;
@@ -505,7 +505,7 @@ rotateVBO:
}
ApplyDrawStateLate();
vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_);
vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat);
IDirect3DVertexDeclaration9 *pHardwareVertexDecl = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType());
if (pHardwareVertexDecl) {
@@ -568,7 +568,7 @@ rotateVBO:
framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight);
ApplyDrawStateLate();
vshader = shaderManager_->ApplyShader(false, false, lastVType_);
vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat);
if (result.action == SW_DRAW_PRIMITIVES) {
if (result.setStencil) {
-5
View File
@@ -31,7 +31,6 @@
#include "Common/GPU/D3D9/D3D9StateCache.h"
#include "GPU/Common/FramebufferManagerCommon.h"
#include "GPU/Common/PresentationCommon.h"
#include "Common/GPU/ShaderTranslation.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Directx9/FramebufferManagerDX9.h"
#include "GPU/Directx9/ShaderManagerDX9.h"
@@ -114,15 +113,11 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
memset(rect.pBits, 0, 4);
nullTex_->UnlockRect(0);
ShaderTranslationInit();
presentation_->SetLanguage(HLSL_D3D9);
preferredPixelsFormat_ = Draw::DataFormat::B8G8R8A8_UNORM;
}
FramebufferManagerDX9::~FramebufferManagerDX9() {
ShaderTranslationShutdown();
if (pFramebufferVertexShader) {
pFramebufferVertexShader->Release();
pFramebufferVertexShader = nullptr;
+3 -3
View File
@@ -542,7 +542,7 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays
lastPShader_ = nullptr;
}
VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType) {
VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat) {
// Always use software for flat shading to fix the provoking index.
bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE;
useHWTransform = useHWTransform && (tess || gstate.getShadeMode() != GE_SHADE_FLAT);
@@ -550,7 +550,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat
VShaderID VSID;
if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) {
gstate_c.Clean(DIRTY_VERTEXSHADER_STATE);
ComputeVertexShaderID(&VSID, vertType, useHWTransform, useHWTessellation);
ComputeVertexShaderID(&VSID, vertType, useHWTransform, useHWTessellation, weightsAsFloat);
} else {
VSID = lastVSID_;
}
@@ -599,7 +599,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat
}
delete vs;
ComputeVertexShaderID(&VSID, vertType, false, false);
ComputeVertexShaderID(&VSID, vertType, false, false, weightsAsFloat);
// TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure
// that that shader ID is not used when computing the linked shader ID below, because then IDs won't match
+1 -1
View File
@@ -80,7 +80,7 @@ public:
~ShaderManagerDX9();
void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected
VSShader *ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType);
VSShader *ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat);
void DirtyShader();
void DirtyLastShader() override;
+1 -1
View File
@@ -333,7 +333,7 @@ void DrawEngineGLES::DoFlush() {
GEPrimitiveType prim = prevPrim_;
VShaderID vsid;
Shader *vshader = shaderManager_->ApplyVertexShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, &vsid);
Shader *vshader = shaderManager_->ApplyVertexShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat, &vsid);
GLRBuffer *vertexBuffer = nullptr;
GLRBuffer *indexBuffer = nullptr;
+3 -3
View File
@@ -656,10 +656,10 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) {
return new Shader(render_, codeBuffer_, desc, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask);
}
Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertType, VShaderID *VSID) {
Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, VShaderID *VSID) {
if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) {
gstate_c.Clean(DIRTY_VERTEXSHADER_STATE);
ComputeVertexShaderID(VSID, vertType, useHWTransform, useHWTessellation);
ComputeVertexShaderID(VSID, vertType, useHWTransform, useHWTessellation, weightsAsFloat);
} else {
*VSID = lastVSID_;
}
@@ -690,7 +690,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess
// Can still work with software transform.
VShaderID vsidTemp;
ComputeVertexShaderID(&vsidTemp, vertType, false, false);
ComputeVertexShaderID(&vsidTemp, vertType, false, false, weightsAsFloat);
vs = CompileVertexShader(vsidTemp);
}
+1 -1
View File
@@ -146,7 +146,7 @@ public:
// This is the old ApplyShader split into two parts, because of annoying information dependencies.
// If you call ApplyVertexShader, you MUST call ApplyFragmentShader soon afterwards.
Shader *ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertType, VShaderID *VSID);
Shader *ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, VShaderID *VSID);
LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, u32 vertType, bool useBufferedRendering);
void DeviceLost();
+9 -9
View File
@@ -96,7 +96,7 @@ void DrawSprite(const VertexData& v0, const VertexData& v1) {
int z = pos0.z;
float fog = 1.0f;
bool isWhite = v0.color0 == Vec4<int>(255, 255, 255, 255);
bool isWhite = v1.color0 == Vec4<int>(255, 255, 255, 255);
if (gstate.isTextureMapEnabled()) {
// 1:1 (but with mirror support) texture mapping!
@@ -153,7 +153,7 @@ void DrawSprite(const VertexData& v0, const VertexData& v1) {
}
} else {
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> prim_color = v0.color0;
Vec4<int> prim_color = v1.color0;
Vec4<int> tex_color = Vec4<int>::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0));
prim_color = ModulateRGBA(prim_color, tex_color);
if (prim_color.a() > 0) {
@@ -171,7 +171,7 @@ void DrawSprite(const VertexData& v0, const VertexData& v1) {
int s = s_start;
// Not really that fast but faster than triangle.
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> prim_color = v0.color0;
Vec4<int> prim_color = v1.color0;
Vec4<int> tex_color = Vec4<int>::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0));
prim_color = GetTextureFunctionOutput(prim_color, tex_color);
DrawingCoords pos(x, y, z);
@@ -199,13 +199,13 @@ void DrawSprite(const VertexData& v0, const VertexData& v1) {
gstate.getTextureFunction() == GE_TEXFUNC_MODULATE &&
gstate.getColorMask() == 0x000000 &&
gstate.FrameBufFormat() == GE_FORMAT_5551) {
if (v0.color0.a() == 0)
if (v1.color0.a() == 0)
return;
for (int y = pos0.y; y < pos1.y; y++) {
u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride());
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> prim_color = v0.color0;
Vec4<int> prim_color = v1.color0;
DrawSinglePixel5551(pixel, prim_color.ToRGBA());
pixel++;
}
@@ -213,7 +213,7 @@ void DrawSprite(const VertexData& v0, const VertexData& v1) {
} else {
for (int y = pos0.y; y < pos1.y; y++) {
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> prim_color = v0.color0;
Vec4<int> prim_color = v1.color0;
DrawingCoords pos(x, y, z);
DrawSinglePixelNonClear(pos, (u16)z, fog, prim_color);
}
@@ -259,9 +259,9 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1) {
g_needsClearAfterDialog = false;
// Afterwards, we also need to clear the actual destination. Can do a fast rectfill.
gstate.textureMapEnable &= ~1;
VertexData newV0 = v0;
newV0.color0 = Vec4<int>(0, 0, 0, 255);
Rasterizer::DrawSprite(newV0, v1);
VertexData newV1 = v1;
newV1.color0 = Vec4<int>(0, 0, 0, 255);
Rasterizer::DrawSprite(v0, newV1);
gstate.textureMapEnable |= 1;
}
return true;
-11
View File
@@ -78,11 +78,9 @@ SoftGPU::SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
presentation_->SetLanguage(draw_->GetShaderLanguageDesc().shaderLanguage);
break;
case GPUBackend::DIRECT3D9:
ShaderTranslationInit();
presentation_->SetLanguage(HLSL_D3D9);
break;
case GPUBackend::DIRECT3D11:
ShaderTranslationInit();
presentation_->SetLanguage(HLSL_D3D11);
break;
case GPUBackend::VULKAN:
@@ -119,15 +117,6 @@ SoftGPU::~SoftGPU() {
if (presentation_) {
delete presentation_;
switch (GetGPUBackend()) {
case GPUBackend::DIRECT3D9:
case GPUBackend::DIRECT3D11:
ShaderTranslationShutdown();
break;
case GPUBackend::OPENGL:
case GPUBackend::VULKAN:
break;
}
}
Sampler::Shutdown();
+2 -2
View File
@@ -818,7 +818,7 @@ void DrawEngineVulkan::DoFlush() {
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
}
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true, useHWTessellation_); // usehwtransform
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform
_dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader");
Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS;
@@ -939,7 +939,7 @@ void DrawEngineVulkan::DoFlush() {
sampler = nullSampler_;
}
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) {
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false); // usehwtransform
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform
_dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader");
if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) {
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
+2 -2
View File
@@ -228,11 +228,11 @@ uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) {
return dirty;
}
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation) {
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
VShaderID VSID;
if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) {
gstate_c.Clean(DIRTY_VERTEXSHADER_STATE);
ComputeVertexShaderID(&VSID, vertType, useHWTransform, useHWTessellation);
ComputeVertexShaderID(&VSID, vertType, useHWTransform, useHWTessellation, weightsAsFloat);
} else {
VSID = lastVSID_;
}
+1 -1
View File
@@ -88,7 +88,7 @@ public:
void DeviceRestore(VulkanContext *vulkan, Draw::DrawContext *draw);
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation);
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
void ClearShaders();
void DirtyShader();
void DirtyLastShader() override;
+3
View File
@@ -394,6 +394,9 @@ void TextureCacheVulkan::CompileScalingShader() {
if (copyCS_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteShaderModule(copyCS_);
textureShader_.clear();
} else if (uploadCS_ || copyCS_) {
// No need to recreate.
return;
}
if (!g_Config.bTexHardwareScaling)
return;
+17
View File
@@ -149,6 +149,23 @@ std::string System_GetProperty(SystemProperty prop) {
}
}
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
std::vector<std::string> result;
switch (prop) {
case SYSPROP_TEMP_DIRS:
if (getenv("TMPDIR") && strlen(getenv("TMPDIR")) != 0)
result.push_back(getenv("TMPDIR"));
if (getenv("TMP") && strlen(getenv("TMP")) != 0)
result.push_back(getenv("TMP"));
if (getenv("TEMP") && strlen(getenv("TEMP")) != 0)
result.push_back(getenv("TEMP"));
return result;
default:
return result;
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
#if defined(SDL)
+18
View File
@@ -325,6 +325,24 @@ std::string System_GetProperty(SystemProperty prop) {
}
}
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
std::vector<std::string> result;
switch (prop) {
case SYSPROP_TEMP_DIRS:
if (getenv("TMPDIR") && strlen(getenv("TMPDIR")) != 0)
result.push_back(getenv("TMPDIR"));
if (getenv("TMP") && strlen(getenv("TMP")) != 0)
result.push_back(getenv("TMP"));
if (getenv("TEMP") && strlen(getenv("TEMP")) != 0)
result.push_back(getenv("TEMP"));
return result;
default:
return result;
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
+4 -2
View File
@@ -335,8 +335,10 @@ bool CwCheatScreen::RebuildCheatFile(int index) {
return false;
}
for (const auto &line : lines) {
fs << line << '\n';
for (int i = 0; i < lines.size(); ++i) {
fs << lines[i];
if (i != lines.size()-1)
fs << '\n';
}
fs.close();
+2
View File
@@ -420,6 +420,8 @@ void EmuScreen::sendMessage(const char *message, const char *value) {
PSP_Shutdown();
bootPending_ = true;
gamePath_ = value;
// Don't leave it on CORE_POWERDOWN, we'll sometimes aggressively bail.
Core_UpdateState(CORE_POWERUP);
}
} else if (!strcmp(message, "config_loaded")) {
// In case we need to position touch controls differently.
+316 -9
View File
@@ -2,6 +2,14 @@
#include "Common/Data/Text/I18n.h"
#include "Common/UI/View.h"
#include "Common/GPU/Shader.h"
#include "Common/GPU/ShaderWriter.h"
const uint32_t textColorOK = 0xFF30FF30;
const uint32_t textColorBAD = 0xFF3030FF;
const uint32_t bgColorOK = 0xFF106010;
const uint32_t bgColorBAD = 0xFF101060;
// TODO: One day, port these to use ShaderWriter.
static const std::vector<Draw::ShaderSource> fsDiscard = {
{ShaderLanguage::GLSL_1xx,
@@ -45,6 +53,188 @@ static const std::vector<Draw::ShaderSource> fsDiscard = {
},
};
static const std::vector<Draw::ShaderSource> fsAdrenoLogicTest = {
{ShaderLanguage::GLSL_1xx,
R"(
#ifdef GL_ES
precision lowp float;
#endif
#if __VERSION__ >= 130
#define varying in
#define gl_FragColor fragColor0
out vec4 fragColor0;
#endif
varying vec4 oColor0;
varying vec2 oTexCoord0;
uniform sampler2D Sampler0;
void main() {
#if __VERSION__ >= 130
vec4 color = (texture(Sampler0, oTexCoord0) * oColor0).aaaa;
#else
vec4 color = (texture2D(Sampler0, oTexCoord0) * oColor0).aaaa;
#endif
color *= 2.0;
if (color.r < 0.002 && color.g < 0.002 && color.b < 0.002) discard;
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})"
},
{ShaderLanguage::GLSL_VULKAN,
R"(#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
precision lowp float;
layout(location = 0) flat in lowp vec4 oColor0;
layout(location = 1) in highp vec2 oTexCoord0;
layout(location = 0) out vec4 fragColor0;
layout(set = 0, binding = 1) uniform sampler2D Sampler0;
void main() {
vec4 t = texture(Sampler0, oTexCoord0).aaaa;
vec4 p = oColor0;
vec4 v = p * t;
v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);
if (v.r < 0.2 && v.g < 0.2 && v.b < 0.2) discard;
fragColor0 = vec4(0.0, 1.0, 0.0, 1.0);
})"
},
};
static const std::vector<Draw::ShaderSource> vsAdrenoLogicTest = {
{ GLSL_1xx,
"#if __VERSION__ >= 130\n"
"#define attribute in\n"
"#define varying out\n"
"#endif\n"
"attribute vec3 Position;\n"
"attribute vec4 Color0;\n"
"attribute vec2 TexCoord0;\n"
"varying vec4 oColor0;\n"
"varying vec2 oTexCoord0;\n"
"uniform mat4 WorldViewProj;\n"
"void main() {\n"
" gl_Position = WorldViewProj * vec4(Position, 1.0);\n"
" oColor0 = Color0;\n"
" oTexCoord0 = TexCoord0;\n"
"}\n"
},
{ ShaderLanguage::GLSL_VULKAN,
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"layout (std140, set = 0, binding = 0) uniform bufferVals {\n"
" mat4 WorldViewProj;\n"
"} myBufferVals;\n"
"layout (location = 0) in vec4 pos;\n"
"layout (location = 1) in vec4 inColor;\n"
"layout (location = 2) in vec2 inTexCoord;\n"
"layout (location = 0) out lowp vec4 outColor;\n"
"layout (location = 1) out highp vec2 outTexCoord;\n"
"out gl_PerVertex { vec4 gl_Position; };\n"
"void main() {\n"
" outColor = inColor;\n"
" outTexCoord = inTexCoord;\n"
" gl_Position = myBufferVals.WorldViewProj * pos;\n"
"}\n"
}
};
static const std::vector<Draw::ShaderSource> fsFlat = {
{ShaderLanguage::GLSL_3xx,
"#ifdef GL_ES\n"
"precision lowp float;\n"
"precision highp int;\n"
"#endif\n"
"uniform sampler2D Sampler0;\n"
"flat in lowp vec4 oColor0;\n"
"in mediump vec3 oTexCoord0;\n"
"out vec4 fragColor0;\n"
"void main() {\n"
" vec4 t = texture(Sampler0, oTexCoord0.xy);\n"
" vec4 p = oColor0;\n"
" vec4 v = p * t;\n"
" fragColor0 = v;\n"
"}\n"
},
{ShaderLanguage::GLSL_1xx,
"#ifdef GL_ES\n"
"precision lowp float;\n"
"#endif\n"
"#if __VERSION__ >= 130\n"
"#define varying in\n"
"#define texture2D texture\n"
"#define gl_FragColor fragColor0\n"
"out vec4 fragColor0;\n"
"#endif\n"
"varying vec4 oColor0;\n"
"varying vec2 oTexCoord0;\n"
"uniform sampler2D Sampler0;\n"
"void main() { gl_FragColor = texture2D(Sampler0, oTexCoord0) * oColor0; }\n"
},
{ShaderLanguage::GLSL_VULKAN,
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"layout(location = 0) flat in lowp vec4 oColor0;\n"
"layout(location = 1) in highp vec2 oTexCoord0;\n"
"layout(location = 0) out vec4 fragColor0;\n"
"layout(set = 0, binding = 1) uniform sampler2D Sampler0;\n"
"void main() { fragColor0 = texture(Sampler0, oTexCoord0) * oColor0; }\n"
}
};
static const std::vector<Draw::ShaderSource> vsFlat = {
{ GLSL_3xx,
"#version 300 es\n"
"in vec3 Position;\n"
"in vec2 TexCoord0;\n"
"in lowp vec4 Color0;\n"
"uniform mat4 WorldViewProj;\n"
"flat out lowp vec4 oColor0;\n"
"out mediump vec3 oTexCoord0;\n"
"void main() {\n"
" oTexCoord0 = vec3(TexCoord0, 1.0);\n"
" oColor0 = Color0;\n"
" vec4 outPos = WorldViewProj * vec4(Position, 1.0);\n"
" gl_Position = outPos;\n"
"}\n"
},
{ GLSL_1xx, // Doesn't actually repro the problem since flat support isn't guaranteed
"#if __VERSION__ >= 130\n"
"#define attribute in\n"
"#define varying out\n"
"#endif\n"
"attribute vec3 Position;\n"
"attribute vec4 Color0;\n"
"attribute vec2 TexCoord0;\n"
"varying vec4 oColor0;\n"
"varying vec2 oTexCoord0;\n"
"uniform mat4 WorldViewProj;\n"
"void main() {\n"
" gl_Position = WorldViewProj * vec4(Position, 1.0);\n"
" oColor0 = Color0;\n"
" oTexCoord0 = TexCoord0;\n"
"}\n"
},
{ ShaderLanguage::GLSL_VULKAN,
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"layout (std140, set = 0, binding = 0) uniform bufferVals {\n"
" mat4 WorldViewProj;\n"
"} myBufferVals;\n"
"layout (location = 0) in vec4 pos;\n"
"layout (location = 1) in vec4 inColor;\n"
"layout (location = 2) in vec2 inTexCoord;\n"
"layout (location = 0) flat out lowp vec4 outColor;\n"
"layout (location = 1) out highp vec2 outTexCoord;\n"
"out gl_PerVertex { vec4 gl_Position; };\n"
"void main() {\n"
" outColor = inColor;\n"
" outTexCoord = inTexCoord;\n"
" gl_Position = myBufferVals.WorldViewProj * pos;\n"
"}\n"
}
};
GPUDriverTestScreen::GPUDriverTestScreen() {
using namespace Draw;
}
@@ -76,11 +266,35 @@ GPUDriverTestScreen::~GPUDriverTestScreen() {
if (discardFragShader_)
discardFragShader_->Release();
// Shader test
if (adrenoLogicDiscardPipeline_)
adrenoLogicDiscardPipeline_->Release();
if (flatShadingPipeline_)
flatShadingPipeline_->Release();
if (adrenoLogicDiscardFragShader_)
adrenoLogicDiscardFragShader_->Release();
if (adrenoLogicDiscardVertShader_)
adrenoLogicDiscardVertShader_->Release();
if (flatFragShader_)
flatFragShader_->Release();
if (flatVertShader_)
flatVertShader_->Release();
if (samplerNearest_)
samplerNearest_->Release();
}
void GPUDriverTestScreen::CreateViews() {
using namespace Draw;
if (!samplerNearest_) {
DrawContext *draw = screenManager()->getDrawContext();
SamplerStateDesc nearestDesc{};
samplerNearest_ = draw->CreateSamplerState(nearestDesc);
}
// Don't bother with views for now.
using namespace UI;
auto di = GetI18NCategory("Dialog");
@@ -92,6 +306,7 @@ void GPUDriverTestScreen::CreateViews() {
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 30.0f, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, false));
anchor->Add(tabHolder_);
tabHolder_->AddTab("Discard", new LinearLayout(ORIENT_VERTICAL));
tabHolder_->AddTab("Shader", new LinearLayout(ORIENT_VERTICAL));
Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(100, WRAP_CONTENT, 10, NONE, NONE, 10));
back->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
@@ -230,9 +445,6 @@ void GPUDriverTestScreen::DiscardTest() {
depthTestLessEqual->Release();
depthTestGreater->Release();
rasterNoCull->Release();
SamplerStateDesc nearestDesc{};
samplerNearest_ = draw->CreateSamplerState(nearestDesc);
}
UIContext &dc = *screenManager()->getUIContext();
@@ -253,11 +465,6 @@ void GPUDriverTestScreen::DiscardTest() {
{false, false, true, true},
};
uint32_t textColorOK = 0xFF30FF30;
uint32_t textColorBAD = 0xFF3030FF;
uint32_t bgColorOK = 0xFF106010;
uint32_t bgColorBAD = 0xFF101060;
// Don't want any fancy font texture stuff going on here, so use FLAG_DYNAMIC_ASCII everywhere!
// We draw the background at Z=0.5 and the text at Z=0.9.
@@ -328,11 +535,111 @@ void GPUDriverTestScreen::DiscardTest() {
dc.Flush();
}
void GPUDriverTestScreen::ShaderTest() {
using namespace Draw;
UIContext &dc = *screenManager()->getUIContext();
Draw::DrawContext *draw = dc.GetDrawContext();
if (!adrenoLogicDiscardPipeline_) {
adrenoLogicDiscardFragShader_ = CreateShader(draw, ShaderStage::Fragment, fsAdrenoLogicTest);
adrenoLogicDiscardVertShader_ = CreateShader(draw, ShaderStage::Vertex, vsAdrenoLogicTest);
flatFragShader_ = CreateShader(draw, ShaderStage::Fragment, fsFlat);
flatVertShader_ = CreateShader(draw, ShaderStage::Vertex, vsFlat);
InputLayout *inputLayout = ui_draw2d.CreateInputLayout(draw);
// No blending used.
BlendState *blendOff = draw->CreateBlendState({ false, 0xF });
// No depth or stencil, we only use discard in this test.
DepthStencilStateDesc dsDesc{};
dsDesc.depthTestEnabled = false;
dsDesc.depthWriteEnabled = false;
DepthStencilState *depthStencilOff = draw->CreateDepthStencilState(dsDesc);
RasterState *rasterNoCull = draw->CreateRasterState({});
PipelineDesc adrenoLogicDiscardDesc{
Primitive::TRIANGLE_LIST,
{ adrenoLogicDiscardVertShader_, adrenoLogicDiscardFragShader_ },
inputLayout, depthStencilOff, blendOff, rasterNoCull, &vsColBufDesc,
};
adrenoLogicDiscardPipeline_ = draw->CreateGraphicsPipeline(adrenoLogicDiscardDesc);
PipelineDesc flatDesc{
Primitive::TRIANGLE_LIST,
{ flatVertShader_, flatFragShader_ },
inputLayout, depthStencilOff, blendOff, rasterNoCull, &vsColBufDesc,
};
flatShadingPipeline_ = draw->CreateGraphicsPipeline(flatDesc);
inputLayout->Release();
blendOff->Release();
depthStencilOff->Release();
rasterNoCull->Release();
}
Bounds layoutBounds = dc.GetLayoutBounds();
dc.Begin();
dc.SetFontScale(1.0f, 1.0f);
std::string apiName = screenManager()->getDrawContext()->GetInfoString(InfoField::APINAME);
std::string vendor = screenManager()->getDrawContext()->GetInfoString(InfoField::VENDORSTRING);
std::string driver = screenManager()->getDrawContext()->GetInfoString(InfoField::DRIVER);
dc.DrawText(apiName.c_str(), layoutBounds.centerX(), 20, 0xFFFFFFFF, ALIGN_CENTER);
dc.DrawText(vendor.c_str(), layoutBounds.centerX(), 60, 0xFFFFFFFF, ALIGN_CENTER);
dc.DrawText(driver.c_str(), layoutBounds.centerX(), 100, 0xFFFFFFFF, ALIGN_CENTER);
dc.Flush();
float y = layoutBounds.y + 150;
float x = layoutBounds.x + 10;
dc.Begin();
dc.DrawText("Adreno logic", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
dc.Flush();
float testW = 170.f;
Bounds bounds = { x + 200, y, testW, 70 };
// Draw rectangle that should result in the bg
dc.Begin();
dc.FillRect(UI::Drawable(bgColorOK), bounds);
dc.Flush();
// Draw text on it using the shader.
dc.BeginPipeline(adrenoLogicDiscardPipeline_, samplerNearest_);
dc.DrawTextRect("TEST OK", bounds, textColorOK, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII);
dc.Flush();
y += 100;
dc.Begin();
dc.DrawText("Flat shaded tex", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
dc.DrawText("(TEST OK if logo but no gradient!)", x + 400, y, 0xFFFFFFFF, ALIGN_LEFT);
dc.Flush();
bounds = { x + 200, y, 100, 100 };
// Draw rectangle that should be flat shaded
dc.BeginPipeline(flatShadingPipeline_, samplerNearest_);
// There is a "provoking vertex" difference here between GL and Vulkan when using flat shading. One gets one color, one gets the other.
// Wherever possible we should reconfigure the GL provoking vertex to match Vulkan, probably.
dc.DrawImageVGradient(ImageID("I_ICON"), 0xFFFFFFFF, 0xFF808080, bounds);
dc.Flush();
}
void GPUDriverTestScreen::render() {
using namespace Draw;
UIScreen::render();
if (tabHolder_->GetCurrentTab() == 0) {
switch (tabHolder_->GetCurrentTab()) {
case 0:
DiscardTest();
break;
case 1:
ShaderTest();
break;
}
}
+18 -1
View File
@@ -20,6 +20,13 @@ public:
private:
void DiscardTest();
void ShaderTest();
// Common objects
Draw::SamplerState *samplerNearest_ = nullptr;
// Discard/depth/stencil stuff
// ===========================
Draw::ShaderModule *discardFragShader_ = nullptr;
Draw::Pipeline *discardWriteDepthStencil_ = nullptr;
@@ -38,6 +45,16 @@ private:
Draw::Pipeline *drawTestDepthLessEqual_ = nullptr;
Draw::Pipeline *drawTestDepthGreater_ = nullptr;
Draw::SamplerState *samplerNearest_ = nullptr;
// Shader tests
// ============
Draw::Pipeline *adrenoLogicDiscardPipeline_ = nullptr;
Draw::ShaderModule *adrenoLogicDiscardFragShader_ = nullptr;
Draw::ShaderModule *adrenoLogicDiscardVertShader_ = nullptr;
Draw::Pipeline *flatShadingPipeline_ = nullptr;
Draw::ShaderModule *flatFragShader_ = nullptr;
Draw::ShaderModule *flatVertShader_ = nullptr;
UI::TabHolder *tabHolder_ = nullptr;
};
+1 -1
View File
@@ -796,7 +796,7 @@ void GameSettingsScreen::CreateViews() {
networkingSettingsScroll->Add(networkingSettings);
tabHolder->AddTab(ms->T("Networking"), networkingSettingsScroll);
networkingSettings->Add(new ItemHeader(n->T("Networking")));
networkingSettings->Add(new ItemHeader(ms->T("Networking")));
networkingSettings->Add(new Choice(n->T("Adhoc Multiplayer forum")))->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides);
+98 -55
View File
@@ -15,8 +15,9 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cmath>
#include <algorithm>
#include <cmath>
#include <sstream>
#include "ppsspp_config.h"
@@ -33,6 +34,7 @@
#include "Common/Data/Encoding/Utf8.h"
#include "Common/File/PathBrowser.h"
#include "Common/Math/curves.h"
#include "Common/Net/URL.h"
#include "Common/File/FileUtil.h"
#include "Common/TimeUtil.h"
#include "Common/StringUtils.h"
@@ -63,13 +65,6 @@
#include "Core/HLE/sceDisplay.h"
#include "Core/HLE/sceUmd.h"
#ifdef _WIN32
// Unfortunate, for undef DrawText...
#include "Common/CommonWindows.h"
#endif
#include <sstream>
bool MainScreen::showHomebrewTab = false;
bool LaunchFile(ScreenManager *screenManager, std::string path) {
@@ -96,8 +91,13 @@ bool LaunchFile(ScreenManager *screenManager, std::string path) {
static bool IsTempPath(const std::string &str) {
std::string item = str;
#ifdef _WIN32
// Normalize slashes.
item = ReplaceAll(str, "/", "\\");
#endif
const auto testPath = [&](std::string temp) {
std::vector<std::string> tempPaths = System_GetPropertyStringVec(SYSPROP_TEMP_DIRS);
for (auto temp : tempPaths) {
#ifdef _WIN32
temp = ReplaceAll(temp, "/", "\\");
if (!temp.empty() && temp[temp.size() - 1] != '\\')
@@ -106,37 +106,9 @@ static bool IsTempPath(const std::string &str) {
if (!temp.empty() && temp[temp.size() - 1] != '/')
temp += "/";
#endif
return startsWith(item, temp);
};
const auto testCPath = [&](const char *temp) {
if (temp && temp[0])
return testPath(temp);
return false;
};
#ifdef _WIN32
// Normalize slashes.
item = ReplaceAll(str, "/", "\\");
std::wstring tempPath(MAX_PATH, '\0');
size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
if (sz >= tempPath.size()) {
tempPath.resize(sz);
sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
if (startsWith(item, temp))
return true;
}
// Need to resize off the null terminator either way.
tempPath.resize(sz);
if (testPath(ConvertWStringToUTF8(tempPath)))
return true;
#endif
if (testCPath(getenv("TMPDIR")))
return true;
if (testCPath(getenv("TMP")))
return true;
if (testCPath(getenv("TEMP")))
return true;
return false;
}
@@ -540,13 +512,29 @@ UI::EventReturn GameBrowser::LastClick(UI::EventParams &e) {
return UI::EVENT_DONE;
}
UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) {
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) || defined(USING_QT_UI) || defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP)
if (System_GetPropertyBool(SYSPROP_HAS_FOLDER_BROWSER)) {
System_SendMessage("browse_folder", "");
} else {
SetPath(g_Config.memStickDirectory);
UI::EventReturn GameBrowser::BrowseClick(UI::EventParams &e) {
System_SendMessage("browse_folder", "");
return UI::EVENT_DONE;
}
UI::EventReturn GameBrowser::StorageClick(UI::EventParams &e) {
std::vector<std::string> storageDirs = System_GetPropertyStringVec(SYSPROP_ADDITIONAL_STORAGE_DIRS);
if (storageDirs.empty()) {
// Shouldn't happen - this button shouldn't be clickable.
return UI::EVENT_DONE;
}
if (storageDirs.size() == 1) {
SetPath(storageDirs[0]);
} else {
// TODO: We should popup a dialog letting the user choose one.
SetPath(storageDirs[0]);
}
return UI::EVENT_DONE;
}
UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) {
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) || defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP)
SetPath(g_Config.memStickDirectory);
#else
SetPath(getenv("HOME"));
#endif
@@ -656,10 +644,12 @@ void GameBrowser::Refresh() {
if (browseFlags_ & BrowseFlags::NAVIGATE) {
topBar->Add(new Spacer(2.0f));
topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f)));
topBar->Add(new Choice(ImageID("I_HOME"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick);
if (System_GetPropertyBool(SYSPROP_HAS_ADDITIONAL_STORAGE)) {
topBar->Add(new Choice(ImageID("I_SDCARD"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::StorageClick);
}
if (System_GetPropertyBool(SYSPROP_HAS_FOLDER_BROWSER)) {
topBar->Add(new Choice(mm->T("Browse", "Browse..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick);
} else {
topBar->Add(new Choice(mm->T("Home"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick);
topBar->Add(new Choice(mm->T("Browse", "Browse..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::BrowseClick);
}
} else {
topBar->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f)));
@@ -762,6 +752,7 @@ void GameBrowser::Refresh() {
gameList_->Add(new DirButton("..", *gridStyle_, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))->
OnClick.Handle(this, &GameBrowser::NavigateClick);
}
// Add any pinned paths before other directories.
auto pinnedPaths = GetPinnedPaths();
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
@@ -1073,16 +1064,26 @@ void MainScreen::CreateViews() {
//sprintf(versionString, "%s", PPSSPP_GIT_VERSION);
sprintf(versionString, "Luna Edition");
rightColumnItems->SetSpacing(0.0f);
LinearLayout *logos = new LinearLayout(ORIENT_HORIZONTAL);
logos->Add(new ImageView(ImageID("I_ICON"), IS_DEFAULT, new AnchorLayoutParams(64, 64, 10, 10, NONE, NONE, false)));
logos->Add(new ImageView(ImageID("I_LOGO"), IS_DEFAULT, new LinearLayoutParams(Margins(-12, 0, 0, 0))));
AnchorLayout *logos = new AnchorLayout(new AnchorLayoutParams(FILL_PARENT, 60.0f, false));
logos->Add(new ImageView(ImageID("I_ICON"), IS_DEFAULT, new AnchorLayoutParams(64, 64, 0, 0, NONE, NONE, false)));
logos->Add(new ImageView(ImageID("I_LOGO"), IS_DEFAULT, new AnchorLayoutParams(180, 64, 64, -5.0f, NONE, NONE, false)));
#if !defined(MOBILE_DEVICE)
if (!g_Config.bFullScreen) {
fullscreenButton_ = logos->Add(new Button(ImageID(g_Config.bFullScreen ? "I_RESTORE" : "I_FULLSCREEN"), new AnchorLayoutParams(48, 48, NONE, 0, 0, NONE, false)));
fullscreenButton_->OnClick.Handle(this, &MainScreen::OnFullScreenToggle);
}
#endif
rightColumnItems->Add(logos);
TextView *ver = rightColumnItems->Add(new TextView(versionString, new LinearLayoutParams(Margins(70, -6, 0, 0))));
ver->SetSmall(true);
ver->SetClip(false);
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP)
rightColumnItems->Add(new Choice(mm->T("Load","Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile);
#endif
if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) {
rightColumnItems->Add(new Choice(mm->T("Load", "Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile);
}
rightColumnItems->Add(new Choice(mm->T("Game Settings", "Settings")))->OnClick.Handle(this, &MainScreen::OnGameSettings);
rightColumnItems->Add(new Choice(mm->T("Credits")))->OnClick.Handle(this, &MainScreen::OnCredits);
if (!g_Config.bSimpleUIhide)
@@ -1180,9 +1181,38 @@ void MainScreen::sendMessage(const char *message, const char *value) {
LaunchFile(screenManager(), std::string(value));
}
if (!strcmp(message, "browse_folderSelect")) {
std::string filename;
#if PPSSPP_PLATFORM(ANDROID)
// Hacky way to get a normal path from a Android Storage Framework path.
// Is not gonna work forever, but ship-hack for 1.11.
std::string url = value;
const char *prefix = "content://com.android.externalstorage.documents/tree/";
const char *primaryPrefix = "/storage/primary/";
if (startsWith(url, prefix)) {
url = UriDecode(url.substr(strlen(prefix)));
size_t colonPos = url.find(":");
if (colonPos != std::string::npos) {
url[colonPos] = '/';
}
url = "/storage/" + url;
if (startsWith(url, primaryPrefix)) {
url = g_Config.memStickDirectory + url.substr(strlen(primaryPrefix));
}
INFO_LOG(SYSTEM, "Translated '%s' into '%s'", value, url.c_str());
} else {
// It's not gonna work.
// TODO: Show an error message?
INFO_LOG(SYSTEM, "Failed to parse content string: '%s'", value);
return;
}
filename = url;
#else
filename = value;
#endif
INFO_LOG(SYSTEM, "Got folder: '%s'", filename.c_str());
int tab = tabHolder_->GetCurrentTab();
if (tab >= 0 && tab < (int)gameBrowsers_.size()) {
gameBrowsers_[tab]->SetPath(value);
gameBrowsers_[tab]->SetPath(filename);
}
}
}
@@ -1215,6 +1245,17 @@ UI::EventReturn MainScreen::OnLoadFile(UI::EventParams &e) {
return UI::EVENT_DONE;
}
UI::EventReturn MainScreen::OnFullScreenToggle(UI::EventParams &e) {
if (fullscreenButton_) {
fullscreenButton_->SetImageID(ImageID(!g_Config.bFullScreen ? "I_RESTORE" : "I_FULLSCREEN"));
}
#if !defined(MOBILE_DEVICE)
g_Config.bFullScreen = !g_Config.bFullScreen;
System_SendMessage("toggle_fullscreen", "");
#endif
return UI::EVENT_DONE;
}
void MainScreen::DrawBackground(UIContext &dc) {
UIScreenWithBackground::DrawBackground(dc);
if (highlightedGamePath_.empty() && prevHighlightedGamePath_.empty()) {
@@ -1270,6 +1311,7 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, f
}
UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) {
g_Config.Save("MainScreen::OnGameSelected");
#ifdef _WIN32
std::string path = ReplaceAll(e.s, "\\", "/");
#else
@@ -1324,6 +1366,7 @@ UI::EventReturn MainScreen::OnGameHighlight(UI::EventParams &e) {
}
UI::EventReturn MainScreen::OnGameSelectedInstant(UI::EventParams &e) {
g_Config.Save("MainScreen::OnGameSelectedInstant");
#ifdef _WIN32
std::string path = ReplaceAll(e.s, "\\", "/");
#else
+4
View File
@@ -68,6 +68,8 @@ private:
UI::EventReturn NavigateClick(UI::EventParams &e);
UI::EventReturn LayoutChange(UI::EventParams &e);
UI::EventReturn LastClick(UI::EventParams &e);
UI::EventReturn BrowseClick(UI::EventParams &e);
UI::EventReturn StorageClick(UI::EventParams &e);
UI::EventReturn HomeClick(UI::EventParams &e);
UI::EventReturn PinToggleClick(UI::EventParams &e);
UI::EventReturn GridSettingsClick(UI::EventParams &e);
@@ -124,9 +126,11 @@ protected:
UI::EventReturn OnDownloadUpgrade(UI::EventParams &e);
UI::EventReturn OnDismissUpgrade(UI::EventParams &e);
UI::EventReturn OnAllowStorage(UI::EventParams &e);
UI::EventReturn OnFullScreenToggle(UI::EventParams &e);
UI::LinearLayout *upgradeBar_ = nullptr;
UI::TabHolder *tabHolder_ = nullptr;
UI::Button *fullscreenButton_ = nullptr;
std::string restoreFocusGamePath_;
std::vector<GameBrowser *> gameBrowsers_;
+5
View File
@@ -75,6 +75,7 @@
#include "Common/MemArena.h"
#include "Common/GraphicsContext.h"
#include "Common/OSVersion.h"
#include "Common/GPU/ShaderTranslation.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
@@ -438,6 +439,8 @@ static void ClearFailedGPUBackends() {
void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir) {
net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times.
ShaderTranslationInit();
InitFastMath(cpu_info.bNEON);
SetupAudioFormats();
@@ -1404,6 +1407,8 @@ void NativeShutdown() {
g_Discord.Shutdown();
ShaderTranslationShutdown();
// Avoid shutting this down when restarting core.
if (!restarting)
LogManager::Shutdown();
+4
View File
@@ -399,6 +399,8 @@
<ClInclude Include="..\..\Core\Debugger\WebSocket\GPUBufferSubscriber.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\GPURecordSubscriber.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\HLESubscriber.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\InputBroadcaster.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\InputSubscriber.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\MemorySubscriber.h" />
<ClInclude Include="..\..\Core\Debugger\WebSocket\SteppingBroadcaster.h" />
@@ -629,6 +631,8 @@
<ClCompile Include="..\..\Core\Debugger\WebSocket\GPUBufferSubscriber.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\GPURecordSubscriber.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\HLESubscriber.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\InputBroadcaster.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\InputSubscriber.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\MemorySubscriber.cpp" />
<ClCompile Include="..\..\Core\Debugger\WebSocket\SteppingBroadcaster.cpp" />
+12
View File
@@ -682,6 +682,12 @@
<ClCompile Include="..\..\Core\Debugger\WebSocket\HLESubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="..\..\Core\Debugger\WebSocket\InputBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="..\..\Core\Debugger\WebSocket\InputSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
@@ -1488,6 +1494,12 @@
<ClInclude Include="..\..\Core\Debugger\WebSocket\HLESubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="..\..\Core\Debugger\WebSocket\InputBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="..\..\Core\Debugger\WebSocket\InputSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
+36 -2
View File
@@ -356,6 +356,35 @@ std::string System_GetProperty(SystemProperty prop) {
}
}
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
std::vector<std::string> result;
switch (prop) {
case SYSPROP_TEMP_DIRS:
{
std::wstring tempPath(MAX_PATH, '\0');
size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
if (sz >= tempPath.size()) {
tempPath.resize(sz);
sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
}
// Need to resize off the null terminator either way.
tempPath.resize(sz);
result.push_back(ConvertWStringToUTF8(tempPath));
if (getenv("TMPDIR") && strlen(getenv("TMPDIR")) != 0)
result.push_back(getenv("TMPDIR"));
if (getenv("TMP") && strlen(getenv("TMP")) != 0)
result.push_back(getenv("TMP"));
if (getenv("TEMP") && strlen(getenv("TEMP")) != 0)
result.push_back(getenv("TEMP"));
return result;
}
default:
return result;
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
@@ -440,11 +469,16 @@ void System_SendMessage(const char *command, const char *parameter) {
});
} else if (!strcmp(command, "toggle_fullscreen")) {
auto view = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
bool flag = !view->IsFullScreenMode;
if (strcmp(parameter, "0") == 0) {
view->ExitFullScreenMode();
flag = false;
} else if (strcmp(parameter, "1") == 0){
flag = true;
}
else if (strcmp(parameter, "1") == 0){
if (flag) {
view->TryEnterFullScreenMode();
} else {
view->ExitFullScreenMode();
}
}
}
+6
View File
@@ -132,6 +132,7 @@ namespace MainWindow
static bool inFullscreenResize = false;
static bool inResizeMove = false;
static bool hasFocus = true;
static bool g_isFullscreen = false;
// gross hack
bool noFocusPause = false; // TOGGLE_PAUSE state to override pause on lost focus
@@ -355,6 +356,7 @@ namespace MainWindow
::SetMenu(hWnd, goingFullscreen ? NULL : menu);
g_Config.bFullScreen = goingFullscreen;
g_isFullscreen = goingFullscreen;
g_IgnoreWM_SIZE = false;
@@ -1069,4 +1071,8 @@ namespace MainWindow
PostMessage(hwndMain, WM_USER_TOGGLE_FULLSCREEN, fullscreen, 0);
}
bool IsFullscreen() {
return g_isFullscreen;
}
} // namespace
+1
View File
@@ -77,6 +77,7 @@ namespace MainWindow
void ToggleFullscreen(HWND hWnd, bool goingFullscreen);
void Minimize();
void SendToggleFullscreen(bool fullscreen); // To be used off-thread
bool IsFullscreen();
void ToggleDebugConsoleVisibility();
void SetInternalResolution(int res = -1);
void SetWindowSize(int zoom);
+30 -1
View File
@@ -216,6 +216,35 @@ std::string System_GetProperty(SystemProperty prop) {
}
}
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
std::vector<std::string> result;
switch (prop) {
case SYSPROP_TEMP_DIRS:
{
std::wstring tempPath(MAX_PATH, '\0');
size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
if (sz >= tempPath.size()) {
tempPath.resize(sz);
sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
}
// Need to resize off the null terminator either way.
tempPath.resize(sz);
result.push_back(ConvertWStringToUTF8(tempPath));
if (getenv("TMPDIR") && strlen(getenv("TMPDIR")) != 0)
result.push_back(getenv("TMPDIR"));
if (getenv("TMP") && strlen(getenv("TMP")) != 0)
result.push_back(getenv("TMP"));
if (getenv("TEMP") && strlen(getenv("TEMP")) != 0)
result.push_back(getenv("TEMP"));
return result;
}
default:
return result;
}
}
// Ugly!
extern WindowsAudioBackend *winAudioBackend;
@@ -323,7 +352,7 @@ void System_SendMessage(const char *command, const char *parameter) {
} else if (!strcmp(command, "bgImage_browse")) {
MainWindow::BrowseBackground();
} else if (!strcmp(command, "toggle_fullscreen")) {
bool flag = !g_Config.bFullScreen;
bool flag = !MainWindow::IsFullscreen();
if (strcmp(parameter, "0") == 0) {
flag = false;
} else if (strcmp(parameter, "1") == 0) {
Binary file not shown.
Binary file not shown.
+9 -12
View File
@@ -17,6 +17,7 @@ ARCH_FILES := \
$(SRC)/Common/x64Emitter.cpp \
$(SRC)/Common/x64Analyzer.cpp \
$(SRC)/Common/CPUDetect.cpp \
$(SRC)/Common/Math/fast/fast_matrix_sse.c \
$(SRC)/Common/Thunk.cpp \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
$(SRC)/Core/MIPS/x86/CompBranch.cpp \
@@ -39,6 +40,7 @@ ARCH_FILES := \
$(SRC)/Common/x64Emitter.cpp \
$(SRC)/Common/x64Analyzer.cpp \
$(SRC)/Common/CPUDetect.cpp \
$(SRC)/Common/Math/fast/fast_matrix_sse.c \
$(SRC)/Common/Thunk.cpp \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
$(SRC)/Core/MIPS/x86/CompBranch.cpp \
@@ -62,6 +64,7 @@ ARCH_FILES := \
$(SRC)/Common/ArmEmitter.cpp \
$(SRC)/Common/ArmCPUDetect.cpp \
$(SRC)/Common/ColorConvNEON.cpp.neon \
$(SRC)/Common/Math/fast/fast_matrix_neon.S.neon \
$(SRC)/Core/MIPS/ARM/ArmCompALU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompBranch.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompFPU.cpp \
@@ -76,6 +79,9 @@ ARCH_FILES := \
$(SRC)/Core/MIPS/ARM/ArmRegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderArm.cpp \
$(SRC)/ext/disarm.cpp \
$(SRC)/ext/libpng17/arm/arm_init.c \
$(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c \
$(SRC)/ext/libpng17/arm/filter_neon.S.neon \
ArmEmitterTest.cpp
endif
@@ -407,6 +413,8 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/GPURecordSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/InputBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/InputSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/MemorySubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \
@@ -620,18 +628,6 @@ LOCAL_SRC_FILES := \
ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a)
LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
$(SRC)/Common/Math/fast/fast_matrix_neon.S.neon \
$(SRC)/ext/libpng17/arm/arm_init.c \
$(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c \
$(SRC)/ext/libpng17/arm/filter_neon.S.neon
else ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
$(SRC)/Common/Math/fast/fast_matrix_sse.c
else ifeq ($(TARGET_ARCH_ABI),x86_64)
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
$(SRC)/Common/Math/fast/fast_matrix_sse.c
endif
ifneq ($(SKIPAPP),1)
@@ -684,6 +680,7 @@ ifeq ($(UNITTEST),1)
LOCAL_MODULE := ppsspp_unittest
LOCAL_SRC_FILES := \
$(SRC)/unittest/JitHarness.cpp \
$(SRC)/unittest/TestShaderGenerators.cpp \
$(SRC)/unittest/TestVertexJit.cpp \
$(TESTARMEMITTER_FILE) \
$(SRC)/unittest/UnitTest.cpp
+43 -7
View File
@@ -13,6 +13,8 @@
#include <thread>
#include <atomic>
#include <android/log.h>
#ifndef _MSC_VER
#include <jni.h>
#include <android/native_window_jni.h>
@@ -122,6 +124,8 @@ std::string langRegion;
std::string mogaVersion;
std::string boardName;
std::vector<std::string> g_additionalStorageDirs;
static float left_joystick_x_async;
static float left_joystick_y_async;
static float right_joystick_x_async;
@@ -364,6 +368,17 @@ std::string System_GetProperty(SystemProperty prop) {
}
}
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
switch (prop) {
case SYSPROP_ADDITIONAL_STORAGE_DIRS:
return g_additionalStorageDirs;
case SYSPROP_TEMP_DIRS:
default:
return std::vector<std::string>();
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_SYSTEMVERSION:
@@ -410,10 +425,17 @@ bool System_GetPropertyBool(SystemProperty prop) {
return androidVersion >= 23; // 6.0 Marshmallow introduced run time permissions.
case SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE:
return sustainedPerfSupported; // 7.0 introduced sustained performance mode as an optional feature.
case SYSPROP_HAS_ADDITIONAL_STORAGE:
return !g_additionalStorageDirs.empty();
case SYSPROP_HAS_BACK_BUTTON:
return true;
case SYSPROP_HAS_IMAGE_BROWSER:
return true;
case SYSPROP_HAS_FILE_BROWSER:
return false; // We kind of have but needs more work.
case SYSPROP_HAS_FOLDER_BROWSER:
// Uses OPEN_DOCUMENT_TREE to let you select a folder.
return androidVersion >= 21;
case SYSPROP_APP_GOLD:
#ifdef GOLD
return true;
@@ -530,16 +552,19 @@ static void parse_args(std::vector<std::string> &args, const std::string value)
}
}
// Need to use raw Android logging before NativeInit.
#define EARLY_LOG(...) __android_log_print(ANDROID_LOG_INFO, "PPSSPP", __VA_ARGS__)
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
(JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath,
jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
jstring jdataDir, jstring jexternalStorageDir, jstring jadditionalStorageDirs, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
jint jAndroidVersion, jstring jboard) {
setCurrentThreadName("androidInit");
// Makes sure we get early permission grants.
ProcessFrameCommands(env);
INFO_LOG(SYSTEM, "NativeApp.init() -- begin");
EARLY_LOG("NativeApp.init() -- begin");
PROFILE_INIT();
renderer_inited = false;
@@ -559,9 +584,18 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
systemName = GetJavaString(env, jmodel);
langRegion = GetJavaString(env, jlangRegion);
INFO_LOG(SYSTEM, "NativeApp.init(): device name: '%s'", systemName.c_str());
EARLY_LOG("NativeApp.init(): device name: '%s'", systemName.c_str());
std::string externalStorageDir = GetJavaString(env, jexternalStorageDir);
std::string additionalStorageDirsString = GetJavaString(env, jadditionalStorageDirs);
if (!additionalStorageDirsString.empty()) {
SplitString(additionalStorageDirsString, ':', g_additionalStorageDirs);
for (auto &str : g_additionalStorageDirs) {
EARLY_LOG("Additional storage: %s", str.c_str());
}
}
std::string externalDir = GetJavaString(env, jexternalDir);
std::string user_data_path = GetJavaString(env, jdataDir);
if (user_data_path.size() > 0)
user_data_path += "/";
@@ -570,8 +604,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
std::string cacheDir = GetJavaString(env, jcacheDir);
std::string buildBoard = GetJavaString(env, jboard);
boardName = buildBoard;
INFO_LOG(SYSTEM, "NativeApp.init(): External storage path: %s", externalDir.c_str());
INFO_LOG(SYSTEM, "NativeApp.init(): Launch shortcut parameter: %s", shortcut_param.c_str());
EARLY_LOG("NativeApp.init(): External storage path: %s", externalStorageDir.c_str());
EARLY_LOG("NativeApp.init(): Launch shortcut parameter: %s", shortcut_param.c_str());
std::string app_name;
std::string app_nice_name;
@@ -600,7 +634,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
}
}
NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str());
NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalStorageDir.c_str(), cacheDir.c_str());
// No need to use EARLY_LOG anymore.
retry:
// Now that we've loaded config, set javaGL.
@@ -25,6 +25,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
import android.os.Vibrator;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.InputType;
import android.util.Log;
@@ -46,6 +47,7 @@ import android.widget.Toast;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -98,7 +100,10 @@ public abstract class NativeActivity extends Activity {
// This is to avoid losing the game/menu state etc when we are just
// switched-away from or rotated etc.
private boolean shuttingDown;
private static final int RESULT_LOAD_IMAGE = 1;
private static final int RESULT_BROWSE_FILE = 2;
private static final int RESULT_OPEN_DOCUMENT_TREE = 3;
// Allow for multiple connected gamepads but just consider them the same for now.
// Actually this is not entirely true, see the code.
@@ -241,6 +246,108 @@ public abstract class NativeActivity extends Activity {
this.shortcutParam = ((shortcutParam == null) ? "" : shortcutParam);
}
// Unofficial hacks to get a list of SD cards that are not the main "external storage".
private static ArrayList<String> getSdCardPaths(final Context context) {
// Q is the last version that will support normal file access.
ArrayList<String> list = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
Log.i(TAG, "getSdCardPaths: Trying KitKat method");
list = getSdCardPaths19(context);
}
if (list == null) {
Log.i(TAG, "getSdCardPaths: Attempting fallback");
// Try another method.
String removableStoragePath;
list = new ArrayList<String>();
File fileList[] = new File("/storage/").listFiles();
if (fileList != null) {
for (File file : fileList) {
if (!file.getAbsolutePath().equalsIgnoreCase(Environment.getExternalStorageDirectory().getAbsolutePath()) && file.isDirectory() && file.canRead()) {
list.add(file.getAbsolutePath());
}
}
}
}
if (list == null) {
String[] varNames = { "EXTERNAL_SDCARD_STORAGE", "SECONDARY_STORAGE" };
for (String var : varNames) {
Log.i(TAG, "getSdCardPaths: Checking env " + var);
String secStore = System.getenv("SECONDARY_STORAGE");
if (secStore != null && secStore.length() > 0) {
list = new ArrayList<String>();
list.add(secStore);
break;
}
}
}
if (list == null) {
return new ArrayList<String>();
} else {
return list;
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private static ArrayList<String> getSdCardPaths19(final Context context)
{
final File[] externalCacheDirs = context.getExternalCacheDirs();
if (externalCacheDirs == null || externalCacheDirs.length==0)
return null;
if (externalCacheDirs.length == 1) {
if (externalCacheDirs[0] == null)
return null;
final String storageState = Environment.getStorageState(externalCacheDirs[0]);
if (!Environment.MEDIA_MOUNTED.equals(storageState))
return null;
if (Environment.isExternalStorageEmulated())
return null;
}
final ArrayList<String> result = new ArrayList<>();
if (externalCacheDirs.length == 1)
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
for (int i = 1; i < externalCacheDirs.length; ++i)
{
final File file = externalCacheDirs[i];
if (file == null)
continue;
final String storageState = Environment.getStorageState(file);
if (Environment.MEDIA_MOUNTED.equals(storageState)) {
String root = getRootOfInnerSdCardFolder(externalCacheDirs[i]);
if (root != null) {
result.add(root);
}
}
}
if (result.isEmpty())
return null;
return result;
}
/** Given any file/folder inside an sd card, this will return the path of the sd card */
private static String getRootOfInnerSdCardFolder(File file)
{
if (file == null)
return null;
final long totalSpace = file.getTotalSpace();
if (totalSpace <= 0) {
return null;
}
while (true) {
final File parentFile = file.getParentFile();
if (parentFile == null || !parentFile.canRead()) {
break;
}
if (parentFile.getTotalSpace() != totalSpace) {
break;
}
file = parentFile;
}
return file.getAbsolutePath();
}
public void Initialize() {
// Initialize audio classes. Do this here since detectOptimalAudioSettings()
// needs audioManager
@@ -292,9 +399,34 @@ public abstract class NativeActivity extends Activity {
isXperiaPlay = IsXperiaPlay();
String libraryDir = getApplicationLibraryDir(appInfo);
File sdcard = Environment.getExternalStorageDirectory();
String externalStorageDir = sdcard.getAbsolutePath();
String extStorageState = Environment.getExternalStorageState();
String extStorageDir = Environment.getExternalStorageDirectory().getAbsolutePath();
Log.i(TAG, "Ext storage: " + extStorageState + " " + extStorageDir);
String additionalStorageDirs = "";
try {
ArrayList<String> sdCards = getSdCardPaths(this);
// String.join doesn't exist on old devices (???).
StringBuilder s = new StringBuilder();
for (int i = 0; i < sdCards.size(); i++) {
String sdCard = sdCards.get(i);
Log.i(TAG, "SD card: " + sdCard);
s.append(sdCard);
if (i != sdCards.size() - 1) {
s.append(":");
}
}
additionalStorageDirs = s.toString();
}
catch (Exception e) {
Log.e(TAG, "Failed to get SD storage dirs: " + e.toString());
}
Log.i(TAG, "End of storage paths");
File filesDir = this.getFilesDir();
String dataDir = null;
if (filesDir != null) {
@@ -310,7 +442,7 @@ public abstract class NativeActivity extends Activity {
overrideShortcutParam = null;
NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate);
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD);
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, additionalStorageDirs, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD);
// Allow C++ to tell us to use JavaGL or not.
javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL"));
@@ -993,7 +1125,10 @@ public abstract class NativeActivity extends Activity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
if (resultCode != RESULT_OK || data == null) {
return;
}
if (requestCode == RESULT_LOAD_IMAGE) {
Uri selectedImage = data.getData();
if (selectedImage != null) {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
@@ -1004,6 +1139,20 @@ public abstract class NativeActivity extends Activity {
cursor.close();
NativeApp.sendMessage("bgImage_updated", picturePath);
}
} else if (requestCode == RESULT_BROWSE_FILE) {
Uri selectedFile = data.getData();
if (selectedFile != null) {
// NativeApp.sendMessage("br");
Log.i(TAG, "Browse file finished:" + selectedFile.toString());
}
} else if (requestCode == RESULT_OPEN_DOCUMENT_TREE) {
Uri selectedFile = data.getData();
if (selectedFile != null) {
// Convert URI to normal path. (This might not be possible in Android 12+)
String path = selectedFile.toString();
Log.i(TAG, "Browse folder finished: " + path);
NativeApp.sendMessage("browse_folderSelect", path);
}
}
}
@@ -1135,6 +1284,30 @@ public abstract class NativeActivity extends Activity {
Log.e(TAG, e.toString());
return false;
}
} else if (command.equals("browse_file")) {
try {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/octet-stream");
//intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
startActivityForResult(intent, RESULT_BROWSE_FILE);
} catch (Exception e) {
Log.e(TAG, e.toString());
return false;
}
} else if (command.equals("browse_folder")) {
try {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); // not yet used properly
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); // Only allow local folders.
startActivityForResult(intent, RESULT_OPEN_DOCUMENT_TREE);
return true;
} catch (Exception e) {
Log.e(TAG, e.toString());
return false;
}
} else if (command.equals("sharejpeg")) {
try {
Intent share = new Intent(Intent.ACTION_SEND);
+1 -1
View File
@@ -12,7 +12,7 @@ public class NativeApp {
public static final int DEVICE_TYPE_TV = 1;
public static final int DEVICE_TYPE_DESKTOP = 2;
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board);
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String additionalStorageDirs, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board);
public static native void audioInit();
public static native void audioShutdown();
public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate);
+111 -32
View File
@@ -520,12 +520,18 @@ ULUS10216 = true
ULES00629 = true
# Digimon Adventure
NPJH50686 = true
ULJS00541 = true
ULAS42340 = true
# Danganronpa
ULJS00337 = true
ULJS19060 = true
NPJH50372 = true
NPJH50515 = true
# Danganronpa Demo
NPJH90164 = true
# Super Danganronpa 2
ULJS00521 = true
ULJS00522 = true
NPJH50631 = true
# The Simpsons Game
ULUS10295 = true
@@ -537,19 +543,46 @@ ULES00976 = true
# Jeanne d'Arc
UCUS98700 = true
UCJS10048 = true
# Jeanne d'Arc Senkou Taikenban Demo
# Jeanne d'Arc Senkou Taikenban (Demo)
UCJX90019 = true
# Dante's Inferno
ULUS10469 = true
ULES01384 = true
ULJM05621 = true
NPJH50220 = true
ULJM05799 = true
ULKS46248 = true
# Test Drive Unlimited
ULUS10249 = true
ULES00637 = true
ULKS46126 = true
# Silent Hill: Shattered Memories
ULUS10450 = true
ULES01352 = true
ULJM05564 = true
NPJH50148 = true
ULAS42189 = true
# Silent Hill: Origins
ULUS10285 = true
ULES00869 = true
ULJM05281 = true
NPJH50051 = true
# Manhunt 2
ULUS10280 = true
ULES00756 = true
# LEGO Star Wars II: The Original Trilogy
ULUS10155 = true
ULES00479 = true
# LEGO Indiana Jones: The Original Adventures
ULUS10365 = true
ULES01086 = true
# LEGO Batman: The Videogame
ULUS10380 = true
ULES01151 = true
# TODO: There are many more.
[RequireBlockTransfer]
# Warn the user that the game will have issue graphic, if simulate block transfer is not enabled.
# Ys Seven need it to fix minimap. See issues #2928
ULUS10551 = true
ULJM05475 = true
NPEH00065 = true
NPJH50350 = true
ULJM08041 = true
NPEH00065 = true
# The Legend of Heroes: Trails in the Sky need it to fix graphical glitch in menu screen. See issues #8053
ULUS10540 = true
ULUS10578 = true
@@ -563,17 +596,10 @@ NPUH10197 = true
ULJS00394 = true
ULJS19068 = true
NPJH50518 = true
# TODO: Will add some games in the future
NPJH50686 = true # Digimon Adventures (JP, and English patches...)
ULJS00078 = true # MotoGP
ULUS10153 = true # MotoGP
UCES00373 = true # MotoGP
ULUS10551 = true # Ys Seven
ULJM05475 = true # Ys Seven JP
ULUS10549 = true # Ys Seven
NPEH00065 = true # Ys Seven
NPJH50350 = true # Ys Seven
ULJM08041 = true # Ys Seven
# Gradius Collection (see #13887)
ULUS10103 = true
ULES00381 = true
ULJM05091 = true
[DisableAccurateDepth]
# Midnight Club: LA Remix
@@ -627,8 +653,13 @@ NPJG90095 = true
NPEG90035 = true
NPUG70125 = true
# Tekken 6
ULES01376 = true
ULUS10466 = true
ULES01376 = true
ULJS00224 = true
NPUH10047 = true
ULAS42214 = true
ULJS19054 = true
NPJH50184 = true
[MGS2AcidHack]
ULUS10006 = true # Metal Gear Acid
@@ -686,16 +717,23 @@ NPJH90157 = true # Infinity demo
[BlockTransferAllowCreateFB]
NPJH50686 = true # Digimon Adventures (JP, and English patches...)
ULJS00078 = true # MotoGP
ULUS10153 = true # MotoGP
UCES00373 = true # MotoGP
ULUS10551 = true # Ys Seven
ULJM05475 = true # Ys Seven JP
ULUS10549 = true # Ys Seven
NPEH00065 = true # Ys Seven
NPJH50350 = true # Ys Seven
ULJM08041 = true # Ys Seven
# Digimon Adventure (JP and English patches)
NPJH50686 = true
ULJS00541 = true
ULAS42340 = true
# MotoGP
ULUS10153 = true
UCES00373 = true
ULJS00078 = true
UCAS40104 = true
# Ys Seven
ULUS10551 = true
ULJM05475 = true
ULJM05475 = true
NPEH00065 = true
NPJH50350 = true
# Burnout Legends
ULES00125 = true
@@ -723,11 +761,30 @@ ULKS46126 = true
# Naruto Shippuden: Ultimate Ninja Impact
ULUS10582 = true
ULES01537 = true
ULJS00390 = true
ULAS42297 = true
ULJS19071 = true
NPJH50435 = true
NPJH50435 = true
# Gradius Collection
ULUS10103 = true
ULES00381 = true
ULJM05091 = true
ULJM05222 = true
ULAS42040 = true
ULKS46064 = true
[IntraVRAMBlockTransferAllowCreateFB]
# Final Fantasy - Type 0
ULJM05900 = true
ULJM05901 = true
NPJH50443 = true
NPJH50444 = true
ULJM06217 = true
ULJM06218 = true
UCAS40346 = true
UCAS40347 = true
# Note! This whole flag is disabled temporarily by appending "Disabled" to its name). See 7914
[YugiohSaveFixDisabled]
@@ -848,8 +905,13 @@ ULES01287 = true
ULUS10433 = true
# Tekken 6
ULES01376 = true
ULUS10466 = true
ULES01376 = true
ULJS00224 = true
NPUH10047 = true
ULAS42214 = true
ULJS19054 = true
NPJH50184 = true
# Hot Wheels - Ultimate Racing (issue #12604, this is a workaround)
ULUS10239 = true
@@ -868,7 +930,6 @@ ULUS10287 = true # Super Collapse 3
ULES01027 = true # Super Collapse 3
ULUS10203 = true # Platypus
ULES01029 = true # Platypus
ULUS10201 = true # Luxor: The Wrath of Set
ULUS10227 = true # 7 Wonders of the Ancient World
ULES01037 = true # 7 Wonders of the Ancient World
@@ -879,6 +940,18 @@ ULUS10132 = true
# Homebrew Kurok (we don't normally add homebrew here but it has a distinctive GameID)
KURO00767 = true
# Scooby-Doo! Who's Watching Who?
ULUS10168 = true
ULES00571 = true
# Tron Evolution
ULUS10548 = true
ULES01494 = true
ULES01495 = true
# Atari Classics Evolved
ULUS10325 = true
[JitInvalidationHack]
# This is an absolutely awful hack that somehow prevents issues when clearing the JIT,
# if the game has copied code with EmuHack opcodes or something. Hopefully will be able
@@ -992,6 +1065,12 @@ ULAS42073 = true
ULJM05265 = true
ULJM05366 = true
# Kingdom Hearts (see #11223)
ULUS10505 = true
ULES01441 = true
ULJM05600 = true
ULJM05775 = true
[ShaderColorBitmask]
# Outrun 2006: Coast to Coast - issue #11358
ULES00262 = true
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 134 files, more files were not shown because too many files have changed in this diff. Show more