From 14ed2f9929a1fed7b57645eda147f08bb927106e Mon Sep 17 00:00:00 2001 From: xfangfang <2553041586@qq.com> Date: Thu, 14 Nov 2024 09:24:29 +0800 Subject: [PATCH] PSV: Support gxm graphics api --- .github/workflows/ci.yaml | 21 +- CMakeLists.txt | 27 +- README.md | 23 +- library/CMakeLists.txt | 72 +- library/cmake/commonOption.cmake | 4 + library/cmake/toolchain.cmake | 20 +- library/include/borealis/core/application.hpp | 1 - .../borealis/extern/nanovg/nanovg_gxm.h | 1764 +++++++++++++++++ .../borealis/extern/nanovg/nanovg_gxm_utils.h | 905 +++++++++ .../borealis/platforms/psv/psv_ime.hpp | 39 + .../borealis/platforms/psv/psv_input.hpp | 52 + .../borealis/platforms/psv/psv_platform.hpp | 68 +- .../borealis/platforms/psv/psv_video.hpp | 45 + library/lib/core/application.cpp | 4 +- library/lib/core/util.cpp | 8 + library/lib/extern/nanovg/nanovg.c | 4 +- library/lib/platforms/psv/psv_ime.cpp | 246 +++ library/lib/platforms/psv/psv_input.cpp | 170 ++ library/lib/platforms/psv/psv_platform.cpp | 110 +- library/lib/platforms/psv/psv_video.cpp | 114 ++ 20 files changed, 3624 insertions(+), 73 deletions(-) create mode 100644 library/include/borealis/extern/nanovg/nanovg_gxm.h create mode 100644 library/include/borealis/extern/nanovg/nanovg_gxm_utils.h create mode 100644 library/include/borealis/platforms/psv/psv_ime.hpp create mode 100644 library/include/borealis/platforms/psv/psv_input.hpp create mode 100644 library/include/borealis/platforms/psv/psv_video.hpp create mode 100644 library/lib/platforms/psv/psv_ime.cpp create mode 100644 library/lib/platforms/psv/psv_input.cpp create mode 100644 library/lib/platforms/psv/psv_video.cpp diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d230b4a22..f8c30b745 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -57,20 +57,37 @@ jobs: psv: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - { driver: gxm } + - { driver: gles2 } steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive - - name: Build + + - name: Build GLES2 + if: ${{ matrix.driver == 'gles2' }} run: | docker run --rm -v $(pwd):/src/ xfangfang/vitasdk_sdl2_gles2:latest \ "cmake -B build -G Ninja -DPLATFORM_PSV=ON -DUSE_SYSTEM_SDL2=ON -DCMAKE_BUILD_TYPE=Release && \ cmake --build build" + + - name: Build GXM + if: ${{ matrix.driver == 'gxm' }} + run: | + echo "cd /src" > build_gxm.sh + echo "cmake -B build -DPLATFORM_PSV=ON -DCMAKE_BUILD_TYPE=Release -DUSE_GXM=ON" >> build_gxm.sh + echo "cmake --build build -- -j$(nproc)" >> build_gxm.sh + docker run --rm -v $(pwd):/src/ vitasdk/vitasdk:latest sh -C /src/build_gxm.sh + - name: Upload dist uses: actions/upload-artifact@v4 with: - name: borealis-psv + name: borealis-psv-${{ matrix.driver }} path: build/*.vpk switch: diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cc98afd1..091836518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,22 +107,21 @@ if (PLATFORM_DESKTOP) elseif (PLATFORM_PSV) set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d ATTRIBUTE2=12") # max heap size mode vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME} UNSAFE) - if (USE_LIBROMFS) - vita_create_vpk(${PROJECT_NAME}.vpk ${PSN_TITLE_ID} ${PROJECT_NAME}.self - VERSION ${PSN_VERSION} - NAME ${PROJECT_NAME} - FILE ${CMAKE_SOURCE_DIR}/psv/sce_sys sce_sys - FILE ${CMAKE_SOURCE_DIR}/psv/module/ module - ) - else() - vita_create_vpk(${PROJECT_NAME}.vpk ${PSN_TITLE_ID} ${PROJECT_NAME}.self + set(PSV_ASSETS_FILES ${CMAKE_SOURCE_DIR}/psv/sce_sys sce_sys) + if (NOT USE_LIBROMFS) + list(APPEND PSV_ASSETS_FILES ${CMAKE_SOURCE_DIR}/resources resources) + endif () + if (NOT USE_GXM) + list(APPEND PSV_ASSETS_FILES ${CMAKE_SOURCE_DIR}/psv/module/ module) + endif () + if (USE_VITA_SHARK) + list(APPEND PSV_ASSETS_FILES "${CMAKE_BINARY_DIR}/vendor/SceShaccCg" module) + endif () + vita_create_vpk(${PROJECT_NAME}.vpk ${PSN_TITLE_ID} ${PROJECT_NAME}.self VERSION ${PSN_VERSION} NAME ${PROJECT_NAME} - FILE ${CMAKE_SOURCE_DIR}/resources resources - FILE ${CMAKE_SOURCE_DIR}/psv/sce_sys sce_sys - FILE ${CMAKE_SOURCE_DIR}/psv/module/ module - ) - endif() + FILE ${PSV_ASSETS_FILES} + ) elseif (PLATFORM_PS4) # There is no `add_self` and `add_pkg` functions without pacbrew toolchain. # We can install the OpenOrbits SDK so that the IDE can provide some basic code prompts, diff --git a/README.md b/README.md index 175c365a1..d6317f700 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,13 @@ Check the [daily builds](https://github.com/xfangfang/borealis/actions) for what To build for Switch, a standard development environment must first be set up. In order to do so, [refer to the Getting Started guide](https://devkitpro.org/wiki/Getting_Started). ```bash +# 1. OpenGL cmake -B build_switch -DPLATFORM_SWITCH=ON make -C build_switch borealis_demo.nro -j$(nproc) + +# 2. deko3d +cmake -B build_switch -DPLATFORM_SWITCH=ON -DUSE_DEKO3D=ON +make -C build_switch borealis_demo.nro -j$(nproc) ``` ## Building the demo for PC @@ -70,16 +75,26 @@ xmake b -y demo ## Building the demo for PSV - install [VITASDK](https://github.com/vitasdk/vdpm) -- install [PVR_PSP2](https://github.com/GrapheneCt/PVR_PSP2) headers and libs. refer to: [SDL/vita.yaml](https://github.com/libsdl-org/SDL/blob/5733f42c7c2cbfbbd03282919534ed30c3b07da6/.github/workflows/vita.yaml#L28-L44) -- put `*.suprx` files ([PVR_PSP2](https://github.com/GrapheneCt/PVR_PSP2)) to `psv/module` +- (GLES2.0 Only) install [PVR_PSP2](https://github.com/GrapheneCt/PVR_PSP2) headers and libs. refer to: [SDL/vita.yaml](https://github.com/libsdl-org/SDL/blob/5733f42c7c2cbfbbd03282919534ed30c3b07da6/.github/workflows/vita.yaml#L28-L44) +- (GLES2.0 Only) put `*.suprx` files ([PVR_PSP2](https://github.com/GrapheneCt/PVR_PSP2)) to `psv/module` - Unlock unsafe mode in `System Settings/HENkaku` -> We only need: `libGLESv2.suprx` `libgpu_es4_ext.suprx` `libIMGEGL.suprx` `libpvrPSP2_WSEGL.suprx` -> Overclock ES4(GPU) to 166MHz or higher for a smoother experience. +> (GLES2.0 Only) We only need: `libGLESv2.suprx` `libgpu_es4_ext.suprx` `libIMGEGL.suprx` `libpvrPSP2_WSEGL.suprx` +> (GLES2.0 Only) Overclock ES4(GPU) to 166MHz or higher for a smoother experience. ```bash +# 1. OpenGL ES 2.0 cmake -B build_psv -DPLATFORM_PSV=ON make -C build_psv borealis_demo.vpk -j$(nproc) + +# 2. OpenGL ES 2.0; Using Docker image +docker run --rm -v $(pwd):/src/ xfangfang/vitasdk_sdl2_gles2:latest \ + "cmake -B build -G Ninja -DPLATFORM_PSV=ON -DUSE_SYSTEM_SDL2=ON -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build" + +# 3. GXM +cmake -B build_psv -DPLATFORM_PSV=ON -DUSE_GXM=ON +make -C build_psv borealis_demo.vpk -j$(nproc) ``` #### My daily development experience on PSV diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 085b1f229..f81278377 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -116,27 +116,58 @@ if (PLATFORM_DESKTOP) list(APPEND BOREALIS_SOURCE ${BOREALIS_PATH}/lib/platforms/desktop) set(BRLS_PLATFORM_RESOURCES_PATH "\"${BRLS_RESOURCES_DIR}/resources/\"") elseif (PLATFORM_PSV) - if (USE_SYSTEM_SDL2) - find_package(SDL2 REQUIRED) - else () - set(BUILD_SHARED_LIBS OFF) - set(VIDEO_VITA_PVR ON CACHE BOOL "") - add_subdirectory(${BOREALIS_PATH}/lib/extern/SDL EXCLUDE_FROM_ALL) - endif () - list(APPEND BOREALIS_SOURCE - ${BOREALIS_PATH}/lib/platforms/psv - ${BOREALIS_PATH}/lib/platforms/sdl - ${BOREALIS_PATH}/lib/platforms/desktop) + list(APPEND BOREALIS_SOURCE ${BOREALIS_PATH}/lib/platforms/desktop) list(APPEND BRLS_PLATFORM_LIBS - SDL2::SDL2-static - libgpu_es4_ext_stub_weak - libIMGEGL_stub_weak - libGLESv2_stub_weak + SceGxm_stub + SceDisplay_stub + SceCtrl_stub + SceCommonDialog_stub + SceTouch_stub + SceAppUtil_stub + SceCtrl_stub + SceIme_stub + ScePower_stub SceAVConfig_stub SceRegistryMgr_stub - -Wl,--whole-archive -lpthread -Wl,--no-whole-archive - ) - list(APPEND BRLS_PLATFORM_OPTION -D__SDL2__ -D__PSV__ -DUSE_GLES2) + -Wl,--whole-archive -lpthread -Wl,--no-whole-archive) + if (BOREALIS_USE_GXM) + if (USE_VITA_SHARK) + include(FetchContent) + FetchContent_Populate( + libshacccg + URL "https://codeload.github.com/AnimMouse/SceShaccCg/zip/refs/heads/main" + SOURCE_DIR "${CMAKE_BINARY_DIR}/vendor/SceShaccCg" + SUBBUILD_DIR "${CMAKE_BINARY_DIR}/CMakeFiles/SceShaccCg" + BINARY_DIR "${CMAKE_BINARY_DIR}/CMakeFiles/SceShaccCg" + ) + list(APPEND BRLS_PLATFORM_LIBS + vitashark + SceShaccCgExt + taihen_stub + SceShaccCg_stub + ) + endif () + file(GLOB_RECURSE PSV_PLATFORM_SRC "${BOREALIS_PATH}/lib/platforms/psv/*.cpp") + list(APPEND BOREALIS_SRC ${PSV_PLATFORM_SRC}) + else() + if (USE_SYSTEM_SDL2) + find_package(SDL2 REQUIRED) + else () + set(BUILD_SHARED_LIBS OFF) + set(VIDEO_VITA_PVR ${BOREALIS_USE_GXM} CACHE BOOL "") + add_subdirectory(${BOREALIS_PATH}/lib/extern/SDL EXCLUDE_FROM_ALL) + endif () + list(APPEND BRLS_PLATFORM_LIBS + SDL2::SDL2-static + libgpu_es4_ext_stub_weak + libIMGEGL_stub_weak + libGLESv2_stub_weak) + # Using SDL implementation instead + file(GLOB_RECURSE SDL_PLATFORM_SRC "${BOREALIS_PATH}/lib/platforms/sdl/*.cpp") + list(APPEND BOREALIS_SRC ${SDL_PLATFORM_SRC}) + list(APPEND BOREALIS_SRC ${BOREALIS_PATH}/lib/platforms/psv/psv_platform.cpp) + endif () + list(APPEND BRLS_PLATFORM_OPTION -D__PSV__ -DSTBI_NEON) set(BRLS_PLATFORM_RESOURCES_PATH "\"app0:resources/\"") set_target_properties(yogacore PROPERTIES POSITION_INDEPENDENT_CODE OFF) elseif(PLATFORM_PS4) @@ -255,6 +286,11 @@ elseif (BOREALIS_USE_D3D11) set(BOREALIS_DRIVER BOREALIS_USE_D3D11) elseif (BOREALIS_USE_DEKO3D) set(BOREALIS_DRIVER BOREALIS_USE_DEKO3D) +elseif (BOREALIS_USE_GXM) + set(BOREALIS_DRIVER BOREALIS_USE_GXM) + if (USE_VITA_SHARK) + list(APPEND BRLS_PLATFORM_OPTION -DUSE_VITA_SHARK) + endif () endif () list(APPEND BRLS_PLATFORM_OPTION -D${BOREALIS_DRIVER}) message(STATUS "borealis driver ${BOREALIS_DRIVER}") diff --git a/library/cmake/commonOption.cmake b/library/cmake/commonOption.cmake index 2eba760c8..69e2e075d 100644 --- a/library/cmake/commonOption.cmake +++ b/library/cmake/commonOption.cmake @@ -24,6 +24,10 @@ cmake_dependent_option(INSTALL "Install to system." OFF "UNIX;NOT APPLE" OFF) # PS4 Only cmake_dependent_option(LIBJBC "Root access enabled" OFF "PLATFORM_PS4" OFF) +# PSVita Only +cmake_dependent_option(USE_GXM "Using gxm instead of OpenGL." OFF "PLATFORM_PSV" OFF) +cmake_dependent_option(USE_VITA_SHARK "Using runtime shader compiler." OFF "USE_GXM" OFF) + # iOS Only (If empty then not sign) set(IOS_CODE_SIGN_IDENTITY "" CACHE STRING "The code sign identity to use when building the IPA.") set(IOS_GUI_IDENTIFIER "" CACHE STRING "Package name.") diff --git a/library/cmake/toolchain.cmake b/library/cmake/toolchain.cmake index c37f81c8e..80448563a 100644 --- a/library/cmake/toolchain.cmake +++ b/library/cmake/toolchain.cmake @@ -14,6 +14,9 @@ if (PLATFORM_DESKTOP) message(STATUS "building for Desktop") set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -g2 -ggdb -Wall") set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") + if (NOT USE_GLFW AND NOT SDL2) + set(USE_GLFW ON) + endif () elseif (PLATFORM_IOS OR PLATFORM_TVOS) if(PLATFORM_IOS) message(STATUS "building for iOS") @@ -48,9 +51,12 @@ elseif(PLATFORM_ANDROID) check_libromfs_generator() elseif(PLATFORM_PSV) message(STATUS "building for PSVita") - set(USE_SDL2 ON) + set(USE_SDL2 OFF) set(USE_GLFW OFF) - set(USE_GLES2 ON) + if (NOT USE_GXM) + set(USE_GLES2 ON) + set(USE_SDL2 ON) + endif () if (NOT DEFINED ENV{VITASDK}) message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") endif () @@ -81,6 +87,9 @@ elseif (PLATFORM_SWITCH) set(CMAKE_C_FLAGS "-I${DEVKITPRO}/libnx/include -I${DEVKITPRO}/portlibs/switch/include") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") include(${DEVKITPRO}/cmake/Switch.cmake REQUIRED) + if (NOT USE_GLFW AND NOT SDL2) + set(USE_GLFW ON) + endif () else() message(FATAL_ERROR "Please set build target. Example: -DPLATFORM_DESKTOP=ON or -DPLATFORM_SWITCH=ON") endif () @@ -92,6 +101,9 @@ if (USE_DEKO3D) elseif (USE_D3D11) message(STATUS "USE_D3D11") set(BOREALIS_USE_D3D11 ON) +elseif (USE_GXM) + message(STATUS "USE_GXM") + set(BOREALIS_USE_GXM ON) elseif (USE_METAL) message(STATUS "USE_METAL") set(BOREALIS_USE_METAL ON) @@ -114,11 +126,13 @@ if (USE_SDL2) set(USE_SDL2 ON) set(USE_GLFW OFF) add_definitions(-D__SDL2__) -else () +elseif (USE_GLFW) message(STATUS "GLFW") set(USE_SDL2 OFF) set(USE_GLFW ON) add_definitions(-D__GLFW__) +else () + message(STATUS "SDL2 AND GLFW is both disabled, make sure this is what you want") endif () if (SIMPLE_HIGHLIGHT) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 28861842c..8c23c24fe 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -337,7 +337,6 @@ class Application inline static bool muteSounds = false; inline static std::string commonFooter; - inline static NVGcolor backgroundColor{}; inline static bool globalQuitEnabled = false; inline static ActionIdentifier gloablQuitIdentifier = ACTION_NONE; diff --git a/library/include/borealis/extern/nanovg/nanovg_gxm.h b/library/include/borealis/extern/nanovg/nanovg_gxm.h new file mode 100644 index 000000000..3bb3d4d3e --- /dev/null +++ b/library/include/borealis/extern/nanovg/nanovg_gxm.h @@ -0,0 +1,1764 @@ +// +// Copyright (c) 2024 xfangfang xfangfang@126.com +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +#ifndef NANOVG_GXM_H +#define NANOVG_GXM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "nanovg_gxm_utils.h" + + // Create flags + enum NVGcreateFlags + { + // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). + NVG_ANTIALIAS = 1 << 0, + // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little + // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. + NVG_STENCIL_STROKES = 1 << 1, + // Flag indicating that additional debug checks are done. + NVG_DEBUG = 1 << 2, + }; + + NVGcontext* nvgCreateGXM(NVGXMframebuffer* fb, int flags); + + void nvgDeleteGXM(NVGcontext* ctx); + + // These are additional flags on top of NVGimageFlags. + enum NVGimageFlagsGXM + { + NVG_IMAGE_NODELETE = 1 << 16, // Do not delete GXM texture handle. + }; + + int __attribute__((weak)) nvg_gxm_vertex_buffer_size = 1024 * 1024; + +#ifdef __cplusplus +} +#endif + +#endif /* NANOVG_GXM_H */ + +#ifdef NANOVG_GXM_IMPLEMENTATION + +#include +#include +#include +#include + +#include "nanovg.h" + +#ifdef USE_VITA_SHARK + +#include + +static void shark_log_cb(const char* msg, shark_log_level msg_level, int line) +{ + switch (msg_level) + { + case SHARK_LOG_INFO: + sceClibPrintf("\033[0;34m[GXP #%d]\033[0m %s\n", line, msg); + break; + case SHARK_LOG_WARNING: + sceClibPrintf("\033[0;33m[GXP #%d]\033[0m %s\n", line, msg); + break; + case SHARK_LOG_ERROR: + sceClibPrintf("\033[0;31m[GXP #%d]\033[0m %s\n", line, msg); + break; + } +} + +#endif + +enum GXMNVGuniformLoc +{ + GXMNVG_LOC_VIEWSIZE, + GXMNVG_LOC_FRAG, + GXMNVG_MAX_LOCS +}; + +enum GXMNVGshaderType +{ + NSVG_SHADER_FILLGRAD, + NSVG_SHADER_FILLIMG, + NSVG_SHADER_SIMPLE, + NSVG_SHADER_IMG +}; + +struct GXMNVGshader +{ + NVGXMshaderProgram prog; + const SceGxmProgramParameter* loc[GXMNVG_MAX_LOCS]; +}; +typedef struct GXMNVGshader GXMNVGshader; + +struct GXMNVGtexture +{ + int id; + int width, height; + int type; + int flags; + + int stride; + int unused; + SceGxmTexture gxm_tex; + uint8_t* tex_data; + SceUID data_UID; +}; +typedef struct GXMNVGtexture GXMNVGtexture; + +struct GXMNVGblend +{ + SceGxmBlendFactor srcRGB; + SceGxmBlendFactor dstRGB; + SceGxmBlendFactor srcAlpha; + SceGxmBlendFactor dstAlpha; +}; +typedef struct GXMNVGblend GXMNVGblend; + +enum GXMNVGcallType +{ + GXMNVG_NONE = 0, + GXMNVG_FILL, + GXMNVG_CONVEXFILL, + GXMNVG_STROKE, + GXMNVG_TRIANGLES, +}; + +struct GXMNVGcall +{ + int type; + int image; + int pathOffset; + int pathCount; + int triangleOffset; + int triangleCount; + int uniformOffset; + GXMNVGblend blendFunc; +}; +typedef struct GXMNVGcall GXMNVGcall; + +struct GXMNVGpath +{ + int fillOffset; + int fillCount; + int strokeOffset; + int strokeCount; +}; +typedef struct GXMNVGpath GXMNVGpath; + +struct GXMNVGfragUniforms +{ +// note: after modifying layout or size of uniform array, +// don't forget to also update the fragment shader source! +#define NANOVG_GXM_UNIFORMARRAY_SIZE 11 + union + { + struct + { + float scissorMat[12]; // matrices are actually float3x4 + float paintMat[12]; + struct NVGcolor innerCol; + struct NVGcolor outerCol; + float scissorExt[2]; + float scissorScale[2]; + float extent[2]; + float radius; + float feather; + float strokeMult; + float strokeThr; + float texType; + float type; + }; + float uniformArray[NANOVG_GXM_UNIFORMARRAY_SIZE][4]; + }; +}; +typedef struct GXMNVGfragUniforms GXMNVGfragUniforms; + +struct GXMNVGcontext +{ + SceGxmContext* context; + SceGxmShaderPatcher* shader_patcher; + SceGxmMultisampleMode msaa; + + GXMNVGshader shader; + SceUID verticesUid; + struct NVGvertex* vertBuf; + + GXMNVGshader depth_shader; + + GXMNVGtexture* textures; + float view[2]; + int ntextures; + int ctextures; + int textureId; + int fragSize; + int flags; + + // Per frame buffers + GXMNVGcall* calls; + int ccalls; + int ncalls; + GXMNVGpath* paths; + int cpaths; + int npaths; + struct NVGvertex* verts; + int cverts; + int nverts; + unsigned char* uniforms; + int cuniforms; + int nuniforms; + + int dummyTex; +}; +typedef struct GXMNVGcontext GXMNVGcontext; + +static void gxmDrawArrays(GXMNVGcontext* gxm, SceGxmPrimitiveType type, int fillOffset, int fillCount) +{ + if (fillCount > UINT16_MAX) + { + return; + } + + static int index = 0; + if (index + fillCount > nvg_gxm_vertex_buffer_size) + { + index = 0; + } + + memcpy(&gxm->vertBuf[index], &gxm->verts[fillOffset], sizeof(NVGvertex) * fillCount); + GXM_CHECK_VOID(sceGxmSetVertexStream(gxm->context, 0, &gxm->vertBuf[index])); + GXM_CHECK_VOID(sceGxmDraw(gxm->context, type, SCE_GXM_INDEX_FORMAT_U16, gxmGetSharedIndices(), fillCount)); + + index += fillCount; +} + +static int gxmnvg__maxi(int a, int b) { return a > b ? a : b; } + +static unsigned int gxmnvg__nearestPow2(unsigned int num) +{ + unsigned n = num > 0 ? num - 1 : 0; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; +} + +static void +gxmnvg__stencilFunc(GXMNVGcontext* gxm, SceGxmStencilFunc func, SceGxmStencilOp stencilFail, SceGxmStencilOp depthFail, + SceGxmStencilOp depthPass) +{ + sceGxmSetFrontStencilFunc(gxm->context, func, stencilFail, depthFail, depthPass, 0xff, 0xff); + sceGxmSetBackStencilFunc(gxm->context, func, stencilFail, depthFail, depthPass, 0xff, 0xff); +} + +static void gxmnvg__disableStencilTest(GXMNVGcontext* gxm) +{ + gxmnvg__stencilFunc(gxm, SCE_GXM_STENCIL_FUNC_ALWAYS, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP); +} + +// static void gxmnvg__blendFuncSeparate(GXMNVGcontext *gxm, const GXMNVGblend *blend) {} + +static GXMNVGtexture* gxmnvg__allocTexture(GXMNVGcontext* gxm) +{ + GXMNVGtexture* tex = NULL; + int i; + + for (i = 0; i < gxm->ntextures; i++) + { + if (gxm->textures[i].id == 0) + { + tex = &gxm->textures[i]; + break; + } + } + if (tex == NULL) + { + if (gxm->ntextures + 1 > gxm->ctextures) + { + GXMNVGtexture* textures; + int ctextures = gxmnvg__maxi(gxm->ntextures + 1, 4) + gxm->ctextures / 2; // 1.5x Overallocate + textures = (GXMNVGtexture*)realloc(gxm->textures, sizeof(GXMNVGtexture) * ctextures); + if (textures == NULL) + return NULL; + gxm->textures = textures; + gxm->ctextures = ctextures; + } + tex = &gxm->textures[gxm->ntextures++]; + } + + memset(tex, 0, sizeof(*tex)); + tex->id = ++gxm->textureId; + + return tex; +} + +static GXMNVGtexture* gxmnvg__findTexture(GXMNVGcontext* gxm, int id) +{ + int i; + for (i = 0; i < gxm->ntextures; i++) + if (gxm->textures[i].id == id) + return &gxm->textures[i]; + return NULL; +} + +static int gxmnvg__deleteTexture(GXMNVGcontext* gxm, int id) +{ + int i; + for (i = 0; i < gxm->ntextures; i++) + { + if (gxm->textures[i].id == id) + { + if (gxm->textures[i].data_UID != 0 && (gxm->textures[i].flags & NVG_IMAGE_NODELETE) == 0) + { + gxm->textures[i].unused = 1; + } + return 1; + } + } + return 0; +} + +static int gxmnvg__garbageCollector(GXMNVGcontext* gxm) +{ + int i; + for (i = 0; i < gxm->ntextures; i++) + { + if (gxm->textures[i].unused == 0) + continue; + gpu_unmap_free(gxm->textures[i].data_UID); + memset(&gxm->textures[i], 0, sizeof(gxm->textures[i])); + } + return 0; +} + +static int gxmnvg__createShader(GXMNVGshader* shader, const char* name, const char* vshader, const char* fshader) +{ + return gxmCreateShader(&shader->prog, name, vshader, fshader); +} + +static void gxmnvg__deleteShader(GXMNVGshader* shader) +{ + gxmDeleteShader(&shader->prog); +} + +static void gxmnvg__getUniforms(GXMNVGshader* shader) +{ + shader->loc[GXMNVG_LOC_VIEWSIZE] = sceGxmProgramFindParameterByName(shader->prog.vert_gxp, "viewSize"); + shader->loc[GXMNVG_LOC_FRAG] = sceGxmProgramFindParameterByName(shader->prog.frag_gxp, "frag"); +} + +static int gxmnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); + +static int gxmnvg__renderCreate(void* uptr) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + int align = 4; + +#if USE_VITA_SHARK + char fillVertShader[500] = "struct VS_OUTPUT\n" + "{\n" + " float4 position : POSITION;\n" + " float2 ftcoord : TEXCOORD0;\n" + " float2 fpos : TEXCOORD1;\n" + "};\n" + "void main(\n" + " float2 vertex : POSITION,\n" + " float2 tcoord : TEXCOORD0,\n" + " uniform float2 viewSize,\n" + " out VS_OUTPUT output\n" + ")\n" + "{\n" + " output.ftcoord = tcoord;\n" + " output.fpos = vertex;\n" + " output.position = float4(2.0 * vertex.x / viewSize.x - 1.0, 1.0 - 2.0 * vertex.y / viewSize.y, 1.0f, 1.0f);\n" + "}\n"; + + char fillFragShader[2500] = "#define EDGE_AA 0\n" + "#define UNIFORMARRAY_SIZE 11\n" + "uniform float4 frag[UNIFORMARRAY_SIZE];\n" + "#define scissorMat float3x3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" + "#define paintMat float3x3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" + "#define innerCol frag[6]\n" + "#define outerCol frag[7]\n" + "#define scissorExt frag[8].xy\n" + "#define scissorScale frag[8].zw\n" + "#define extent frag[9].xy\n" + "#define radius frag[9].z\n" + "#define feather frag[9].w\n" + "#define strokeMult frag[10].x\n" + "#define strokeThr frag[10].y\n" + "#define texType frag[10].z\n" + "#define type frag[10].w\n" + "float sdroundrect(float2 pt, float2 ext, float rad)\n" + "{\n" + " float2 ext2 = ext - float2(rad,rad);\n" + " float2 d = abs(pt) - ext2;\n" + " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" + "}\n" + "float scissorMask(float2 p) {\n" + " float2 sc = (abs((mul(scissorMat, float3(p,1.0))).xy) - scissorExt);\n" + " sc = float2(0.5,0.5) - sc * scissorScale;\n" + " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" + "}\n" + "#if EDGE_AA\n" // Stroke - from [0..1] to clipped pyramid, where the slope is 1px. + "float strokeMask(float2 ftcoord)\n" + "{\n" + " return min(1.0, (1.0 - abs(ftcoord.x*2.0 - 1.0))*strokeMult) * min(1.0f, ftcoord.y);\n" + "}\n" + "#endif\n" + "float4 main(\n" + " uniform sampler2D tex : TEXUNIT0,\n" + " float2 ftcoord: TEXCOORD0,\n" + " float2 fpos: TEXCOORD1\n" + ") : COLOR\n" + "{\n" + " float4 result;\n" + " float scissor = scissorMask(fpos);\n" + "#if EDGE_AA\n" + " float strokeAlpha = strokeMask(ftcoord);\n" + " if (strokeAlpha < strokeThr) discard;\n" + "#else\n" + " float strokeAlpha = 1.0f;\n" + "#endif\n" + " if (type == 0.0f) {\n" // Gradient + " float2 pt = (mul(paintMat, float3(fpos,1.0))).xy;\n" + " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" + " float4 color = lerp(innerCol, outerCol, d);\n" + " color *= strokeAlpha * scissor;\n" // Combine alpha + " result = color;\n" + " } else if (type == 1.0f) {\n" // Image + " float2 pt = (mul(paintMat, float3(fpos,1.0))).xy / extent.xy;\n" + " float4 color = tex2D(tex, pt);\n" + " if (texType == 1.0f) color = float4(color.xyz*color.w, color.w);\n" + " if (texType == 2.0f) color = float4(color.x, color.x, color.x, color.x);\n" + " color *= innerCol;\n" + " color *= strokeAlpha * scissor;\n" + " result = color;\n" + " } else if (type == 2.0f) {\n" // Stencil fill + " result = float4(1.0f, 1.0f, 1.0f, 1.0f);\n" + " } else {\n" // Textured tris + " float4 color = tex2D(tex, ftcoord);\n" + " if (texType == 1.0f) color = float4(color.xyz*color.w, color.w);\n" + " if (texType == 2.0f) color = float4(color.x, color.x, color.x, color.x);\n" + " color *= scissor;\n" + " result = (color * innerCol);\n" + " }\n" + " return result;\n" + "}\n"; + + char depthFragShader[20] = "void main() {}"; + + if (gxm->flags & NVG_ANTIALIAS) + { + fillFragShader[16] = '1'; // #define EDGE_AA 1 + if (gxmnvg__createShader(&gxm->shader, "fillAA", fillVertShader, fillFragShader) == 0) + return 0; + } + else + { + if (gxmnvg__createShader(&gxm->shader, "fill", fillVertShader, fillFragShader) == 0) + return 0; + } +#else + static const unsigned char fillVertShader[384] = { + 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x00, 0x03, 0x7f, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x90, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x90, 0x3a, + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x0a, 0x00, 0x80, 0x30, 0x01, 0x00, + 0x00, 0x80, 0x02, 0x00, 0x84, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, 0xfa, 0x80, 0x00, 0x08, 0x83, + 0x21, 0x05, 0x80, 0x38, 0x40, 0x80, 0x64, 0xe0, 0x82, 0x41, 0x84, + 0x08, 0x01, 0x10, 0x40, 0xf0, 0x8e, 0x00, 0x80, 0x00, 0x01, 0x10, + 0x54, 0xf0, 0x26, 0x01, 0x80, 0x00, 0x41, 0x00, 0x04, 0x90, 0x85, + 0x11, 0xa5, 0x08, 0x01, 0x80, 0x56, 0x90, 0x81, 0x11, 0x83, 0x08, + 0x00, 0x00, 0x0c, 0x83, 0x21, 0x05, 0x80, 0x38, 0x00, 0x00, 0x20, + 0xa0, 0x00, 0x50, 0x27, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0b, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0xe2, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x00, 0x74, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x00, + 0x76, 0x69, 0x65, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x00, 0x00 + }; + + static const unsigned char fillFragShader[1184] = { + 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x00, 0x03, 0x9d, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x04, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x36, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x00, 0x00, 0xdc, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2c, 0x00, 0x00, 0x00, 0xcc, 0x03, 0x00, 0x00, 0x90, 0x3a, + 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd8, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x03, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, 0xdc, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x04, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x40, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x40, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x68, 0x43, 0x0a, 0x05, 0x82, 0x38, 0x01, 0x09, 0x80, 0x82, + 0x02, 0x00, 0x80, 0x30, 0x02, 0x09, 0x80, 0x82, 0x0a, 0x00, 0x80, + 0x30, 0xd2, 0x14, 0xc0, 0xa2, 0xa2, 0x41, 0x80, 0x08, 0xd9, 0x84, + 0xa4, 0xa2, 0x82, 0x00, 0x84, 0x08, 0x82, 0x09, 0x20, 0x80, 0x0a, + 0x00, 0x80, 0x30, 0x0e, 0x13, 0x04, 0xa1, 0xa6, 0x41, 0xa4, 0x08, + 0x4f, 0x13, 0x44, 0xa1, 0xaa, 0x41, 0xc0, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x01, 0x04, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x44, 0xfa, 0x80, 0x8a, 0x03, 0xd0, 0x91, + 0xc1, 0xc9, 0x48, 0x02, 0x80, 0x81, 0xff, 0x9c, 0x0d, 0x80, 0x40, + 0x01, 0xd2, 0x11, 0x80, 0x80, 0x88, 0x81, 0x18, 0x06, 0x82, 0xa1, + 0xff, 0x9c, 0x0d, 0x80, 0x40, 0x01, 0xd2, 0x11, 0x90, 0x00, 0x89, + 0x81, 0x18, 0x22, 0x90, 0xb9, 0xff, 0xbc, 0x0d, 0xc0, 0x40, 0x00, + 0x11, 0x1d, 0x00, 0xb4, 0x81, 0xa1, 0x18, 0x0c, 0xd0, 0x0f, 0x10, + 0xa0, 0x11, 0xa1, 0x00, 0x40, 0xd6, 0x24, 0xc0, 0x84, 0x41, 0xc0, + 0x08, 0x00, 0x60, 0x00, 0x40, 0x80, 0x41, 0x82, 0x08, 0x00, 0x00, + 0x80, 0x00, 0x80, 0x10, 0x80, 0x08, 0x80, 0x18, 0x00, 0xe0, 0x00, + 0x10, 0x81, 0x91, 0x00, 0x18, 0x00, 0xe0, 0x08, 0x00, 0x81, 0x55, + 0x30, 0x00, 0x0a, 0x30, 0x81, 0x02, 0xa8, 0x48, 0x39, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0xf9, 0x82, 0x8a, 0x03, 0xd0, 0x91, 0xc1, + 0x89, 0x48, 0x80, 0x18, 0x00, 0xe0, 0x00, 0x10, 0x81, 0x91, 0x00, + 0x18, 0x00, 0xe0, 0x08, 0x00, 0x81, 0x55, 0x30, 0x00, 0x0a, 0x30, + 0x81, 0x02, 0xa8, 0x48, 0x1c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0xf9, 0x84, 0x8a, 0x03, 0xd0, 0x91, 0xc1, 0x89, 0x48, 0x80, 0x18, + 0x40, 0xe0, 0x02, 0x10, 0x81, 0x91, 0x00, 0x18, 0x40, 0xe0, 0x0a, + 0x00, 0x81, 0x55, 0x30, 0x01, 0x0a, 0xb0, 0x81, 0x02, 0xa8, 0x48, + 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xf9, 0x82, 0x8a, 0x03, + 0xd0, 0x81, 0x81, 0x89, 0x48, 0x00, 0x0b, 0x00, 0xe0, 0x04, 0xc4, + 0x01, 0xe0, 0x80, 0x18, 0x00, 0xe0, 0x02, 0x10, 0x81, 0x91, 0x00, + 0x18, 0x00, 0xe0, 0x0a, 0x00, 0x81, 0x55, 0x30, 0x00, 0x0a, 0xb0, + 0x81, 0x02, 0xa8, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0xf9, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xfd, 0x40, 0x80, + 0x24, 0x00, 0x80, 0x41, 0x84, 0x08, 0x41, 0x00, 0x44, 0x00, 0x88, + 0x10, 0xc0, 0x08, 0x84, 0x8a, 0x03, 0xd0, 0x81, 0x81, 0x89, 0x48, + 0x80, 0x18, 0x00, 0xe0, 0x02, 0x10, 0x81, 0x91, 0x00, 0x18, 0x00, + 0xe0, 0x0a, 0x00, 0x81, 0x55, 0x30, 0x00, 0x0a, 0xb0, 0x81, 0x02, + 0x88, 0x48, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x80, 0x39, 0x00, + 0x00, 0x04, 0x03, 0x20, 0x0d, 0x80, 0x39, 0x02, 0x00, 0x04, 0x0f, + 0x84, 0x0f, 0xa4, 0x08, 0x0c, 0x81, 0x11, 0xc0, 0x82, 0x81, 0xe1, + 0x18, 0x0d, 0x0f, 0x4d, 0x30, 0x82, 0x41, 0x80, 0x08, 0x32, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0xf8, 0x34, 0x1a, 0x03, 0xf0, 0x8e, + 0x0d, 0x80, 0x40, 0x34, 0x1a, 0x43, 0xf0, 0x8e, 0x0d, 0x80, 0x40, + 0x2f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xf8, 0x82, 0x8a, 0x03, + 0xd0, 0x81, 0x81, 0xc9, 0x48, 0x0e, 0x86, 0x81, 0xff, 0x9c, 0x0d, + 0x00, 0x40, 0x01, 0xd2, 0x11, 0x80, 0x82, 0x88, 0x01, 0x18, 0x12, + 0x88, 0xa1, 0xff, 0x9c, 0x0d, 0x00, 0x40, 0x01, 0xd2, 0x11, 0x90, + 0x02, 0x81, 0x01, 0x18, 0x14, 0x00, 0x04, 0xb0, 0x86, 0x41, 0x24, + 0x08, 0x00, 0x0b, 0x00, 0xe0, 0x04, 0xc4, 0x01, 0xe0, 0x80, 0x18, + 0x00, 0xe0, 0x02, 0x10, 0x81, 0x91, 0x00, 0x18, 0x00, 0xe0, 0x0a, + 0x00, 0x81, 0x55, 0x30, 0x00, 0x0a, 0xb0, 0x81, 0x02, 0xa8, 0x48, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xf9, 0x03, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0xfd, 0x40, 0x80, 0x24, 0x00, 0x80, 0x41, + 0x84, 0x08, 0x41, 0x00, 0x44, 0x00, 0x88, 0x10, 0xc0, 0x08, 0x84, + 0x8a, 0x03, 0xd0, 0x81, 0x81, 0x89, 0x48, 0x80, 0x18, 0x00, 0xe0, + 0x02, 0x10, 0x81, 0x91, 0x00, 0x18, 0x00, 0xe0, 0x0a, 0x00, 0x81, + 0x55, 0x30, 0x00, 0x0a, 0xb0, 0x81, 0x02, 0xc8, 0x48, 0x02, 0x80, + 0x99, 0x0f, 0xbc, 0x0d, 0xc0, 0x40, 0x00, 0x0f, 0xf0, 0x0f, 0x00, + 0x0d, 0x80, 0x39, 0x3c, 0x03, 0x04, 0xcf, 0x84, 0x4f, 0xa4, 0x08, + 0x02, 0x80, 0x11, 0x00, 0x82, 0x81, 0xe1, 0x18, 0x02, 0x0f, 0x4d, + 0x00, 0x82, 0x01, 0x80, 0x08, 0x17, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x09, 0x00, 0xf8, 0x0e, + 0x86, 0x81, 0xff, 0x9c, 0x0d, 0x80, 0x40, 0x01, 0xd2, 0x11, 0x80, + 0x82, 0x88, 0x81, 0x18, 0x12, 0x88, 0xa1, 0xff, 0x9c, 0x0d, 0x80, + 0x40, 0x01, 0xd2, 0x11, 0x90, 0x02, 0x81, 0x81, 0x18, 0xc0, 0x12, + 0x04, 0xe0, 0xb6, 0x49, 0xa4, 0x08, 0x00, 0x60, 0x04, 0xaf, 0x84, + 0x18, 0xa4, 0x08, 0x00, 0x5f, 0x44, 0x1f, 0x84, 0x08, 0xa5, 0x08, + 0x00, 0x60, 0x04, 0x9f, 0x84, 0x09, 0xa5, 0x08, 0x3c, 0x42, 0x3e, + 0x0f, 0x80, 0x88, 0x81, 0x18, 0x01, 0x3e, 0x80, 0x0f, 0x00, 0x0a, + 0x80, 0x30, 0x01, 0x3e, 0x80, 0x0f, 0x00, 0x08, 0x80, 0x30, 0x3d, + 0x00, 0x1c, 0x0f, 0x84, 0x88, 0xa1, 0x18, 0xfc, 0x14, 0x10, 0xcf, + 0xa8, 0x08, 0xc0, 0x08, 0x0a, 0x00, 0x1c, 0xcf, 0x84, 0x88, 0xa1, + 0x18, 0x41, 0x80, 0x01, 0xcf, 0x80, 0x88, 0xe1, 0x18, 0x7c, 0xd6, + 0x28, 0xcf, 0x84, 0x08, 0xc0, 0x08, 0x3c, 0x60, 0x00, 0x4f, 0x80, + 0x08, 0x82, 0x08, 0x1a, 0x8c, 0xb9, 0xff, 0xbc, 0x0d, 0xc0, 0x40, + 0x04, 0x11, 0x01, 0xcf, 0x80, 0x8f, 0xb1, 0x18, 0x02, 0x80, 0x11, + 0x00, 0x82, 0x81, 0xe1, 0x18, 0x02, 0x0f, 0x4d, 0x00, 0x82, 0x01, + 0x80, 0x08, 0x02, 0x80, 0x19, 0xa0, 0x7e, 0x0d, 0x80, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x01, 0xe4, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x02, 0x04, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x00, + 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00 + }; + + static const unsigned char fillAAFragShader[1324] = { + 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x00, 0x03, 0x29, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb8, 0x04, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x3a, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xe0, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2c, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x90, 0x3a, + 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x64, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x3c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x04, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x3c, 0x04, 0x00, 0x00, 0x68, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x04, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x40, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x40, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x70, 0x43, 0x0a, 0x05, 0x82, 0x38, 0x01, 0x09, 0x60, 0x83, + 0x02, 0x00, 0x80, 0x30, 0x02, 0x09, 0x60, 0x83, 0x0a, 0x00, 0x80, + 0x30, 0xd2, 0x14, 0xc0, 0xa2, 0xa2, 0x41, 0x80, 0x08, 0xd9, 0x84, + 0xa4, 0xa2, 0x82, 0x10, 0x84, 0x08, 0x82, 0x09, 0x20, 0x80, 0x0a, + 0x00, 0x80, 0x30, 0x0e, 0x13, 0x04, 0xa1, 0xa6, 0x41, 0xa4, 0x08, + 0x4f, 0x13, 0x44, 0xa1, 0xaa, 0x41, 0xc0, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x01, 0x04, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0xfa, 0x9a, + 0x06, 0x00, 0xf0, 0x8c, 0x28, 0x80, 0x00, 0x1a, 0x10, 0x04, 0x3f, + 0xe4, 0x10, 0xa4, 0x08, 0x14, 0x80, 0x01, 0xc0, 0x80, 0x80, 0xe1, + 0x18, 0x80, 0x00, 0x20, 0xa0, 0x08, 0x00, 0x81, 0x50, 0x80, 0xd6, + 0x24, 0xc0, 0x84, 0x41, 0xc0, 0x08, 0x00, 0x00, 0xc0, 0x00, 0x80, + 0x10, 0x80, 0x08, 0x03, 0x8a, 0x00, 0xc0, 0x91, 0xc6, 0x8c, 0x48, + 0x00, 0x19, 0x00, 0xe0, 0x00, 0x10, 0x81, 0x91, 0x80, 0x16, 0x00, + 0xe0, 0x08, 0x00, 0x81, 0x55, 0x2d, 0x00, 0x0a, 0x30, 0x85, 0x01, + 0x88, 0x48, 0x2c, 0x16, 0x00, 0xf0, 0x06, 0x04, 0x30, 0xf9, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x01, 0x04, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x44, 0xfa, 0x80, 0x8a, 0x03, 0xd0, 0x91, 0xc1, 0xc9, + 0x48, 0x02, 0x80, 0x81, 0xff, 0x9c, 0x0d, 0x80, 0x40, 0x01, 0xd2, + 0x11, 0x80, 0x80, 0x88, 0x81, 0x18, 0x06, 0x82, 0xa1, 0xff, 0x9c, + 0x0d, 0x80, 0x40, 0x01, 0xd2, 0x11, 0x90, 0x00, 0x89, 0x81, 0x18, + 0x22, 0x90, 0xb9, 0xff, 0xbc, 0x0d, 0xc0, 0x40, 0x00, 0x11, 0x1d, + 0x00, 0xb4, 0x81, 0xa1, 0x18, 0x0c, 0xd0, 0x0f, 0x10, 0xa0, 0x11, + 0xa1, 0x00, 0x80, 0xd6, 0x24, 0xc0, 0x84, 0x41, 0xc0, 0x08, 0x00, + 0x60, 0x00, 0x40, 0x80, 0x41, 0x82, 0x08, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x10, 0x80, 0x08, 0x00, 0x19, 0x00, 0xe0, 0x00, 0x10, 0x81, + 0x91, 0x80, 0x16, 0x00, 0xe0, 0x08, 0x00, 0x81, 0x55, 0x2d, 0x00, + 0x0a, 0x30, 0x81, 0x02, 0xa8, 0x48, 0x3a, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0xf9, 0x82, 0x8a, 0x03, 0xd0, 0x91, 0xc1, 0x89, 0x48, + 0x00, 0x19, 0x00, 0xe0, 0x00, 0x10, 0x81, 0x91, 0x80, 0x16, 0x00, + 0xe0, 0x08, 0x00, 0x81, 0x55, 0x2d, 0x00, 0x0a, 0x30, 0x81, 0x02, + 0xa8, 0x48, 0x1c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xf9, 0x84, + 0x8a, 0x03, 0xd0, 0x91, 0xc1, 0x89, 0x48, 0x00, 0x19, 0x40, 0xe0, + 0x02, 0x10, 0x81, 0x91, 0x80, 0x16, 0x40, 0xe0, 0x0a, 0x00, 0x81, + 0x55, 0x2d, 0x01, 0x0a, 0xb0, 0x81, 0x02, 0xa8, 0x48, 0x14, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0xf9, 0x82, 0x8a, 0x03, 0xd0, 0x81, + 0x81, 0x89, 0x48, 0x80, 0x0b, 0x00, 0xe0, 0x04, 0xc4, 0x01, 0xe0, + 0x00, 0x19, 0x00, 0xe0, 0x02, 0x10, 0x81, 0x91, 0x80, 0x16, 0x00, + 0xe0, 0x0a, 0x00, 0x81, 0x55, 0x2d, 0x00, 0x0a, 0xb0, 0x81, 0x02, + 0xa8, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xf9, 0x03, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xfd, 0x40, 0x80, 0x24, 0x00, + 0x80, 0x41, 0x84, 0x08, 0x41, 0x00, 0x44, 0x00, 0x88, 0x10, 0xc0, + 0x08, 0x84, 0x8a, 0x03, 0xd0, 0x81, 0x81, 0x89, 0x48, 0x00, 0x19, + 0x00, 0xe0, 0x02, 0x10, 0x81, 0x91, 0x80, 0x16, 0x00, 0xe0, 0x0a, + 0x00, 0x81, 0x55, 0x2d, 0x00, 0x0a, 0xb0, 0x81, 0x02, 0x88, 0x48, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x80, 0x39, 0x00, 0x00, 0x04, + 0x03, 0x20, 0x0d, 0x80, 0x39, 0x02, 0x00, 0x04, 0x0f, 0x84, 0x0f, + 0xa4, 0x08, 0x0c, 0x81, 0x11, 0xc0, 0x82, 0x81, 0xe1, 0x18, 0x0d, + 0x0f, 0x4d, 0x30, 0x82, 0x41, 0x80, 0x08, 0x34, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0xf8, 0x38, 0x1c, 0x03, 0xf0, 0x8e, 0x0d, 0x80, + 0x40, 0x38, 0x1c, 0x43, 0xf0, 0x8e, 0x0d, 0x80, 0x40, 0x31, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0xf8, 0x82, 0x8a, 0x03, 0xd0, 0x81, + 0x81, 0xc9, 0x48, 0x0e, 0x86, 0x81, 0xff, 0x9c, 0x0d, 0x00, 0x40, + 0x01, 0xd2, 0x11, 0x80, 0x82, 0x88, 0x01, 0x18, 0x12, 0x88, 0xa1, + 0xff, 0x9c, 0x0d, 0x00, 0x40, 0x01, 0xd2, 0x11, 0x90, 0x02, 0x81, + 0x01, 0x18, 0x1b, 0x00, 0x04, 0xb0, 0x86, 0x41, 0x24, 0x08, 0x80, + 0x0b, 0x00, 0xe0, 0x04, 0xc4, 0x01, 0xe0, 0x00, 0x19, 0x00, 0xe0, + 0x02, 0x10, 0x81, 0x91, 0x80, 0x16, 0x00, 0xe0, 0x0a, 0x00, 0x81, + 0x55, 0x2d, 0x00, 0x0a, 0xb0, 0x81, 0x02, 0xa8, 0x48, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0xf9, 0x03, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0xfd, 0x40, 0x80, 0x24, 0x00, 0x80, 0x41, 0x84, 0x08, + 0x41, 0x00, 0x44, 0x00, 0x88, 0x10, 0xc0, 0x08, 0x84, 0x8a, 0x03, + 0xd0, 0x81, 0x81, 0x89, 0x48, 0x00, 0x19, 0x00, 0xe0, 0x02, 0x10, + 0x81, 0x91, 0x80, 0x16, 0x00, 0xe0, 0x0a, 0x00, 0x81, 0x55, 0x2d, + 0x00, 0x0a, 0xb0, 0x81, 0x02, 0xc8, 0x48, 0x02, 0x80, 0x99, 0x0f, + 0xbc, 0x0d, 0xc0, 0x40, 0x00, 0x0f, 0xf0, 0x0f, 0x00, 0x0d, 0x80, + 0x39, 0x3c, 0x03, 0x04, 0xcf, 0x84, 0x4f, 0xa4, 0x08, 0xc2, 0x00, + 0x40, 0x0f, 0x80, 0x08, 0x80, 0x08, 0x3c, 0x81, 0x01, 0x10, 0x82, + 0x81, 0xe1, 0x18, 0x3d, 0x0f, 0x4d, 0x00, 0x82, 0x01, 0x80, 0x08, + 0x18, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x09, 0x00, 0xf8, 0x0e, 0x86, 0x81, 0xff, 0x9c, 0x0d, + 0x80, 0x40, 0x01, 0xd2, 0x11, 0x80, 0x82, 0x88, 0x81, 0x18, 0x12, + 0x88, 0xa1, 0xff, 0x9c, 0x0d, 0x80, 0x40, 0x01, 0xd2, 0x11, 0x90, + 0x02, 0x81, 0x81, 0x18, 0xc0, 0x12, 0x04, 0xe0, 0xb6, 0x49, 0xa4, + 0x08, 0x00, 0x60, 0x04, 0xaf, 0x84, 0x18, 0xa4, 0x08, 0x00, 0x5f, + 0x44, 0x1f, 0x84, 0x08, 0xa5, 0x08, 0x00, 0x60, 0x04, 0x9f, 0x84, + 0x09, 0xa5, 0x08, 0x3c, 0x42, 0x3e, 0x0f, 0x80, 0x88, 0x81, 0x18, + 0x01, 0x3e, 0x80, 0x0f, 0x00, 0x0a, 0x80, 0x30, 0x01, 0x3e, 0x80, + 0x0f, 0x00, 0x08, 0x80, 0x30, 0x3d, 0x00, 0x1c, 0x0f, 0x84, 0x88, + 0xa1, 0x18, 0xfc, 0x14, 0x10, 0xcf, 0xa8, 0x08, 0xc0, 0x08, 0x0a, + 0x00, 0x1c, 0xcf, 0x84, 0x88, 0xa1, 0x18, 0x41, 0x80, 0x01, 0xcf, + 0x80, 0x88, 0xe1, 0x18, 0xbc, 0xd6, 0x28, 0xcf, 0x84, 0x08, 0xc0, + 0x08, 0x3c, 0x60, 0x00, 0x4f, 0x80, 0x08, 0x82, 0x08, 0x1a, 0x8c, + 0xb9, 0xff, 0xbc, 0x0d, 0xc0, 0x40, 0x04, 0x11, 0x01, 0xcf, 0x80, + 0x8f, 0xb1, 0x18, 0xc2, 0x00, 0x40, 0x0f, 0x80, 0x08, 0x80, 0x08, + 0x3c, 0x81, 0x01, 0x10, 0x82, 0x81, 0xe1, 0x18, 0x3d, 0x0f, 0x4d, + 0x00, 0x82, 0x01, 0x80, 0x08, 0x02, 0x80, 0x19, 0xa0, 0x7e, 0x0d, + 0x80, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x02, 0x00, 0x04, 0x00, 0x03, 0x00, 0x05, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x13, 0x00, 0x00, + 0x00, 0x2c, 0x00, 0x0a, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0xe4, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, + 0x00, 0x00, 0x00, 0x02, 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x00, 0x74, 0x65, 0x78, + 0x00, 0x00, 0x00, 0x00 + }; + + static const unsigned char depthFragShader[188] = { + 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x00, 0x03, 0xbc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x90, 0x3a, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, + 0xfa + }; + + if (gxm->flags & NVG_ANTIALIAS) + { + if (gxmnvg__createShader(&gxm->shader, "fillAA", (const char*)fillVertShader, (const char*)fillAAFragShader) == 0) + return 0; + } + else + { + if (gxmnvg__createShader(&gxm->shader, "fill", (const char*)fillVertShader, (const char*)fillFragShader) == 0) + return 0; + } +#endif + + if (gxmnvg__createShader(&gxm->depth_shader, "depth", NULL, (const char*)depthFragShader) == 0) + return 0; + + gxm->vertBuf = (struct NVGvertex*)gpu_alloc_map( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, + SCE_GXM_MEMORY_ATTRIB_READ, + sizeof(struct NVGvertex) * nvg_gxm_vertex_buffer_size, + &gxm->verticesUid); + + const SceGxmProgramParameter* basic_vertex_param = sceGxmProgramFindParameterByName(gxm->shader.prog.vert_gxp, + "vertex"); + const SceGxmProgramParameter* basic_tcoord_param = sceGxmProgramFindParameterByName(gxm->shader.prog.vert_gxp, + "tcoord"); + SceGxmVertexAttribute basic_vertex_attributes[2]; + basic_vertex_attributes[0].streamIndex = 0; + basic_vertex_attributes[0].offset = 0; + basic_vertex_attributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32; + basic_vertex_attributes[0].componentCount = 2; + basic_vertex_attributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(basic_vertex_param); + basic_vertex_attributes[1].streamIndex = 0; + basic_vertex_attributes[1].offset = 2 * sizeof(float); + basic_vertex_attributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32; + basic_vertex_attributes[1].componentCount = 2; + basic_vertex_attributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(basic_tcoord_param); + SceGxmVertexStream basic_vertex_stream[1]; + basic_vertex_stream[0].stride = sizeof(struct NVGvertex); + basic_vertex_stream[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT; + + GXM_CHECK(sceGxmShaderPatcherCreateVertexProgram(gxm->shader_patcher, gxm->shader.prog.vert_id, + basic_vertex_attributes, + sizeof(basic_vertex_attributes) / sizeof(SceGxmVertexAttribute), + basic_vertex_stream, + sizeof(basic_vertex_stream) / sizeof(SceGxmVertexStream), + &gxm->shader.prog.vert)); + + /** + * TODO: Custom blend function + * Currently, these functions in nanovg.h are not supported: + * nvgGlobalCompositeOperation, nvgGlobalCompositeBlendFunc, nvgGlobalCompositeBlendFuncSeparate + * The default behavior is equivalent to: nvgGlobalCompositeOperation(NVG_SOURCE_OVER) + */ + SceGxmBlendInfo blendInfo; + blendInfo.colorMask = SCE_GXM_COLOR_MASK_ALL; + blendInfo.colorFunc = SCE_GXM_BLEND_FUNC_ADD; + blendInfo.alphaFunc = SCE_GXM_BLEND_FUNC_ADD; + blendInfo.colorSrc = SCE_GXM_BLEND_FACTOR_ONE; + blendInfo.colorDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendInfo.alphaSrc = SCE_GXM_BLEND_FACTOR_ONE; + blendInfo.alphaDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + + GXM_CHECK(sceGxmShaderPatcherCreateFragmentProgram(gxm->shader_patcher, + gxm->shader.prog.frag_id, SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, + gxm->msaa, &blendInfo, gxm->shader.prog.vert_gxp, + &gxm->shader.prog.frag)); + + gxmnvg__getUniforms(&gxm->shader); + + GXM_CHECK(sceGxmShaderPatcherCreateFragmentProgram(gxm->shader_patcher, + gxm->depth_shader.prog.frag_id, + SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, + gxm->msaa, NULL, gxm->shader.prog.vert_gxp, + &gxm->depth_shader.prog.frag)); + + gxm->fragSize = ALIGN(sizeof(GXMNVGfragUniforms), align); + + // Some platforms does not allow to have samples to unset textures. + // Create empty one which is bound when there's no texture specified. + gxm->dummyTex = gxmnvg__renderCreateTexture(gxm, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); + + return 1; +} + +static int gxmnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + GXMNVGtexture* tex = gxmnvg__allocTexture(gxm); + + if (tex == NULL) + return 0; + + SceGxmTextureFormat format = type == NVG_TEXTURE_RGBA ? SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR : SCE_GXM_TEXTURE_FORMAT_U8_R; + int aligned_w = ALIGN(w, 8); + int texture_w = w; + int spp = type == NVG_TEXTURE_RGBA ? 4 : 1; + int tex_size = aligned_w * h * spp; + int ret; + + tex->stride = aligned_w * spp; + tex->tex_data = (uint8_t*)gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, SCE_GXM_MEMORY_ATTRIB_RW, + tex_size, &tex->data_UID); + if (tex->tex_data == NULL) + { + return 0; + } + + /* Clear the texture */ + if (data == NULL) + { + memset(tex->tex_data, 0, tex_size); + } + else + { + for (int i = 0; i < h; i++) + { + memcpy(tex->tex_data + i * tex->stride, data + i * w * spp, w * spp); + } + } + + // TODO: Support mipmap + imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; + + /* Create the gxm texture */ + ret = sceGxmTextureInitLinear(&tex->gxm_tex, tex->tex_data, format, texture_w, h, 0); + if (ret < 0) + { + GXM_PRINT_ERROR(ret); + gpu_unmap_free(tex->data_UID); + tex->data_UID = 0; + return 0; + } + + if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) + { + if (imageFlags & NVG_IMAGE_NEAREST) + { + sceGxmTextureSetMinFilter(&tex->gxm_tex, SCE_GXM_TEXTURE_FILTER_MIPMAP_POINT); + } + else + { + sceGxmTextureSetMinFilter(&tex->gxm_tex, SCE_GXM_TEXTURE_FILTER_MIPMAP_LINEAR); + } + } + else + { + if (imageFlags & NVG_IMAGE_NEAREST) + { + sceGxmTextureSetMinFilter(&tex->gxm_tex, SCE_GXM_TEXTURE_FILTER_POINT); + } + else + { + sceGxmTextureSetMinFilter(&tex->gxm_tex, SCE_GXM_TEXTURE_FILTER_LINEAR); + } + } + + if (imageFlags & NVG_IMAGE_NEAREST) + sceGxmTextureSetMagFilter(&tex->gxm_tex, SCE_GXM_TEXTURE_FILTER_POINT); + else + sceGxmTextureSetMagFilter(&tex->gxm_tex, SCE_GXM_TEXTURE_FILTER_LINEAR); + + if (imageFlags & NVG_IMAGE_REPEATX) + sceGxmTextureSetUAddrMode(&tex->gxm_tex, SCE_GXM_TEXTURE_ADDR_REPEAT); + else + sceGxmTextureSetUAddrMode(&tex->gxm_tex, SCE_GXM_TEXTURE_ADDR_CLAMP); + + if (imageFlags & NVG_IMAGE_REPEATY) + sceGxmTextureSetVAddrMode(&tex->gxm_tex, SCE_GXM_TEXTURE_ADDR_REPEAT); + else + sceGxmTextureSetVAddrMode(&tex->gxm_tex, SCE_GXM_TEXTURE_ADDR_CLAMP); + + tex->width = w; + tex->height = h; + tex->type = type; + tex->flags = imageFlags; + + return tex->id; +} + +static int gxmnvg__renderDeleteTexture(void* uptr, int image) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + return gxmnvg__deleteTexture(gxm, image); +} + +static int gxmnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + GXMNVGtexture* tex = gxmnvg__findTexture(gxm, image); + + if (tex == NULL) + return 0; + + int spp = tex->type == NVG_TEXTURE_RGBA ? 4 : 1; + + for (int i = 0; i < h; i++) + { + int start = ((i + y) * tex->stride + x) * spp; + memcpy(tex->tex_data + start, data + start, w * spp); + } + + return 1; +} + +static int gxmnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + GXMNVGtexture* tex = gxmnvg__findTexture(gxm, image); + if (tex == NULL) + return 0; + *w = tex->width; + *h = tex->height; + return 1; +} + +static void gxmnvg__xformToMat3x4(float* m3, float* t) +{ + // transpose + m3[0] = t[0]; + m3[1] = t[2]; + m3[2] = t[4]; + m3[3] = 0.0f; + m3[4] = t[1]; + m3[5] = t[3]; + m3[6] = t[5]; + m3[7] = 0.0f; + m3[8] = 0.0f; + m3[9] = 0.0f; + m3[10] = 1.0f; + m3[11] = 0.0f; +} + +static NVGcolor gxmnvg__premulColor(NVGcolor c) +{ + c.r *= c.a; + c.g *= c.a; + c.b *= c.a; + return c; +} + +static int gxmnvg__convertPaint(GXMNVGcontext* gxm, GXMNVGfragUniforms* frag, NVGpaint* paint, + NVGscissor* scissor, float width, float fringe, float strokeThr) +{ + GXMNVGtexture* tex = NULL; + float invxform[6]; + + memset(frag, 0, sizeof(*frag)); + + frag->innerCol = gxmnvg__premulColor(paint->innerColor); + frag->outerCol = gxmnvg__premulColor(paint->outerColor); + + if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) + { + memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); + frag->scissorExt[0] = 1.0f; + frag->scissorExt[1] = 1.0f; + frag->scissorScale[0] = 1.0f; + frag->scissorScale[1] = 1.0f; + } + else + { + nvgTransformInverse(invxform, scissor->xform); + gxmnvg__xformToMat3x4(frag->scissorMat, invxform); + frag->scissorExt[0] = scissor->extent[0]; + frag->scissorExt[1] = scissor->extent[1]; + frag->scissorScale[0] = sqrtf(scissor->xform[0] * scissor->xform[0] + scissor->xform[2] * scissor->xform[2]) / fringe; + frag->scissorScale[1] = sqrtf(scissor->xform[1] * scissor->xform[1] + scissor->xform[3] * scissor->xform[3]) / fringe; + } + + memcpy(frag->extent, paint->extent, sizeof(frag->extent)); + frag->strokeMult = (width * 0.5f + fringe * 0.5f) / fringe; + frag->strokeThr = strokeThr; + + if (paint->image != 0) + { + tex = gxmnvg__findTexture(gxm, paint->image); + if (tex == NULL) + return 0; + if ((tex->flags & NVG_IMAGE_FLIPY) != 0) + { + float m1[6], m2[6]; + nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); + nvgTransformMultiply(m1, paint->xform); + nvgTransformScale(m2, 1.0f, -1.0f); + nvgTransformMultiply(m2, m1); + nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); + nvgTransformMultiply(m1, m2); + nvgTransformInverse(invxform, m1); + } + else + { + nvgTransformInverse(invxform, paint->xform); + } + frag->type = NSVG_SHADER_FILLIMG; + + if (tex->type == NVG_TEXTURE_RGBA) + frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; + else + frag->texType = 2.0f; + } + else + { + frag->type = NSVG_SHADER_FILLGRAD; + frag->radius = paint->radius; + frag->feather = paint->feather; + nvgTransformInverse(invxform, paint->xform); + } + + gxmnvg__xformToMat3x4(frag->paintMat, invxform); + return 1; +} + +static GXMNVGfragUniforms* nvg__fragUniformPtr(GXMNVGcontext* gxm, int i); + +static void gxmnvg__setUniforms(GXMNVGcontext* gxm, int uniformOffset, int image) +{ + GXMNVGtexture* tex = NULL; + GXMNVGfragUniforms* frag = nvg__fragUniformPtr(gxm, uniformOffset); + + void* buffer; + sceGxmReserveFragmentDefaultUniformBuffer(gxm->context, &buffer); + sceGxmSetUniformDataF(buffer, gxm->shader.loc[GXMNVG_LOC_FRAG], 0, sizeof(float) * NANOVG_GXM_UNIFORMARRAY_SIZE, + (const float*)frag->uniformArray); + + if (image != 0) + { + tex = gxmnvg__findTexture(gxm, image); + } + // If no image is set, use empty texture + if (tex == NULL) + { + tex = gxmnvg__findTexture(gxm, gxm->dummyTex); + } + if (tex != NULL) + { + GXM_CHECK_VOID(sceGxmSetFragmentTexture(gxm->context, 0, &tex->gxm_tex)); + } +} + +static void gxmnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) +{ + NVG_NOTUSED(devicePixelRatio); + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + gxm->view[0] = width; + gxm->view[1] = height; +} + +static void gxmnvg__fill(GXMNVGcontext* gxm, GXMNVGcall* call) +{ + GXMNVGpath* paths = &gxm->paths[call->pathOffset]; + int i, npaths = call->pathCount; + + // set bindpoint for solid loc + gxmnvg__setUniforms(gxm, call->uniformOffset, 0); + + // Draw shapes + { + // Disable color output + sceGxmSetFragmentProgram(gxm->context, gxm->depth_shader.prog.frag); + + sceGxmSetTwoSidedEnable(gxm->context, SCE_GXM_TWO_SIDED_ENABLED); + sceGxmSetCullMode(gxm->context, SCE_GXM_CULL_NONE); + sceGxmSetFrontStencilFunc(gxm->context, + SCE_GXM_STENCIL_FUNC_ALWAYS, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_INCR_WRAP, + 0xff, 0xff); + sceGxmSetBackStencilFunc(gxm->context, + SCE_GXM_STENCIL_FUNC_ALWAYS, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_DECR_WRAP, + 0xff, 0xff); + + for (i = 0; i < npaths; i++) + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); + + sceGxmSetCullMode(gxm->context, SCE_GXM_CULL_CW); + sceGxmSetTwoSidedEnable(gxm->context, SCE_GXM_TWO_SIDED_DISABLED); + + // Enable color output + sceGxmSetFragmentProgram(gxm->context, gxm->shader.prog.frag); + } + + // Draw anti-aliased pixels + gxmnvg__setUniforms(gxm, call->uniformOffset + gxm->fragSize, call->image); + + if (gxm->flags & NVG_ANTIALIAS) + { + gxmnvg__stencilFunc(gxm, SCE_GXM_STENCIL_FUNC_EQUAL, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP); + + // Draw fringes + for (i = 0; i < npaths; i++) + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); + } + + // Draw fill + gxmnvg__stencilFunc(gxm, SCE_GXM_STENCIL_FUNC_NOT_EQUAL, + SCE_GXM_STENCIL_OP_ZERO, SCE_GXM_STENCIL_OP_ZERO, SCE_GXM_STENCIL_OP_ZERO); + + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); + + gxmnvg__disableStencilTest(gxm); +} + +static void gxmnvg__convexFill(GXMNVGcontext* gxm, GXMNVGcall* call) +{ + GXMNVGpath* paths = &gxm->paths[call->pathOffset]; + int i, npaths = call->pathCount; + + gxmnvg__setUniforms(gxm, call->uniformOffset, call->image); + + for (i = 0; i < npaths; i++) + { + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); + + // Draw fringes + if (paths[i].strokeCount > 0) + { + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); + } + } +} + +static void gxmnvg__stroke(GXMNVGcontext* gxm, GXMNVGcall* call) +{ + GXMNVGpath* paths = &gxm->paths[call->pathOffset]; + int npaths = call->pathCount, i; + + if (gxm->flags & NVG_STENCIL_STROKES) + { + gxmnvg__stencilFunc(gxm, SCE_GXM_STENCIL_FUNC_EQUAL, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_INCR); + + gxmnvg__setUniforms(gxm, call->uniformOffset + gxm->fragSize, call->image); + + for (i = 0; i < npaths; i++) + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); + + // Draw anti-aliased pixels. + gxmnvg__setUniforms(gxm, call->uniformOffset, call->image); + + sceGxmSetFrontStencilFunc(gxm->context, + SCE_GXM_STENCIL_FUNC_EQUAL, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, + 0xff, 0xff); + sceGxmSetBackStencilFunc(gxm->context, + SCE_GXM_STENCIL_FUNC_EQUAL, + SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, SCE_GXM_STENCIL_OP_KEEP, + 0xff, 0xff); + + for (i = 0; i < npaths; i++) + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); + + // Clear stencil buffer. + { + gxmnvg__stencilFunc(gxm, SCE_GXM_STENCIL_FUNC_ALWAYS, + SCE_GXM_STENCIL_OP_ZERO, SCE_GXM_STENCIL_OP_ZERO, SCE_GXM_STENCIL_OP_ZERO); + + // Disable color output + sceGxmSetFragmentProgram(gxm->context, gxm->depth_shader.prog.frag); + + for (i = 0; i < npaths; i++) + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); + + // Enable color output + sceGxmSetFragmentProgram(gxm->context, gxm->shader.prog.frag); + + gxmnvg__disableStencilTest(gxm); + } + + // gxmnvg__convertPaint(gxm, nvg__fragUniformPtr(gxm, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); + } + else + { + gxmnvg__setUniforms(gxm, call->uniformOffset, call->image); + // Draw Strokes + for (i = 0; i < npaths; i++) + { + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); + } + } +} + +static void gxmnvg__triangles(GXMNVGcontext* gxm, GXMNVGcall* call) +{ + gxmnvg__setUniforms(gxm, call->uniformOffset, call->image); + gxmDrawArrays(gxm, SCE_GXM_PRIMITIVE_TRIANGLES, call->triangleOffset, call->triangleCount); +} + +static void gxmnvg__renderCancel(void* uptr) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + gxm->nverts = 0; + gxm->npaths = 0; + gxm->ncalls = 0; + gxm->nuniforms = 0; +} + +static SceGxmBlendFactor gxmnvg_convertBlendFuncFactor(int factor) +{ + switch (factor) + { + case NVG_ZERO: + return SCE_GXM_BLEND_FACTOR_ZERO; + case NVG_ONE: + return SCE_GXM_BLEND_FACTOR_ONE; + case NVG_SRC_COLOR: + return SCE_GXM_BLEND_FACTOR_SRC_COLOR; + case NVG_ONE_MINUS_SRC_COLOR: + return SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case NVG_DST_COLOR: + return SCE_GXM_BLEND_FACTOR_DST_COLOR; + case NVG_ONE_MINUS_DST_COLOR: + return SCE_GXM_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case NVG_SRC_ALPHA: + return SCE_GXM_BLEND_FACTOR_SRC_ALPHA; + case NVG_ONE_MINUS_SRC_ALPHA: + return SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case NVG_DST_ALPHA: + return SCE_GXM_BLEND_FACTOR_DST_ALPHA; + case NVG_ONE_MINUS_DST_ALPHA: + return SCE_GXM_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case NVG_SRC_ALPHA_SATURATE: + return SCE_GXM_BLEND_FACTOR_SRC_ALPHA_SATURATE; + } + + // act like invalid + return SCE_GXM_BLEND_FACTOR_DST_ALPHA_SATURATE; +} + +static GXMNVGblend gxmnvg__blendCompositeOperation(NVGcompositeOperationState op) +{ + GXMNVGblend blend; + blend.srcRGB = gxmnvg_convertBlendFuncFactor(op.srcRGB); + blend.dstRGB = gxmnvg_convertBlendFuncFactor(op.dstRGB); + blend.srcAlpha = gxmnvg_convertBlendFuncFactor(op.srcAlpha); + blend.dstAlpha = gxmnvg_convertBlendFuncFactor(op.dstAlpha); + // act like invalid + if (blend.srcRGB == SCE_GXM_BLEND_FACTOR_DST_ALPHA_SATURATE || blend.dstRGB == SCE_GXM_BLEND_FACTOR_DST_ALPHA_SATURATE || blend.srcAlpha == SCE_GXM_BLEND_FACTOR_DST_ALPHA_SATURATE || blend.dstAlpha == SCE_GXM_BLEND_FACTOR_DST_ALPHA_SATURATE) + { + blend.srcRGB = SCE_GXM_BLEND_FACTOR_ONE; + blend.dstRGB = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blend.srcAlpha = SCE_GXM_BLEND_FACTOR_ONE; + blend.dstAlpha = SCE_GXM_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + } + return blend; +} + +static void gxmnvg__renderFlush(void* uptr) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + + int i; + if (gxm->ncalls > 0) + { + // Setup require GXM state. + sceGxmSetVertexProgram(gxm->context, gxm->shader.prog.vert); + sceGxmSetFragmentProgram(gxm->context, gxm->shader.prog.frag); + + sceGxmSetCullMode(gxm->context, SCE_GXM_CULL_CW); + sceGxmSetTwoSidedEnable(gxm->context, SCE_GXM_TWO_SIDED_DISABLED); + sceGxmSetFrontStencilRef(gxm->context, 0); + sceGxmSetBackStencilRef(gxm->context, 0); + + gxmnvg__disableStencilTest(gxm); + + // Set view just once per frame. + { + void* uniform_buffer; + sceGxmReserveVertexDefaultUniformBuffer(gxm->context, &uniform_buffer); + sceGxmSetUniformDataF(uniform_buffer, gxm->shader.loc[GXMNVG_LOC_VIEWSIZE], 0, 2, gxm->view); + } + + for (i = 0; i < gxm->ncalls; i++) + { + GXMNVGcall* call = &gxm->calls[i]; + // gxmnvg__blendFuncSeparate(gxm, &call->blendFunc); + if (call->type == GXMNVG_FILL) + gxmnvg__fill(gxm, call); + else if (call->type == GXMNVG_CONVEXFILL) + gxmnvg__convexFill(gxm, call); + else if (call->type == GXMNVG_STROKE) + gxmnvg__stroke(gxm, call); + else if (call->type == GXMNVG_TRIANGLES) + gxmnvg__triangles(gxm, call); + } + } + + // Reset calls + gxm->nverts = 0; + gxm->npaths = 0; + gxm->ncalls = 0; + gxm->nuniforms = 0; + + // texture gc + gxmnvg__garbageCollector(gxm); +} + +static int gxmnvg__maxVertCount(const NVGpath* paths, int npaths) +{ + int i, count = 0; + for (i = 0; i < npaths; i++) + { + count += paths[i].nfill; + count += paths[i].nstroke; + } + return count; +} + +static GXMNVGcall* gxmnvg__allocCall(GXMNVGcontext* gxm) +{ + GXMNVGcall* ret = NULL; + if (gxm->ncalls + 1 > gxm->ccalls) + { + GXMNVGcall* calls; + int ccalls = gxmnvg__maxi(gxm->ncalls + 1, 128) + gxm->ccalls / 2; // 1.5x Overallocate + calls = (GXMNVGcall*)realloc(gxm->calls, sizeof(GXMNVGcall) * ccalls); + if (calls == NULL) + return NULL; + gxm->calls = calls; + gxm->ccalls = ccalls; + } + ret = &gxm->calls[gxm->ncalls++]; + memset(ret, 0, sizeof(GXMNVGcall)); + return ret; +} + +static int gxmnvg__allocPaths(GXMNVGcontext* gxm, int n) +{ + int ret = 0; + if (gxm->npaths + n > gxm->cpaths) + { + GXMNVGpath* paths; + int cpaths = gxmnvg__maxi(gxm->npaths + n, 128) + gxm->cpaths / 2; // 1.5x Overallocate + paths = (GXMNVGpath*)realloc(gxm->paths, sizeof(GXMNVGpath) * cpaths); + if (paths == NULL) + return -1; + gxm->paths = paths; + gxm->cpaths = cpaths; + } + ret = gxm->npaths; + gxm->npaths += n; + return ret; +} + +static int gxmnvg__allocVerts(GXMNVGcontext* gxm, int n) +{ + int ret = 0; + if (gxm->nverts + n > gxm->cverts) + { + NVGvertex* verts; + int cverts = gxmnvg__maxi(gxm->nverts + n, 4096) + gxm->cverts / 2; // 1.5x Overallocate + verts = (NVGvertex*)realloc(gxm->verts, sizeof(NVGvertex) * cverts); + if (verts == NULL) + return -1; + gxm->verts = verts; + gxm->cverts = cverts; + } + ret = gxm->nverts; + gxm->nverts += n; + return ret; +} + +static int gxmnvg__allocFragUniforms(GXMNVGcontext* gxm, int n) +{ + int ret = 0, structSize = gxm->fragSize; + if (gxm->nuniforms + n > gxm->cuniforms) + { + unsigned char* uniforms; + int cuniforms = gxmnvg__maxi(gxm->nuniforms + n, 128) + gxm->cuniforms / 2; // 1.5x Overallocate + uniforms = (unsigned char*)realloc(gxm->uniforms, structSize * cuniforms); + if (uniforms == NULL) + return -1; + gxm->uniforms = uniforms; + gxm->cuniforms = cuniforms; + } + ret = gxm->nuniforms * structSize; + gxm->nuniforms += n; + return ret; +} + +static GXMNVGfragUniforms* nvg__fragUniformPtr(GXMNVGcontext* gxm, int i) +{ + return (GXMNVGfragUniforms*)&gxm->uniforms[i]; +} + +static void gxmnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) +{ + vtx->x = x; + vtx->y = y; + vtx->u = u; + vtx->v = v; +} + +static void gxmnvg__renderFill(void* uptr, NVGpaint* paint, + NVGcompositeOperationState compositeOperation, NVGscissor* scissor, + float fringe, const float* bounds, const NVGpath* paths, int npaths) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + GXMNVGcall* call = gxmnvg__allocCall(gxm); + NVGvertex* quad; + GXMNVGfragUniforms* frag; + int i, maxverts, offset; + + if (call == NULL) + return; + + call->type = GXMNVG_FILL; + call->triangleCount = 4; + call->pathOffset = gxmnvg__allocPaths(gxm, npaths); + if (call->pathOffset == -1) + goto error; + call->pathCount = npaths; + call->image = paint->image; + call->blendFunc = gxmnvg__blendCompositeOperation(compositeOperation); + + if (npaths == 1 && paths[0].convex) + { + call->type = GXMNVG_CONVEXFILL; + call->triangleCount = 0; // Bounding box fill quad not needed for convex fill + } + + // Allocate vertices for all the paths. + maxverts = gxmnvg__maxVertCount(paths, npaths) + call->triangleCount; + offset = gxmnvg__allocVerts(gxm, maxverts); + if (offset == -1) + goto error; + + for (i = 0; i < npaths; i++) + { + GXMNVGpath* copy = &gxm->paths[call->pathOffset + i]; + const NVGpath* path = &paths[i]; + memset(copy, 0, sizeof(GXMNVGpath)); + if (path->nfill > 0) + { + copy->fillOffset = offset; + copy->fillCount = path->nfill; + memcpy(&gxm->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill); + offset += path->nfill; + } + if (path->nstroke > 0) + { + copy->strokeOffset = offset; + copy->strokeCount = path->nstroke; + memcpy(&gxm->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); + offset += path->nstroke; + } + } + + // Setup uniforms for draw calls + if (call->type == GXMNVG_FILL) + { + // Quad + call->triangleOffset = offset; + quad = &gxm->verts[call->triangleOffset]; + gxmnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); + gxmnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); + gxmnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); + gxmnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); + + call->uniformOffset = gxmnvg__allocFragUniforms(gxm, 2); + if (call->uniformOffset == -1) + goto error; + // Simple shader for stencil + frag = nvg__fragUniformPtr(gxm, call->uniformOffset); + memset(frag, 0, sizeof(*frag)); + frag->strokeThr = -1.0f; + frag->type = NSVG_SHADER_SIMPLE; + // Fill shader + gxmnvg__convertPaint(gxm, nvg__fragUniformPtr(gxm, call->uniformOffset + gxm->fragSize), paint, scissor, fringe, + fringe, -1.0f); + } + else + { + call->uniformOffset = gxmnvg__allocFragUniforms(gxm, 1); + if (call->uniformOffset == -1) + goto error; + // Fill shader + gxmnvg__convertPaint(gxm, nvg__fragUniformPtr(gxm, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); + } + + return; + +error: + // We get here if call alloc was ok, but something else is not. + // Roll back the last call to prevent drawing it. + if (gxm->ncalls > 0) + gxm->ncalls--; +} + +static void gxmnvg__renderStroke(void* uptr, NVGpaint* paint, + NVGcompositeOperationState compositeOperation, NVGscissor* scissor, + float fringe, float strokeWidth, const NVGpath* paths, int npaths) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + GXMNVGcall* call = gxmnvg__allocCall(gxm); + int i, maxverts, offset; + + if (call == NULL) + return; + + call->type = GXMNVG_STROKE; + call->pathOffset = gxmnvg__allocPaths(gxm, npaths); + if (call->pathOffset == -1) + goto error; + call->pathCount = npaths; + call->image = paint->image; + call->blendFunc = gxmnvg__blendCompositeOperation(compositeOperation); + + // Allocate vertices for all the paths. + maxverts = gxmnvg__maxVertCount(paths, npaths); + offset = gxmnvg__allocVerts(gxm, maxverts); + if (offset == -1) + goto error; + + for (i = 0; i < npaths; i++) + { + GXMNVGpath* copy = &gxm->paths[call->pathOffset + i]; + const NVGpath* path = &paths[i]; + memset(copy, 0, sizeof(GXMNVGpath)); + if (path->nstroke) + { + copy->strokeOffset = offset; + copy->strokeCount = path->nstroke; + memcpy(&gxm->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); + offset += path->nstroke; + } + } + + if (gxm->flags & NVG_STENCIL_STROKES) + { + // Fill shader + call->uniformOffset = gxmnvg__allocFragUniforms(gxm, 2); + if (call->uniformOffset == -1) + goto error; + + gxmnvg__convertPaint(gxm, nvg__fragUniformPtr(gxm, call->uniformOffset), paint, scissor, strokeWidth, fringe, + -1.0f); + gxmnvg__convertPaint(gxm, nvg__fragUniformPtr(gxm, call->uniformOffset + gxm->fragSize), paint, scissor, + strokeWidth, fringe, 1.0f - 0.5f / 255.0f); + } + else + { + // Fill shader + call->uniformOffset = gxmnvg__allocFragUniforms(gxm, 1); + if (call->uniformOffset == -1) + goto error; + gxmnvg__convertPaint(gxm, nvg__fragUniformPtr(gxm, call->uniformOffset), paint, scissor, strokeWidth, fringe, + -1.0f); + } + + return; + +error: + // We get here if call alloc was ok, but something else is not. + // Roll back the last call to prevent drawing it. + if (gxm->ncalls > 0) + gxm->ncalls--; +} + +static void gxmnvg__renderTriangles(void* uptr, NVGpaint* paint, + NVGcompositeOperationState compositeOperation, NVGscissor* scissor, + const NVGvertex* verts, int nverts, float fringe) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + GXMNVGcall* call = gxmnvg__allocCall(gxm); + GXMNVGfragUniforms* frag; + + if (call == NULL) + return; + + call->type = GXMNVG_TRIANGLES; + call->image = paint->image; + call->blendFunc = gxmnvg__blendCompositeOperation(compositeOperation); + + // Allocate vertices for all the paths. + call->triangleOffset = gxmnvg__allocVerts(gxm, nverts); + if (call->triangleOffset == -1) + goto error; + call->triangleCount = nverts; + + memcpy(&gxm->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); + + // Fill shader + call->uniformOffset = gxmnvg__allocFragUniforms(gxm, 1); + if (call->uniformOffset == -1) + goto error; + frag = nvg__fragUniformPtr(gxm, call->uniformOffset); + gxmnvg__convertPaint(gxm, frag, paint, scissor, 1.0f, fringe, -1.0f); + frag->type = NSVG_SHADER_IMG; + + return; + +error: + // We get here if call alloc was ok, but something else is not. + // Roll back the last call to prevent drawing it. + if (gxm->ncalls > 0) + gxm->ncalls--; +} + +static void gxmnvg__renderDelete(void* uptr) +{ + GXMNVGcontext* gxm = (GXMNVGcontext*)uptr; + int i; + if (gxm == NULL) + return; + + // Ensure the GPU is not using memory + sceGxmFinish(gxm->context); + sceGxmDisplayQueueFinish(); + + gpu_unmap_free(gxm->verticesUid); // vertex stream + + for (i = 0; i < gxm->ntextures; i++) + { + if (gxm->textures[i].data_UID != 0 && (gxm->textures[i].flags & NVG_IMAGE_NODELETE) == 0) + gpu_unmap_free(gxm->textures[i].data_UID); + } + + gxmnvg__deleteShader(&gxm->shader); + gxmnvg__deleteShader(&gxm->depth_shader); + + free(gxm->textures); + free(gxm->paths); + free(gxm->verts); + free(gxm->uniforms); + free(gxm->calls); + + free(gxm); +} + +NVGcontext* nvgCreateGXM(NVGXMframebuffer* fb, int flags) +{ + NVGparams params; + NVGcontext* ctx = NULL; + GXMNVGcontext* gxm = (GXMNVGcontext*)malloc(sizeof(GXMNVGcontext)); + if (gxm == NULL) + goto error; + memset(gxm, 0, sizeof(GXMNVGcontext)); + gxm->context = fb->context; + gxm->shader_patcher = fb->shader_patcher; + gxm->msaa = fb->msaa; + + memset(¶ms, 0, sizeof(params)); + params.renderCreate = gxmnvg__renderCreate; + params.renderCreateTexture = gxmnvg__renderCreateTexture; + params.renderDeleteTexture = gxmnvg__renderDeleteTexture; + params.renderUpdateTexture = gxmnvg__renderUpdateTexture; + params.renderGetTextureSize = gxmnvg__renderGetTextureSize; + params.renderViewport = gxmnvg__renderViewport; + params.renderCancel = gxmnvg__renderCancel; + params.renderFlush = gxmnvg__renderFlush; + params.renderFill = gxmnvg__renderFill; + params.renderStroke = gxmnvg__renderStroke; + params.renderTriangles = gxmnvg__renderTriangles; + params.renderDelete = gxmnvg__renderDelete; + params.userPtr = gxm; + params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; + + gxm->flags = flags; + +#ifdef USE_VITA_SHARK + if (flags & NVG_DEBUG) + { + shark_set_warnings_level(SHARK_WARN_MAX); + shark_install_log_cb(shark_log_cb); + } +#endif + + ctx = nvgCreateInternal(¶ms); + if (ctx == NULL) + goto error; + + return ctx; + +error: + // 'gxm' is freed by nvgDeleteInternal. + if (ctx != NULL) + nvgDeleteInternal(ctx); + return NULL; +} + +void nvgDeleteGXM(NVGcontext* ctx) +{ + nvgDeleteInternal(ctx); +} + +#endif /* NANOVG_GL_IMPLEMENTATION */ diff --git a/library/include/borealis/extern/nanovg/nanovg_gxm_utils.h b/library/include/borealis/extern/nanovg/nanovg_gxm_utils.h new file mode 100644 index 000000000..6843cf8b1 --- /dev/null +++ b/library/include/borealis/extern/nanovg/nanovg_gxm_utils.h @@ -0,0 +1,905 @@ +// +// Copyright (c) 2024 xfangfang xfangfang@126.com +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef NANOVG_GXM_UTILS_H +#define NANOVG_GXM_UTILS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#ifdef USE_VITA_SHARK + +#include + +#endif + +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) + +#define DISPLAY_WIDTH 960 +#define DISPLAY_HEIGHT 544 +#define DISPLAY_STRIDE 960 +#define DISPLAY_BUFFER_COUNT 3 +#define MAX_PENDING_SWAPS (DISPLAY_BUFFER_COUNT - 1) + +#define DISPLAY_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8 +#define DISPLAY_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8 + +static int gxm__error_status = SCE_OK; +#define GXM_PRINT_ERROR(status) sceClibPrintf("[line %d] failed with reason: %s\n", __LINE__, gxmnvg__easy_strerror(status)) +#define GXM_CHECK_RETURN(func, ret) \ + gxm__error_status = func; \ + if (gxm__error_status != SCE_OK) \ + { \ + GXM_PRINT_ERROR(gxm__error_status); \ + return ret; \ + } +#define GXM_CHECK(func) GXM_CHECK_RETURN(func, 0) +#define GXM_CHECK_VOID(func) GXM_CHECK_RETURN(func, ) + +struct NVGXMinitOptions { + SceGxmMultisampleMode msaa; + int swapInterval; + int dumpShader; // dump shader to ux0:data/nvg_name_type.c +}; +typedef struct NVGXMinitOptions NVGXMinitOptions; + +struct NVGXMshaderProgram { + SceGxmShaderPatcherId vert_id; + SceGxmShaderPatcherId frag_id; + + SceGxmVertexProgram *vert; + SceGxmFragmentProgram *frag; + + SceGxmProgram *vert_gxp; + SceGxmProgram *frag_gxp; +}; +typedef struct NVGXMshaderProgram NVGXMshaderProgram; + +struct NVGXMframebuffer { + SceGxmContext *context; + SceGxmShaderPatcher *shader_patcher; + SceGxmMultisampleMode msaa; +}; +typedef struct NVGXMframebuffer NVGXMframebuffer; + +// Helper function to init gxm. +NVGXMframebuffer *nvgxmCreateFramebuffer(const NVGXMinitOptions *opts); + +void nvgxmDeleteFramebuffer(NVGXMframebuffer *gxm); + +/** + * @brief Begin a scene. + */ +void gxmBeginFrame(); + +/** + * @brief End a scene. + */ +void gxmEndFrame(); + +/** + * @brief Swap the buffers. + */ +void gxmSwapBuffer(); + +/** + * @brief Set the clear color. + */ +void gxmClearColor(float r, float g, float b, float a); + +/** + * @brief Clear the framebuffer and stencil buffer. + * Must be called between gxmBeginFrame and gxmEndFrame. + */ +void gxmClear(); + +/** + * @brief Get framebuffer data. + */ +void *gxmReadPixels(); + +/** + * @brief Set the swap interval. + * @param interval N for vsync, 0 for immediate. + */ +void gxmSwapInterval(int interval); + +int gxmDialogUpdate(); + +unsigned short *gxmGetSharedIndices(); + +int gxmCreateShader(NVGXMshaderProgram *shader, const char *name, const char *vshader, const char *fshader); + +void gxmDeleteShader(NVGXMshaderProgram *prog); + +void gpu_unmap_free(SceUID uid); + +void *gpu_alloc_map(SceKernelMemBlockType type, SceGxmMemoryAttribFlags gpu_attrib, size_t size, SceUID *uid); + +#ifdef __cplusplus +} +#endif + +#endif // NANOVG_GXM_UTILS_H + +#ifdef NANOVG_GXM_UTILS_IMPLEMENTATION + +#include +#include +#include + +#include +#include + +struct display_queue_callback_data { + void *addr; +}; + +struct clear_vertex { + float x, y; +}; + +static struct gxm_internal { + SceGxmContext *context; + SceGxmShaderPatcher *shader_patcher; + NVGXMinitOptions initOptions; + + // clear shader + NVGXMshaderProgram clearProg; + NVGcolor *clearColor; + SceUID clearColorUid; + SceUID clearVerticesUid; + struct clear_vertex *clearVertices; + + // shared indices + SceUID linearIndicesUid; + unsigned short *linearIndices; +} gxm_internal; + +static SceUID vdm_ring_buffer_uid; +static void *vdm_ring_buffer_addr; +static SceUID vertex_ring_buffer_uid; +static void *vertex_ring_buffer_addr; +static SceUID fragment_ring_buffer_uid; +static void *fragment_ring_buffer_addr; +static SceUID fragment_usse_ring_buffer_uid; +static void *fragment_usse_ring_buffer_addr; +static SceGxmRenderTarget *gxm_render_target; +static SceGxmColorSurface gxm_color_surfaces[DISPLAY_BUFFER_COUNT]; +static SceUID gxm_color_surfaces_uid[DISPLAY_BUFFER_COUNT]; +static void *gxm_color_surfaces_addr[DISPLAY_BUFFER_COUNT]; +static SceGxmSyncObject *gxm_sync_objects[DISPLAY_BUFFER_COUNT]; +static unsigned int gxm_front_buffer_index; +static unsigned int gxm_back_buffer_index; +static SceUID gxm_depth_stencil_surface_uid; +static void *gxm_depth_stencil_surface_addr; +static SceGxmDepthStencilSurface gxm_depth_stencil_surface; +static SceUID gxm_shader_patcher_buffer_uid; +static void *gxm_shader_patcher_buffer_addr; +static SceUID gxm_shader_patcher_vertex_usse_uid; +static void *gxm_shader_patcher_vertex_usse_addr; +static SceUID gxm_shader_patcher_fragment_usse_uid; +static void *gxm_shader_patcher_fragment_usse_addr; + +static const char *gxmnvg__easy_strerror(int code) { + switch ((SceGxmErrorCode) code) { + case SCE_GXM_ERROR_UNINITIALIZED: + return "SCE_GXM_ERROR_UNINITIALIZED"; + case SCE_GXM_ERROR_ALREADY_INITIALIZED: + return "SCE_GXM_ERROR_ALREADY_INITIALIZED"; + case SCE_GXM_ERROR_OUT_OF_MEMORY: + return "SCE_GXM_ERROR_OUT_OF_MEMORY"; + case SCE_GXM_ERROR_INVALID_VALUE: + return "SCE_GXM_ERROR_INVALID_VALUE"; + case SCE_GXM_ERROR_INVALID_POINTER: + return "SCE_GXM_ERROR_INVALID_POINTER"; + case SCE_GXM_ERROR_INVALID_ALIGNMENT: + return "SCE_GXM_ERROR_INVALID_ALIGNMENT"; + case SCE_GXM_ERROR_NOT_WITHIN_SCENE: + return "SCE_GXM_ERROR_NOT_WITHIN_SCENE"; + case SCE_GXM_ERROR_WITHIN_SCENE: + return "SCE_GXM_ERROR_WITHIN_SCENE"; + case SCE_GXM_ERROR_NULL_PROGRAM: + return "SCE_GXM_ERROR_NULL_PROGRAM"; + case SCE_GXM_ERROR_UNSUPPORTED: + return "SCE_GXM_ERROR_UNSUPPORTED"; + case SCE_GXM_ERROR_PATCHER_INTERNAL: + return "SCE_GXM_ERROR_PATCHER_INTERNAL"; + case SCE_GXM_ERROR_RESERVE_FAILED: + return "SCE_GXM_ERROR_RESERVE_FAILED"; + case SCE_GXM_ERROR_PROGRAM_IN_USE: + return "SCE_GXM_ERROR_PROGRAM_IN_USE"; + case SCE_GXM_ERROR_INVALID_INDEX_COUNT: + return "SCE_GXM_ERROR_INVALID_INDEX_COUNT"; + case SCE_GXM_ERROR_INVALID_POLYGON_MODE: + return "SCE_GXM_ERROR_INVALID_POLYGON_MODE"; + case SCE_GXM_ERROR_INVALID_SAMPLER_RESULT_TYPE_PRECISION: + return "SCE_GXM_ERROR_INVALID_SAMPLER_RESULT_TYPE_PRECISION"; + case SCE_GXM_ERROR_INVALID_SAMPLER_RESULT_TYPE_COMPONENT_COUNT: + return "SCE_GXM_ERROR_INVALID_SAMPLER_RESULT_TYPE_COMPONENT_COUNT"; + case SCE_GXM_ERROR_UNIFORM_BUFFER_NOT_RESERVED: + return "SCE_GXM_ERROR_UNIFORM_BUFFER_NOT_RESERVED"; + case SCE_GXM_ERROR_INVALID_AUXILIARY_SURFACE: + return "SCE_GXM_ERROR_INVALID_AUXILIARY_SURFACE"; + case SCE_GXM_ERROR_INVALID_PRECOMPUTED_DRAW: + return "SCE_GXM_ERROR_INVALID_PRECOMPUTED_DRAW"; + case SCE_GXM_ERROR_INVALID_PRECOMPUTED_VERTEX_STATE: + return "SCE_GXM_ERROR_INVALID_PRECOMPUTED_VERTEX_STATE"; + case SCE_GXM_ERROR_INVALID_PRECOMPUTED_FRAGMENT_STATE: + return "SCE_GXM_ERROR_INVALID_PRECOMPUTED_FRAGMENT_STATE"; + case SCE_GXM_ERROR_DRIVER: + return "SCE_GXM_ERROR_DRIVER"; + case SCE_GXM_ERROR_INVALID_TEXTURE: + return "SCE_GXM_ERROR_INVALID_TEXTURE"; + case SCE_GXM_ERROR_INVALID_TEXTURE_DATA_POINTER: + return "SCE_GXM_ERROR_INVALID_TEXTURE_DATA_POINTER"; + case SCE_GXM_ERROR_INVALID_TEXTURE_PALETTE_POINTER: + return "SCE_GXM_ERROR_INVALID_TEXTURE_PALETTE_POINTER"; + case SCE_GXM_ERROR_OUT_OF_RENDER_TARGETS: + return "SCE_GXM_ERROR_OUT_OF_RENDER_TARGETS"; + default: + return "Unknown error"; + } +} + +static void display_queue_callback(const void *callbackData) { + SceDisplayFrameBuf display_fb; + const struct display_queue_callback_data *cb_data = (struct display_queue_callback_data *) callbackData; + + memset(&display_fb, 0, sizeof(display_fb)); + display_fb.size = sizeof(display_fb); + display_fb.base = cb_data->addr; + display_fb.pitch = DISPLAY_STRIDE; + display_fb.pixelformat = DISPLAY_PIXEL_FORMAT; + display_fb.width = DISPLAY_WIDTH; + display_fb.height = DISPLAY_HEIGHT; + + sceDisplaySetFrameBuf(&display_fb, SCE_DISPLAY_SETBUF_NEXTFRAME); + + if (gxm_internal.initOptions.swapInterval) { + GXM_CHECK_VOID(sceDisplayWaitVblankStartMulti(gxm_internal.initOptions.swapInterval)); + } +} + +void *gpu_alloc_map(SceKernelMemBlockType type, SceGxmMemoryAttribFlags gpu_attrib, size_t size, SceUID *uid) { + SceUID memuid; + void *addr; + + if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW) + size = ALIGN(size, 256 * 1024); + else + size = ALIGN(size, 4 * 1024); + + memuid = sceKernelAllocMemBlock("gpumem", type, size, NULL); + if (memuid < 0) + return NULL; + + if (sceKernelGetMemBlockBase(memuid, &addr) < 0) + return NULL; + + if (sceGxmMapMemory(addr, size, gpu_attrib) < 0) { + sceKernelFreeMemBlock(memuid); + return NULL; + } + + if (uid) + *uid = memuid; + + return addr; +} + +void gpu_unmap_free(SceUID uid) { + void *addr; + + if (sceKernelGetMemBlockBase(uid, &addr) < 0) + return; + + sceGxmUnmapMemory(addr); + + sceKernelFreeMemBlock(uid); +} + +static void *gpu_vertex_usse_alloc_map(size_t size, SceUID *uid, unsigned int *usse_offset) { + SceUID memuid; + void *addr; + + size = ALIGN(size, 4 * 1024); + + memuid = sceKernelAllocMemBlock("gpu_vertex_usse", + SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, size, NULL); + if (memuid < 0) + return NULL; + + if (sceKernelGetMemBlockBase(memuid, &addr) < 0) + return NULL; + + if (sceGxmMapVertexUsseMemory(addr, size, usse_offset) < 0) + return NULL; + + return addr; +} + +static void gpu_vertex_usse_unmap_free(SceUID uid) { + void *addr; + + if (sceKernelGetMemBlockBase(uid, &addr) < 0) + return; + + sceGxmUnmapVertexUsseMemory(addr); + + sceKernelFreeMemBlock(uid); +} + +static void *gpu_fragment_usse_alloc_map(size_t size, SceUID *uid, unsigned int *usse_offset) { + SceUID memuid; + void *addr; + + size = ALIGN(size, 4 * 1024); + + memuid = sceKernelAllocMemBlock("gpu_fragment_usse", + SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, size, NULL); + if (memuid < 0) + return NULL; + + if (sceKernelGetMemBlockBase(memuid, &addr) < 0) + return NULL; + + if (sceGxmMapFragmentUsseMemory(addr, size, usse_offset) < 0) + return NULL; + + return addr; +} + +static void gpu_fragment_usse_unmap_free(SceUID uid) { + void *addr; + + if (sceKernelGetMemBlockBase(uid, &addr) < 0) + return; + + sceGxmUnmapFragmentUsseMemory(addr); + + sceKernelFreeMemBlock(uid); +} + +static void *shader_patcher_host_alloc_cb(void *user_data, unsigned int size) { + return malloc(size); +} + +static void shader_patcher_host_free_cb(void *user_data, void *mem) { + return free(mem); +} + +NVGXMframebuffer *nvgxmCreateFramebuffer(const NVGXMinitOptions *opts) { + NVGXMframebuffer *fb = NULL; + fb = (NVGXMframebuffer *) malloc(sizeof(NVGXMframebuffer)); + if (fb == NULL) { + nvgxmDeleteFramebuffer(fb); + return NULL; + } + memset(fb, 0, sizeof(NVGXMframebuffer)); + memcpy(&gxm_internal.initOptions, opts, sizeof(NVGXMinitOptions)); + fb->msaa = opts->msaa; + + SceGxmInitializeParams gxm_init_params; + memset(&gxm_init_params, 0, sizeof(gxm_init_params)); + gxm_init_params.flags = 0; + gxm_init_params.displayQueueMaxPendingCount = MAX_PENDING_SWAPS; + gxm_init_params.displayQueueCallback = display_queue_callback; + gxm_init_params.displayQueueCallbackDataSize = sizeof(struct display_queue_callback_data); + gxm_init_params.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE; + + sceGxmInitialize(&gxm_init_params); + + vdm_ring_buffer_addr = gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCE_GXM_MEMORY_ATTRIB_READ, SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE, + &vdm_ring_buffer_uid); + + vertex_ring_buffer_addr = gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCE_GXM_MEMORY_ATTRIB_READ, SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE, + &vertex_ring_buffer_uid); + + fragment_ring_buffer_addr = gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCE_GXM_MEMORY_ATTRIB_READ, SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE, + &fragment_ring_buffer_uid); + + unsigned int fragment_usse_offset; + fragment_usse_ring_buffer_addr = gpu_fragment_usse_alloc_map( + SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE, + &fragment_ring_buffer_uid, &fragment_usse_offset); + + SceGxmContextParams gxm_context_params; + memset(&gxm_context_params, 0, sizeof(gxm_context_params)); + gxm_context_params.hostMem = malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE); + gxm_context_params.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE; + gxm_context_params.vdmRingBufferMem = vdm_ring_buffer_addr; + gxm_context_params.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE; + gxm_context_params.vertexRingBufferMem = vertex_ring_buffer_addr; + gxm_context_params.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE; + gxm_context_params.fragmentRingBufferMem = fragment_ring_buffer_addr; + gxm_context_params.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE; + gxm_context_params.fragmentUsseRingBufferMem = fragment_usse_ring_buffer_addr; + gxm_context_params.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE; + gxm_context_params.fragmentUsseRingBufferOffset = fragment_usse_offset; + + sceGxmCreateContext(&gxm_context_params, &fb->context); + + SceGxmRenderTargetParams render_target_params; + memset(&render_target_params, 0, sizeof(render_target_params)); + render_target_params.flags = 0; + render_target_params.width = DISPLAY_WIDTH; + render_target_params.height = DISPLAY_HEIGHT; + render_target_params.scenesPerFrame = 1; + render_target_params.multisampleMode = opts->msaa; + render_target_params.multisampleLocations = 0; + render_target_params.driverMemBlock = -1; + + sceGxmCreateRenderTarget(&render_target_params, &gxm_render_target); + + for (int i = 0; i < DISPLAY_BUFFER_COUNT; i++) { + gxm_color_surfaces_addr[i] = gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCE_GXM_MEMORY_ATTRIB_RW, + ALIGN(4 * DISPLAY_STRIDE * DISPLAY_HEIGHT, 1 * 1024 * 1024), + &gxm_color_surfaces_uid[i]); + + memset(gxm_color_surfaces_addr[i], 0, DISPLAY_STRIDE * DISPLAY_HEIGHT); + + sceGxmColorSurfaceInit(&gxm_color_surfaces[i], + DISPLAY_COLOR_FORMAT, + SCE_GXM_COLOR_SURFACE_LINEAR, + (opts->msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE + : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE, + SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT, + DISPLAY_WIDTH, + DISPLAY_HEIGHT, + DISPLAY_STRIDE, + gxm_color_surfaces_addr[i]); + + sceGxmSyncObjectCreate(&gxm_sync_objects[i]); + } + + unsigned int depth_stencil_width = ALIGN(DISPLAY_WIDTH, SCE_GXM_TILE_SIZEX); + unsigned int depth_stencil_height = ALIGN(DISPLAY_HEIGHT, SCE_GXM_TILE_SIZEY); + unsigned int depth_stencil_samples = depth_stencil_width * depth_stencil_height; + + if (opts->msaa == SCE_GXM_MULTISAMPLE_4X) { + // samples increase in X and Y + depth_stencil_samples *= 4; + depth_stencil_width *= 2; + } else if (opts->msaa == SCE_GXM_MULTISAMPLE_2X) { + // samples increase in Y only + depth_stencil_samples *= 2; + } + gxm_depth_stencil_surface_addr = gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCE_GXM_MEMORY_ATTRIB_RW, + 4 * depth_stencil_samples, &gxm_depth_stencil_surface_uid); + + sceGxmDepthStencilSurfaceInit(&gxm_depth_stencil_surface, + SCE_GXM_DEPTH_STENCIL_FORMAT_S8D24, + SCE_GXM_DEPTH_STENCIL_SURFACE_TILED, + depth_stencil_width, + gxm_depth_stencil_surface_addr, + NULL); + + static const unsigned int shader_patcher_buffer_size = 64 * 1024; + static const unsigned int shader_patcher_vertex_usse_size = 64 * 1024; + static const unsigned int shader_patcher_fragment_usse_size = 64 * 1024; + + gxm_shader_patcher_buffer_addr = gpu_alloc_map(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCE_GXM_MEMORY_ATTRIB_RW, + shader_patcher_buffer_size, &gxm_shader_patcher_buffer_uid); + + unsigned int shader_patcher_vertex_usse_offset; + gxm_shader_patcher_vertex_usse_addr = gpu_vertex_usse_alloc_map( + shader_patcher_vertex_usse_size, &gxm_shader_patcher_vertex_usse_uid, + &shader_patcher_vertex_usse_offset); + + unsigned int shader_patcher_fragment_usse_offset; + gxm_shader_patcher_fragment_usse_addr = gpu_fragment_usse_alloc_map( + shader_patcher_fragment_usse_size, &gxm_shader_patcher_fragment_usse_uid, + &shader_patcher_fragment_usse_offset); + + SceGxmShaderPatcherParams shader_patcher_params; + memset(&shader_patcher_params, 0, sizeof(shader_patcher_params)); + shader_patcher_params.userData = NULL; + shader_patcher_params.hostAllocCallback = shader_patcher_host_alloc_cb; + shader_patcher_params.hostFreeCallback = shader_patcher_host_free_cb; + shader_patcher_params.bufferAllocCallback = NULL; + shader_patcher_params.bufferFreeCallback = NULL; + shader_patcher_params.bufferMem = gxm_shader_patcher_buffer_addr; + shader_patcher_params.bufferMemSize = shader_patcher_buffer_size; + shader_patcher_params.vertexUsseAllocCallback = NULL; + shader_patcher_params.vertexUsseFreeCallback = NULL; + shader_patcher_params.vertexUsseMem = gxm_shader_patcher_vertex_usse_addr; + shader_patcher_params.vertexUsseMemSize = shader_patcher_vertex_usse_size; + shader_patcher_params.vertexUsseOffset = shader_patcher_vertex_usse_offset; + shader_patcher_params.fragmentUsseAllocCallback = NULL; + shader_patcher_params.fragmentUsseFreeCallback = NULL; + shader_patcher_params.fragmentUsseMem = gxm_shader_patcher_fragment_usse_addr; + shader_patcher_params.fragmentUsseMemSize = shader_patcher_fragment_usse_size; + shader_patcher_params.fragmentUsseOffset = shader_patcher_fragment_usse_offset; + + sceGxmShaderPatcherCreate(&shader_patcher_params, &fb->shader_patcher); + + gxm_internal.context = fb->context; + gxm_internal.shader_patcher = fb->shader_patcher; + + // shared indices + gxm_internal.linearIndices = (unsigned short *) gpu_alloc_map( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, + SCE_GXM_MEMORY_ATTRIB_READ, + UINT16_MAX * sizeof(unsigned short), + &gxm_internal.linearIndicesUid); + + for (uint32_t i = 0; i < UINT16_MAX; ++i) { + gxm_internal.linearIndices[i] = i; + } + + // clear shader +#if USE_VITA_SHARK + static const char *clearVertShader = "float4 main(float2 position) : POSITION\n" + "{\n" + " return float4(position, 1.f, 1.f);\n" + "}\n"; + + static const char *clearFragShader = "float4 main(uniform float4 color : BUFFER[0]) : COLOR\n" + "{\n" + " return color;\n" + "}\n"; +#else + static const unsigned char clearVertShader[252] = { + 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x00, 0x03, 0xf9, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x90, 0x3a, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x44, 0xfa, 0x01, 0x00, 0x04, 0x90, 0x85, 0x11, 0xa5, 0x08, 0x01, + 0x80, 0x56, 0x90, 0x81, 0x11, 0x83, 0x08, 0x00, 0x00, 0x20, 0xa0, + 0x00, 0x50, 0x27, 0xfb, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00 + }; + + static const unsigned char clearFragShader[276] = { + 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x00, 0x03, 0x12, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x90, 0x3a, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0xa0, 0x84, 0x30, 0x83, 0xe8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, + 0x04, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x44, 0xfa, 0x04, 0x81, 0x19, 0xf0, 0x7e, 0x0d, 0x80, 0x40, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x01, 0x04, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, + 0x00 + }; +#endif + gxmCreateShader(&gxm_internal.clearProg, "clear", (const char*)clearVertShader, (const char*)clearFragShader); + + gxm_internal.clearVertices = (struct clear_vertex *) gpu_alloc_map( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, + SCE_GXM_MEMORY_ATTRIB_READ, + 3 * sizeof(struct clear_vertex), + &gxm_internal.clearVerticesUid); + gxm_internal.clearVertices[0] = (struct clear_vertex) {-1.0f, -1.0f}; + gxm_internal.clearVertices[1] = (struct clear_vertex) {3.0f, -1.0f}; + gxm_internal.clearVertices[2] = (struct clear_vertex) {-1.0f, 3.0f}; + + gxm_internal.clearColor = (NVGcolor *) gpu_alloc_map( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, + SCE_GXM_MEMORY_ATTRIB_READ, + sizeof(NVGcolor), + &gxm_internal.clearColorUid); + gxm_internal.clearColor->r = 1.0f; + gxm_internal.clearColor->g = 1.0f; + gxm_internal.clearColor->b = 1.0f; + gxm_internal.clearColor->a = 1.0f; + sceGxmSetFragmentUniformBuffer(gxm_internal.context, 0, gxm_internal.clearColor->rgba); + + const SceGxmProgramParameter *clear_position_param = sceGxmProgramFindParameterByName( + gxm_internal.clearProg.vert_gxp, + "position"); + SceGxmVertexAttribute clear_vertex_attribute; + clear_vertex_attribute.streamIndex = 0; + clear_vertex_attribute.offset = 0; + clear_vertex_attribute.format = SCE_GXM_ATTRIBUTE_FORMAT_F32; + clear_vertex_attribute.componentCount = 2; + clear_vertex_attribute.regIndex = sceGxmProgramParameterGetResourceIndex(clear_position_param); + + SceGxmVertexStream clear_vertex_stream; + clear_vertex_stream.stride = sizeof(struct clear_vertex); + clear_vertex_stream.indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT; + + GXM_CHECK(sceGxmShaderPatcherCreateVertexProgram( + gxm_internal.shader_patcher, gxm_internal.clearProg.vert_id, + &clear_vertex_attribute, 1, + &clear_vertex_stream, 1, + &gxm_internal.clearProg.vert)); + + GXM_CHECK(sceGxmShaderPatcherCreateFragmentProgram( + gxm_internal.shader_patcher, gxm_internal.clearProg.frag_id, + SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, SCE_GXM_MULTISAMPLE_NONE, + NULL, gxm_internal.clearProg.vert_gxp, + &gxm_internal.clearProg.frag)); + + return fb; +} + +void nvgxmDeleteFramebuffer(NVGXMframebuffer *gxm) { + if (gxm == NULL) return; + + gpu_unmap_free(gxm_internal.linearIndicesUid); // linear index buffer + gpu_unmap_free(gxm_internal.clearVerticesUid); // clear vertex stream + gpu_unmap_free(gxm_internal.clearColorUid); // clear color uniform buffer + + gxmDeleteShader(&gxm_internal.clearProg); + + sceGxmShaderPatcherDestroy(gxm->shader_patcher); + + gpu_unmap_free(gxm_shader_patcher_buffer_uid); + gpu_vertex_usse_unmap_free(gxm_shader_patcher_vertex_usse_uid); + gpu_fragment_usse_unmap_free(gxm_shader_patcher_fragment_usse_uid); + + gpu_unmap_free(gxm_depth_stencil_surface_uid); + + for (int i = 0; i < DISPLAY_BUFFER_COUNT; i++) { + gpu_unmap_free(gxm_color_surfaces_uid[i]); + sceGxmSyncObjectDestroy(gxm_sync_objects[i]); + } + + sceGxmDestroyRenderTarget(gxm_render_target); + + gpu_unmap_free(vdm_ring_buffer_uid); + gpu_unmap_free(vertex_ring_buffer_uid); + gpu_unmap_free(fragment_ring_buffer_uid); + gpu_fragment_usse_unmap_free(fragment_usse_ring_buffer_uid); + + sceGxmDestroyContext(gxm->context); + sceGxmTerminate(); + + free(gxm); +} + +void gxmClearColor(float r, float g, float b, float a) { + gxm_internal.clearColor->r = r; + gxm_internal.clearColor->g = g; + gxm_internal.clearColor->b = b; + gxm_internal.clearColor->a = a; + + // TODO: Fix screen tearing + GXM_CHECK_VOID(sceGxmSetFragmentUniformBuffer(gxm_internal.context, 0, gxm_internal.clearColor->rgba)); +} + +void gxmClear() { + sceGxmSetVertexProgram(gxm_internal.context, gxm_internal.clearProg.vert); + sceGxmSetFragmentProgram(gxm_internal.context, gxm_internal.clearProg.frag); + + sceGxmSetVertexStream(gxm_internal.context, 0, gxm_internal.clearVertices); + + // clear stencil buffer + sceGxmSetFrontStencilRef(gxm_internal.context, 0); + sceGxmSetBackStencilRef(gxm_internal.context, 0); + sceGxmSetFrontStencilFunc(gxm_internal.context, SCE_GXM_STENCIL_FUNC_ALWAYS, SCE_GXM_STENCIL_OP_ZERO, + SCE_GXM_STENCIL_OP_ZERO, SCE_GXM_STENCIL_OP_ZERO, 0xff, 0xff); + sceGxmSetBackStencilFunc(gxm_internal.context, SCE_GXM_STENCIL_FUNC_ALWAYS, SCE_GXM_STENCIL_OP_ZERO, + SCE_GXM_STENCIL_OP_ZERO, SCE_GXM_STENCIL_OP_ZERO, 0xff, 0xff); + + sceGxmDraw(gxm_internal.context, SCE_GXM_PRIMITIVE_TRIANGLES, SCE_GXM_INDEX_FORMAT_U16, gxm_internal.linearIndices, + 3); +} + +void gxmBeginFrame() { + GXM_CHECK_VOID(sceGxmBeginScene(gxm_internal.context, + 0, + gxm_render_target, + NULL, + NULL, + gxm_sync_objects[gxm_back_buffer_index], + &gxm_color_surfaces[gxm_back_buffer_index], + &gxm_depth_stencil_surface)); +} + +void gxmEndFrame() { + GXM_CHECK_VOID(sceGxmEndScene(gxm_internal.context, NULL, NULL)); +} + +void gxmSwapBuffer() { + struct display_queue_callback_data queue_cb_data; + queue_cb_data.addr = gxm_color_surfaces_addr[gxm_back_buffer_index]; + + GXM_CHECK_VOID(sceGxmDisplayQueueAddEntry( + gxm_sync_objects[gxm_front_buffer_index], + gxm_sync_objects[gxm_back_buffer_index], + &queue_cb_data)); + + gxm_front_buffer_index = gxm_back_buffer_index; + gxm_back_buffer_index = (gxm_back_buffer_index + 1) % DISPLAY_BUFFER_COUNT; +} + +void gxmSwapInterval(int interval) { + gxm_internal.initOptions.swapInterval = interval; +} + +int gxmDialogUpdate() +{ + SceCommonDialogUpdateParam updateParam; + memset(&updateParam, 0, sizeof(updateParam)); + + updateParam.renderTarget.colorFormat = DISPLAY_COLOR_FORMAT; + updateParam.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR; + updateParam.renderTarget.width = DISPLAY_WIDTH; + updateParam.renderTarget.height = DISPLAY_HEIGHT; + updateParam.renderTarget.strideInPixels = DISPLAY_STRIDE; + + updateParam.renderTarget.colorSurfaceData = gxm_color_surfaces_addr[gxm_back_buffer_index]; + updateParam.renderTarget.depthSurfaceData = gxm_depth_stencil_surface_addr; + updateParam.displaySyncObject = gxm_sync_objects[gxm_back_buffer_index]; + + return sceCommonDialogUpdate(&updateParam); +} + +void *gxmReadPixels() { + return sceGxmColorSurfaceGetData(&gxm_color_surfaces[gxm_front_buffer_index]); +} + +unsigned short *gxmGetSharedIndices() { + return gxm_internal.linearIndices; +} + +#ifdef USE_VITA_SHARK + +void dumpShader(const char *name, const char *type, const SceGxmProgram *program, uint32_t size) { + char path[256]; + + int need_comma = 0; + char *buf = (char *) malloc(0x5000); + memset(buf, 0, 0x5000); + memcpy(buf, program, size); + + snprintf(path, sizeof(path), "ux0:data/nvg_%s%s.c", name, type); + FILE *fp = fopen(path, "w"); + if (fp) { + fprintf(fp, "static const unsigned %s%sShader[%i] = {", name, type, size); + for (int i = 0; i < size; ++i) { + if (need_comma) + fprintf(fp, ", "); + else + need_comma = 1; + if ((i % 11) == 0) + fprintf(fp, "\n\t"); + fprintf(fp, "0x%.2x", buf[i] & 0xff); + } + fprintf(fp, "\n};\n\n"); + fclose(fp); + } +} + +int gxmCreateShader(NVGXMshaderProgram *shader, const char *name, const char *vshader, const char *fshader) { + if (vshader != NULL) { + uint32_t size = strlen(vshader); + SceGxmProgram *p = shark_compile_shader(vshader, &size, SHARK_VERTEX_SHADER); + if (!p) { + sceClibPrintf("shark_compile_shader failed (vert): %s\n", name); + return 0; + } + shader->vert_gxp = (SceGxmProgram *) malloc(size); + sceClibMemcpy((void *) shader->vert_gxp, (void *) p, size); + shark_clear_output(); + GXM_CHECK(sceGxmShaderPatcherRegisterProgram(gxm_internal.shader_patcher, shader->vert_gxp, &shader->vert_id)); + GXM_CHECK(sceGxmProgramCheck(shader->vert_gxp)); + + if (gxm_internal.initOptions.dumpShader) { + dumpShader(name, "Vert", shader->vert_gxp, size); + } + } + + if (fshader != NULL) { + uint32_t size = strlen(fshader); + SceGxmProgram *p = shark_compile_shader(fshader, &size, SHARK_FRAGMENT_SHADER); + if (!p) { + sceClibPrintf("shark_compile_shader failed (frag): %s\n", name); + return 0; + } + shader->frag_gxp = (SceGxmProgram *) malloc(size); + sceClibMemcpy((void *) shader->frag_gxp, (void *) p, size); + shark_clear_output(); + GXM_CHECK(sceGxmShaderPatcherRegisterProgram(gxm_internal.shader_patcher, shader->frag_gxp, &shader->frag_id)); + GXM_CHECK(sceGxmProgramCheck(shader->frag_gxp)); + + if (gxm_internal.initOptions.dumpShader) { + dumpShader(name, "Frag", shader->frag_gxp, size); + } + } + return 1; +} + +#else + +int gxmCreateShader(NVGXMshaderProgram *shader, const char *name, const char *vshader, const char *fshader) { + (void) name; + if (vshader != NULL) { + shader->vert_gxp = (SceGxmProgram *) vshader; + GXM_CHECK(sceGxmShaderPatcherRegisterProgram(gxm_internal.shader_patcher, shader->vert_gxp, &shader->vert_id)); + GXM_CHECK(sceGxmProgramCheck(shader->vert_gxp)); + } + + if (fshader != NULL) { + shader->frag_gxp = (SceGxmProgram *) fshader; + GXM_CHECK(sceGxmShaderPatcherRegisterProgram(gxm_internal.shader_patcher, shader->frag_gxp, &shader->frag_id)); + GXM_CHECK(sceGxmProgramCheck(shader->frag_gxp)); + } + return 1; +} + +#endif + +void gxmDeleteShader(NVGXMshaderProgram *prog) { + if (gxm_internal.shader_patcher == NULL) + return; + + if (prog->vert) + sceGxmShaderPatcherReleaseVertexProgram(gxm_internal.shader_patcher, prog->vert); + if (prog->frag) + sceGxmShaderPatcherReleaseFragmentProgram(gxm_internal.shader_patcher, prog->frag); + + if (prog->vert_id) + sceGxmShaderPatcherUnregisterProgram(gxm_internal.shader_patcher, prog->vert_id); + if (prog->frag_id) + sceGxmShaderPatcherUnregisterProgram(gxm_internal.shader_patcher, prog->frag_id); + +#ifdef USE_VITA_SHARK + if (prog->vert_gxp) + free(prog->vert_gxp); + if (prog->frag_gxp) + free(prog->frag_gxp); +#endif +} + +#endif // NANOVG_GXM_UTILS_IMPLEMENTATION diff --git a/library/include/borealis/platforms/psv/psv_ime.hpp b/library/include/borealis/platforms/psv/psv_ime.hpp new file mode 100644 index 000000000..b7c94fe2f --- /dev/null +++ b/library/include/borealis/platforms/psv/psv_ime.hpp @@ -0,0 +1,39 @@ +/* +Copyright 2024 xfangfang + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include + +namespace brls +{ + class PsvImeManager : public ImeManager + { + public: + PsvImeManager(); + + bool openForText(std::function f, std::string headerText = "", + std::string subText = "", int maxStringLength = 32, std::string initialText = "", + int kbdDisableBitmask = KeyboardKeyDisableBitmask::KEYBOARD_DISABLE_NONE) override; + + bool openForNumber(std::function f, std::string headerText = "", + std::string subText = "", int maxStringLength = 18, std::string initialText = "", + std::string leftButton = "", std::string rightButton = "", + int kbdDisableBitmask = KeyboardKeyDisableBitmask::KEYBOARD_DISABLE_NONE) override; + + }; +} \ No newline at end of file diff --git a/library/include/borealis/platforms/psv/psv_input.hpp b/library/include/borealis/platforms/psv/psv_input.hpp new file mode 100644 index 000000000..baa087a09 --- /dev/null +++ b/library/include/borealis/platforms/psv/psv_input.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include + +#include + +namespace brls +{ + +// Input manager for GLFW gamepad and keyboard +class PsvInputManager : public InputManager +{ + public: + PsvInputManager(); + + ~PsvInputManager(); + + short getControllersConnectedCount() override; + + void updateUnifiedControllerState(ControllerState* state) override; + + void updateControllerState(ControllerState* state, int controller) override; + + bool getKeyboardKeyState(BrlsKeyboardScancode state) override; + + void updateTouchStates(std::vector* states) override; + + void updateMouseStates(RawMouseState* state) override; + + void sendRumble(unsigned short controller, unsigned short lowFreqMotor, unsigned short highFreqMotor) override; + + void runloopStart() override; + + void setPointerLock(bool lock) override; + + void updateMouseMotion(SDL_MouseMotionEvent event); + + void updateMouseWheel(SDL_MouseWheelEvent event); + + void updateControllerSensorsUpdate(SDL_ControllerSensorEvent event); + + void updateKeyboardState(SDL_KeyboardEvent event); + + private: + SceCtrlData pad{}; + Rect screen{}; + [[nodiscard]] Point convertVitaTouchXY(float x, float y) const; +}; + +}; diff --git a/library/include/borealis/platforms/psv/psv_platform.hpp b/library/include/borealis/platforms/psv/psv_platform.hpp index 930b12080..b2e3e83e8 100644 --- a/library/include/borealis/platforms/psv/psv_platform.hpp +++ b/library/include/borealis/platforms/psv/psv_platform.hpp @@ -16,29 +16,59 @@ limitations under the License. #pragma once +#ifdef BOREALIS_USE_GXM +#include +#include +#include +#include +#else #include +#endif namespace brls { - class PsvPlatform : public SDLPlatform - { - public: - PsvPlatform(); - ~PsvPlatform() override; - bool canShowBatteryLevel() override; - bool canShowWirelessLevel() override; - int getBatteryLevel() override; - bool isBatteryCharging() override; - bool hasWirelessConnection() override; - int getWirelessLevel() override; - bool hasEthernetConnection() override; - std::string getIpAddress() override; - std::string getDnsServer() override; - void openBrowser(std::string url) override; - void setBacklightBrightness(float brightness) override; - float getBacklightBrightness() override; - bool canSetBacklightBrightness() override; - }; +#ifdef BOREALIS_USE_GXM +class PsvPlatform : public DesktopPlatform +#else +class PsvPlatform : public SDLPlatform +#endif +{ + public: + PsvPlatform(); + ~PsvPlatform() override; + + void createWindow(std::string windowTitle, uint32_t windowWidth, uint32_t windowHeight, float windowXPos, float windowYPos) override; + + bool canShowBatteryLevel() override; + bool canShowWirelessLevel() override; + int getBatteryLevel() override; + bool isBatteryCharging() override; + bool hasWirelessConnection() override; + int getWirelessLevel() override; + bool hasEthernetConnection() override; + std::string getIpAddress() override; + std::string getDnsServer() override; + void openBrowser(std::string url) override; + void setBacklightBrightness(float brightness) override; + float getBacklightBrightness() override; + bool canSetBacklightBrightness() override; + bool isScreenDimmingDisabled() override; + void disableScreenDimming(bool disable, const std::string& reason, const std::string& app) override; + bool mainLoopIteration() override; + InputManager* getInputManager() override; + ImeManager* getImeManager() override; + VideoContext* getVideoContext() override; + AudioPlayer* getAudioPlayer() override; + + private: + bool suspendDisabled{}; +#ifdef BOREALIS_USE_GXM + PsvInputManager* inputManager = nullptr; + PsvImeManager* imeManager = nullptr; + PsvVideoContext* videoContext = nullptr; + NullAudioPlayer* audioPlayer = nullptr; +#endif +}; } // namespace brls diff --git a/library/include/borealis/platforms/psv/psv_video.hpp b/library/include/borealis/platforms/psv/psv_video.hpp new file mode 100644 index 000000000..704e3dffe --- /dev/null +++ b/library/include/borealis/platforms/psv/psv_video.hpp @@ -0,0 +1,45 @@ +/* +Copyright 2024 xfangfang + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include + +namespace brls +{ + +class PsvVideoContext : public VideoContext +{ + public: + PsvVideoContext(); + ~PsvVideoContext() override; + + void clear(NVGcolor color) override; + void resetState() override; + void beginFrame() override; + void endFrame() override; + double getScaleFactor() override; + NVGcontext* getNVGContext() override; + + NVGXMframebuffer *getFramebuffer(); + + private: + NVGcontext* nvgContext; + NVGXMframebuffer* gxm; +}; + +} // namespace brls diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index e2339ddcc..6bd169eb9 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -156,8 +156,6 @@ void Application::createWindow(std::string windowTitle) Application::registerBuiltInXMLViews(); Application::getWindowCreationDoneEvent()->fire(); - - Application::backgroundColor = Application::getTheme().getColor("brls/clear"); } bool Application::mainLoop() @@ -656,7 +654,7 @@ void Application::frame() // Begin frame and clear videoContext->beginFrame(); - videoContext->clear(backgroundColor); + videoContext->clear(Application::getTheme().getColor("brls/clear")); float scaleFactor = videoContext->getScaleFactor(); nvgBeginFrame(frameContext.vg, Application::windowWidth, Application::windowHeight, scaleFactor); diff --git a/library/lib/core/util.cpp b/library/lib/core/util.cpp index 74edf928d..a6cb31a9d 100644 --- a/library/lib/core/util.cpp +++ b/library/lib/core/util.cpp @@ -17,6 +17,10 @@ #include #include +#ifdef __PSV__ +#include +#endif + namespace brls { @@ -33,7 +37,11 @@ bool startsWith(const std::string& data, const std::string& prefix) [[noreturn]] void fatal(std::string message) { brls::Logger::error("Fatal error: {}", message); +#ifdef __PSV__ + sceKernelExitProcess(1); +#else throw std::logic_error(message); +#endif } std::string loadFileContents(const std::string &path) { diff --git a/library/lib/extern/nanovg/nanovg.c b/library/lib/extern/nanovg/nanovg.c index efe75c3ea..b5063b11f 100644 --- a/library/lib/extern/nanovg/nanovg.c +++ b/library/lib/extern/nanovg/nanovg.c @@ -38,7 +38,7 @@ #endif #ifndef NVG_INIT_FONTIMAGE_SIZE -# ifdef __PSV__ +# if defined(__PSV__) && defined(USE_GLES2) # define NVG_INIT_FONTIMAGE_SIZE 240 # else # define NVG_INIT_FONTIMAGE_SIZE 512 @@ -46,7 +46,7 @@ #endif #ifndef NVG_MAX_FONTIMAGE_SIZE -# ifdef __PSV__ +# if defined(__PSV__) && defined(USE_GLES2) # define NVG_MAX_FONTIMAGE_SIZE 960 # else # define NVG_MAX_FONTIMAGE_SIZE 4096 diff --git a/library/lib/platforms/psv/psv_ime.cpp b/library/lib/platforms/psv/psv_ime.cpp new file mode 100644 index 000000000..4429a9a19 --- /dev/null +++ b/library/lib/platforms/psv/psv_ime.cpp @@ -0,0 +1,246 @@ +/* +Copyright 2024 xfangfang + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace brls +{ + +static void utf16_to_utf8(const uint16_t* src, uint8_t* dst) +{ + int i; + for (i = 0; src[i] != 0; i++) + { + if (!(src[i] & 0xFF80)) + { + *(dst++) = src[i] & 0xFF; + } + else if (!(src[i] & 0xF800)) + { + *(dst++) = ((src[i] >> 6) & 0xFF) | 0xC0; + *(dst++) = (src[i] & 0x3F) | 0x80; + } + else if ((src[i] & 0xFC00) == 0xD800 && (src[i + 1] & 0xFC00) == 0xDC00) + { + *(dst++) = (((src[i] + 64) >> 8) & 0x3) | 0xF0; + *(dst++) = (((src[i] >> 2) + 16) & 0x3F) | 0x80; + *(dst++) = ((src[i] >> 4) & 0x30) | 0x80 | ((src[i + 1] << 2) & 0xF); + *(dst++) = (src[i + 1] & 0x3F) | 0x80; + i += 1; + } + else + { + *(dst++) = ((src[i] >> 12) & 0xF) | 0xE0; + *(dst++) = ((src[i] >> 6) & 0x3F) | 0x80; + *(dst++) = (src[i] & 0x3F) | 0x80; + } + } + + *dst = '\0'; +} + +static void utf8_to_utf16(const uint8_t* src, uint16_t* dst) +{ + int i; + for (i = 0; src[i];) + { + if ((src[i] & 0xE0) == 0xE0) + { + *(dst++) = ((src[i] & 0x0F) << 12) | ((src[i + 1] & 0x3F) << 6) | (src[i + 2] & 0x3F); + i += 3; + } + else if ((src[i] & 0xC0) == 0xC0) + { + *(dst++) = ((src[i] & 0x1F) << 6) | (src[i + 1] & 0x3F); + i += 2; + } + else + { + *(dst++) = src[i]; + i += 1; + } + } + + *dst = '\0'; +} + +class PsvImeView : public View +{ + public: + PsvImeView() + { + this->setFocusable(true); + this->setHideHighlight(true); + Application::blockInputs(); + } + + void setMaxStringLength(int length) + { + this->maxStringLength = length; + } + + void setInitialText(const std::string& initialText) + { + utf8_to_utf16(reinterpret_cast(initialText.c_str()), libime_initval); + } + + void setHeaderText(const std::string& headerText) + { + utf8_to_utf16(reinterpret_cast(headerText.c_str()), libime_title); + } + + void setCallback(std::function f) + { + callback = std::move(f); + } + + void setTextFieldType(SceImeType t) + { + type = t; + } + + void frame(FrameContext* ctx) override + { + this->checkImeEvent(); + } + + bool openImeDialog() + { + SceImeDialogParam param; + sceImeDialogParamInit(¶m); + + param.supportedLanguages = 0; + param.languagesForced = SCE_FALSE; + param.type = type; + param.option = 0; + param.textBoxMode = SCE_IME_DIALOG_TEXTBOX_MODE_WITH_CLEAR; + param.dialogMode = SCE_IME_DIALOG_DIALOG_MODE_WITH_CANCEL; + param.maxTextLength = maxStringLength; + param.title = libime_title; + param.initialText = libime_initval; + param.inputTextBuffer = ime_buffer; + + SceInt32 res = sceImeDialogInit(¶m); + if (res < 0) + { + brls::Logger::error("Failed to init IME dialog: {}", res); + return false; + } + running = true; + return true; + } + + void checkImeEvent() + { + if (!running) + return; + SceCommonDialogStatus dialogStatus = sceImeDialogGetStatus(); + if (dialogStatus != SCE_COMMON_DIALOG_STATUS_FINISHED) + return; + + uint8_t utf8_buffer[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; + SceImeDialogResult result; + memset(&result, 0, sizeof(SceImeDialogResult)); + sceImeDialogGetResult(&result); + if (result.button == SCE_IME_DIALOG_BUTTON_ENTER) + { + + utf16_to_utf8(ime_buffer, utf8_buffer); + std::string text { (const char*)utf8_buffer }; + if (callback) + callback(text); + } + + sceImeDialogTerm(); + Application::popActivity(TransitionAnimation::NONE); + Application::unblockInputs(); + running = false; + } + + void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) { } + + private: + SceImeType type; + SceWChar16 ime_buffer[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; + SceWChar16 libime_initval[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; + SceWChar16 libime_title[SCE_IME_DIALOG_MAX_TITLE_LENGTH]; + + bool running {}; + int maxStringLength { SCE_IME_DIALOG_MAX_TEXT_LENGTH }; + std::function callback {}; +}; + +PsvImeManager::PsvImeManager() + = default; + +bool PsvImeManager::openForText(std::function f, std::string headerText, + std::string subText, int maxStringLength, std::string initialText, + int kbdDisableBitmask) +{ + bool ret; + auto ime = new PsvImeView(); + ime->setTextFieldType(SCE_IME_TYPE_DEFAULT); + ime->setCallback(f); + ime->setHeaderText(headerText); + ime->setInitialText(initialText); + ime->setMaxStringLength(maxStringLength); + ret = ime->openImeDialog(); + Application::pushActivity(new Activity(ime)); + return ret; +} + +bool PsvImeManager::openForNumber(std::function f, std::string headerText, + std::string subText, int maxStringLength, std::string initialText, + std::string leftButton, std::string rightButton, + int kbdDisableBitmask) +{ + bool ret; + auto ime = new PsvImeView(); + ime->setTextFieldType(SCE_IME_TYPE_NUMBER); + ime->setCallback([f](const std::string& text) { + if(text.empty()) return ; + try + { + f(stoll(text)); + } + catch (const std::invalid_argument& e) + { + Logger::error("Could not parse input, did you enter a valid integer? {}", e.what()); + } + catch (const std::out_of_range& e) { + Logger::error("Out of range: {}", e.what()); + } + catch (const std::exception& e) + { + Logger::error("Unexpected error occurred: {}", e.what()); + } + }); + ime->setHeaderText(headerText); + ime->setInitialText(initialText); + ime->setMaxStringLength(maxStringLength); + ret = ime->openImeDialog(); + Application::pushActivity(new Activity(ime)); + return ret; +} + +}; \ No newline at end of file diff --git a/library/lib/platforms/psv/psv_input.cpp b/library/lib/platforms/psv/psv_input.cpp new file mode 100644 index 000000000..387970a81 --- /dev/null +++ b/library/lib/platforms/psv/psv_input.cpp @@ -0,0 +1,170 @@ +/* +Copyright 2024 xfangfang + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include + +#include +#include +#include + +namespace brls +{ + +PsvInputManager::PsvInputManager() +{ + sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG); + memset(&pad, 0, sizeof(pad)); + + SceTouchPanelInfo panelInfo; + sceTouchGetPanelInfo(SCE_TOUCH_PORT_FRONT, &panelInfo); + + screen.origin.x = panelInfo.minAaX; + screen.origin.y = panelInfo.minAaY; + screen.size.width = panelInfo.maxAaX - panelInfo.minAaX; + screen.size.height = panelInfo.maxAaY - panelInfo.minAaY; + + sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); +} + +PsvInputManager::~PsvInputManager() = default; + +Point PsvInputManager::convertVitaTouchXY(float vita_x, float vita_y) const +{ + float x = (vita_x - screen.origin.x) / screen.size.width; + float y = (vita_y - screen.origin.y) / screen.size.height; + + x = SDL_max(x, 0.0); + x = SDL_min(x, 1.0); + + y = SDL_max(y, 0.0); + y = SDL_min(y, 1.0); + + return { Application::contentWidth * x, Application::contentHeight * y }; +} + +short PsvInputManager::getControllersConnectedCount() +{ + return 1; +} + +void PsvInputManager::updateUnifiedControllerState(ControllerState* state) +{ + this->updateControllerState(state, 0); +} + +void PsvInputManager::updateControllerState(ControllerState* state, int controller) +{ + for (bool& button : state->buttons) + button = false; + + for (float& axe : state->axes) + axe = 0; + + sceCtrlPeekBufferPositive(0, &pad, 1); + + state->buttons[BUTTON_UP] = pad.buttons & SCE_CTRL_UP; + state->buttons[BUTTON_RIGHT] = pad.buttons & SCE_CTRL_RIGHT; + state->buttons[BUTTON_DOWN] = pad.buttons & SCE_CTRL_DOWN; + state->buttons[BUTTON_LEFT] = pad.buttons & SCE_CTRL_LEFT; + + state->buttons[BUTTON_START] = pad.buttons & SCE_CTRL_START; + state->buttons[BUTTON_BACK] = pad.buttons & SCE_CTRL_SELECT; + + state->buttons[BUTTON_LB] = pad.buttons & SCE_CTRL_L1; + state->buttons[BUTTON_RB] = pad.buttons & SCE_CTRL_R1; + + if (Application::isSwapInputKeys()) + { + state->buttons[BUTTON_A] = pad.buttons & SCE_CTRL_CROSS; + state->buttons[BUTTON_B] = pad.buttons & SCE_CTRL_CIRCLE; + state->buttons[BUTTON_X] = pad.buttons & SCE_CTRL_SQUARE; + state->buttons[BUTTON_Y] = pad.buttons & SCE_CTRL_TRIANGLE; + } + else + { + state->buttons[BUTTON_A] = pad.buttons & SCE_CTRL_CIRCLE; + state->buttons[BUTTON_B] = pad.buttons & SCE_CTRL_CROSS; + state->buttons[BUTTON_X] = pad.buttons & SCE_CTRL_TRIANGLE; + state->buttons[BUTTON_Y] = pad.buttons & SCE_CTRL_SQUARE; + } + + state->axes[LEFT_X] = pad.lx / 255.0f - 1.0f; + state->axes[LEFT_Y] = pad.ly / 255.0f - 1.0f; + state->axes[RIGHT_X] = pad.rx / 255.0f - 1.0f; + state->axes[RIGHT_Y] = pad.ry / 255.0f - 1.0f; + + state->buttons[BUTTON_NAV_UP] = pad.ly < 0x40 || pad.ry < 0x40 || state->buttons[BUTTON_UP]; + state->buttons[BUTTON_NAV_RIGHT] = pad.lx > 0xc0 || pad.rx > 0xc0 || state->buttons[BUTTON_RIGHT]; + state->buttons[BUTTON_NAV_DOWN] = pad.ly > 0xc0 || pad.ry > 0xc0 || state->buttons[BUTTON_DOWN]; + state->buttons[BUTTON_NAV_LEFT] = pad.lx < 0x40 || pad.rx < 0x40 || state->buttons[BUTTON_LEFT]; +} + +bool PsvInputManager::getKeyboardKeyState(BrlsKeyboardScancode key) +{ + return false; +} + +void PsvInputManager::updateTouchStates(std::vector* states) +{ + SceTouchData touch; + sceTouchPeek(SCE_TOUCH_PORT_FRONT, &touch, 1); + + for (int i = 0; i < touch.reportNum; i++) + { + Point p = convertVitaTouchXY(touch.report[i].x, touch.report[i].y); + RawTouchState state; + state.pressed = true; + state.fingerId = touch.report[i].id; + state.position.x = p.x; + state.position.y = p.y; + states->push_back(state); + } +} + +void PsvInputManager::updateMouseStates(RawMouseState* state) +{ +} + +void PsvInputManager::setPointerLock(bool lock) +{ +} + +void PsvInputManager::runloopStart() +{ +} + +void PsvInputManager::sendRumble(unsigned short controller, unsigned short lowFreqMotor, unsigned short highFreqMotor) +{ +} + +void PsvInputManager::updateMouseMotion(SDL_MouseMotionEvent event) +{ +} + +void PsvInputManager::updateMouseWheel(SDL_MouseWheelEvent event) +{ +} + +void PsvInputManager::updateControllerSensorsUpdate(SDL_ControllerSensorEvent event) +{ +} + +void PsvInputManager::updateKeyboardState(SDL_KeyboardEvent event) +{ +} + +}; diff --git a/library/lib/platforms/psv/psv_platform.cpp b/library/lib/platforms/psv/psv_platform.cpp index a9da06351..e1d882db5 100644 --- a/library/lib/platforms/psv/psv_platform.cpp +++ b/library/lib/platforms/psv/psv_platform.cpp @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include #include #include #include @@ -38,8 +37,6 @@ extern "C" namespace brls { -static bool DISABLE_SUSPEND = false; - static int powerCallback(int notifyId, int notifyCount, int powerInfo, void* common) { if ((powerInfo & SCE_POWER_CB_APP_RESUME) || (powerInfo & SCE_POWER_CB_APP_RESUMING)) @@ -73,23 +70,108 @@ PsvPlatform::PsvPlatform() if (thid >= 0) sceKernelStartThread(thid, 0, NULL); - // Auto swap X and O buttons - int enterButton; SceAppUtilInitParam initParam; SceAppUtilBootParam bootParam; memset(&initParam, 0, sizeof(SceAppUtilInitParam)); memset(&bootParam, 0, sizeof(SceAppUtilBootParam)); sceAppUtilInit(&initParam, &bootParam); + + // Auto swap X and O buttons + int enterButton; sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_ENTER_BUTTON, &enterButton); - sceAppUtilShutdown(); if (enterButton == SCE_SYSTEM_PARAM_ENTER_BUTTON_CIRCLE) brls::Application::setSwapInputKeys(true); + // Override locale + if (Platform::APP_LOCALE_DEFAULT == LOCALE_AUTO) + { + int systemLanguage; + sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, &systemLanguage); + + std::unordered_map psv2brls = { + { SCE_SYSTEM_PARAM_LANG_CHINESE_S, LOCALE_ZH_HANS }, + { SCE_SYSTEM_PARAM_LANG_CHINESE_T, LOCALE_ZH_HANT }, + { SCE_SYSTEM_PARAM_LANG_JAPANESE, LOCALE_JA }, + { SCE_SYSTEM_PARAM_LANG_KOREAN, LOCALE_Ko }, + { SCE_SYSTEM_PARAM_LANG_ITALIAN, LOCALE_IT } + }; + + if (psv2brls.count(systemLanguage) > 0) { + this->locale = psv2brls[systemLanguage]; + } else { + this->locale = LOCALE_EN_US; + } + brls::Logger::info("Set app locale: {}", this->locale); + } + // trigger internal network init in newlib gethostname(nullptr, 0); } -PsvPlatform::~PsvPlatform() = default; +PsvPlatform::~PsvPlatform() +{ + delete this->audioPlayer; + delete this->inputManager; + delete this->imeManager; + delete this->videoContext; +} + +void PsvPlatform::createWindow(std::string windowTitle, uint32_t windowWidth, uint32_t windowHeight, float windowXPos, float windowYPos) +{ +#ifdef BOREALIS_USE_GXM + this->videoContext = new PsvVideoContext(); + this->inputManager = new PsvInputManager(); + this->imeManager = new PsvImeManager(); +#else + this->videoContext = new SDLVideoContext(windowTitle, windowWidth, windowHeight, windowXPos, windowYPos); + this->inputManager = new SDLInputManager(this->videoContext->getSDLWindow()); + this->imeManager = new SDLImeManager(&this->otherEvent); +#endif + this->audioPlayer = new NullAudioPlayer(); +} + +bool PsvPlatform::mainLoopIteration() +{ + if (this->suspendDisabled) { + sceKernelPowerTick(SCE_KERNEL_POWER_TICK_DEFAULT); + } +#ifdef __SDL2__ + return SDLPlatform::mainLoopIteration(); +#else + return true; +#endif +} + +InputManager* PsvPlatform::getInputManager() +{ + return this->inputManager; +} + +ImeManager* PsvPlatform::getImeManager() +{ + return this->imeManager; +} + +VideoContext* PsvPlatform::getVideoContext() +{ + return this->videoContext; +} + +AudioPlayer* PsvPlatform::getAudioPlayer() +{ + return this->audioPlayer; +} + +bool PsvPlatform::isScreenDimmingDisabled() +{ + return suspendDisabled; +} +void PsvPlatform::disableScreenDimming(bool disable, const std::string& reason, const std::string& app) +{ + (void) reason; + (void) app; + suspendDisabled = disable; +} bool PsvPlatform::canShowBatteryLevel() { @@ -163,6 +245,20 @@ std::string PsvPlatform::getDnsServer() void PsvPlatform::openBrowser(std::string url) { + if (url.empty()) + return; + SceAppUtilWebBrowserParam browser_param; + browser_param.str = url.c_str(); + browser_param.strlen = url.length(); + browser_param.launchMode = 1; + if (sceAppUtilLaunchWebBrowser(&browser_param) == SCE_OK) { + return; + } + brls::Logger::error("Failed to open browser in MODAL mode, trying NORMAL mode"); + browser_param.launchMode = 0; + if (sceAppUtilLaunchWebBrowser(&browser_param) != SCE_OK) { + brls::Logger::error("Failed to open browser in NORMAL mode"); + } } void PsvPlatform::setBacklightBrightness(float brightness) diff --git a/library/lib/platforms/psv/psv_video.cpp b/library/lib/platforms/psv/psv_video.cpp new file mode 100644 index 000000000..0eefa7844 --- /dev/null +++ b/library/lib/platforms/psv/psv_video.cpp @@ -0,0 +1,114 @@ +/* +Copyright 2024 xfangfang + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include + +#define NANOVG_GXM_IMPLEMENTATION +#define NANOVG_GXM_UTILS_IMPLEMENTATION +#include + +namespace brls +{ + +PsvVideoContext::PsvVideoContext() +{ +#ifdef USE_VITA_SHARK + if (shark_init("app0:module/libshacccg.suprx") < 0) { + sceClibPrintf("vitashark: failed to initialize"); + return EXIT_FAILURE; + } +#endif + + // Initialize nanovg + NVGXMinitOptions initOptions = { + .msaa = SCE_GXM_MULTISAMPLE_4X, + .swapInterval = 1, + .dumpShader = 0, + }; + + gxm = nvgxmCreateFramebuffer(&initOptions); + if (gxm == NULL) { + fatal("gxm: failed to initialize"); + } + + this->nvgContext = nvgCreateGXM(gxm, 0); + if (!this->nvgContext) + { + brls::fatal("sdl: unable to init nanovg"); + } + + Application::setWindowSize(DISPLAY_WIDTH, DISPLAY_HEIGHT); + + + // TODO: 删除 + brls::Logger::info("PSVita Video Context initialized"); +} + +PsvVideoContext::~PsvVideoContext() +{ + try + { + if (this->nvgContext) + { + nvgDeleteGXM(this->nvgContext); + nvgxmDeleteFramebuffer(gxm); + } + } + catch (...) + { + Logger::error("Cannot delete nvg Context"); + } +#ifdef USE_VITA_SHARK + shark_end(); +#endif +} + +void PsvVideoContext::clear(NVGcolor color) { + static NVGcolor clearColor{}; + if (clearColor.r != color.r || clearColor.g != color.g || clearColor.b != color.b || clearColor.a != color.a) { + clearColor = color; + gxmClearColor(color.r, color.g, color.b, color.a); + } +} + +void PsvVideoContext::resetState() { } + +void PsvVideoContext::beginFrame() { + gxmBeginFrame(); + gxmClear(); +} + +void PsvVideoContext::endFrame() { + gxmEndFrame(); + gxmDialogUpdate(); + gxmSwapBuffer(); +} + +double PsvVideoContext::getScaleFactor() { + return 1.0; +} + +NVGcontext* PsvVideoContext::getNVGContext() { + return this->nvgContext; +} + +NVGXMframebuffer* PsvVideoContext::getFramebuffer() +{ + return this->gxm; +} + +} \ No newline at end of file