From dc09f59be5682577985a0bac9cdb1d9cbeab6697 Mon Sep 17 00:00:00 2001 From: xfangfang <2553041586@qq.com> Date: Sat, 4 Jan 2025 23:51:23 +0800 Subject: [PATCH] Support GXM rendering api --- .github/workflows/build.yaml | 136 ++++--------------------- CMakeLists.txt | 26 ++--- library/borealis | 2 +- scripts/psv/ffmpeg/VITABUILD | 16 ++- scripts/psv/ffmpeg/optimize.patch | 31 ++++++ scripts/psv/gxm.Dockerfile | 39 +++++++ scripts/psv/mpv_gxm/VITABUILD | 36 +++++++ wiliwili/include/view/mpv_core.hpp | 25 ++++- wiliwili/source/utils/image_helper.cpp | 79 +++++++++++++- wiliwili/source/view/mpv_core.cpp | 72 +++++++++++-- 10 files changed, 317 insertions(+), 145 deletions(-) create mode 100644 scripts/psv/ffmpeg/optimize.patch create mode 100644 scripts/psv/gxm.Dockerfile create mode 100644 scripts/psv/mpv_gxm/VITABUILD diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1295fe1df..cb7207e58 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -298,7 +298,7 @@ jobs: - name: Upload dist uses: actions/upload-artifact@v4 with: - name: ${{ needs.version.outputs.DIST_NRO }}${{ matrix.shuffix }} + name: ${{ needs.version.outputs.DIST_NRO }}-${{ matrix.driver }} path: "cmake-build-switch/wiliwili.nro" build-ps4: @@ -496,143 +496,47 @@ jobs: needs: [ version ] name: build-psv runs-on: ubuntu-latest - container: - image: vitasdk/vitasdk:latest + strategy: + fail-fast: false + matrix: + include: + - { driver: gxm } + - { driver: gles2 } steps: - - name: Install build requirements - run: | - apk update - apk add cmake ninja meson pkgconf bash git zstd tar - git config --global --add safe.directory $(pwd) - - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/cache/restore@v4 - id: restore-pvr-cache - with: - path: /vita/dependencies - key: SDL-vita-pvr-3.9 - - - name: Download PVR_PSP2 (GLES) - if: ${{ !steps.restore-pvr-cache.outputs.cache-hit }} - run: | - pvr_psp2_version=3.9 - - mkdir -p /vita/dependencies/include - mkdir -p /vita/dependencies/lib - mkdir -p /vita/dependencies/suprx - - # Configure PVR_PSP2 headers - wget https://github.com/GrapheneCt/PVR_PSP2/archive/refs/tags/v$pvr_psp2_version.zip -P/tmp - unzip /tmp/v$pvr_psp2_version.zip -d/tmp - cp -r /tmp/PVR_PSP2-$pvr_psp2_version/include/* /vita/dependencies/include - rm /tmp/v$pvr_psp2_version.zip - - # include guard of PVR_PSP2's khrplatform.h does not match the usual one - sed -i -e s/__drvkhrplatform_h_/__khrplatform_h_/ /vita/dependencies/include/KHR/khrplatform.h - - # Configure PVR_PSP2 stub libraries - wget https://github.com/GrapheneCt/PVR_PSP2/releases/download/v$pvr_psp2_version/vitasdk_stubs.zip -P/tmp - unzip /tmp/vitasdk_stubs.zip -d/tmp/pvr_psp2_stubs - find /tmp/pvr_psp2_stubs -type f -name "*.a" -exec cp {} /vita/dependencies/lib \; - rm /tmp/vitasdk_stubs.zip - rm -rf /tmp/pvr_psp2_stubs - - # Configure PVR_PSP2 *.suprx - wget https://github.com/GrapheneCt/PVR_PSP2/releases/download/v$pvr_psp2_version/PSVita_Release.zip -P/tmp - unzip /tmp/PSVita_Release.zip -d/tmp/PSVita_Release - rm /tmp/PSVita_Release/libGLESv1_CM.suprx - rm /tmp/PSVita_Release/libpvr2d.suprx - mv /tmp/PSVita_Release/*.suprx /vita/dependencies/suprx/ - rm -rf /tmp/PSVita_Release.zip - rm -rf /tmp/PSVita_Release - - - uses: actions/cache/save@v4 - if: ${{ !steps.restore-pvr-cache.outputs.cache-hit }} - with: - path: /vita/dependencies - key: SDL-vita-pvr-3.9 - - - name: Copy PVR_PSP2 (GLES) to vita toolchain dir + - name: Build GLES2 + if: ${{ matrix.driver == 'gles2' }} run: | - cp -rv /vita/dependencies/* ${VITASDK}/arm-vita-eabi - mv /vita/dependencies/suprx/*.suprx scripts/psv/module/ - ls -lah ${VITASDK}/arm-vita-eabi - ls -lah scripts/psv/module/ - - - uses: actions/cache/restore@v4 - id: restore-common-deps-cache - with: - path: /vita/common_deps - key: Dep-vita-${{ hashFiles('**/VITABUILD') }} + docker run --rm -v $(pwd):/src/ xfangfang/wiliwili_psv_builder:latest \ + "cmake -B build -G Ninja -DPLATFORM_PSV=ON -DUSE_SYSTEM_CURL=ON -DUSE_SYSTEM_SDL2=ON -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build" - - name: Install VDPM Dependencies - run: vdpm mbedtls libass harfbuzz fribidi freetype libpng libwebp - - - name: Build Dependencies - if: ${{ !steps.restore-common-deps-cache.outputs.cache-hit }} + - name: Build GXM + if: ${{ matrix.driver == 'gxm' }} run: | - mkdir -p /vita/common_deps - apk add patch - adduser --gecos '' --disabled-password builder - echo 'builder ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/builder - chown -R builder:builder $(pwd)/scripts/psv - su - builder -c "cd $(pwd)/scripts/psv/ffmpeg && vita-makepkg" - su - builder -c "cd $(pwd)/scripts/psv/curl && vita-makepkg" - su - builder -c "cd $(pwd)/scripts/psv/sdl2 && vita-makepkg" - vdpm $(pwd)/scripts/psv/ffmpeg/*-arm.tar.xz - vdpm $(pwd)/scripts/psv/sdl2/*-arm.tar.xz - touch /tmp/vdpm_install_ffmpeg - touch /tmp/vdpm_install_sdl2 - su - builder -c "cd $(pwd)/scripts/psv/mpv && vita-makepkg" - mv $(pwd)/scripts/psv/curl/*-arm.tar.xz /vita/common_deps/ - mv $(pwd)/scripts/psv/sdl2/*-arm.tar.xz /vita/common_deps/ - mv $(pwd)/scripts/psv/ffmpeg/*-arm.tar.xz /vita/common_deps/ - mv $(pwd)/scripts/psv/mpv/*-arm.tar.xz /vita/common_deps/ - ls -lah /vita/common_deps/ - - - uses: actions/cache/save@v4 - if: ${{ !steps.restore-common-deps-cache.outputs.cache-hit }} - with: - path: /vita/common_deps - key: Dep-vita-${{ hashFiles('**/VITABUILD') }} - - - name: Install Dependencies - run: vdpm /vita/common_deps/*-arm.tar.xz - - - name: Bulid wiliwili - run: | - cmake -S . -B build -G Ninja \ - -DPLATFORM_PSV=ON \ - -DUSE_SYSTEM_CURL=ON \ - -DUSE_SYSTEM_SDL2=ON \ - -DMPV_NO_FB=ON \ - -DSIMPLE_HIGHLIGHT=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DBRLS_UNITY_BUILD=${{ github.event.inputs.disable_unity_build == 'true' && 'OFF' || 'ON' }} \ - -DCMAKE_UNITY_BUILD_BATCH_SIZE=16 \ - -DCMAKE_CXX_FLAGS="-Wno-error=pedantic -Wno-psabi" - cmake --build build --verbose - mv build/wiliwili.self build/eboot.bin + docker run --rm -v $(pwd):/src/ xfangfang/wiliwili_psv_builder:latest-gxm \ + "cmake -B build -G Ninja -DPLATFORM_PSV=ON -DUSE_SYSTEM_CURL=ON -DUSE_GXM=ON -DUSE_VITA_SHARK=ON -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build" - name: Upload vpk uses: actions/upload-artifact@v4 with: - name: wiliwili-PSVita-${{ needs.version.outputs.VERSION }} + name: wiliwili-PSVita-${{ needs.version.outputs.VERSION }}-${{ matrix.driver }} path: build/wiliwili.vpk - name: Upload eboot uses: actions/upload-artifact@v4 with: - name: psv_dev_eboot.bin + name: psv_dev_eboot_${{ matrix.driver }}.bin path: build/eboot.bin - name: Upload elf uses: actions/upload-artifact@v4 with: - name: psv_dev_elf + name: psv_dev_elf_${{ matrix.driver }} path: build/wiliwili diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b1e8e81..3a6e49217 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,25 +253,25 @@ elseif (PLATFORM_IOS) "wiliwili" "${APP_VERSION}") elseif (PLATFORM_PSV) + set(PSV_ASSETS_FILES ${CMAKE_SOURCE_DIR}/scripts/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}/scripts/psv/module/ module) + endif () + if (USE_VITA_SHARK) + list(APPEND PSV_ASSETS_FILES "${CMAKE_BINARY_DIR}/vendor/SceShaccCg" module) + endif () set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d ATTRIBUTE2=12") # max heap size mode - # I don't know why, but only manually specifying the link libraries in this order will not cause the program to crash - set(APP_PLATFORM_LIB - stdc++;mpv; - avfilter;swscale;avformat;avcodec;swresample;avutil; - ass;harfbuzz;fribidi;freetype;bz2;png16; - webp;sharpyuv; - m;z; - SceCodecEngine_stub - SceVideodec_stub - SceAudiodec_stub) vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME} UNSAFE NOASLR) vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self VERSION ${VITA_VERSION} NAME ${PROJECT_NAME} - FILE ${CMAKE_SOURCE_DIR}/resources resources - FILE ${CMAKE_SOURCE_DIR}/scripts/psv/sce_sys sce_sys - FILE ${CMAKE_SOURCE_DIR}/scripts/psv/module/ module + FILE ${PSV_ASSETS_FILES} ) + # TODO: Remove after mpv related code is done + list(APPEND APP_PLATFORM_LINK_OPTION "-Wl,--allow-multiple-definition") elseif (PLATFORM_PS4) add_self(${PROJECT_NAME}) add_pkg(${PROJECT_NAME} diff --git a/library/borealis b/library/borealis index 53ee60dd6..917655f76 160000 --- a/library/borealis +++ b/library/borealis @@ -1 +1 @@ -Subproject commit 53ee60dd655a71353fad07fb505258c39222b264 +Subproject commit 917655f7612f7fcd208508c6c727dfa247893706 diff --git a/scripts/psv/ffmpeg/VITABUILD b/scripts/psv/ffmpeg/VITABUILD index 8c28d2c9c..1b9f7e707 100644 --- a/scripts/psv/ffmpeg/VITABUILD +++ b/scripts/psv/ffmpeg/VITABUILD @@ -1,13 +1,14 @@ pkgname=ffmpeg -pkgver=7900d3684acc8e5e98fd39cbb12cb7ab73e6d4a5 -pkgrel=2 +pkgver=3736b0c78da9553b2098dbb0ade0a2e0ddd1753f +pkgrel=3 url="https://ffmpeg.org/" -source=("${pkgname}-${pkgver}.tar.gz::https://github.com/fish47/FFmpeg-vita/archive/${pkgver}.tar.gz") -sha256sums=('SKIP') +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/fish47/FFmpeg-vita/archive/${pkgver}.tar.gz" "optimize.patch") +sha256sums=('SKIP' 'SKIP') depends=('mbedtls') prepare() { cd FFmpeg-vita-$pkgver + patch --strip=1 --input=${srcdir}/optimize.patch } build() { @@ -15,6 +16,13 @@ build() { ./configure --prefix=$VITASDK/arm-vita-eabi \ --enable-vita \ --target-os=vita \ + --enable-cross-compile \ + --disable-runtime-cpudetect \ + --disable-armv5te \ + --cross-prefix=$VITASDK/bin/arm-vita-eabi- \ + --extra-cflags=" -Wl,-q -O2 -ftree-vectorize -fomit-frame-pointer -ffast-math -D_BSD_SOURCE" \ + --extra-cxxflags=" -Wl,-q -O2 -ftree-vectorize -fomit-frame-pointer -ffast-math -fno-rtti -fno-exceptions -std=gnu++11 -D_BSD_SOURCE" \ + --extra-ldflags=" -L$VITASDK/lib " \ --disable-shared \ --enable-static \ --disable-programs \ diff --git a/scripts/psv/ffmpeg/optimize.patch b/scripts/psv/ffmpeg/optimize.patch new file mode 100644 index 000000000..eabb34832 --- /dev/null +++ b/scripts/psv/ffmpeg/optimize.patch @@ -0,0 +1,31 @@ +diff --git a/libavcodec/vitadec_video.c b/libavcodec/vitadec_video.c +index 8e0b6348c5..6a391be944 100644 +--- a/libavcodec/vitadec_video.c ++++ b/libavcodec/vitadec_video.c +@@ -270,7 +270,7 @@ fail: + return false; + } + +-static void do_mem_free(VitaDecodeBufferFreeParams *p) ++static void __attribute__((optimize("no-optimize-sibling-calls"))) do_mem_free(VitaDecodeBufferFreeParams *p) + { + sceKernelFreeMemBlock(*p->mb); + } +@@ -285,7 +285,7 @@ static bool do_unmap_open(VitaDecodeBufferAllocParams *p) + return true; + } + +-static void do_unmap_close(VitaDecodeBufferFreeParams *p) ++static void __attribute__((optimize("no-optimize-sibling-calls"))) do_unmap_close(VitaDecodeBufferFreeParams *p) + { + sceCodecEngineCloseUnmapMemBlock(*p->mb); + } +@@ -302,7 +302,7 @@ static bool do_vaddr_alloc(VitaDecodeBufferAllocParams *p) + return true; + } + +-static void do_vaddr_free(VitaDecodeBufferFreeParams *p) ++static void __attribute__((optimize("no-optimize-sibling-calls"))) do_vaddr_free(VitaDecodeBufferFreeParams *p) + { + SceUID *unmap = p->ref; + SceUIntVAddr *vaddr = p->ptr; diff --git a/scripts/psv/gxm.Dockerfile b/scripts/psv/gxm.Dockerfile new file mode 100644 index 000000000..432bb6166 --- /dev/null +++ b/scripts/psv/gxm.Dockerfile @@ -0,0 +1,39 @@ +FROM vitasdk/vitasdk:latest + +MAINTAINER xfangfang + +RUN apk update && \ + apk add cmake ninja meson pkgconf bash git zstd tar patch && \ + git config --global --add safe.directory $(pwd) + +# Install VDPM Dependencies +ADD . /vdpm +RUN vdpm sdl2 mbedtls libass harfbuzz fribidi freetype libpng libwebp && \ + adduser --gecos '' --disabled-password builder && \ + echo 'builder ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/builder && \ + chown -R builder:builder /vdpm && \ + ls -l /vdpm && \ + su - builder -c "cd /vdpm/ffmpeg && vita-makepkg" && \ + vdpm /vdpm/ffmpeg/*-arm.tar.xz && \ + touch /tmp/vdpm_install_ffmpeg && \ + su - builder -c "cd /vdpm/mpv_gxm && vita-makepkg" && \ + vdpm /vdpm/mpv_gxm/*-arm.tar.xz && \ + touch /tmp/vdpm_install_mpv && \ + su - builder -c "cd /vdpm/curl && vita-makepkg" && \ + vdpm /vdpm/curl/*-arm.tar.xz && \ + touch /tmp/vdpm_install_curl && \ + rm -rf /vdpm + +RUN mkdir /src/ &&\ + echo \#\!/bin/bash -i >> /entrypoint.sh &&\ + echo >> /entrypoint.sh &&\ + echo "set -e" >> /entrypoint.sh &&\ + echo "cd /src" >> /entrypoint.sh &&\ + echo "echo \"\$@\"" >> /entrypoint.sh &&\ + echo "bash -c \"\$@\"" >> /entrypoint.sh &&\ + chmod +x /entrypoint.sh + +VOLUME /src/ +WORKDIR /src/ +SHELL ["/bin/bash", "-i", "-c"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/scripts/psv/mpv_gxm/VITABUILD b/scripts/psv/mpv_gxm/VITABUILD new file mode 100644 index 000000000..18239cde9 --- /dev/null +++ b/scripts/psv/mpv_gxm/VITABUILD @@ -0,0 +1,36 @@ +pkgname=mpv +pkgver=e6f1d2fe199d3442150cef71de2aca3f7a554047 +pkgrel=3 +url="https://mpv.io/" +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/xfangfang/mpv/archive/${pkgver}.tar.gz") +sha256sums=('SKIP') +depends=('sdl2' 'libass' 'ffmpeg') + +prepare() { + cd $pkgname-$pkgver +} + +build() { + cd $pkgname-$pkgver + meson setup build --prefix=$prefix --cross-file crossfile.txt \ + --default-library static \ + -Diconv=disabled \ + -Dlua=disabled \ + -Djpeg=disabled \ + -Dopensles=disabled \ + -Dlibavdevice=disabled \ + -Dmanpage-build=disabled \ + -Dhtml-build=disabled \ + -Dsdl2=enabled \ + -Dlibmpv=true \ + -Dgxm=enabled \ + -Dvitashark=enabled \ + -Dcplayer=false + + meson compile -C build +} + +package () { + cd $pkgname-$pkgver + DESTDIR=$pkgdir meson install -C build +} \ No newline at end of file diff --git a/wiliwili/include/view/mpv_core.hpp b/wiliwili/include/view/mpv_core.hpp index 569cfa78c..4e9fbfd58 100644 --- a/wiliwili/include/view/mpv_core.hpp +++ b/wiliwili/include/view/mpv_core.hpp @@ -19,6 +19,8 @@ #include #elif defined(BOREALIS_USE_D3D11) #include +#elif defined(BOREALIS_USE_GXM) +#include #elif defined(BOREALIS_USE_OPENGL) #include #if defined(__PSV__) || defined(PS4) @@ -313,7 +315,7 @@ class MPVCore : public brls::Singleton { _command_async(commands); } - void _command_async(const std::vector& commands); + void _command_async(const std::vector &commands); // core states int64_t duration = 0; // second @@ -332,9 +334,9 @@ class MPVCore : public brls::Singleton { int mpv_error_code = 0; std::string hwCurrent; std::string filepath; - std::string currentShaderProfile; // 当前着色器脚本名 - std::string currentShader; // 当前着色器脚本 - std::vector> currentSetting; // 当前着色器脚本附加的mpv配置 + std::string currentShaderProfile; // 当前着色器脚本名 + std::string currentShader; // 当前着色器脚本 + std::vector> currentSetting; // 当前着色器脚本附加的mpv配置 double video_brightness = 0; double video_contrast = 0; @@ -420,6 +422,21 @@ class MPVCore : public brls::Singleton { mpv_render_param mpv_params[1] = { {MPV_RENDER_PARAM_INVALID, nullptr}, }; +#elif defined(BOREALIS_USE_GXM) + int nvg_image = 0; + bool redraw = false; + mpv_gxm_fbo mpv_fbo = { + .tex = nullptr, + .w = DISPLAY_WIDTH, + .h = DISPLAY_HEIGHT, + .format = SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_RGBA, + }; + int flip_y{1}; + mpv_render_param mpv_params[3] = { + {MPV_RENDER_PARAM_FLIP_Y, &flip_y}, + {MPV_RENDER_PARAM_GXM_FBO, &mpv_fbo}, + {MPV_RENDER_PARAM_INVALID, nullptr}, + }; #elif defined(MPV_NO_FB) GLint default_framebuffer = 0; mpv_opengl_fbo mpv_fbo{0, 1920, 1080}; diff --git a/wiliwili/source/utils/image_helper.cpp b/wiliwili/source/utils/image_helper.cpp index c97e9130c..48ab82b36 100644 --- a/wiliwili/source/utils/image_helper.cpp +++ b/wiliwili/source/utils/image_helper.cpp @@ -15,6 +15,81 @@ #include #endif +#ifdef __PSV__ +#define STB_DXT_IMPLEMENTATION +#include +#include + +static inline __attribute__((always_inline)) uint32_t nearest_po2(uint32_t val) { + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +static inline __attribute__((always_inline)) uint64_t morton_1(uint64_t x) { + x = x & 0x5555555555555555; + x = (x | (x >> 1)) & 0x3333333333333333; + x = (x | (x >> 2)) & 0x0F0F0F0F0F0F0F0F; + x = (x | (x >> 4)) & 0x00FF00FF00FF00FF; + x = (x | (x >> 8)) & 0x0000FFFF0000FFFF; + x = (x | (x >> 16)) & 0xFFFFFFFFFFFFFFFF; + return x; +} + +static inline __attribute__((always_inline)) void d2xy_morton(uint64_t d, uint64_t *x, uint64_t *y) { + *x = morton_1(d); + *y = morton_1(d >> 1); +} + +static inline __attribute__((always_inline)) void extract_block(const uint8_t *src, uint32_t width, uint8_t *block) { + for (int j = 0; j < 4; j++) { + memcpy(&block[j * 4 * 4], src, 16); + src += width * 4; + } +} + +/** + * Compress RGBA data to DXT1 or DXT5 + * @param dst DXT data + * @param src RGBA data + * @param w source width + * @param h source height + * @param stride source stride + * @param isdxt5 0 for DXT1, others for DXT5 + */ +static void _dxt_compress(uint8_t *dst, uint8_t *src, uint32_t w, uint32_t h, uint32_t stride, int isdxt5) { + uint8_t block[64]; + uint32_t align_w = nearest_po2(w); + uint32_t align_h = nearest_po2(h); + uint32_t s = align_w > align_h ? align_h : align_w; + uint32_t num_blocks = s * s / 16; + const uint32_t block_size = isdxt5 ? 16 : 8; + uint64_t d, offs_x, offs_y; + + for (d = 0; d < num_blocks; d++, dst += block_size) { + d2xy_morton(d, &offs_x, &offs_y); + if (offs_x * 4 >= h || offs_y * 4 >= w) + continue; + extract_block(src + offs_y * 16 + offs_x * stride * 16, stride, block); + stb_compress_dxt_block(dst, block, isdxt5, STB_DXT_NORMAL); + } + if (align_w > align_h) + return _dxt_compress(dst, src + s * 4, w - s, h, stride, isdxt5); + else if (align_w < align_h) + return _dxt_compress(dst, src + w * s * 4, w, h - s, stride, isdxt5); +} + +void dxt_compress(uint8_t *dst, uint8_t *src, uint32_t w, uint32_t h, int isdxt5) { + _dxt_compress(dst, src, w, h, w, isdxt5); +} +#endif + class ImageThreadPool : public cpr::ThreadPool, public brls::Singleton { public: ImageThreadPool() : cpr::ThreadPool(1, ImageHelper::REQUEST_THREADS, std::chrono::milliseconds(5000)) { @@ -135,7 +210,9 @@ void ImageHelper::requestImage() { } else { NVGcontext* vg = brls::Application::getNVGContext(); if (imageData) { - tex = nvgCreateImageRGBA(vg, imageW, imageH, 0, imageData); + tex = nvgCreateImageRGBA(vg, imageW, imageH, NVG_IMAGE_DXT1 | NVG_IMAGE_LPDDR, imageData); + NVGXMtexture *gxmTex = nvgxmImageHandle(vg, tex); + dxt_compress(gxmTex->data, imageData, imageW, imageH, 0); } else { brls::Logger::error("Failed to load image: {}", this->imageUrl); } diff --git a/wiliwili/source/view/mpv_core.cpp b/wiliwili/source/view/mpv_core.cpp index 58ff6bbfa..58ef30cf5 100644 --- a/wiliwili/source/view/mpv_core.cpp +++ b/wiliwili/source/view/mpv_core.cpp @@ -151,6 +151,9 @@ static GLuint linkProgram(GLuint s1, GLuint s2) { #elif defined(BOREALIS_USE_D3D11) #include extern std::unique_ptr D3D11_CONTEXT; +#elif defined(BOREALIS_USE_GXM) +#include +#include #elif defined(USE_GL2) #undef glBindFramebuffer #define glBindFramebuffer(a, b) void() @@ -195,9 +198,11 @@ static inline float aspectConverter(const std::string &value) { void MPVCore::on_update(void *self) { brls::sync([]() { uint64_t flags = mpvRenderContextUpdate(MPVCore::instance().getContext()); -#if !defined(MPV_SW_RENDER) && !defined(MPV_USE_FB) +#if defined(MPV_NO_FB) || defined(BOREALIS_USE_DEKO3D) || defined(BOREALIS_USE_D3D11) + // 直接绘制到屏幕上,需要屏幕每刷新一次绘制一次,在 MPVCore 的绘制函数内部处理 (void)flags; #else + // 绘制到 FBO、纹理、buffer...上,在主循环结尾进行绘制,MPVCore 的绘制函数内直接绘制对应的 FBO、纹理、buffer... MPVCore::instance().redraw = flags & MPV_RENDER_UPDATE_FRAME; if (MPVCore::instance().redraw) { #ifdef MPV_SW_RENDER @@ -206,8 +211,10 @@ void MPVCore::on_update(void *self) { mpvRenderContextReportSwap(MPVCore::instance().mpv_context); #else mpvRenderContextRender(MPVCore::instance().mpv_context, MPVCore::instance().mpv_params); +#ifdef BOREALIS_USE_GL glBindFramebuffer(GL_FRAMEBUFFER, MPVCore::instance().default_framebuffer); glViewport(0, 0, (GLsizei)brls::Application::windowWidth, (GLsizei)brls::Application::windowHeight); +#endif mpvRenderContextReportSwap(MPVCore::instance().mpv_context); #endif } @@ -371,7 +378,7 @@ void MPVCore::init() { // mpvSetOptionString(mpv, "msg-level", "all=no"); if (MPVCore::TERMINAL) { mpvSetOptionString(mpv, "terminal", "yes"); - if ( brls::Logger::getLogLevel() >= brls::LogLevel::LOG_DEBUG ) { + if (brls::Logger::getLogLevel() >= brls::LogLevel::LOG_DEBUG) { mpvSetOptionString(mpv, "msg-level", "all=v"); } } @@ -423,6 +430,39 @@ void MPVCore::init() { {MPV_RENDER_PARAM_DXGI_INIT_PARAMS, &init_params}, {MPV_RENDER_PARAM_INVALID, nullptr}, }; +#elif defined(BOREALIS_USE_GXM) + auto *video_context = (brls::PsvVideoContext *)brls::Application::getPlatform()->getVideoContext(); + auto *window = video_context->getWindow(); + auto *vg = brls::Application::getNVGContext(); + mpv_gxm_init_params gxm_params = { + .context = window->context, + .shader_patcher = window->shader_patcher, + .buffer_index = 0, + }; + + mpv_render_param params[] = {{MPV_RENDER_PARAM_API_TYPE, (void *)MPV_RENDER_API_TYPE_GXM}, + {MPV_RENDER_PARAM_GXM_INIT_PARAMS, &gxm_params}, + {MPV_RENDER_PARAM_INVALID, nullptr}}; + + int texture_width = DISPLAY_WIDTH; + int texture_height = DISPLAY_HEIGHT; + int texture_stride = ALIGN(texture_width, 8); + nvg_image = nvgCreateImageRGBA(vg, texture_width, texture_height, 0, nullptr); + NVGXMtexture *texture = nvgxmImageHandle(vg, nvg_image); + + NVGXMframebufferInitOptions framebufferOpts = { + .display_buffer_count = 1, // Must be 1 for custom FBOs + .scenesPerFrame = 1, + .render_target = texture, + .color_format = SCE_GXM_COLOR_FORMAT_U8U8U8U8_ABGR, + .color_surface_type = SCE_GXM_COLOR_SURFACE_LINEAR, + .display_width = texture_width, + .display_height = texture_height, + .display_stride = texture_stride, + }; + mpv_fbo.tex = gxmCreateFramebuffer(&framebufferOpts); + mpv_fbo.w = texture_width; + mpv_fbo.h = texture_height; #else int advanced_control{1}; mpv_opengl_init_params gl_init_params{get_proc_address, nullptr}; @@ -660,6 +700,11 @@ void MPVCore::setFrameSize(brls::Rect r) { // 在视频暂停时调整纹理尺寸,视频画面会被清空为黑色,强制重新绘制一次,避免这个问题 mpvRenderContextRender(mpv_context, mpv_params); mpvRenderContextReportSwap(mpv_context); +#elif defined(BOREALIS_USE_GXM) + brls::sync([this]() { + mpvRenderContextRender(mpv_context, mpv_params); + mpvRenderContextReportSwap(mpv_context); + }); #elif !defined(MPV_USE_FB) // Using default framebuffer #ifndef BOREALIS_USE_D3D11 @@ -734,7 +779,23 @@ void MPVCore::draw(brls::Rect area, float alpha) { nvgRect(vg, rect.getMinX(), rect.getMinY(), rect.getWidth(), rect.getHeight()); nvgFillPaint(vg, nvgImagePattern(vg, 0, 0, rect.getWidth(), rect.getHeight(), 0, nvg_image, alpha)); nvgFill(vg); -#elif !defined(MPV_USE_FB) +#elif defined(BOREALIS_USE_GXM) + auto *vg = brls::Application::getNVGContext(); + + // TODO: 完全完成mpv后移除, 目前mpv调用nanovg内的 gxmClear 会导致视频画面闪烁 + if (rect.getWidth() < brls::Application::contentWidth) { + nvgBeginPath(vg); + nvgFillColor(vg, brls::Application::getTheme().getColor("brls/background")); + nvgRect(vg, 0, 0, brls::Application::contentWidth, brls::Application::contentHeight); + nvgFill(vg); + } + + NVGpaint img = nvgImagePattern(vg, 0, 0, area.getWidth(), area.getHeight(), 0, nvg_image, alpha); + nvgBeginPath(vg); + nvgRect(vg, area.getMinX(), area.getMinY(), area.getWidth(), area.getHeight()); + nvgFillPaint(vg, img); + nvgFill(vg); +#elif defined(MPV_NO_FB) || defined(BOREALIS_USE_DEKO3D) || defined(BOREALIS_USE_D3D11) // 只在非透明时绘制视频,可以避免退出页面时视频画面残留 if (alpha >= 1) { #ifdef BOREALIS_USE_DEKO3D @@ -743,7 +804,6 @@ void MPVCore::draw(brls::Rect area, float alpha) { videoContext->queueSignalFence(&readyFence); videoContext->queueFlush(); #endif - // 绘制视频 mpvRenderContextRender(this->mpv_context, mpv_params); #ifdef BOREALIS_USE_DEKO3D videoContext->queueWaitFence(&doneFence); @@ -770,7 +830,7 @@ void MPVCore::draw(brls::Rect area, float alpha) { } } #else - // shader draw + // OpenGL 将 FBO 绘制到屏幕上 glUseProgram(shader.prog); glBindTexture(GL_TEXTURE_2D, this->media_texture); #ifdef MPV_USE_VAO @@ -1154,7 +1214,7 @@ void MPVCore::setAspect(const std::string &value) { void MPVCore::setMirror(bool value) { MPVCore::VIDEO_MIRROR = value; - command_async("set", "vf", value ? "hflip": ""); + command_async("set", "vf", value ? "hflip" : ""); } void MPVCore::setBrightness(int value) {