diff --git a/.github/workflows/vita.yml b/.github/workflows/vita.yml new file mode 100644 index 0000000000..cc710a5ea0 --- /dev/null +++ b/.github/workflows/vita.yml @@ -0,0 +1,72 @@ +name: PlayStation Vita + +on: + push: + branches-ignore: + - 'l10n_**' + pull_request: + +env: + VITASDK: /usr/local/vitasdk + +jobs: + build: + name: Vita ${{ matrix.config.name }} + runs-on: ubuntu-latest + + strategy: + matrix: + config: + - {name: standalone, destDir: vita} + + steps: + - name: Set up build environment + run: | + sudo apt-get update + sudo apt-get -y install ccache + git clone --depth=1 https://github.com/vitasdk/vdpm.git + bash vdpm/bootstrap-vitasdk.sh + echo "$VITASDK/bin" >> "$GITHUB_PATH" + + - name: Set up dependencies + run: | + vdpm asio bzip2 curl-mbedtls kubridge libmathneon libpng libzip mbedtls openssl SceShaccCgExt vitaShaRK taihen zlib zstd xz + git clone --depth 1 https://github.com/Rinnegatamante/vitaGL.git + make -C vitaGL CIRCULAR_VERTEX_POOL=2 DEPTH_STENCIL_HACK=1 HAVE_SHADER_CACHE=1 HAVE_WRAPPED_ALLOCATORS=1 NO_DEBUG=1 -j4 install + git clone -b vitagl --depth 1 https://github.com/Northfear/SDL.git + cmake -S SDL -B SDL_build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake -DVIDEO_VITA_VGL=ON + cmake --build SDL_build --config Release + cmake --install SDL_build + + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + submodules: recursive + + - uses: actions/cache@v5 + with: + path: ~/.ccache + key: ccache-vita-${{ matrix.config.name }}-${{ github.sha }} + restore-keys: ccache-vita-${{ matrix.config.name }}- + + - name: CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake -G Ninja ${{ matrix.config.cmakeArgs }} + cmake --build build --config Release --target Flycast.vpk-vpk + + - uses: actions/upload-artifact@v6 + with: + name: flycast-vita-${{ matrix.config.name }} + path: | + build/flycast + build/Flycast.vpk + + - name: Setup Rclone + uses: AnimMouse/setup-rclone@v1 + with: + rclone_config: ${{ secrets.RCLONE_CONFIG }} + version: v1.69.1 + + - name: Upload to S3 + run: rclone copy build scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA + if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != '' diff --git a/CMakeLists.txt b/CMakeLists.txt index 13b9485db8..b4e7e6de1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,16 @@ if(APPLE) set(ZLIB_LIBRARY "-lz" CACHE STRING "Use generic linker flag for Xcode to support multiple SDKs") 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() @@ -49,7 +59,7 @@ endif() set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if(CMAKE_SYSTEM_NAME MATCHES "NetBSD|Haiku" OR NINTENDO_SWITCH) +if(CMAKE_SYSTEM_NAME MATCHES "NetBSD|Haiku" OR NINTENDO_SWITCH OR VITA) set(CMAKE_CXX_EXTENSIONS ON) else() set(CMAKE_CXX_EXTENSIONS OFF) @@ -159,7 +169,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} -Wl,-q,--wrap,memcpy,--wrap,memset,--wrap,malloc,--wrap,memalign,--wrap,free,--wrap,calloc,--wrap,realloc -fno-optimize-sibling-calls -fsigned-char -fno-short-enums -marm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q,--wrap,malloc,--wrap,memalign,--wrap,free,--wrap,calloc,--wrap,realloc -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) + + if(LIBRETRO) + add_library(${PROJECT_NAME} STATIC core/emulator.cpp) + target_compile_definitions(${PROJECT_NAME} PRIVATE LIBRETRO VITA) + set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "flycast_libretro_vita") + else() + add_executable(${PROJECT_NAME} core/emulator.cpp) + endif() + + target_compile_definitions(${PROJECT_NAME} PRIVATE GLES IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +elseif(NINTENDO_SWITCH) set(USE_VULKAN OFF) enable_language(ASM) @@ -276,7 +311,9 @@ if(WINDOWS_STORE) set(USE_VULKAN OFF) endif() -set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE -s) +if(NOT VITA) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE -s) +endif() if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /GR /GS- /utf-8) if(WINDOWS_STORE) @@ -320,7 +357,10 @@ if(NOT "${SENTRY_UPLOAD_URL}" STREQUAL "") target_compile_definitions(${PROJECT_NAME} PRIVATE SENTRY_UPLOAD="${SENTRY_UPLOAD_URL}") endif() -target_include_directories(${PROJECT_NAME} PRIVATE core core/deps core/deps/stb core/deps/json core/deps/asio/asio/include) +target_include_directories(${PROJECT_NAME} PRIVATE core core/deps core/deps/stb core/deps/json) +if (NOT VITA) + target_include_directories(${PROJECT_NAME} PRIVATE core/deps/asio/asio/include) +endif() if(LIBRETRO) target_include_directories(${PROJECT_NAME} PRIVATE shell/libretro) endif() @@ -482,7 +522,7 @@ if(NOT LIBRETRO) endif() if(NOT ANDROID AND NOT IOS) - if (NOT NINTENDO_SWITCH) + if (NOT NINTENDO_SWITCH AND NOT VITA) # DreamLink enabled for non-mobile Linux, MacOS, and Windows target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DREAMLINK_DEVICES=1) set(USE_DREAMLINK_DEVICES ON) # Must be set before adding core/sdl @@ -518,6 +558,9 @@ if(NOT LIBRETRO) if(USE_HOST_SDL) find_package(SDL2 2.0.9) endif() + if(VITA) + set(SDL2_FOUND 1) + endif() if(NOT SDL2_FOUND) set(SDL_TEST_ENABLED_BY_DEFAULT OFF) add_subdirectory(core/deps/SDL EXCLUDE_FROM_ALL) @@ -530,7 +573,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) @@ -542,7 +585,7 @@ if(NOT LIBRETRO) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_SDL USE_SDL_AUDIO) add_subdirectory(core/sdl) - if((UNIX AND NOT APPLE) OR NINTENDO_SWITCH) + if((UNIX AND NOT APPLE) OR NINTENDO_SWITCH OR VITA) find_package(CURL REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl) endif() @@ -643,6 +686,10 @@ if(PKG_CONFIG_FOUND AND NOT ANDROID AND NOT APPLE AND NOT LIBRETRO) endif() endif() +if(VITA) + target_link_libraries(${PROJECT_NAME} PRIVATE vitaGL vitashark SceShaccCg_stub SceShaccCgExt taihen_stub mathneon) +endif() + if(UNIX AND NOT APPLE AND NOT ANDROID) add_definitions( -DFLYCAST_DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}/" @@ -718,7 +765,7 @@ add_subdirectory(core/deps/nowide EXCLUDE_FROM_ALL) 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) @@ -776,11 +823,37 @@ if(USE_LIBCDIO) endif() 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() -if(NOT LIBRETRO AND NOT NINTENDO_SWITCH) +if(NOT LIBRETRO AND NOT NINTENDO_SWITCH AND NOT VITA) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_ICE) # libjuice option(BUILD_SHARED_LIBS "Build shared libraries" OFF) @@ -990,6 +1063,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 @@ -1550,7 +1629,7 @@ if(NOT LIBRETRO) COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib" $/../Frameworks/libvulkan.dylib) endif() - elseif(UNIX) + elseif(UNIX OR VITA) if(NOT BUILD_TESTING) target_sources(${PROJECT_NAME} PRIVATE core/linux-dist/main.cpp) @@ -1624,6 +1703,23 @@ if(NINTENDO_SWITCH) endif() endif() +if(VITA) + if(LIBRETRO) + # TODO? + else() + 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() +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 6ea5355047..d39d5a74dd 100755 --- a/core/build.h +++ b/core/build.h @@ -110,7 +110,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 f7ee0fc4ba..5d1d04f5f8 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 Option UIScaling("UIScaling", 100); Option UITheme("UITheme", 0); @@ -85,6 +93,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 fe254b6169..9899573dbd 100644 --- a/core/cfg/option.h +++ b/core/cfg/option.h @@ -353,6 +353,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 a2ba775341..ebbef30612 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__) || defined(__HAIKU__) +#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__) || 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 de7c3843d2..d7ecaac9c4 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__) || defined(__HAIKU__) +#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__) || 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 5a3f4e3f3a..8e9258723f 100755 --- a/core/deps/imgui/backends/imgui_impl_opengl3.cpp +++ b/core/deps/imgui/backends/imgui_impl_opengl3.cpp @@ -166,6 +166,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) io.BackendRendererName = "imgui_impl_opengl3"; io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; +#ifdef __vita__ + glsl_version = ""; +#else // Flycast: Detect version using theGLContext if (glsl_version == nullptr) { @@ -179,6 +182,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) #endif } IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString)); +#endif strcpy(bd->GlslVersionString, glsl_version); strcat(bd->GlslVersionString, "\n"); @@ -355,7 +359,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Bind texture, Draw glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); - + } } } @@ -412,10 +416,14 @@ void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) glcache.BindTexture(GL_TEXTURE_2D, gl_tex_id); if (theGLContext.getMajorVersion() >= 3) { +#ifndef __vita__ glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width); +#endif for (auto& r : tex->Updates) glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y)); +#ifndef __vita__ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif } else { @@ -479,6 +487,43 @@ 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(bd->GlslVersionString, "#version %d", &glsl_version); const GLchar* vertex_shader_glsl_120 = @@ -580,6 +625,7 @@ 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 = nullptr; @@ -628,12 +674,18 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() if (!CheckProgram(bd->ShaderHandle, "shader program")) return false; +#ifndef __vita__ glDetachShader(bd->ShaderHandle, vert_handle); glDetachShader(bd->ShaderHandle, frag_handle); +#endif glDeleteShader(vert_handle); glDeleteShader(frag_handle); +#ifdef __vita__ + bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "tex"); +#else bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture"); +#endif bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx"); bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position"); bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV"); 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/emulator.cpp b/core/emulator.cpp index e232ef42ec..32a3d962cc 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -576,6 +576,11 @@ void Emulator::loadGame(const char *path, LoadProgress *progress) settings.content.path.clear(); settings.content.fileName.clear(); } + +#ifndef __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/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index a4641de851..2a8839e78e 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -395,7 +395,7 @@ static void rend_create_renderer() case RenderType::OpenGL: renderer = rend_GLES2(); break; -#if !defined(GLES2) && !defined(__APPLE__) +#if !defined(GLES2) && !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..8eb2e4da63 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; +#ifndef __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..7bbed8c0d1 100644 --- a/core/hw/pvr/ta_util.cpp +++ b/core/hw/pvr/ta_util.cpp @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ +#include "cfg/option.h" #include "ta_ctx.h" #include "pvr_mem.h" #include @@ -130,6 +131,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 +260,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/modules/serial.cpp b/core/hw/sh4/modules/serial.cpp index aca43a0157..253e5df44b 100644 --- a/core/hw/sh4/modules/serial.cpp +++ b/core/hw/sh4/modules/serial.cpp @@ -6,7 +6,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 cace4bc949..0ae530b8d3 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(__HAIKU__) +#if defined(__unix__) || defined(__HAIKU__) || defined(__vita__) #include "log/LogManager.h" #include "emulator.h" #include "ui/mainui.h" @@ -18,6 +18,42 @@ #include #include +#ifdef __vita__ +#include +#include +#include +int _newlib_heap_size_user = 250 * 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(SUPPORT_X11) #include "x11.h" #endif @@ -44,6 +80,10 @@ void common_linux_setup(); // $HOME/.config/flycast on linux static std::string find_user_config_dir() { +#ifdef __vita__ + flycast::mkdir("ux0:data/flycast", 0777); + return "ux0:data/flycast/"; +#else std::string xdg_home; if (nowide::getenv("XDG_CONFIG_HOME") != nullptr) // If XDG_CONFIG_HOME is set explicitly, we'll use that instead of $HOME/.config @@ -67,12 +107,17 @@ static std::string find_user_config_dir() } // Unable to detect config dir, use the current folder return "."; +#endif } // Find the user data directory. // $HOME/.local/share/flycast on linux static std::string find_user_data_dir() { +#ifdef __vita__ + flycast::mkdir("ux0:data/flycast/data", 0777); + return "ux0:data/flycast/data/"; +#else std::string xdg_home; if (nowide::getenv("XDG_DATA_HOME") != nullptr) // If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.local/share @@ -96,8 +141,10 @@ static std::string find_user_data_dir() } // Unable to detect data dir, use the current folder return "."; +#endif } +#ifndef __vita__ static void addDirectoriesFromPath(std::vector& dirs, const std::string& path, const std::string& suffix) { std::string::size_type pos = 0; @@ -113,6 +160,7 @@ static void addDirectoriesFromPath(std::vector& dirs, const std::st if (pos < path.length()) dirs.push_back(path.substr(pos) + suffix); } +#endif // Find a file in the user and system config directories. // The following folders are checked in this order: @@ -127,6 +175,9 @@ static std::vector find_system_config_dirs() { std::vector dirs; +#ifdef __vita__ + dirs.push_back("ux0:data/flycast/"); +#else std::string xdg_home; if (nowide::getenv("XDG_CONFIG_HOME") != nullptr) // If XDG_CONFIG_HOME is set explicitly, we'll use that instead of $HOME/.config @@ -151,6 +202,7 @@ static std::vector find_system_config_dirs() dirs.push_back("/etc/flycast/"); // This isn't part of the XDG spec, but much more common than /etc/xdg/ dirs.push_back("/etc/xdg/flycast/"); } +#endif dirs.push_back("./"); return dirs; @@ -171,6 +223,9 @@ static std::vector find_system_data_dirs() { std::vector dirs; +#ifdef __vita__ + dirs.push_back("ux0:data/flycast/data/"); +#else std::string xdg_home; if (nowide::getenv("XDG_DATA_HOME") != nullptr) // If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.local/share @@ -200,6 +255,7 @@ static std::vector find_system_data_dirs() std::string path = (std::string)nowide::getenv("FLYCAST_BIOS_PATH"); addDirectoriesFromPath(dirs, path, "/"); } +#endif dirs.push_back("./"); dirs.push_back("data/"); @@ -210,6 +266,7 @@ static const char *selfPath; void os_RunInstance(int argc, const char *argv[]) { +#ifndef __vita__ if (fork() == 0) { std::vector localArgs; @@ -219,6 +276,7 @@ void os_RunInstance(int argc, const char *argv[]) localArgs.push_back(nullptr); execv(selfPath, &localArgs[0]); } +#endif } #if defined(USE_BREAKPAD) @@ -252,6 +310,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) @@ -283,10 +389,12 @@ int main(int argc, char* argv[]) return 0; } +#ifndef __vita__ [[noreturn]] void os_DebugBreak() { raise(SIGTRAP); std::abort(); } +#endif -#endif // __unix__ +#endif // __unix__ || __vita__ diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 9e2078486d..dd11d49fa3 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__) || defined(__HAIKU__) +#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__) || defined(__vita__) #if defined(__APPLE__) #define _XOPEN_SOURCE 1 #define __USE_GNU 1 @@ -30,6 +30,7 @@ extern "C" char __start__; #define siginfo_t switch_siginfo_t #endif // __SWITCH__ +#ifndef __vita__ void context_from_segfault(host_context_t* hctx, void* segfault_ctx); void context_to_segfault(host_context_t* hctx, void* segfault_ctx); @@ -110,6 +111,7 @@ void os_UninstallFaultHandler() sigaction(SIGBUS, &next_bus_handler, nullptr); #endif } +#endif #if !defined(__unix__) && !defined(LIBRETRO) && !defined(__SWITCH__) && !defined(__HAIKU__) [[noreturn]] void os_DebugBreak() @@ -170,9 +172,11 @@ void common_linux_setup() // exit cleanly on ^C signal(SIGINT, sigintHandler); #endif - + +#ifndef __vita__ DEBUG_LOG(BOOT, "Linux paging: sysconf %ld PAGE_SIZE %ld PAGE_MASK %lX", sysconf(_SC_PAGESIZE), (unsigned long)PAGE_SIZE, (long)PAGE_MASK); verify(PAGE_MASK==(sysconf(_SC_PAGESIZE)-1)); +#endif } #ifndef __APPLE__ 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..d5b607e51e --- /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. Extra 8MB is for ARAM mapping at 0x20000000 + reserved_size = 512 * 1024 * 1024 + ALIGN(sizeof(Sh4RCB), PAGE_SIZE) + 8_MB; + 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 0d5086b0d9..bfa77b9392 100644 --- a/core/log/LogManager.cpp +++ b/core/log/LogManager.cpp @@ -152,7 +152,7 @@ void LogManager::UpdateConfig() m_listeners[LogListener::FILE_LISTENER].reset(); } else { -#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 edec7697b8..58c791e518 100644 --- a/core/log/StringUtil.h +++ b/core/log/StringUtil.h @@ -4,7 +4,7 @@ #include #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__) #define DO_SWITCH_LOCALE #endif diff --git a/core/network/dns.cpp b/core/network/dns.cpp index e6654f36ec..179712d11e 100644 --- a/core/network/dns.cpp +++ b/core/network/dns.cpp @@ -153,7 +153,7 @@ pico_ip4 parseDnsResponsePacket(const void *buf, size_t len) return { ~0u }; } -#if !defined(_WIN32) && !defined(__SWITCH__) +#if !defined(_WIN32) && !defined(__SWITCH__) && !defined(__vita__) #include #include #endif @@ -200,7 +200,7 @@ bool is_local_address(u32 addr) } closesocket(sd); -#elif defined(__SWITCH__) +#elif defined(__SWITCH__) || defined(__vita__) // TODO #elif defined(__HAIKU__) // TODO diff --git a/core/network/net_platform.h b/core/network/net_platform.h index bf4fd410e4..2160008f5e 100644 --- a/core/network/net_platform.h +++ b/core/network/net_platform.h @@ -32,6 +32,11 @@ #define INET_ADDRSTRLEN 16 #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/network/picoppp.cpp b/core/network/picoppp.cpp index c3b4be18c0..c5ba4f31a0 100644 --- a/core/network/picoppp.cpp +++ b/core/network/picoppp.cpp @@ -57,6 +57,25 @@ extern "C" { #include #include +#ifdef __vita__ +#include +namespace asio::placeholders +{ + static inline constexpr auto& error + = std::placeholders::_1; + static inline constexpr auto& bytes_transferred + = std::placeholders::_2; + static inline constexpr auto& iterator + = std::placeholders::_2; + static inline constexpr auto& results + = std::placeholders::_2; + static inline constexpr auto& endpoint + = std::placeholders::_2; + static inline constexpr auto& signal_number + = std::placeholders::_2; +} +#endif + #define RESOLVER1_OPENDNS_COM "208.67.222.222" #define AFO_ORIG_IP 0x83f2fb3f // 63.251.242.131 in network order #define IGP_ORIG_IP 0xef2bd2cc // 204.210.43.239 in network order diff --git a/core/oslib/http_client.cpp b/core/oslib/http_client.cpp index c7c71e2293..90aa71ff98 100644 --- a/core/oslib/http_client.cpp +++ b/core/oslib/http_client.cpp @@ -309,13 +309,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) @@ -345,6 +361,11 @@ static CURL *makeCurlEasy(const std::string& url) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); // default is 300 s so 5 min +#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()); @@ -385,6 +406,7 @@ int get(const std::string& url, std::vector& content, const Headers *reqHead int post(const std::string& url, const char *payload, const char *contentType, std::vector& reply) { +#ifndef __vita__ CURL *curl = makeCurlEasy(url); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); @@ -412,6 +434,9 @@ int post(const std::string& url, const char *payload, const char *contentType, s curl_easy_cleanup(curl); return (int)httpCode; +#else + return 500; +#endif } static size_t nullCallback(char *ptr, size_t size, size_t nmemb, void *userdata) { @@ -420,6 +445,7 @@ static size_t nullCallback(char *ptr, size_t size, size_t nmemb, void *userdata) int post(const std::string& url, const std::vector& fields) { +#ifndef __vita__ CURL *curl = makeCurlEasy(url); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); @@ -449,11 +475,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/oslib/oslib.cpp b/core/oslib/oslib.cpp index e425e70e3e..be0f55f417 100644 --- a/core/oslib/oslib.cpp +++ b/core/oslib/oslib.cpp @@ -401,7 +401,12 @@ std::string getScreenshotsPath(); #endif -#if !defined(__ANDROID__) && !defined(TARGET_UWP) && !defined(TARGET_IPHONE) && !defined(__SWITCH__) +#ifdef __vita__ +void saveScreenshot(const std::string& name, const std::vector& data) +{ + throw FlycastException("Not supported on Vita"); +} +#elif !defined(__ANDROID__) && !defined(TARGET_UWP) && !defined(TARGET_IPHONE) && !defined(__SWITCH__) void saveScreenshot(const std::string& name, const std::vector& data) { diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index e55fffea31..7fa42df470 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 @@ -74,7 +74,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 16ff81d2e8..962ee806e1 100644 --- a/core/oslib/storage.cpp +++ b/core/oslib/storage.cpp @@ -121,9 +121,11 @@ class StdStorage : public Storage if (S_ISDIR(st.st_mode)) isDir = true; #elif !defined(_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 fcff5d6ea7..6567a73bc2 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__) || defined(__HAIKU__) +#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__) || defined(__vita__) int stackOffset = 0; uintptr_t lastOffset = 0; std::vector cieInstructions; diff --git a/core/oslib/virtmem.h b/core/oslib/virtmem.h index 1a62db5c35..704fd1f2e0 100644 --- a/core/oslib/virtmem.h +++ b/core/oslib/virtmem.h @@ -7,7 +7,7 @@ #define DECLARE_CODE_CACHE(Name, Size) alignas(4096) static u8 Name[Size]; #elif defined(__OpenBSD__) #define DECLARE_CODE_CACHE(Name, Size) alignas(4096) static u8 Name[Size] __attribute__((section(".openbsd.mutable"))); -#elif defined(__unix__) || defined(__SWITCH__) || defined(__HAIKU__) +#elif defined(__unix__) || defined(__SWITCH__) || defined(__HAIKU__) || defined(__vita__) #define DECLARE_CODE_CACHE(Name, Size) alignas(4096) static u8 Name[Size] __attribute__((section(".text"))); #else #error Unknown platform for dynarec code cache declaration diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 91ac7e598f..a583e1650e 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -1956,7 +1956,11 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op } if (!rd.Is(rs1)) Vmov(rd, rs1); +#ifdef __ARM_FEATURE_FMA Vfma(rd, rs2, rs3); +#else + Vmla(rd, rs2, rs3); +#endif } break; @@ -2168,6 +2172,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); @@ -2179,7 +2186,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) @@ -2214,6 +2225,9 @@ void Arm32Assembler::compile(RuntimeBlockInfo* block, bool force_checks, bool op } } } +#ifdef __vita__ + } +#endif if (mmu_enabled() && block->has_fpu_op) { Mov(r0, block->vaddr); diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index 9316bef5cc..ad757546e4 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -548,7 +548,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/gles/gles.cpp b/core/rend/gles/gles.cpp index 27cf909d98..3d5b134f73 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -34,7 +34,284 @@ #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; + +#if DITHERING == 1 + mediump 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 + ); + mediump 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 +#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); +} +)"; +#else const char* ShaderCompatSource = R"( #define GLES2 0 #define GLES3 1 @@ -394,7 +671,7 @@ void main() gl_FragColor=vec4(0.0, 0.0, 0.0, sp_ShaderColor); } )"; - +#endif void os_VideoRoutingTermGL(); GLCache glcache; @@ -431,7 +708,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; @@ -542,7 +821,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; @@ -600,6 +881,9 @@ void findGLVersion() if (anisotropicExtension) glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &gl.max_anisotropy); } +#endif +#ifdef __vita__ + gl.glsl_version_header = ""; #endif const char *vendor = (const char *)glGetString(GL_VENDOR); const char *renderer = (const char *)glGetString(GL_RENDERER); @@ -703,8 +987,10 @@ GLuint gl_CompileAndLink(const char *vertexShader, const char *fragmentShader) throw RendererException(i18n::T("OpenGL shader compilation failed")); // FIXME MINIDUMP-8A1, MINIDUMP-8A2, MINIDUMP-7YE } +#ifndef __vita__ glDetachShader(program, vs); glDetachShader(program, ps); +#endif glDeleteShader(vs); glDeleteShader(ps); @@ -768,6 +1054,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); @@ -795,6 +1084,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); @@ -883,6 +1175,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); @@ -976,7 +1271,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 @@ -1015,7 +1310,9 @@ static void updateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image u8 temp_tex_buffer[256]; MakeFogTexture(temp_tex_buffer); +#ifndef __vita__ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#endif GLint internalformat; if (gl.is_gles && fog_image_format == GL_RED) internalformat = GL_R8; @@ -1043,7 +1340,9 @@ static void updatePaletteTexture(GLenum texture_slot) glcache.BindTexture(GL_TEXTURE_2D, paletteTextureId); } +#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 348ab0612d..c8f22a119f 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -228,7 +228,9 @@ void ReadRTTBuffer() VramLockedWriteOffset(page); #endif +#ifndef __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 001999cc1d..e6db5e4cfd 100644 --- a/core/rend/gles/opengl_driver.cpp +++ b/core/rend/gles/opengl_driver.cpp @@ -93,8 +93,10 @@ ImTextureID OpenGLDriver::updateTexture(const std::string& name, const u8 *data, } 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 5d3b4efdca..9aea6b447a 100644 --- a/core/rend/gles/postprocess.cpp +++ b/core/rend/gles/postprocess.cpp @@ -25,6 +25,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; @@ -164,6 +297,7 @@ void main() FragColor = vec4(color); } )"; +#endif class PostProcessShader { diff --git a/core/rend/gles/quad.cpp b/core/rend/gles/quad.cpp index d331b703cd..eaec66834a 100644 --- a/core/rend/gles/quad.cpp +++ b/core/rend/gles/quad.cpp @@ -18,6 +18,32 @@ */ #include "quad.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; @@ -45,6 +71,7 @@ void main() gl_FragColor = tint * texture(tex, vtx_uv); } )"; +#endif GlQuadDrawer::GlQuadDrawer() { diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index 667b5a85f5..c58cb28768 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -19,7 +19,7 @@ #include "stdclass.h" #include "imgui.h" #include "hw/naomi/card_reader.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 @@ -241,6 +241,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, ... @@ -735,7 +739,7 @@ bool sdl_recreate_window(u32 flags) } #endif -#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++) @@ -859,7 +863,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 4d79eb5f64..d9e4602d62 100644 --- a/core/types.h +++ b/core/types.h @@ -259,9 +259,15 @@ constexpr size_t operator""_GB(unsigned long long x) return 1024 * 1024 * 1024 * x; } +#ifndef __vita__ constexpr u32 RAM_SIZE_MAX = 32_MB; constexpr u32 VRAM_SIZE_MAX = 16_MB; constexpr u32 ARAM_SIZE_MAX = 8_MB; +#else // Vita does not target Atomiswave/Naomi +constexpr u32 RAM_SIZE_MAX = 16_MB; +constexpr u32 VRAM_SIZE_MAX = 8_MB; +constexpr u32 ARAM_SIZE_MAX = 2_MB; +#endif constexpr unsigned operator""_sh4ms(unsigned long long t) { return SH4_MAIN_CLOCK / 1'000 * t; diff --git a/core/ui/gui.cpp b/core/ui/gui.cpp index 501b179e3e..b3a676d362 100644 --- a/core/ui/gui.cpp +++ b/core/ui/gui.cpp @@ -64,6 +64,15 @@ using namespace i18n; #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 + bool game_started; int insetLeft, insetRight, insetTop, insetBottom; @@ -206,7 +215,7 @@ void gui_updateStyle() verify(inited); uiThreadRunner.init(); -#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) @@ -455,6 +464,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) + boxart.term(); +#endif gui_setState(GuiState::Loading); gameLoader.load(path); } @@ -626,6 +640,11 @@ static void gui_display_commands() // Exit if (IconButton(ICON_FA_POWER_OFF, commandLineStart ? T("Exit") : T("Close Game"), ScaledVec2(buttonWidth, 50)).realize()) +#ifdef __vita__ + if (is_standalone) + sceKernelExitProcess(0); + else +#endif gui_stop_game(); ImGui::NextColumn(); @@ -1239,7 +1258,7 @@ static void gui_display_loadscreen() else label = T("Loading..."); } - + const bool customTexPreloading = custom_texture.isPreloading(); if (gameLoader.ready() && !customTexPreloading) @@ -1261,11 +1280,11 @@ static void gui_display_loadscreen() int texTotal = 0; size_t loaded_size_b = 0; custom_texture.getPreloadProgress(texLoaded, texTotal, loaded_size_b); - + ImGui::Text("%s", label); float progress = 0; char overlay[64] = ""; - + if (!gameLoader.ready()) { progress = gameLoader.getProgress().progress; @@ -1283,7 +1302,7 @@ static void gui_display_loadscreen() snprintf(overlay, sizeof(overlay), "%d / %d (%.1f MB)", texLoaded, texTotal, loaded_size_mb); } } - + ImguiStyleColor _(ImGuiCol_PlotHistogram, ImVec4(0.557f, 0.268f, 0.965f, 1.f)); ImGui::ProgressBar(progress, ImVec2(-1, uiScaled(20.f)), overlay); diff --git a/core/ui/gui_cheats.cpp b/core/ui/gui_cheats.cpp index 98709a4654..56176fa218 100644 --- a/core/ui/gui_cheats.cpp +++ b/core/ui/gui_cheats.cpp @@ -27,6 +27,11 @@ #endif using namespace i18n; +#ifdef __vita__ +extern bool folder_reset; +extern bool subfolders_read; +#endif + static void addCheat() { static char cheatName[64]; @@ -97,17 +102,69 @@ void gui_cheats() ImguiStyleVar _(ImGuiStyleVar_FramePadding, ScaledVec2(20, 8)); ImGui::AlignTextToFramePadding(); ImGui::Indent(uiScaled(10)); - ImGui::Text("%s", (std::string(ICON_FA_MASK " ") + T("CHEATS")).c_str()); - const char *addLbl = T("Add"); const char *closeLbl = T("Close"); +#ifndef __vita__ + ImGui::Text("%s", (std::string(ICON_FA_MASK " ") + T("CHEATS")).c_str()); + const char *loadLbl = T("Load"); ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - ImGui::CalcTextSize(addLbl).x - ImGui::CalcTextSize(closeLbl).x - ImGui::GetStyle().FramePadding.x * 6.f - ImGui::CalcTextSize(loadLbl).x - ImGui::GetStyle().ItemSpacing.x * 2); +#endif if (ImGui::Button(addLbl)) ImGui::OpenPopup("addCheat"); addCheat(); ImGui::SameLine(); +#ifdef __vita__ + if (ImGui::Button(T("Load from ux0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup(T("Select cheat file 1")); + } + select_file_popup(T("Select cheat file 1"), [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "ux0:/"); + ImGui::SameLine(); + if (ImGui::Button(T("Load from uma0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select cheat file 2"); + } + select_file_popup(T("Select cheat file 2"), [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "uma0:/"); + ImGui::SameLine(); + if (ImGui::Button(T("Load from imc0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup(T("Select cheat file 3")); + } + select_file_popup(T("Select cheat file 3"), [](bool cancelled, std::string selection) + { + if (!cancelled) + cheatManager.loadCheatFile(selection); + return true; + }, true, "cht", "imc0:/"); + ImGui::SameLine(); + if (ImGui::Button(T("Load from xmc0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup("Select cheat file 4"); + } + select_file_popup(T("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(loadLbl)) { @@ -117,6 +174,7 @@ void gui_cheats() #else if (ImGui::Button(loadLbl)) ImGui::OpenPopup(title); +#endif #endif ImGui::SameLine(); diff --git a/core/ui/gui_util.cpp b/core/ui/gui_util.cpp index 9a72c28ce2..163be786d5 100644 --- a/core/ui/gui_util.cpp +++ b/core/ui/gui_util.cpp @@ -43,6 +43,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(); @@ -54,9 +57,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); ImguiStyleVar _(ImGuiStyleVar_WindowRounding, 0); ImguiStyleVar _1(ImGuiStyleVar_FramePadding, ImVec2(4, 3)); // default @@ -67,6 +81,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/ui/gui_util.h b/core/ui/gui_util.h index edc7121d28..e499de5da6 100644 --- a/core/ui/gui_util.h +++ b/core/ui/gui_util.h @@ -36,8 +36,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/ui/settings.cpp b/core/ui/settings.cpp index c48e299975..56743de185 100644 --- a/core/ui/settings.cpp +++ b/core/ui/settings.cpp @@ -88,6 +88,22 @@ static void gui_settings_advanced() ImGui::SameLine(0, style.ItemInnerSpacing.x); ShowHelpMarker("Default port is 3263"); } +#endif +#ifdef __vita__ + if (config::DynarecEnabled) + { + ImGui::Spacing(); + header(T("Dynarec Options")); + ImGui::Text("Self-Modifying Code Checks:"); + ImGui::Columns(3, "DynarecSmcChecks", false); + OptionRadioButton(T("Off"), config::DynarecSmcChecks, 0, T("Disables checks for self-modifying code")); + ImGui::NextColumn(); + OptionRadioButton(T("Reduced"), config::DynarecSmcChecks, 1, T("Performs a simplified check for self-modifying code")); + ImGui::NextColumn(); + OptionRadioButton(T("Full"), config::DynarecSmcChecks, 2, T("Checks the whole code block for self-modifying code")); + ImGui::Columns(1, nullptr, false); + ImGui::Spacing(); + } #endif ImGui::Spacing(); #endif @@ -96,6 +112,9 @@ static void gui_settings_advanced() OptionCheckbox(T("HLE BIOS"), config::UseReios, T("Force high-level BIOS emulation")); OptionCheckbox(T("Multi-threaded emulation"), config::ThreadedRendering, T("Run the emulated CPU and GPU on different threads")); +#ifdef __vita__ + OptionCheckbox(T("Fast GDRom Load"), config::FastGDRomLoad, T("Enables fast GDRom loading for smaller loading times.")); +#endif #if !defined(__ANDROID) && !defined(GDB_SERVER) OptionCheckbox(T("Serial Console"), config::SerialConsole, T("Dump the Dreamcast serial console to stdout")); diff --git a/core/ui/settings_about.cpp b/core/ui/settings_about.cpp index 24eea30729..4165c8712a 100644 --- a/core/ui/settings_about.cpp +++ b/core/ui/settings_about.cpp @@ -87,6 +87,8 @@ void gui_settings_about() "Windows" #elif defined(__SWITCH__) "Switch" +#elif defined(__vita__) + "PSVita" #else T("Unknown") #endif diff --git a/core/ui/settings_general.cpp b/core/ui/settings_general.cpp index 4bee8134af..35a37c3078 100644 --- a/core/ui/settings_general.cpp +++ b/core/ui/settings_general.cpp @@ -25,6 +25,11 @@ #include "achievements/achievements.h" #include "imgui_stdlib.h" +#ifdef __vita__ +extern bool folder_reset; +extern bool subfolders_read; +#endif + static std::vector* g_currentPathList = nullptr; static void managePathListCallback(std::string selection) { @@ -41,7 +46,7 @@ static void manageSinglePath(const char* label, const char *popupName, config::O size.x = 0.0f; size.y = ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().FramePadding.y * 2.f; bool openPopup = false; - + ImVec2 childSize; if (beginFrame(label, size, &childSize)) { @@ -66,7 +71,7 @@ static void manageSinglePath(const char* label, const char *popupName, config::O } ImGui::SameLine(); ShowHelpMarker(helpText); - + static std::string *pCurrentPath; pCurrentPath = &pathOption.get(); select_file_popup(popupName, [](bool cancelled, std::string selection) { @@ -158,7 +163,52 @@ static void addContentPathCallback(const std::string& path) void addContentPath(bool start) { - const char *title = T("Select a Content Folder"); +#ifdef __vita__ + if (ImGui::Button(T("Add from ux0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup(T("Select Directory 1")); + } + select_file_popup(T("Select Directory 1"), [](bool cancelled, std::string selection) { + if (!cancelled) + addContentPathCallback(selection); + return true; + }, false, "", "ux0:/"); + ImGui::SameLine(); + if (ImGui::Button(T("Add from uma0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup(T("Select Directory 2")); + } + select_file_popup(T("Select Directory 2"), [](bool cancelled, std::string selection) { + if (!cancelled) + addContentPathCallback(selection); + return true; + }, false, "", "uma0:/"); + ImGui::SameLine(); + if (ImGui::Button(T("Add from imc0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup(T("Select Directory 3")); + } + select_file_popup(T("Select Directory 3"), [](bool cancelled, std::string selection) { + if (!cancelled) + addContentPathCallback(selection); + return true; + }, false, "", "imc0:/"); + ImGui::SameLine(); + if (ImGui::Button(T("Add from xmc0:"))) { + folder_reset = true; + subfolders_read = false; + ImGui::OpenPopup(T("Select Directory 4")); + } + select_file_popup(T("Select Directory 4"), [](bool cancelled, std::string selection) { + if (!cancelled) + addContentPathCallback(selection); + return true; + }, false, "", "xmc0:/"); +#else + const char *title = T("Select a Content Folder"); select_file_popup(title, [](bool cancelled, std::string selection) { if (!cancelled) addContentPathCallback(selection); @@ -178,6 +228,7 @@ void addContentPath(bool start) if (start) ImGui::OpenPopup(title); #endif +#endif } void gui_settings_general() @@ -291,7 +342,11 @@ void gui_settings_general() } ImguiStyleVar _(ImGuiStyleVar_FramePadding, ScaledVec2(24, 3)); +#ifdef __vita__ + const bool addContent = false; +#else const bool addContent = ImGui::Button((T("Add") + std::string("##") + "ContentLocation").c_str()); +#endif addContentPath(addContent); ImGui::SameLine(); @@ -490,7 +545,7 @@ void gui_settings_general() manageSinglePath(T("Texture Dump Folder"), T("Select the texture dump folder"), config::TextureDumpPath, T("Folder where texture dumps are saved. Game-specific subfolders will be created automatically")); ImGui::Spacing(); - + manageSinglePath(T("Box Art Folder"), T("Select the box art folder"), config::BoxartPath, T("Folder containing box art images (png/jpg). If empty, Flycast will use the default Home Folder/boxart for downloads and generated art")); ImGui::Spacing(); diff --git a/core/ui/settings_video.cpp b/core/ui/settings_video.cpp index 976999de63..88a9966023 100644 --- a/core/ui/settings_video.cpp +++ b/core/ui/settings_video.cpp @@ -148,6 +148,9 @@ void gui_settings_video() break; } } +#ifdef __vita__ + OptionCheckbox(T("Fast Sorting"), config::FastSorting, T("Use a more unsafe but faster algorithm for transparency sorting")); +#endif ImGui::Spacing(); header(T("Rendering Options")); @@ -332,6 +335,7 @@ void gui_settings_video() T("Helps with texture corruption and depth issues on AMD GPUs. Can also help Intel GPUs in some cases.")); OptionCheckbox(T("Copy Rendered Textures to VRAM"), config::RenderToTextureBuffer, T("Copy rendered-to textures back to VRAM. Slower but accurate")); +#ifndef __vita__ const std::array aniso{ 1, 2, 4, 8, 16 }; const std::array anisoText{ T("Disabled"), "2x", "4x", "8x", "16x" }; u32 afSelected = 0; @@ -373,6 +377,7 @@ void gui_settings_video() ImGui::Text("%s", T("Anisotropic Filtering")); ImGui::SameLine(); ShowHelpMarker(T("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("%s", T("Texture Filtering:")); ImGui::Columns(3, "textureFiltering", false); @@ -383,6 +388,11 @@ void gui_settings_video() OptionRadioButton(T("Force Linear"), config::TextureFiltering, 2, T("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(T("Use Mipmaps"), config::UseMipmaps, T("Enables the generation and use of texture mipmaps")); + OptionCheckbox(T("Use Simple Shaders"), config::UseSimpleShaders, T("Enables usage of simplified shaders")); +#endif + OptionCheckbox(T("Show FPS Counter"), config::ShowFPS, T("Show on-screen frame/sec counter")); } #ifdef VIDEO_ROUTING diff --git a/shell/vita/bg.png b/shell/vita/bg.png new file mode 100644 index 0000000000..bfaf199d7d Binary files /dev/null and b/shell/vita/bg.png differ diff --git a/shell/vita/icon0.png b/shell/vita/icon0.png new file mode 100644 index 0000000000..ffc3c5040b Binary files /dev/null and b/shell/vita/icon0.png differ diff --git a/shell/vita/startup.png b/shell/vita/startup.png new file mode 100644 index 0000000000..2dff6df3fc Binary files /dev/null and b/shell/vita/startup.png differ diff --git a/shell/vita/template.xml b/shell/vita/template.xml new file mode 100644 index 0000000000..a4d43f0130 --- /dev/null +++ b/shell/vita/template.xml @@ -0,0 +1,11 @@ + + + + + bg.png + + + + startup.png + +