diff --git a/CMakeLists.txt b/CMakeLists.txt index dabf14e1f6..9e18f69f63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,16 @@ if(APPLE) endif() endif() +if (VITA) + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + if(DEFINED ENV{VITASDK}) + set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") + else() + message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") + endif() + endif() +endif() + if(LIBRETRO) project(flycast_libretro) else() @@ -111,7 +121,32 @@ endif() string(TIMESTAMP BUILD_TIMESTAMP UTC) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/core/version.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/core/version.h" @ONLY) -if(NINTENDO_SWITCH) +if(VITA) + include("${VITASDK}/share/vita.cmake" REQUIRED) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${VITASDK}/arm-vita-eabi/include/SDL2 -Wl,-q,--wrap,memcpy,--wrap,memset,--wrap,malloc,--wrap,memalign,--wrap,free,--wrap,calloc,--wrap,realloc -O3 -DNDEBUG -fno-optimize-sibling-calls -fsigned-char -fno-short-enums -marm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${VITASDK}/arm-vita-eabi/include/SDL2 -Wl,-q,--wrap,malloc,--wrap,memalign,--wrap,free,--wrap,calloc,--wrap,realloc -O3 -DNDEBUG -fno-optimize-sibling-calls -fsigned-char -fno-short-enums -marm") + + set(VITA_APP_NAME "Flycast") + set(VITA_TITLEID "FLYCASTDC") + set(VITA_VPKNAME "Flycast") + set(VITA_VERSION "01.00") + set(VITA_MKSFOEX_FLAGS "-d ATTRIBUTE2=12") + + set(USE_VULKAN OFF) + set(USE_GLES2 ON) + enable_language(ASM) + + add_executable(${PROJECT_NAME} core/emulator.cpp) + target_compile_definitions(${PROJECT_NAME} PRIVATE EGL_NO_PLATFORM_SPECIFIC_TYPES) + # target_compile_definitions(${PROJECT_NAME} PRIVATE TARGET_NO_EXCEPTIONS) + target_compile_definitions(${PROJECT_NAME} PRIVATE VITA _GNU_SOURCE) + target_compile_definitions(${PROJECT_NAME} PRIVATE GLES) + + # For proper fseek/ftell definition in external dependency "libchdr", doesn't impact anything else: + target_compile_definitions(${PROJECT_NAME} PRIVATE __PS3__) + +elseif(NINTENDO_SWITCH) set(USE_VULKAN OFF) enable_language(ASM) @@ -200,9 +235,11 @@ if(WINDOWS_STORE) set(USE_VULKAN OFF) endif() -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_EXTENSIONS OFF - LINK_FLAGS_RELEASE -s) +if(NOT VITA) + set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_EXTENSIONS OFF + LINK_FLAGS_RELEASE -s) +endif() if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /GR /GS-) if(WINDOWS_STORE) @@ -400,6 +437,9 @@ if(NOT LIBRETRO) if(USE_HOST_SDL) find_package(SDL2) endif() + if(VITA) + set(SDL2_FOUND 1) + endif() if(NOT SDL2_FOUND) add_subdirectory(core/deps/SDL EXCLUDE_FROM_ALL) set(SDL2_FOUND 1) @@ -411,7 +451,7 @@ if(NOT LIBRETRO) target_link_libraries(${PROJECT_NAME} PRIVATE SDL2::SDL2main) endif() - if((APPLE OR WIN32) AND TARGET SDL2::SDL2-static) + if((APPLE OR WIN32 OR VITA) AND TARGET SDL2::SDL2-static) target_link_libraries(${PROJECT_NAME} PRIVATE SDL2::SDL2-static) elseif(TARGET SDL2::SDL2) target_link_libraries(${PROJECT_NAME} PRIVATE SDL2::SDL2) @@ -510,6 +550,10 @@ if(NINTENDO_SWITCH AND USE_GLES) target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GLESV2) endif() +if(VITA) + target_link_libraries(${PROJECT_NAME} PRIVATE vitaGL vitashark SceShaccCg_stub SceShaccCgExt taihen_stub mathneon curl ssl crypto) +endif() + if(UNIX AND NOT APPLE AND NOT ANDROID) add_definitions( -DFLYCAST_DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}/" @@ -598,7 +642,7 @@ target_compile_definitions(nowide PRIVATE $<$:_DEFAULT_ target_link_libraries(${PROJECT_NAME} PRIVATE nowide::nowide) if(NOT MINIUPNP_FOUND) - if(NINTENDO_SWITCH) + if(NINTENDO_SWITCH OR VITA) target_compile_definitions(${PROJECT_NAME} PRIVATE FEAT_NO_MINIUPNPC) else() option(UPNPC_BUILD_SHARED "Build shared library" OFF) @@ -615,7 +659,7 @@ if(NOT MINIUPNP_FOUND) endif() endif() -if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH) +if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH OR VITA) option(ENABLE_COMMONCRYPTO "Enable use of CommonCrypto" OFF) option(ENABLE_GNUTLS "Enable use of GnuTLS" OFF) option(ENABLE_MBEDTLS "Enable use of mbed TLS" OFF) @@ -635,6 +679,32 @@ if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH) target_link_libraries(${PROJECT_NAME} PRIVATE zip) endif() +if(VITA) + target_link_libraries(${PROJECT_NAME} PRIVATE + -Wl,--whole-archive pthread -Wl,--no-whole-archive + stdc++ + SDL2 + kubridge_stub + ScePgf_stub + SceHid_stub + SceAppMgr_stub + SceAppUtil_stub + SceAudio_stub + SceAudioIn_stub + SceDisplay_stub + SceKernelDmacMgr_stub + SceIme_stub + ScePower_stub + SceSysmodule_stub + SceTouch_stub + SceMotion_stub + SceCommonDialog_stub + SceCtrl_stub + SceGxm_stub + SceVshBridge_stub + ) +endif() + if(WIN32) target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent) endif() @@ -1023,6 +1093,12 @@ if(WIN32) core/windows/fault_handler.cpp core/windows/unwind_info.cpp core/windows/win_vmem.cpp) +elseif(VITA) + target_sources(${PROJECT_NAME} PRIVATE + core/linux/common.cpp + core/linux/unwind_info.cpp + core/linux/vita_fault_handler.cpp + core/linux/vita_vmem.cpp) else() target_sources(${PROJECT_NAME} PRIVATE core/linux/common.cpp @@ -1702,7 +1778,7 @@ if(NOT LIBRETRO) ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/Frameworks/libvulkan.dylib) endif() endif() - elseif(UNIX OR NINTENDO_SWITCH) + elseif(UNIX OR NINTENDO_SWITCH OR VITA) if(NOT BUILD_TESTING) target_sources(${PROJECT_NAME} PRIVATE core/linux-dist/main.cpp) @@ -1787,6 +1863,19 @@ if(NINTENDO_SWITCH) endif() endif() +if(VITA) + vita_create_self(eboot.bin ${PROJECT_NAME} UNSAFE STRIPPED NOASLR) + vita_create_vpk(${VITA_VPKNAME}.vpk ${VITA_TITLEID} eboot.bin + VERSION ${VITA_VERSION} + NAME ${VITA_APP_NAME} + FILE ${CMAKE_SOURCE_DIR}/shell/vita/icon0.png sce_sys/icon0.png + ${CMAKE_SOURCE_DIR}/shell/vita/startup.png sce_sys/livearea/contents/startup.png + ${CMAKE_SOURCE_DIR}/shell/vita/bg.png sce_sys/livearea/contents/bg.png + ${CMAKE_SOURCE_DIR}/shell/vita/template.xml sce_sys/livearea/contents/template.xml + ${VPK_INCLUDES} + ) +endif() + if(IOS) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$-${CMAKE_OSX_SYSROOT}/Flycast.ipa" TYPE BIN) elseif(NINTENDO_SWITCH AND NOT LIBRETRO) diff --git a/core/build.h b/core/build.h index eddcecf137..17b9823624 100755 --- a/core/build.h +++ b/core/build.h @@ -105,7 +105,7 @@ #endif #if !defined(__ANDROID__) && !defined(TARGET_IPHONE) && !defined(TARGET_UWP) \ - && !defined(__SWITCH__) && !defined(LIBRETRO) && !defined(__NetBSD__) && !defined(__OpenBSD__) + && !defined(__SWITCH__) && !defined(LIBRETRO) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__vita__) #define NAOMI_MULTIBOARD #endif diff --git a/core/cfg/option.cpp b/core/cfg/option.cpp index 095f97f487..e737266b8a 100644 --- a/core/cfg/option.cpp +++ b/core/cfg/option.cpp @@ -26,6 +26,9 @@ namespace config { Option DynarecEnabled("Dynarec.Enabled", true); Option Sh4Clock("Sh4Clock", 200); +#ifdef __vita__ +Option DynarecSmcChecks("Dynarec.smcChecks", 0); +#endif // General @@ -38,8 +41,13 @@ Option AutoLoadState("Dreamcast.AutoLoadState"); Option AutoSaveState("Dreamcast.AutoSaveState"); Option SavestateSlot("Dreamcast.SavestateSlot"); Option ForceFreePlay("ForceFreePlay", true); +#ifdef __vita__ +Option FetchBoxart("FetchBoxart", false); +Option BoxartDisplayMode("BoxartDisplayMode", false); +#else Option FetchBoxart("FetchBoxart", true); Option BoxartDisplayMode("BoxartDisplayMode", true); +#endif // Sound @@ -81,6 +89,10 @@ Option Fog("rend.Fog", true); Option FloatVMUs("rend.FloatVMUs"); Option Rotate90("rend.Rotate90"); Option PerStripSorting("rend.PerStripSorting"); +#ifdef __vita__ +Option UseSimpleShaders("rend.UseSimpleShaders", true); +Option FastSorting("rend.FastSorting", true); +#endif #ifdef __APPLE__ Option DelayFrameSwapping("rend.DelayFrameSwapping", false); #else diff --git a/core/cfg/option.h b/core/cfg/option.h index 5a14874864..a5401ebd85 100644 --- a/core/cfg/option.h +++ b/core/cfg/option.h @@ -359,6 +359,11 @@ extern Option DynarecEnabled; #ifndef LIBRETRO extern Option Sh4Clock; #endif +#ifdef __vita__ +extern Option DynarecSmcChecks; +extern Option FastSorting; +extern Option UseSimpleShaders; +#endif // General diff --git a/core/deps/ggpo/lib/ggpo/ggpo_types.h b/core/deps/ggpo/lib/ggpo/ggpo_types.h index 44106ab134..3c2329a74a 100644 --- a/core/deps/ggpo/lib/ggpo/ggpo_types.h +++ b/core/deps/ggpo/lib/ggpo/ggpo_types.h @@ -56,7 +56,7 @@ class GGPOException : public std::runtime_error { */ #if defined(_WIN32) # include "platform_windows.h" -#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) +#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__vita__) # include "platform_linux.h" #else # error Unsupported platform diff --git a/core/deps/ggpo/lib/ggpo/platform_linux.cpp b/core/deps/ggpo/lib/ggpo/platform_linux.cpp index a49fe3fe46..439af9a609 100644 --- a/core/deps/ggpo/lib/ggpo/platform_linux.cpp +++ b/core/deps/ggpo/lib/ggpo/platform_linux.cpp @@ -4,7 +4,7 @@ * Use of this software is governed by the MIT license that can be found * in the LICENSE file. */ -#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) +#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__vita__) #include "platform_linux.h" #include diff --git a/core/deps/ggpo/lib/ggpo/platform_linux.h b/core/deps/ggpo/lib/ggpo/platform_linux.h index b838431357..652880282c 100644 --- a/core/deps/ggpo/lib/ggpo/platform_linux.h +++ b/core/deps/ggpo/lib/ggpo/platform_linux.h @@ -23,7 +23,7 @@ #include #ifdef __SWITCH__ #include "nswitch.h" -#else +#elif !defined(__vita__) #include #endif #include diff --git a/core/deps/imgui/backends/imgui_impl_opengl3.cpp b/core/deps/imgui/backends/imgui_impl_opengl3.cpp index 33fc24254e..c0d2a17069 100644 --- a/core/deps/imgui/backends/imgui_impl_opengl3.cpp +++ b/core/deps/imgui/backends/imgui_impl_opengl3.cpp @@ -90,6 +90,9 @@ bool ImGui_ImplOpenGL3_Init() // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. const char* glsl_version; +#ifdef __vita__ + glsl_version = ""; +#else if (theGLContext.isGLES()) glsl_version = "#version 100"; // OpenGL ES 2.0 else @@ -97,6 +100,7 @@ bool ImGui_ImplOpenGL3_Init() glsl_version = "#version 140"; // OpenGL 3.1 #else glsl_version = "#version 130"; // OpenGL 3.0 +#endif #endif IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); strcpy(g_GlslVersionString, glsl_version); @@ -252,8 +256,10 @@ static bool ImGui_ImplOpenGL3_CreateFontsTexture() glcache.BindTexture(GL_TEXTURE_2D, g_FontTexture); glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#if !defined(__vita__) if (theGLContext.getMajorVersion() >= 3) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier @@ -313,6 +319,43 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() { // Parse GLSL version string int glsl_version = 130; +#ifdef __vita__ + const GLchar* vertex_shader_glsl_120 = ""; + + const GLchar* vertex_shader_glsl_130 = + "uniform float4x4 ProjMtx;\n" + "void main(\n" + "float2 Position,\n" + "float2 UV,\n" + "float4 Color,\n" + "float2 out Frag_UV : TEXCOORD0,\n" + "float4 out Frag_Color : COLOR,\n" + "float4 out gl_Position : POSITION\n" + ") {\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = mul(float4(Position.xy, 0, 1), ProjMtx);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_300_es = ""; + + const GLchar* vertex_shader_glsl_410_core = ""; + + const GLchar* fragment_shader_glsl_120 = ""; + + const GLchar* fragment_shader_glsl_130 = + "uniform sampler2D tex;\n" + "float4 main(\n" + "float2 Frag_UV : TEXCOORD0,\n" + "float4 Frag_Color : COLOR\n" + ") {\n" + " return Frag_Color * tex2D(tex, Frag_UV);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_300_es = ""; + + const GLchar* fragment_shader_glsl_410_core = ""; +#else sscanf(g_GlslVersionString, "#version %d", &glsl_version); const GLchar* vertex_shader_glsl_120 = @@ -414,6 +457,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() "{\n" " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" "}\n"; +#endif // Select shaders matching our GLSL versions const GLchar* vertex_shader = NULL; @@ -463,7 +507,11 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() glDeleteShader(vert_handle); glDeleteShader(frag_handle); +#ifdef __vita__ + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "tex"); +#else g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); +#endif g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); diff --git a/core/deps/khronos/GL4/gl3w.c b/core/deps/khronos/GL4/gl3w.c index f6e0c98157..1de0986c23 100644 --- a/core/deps/khronos/GL4/gl3w.c +++ b/core/deps/khronos/GL4/gl3w.c @@ -88,7 +88,7 @@ static GL3WglProc get_proc(const char *proc) *(void **)(&res) = dlsym(libgl, proc); return res; } -#elif defined(__ANDROID__) || defined(__SWITCH__) +#elif defined(__ANDROID__) || defined(__SWITCH__) || defined(__vita__) #ifdef __ANDROID__ #include #endif @@ -149,6 +149,7 @@ static struct { static int parse_version(void) { +#ifndef __vita__ if (!glGetIntegerv) return GL3W_ERROR_INIT; @@ -157,6 +158,7 @@ static int parse_version(void) if (version.major < 3) return GL3W_ERROR_OPENGL_VERSION; +#endif return GL3W_OK; } @@ -182,11 +184,15 @@ int gl3wInit2(GL3WGetProcAddressProc proc) int gl3wIsSupported(int major, int minor) { +#ifdef __vita__ + return 1; +#else if (major < 3) return 0; if (version.major == major) return version.minor >= minor; return version.major >= major; +#endif } GL3WglProc gl3wGetProcAddress(const char *proc) diff --git a/core/deps/lzma/Alloc.c b/core/deps/lzma/Alloc.c index 30b499e5ff..4316040a85 100644 --- a/core/deps/lzma/Alloc.c +++ b/core/deps/lzma/Alloc.c @@ -283,7 +283,7 @@ const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; #define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align) -#if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32) +#if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32) && !defined(__vita__) #define USE_posix_memalign #endif diff --git a/core/deps/vixl/code-buffer-vixl.cc b/core/deps/vixl/code-buffer-vixl.cc index 2cfe8b71d3..b22ba3320d 100644 --- a/core/deps/vixl/code-buffer-vixl.cc +++ b/core/deps/vixl/code-buffer-vixl.cc @@ -25,10 +25,12 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef VIXL_CODE_BUFFER_MMAP +#ifndef __vita__ extern "C" { #include } #endif +#endif #include "code-buffer-vixl.h" #include "utils-vixl.h" diff --git a/core/emulator.cpp b/core/emulator.cpp index 16b3e90dbd..f19050142f 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -563,6 +563,11 @@ void Emulator::loadGame(const char *path, LoadProgress *progress) settings.content.path.clear(); settings.content.fileName.clear(); } + +#if defined(__vita__) + if (getGamePlatform(path) != DC_PLATFORM_DREAMCAST) + throw FlycastException("PS Vita doesn't support this platform"); +#endif setPlatform(getGamePlatform(settings.content.fileName)); mem_map_default(); diff --git a/core/hw/aica/dsp_arm32.cpp b/core/hw/aica/dsp_arm32.cpp index 1597aa9a5a..6cad7ee35e 100644 --- a/core/hw/aica/dsp_arm32.cpp +++ b/core/hw/aica/dsp_arm32.cpp @@ -48,7 +48,7 @@ namespace dsp constexpr size_t CodeSize = 4096 * 8; //32 kb, 8 pages -#if defined(__unix__) +#if defined(__unix__) || defined(__vita__) alignas(4096) static u8 DynCode[CodeSize] __attribute__((section(".text"))); #elif defined(_M_ARM) static u8 *DynCode; diff --git a/core/hw/arm7/arm7_rec.cpp b/core/hw/arm7/arm7_rec.cpp index d785ebbeb7..6064f8b1cd 100644 --- a/core/hw/arm7/arm7_rec.cpp +++ b/core/hw/arm7/arm7_rec.cpp @@ -52,7 +52,7 @@ void (*EntryPoints[ARAM_SIZE_MAX / 4])(); static u8 *ARM7_TCB; #elif defined(__OpenBSD__) alignas(4096) static u8 ARM7_TCB[ICacheSize] __attribute__((section(".openbsd.mutable"))); -#elif defined(__unix__) || defined(__SWITCH__) +#elif defined(__unix__) || defined(__SWITCH__) || defined(__vita__) alignas(4096) static u8 ARM7_TCB[ICacheSize] __attribute__((section(".text"))); #elif defined(__APPLE__) alignas(4096) static u8 ARM7_TCB[ICacheSize] __attribute__((section("__TEXT, .text"))); diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index 5686eb27fe..be58d3f1d2 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -279,7 +279,7 @@ static void rend_create_renderer() case RenderType::OpenGL: renderer = rend_GLES2(); break; -#if !defined(GLES) && !defined(__APPLE__) +#if !defined(GLES) && !defined(__APPLE__) && !defined(__vita__) case RenderType::OpenGL_OIT: renderer = rend_GL4(); break; diff --git a/core/hw/pvr/elan.h b/core/hw/pvr/elan.h index 6c3b04e5f0..61621e896b 100644 --- a/core/hw/pvr/elan.h +++ b/core/hw/pvr/elan.h @@ -33,5 +33,9 @@ void deserialize(Deserializer& deser); extern u8 *RAM; extern u32 ERAM_SIZE; +#if !defined(__vita__) constexpr u32 ERAM_SIZE_MAX = 32_MB; +#else +constexpr u32 ERAM_SIZE_MAX = 0; +#endif } diff --git a/core/hw/pvr/ta_util.cpp b/core/hw/pvr/ta_util.cpp index d9ed2b4e50..c319b53023 100644 --- a/core/hw/pvr/ta_util.cpp +++ b/core/hw/pvr/ta_util.cpp @@ -16,6 +16,8 @@ You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ +#include "cfg/cfg.h" +#include "cfg/option.h" #include "ta_ctx.h" #include "pvr_mem.h" #include @@ -130,6 +132,11 @@ void sortTriangles(rend_context& ctx, RenderPass& pass, const RenderPass& previo } //sort them +#ifdef __vita__ + if (config::FastSorting) + std::sort(triangleList.begin(), triangleList.end()); + else +#endif std::stable_sort(triangleList.begin(), triangleList.end()); //Merge pids/draw cmds if two different pids are actually equal @@ -254,6 +261,11 @@ void sortPolyParams(std::vector& polys, int first, int end, rend_cont } } +#ifdef __vita__ + if (config::FastSorting) + std::sort(&polys[first], pp_end); + else +#endif std::stable_sort(&polys[first], pp_end); } diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index 768427b351..532e22a482 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -27,7 +27,7 @@ static u8 *SH4_TCB; alignas(4096) static u8 SH4_TCB[FULL_SIZE] #if defined(__OpenBSD__) __attribute__((section(".openbsd.mutable"))); -#elif defined(__unix__) || defined(__SWITCH__) +#elif defined(__unix__) || defined(__SWITCH__) || defined(__vita__) __attribute__((section(".text"))); #elif defined(__APPLE__) __attribute__((section("__TEXT,.text"))); diff --git a/core/hw/sh4/modules/serial.cpp b/core/hw/sh4/modules/serial.cpp index 39421b4b85..6e42c834db 100644 --- a/core/hw/sh4/modules/serial.cpp +++ b/core/hw/sh4/modules/serial.cpp @@ -7,7 +7,9 @@ #include #ifndef _WIN32 #include +#ifndef __vita__ #include +#endif #else #include #include diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index e970f880aa..1940efdb10 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -3,7 +3,7 @@ #endif #include "types.h" -#if defined(__unix__) || defined(__SWITCH__) +#if defined(__unix__) || defined(__SWITCH__) || defined(__vita__) #include "log/LogManager.h" #include "emulator.h" #include "rend/mainui.h" @@ -16,6 +16,43 @@ #include #include +#ifdef __vita__ +#include +#include +#include +#include +int _newlib_heap_size_user = 246 * 1024 * 1024; +unsigned int sceUserMainThreadStackSize = 1 * 1024 * 1024; +bool is_standalone = false; + +extern "C" { +void *__wrap_calloc(uint32_t nmember, uint32_t size) { return vglCalloc(nmember, size); } +void __wrap_free(void *addr) { vglFree(addr); }; +void *__wrap_malloc(uint32_t size) { return vglMalloc(size); }; +void *__wrap_memalign(uint32_t alignment, uint32_t size) { return vglMemalign(alignment, size); }; +void *__wrap_realloc(void *ptr, uint32_t size) { return vglRealloc(ptr, size); }; +void *__wrap_memcpy (void *dst, const void *src, size_t num) { return sceClibMemcpy(dst, src, num); }; +void *__wrap_memset (void *ptr, int value, size_t num) { return sceClibMemset(ptr, value, num); }; +}; + +void early_fatal_error(const char *msg) { + vglInit(0); + SceMsgDialogUserMessageParam msg_param; + sceClibMemset(&msg_param, 0, sizeof(SceMsgDialogUserMessageParam)); + msg_param.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK; + msg_param.msg = (const SceChar8*)msg; + SceMsgDialogParam param; + sceMsgDialogParamInit(¶m); + param.mode = SCE_MSG_DIALOG_MODE_USER_MSG; + param.userMsgParam = &msg_param; + sceMsgDialogInit(¶m); + while (sceMsgDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) { + vglSwapBuffers(GL_TRUE); + } + sceKernelExitProcess(0); +} +#endif + #if defined(__SWITCH__) #include "nswitch.h" #endif @@ -113,7 +150,10 @@ void common_linux_setup(); // $HOME/.config/flycast on linux std::string find_user_config_dir() { -#ifdef __SWITCH__ +#ifdef __vita__ + flycast::mkdir("ux0:data/flycast", 0777); + return "ux0:data/flycast/"; +#elif defined(__SWITCH__) flycast::mkdir("/flycast", 0755); return "/flycast/"; #else @@ -147,7 +187,10 @@ std::string find_user_config_dir() // $HOME/.local/share/flycast on linux std::string find_user_data_dir() { -#ifdef __SWITCH__ +#ifdef __vita__ + flycast::mkdir("ux0:data/flycast/data", 0777); + return "ux0:data/flycast/data/"; +#elif defined(__SWITCH__) flycast::mkdir("/flycast/data", 0755); return "/flycast/data/"; #else @@ -210,6 +253,8 @@ std::vector find_system_config_dirs() #ifdef __SWITCH__ dirs.push_back("/flycast/"); +#elif defined(__vita__) + dirs.push_back("ux0:data/flycast/"); #else std::string xdg_home; if (nowide::getenv("XDG_CONFIG_HOME") != nullptr) @@ -258,6 +303,8 @@ std::vector find_system_data_dirs() #ifdef __SWITCH__ dirs.push_back("/flycast/data/"); +#elif defined(__vita__) + dirs.push_back("ux0:data/flycast/data/"); #else std::string xdg_home; if (nowide::getenv("XDG_DATA_HOME") != nullptr) @@ -299,6 +346,7 @@ static const char *selfPath; void os_RunInstance(int argc, const char *argv[]) { +#ifndef __vita__ if (fork() == 0) { std::vector localArgs; @@ -308,6 +356,7 @@ void os_RunInstance(int argc, const char *argv[]) localArgs.push_back(nullptr); execv(selfPath, &localArgs[0]); } +#endif } #if defined(USE_BREAKPAD) @@ -345,6 +394,54 @@ int main(int argc, char* argv[]) INFO_LOG(BOOT, "Config dir is: %s", get_writable_config_path("").c_str()); INFO_LOG(BOOT, "Data dir is: %s", get_writable_data_path("").c_str()); +#ifdef __vita__ + SceIoStat st1, st2; + // Checking for libshacccg.suprx existence + if (!(sceIoGetstat("ur0:/data/libshacccg.suprx", &st1) >= 0 || sceIoGetstat("ur0:/data/external/libshacccg.suprx", &st2) >= 0)) + early_fatal_error("Error: Runtime shader compiler (libshacccg.suprx) is not installed."); + + // Checking for kubridge existence + if (!(sceIoGetstat("ux0:/tai/kubridge.skprx", &st1) >= 0 || sceIoGetstat("ur0:/tai/kubridge.skprx", &st2) >= 0)) + early_fatal_error("Error: kubridge.skprx is not installed."); + + // Checking for kubridge version + FILE *f = fopen("ux0:/tai/kubridge.skprx", "rb"); + if (!f) + f = fopen("ur0:/tai/kubridge.skprx", "rb"); + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + void *buf = vglMalloc(size); + fread(buf, 1, size, f); + fclose(f); + uint32_t kubridge_hash = XXH32(buf, size, 7); + vglFree(buf); + if (kubridge_hash == 0xFDAE199B) + early_fatal_error("Error: kubridge.skprx is outdated."); + + char boot_params[1024]; + char *launch_argv[2]; + argc = 0; + + // Check if we launched flycast from a custom bubble + sceAppMgrGetAppParam(boot_params); + if (strstr(boot_params,"psgm:play") && strstr(boot_params, "¶m=")) { + argc = 2; + launch_argv[1] = strstr(boot_params, "¶m=") + 7; + is_standalone = true; + } + argv = launch_argv; + + scePowerSetArmClockFrequency(444); + scePowerSetBusClockFrequency(222); + scePowerSetGpuClockFrequency(222); + scePowerSetGpuXbarClockFrequency(166); + SDL_setenv("VITA_DISABLE_TOUCH_BACK", "1", 1); // Disabling rearpad + vglSetParamBufferSize(8 * 1024 * 1024); + vglUseCachedMem(GL_TRUE); + vglInitWithCustomThreshold(0, 960, 544, 256 * 1024 * 1024, 0, 0, 0, SCE_GXM_MULTISAMPLE_4X); +#endif + #if defined(USE_SDL) // init video now: on rpi3 it installs a sigsegv handler(?) if (SDL_Init(SDL_INIT_VIDEO) != 0) diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 11f1bb4564..38fc20b224 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -1,6 +1,6 @@ #include "types.h" -#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) +#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__vita__) #if defined(__APPLE__) #define _XOPEN_SOURCE 1 #define __USE_GNU 1 @@ -29,12 +29,12 @@ extern "C" char __start__; #define siginfo_t switch_siginfo_t #endif // __SWITCH__ -#if !defined(TARGET_NO_EXCEPTIONS) +#if !defined(TARGET_NO_EXCEPTIONS) && !defined(__vita__) void context_from_segfault(host_context_t* hctx, void* segfault_ctx); void context_to_segfault(host_context_t* hctx, void* segfault_ctx); -#ifndef __SWITCH__ +#if !defined(__SWITCH__) && !defined(__vita__) static struct sigaction next_segv_handler; #endif #if defined(__APPLE__) @@ -72,6 +72,8 @@ void fault_handler(int sn, siginfo_t * si, void *segfault_ctx) u32 pageinfo; svcQueryMemory(&meminfo, &pageinfo, (u64)&__start__); ERROR_LOG(COMMON, ".text base: %p", (void*)meminfo.addr); +#elif defined(__vita__) + //TODO #else if (next_segv_handler.sa_sigaction != nullptr) next_segv_handler.sa_sigaction(sn, si, segfault_ctx); @@ -83,7 +85,7 @@ void fault_handler(int sn, siginfo_t * si, void *segfault_ctx) void os_InstallFaultHandler() { -#ifndef __SWITCH__ +#if !defined(__SWITCH__) && !defined(__vita__) struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = fault_handler; @@ -99,7 +101,7 @@ void os_InstallFaultHandler() void os_UninstallFaultHandler() { -#ifndef __SWITCH__ +#if !defined(__SWITCH__) && !defined(__vita__) sigaction(SIGSEGV, &next_segv_handler, nullptr); #endif #if defined(__APPLE__) @@ -107,7 +109,7 @@ void os_UninstallFaultHandler() #endif } -#else // !defined(TARGET_NO_EXCEPTIONS) +#elif !defined(__vita__) // !defined(TARGET_NO_EXCEPTIONS) void os_InstallFaultHandler() { @@ -186,7 +188,9 @@ void common_linux_setup() os_InstallFaultHandler(); signal(SIGINT, exit); +#ifndef __vita__ DEBUG_LOG(BOOT, "Linux paging: %ld %08X %08X", sysconf(_SC_PAGESIZE), PAGE_SIZE, PAGE_MASK); verify(PAGE_MASK==(sysconf(_SC_PAGESIZE)-1)); +#endif } #endif // __unix__ or __APPLE__ or __SWITCH__ diff --git a/core/linux/vita_fault_handler.cpp b/core/linux/vita_fault_handler.cpp new file mode 100644 index 0000000000..ee17ddcc51 --- /dev/null +++ b/core/linux/vita_fault_handler.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include "hw/sh4/dyna/blockmanager.h" + +#include "oslib/host_context.h" + +#include "hw/sh4/dyna/ngen.h" +#include "rend/TexCache.h" +#include "hw/mem/addrspace.h" +#include "hw/mem/mem_watch.h" + +static KuKernelAbortHandler nextHandler; +static __thread bool threadInFault = false; + +void context_to_segfault(host_context_t *hostctx, void *segfault_ctx) +{ + KuKernelAbortContext *abortContext = reinterpret_cast(segfault_ctx); + for (int i = 0; i < 15; i++) + (&abortContext->r0)[i] = hostctx->reg[i]; + + abortContext->pc = hostctx->pc; +} +void context_from_segfault(host_context_t *hostctx, void *segfault_ctx) +{ + KuKernelAbortContext *abortContext = reinterpret_cast(segfault_ctx); + for (int i = 0; i < 15; i++) + hostctx->reg[i] = (&abortContext->r0)[i]; + + hostctx->pc = abortContext->pc; +} + +void abortHandler(KuKernelAbortContext *abortContext) +{ + // Ram watcher for net rollback + if (memwatch::writeAccess((u8 *)abortContext->FAR)) + return; + // code protection in RAM + if (bm_RamWriteAccess((u8*)abortContext->FAR)) + return; + // texture protection in VRAM + if (VramLockedWrite((u8*)abortContext->FAR)) + return; + // FPCB jump table protection + if (addrspace::bm_lockedWrite((u8*)abortContext->FAR)) + return; + +#if FEAT_SHREC == DYNAREC_JIT + // fast mem access rewriting + host_context_t ctx; + context_from_segfault(&ctx, abortContext); + if (sh4Dynarec->rewrite(ctx, (void *)abortContext->FAR)) + { + context_to_segfault(&ctx, abortContext); + return; + } +#endif + if (!threadInFault) + { + threadInFault = true; + ERROR_LOG(COMMON, "Memory remap race condition detected for address %p (0x%08X) Waiting 10 ms...", (void *)abortContext->FAR, *(uint32_t *)abortContext->FAR); + sceKernelDelayThread(10 * 1000); + threadInFault = false; + + return; + } + + ERROR_LOG(COMMON, "SIGSEGV @ %p invalid access to %p", (void *)ctx.pc, (void *)abortContext->FAR); + if (nextHandler != nullptr) + nextHandler(abortContext); + else + die("segfault"); +} + +void os_InstallFaultHandler() +{ + DEBUG_LOG(VMEM, "In the abort handler"); + verify(kuKernelRegisterAbortHandler(abortHandler, &nextHandler, NULL) == 0); +} +void os_UninstallFaultHandler() +{ + kuKernelRegisterAbortHandler(nextHandler, NULL, NULL); +} \ No newline at end of file diff --git a/core/linux/vita_vmem.cpp b/core/linux/vita_vmem.cpp new file mode 100644 index 0000000000..d71ac4547f --- /dev/null +++ b/core/linux/vita_vmem.cpp @@ -0,0 +1,166 @@ +// Implementation of the vmem related function for the PS Vita +// Based on posix_vmem.cpp, requires forked kubridge kernel module: +// https://github.com/bythos14/kubridge/tree/exceptions_mprotect + +#if defined(__vita__) +#include +#include + +#include "hw/mem/addrspace.h" +#include "oslib/virtmem.h" +#include "hw/sh4/dyna/blockmanager.h" +#include "hw/sh4/sh4_if.h" +#include "hw/pvr/elan.h" +#include "stdclass.h" +#include "types.h" + +#define ALIGN(addr, align) (((uintptr_t)addr + (align - 1)) & ~(align - 1)) + +extern bool is_standalone; + +namespace virtmem +{ + +bool region_lock(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + verify(kuKernelMemProtect((u8 *)start - inpage, len + inpage, KU_KERNEL_PROT_READ) == 0); + return true; +} + +bool region_unlock(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + verify(kuKernelMemProtect((u8 *)start - inpage, len + inpage, KU_KERNEL_PROT_READ | KU_KERNEL_PROT_WRITE) == 0); + return true; +} + +bool region_set_exec(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + kuKernelMemProtect((u8 *)start - inpage, len + inpage, KU_KERNEL_PROT_READ | KU_KERNEL_PROT_WRITE | KU_KERNEL_PROT_EXEC); + + return true; +} + +// Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. +// The function supports allocating 512MB or 4GB addr spaces. +static void *reserved_base; +static size_t reserved_size; +static SceUID reserved_block = -1; +static SceUID vmem_block = -1; + +// vmem_base_addr points to an address space of 512MB (or 4GB) that can be used for fast memory ops. +// In negative offsets of the pointer (up to FPCB size, usually 65/129MB) the context and jump table +// can be found. If the platform init returns error, the user is responsible for initializing the +// memory using a fallback (that is, regular mallocs and falling back to slow memory JIT). +bool init(void **vmem_base_addr, void **sh4rcb_addr, size_t ramSize) +{ + + // Now try to allocate a contiguous piece of memory. + reserved_size = 512 * 1024 * 1024 + ALIGN(sizeof(Sh4RCB), PAGE_SIZE) + ARAM_SIZE_MAX; + reserved_base = nullptr; + reserved_block = kuKernelMemReserve(&reserved_base, reserved_size, SCE_KERNEL_MEMBLOCK_TYPE_USER_RW); + verify(reserved_block >= 0); + + vmem_block = sceKernelAllocMemBlock("vmem_backing_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, ALIGN(ramSize, PAGE_SIZE), NULL); + verify(vmem_block >= 0); + + uintptr_t ptrint = (uintptr_t)reserved_base; + *sh4rcb_addr = (void *)ptrint; + *vmem_base_addr = (void *)(ptrint + sizeof(Sh4RCB)); + const size_t fpcb_size = sizeof(((Sh4RCB *)NULL)->fpcb); + + // Now commit the memory for the SH4RCB + verify(kuKernelMemCommit(*sh4rcb_addr, sizeof(Sh4RCB), KU_KERNEL_PROT_READ | KU_KERNEL_PROT_WRITE, NULL) == 0); + // Set the fpcb to be non-accessible so that it can be configured upon use. + verify(kuKernelMemProtect(*sh4rcb_addr, fpcb_size, KU_KERNEL_PROT_NONE) == 0); + + return true; +} + +// Just tries to wipe as much as possible in the relevant area. +void destroy() +{ + if (reserved_block != -1) + sceKernelFreeMemBlock(reserved_block); + if (vmem_block != -1) + sceKernelFreeMemBlock(vmem_block); +} + +// Resets a chunk of memory by deleting its data and setting its protection back. +void reset_mem(void *ptr, unsigned size_bytes) +{ + // Mark them as non accessible. + bm_vmem_pagefill((void **)ptr, size_bytes); // TODO: Figure out why this call is needed + verify(kuKernelMemProtect(ptr, size_bytes, KU_KERNEL_PROT_NONE) == 0); +} + +// Allocates a bunch of memory (page aligned and page-sized) +void ondemand_page(void *address, unsigned size_bytes) +{ + verify(region_unlock(address, size_bytes)); +} + +// Creates mappings to the underlying file including mirroring sections +void create_mappings(const Mapping *vmem_maps, unsigned nummaps) +{ + KuKernelMemCommitOpt opt; + opt.size = sizeof(opt); + opt.attr = 0x1; + opt.baseBlock = vmem_block; + + for (unsigned i = 0; i < nummaps; i++) + { + // Ignore unmapped stuff, it is already reserved as PROT_NONE + if (!vmem_maps[i].memsize) + continue; + + // Calculate the number of mirrors + u64 address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; + unsigned num_mirrors = (address_range_size) / vmem_maps[i].memsize; + verify((address_range_size % vmem_maps[i].memsize) == 0 && num_mirrors >= 1); + + for (unsigned j = 0; j < num_mirrors; j++) + { + u64 offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; + opt.baseOffset = vmem_maps[i].memoffset; + verify(kuKernelMemCommit(&addrspace::ram_base[offset], vmem_maps[i].memsize, KU_KERNEL_PROT_READ | (vmem_maps[i].allow_writes ? KU_KERNEL_PROT_WRITE : 0), &opt) == 0); + } + } +} + +// Prepares the code region for JIT operations, thus marking it as RWX +bool prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) +{ + if (code_area) + { + verify(kuKernelMemProtect(code_area, size, KU_KERNEL_PROT_EXEC | KU_KERNEL_PROT_WRITE | KU_KERNEL_PROT_READ) == 0); + *code_area_rwx = code_area; + } + return true; +} + +void release_jit_block(void *code_area, size_t size) +{ + +} + +// Use two addr spaces: need to remap something twice, therefore use allocate_shared_filemem() +bool prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, ptrdiff_t *rx_offset) +{ + return false; // Unimplemented +} + +void jit_set_exec(void *code, size_t size, bool enable) +{ + +} + +void flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end) +{ + kuKernelFlushCaches(icache_start, (u8 *)icache_end - (u8 *)icache_start + 1); +} + +} +#endif // #if defined(__vita__) diff --git a/core/log/ConsoleListenerNix.cpp b/core/log/ConsoleListenerNix.cpp index 8f5d9337ab..445c368cc2 100644 --- a/core/log/ConsoleListenerNix.cpp +++ b/core/log/ConsoleListenerNix.cpp @@ -16,7 +16,7 @@ ConsoleListener::ConsoleListener() { #if defined(LOG_TO_PTY) || defined(__APPLE__) m_use_color = 1; -#elif defined(__SWITCH__) +#elif defined(__SWITCH__) || defined(__vita__) m_use_color = 0; #else m_use_color = !!isatty(fileno(stderr)); diff --git a/core/log/LogManager.cpp b/core/log/LogManager.cpp index d86102ac47..97576f04ad 100644 --- a/core/log/LogManager.cpp +++ b/core/log/LogManager.cpp @@ -132,7 +132,7 @@ LogManager::LogManager() SetLogLevel(static_cast(verbosity)); if (cfgLoadBool("log", "LogToFile", false)) { -#if defined(__ANDROID__) || defined(__APPLE__) || defined(TARGET_UWP) +#if defined(__ANDROID__) || defined(__APPLE__) || defined(TARGET_UWP) || defined(__vita__) std::string logPath = get_writable_data_path("flycast.log"); #else std::string logPath = "flycast.log"; diff --git a/core/log/StringUtil.h b/core/log/StringUtil.h index 15cd9a5f0e..ef86a0110e 100644 --- a/core/log/StringUtil.h +++ b/core/log/StringUtil.h @@ -10,7 +10,7 @@ constexpr u32 CODEPAGE_WINDOWS_1252 = 1252; #include #endif -#if defined(__SWITCH__) || defined(__HAIKU__) +#if defined(__SWITCH__) || defined(__HAIKU__) || defined(__vita__) int vasprintf(char **s, const char *fmt, va_list ap) { va_list ap2; @@ -23,7 +23,7 @@ int vasprintf(char **s, const char *fmt, va_list ap) } #endif -#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) +#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) && !defined(__vita__) static locale_t GetCLocale() { static locale_t c_locale = newlocale(LC_ALL_MASK, "C", nullptr); @@ -68,11 +68,11 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar writtenCount = vsnprintf(out, outsize, format, args); #endif #else -#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) +#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) && !defined(__vita__) locale_t previousLocale = uselocale(GetCLocale()); #endif writtenCount = vsnprintf(out, outsize, format, args); -#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) +#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) && !defined(__vita__) uselocale(previousLocale); #endif #endif @@ -100,7 +100,7 @@ std::string StringFromFormatV(const char* format, va_list args) std::string temp = buf; delete[] buf; #else -#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) +#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) && !defined(__vita__) locale_t previousLocale = uselocale(GetCLocale()); #endif if (vasprintf(&buf, format, args) < 0) @@ -109,7 +109,7 @@ std::string StringFromFormatV(const char* format, va_list args) buf = nullptr; } -#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) +#if !defined(__ANDROID__) && !defined(__HAIKU__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__SWITCH__) && !defined(__vita__) uselocale(previousLocale); #endif diff --git a/core/network/dns.cpp b/core/network/dns.cpp index af98bfcb51..5db106b17b 100644 --- a/core/network/dns.cpp +++ b/core/network/dns.cpp @@ -148,7 +148,7 @@ char *read_name(char *reader, char *buffer, int *count) return name; } -#if !defined(_WIN32) && !defined(__SWITCH__) +#if !defined(_WIN32) && !defined(__SWITCH__) && !defined(__vita__) #include #include #endif @@ -195,7 +195,7 @@ bool is_local_address(u32 addr) } closesocket(sd); -#elif defined(__SWITCH__) +#elif defined(__SWITCH__) || defined(__vita__) // TODO #else // !_WIN32 && !__SWITCH__ diff --git a/core/network/net_platform.h b/core/network/net_platform.h index ccf6e22226..9303a8e8a6 100644 --- a/core/network/net_platform.h +++ b/core/network/net_platform.h @@ -32,6 +32,11 @@ #define INET_ADDRSTRLEN sizeof(struct sockaddr_in) #endif #define SOL_TCP 6 // Shrug +#elif defined(__vita__) +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif +#define SOL_TCP 6 // Shrug #else #include #endif // __SWITCH__ diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index 7f0421ae8a..e063778249 100644 --- a/core/oslib/oslib.h +++ b/core/oslib/oslib.h @@ -1,7 +1,7 @@ #pragma once #include "types.h" #include -#if defined(__SWITCH__) +#if defined(__SWITCH__) || defined(__vita__) #include #endif @@ -53,7 +53,7 @@ static inline void *allocAligned(size_t alignment, size_t size) { #ifdef _WIN32 return _aligned_malloc(size, alignment); -#elif defined(__SWITCH__) +#elif defined(__SWITCH__) || defined(__vita__) return memalign(alignment, size); #else void *data; diff --git a/core/oslib/storage.cpp b/core/oslib/storage.cpp index e3c7055c57..71c9b47f3f 100644 --- a/core/oslib/storage.cpp +++ b/core/oslib/storage.cpp @@ -100,9 +100,11 @@ class StdStorage : public Storage bool isDir = false; #ifndef _WIN32 +#ifndef __vita__ if (direntry->d_type == DT_DIR) isDir = true; else if (direntry->d_type == DT_UNKNOWN || direntry->d_type == DT_LNK) +#endif { struct stat st; if (flycast::stat(entry.path.c_str(), &st) != 0) diff --git a/core/oslib/unwind_info.h b/core/oslib/unwind_info.h index aa65ed6720..ee08899feb 100644 --- a/core/oslib/unwind_info.h +++ b/core/oslib/unwind_info.h @@ -33,7 +33,7 @@ class UnwindInfo std::vector tables; std::vector codes; #endif -#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) +#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__vita__) int stackOffset = 0; uintptr_t lastOffset = 0; std::vector cieInstructions; diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index edd80b6252..993d01b374 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -2122,6 +2122,9 @@ void Arm32Assembler::compile(RuntimeBlockInfo* block, bool force_checks, bool op reg.OpBegin(&block->oplist[0], 0); // block checks +#ifdef __vita__ + if (config::DynarecSmcChecks) { +#endif if (mmu_enabled()) { Mov(r0, block->vaddr); @@ -2136,7 +2139,11 @@ void Arm32Assembler::compile(RuntimeBlockInfo* block, bool force_checks, bool op u32 addr = block->addr; Mov(r0, addr); +#ifdef __vita__ + s32 sz = config::DynarecSmcChecks == 1 ? 4 : block->sh4_code_size; +#else s32 sz = block->sh4_code_size; +#endif while (sz > 0) { if (sz > 2) @@ -2171,6 +2178,9 @@ void Arm32Assembler::compile(RuntimeBlockInfo* block, bool force_checks, bool op } } } +#ifdef __vita__ + } +#endif //scheduler Ldr(r1, MemOperand(r8, rcbOffset(cntx.cycle_counter))); diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index b380378bc9..8c58451ce1 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -666,7 +666,11 @@ bool BaseTextureCacheData::Update() need_32bit_buffer = false; // TODO avoid upscaling/depost. textures that change too often +#ifdef __vita__ + bool mipmapped = false; +#else bool mipmapped = IsMipmapped() && !config::DumpTextures; +#endif if (texconv32 != NULL && need_32bit_buffer) { diff --git a/core/rend/boxart/http_client.cpp b/core/rend/boxart/http_client.cpp index d68db142fd..20190e2fe9 100644 --- a/core/rend/boxart/http_client.cpp +++ b/core/rend/boxart/http_client.cpp @@ -180,13 +180,29 @@ void term() #endif // !TARGET_UWP #else +#ifdef __vita__ +#include +void *net_mem; +#endif #include namespace http { void init() { +#ifdef __vita__ + sceSysmoduleLoadModule(SCE_SYSMODULE_NET); + int ret = sceNetShowNetstat(); + SceNetInitParam initparam; + if (ret == SCE_NET_ERROR_ENOTINIT) { + initparam.memory = net_mem = malloc(141 * 1024); + initparam.size = 141 * 1024; + initparam.flags = 0; + sceNetInit(&initparam); + } +#else curl_global_init(CURL_GLOBAL_ALL); +#endif } static size_t receiveData(void *buffer, size_t size, size_t nmemb, std::vector *recvBuffer) @@ -202,6 +218,11 @@ int get(const std::string& url, std::vector& content, std::string& contentTy curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); +#ifdef __vita__ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); +#endif curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); @@ -229,6 +250,7 @@ int get(const std::string& url, std::vector& content, std::string& contentTy int post(const std::string& url, const std::vector& fields) { +#ifndef __vita__ CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_USERAGENT, "Flycast/1.0"); curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1); @@ -263,12 +285,19 @@ int post(const std::string& url, const std::vector& fields) curl_easy_cleanup(curl); return (int)httpCode; - +#else + return 500; +#endif } void term() { +#ifdef __vita__ + sceNetTerm(); + free(net_mem); +#else curl_global_cleanup(); +#endif } } diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 24414fabc2..5668b4619c 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -34,7 +34,315 @@ #endif //Fragment and vertex shaders code +#ifdef __vita__ +const char* ShaderCompatSource = R"( +#define GLES2 0 +#define GLES3 1 +#define GL2 2 +#define GL3 3 +#define FOG_CHANNEL a +)"; +const char *VertexCompatShader = R"( +)"; + +const char *PixelCompatShader = R"( +)"; + +const char* GouraudSource = R"( + #define INTERPOLATION +)"; + +static const char* VertexShaderSource = R"( +/* Vertex constants*/ +uniform float4 depth_scale; +uniform float4x4 ndcMat; +uniform float sp_FOG_DENSITY; + +void main( + float4 in_pos, + float4 in_base, + float4 in_offs, + float2 in_uv, + float4 out vtx_base : TEXCOORD0, + float4 out vtx_offs : TEXCOORD1, + float3 out vtx_uv : TEXCOORD2, + float4 out gl_Position : POSITION +) { + float4 vpos = mul(in_pos, ndcMat); + vtx_base = in_base; + vtx_offs = in_offs; +#if USE_GLES2 == 1 + vtx_uv = float3(in_uv, vpos.z * sp_FOG_DENSITY); + vpos.w = 1.0 / vpos.z; + vpos.z = depth_scale.x + depth_scale.y * vpos.w; + vpos.xy *= vpos.w; +#else +#if DIV_POS_Z == 1 + vpos /= vpos.z; + vpos.z = vpos.w; +#endif +#if pp_Gouraud == 1 && DIV_POS_Z != 1 + vtx_base *= vpos.z; + vtx_offs *= vpos.z; +#endif + vtx_uv = float3(in_uv, vpos.z); +#if DIV_POS_Z != 1 + vtx_uv.xy *= vpos.z; + vpos.w = 1.0; + vpos.z = 0.0; +#endif +#endif + gl_Position = vpos; +} +)"; + +const char* PixelPipelineShader = R"( +#define PI 3.1415926 + +/* Shader program params*/ +/* gles has no alpha test stage, so its emulated on the shader */ +uniform float cp_AlphaTestValue; +uniform float4 pp_ClipTest; +uniform float3 sp_FOG_COL_RAM,sp_FOG_COL_VERT; +uniform float sp_FOG_DENSITY; +uniform sampler2D tex,fog_table; +uniform float trilinear_alpha; +uniform float4 fog_clamp_min; +uniform float4 fog_clamp_max; +#if pp_Palette == 1 +uniform sampler2D palette; +uniform short palette_index; +#endif +#if DITHERING == 1 +uniform lowp vec4 ditherColorMax; +#endif + +float fog_mode2(float w, float3 vtx_uv) +{ + float z = clamp( +#if USE_GLES2 == 1 + vtx_uv.z +#else +#if DIV_POS_Z == 1 + sp_FOG_DENSITY / w +#else + sp_FOG_DENSITY * w +#endif +#endif + , 1.0, 255.9999); + float _exp = floor(log2(z)); + float m = z * 16.0 / pow(2.0, _exp) - 16.0; + float idx = floor(m) + _exp * 16.0 + 0.5; + float4 fog_coef = tex2D(fog_table, float2(idx / 128.0, 0.75 - (m - floor(m)) / 2.0)); + return fog_coef.FOG_CHANNEL; +} + +float4 fog_clamp(float4 col) +{ +#if FogClamping == 1 + return clamp(col, fog_clamp_min, fog_clamp_max); +#else + return col; +#endif +} + +#if pp_Palette == 1 + +float4 palettePixel(float3 coords) +{ + short color_idx = short(floor(tex2D(tex, coords.xy).FOG_CHANNEL * 255.0 + 0.5)) + palette_index; + float2 c = float2((fmod(float(color_idx), 32.0) * 2.0 + 1.0) / 64.0, (float(color_idx / 32) * 2.0 + 1.0) / 64.0); + return tex2D(palette, c); +} + +#endif + +#if USE_GLES2 == 1 +#define depth gl_FragCoord.w +#else +#define depth vtx_uv.z +#endif + +void main( + float4 vtx_base : TEXCOORD0, + float4 vtx_offs : TEXCOORD1, + float3 vtx_uv : TEXCOORD2, + float4 gl_FragCoord : WPOS, +#if USE_GLES2 == 0 + float out gl_FragDepth : DEPTH, +#endif + float4 out gl_FragColor : COLOR +) { + // Clip inside the box + #if pp_ClipInside == 1 + if (gl_FragCoord.x >= pp_ClipTest.x && gl_FragCoord.x <= pp_ClipTest.z + && gl_FragCoord.y >= pp_ClipTest.y && gl_FragCoord.y <= pp_ClipTest.w) + discard; + #endif + + float4 color = vtx_base; + float4 offset = vtx_offs; + #if pp_Gouraud == 1 && DIV_POS_Z != 1 && USE_GLES2 == 0 + color /= vtx_uv.z; + offset /= vtx_uv.z; + #endif + #if pp_UseAlpha==0 + color.a=1.0; + #endif + #if pp_FogCtrl==3 + color = float4(sp_FOG_COL_RAM.rgb, fog_mode2(depth, vtx_uv)); + #endif + #if pp_Texture==1 + { + #if pp_Palette == 0 + #if USE_GLES2 == 1 || DIV_POS_Z == 1 + float4 texcol = tex2D(tex, vtx_uv.xy); + #else + float4 texcol = tex2Dproj(tex, vtx_uv); + #endif + #else + float4 texcol = palettePixel(vtx_uv); + #endif + + #if pp_BumpMap == 1 + float s = PI / 2.0 * (texcol.a * 15.0 * 16.0 + texcol.r * 15.0) / 255.0; + float r = 2.0 * PI * (texcol.g * 15.0 * 16.0 + texcol.b * 15.0) / 255.0; + texcol.a = clamp(offset.a + offset.r * sin(s) + offset.g * cos(s) * cos(r - 2.0 * PI * offset.b), 0.0, 1.0); + texcol.rgb = float3(1.0, 1.0, 1.0); + #else + #if pp_IgnoreTexA==1 + texcol.a=1.0; + #endif + + #if cp_AlphaTest == 1 + if (cp_AlphaTestValue > texcol.a) + discard; + texcol.a = 1.0; + #endif + #endif + #if pp_ShadInstr==0 + { + color=texcol; + } + #endif + #if pp_ShadInstr==1 + { + color.rgb*=texcol.rgb; + color.a=texcol.a; + } + #endif + #if pp_ShadInstr==2 + { + color.rgb=lerp(color.rgb,texcol.rgb,texcol.a); + } + #endif + #if pp_ShadInstr==3 + { + color*=texcol; + } + #endif + + #if pp_Offset==1 && pp_BumpMap == 0 + color.rgb += offset.rgb; + #endif + } + #endif + + color = fog_clamp(color); + + #if pp_FogCtrl == 0 + color.rgb = lerp(color.rgb, sp_FOG_COL_RAM.rgb, fog_mode2(depth, vtx_uv)); + #endif + #if pp_FogCtrl == 1 && pp_Offset==1 && pp_BumpMap == 0 + color.rgb = lerp(color.rgb, sp_FOG_COL_VERT.rgb, offset.a); + #endif + + #if pp_TriLinear == 1 + color *= trilinear_alpha; + #endif + + //color.rgb = float3(vtx_uv.z * sp_FOG_DENSITY / 128.0); +#if USE_GLES2 == 0 +#if DIV_POS_Z == 1 + float w = 100000.0 / vtx_uv.z; +#else + float w = 100000.0 * vtx_uv.z; +#endif + gl_FragDepth = log2(1.0 + w) / 34.0; +#endif +#if DITHERING == 1 + float ditherTable[16] = float[]( + 0.9375, 0.1875, 0.75, 0., + 0.4375, 0.6875, 0.25, 0.5, + 0.8125, 0.0625, 0.875, 0.125, + 0.3125, 0.5625, 0.375, 0.625 + ); + float r = ditherTable[int(mod(gl_FragCoord.y, 4.)) * 4 + int(mod(gl_FragCoord.x, 4.))]; + // 31 for 5-bit color, 63 for 6 bits, 15 for 4 bits + color += r / ditherColorMax; + // avoid rounding + color = floor(color * 255.) / 255.; +#endif + gl_FragColor = color; +} +)"; + +static const char* ModifierVolumeShader = R"( +uniform float sp_ShaderColor; + + +void main( +#if USE_GLES2 == 0 + float3 vtx_uv : TEXCOORD2, + float out gl_FragDepth : DEPTH, +#endif + float4 out gl_FragColor : COLOR +) { +#if USE_GLES2 == 0 +#if DIV_POS_Z == 1 + float w = 100000.0 / vtx_uv.z; +#else + float w = 100000.0 * vtx_uv.z; +#endif + gl_FragDepth = log2(1.0 + w) / 34.0; +#endif + gl_FragColor = float4(0.0, 0.0, 0.0, sp_ShaderColor); +} +)"; + +const char* OSD_VertexShader = R"( +uniform float4 scale; + +void main( + float4 in_pos, + float4 in_base, + float2 in_uv, + float4 out vtx_base : TEXCOORD0, + float2 out vtx_uv : TEXCOORD1, + float4 out gl_Position : POSITION +) { + vtx_base = in_base; + vtx_uv = in_uv; + float4 vpos = in_pos; + + vpos.w = 1.0; + vpos.z = vpos.w; + vpos.xy = vpos.xy * scale.xy - scale.zw; + gl_Position = vpos; +} +)"; + +const char* OSD_Shader = R"( +uniform sampler2D tex; +float4 main( + float4 vtx_base : TEXCOORD0, + float2 vtx_uv : TEXCOORD1 +) { + return vtx_base * tex2D(tex, vtx_uv); +} +)"; +#else const char* ShaderCompatSource = R"( #define GLES2 0 #define GLES3 1 @@ -377,6 +685,7 @@ void main() gl_FragColor = vtx_base * texture(tex, vtx_uv); } )"; +#endif static void gl_free_osd_resources(); @@ -414,7 +723,9 @@ void do_swap_automation() u8* img = new u8[bytesz]; framebuffer->bind(GL_READ_FRAMEBUFFER); +#ifndef __vita__ glPixelStorei(GL_PACK_ALIGNMENT, 1); +#endif glReadPixels(0, 0, framebuffer->getWidth(), framebuffer->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, img); dump_screenshot(img, framebuffer->getWidth(), framebuffer->getHeight()); delete[] img; @@ -513,7 +824,9 @@ void findGLVersion() INFO_LOG(RENDERER, "Packed depth/stencil not supported: no modifier volumes when rendering to a texture"); GLint ranges[2]; GLint precision; +#ifndef __vita__ glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, ranges, &precision); +#endif gl.highp_float_supported = (ranges[0] != 0 || ranges[1] != 0) && precision != 0; if (!gl.border_clamp_supported) gl.border_clamp_supported = strstr(extensions, "GL_EXT_texture_border_clamp") != nullptr; @@ -571,6 +884,9 @@ void findGLVersion() if (anisotropicExtension) glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &gl.max_anisotropy); } +#endif +#ifdef __vita__ + gl.glsl_version_header = ""; #endif gl.mesa_nouveau = strstr((const char *)glGetString(GL_VERSION), "Mesa") != nullptr && !strcmp((const char *)glGetString(GL_VENDOR), "nouveau"); NOTICE_LOG(RENDERER, "OpenGL%s version %d.%d", gl.is_gles ? " ES" : "", gl.gl_major, gl.gl_minor); @@ -722,6 +1038,9 @@ class VertexSource : public OpenGlSource VertexSource(bool gouraud, bool divPosZ) : OpenGlSource() { addConstant("pp_Gouraud", gouraud); addConstant("DIV_POS_Z", divPosZ); +#ifdef __vita__ + addConstant("USE_GLES2", config::UseSimpleShaders); +#endif addSource(VertexCompatShader); addSource(GouraudSource); @@ -749,6 +1068,9 @@ class FragmentShaderSource : public OpenGlSource addConstant("pp_Palette", s->palette); addConstant("DIV_POS_Z", s->divPosZ); addConstant("DITHERING", s->dithering); +#ifdef __vita__ + addConstant("USE_GLES2", config::UseSimpleShaders); +#endif addSource(PixelCompatShader); addSource(GouraudSource); @@ -896,6 +1218,9 @@ static void create_modvol_shader() OpenGlSource fragmentShader; fragmentShader.addConstant("pp_Gouraud", 0) .addConstant("DIV_POS_Z", config::NativeDepthInterpolation) +#ifdef __vita__ + .addConstant("USE_GLES2", config::UseSimpleShaders) +#endif .addSource(PixelCompatShader) .addSource(GouraudSource) .addSource(ModifierVolumeShader); @@ -987,7 +1312,7 @@ bool OpenGLRenderer::Init() glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); #endif -#if defined(GL_GENERATE_MIPMAP_HINT) && !defined(__SWITCH__) +#if defined(GL_GENERATE_MIPMAP_HINT) && !defined(__SWITCH__) && !defined(__vita__) if (gl.is_gles) glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); #endif @@ -1025,7 +1350,9 @@ static void updateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image u8 temp_tex_buffer[256]; MakeFogTexture(temp_tex_buffer); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#ifndef __vita__ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#endif glTexImage2D(GL_TEXTURE_2D, 0, fog_image_format, 128, 2, 0, fog_image_format, GL_UNSIGNED_BYTE, temp_tex_buffer); glCheck(); @@ -1047,7 +1374,9 @@ static void updatePaletteTexture(GLenum texture_slot) else glcache.BindTexture(GL_TEXTURE_2D, paletteTextureId); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#ifndef __vita__ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, palette32_ram); glCheck(); diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index e4181933aa..d76032f78d 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -197,7 +197,9 @@ void ReadRTTBuffer() VramLockedWriteOffset(page); #endif +#if !defined(__vita__) glPixelStorei(GL_PACK_ALIGNMENT, 1); +#endif u16 *dst = (u16 *)&vram[tex_addr]; diff --git a/core/rend/gles/opengl_driver.cpp b/core/rend/gles/opengl_driver.cpp index 8705b661f2..e554f4baa4 100644 --- a/core/rend/gles/opengl_driver.cpp +++ b/core/rend/gles/opengl_driver.cpp @@ -183,8 +183,10 @@ ImTextureID OpenGLDriver::updateTexture(const std::string& name, const u8 *data, glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (gl.border_clamp_supported) { +#ifndef __vita__ float color[] = { 0.0f, 0.0f, 0.0f, 0.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); +#endif glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); } diff --git a/core/rend/gles/postprocess.cpp b/core/rend/gles/postprocess.cpp index 52d8eca540..dd0b6f2356 100644 --- a/core/rend/gles/postprocess.cpp +++ b/core/rend/gles/postprocess.cpp @@ -24,6 +24,139 @@ PostProcessor postProcessor; +#ifdef __vita__ +static const char* VertexShaderSource = R"( +void main( + float3 in_pos, + float4 out gl_Position : POSITION +) { + gl_Position = float4(in_pos, 1.0); +} +)"; + +static const char* FragmentShaderSource = R"( +#define LUMBOOST 0 + +uniform int FrameCount; +uniform sampler2D Texture; + +// compatibility #defines +#define Source Texture +#define TextureSize tex2Dsize(Texture, 0) +#define vTexCoord (gl_FragCoord.xy / float2(tex2Dsize(Texture, 0))) + +static float dithertable[16] = float[]( + 16.,4.,13.,1., + 8.,12.,5.,9., + 14.,2.,15.,3., + 6.,10.,7.,11. +); + +//#pragma parameter INTERLACED "PVR - Interlace smoothing" 1.00 0.00 1.00 1.0 +//#pragma parameter VGASIGNAL "PVR - VGA signal loss" 0.00 0.00 1.00 1.0 +//#pragma parameter LUMBOOST "PVR - Luminance gain" 0.35 0.00 1.00 0.01 + +#define LUM_R (76.0/255.0) +#define LUM_G (150.0/255.0) +#define LUM_B (28.0/255.0) + +float4 main( + float4 gl_FragCoord : WPOS +) { + float2 texcoord = vTexCoord; + float2 texcoord2 = vTexCoord; + texcoord2.x *= float(TextureSize.x); + texcoord2.y *= float(TextureSize.y); + float4 color = tex2D(Source, texcoord); + float fc = fmod(float(FrameCount), 2.0); + +#if INTERLACED == 1 + // Blend vertically for composite mode + int taps = int(3); + float tap = (2.666f/float(taps)) / float(min(TextureSize.y, 720)); + float2 texcoord4 = vTexCoord; + texcoord4.y -= tap * 2.f; + int bl; + float4 ble; + + for (bl=0;bl0) color.r -= 0.023; + if (fmod(color.g*64, 2.0)>0) color.g -= 0.01; + if (fmod(color.b*32, 2.0)>0) color.b -= 0.023; +#endif + + // RGB565 clamp + + color.rb = floor(color.rb * 32. + 0.5)/32.; + color.g = floor(color.g * 64. + 0.5)/64.; + +#if VGASIGNAL == 1 + // VGA Signal Loss, which probably is very wrong but i tried my best + int taps = 32; + float tap = 12.0/taps; + float2 texcoord4 = vTexCoord; + texcoord4.x = texcoord4.x + (2.0/640.0); + texcoord4.y = texcoord4.y; + float4 blur1 = tex2D(Source, texcoord4); + int bl; + float4 ble; + for (bl=0;bl=3) + e=0.35; + texcoord4.x -= (tap / 640); + ble.rgb += (tex2D(Source, texcoord4).rgb * e) / (taps/(bl+1)); + } + + color.rgb += ble.rgb * 0.015; + + //color.rb += (4.0/255.0); + color.g += (9.0/255.0); +#endif + + return float4(color); +} +)"; +#else static const char* VertexShaderSource = R"( in vec3 in_pos; @@ -163,6 +296,7 @@ void main() FragColor = vec4(color); } )"; +#endif class PostProcessShader { diff --git a/core/rend/gles/quad.cpp b/core/rend/gles/quad.cpp index 5419b9340e..f1aa132c22 100644 --- a/core/rend/gles/quad.cpp +++ b/core/rend/gles/quad.cpp @@ -18,6 +18,32 @@ */ #include "gles.h" +#ifdef __vita__ +static const char* VertexShader = R"( +void main( + float3 in_pos, + float2 in_uv, + float2 out vtx_uv : TEXCOORD0, + float4 out gl_Position : POSITION +) { + vtx_uv = in_uv; +#if ROTATE == 1 + gl_Position = float4(-in_pos.y, in_pos.x, in_pos.z, 1.0); +#else + gl_Position = float4(in_pos, 1.0); +#endif +} +)"; + +static const char* FragmentShader = R"( +uniform sampler2D tex; + +float4 main(float2 vtx_uv : TEXCOORD0) +{ + return tex2D(tex, vtx_uv); +} +)"; +#else static const char* VertexShader = R"( in highp vec3 in_pos; in mediump vec2 in_uv; @@ -44,6 +70,7 @@ void main() gl_FragColor = texture(tex, vtx_uv); } )"; +#endif class QuadVertexArray final : public GlVertexArray { diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 0a48ea75fa..2e5001ee10 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -59,6 +59,15 @@ #include #include +#ifdef __vita__ +#include +extern bool is_standalone; +extern bool is_ap_on; +extern bool is_bypass_on; +extern bool folder_reset; +extern bool subfolders_read; +#endif + static bool game_started; int insetLeft, insetRight, insetTop, insetBottom; @@ -178,7 +187,7 @@ void gui_initFonts() verify(inited); -#if !defined(TARGET_UWP) && !defined(__SWITCH__) +#if !defined(TARGET_UWP) && !defined(__SWITCH__) && !defined(__vita__) settings.display.uiScale = std::max(1.f, settings.display.dpi / 100.f * 0.75f); // Limit scaling on small low-res screens if (settings.display.width <= 640 || settings.display.height <= 480) @@ -517,6 +526,11 @@ void gui_start_game(const std::string& path) chat.reset(); scanner.stop(); +#ifdef __vita__ + // FIXME: Workaround to get the json database be generated at all + if (config::BoxartDisplayMode) + gui_save(); +#endif gui_setState(GuiState::Loading); gameLoader.load(path); } @@ -644,6 +658,11 @@ static void gui_display_commands() if (ImGui::Button("Exit", ScaledVec2(300, 50) + ImVec2(ImGui::GetStyle().ColumnsMinSpacing + ImGui::GetStyle().FramePadding.x * 2 - 1, 0))) { +#ifdef __vita__ + if (is_standalone) + sceKernelExitProcess(0); + else +#endif gui_stop_game(); } @@ -1527,6 +1546,71 @@ static void gui_display_settings() ImGui::PopID(); } ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ScaledVec2(24, 3)); +#ifdef __vita__ + if (ImGui::Button("Add from ux0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select Directory 1"); + } + select_file_popup("Select Directory 1", [](bool cancelled, std::string selection) + { + if (!cancelled) + { + scanner.stop(); + config::ContentPath.get().push_back(selection); + scanner.refresh(); + } + return true; + }, false, "", "ux0:/"); + ImGui::SameLine(); + if (ImGui::Button("Add from uma0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select Directory 2"); + } + select_file_popup("Select Directory 2", [](bool cancelled, std::string selection) + { + if (!cancelled) + { + scanner.stop(); + config::ContentPath.get().push_back(selection); + scanner.refresh(); + } + return true; + }, false, "", "uma0:/"); + ImGui::SameLine(); + if (ImGui::Button("Add from imc0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select Directory 3"); + } + select_file_popup("Select Directory 3", [](bool cancelled, std::string selection) + { + if (!cancelled) + { + scanner.stop(); + config::ContentPath.get().push_back(selection); + scanner.refresh(); + } + return true; + }, false, "", "imc0:/"); + ImGui::SameLine(); + if (ImGui::Button("Add from xmc0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select Directory 4"); + } + select_file_popup("Select Directory 4", [](bool cancelled, std::string selection) + { + if (!cancelled) + { + scanner.stop(); + config::ContentPath.get().push_back(selection); + scanner.refresh(); + } + return true; + }, false, "", "xmc0:/"); +#else #ifdef __ANDROID__ if (ImGui::Button("Add")) { @@ -1543,6 +1627,7 @@ static void gui_display_settings() addContentPath(selection); return true; }); +#endif #endif ImGui::PopStyleVar(); scrollWhenDraggingOnVoid(); @@ -1873,6 +1958,9 @@ static void gui_display_settings() break; } } +#ifdef __vita__ + OptionCheckbox("Fast Sorting", config::FastSorting, "Use a more unsafe but faster algorithm for transparency sorting"); +#endif ImGui::Spacing(); ImGuiStyle& style = ImGui::GetStyle(); float innerSpacing = style.ItemInnerSpacing.x; @@ -1904,6 +1992,7 @@ static void gui_display_settings() OptionCheckbox("Widescreen Game Cheats", config::WidescreenGameHacks, "Modify the game so that it displays in 16:9 anamorphic format and use horizontal screen stretching. Only some games are supported."); +#ifndef __vita__ const std::array aniso{ 1, 2, 4, 8, 16 }; const std::array anisoText{ "Disabled", "2x", "4x", "8x", "16x" }; u32 afSelected = 0; @@ -1945,6 +2034,7 @@ static void gui_display_settings() ImGui::Text("Anisotropic Filtering"); ImGui::SameLine(); ShowHelpMarker("Higher values make textures viewed at oblique angles look sharper, but are more demanding on the GPU. This option only has a visible impact on mipmapped textures."); +#endif ImGui::Text("Texture Filtering:"); ImGui::Columns(3, "textureFiltering", false); @@ -1955,6 +2045,10 @@ static void gui_display_settings() OptionRadioButton("Force Linear", config::TextureFiltering, 2, "Force linear filtering for all textures. Smoother appearance, but may cause various rendering issues. This option usually does not affect performance."); ImGui::Columns(1, nullptr, false); +#ifdef __vita__ + OptionCheckbox("Use Mipmaps", config::UseMipmaps, "Enables the generation and use of texture mipmaps"); + OptionCheckbox("Use Simple Shaders", config::UseSimpleShaders, "Enables usage of simplified shaders"); +#endif #ifndef TARGET_IPHONE OptionCheckbox("VSync", config::VSync, "Synchronizes the frame rate with the screen refresh rate. Recommended"); if (isVulkan(config::RendererType)) @@ -2332,6 +2426,17 @@ static void gui_display_settings() "%d MHz"); } ImGui::Spacing(); +#ifdef __vita__ + ImGui::Text("Self-Modifying Code Checks:"); + ImGui::Columns(3, "DynarecSmcChecks", false); + OptionRadioButton("Off", config::DynarecSmcChecks, 0, "Disables checks for self-modifying code"); + ImGui::NextColumn(); + OptionRadioButton("Reduced", config::DynarecSmcChecks, 1, "Performs a simplified check for self-modifying code"); + ImGui::NextColumn(); + OptionRadioButton("Full", config::DynarecSmcChecks, 2, "Checks the whole code block for self-modifying code"); + ImGui::Columns(1, nullptr, false); + ImGui::Spacing(); +#endif header("Network"); { { @@ -2421,6 +2526,9 @@ static void gui_display_settings() "Enable full MMU emulation and other Windows CE settings. Do not enable unless necessary"); OptionCheckbox("Multi-threaded emulation", config::ThreadedRendering, "Run the emulated CPU and GPU on different threads"); +#ifdef __vita__ + OptionCheckbox("Fast GDRom Load", config::FastGDRomLoad, "Enables fast GDRom loading for smaller loading times."); +#endif #ifndef __ANDROID OptionCheckbox("Serial Console", config::SerialConsole, "Dump the Dreamcast serial console to stdout"); @@ -2517,6 +2625,8 @@ static void gui_display_settings() "Windows" #elif defined(__SWITCH__) "Switch" +#elif defined(__vita__) + "PSVita" #else "Unknown" #endif diff --git a/core/rend/gui_cheats.cpp b/core/rend/gui_cheats.cpp index f4e9040d7d..1510d37830 100644 --- a/core/rend/gui_cheats.cpp +++ b/core/rend/gui_cheats.cpp @@ -22,6 +22,11 @@ #include "cheats.h" #include "oslib/storage.h" +#ifdef __vita__ +extern bool folder_reset; +extern bool subfolders_read; +#endif + static bool addingCheat; static void addCheat() @@ -82,7 +87,11 @@ void gui_cheats() return; } centerNextWindow(); +#ifdef __vita__ + ImGui::SetNextWindowSize(min(ImGui::GetIO().DisplaySize, ScaledVec2(800.f, 400.f))); +#else ImGui::SetNextWindowSize(min(ImGui::GetIO().DisplaySize, ScaledVec2(600.f, 400.f))); +#endif ImGui::Begin("##main", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize); @@ -90,13 +99,65 @@ void gui_cheats() ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ScaledVec2(20, 8)); ImGui::AlignTextToFramePadding(); ImGui::Indent(10 * settings.display.uiScale); +#ifndef __vita__ ImGui::Text("CHEATS"); ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - ImGui::CalcTextSize("Add").x - ImGui::CalcTextSize("Close").x - ImGui::GetStyle().FramePadding.x * 6.f - ImGui::CalcTextSize("Load").x - ImGui::GetStyle().ItemSpacing.x * 2); +#endif if (ImGui::Button("Add")) addingCheat = true; ImGui::SameLine(); +#ifdef __vita__ + if (ImGui::Button("Load from ux0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select cheat file 1"); + } + select_file_popup("Select cheat file 1", [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "ux0:/"); + ImGui::SameLine(); + if (ImGui::Button("Load from uma0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select cheat file 2"); + } + select_file_popup("Select cheat file 2", [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "uma0:/"); + ImGui::SameLine(); + if (ImGui::Button("Load from imc0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select cheat file 3"); + } + select_file_popup("Select cheat file 3", [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "imc0:/"); + ImGui::SameLine(); + if (ImGui::Button("Load from xmc0:")) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select cheat file 4"); + } + select_file_popup("Select cheat file 4", [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "xmc0:/"); + ImGui::SameLine(); +#else #ifdef __ANDROID__ if (ImGui::Button("Load")) hostfs::addStorage(false, true, cheatFileSelected); @@ -108,6 +169,7 @@ void gui_cheats() cheatFileSelected(cancelled, selection); return true; }, true, "cht"); +#endif #endif ImGui::SameLine(); diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index 2d894642e7..dffe99908e 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -35,6 +35,9 @@ static std::string select_current_directory = "**home**"; static std::vector subfolders; static std::vector folderFiles; bool subfolders_read; +#ifdef __vita__ +bool folder_reset = true; +#endif extern int insetLeft, insetRight, insetTop, insetBottom; void error_popup(); @@ -47,9 +50,20 @@ namespace hostfs } } +#ifdef __vita__ +void select_file_popup(const char *prompt, StringCallback callback, + bool selectFile, const std::string& selectExtension, std::string startDir) +#else void select_file_popup(const char *prompt, StringCallback callback, bool selectFile, const std::string& selectExtension) +#endif { +#ifdef __vita__ + if (folder_reset) { + select_current_directory = startDir.c_str(); + folder_reset = false; + } +#endif fullScreenWindow(true); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0); @@ -59,6 +73,11 @@ void select_file_popup(const char *prompt, StringCallback callback, if (select_current_directory == "**home**") select_current_directory = hostfs::storage().getDefaultDirectory(); + +#if defined(__vita__) + if (select_current_directory.empty()) + select_current_directory = startDir.c_str(); +#endif if (!subfolders_read) { diff --git a/core/rend/gui_util.h b/core/rend/gui_util.h index a3f9403f7f..38f250c09a 100644 --- a/core/rend/gui_util.h +++ b/core/rend/gui_util.h @@ -32,8 +32,13 @@ typedef bool (*StringCallback)(bool cancelled, std::string selection); +#ifdef __vita__ +void select_file_popup(const char *prompt, StringCallback callback, + bool selectFile = false, const std::string& extension = "", std::string startDir = "ux0:/"); +#else void select_file_popup(const char *prompt, StringCallback callback, bool selectFile = false, const std::string& extension = ""); +#endif void scrollWhenDraggingOnVoid(ImGuiMouseButton mouse_button = ImGuiMouseButton_Left); diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index cfdde3f424..7aeb9fee0f 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -18,7 +18,7 @@ #include "emulator.h" #include "stdclass.h" #include "imgui/imgui.h" -#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__SWITCH__) +#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__SWITCH__) && !defined(__vita__) #include "linux-dist/icon.h" #endif #ifdef _WIN32 @@ -198,6 +198,10 @@ void input_sdl_init() checkRawInput(); +#ifdef __vita__ + for (int joy = 0; joy < SDL_NumJoysticks(); joy++) + sdl_open_joystick(joy); +#endif #if defined(__SWITCH__) || defined(__OpenBSD__) // when railed, both joycons are mapped to joystick #0, // else joycons are individually mapped to joystick #0, joystick #1, ... @@ -562,7 +566,7 @@ bool sdl_recreate_window(u32 flags) settings.display.width = windowPos.w * hdpiScaling; settings.display.height = windowPos.h * hdpiScaling; -#if !defined(GLES) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__APPLE__) +#if !defined(GLES) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__APPLE__) && !defined(__vita__) // Set the window icon u32 pixels[48 * 48]; for (int i = 0; i < 48 * 48; i++) @@ -675,7 +679,7 @@ void sdl_window_create() void sdl_window_destroy() { -#ifndef __SWITCH__ +#if !defined(__SWITCH__) && !defined(__vita__) if (!settings.naomi.slave && settings.naomi.drivingSimSlave == 0) { get_window_state(); diff --git a/core/types.h b/core/types.h index e593d1ed23..b6fc9e6e17 100644 --- a/core/types.h +++ b/core/types.h @@ -1,6 +1,10 @@ #pragma once #include "build.h" +#ifdef __vita__ +#include // For strcasecmp +#endif + #if HOST_CPU == CPU_X86 #ifdef _MSC_VER #define DYNACALL __fastcall @@ -359,7 +363,12 @@ constexpr size_t operator""_GB(unsigned long long x) return 1024 * 1024 * 1024 * x; } +#if !defined(__vita__) constexpr u32 RAM_SIZE_MAX = 32_MB; constexpr u32 VRAM_SIZE_MAX = 16_MB; +#else // Vita does not target Atomiswave/Naomi +constexpr u32 RAM_SIZE_MAX = 16_MB; +constexpr u32 VRAM_SIZE_MAX = 8_MB; +#endif constexpr u32 ARAM_SIZE_MAX = 8_MB;