From dff1138e61d110439ec2640c5a3e490fcf686a6a Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 5 Mar 2025 20:28:10 +0900 Subject: [PATCH 01/28] [Tizen] Cherrypicks from flutter-3.27.1 of flutter-tizen/engine --- DEPS | 2 +- engine/src/flutter/common/config.gni | 4 +-- .../src/flutter/display_list/testing/BUILD.gn | 3 ++- engine/src/flutter/impeller/tools/args.gni | 5 ++-- engine/src/flutter/shell/config.gni | 2 +- engine/src/flutter/testing/BUILD.gn | 2 +- engine/src/flutter/tools/gn | 27 ++++++++----------- .../src/flutter/txt/src/txt/platform_linux.cc | 3 +-- 8 files changed, 20 insertions(+), 28 deletions(-) diff --git a/DEPS b/DEPS index e0e46d3e86d2a..a15a90d7656b9 100644 --- a/DEPS +++ b/DEPS @@ -125,7 +125,7 @@ vars = { "checkout_llvm": False, # Setup Git hooks by default. - 'setup_githooks': True, + 'setup_githooks': False, # When this is true, the Flutter Engine's configuration files and scripts for # RBE will be downloaded from CIPD. This option is only usable by Googlers. diff --git a/engine/src/flutter/common/config.gni b/engine/src/flutter/common/config.gni index 318d305bd37fc..f3b3c15534a2f 100644 --- a/engine/src/flutter/common/config.gni +++ b/engine/src/flutter/common/config.gni @@ -157,6 +157,4 @@ if (flutter_prebuilt_dart_sdk) { # TODO: We can't build the engine artifacts for arm (32-bit) right now; # see https://github.com/flutter/flutter/issues/74322. build_engine_artifacts = - flutter_build_engine_artifacts && !is_android && - (current_toolchain == host_toolchain || - (is_linux && !is_chromeos && current_cpu != "arm") || is_mac || is_win) + flutter_build_engine_artifacts && current_toolchain == host_toolchain diff --git a/engine/src/flutter/display_list/testing/BUILD.gn b/engine/src/flutter/display_list/testing/BUILD.gn index 9a9637184af6f..f3cb22539cf78 100644 --- a/engine/src/flutter/display_list/testing/BUILD.gn +++ b/engine/src/flutter/display_list/testing/BUILD.gn @@ -35,7 +35,8 @@ surface_provider_include_software = !is_android && !is_ios # But, since benchmarks do not run on Windows and rendertests only # runs on SW by default, this restriction currently only limits the # ability to manually cross-check OpenGL on Windows for rendertests -surface_provider_include_gl = !is_fuchsia && !is_ios && !is_win && !is_mac +surface_provider_include_gl = + !is_fuchsia && !is_ios && !is_win && !is_mac && !is_linux # TODO (https://github.com/flutter/flutter/issues/107357): # impeller_enable_vulkan currently requires skia to not use VMA, which in turn diff --git a/engine/src/flutter/impeller/tools/args.gni b/engine/src/flutter/impeller/tools/args.gni index d1b387506a5af..2759a69136c9f 100644 --- a/engine/src/flutter/impeller/tools/args.gni +++ b/engine/src/flutter/impeller/tools/args.gni @@ -18,9 +18,8 @@ declare_args() { enable_unittests) && target_os != "fuchsia" # Whether the Vulkan backend is enabled. - impeller_enable_vulkan = - (is_linux || is_win || is_android || is_mac || is_qnx || - enable_unittests) && target_os != "fuchsia" + impeller_enable_vulkan = (is_win || is_android || is_mac || + enable_unittests) && target_os != "fuchsia" } # Arguments that are combinations of other arguments by default but which can diff --git a/engine/src/flutter/shell/config.gni b/engine/src/flutter/shell/config.gni index 3f498d2c044f7..9ad466439ee2e 100644 --- a/engine/src/flutter/shell/config.gni +++ b/engine/src/flutter/shell/config.gni @@ -17,6 +17,6 @@ declare_args() { test_enable_metal = shell_enable_metal # The Vulkan unittests are combined with the GL unittests. - test_enable_vulkan = is_fuchsia || shell_enable_gl + test_enable_vulkan = is_fuchsia test_enable_software = shell_enable_software } diff --git a/engine/src/flutter/testing/BUILD.gn b/engine/src/flutter/testing/BUILD.gn index 84a11db9a3f4c..f01027e666158 100644 --- a/engine/src/flutter/testing/BUILD.gn +++ b/engine/src/flutter/testing/BUILD.gn @@ -54,7 +54,7 @@ source_set("testing") { if (enable_unittests && is_linux) { # So that we can call gtk_init in main(). - configs += [ "//flutter/shell/platform/linux/config:gtk" ] + # configs += [ "//flutter/shell/platform/linux/config:gtk" ] } public_deps = [ ":testing_lib" ] diff --git a/engine/src/flutter/tools/gn b/engine/src/flutter/tools/gn index 726f171555ff5..e5e01f2237875 100755 --- a/engine/src/flutter/tools/gn +++ b/engine/src/flutter/tools/gn @@ -89,18 +89,7 @@ def to_command_line(gn_args): def is_host_build(args): - # If target_os == None, then this is a host build. - if args.target_os is None: - return True - # For linux arm64 builds, we cross compile from x64 hosts, so the - # target_os='linux' and linux-cpu='arm64' - if args.target_os == 'linux' and args.linux_cpu == 'arm64': - return True - # The Mac and host targets are redundant. Again, necessary to disambiguate - # during cross-compilation. - if args.target_os == 'mac': - return True - return False + return args.target_os is None # Determines whether a prebuilt Dart SDK can be used instead of building one. @@ -542,7 +531,7 @@ def to_gn_args(args): # does not exist. Further, we set the 'host_cpu' so that it shares the # bitwidth of the 32-bit arm target. if sys.platform.startswith( - ('cygwin', 'win')) and args.target_os == 'android' and gn_args['target_cpu'] == 'arm': + ('cygwin', 'win')) and args.target_os != 'win' and gn_args['target_cpu'] == 'arm': gn_args['host_cpu'] = 'x86' gn_args['current_cpu'] = 'x86' @@ -660,7 +649,7 @@ def to_gn_args(args): # Enable Vulkan on all platforms except for iOS. This is just # to save on mobile binary size, as there's no reason the Vulkan embedder # features can't work on these platforms. - if gn_args['target_os'] not in ['ios', 'mac']: + if gn_args['target_os'] not in ['ios', 'mac', 'linux']: gn_args['skia_use_vulkan'] = True gn_args['skia_use_vma'] = False gn_args['shell_enable_vulkan'] = True @@ -768,13 +757,17 @@ def to_gn_args(args): # Enable pointer compression on 64-bit mobile targets. iOS is excluded due to # its inability to allocate address space without allocating memory. - if args.target_os in ['android'] and gn_args['target_cpu'] in ['x64', 'arm64']: + if args.target_os in ['android', 'linux'] and gn_args['target_cpu'] in ['x64', 'arm64']: gn_args['dart_use_compressed_pointers'] = True if args.target_os == 'fuchsia': gn_args['gn_configs_path'] = '//flutter/build/config/fuchsia/gn_configs.gni' gn_args['fuchsia_gn_sdk'] = '//flutter/tools/fuchsia/gn-sdk' + # Don't use the default Linux sysroot when buliding for Linux on macOS. + if sys.platform == 'darwin' and args.target_os == 'linux': + gn_args['use_default_linux_sysroot'] = False + # Flags for Dart features: if args.use_mallinfo2: gn_args['dart_use_mallinfo2'] = args.use_mallinfo2 @@ -799,7 +792,9 @@ def to_gn_args(args): # There is a special case for Android on Windows because there we _only_ build # gen_snapshot, but the build defines otherwise make it look like the build is # for a host Windows build and make GN think we will be building ANGLE. - if is_host_build(args) or (args.target_os == 'android' and get_host_os() == 'win'): + # Angle is not used on Mac hosts as there are no tests for the OpenGL backend. + if (is_host_build(args) and gn_args['host_os'] != 'mac') or (args.target_os == 'linux' and + get_host_os() == 'win'): # Don't include git commit information. gn_args['angle_enable_commit_id'] = False # Do not build unnecessary parts of the ANGLE tree. diff --git a/engine/src/flutter/txt/src/txt/platform_linux.cc b/engine/src/flutter/txt/src/txt/platform_linux.cc index e79a96ada9de1..f0fa21e71b113 100644 --- a/engine/src/flutter/txt/src/txt/platform_linux.cc +++ b/engine/src/flutter/txt/src/txt/platform_linux.cc @@ -20,8 +20,7 @@ namespace txt { std::vector GetDefaultFontFamilies() { - return {"Ubuntu", "Adwaita Sans", "Cantarell", - "DejaVu Sans", "Liberation Sans", "Arial"}; + return {"TizenDefaultFont", "SamsungOneUI"}; } sk_sp GetDefaultFontManager(uint32_t font_initialization_data) { From db381a0ab6ad77eedb9388454598fa64405bded2 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 5 Mar 2025 20:50:28 +0900 Subject: [PATCH 02/28] [Tizen] Add CI related files --- .github/workflows/build-docker.yml | 21 ++ .github/workflows/build.yml | 313 ++++++++++++++++++ engine/src/flutter/ci/tizen/.gitignore | 3 + engine/src/flutter/ci/tizen/Dockerfile | 32 ++ engine/src/flutter/ci/tizen/arm.patch | 15 + engine/src/flutter/ci/tizen/build_llvm.sh | 45 +++ .../src/flutter/ci/tizen/generate_sysroot.py | 151 +++++++++ 7 files changed, 580 insertions(+) create mode 100644 .github/workflows/build-docker.yml create mode 100644 .github/workflows/build.yml create mode 100644 engine/src/flutter/ci/tizen/.gitignore create mode 100644 engine/src/flutter/ci/tizen/Dockerfile create mode 100644 engine/src/flutter/ci/tizen/arm.patch create mode 100755 engine/src/flutter/ci/tizen/build_llvm.sh create mode 100755 engine/src/flutter/ci/tizen/generate_sysroot.py diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml new file mode 100644 index 0000000000000..62267c3bbf820 --- /dev/null +++ b/.github/workflows/build-docker.yml @@ -0,0 +1,21 @@ +name: Build Docker + +on: [workflow_dispatch] + +jobs: + builder: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/build-push-action@v5 + with: + context: ci/tizen + file: ci/tizen/Dockerfile + push: true + tags: ghcr.io/${{ github.repository_owner }}/build-engine:latest diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000000..34d2bdd939119 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,313 @@ +name: Build + +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/flutter-tizen/build-engine:latest + credentials: + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + strategy: + matrix: + arch: [arm, arm64, x86] + mode: [debug, release, profile] + include: + - arch: arm + triple: arm-linux-gnueabi + - arch: arm64 + triple: aarch64-linux-gnu + - arch: x86 + triple: i686-linux-gnu + exclude: + - arch: x86 + mode: release + - arch: x86 + mode: profile + + steps: + - uses: actions/checkout@v4 + with: + path: flutter + fetch-depth: 1 + + - uses: actions/cache@v4 + with: + path: /tizen_tools/sysroot + key: sysroot + + - name: Install depot_tools + run: | + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + echo "$PWD/depot_tools" >> $GITHUB_PATH + export PATH="$PWD/depot_tools:$PATH" + # Install ninja + apt install ninja-build + + - name: Run gclient sync + run: | + cd flutter + gclient config --unmanaged https://github.com/flutter-tizen/flutter + sed -i "s/'flutter'/'.'/g" ./.gclient + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Generate sysroot + run: flutter/engine/src/flutter/ci/tizen/generate_sysroot.py --out /tizen_tools/sysroot + + - name: Build + run: | + cd flutter + # Ignore unsupported linker option. + sed -i "/-Wl,--undefined-version/d" engine/src/build/config/compiler/BUILD.gn + + engine/src/flutter/tools/gn \ + --target-os linux \ + --linux-cpu ${{ matrix.arch }} \ + --no-goma \ + --target-toolchain /tizen_tools/toolchains \ + --target-sysroot /tizen_tools/sysroot/${{ matrix.arch }} \ + --target-triple ${{ matrix.triple }} \ + --runtime-mode ${{ matrix.mode }} \ + --enable-fontconfig \ + --disable-desktop-embeddings \ + --target-dir build + cd engine/src + ninja -C out/build flutter_engine_library + cp -f flutter/third_party/icu/flutter/icudtl.dat out/build + + - name: Build gen_snapshot + if: ${{ matrix.mode != 'debug' }} + run: | + cd flutter + ninja -C engine/src/out/build clang_x64/gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }} + path: | + flutter/engine/src/out/build/libflutter_engine.so + flutter/engine/src/out/build/icudtl.dat + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + if: ${{ github.event_name == 'push' }} + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_symbols + path: flutter/engine/src/out/build/so.unstripped/libflutter_engine.so + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + if: ${{ matrix.mode != 'debug' }} + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_linux-x64 + path: flutter/engine/src/out/build/clang_x64/gen_snapshot + if-no-files-found: error + + windows-build: + runs-on: windows-latest + + strategy: + matrix: + arch: [arm, arm64] + mode: [release, profile] + + steps: + - name: Run git checkout + run: | + mkdir C:\workspace\flutter + cd C:\workspace\flutter + git config --global core.autocrlf true + git init --quiet + git remote add origin https://github.com/${{ github.repository }} + git fetch --depth 1 origin ${{ github.sha }} + git checkout FETCH_HEAD + + - name: Environment setup + run: | + Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -Force + echo "DEPOT_TOOLS_WIN_TOOLCHAIN=0" >> $Env:GITHUB_ENV + echo "GYP_MSVS_OVERRIDE_PATH=C:\Program Files\Microsoft Visual Studio\2022\Enterprise" >> $Env:GITHUB_ENV + echo "WINDOWSSDKDIR=C:\Program Files (x86)\Windows Kits\10" >> $Env:GITHUB_ENV + + - name: Install depot_tools + run: | + Invoke-WebRequest -Uri https://storage.googleapis.com/chrome-infra/depot_tools.zip -OutFile depot_tools.zip + 7z x -y -o"C:\workspace\depot_tools" .\depot_tools.zip + echo "C:\workspace\depot_tools" >> $Env:GITHUB_PATH + + - name: Run gclient sync + working-directory: C:\workspace\flutter + shell: powershell + run: | + gclient config --unmanaged https://github.com/flutter-tizen/flutter + (Get-Content ".gclient") | ForEach-Object { $_ -replace "'flutter'","'.'" } | Set-Content ".gclient" + # TODO(jsuya) : pipes deprecated in python 3.13. (https://dart-review.googlesource.com/c/sdk/+/307620) + (Get-Content "engine/src/build/vs_toolchain.py") | ForEach-Object { $_ -replace 'import pipes','' } | Set-Content "engine/src/build/vs_toolchain.py" + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Build + working-directory: C:\workspace\flutter\engine\src + run: | + python3 .\flutter\tools\gn ` + --linux ` + --linux-cpu=${{ matrix.arch }} ` + --runtime-mode=${{ matrix.mode }} ` + --no-goma ` + --target-dir build + # TODO(jsuya) : https://github.com/flutter/flutter/issues/163487 + C:\workspace\flutter\engine\src\flutter/third_party/ninja/ninja -C .\out\build gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_windows-x64 + path: C:\workspace\flutter\engine\src\out\build\gen_snapshot.exe + if-no-files-found: error + + macos-build: + runs-on: macos-latest + + strategy: + matrix: + arch: [arm, arm64] + mode: [release, profile] + + steps: + - uses: actions/checkout@v4 + with: + path: flutter + fetch-depth: 1 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install depot_tools + run: | + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + echo "$PWD/depot_tools" >> $GITHUB_PATH + + - name: Run gclient sync + run: | + cd flutter + gclient config --unmanaged https://github.com/flutter-tizen/flutter + sed -i '' "s/'flutter'/'.'/g" .gclient + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Build + run: | + cd flutter + # Change host_toolchain to mac/clang_arm64. + sed -i "" "s|//build/toolchain/linux:clang_$host_cpu|//build/toolchain/mac:clang_$host_cpu|g" engine/src/build/config/BUILDCONFIG.gn + + # Pass dummy values to prevent using the default (Linux) toolchain. + engine/src/flutter/tools/gn \ + --linux \ + --linux-cpu=${{ matrix.arch }} \ + --no-goma \ + --target-toolchain _ \ + --target-sysroot _ \ + --target-triple _ \ + --runtime-mode=${{ matrix.mode }} \ + --disable-desktop-embeddings \ + --target-dir build + cd engine/src + # TODO(jsuya) : https://github.com/flutter/flutter/issues/163487 + flutter/third_party/ninja/ninja -C out/build clang_arm64/gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_darwin-arm64 + path: flutter/engine/src/out/build/clang_arm64/gen_snapshot + if-no-files-found: error + + macos-intel-build: + runs-on: macos-13 + + strategy: + matrix: + arch: [arm, arm64] + mode: [release, profile] + + steps: + - uses: actions/checkout@v4 + with: + path: flutter + fetch-depth: 1 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install depot_tools + run: | + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + echo "$PWD/depot_tools" >> $GITHUB_PATH + + - name: Run gclient sync + run: | + cd flutter + gclient config --unmanaged https://github.com/flutter-tizen/flutter + sed -i '' "s/'flutter'/'.'/g" .gclient + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Build + run: | + cd flutter + # Change host_toolchain to mac/clang_x64. + sed -i "" "s|//build/toolchain/linux:clang_$host_cpu|//build/toolchain/mac:clang_$host_cpu|g" engine/src/build/config/BUILDCONFIG.gn + + # Pass dummy values to prevent using the default (Linux) toolchain. + engine/src/flutter/tools/gn \ + --linux \ + --linux-cpu=${{ matrix.arch }} \ + --no-goma \ + --target-toolchain _ \ + --target-sysroot _ \ + --target-triple _ \ + --runtime-mode=${{ matrix.mode }} \ + --disable-desktop-embeddings \ + --target-dir build + cd engine/src + # TODO(jsuya) : https://github.com/flutter/flutter/issues/163487 + flutter/third_party/ninja/ninja -C out/build clang_x64/gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_darwin-x64 + path: flutter/engine/src/out/build/clang_x64/gen_snapshot + if-no-files-found: error + + release: + needs: [build] + if: ${{ github.event_name == 'push' }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + + - name: Create archives + run: | + for name in tizen-*; do + 7z a $name.zip ./$name/* + done + + - name: Set variables + run: | + echo "SHORT_SHA=$(git rev-parse --short $GITHUB_SHA)" >> $GITHUB_ENV + echo "VERSION=$(echo "${{ github.ref_name }}" | cut -d'-' -f2)" >> $GITHUB_ENV + + - uses: softprops/action-gh-release@v2 + with: + name: ${{ env.VERSION }} (${{ env.SHORT_SHA }}) + tag_name: ${{ env.SHORT_SHA }} + target_commitish: ${{ github.sha }} + files: tizen-*.zip + body: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/engine/src/flutter/ci/tizen/.gitignore b/engine/src/flutter/ci/tizen/.gitignore new file mode 100644 index 0000000000000..c23e7e850155f --- /dev/null +++ b/engine/src/flutter/ci/tizen/.gitignore @@ -0,0 +1,3 @@ +/llvm-project*/ +/toolchains*/ +/sysroot/ diff --git a/engine/src/flutter/ci/tizen/Dockerfile b/engine/src/flutter/ci/tizen/Dockerfile new file mode 100644 index 0000000000000..f5d0f1fc5520f --- /dev/null +++ b/engine/src/flutter/ci/tizen/Dockerfile @@ -0,0 +1,32 @@ +############################### +### Stage for building LLVM ### +############################### + +FROM ubuntu:22.04 AS llvm + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y git zip build-essential cmake ninja-build clang-11 && \ + apt-get clean + +COPY build_llvm.sh . + +RUN /build_llvm.sh + + +############################## +### Create a release image ### +############################## + +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y binutils-arm-linux-gnueabi binutils-aarch64-linux-gnu binutils-i686-linux-gnu && \ + apt-get install -y git curl pkg-config ca-certificates python3 python3-pip rpm2cpio cpio && \ + apt-get clean + +# Copy build artifacts from the previous stage. +COPY --from=llvm /toolchains/ /tizen_tools/toolchains/ diff --git a/engine/src/flutter/ci/tizen/arm.patch b/engine/src/flutter/ci/tizen/arm.patch new file mode 100644 index 0000000000000..1dabb3f2803d4 --- /dev/null +++ b/engine/src/flutter/ci/tizen/arm.patch @@ -0,0 +1,15 @@ +diff --git a/usr/include/asm-arm/hwcap.h b/usr/include/asm-arm/hwcap.h +index da85060..adaf619 100644 +--- a/usr/include/asm-arm/hwcap.h ++++ b/usr/include/asm-arm/hwcap.h +@@ -26,5 +26,10 @@ + #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ + #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) + ++#define HWCAP2_AES (1 << 0) ++#define HWCAP2_PMULL (1 << 1) ++#define HWCAP2_SHA1 (1 << 2) ++#define HWCAP2_SHA2 (1 << 3) ++#define HWCAP2_CRC32 (1 << 4) + + #endif /* __ASMARM_HWCAP_H */ diff --git a/engine/src/flutter/ci/tizen/build_llvm.sh b/engine/src/flutter/ci/tizen/build_llvm.sh new file mode 100755 index 0000000000000..507395cea8363 --- /dev/null +++ b/engine/src/flutter/ci/tizen/build_llvm.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +SCRIPT_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" +OUTPUT_DIR="$SCRIPT_DIR/toolchains" +cd "$SCRIPT_DIR" + +# Check out the LLVM project source code. +if [ -d llvm-project ]; then + echo "The directory already exists. Skipping download." + cd llvm-project +else + mkdir llvm-project + cd llvm-project + git init + git remote add origin https://github.com/llvm/llvm-project.git + git fetch --depth=1 origin llvmorg-17.0.6 + git checkout FETCH_HEAD +fi + +# Run the ninja build. +mkdir -p build && cd build +cmake -G Ninja \ + -DCLANG_VENDOR="Tizen" \ + -DLLVM_ENABLE_PROJECTS="clang" \ + -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \ + -DCMAKE_C_COMPILER=clang-11 \ + -DCMAKE_CXX_COMPILER=clang++-11 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$OUTPUT_DIR" \ + ../llvm +ninja install -j$(nproc) + +# Create symbolic links to binutils. +# See build/toolchain/custom/BUILD.gn for more information. +cd "$OUTPUT_DIR/bin" +for name in ar readelf nm strip; do + ln -sf llvm-$name arm-linux-gnueabi-$name + ln -sf llvm-$name aarch64-linux-gnu-$name + ln -sf llvm-$name i686-linux-gnu-$name +done diff --git a/engine/src/flutter/ci/tizen/generate_sysroot.py b/engine/src/flutter/ci/tizen/generate_sysroot.py new file mode 100755 index 0000000000000..c4bdc51f347cf --- /dev/null +++ b/engine/src/flutter/ci/tizen/generate_sysroot.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import re +import shutil +import subprocess +import sys +import urllib.parse +import urllib.request +from pathlib import Path + +base_packages = [ + 'gcc', + 'glibc', + 'glibc-devel', + 'libgcc', + 'libstdc++', + 'linux-glibc-devel', + 'zlib-devel', +] + +unified_packages = [ + 'fontconfig', + 'fontconfig-devel', + 'freetype2-devel', + 'libpng-devel', +] + + +def generate_sysroot(sysroot: Path, api_version: float, arch: str, quiet=False): + if arch == 'arm': + tizen_arch = 'armv7l' + elif arch == 'arm64': + tizen_arch = 'aarch64' + elif arch == 'x86': + tizen_arch = 'i686' + else: + sys.exit('Unknown arch: ' + arch) + + base_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Base/latest/repos/standard/packages'.format( + api_version, api_version + ) + unified_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Unified/latest/repos/standard/packages'.format( + api_version, api_version + ) + + # Retrieve html documents. + documents = {} + for url in ['{}/{}'.format(base_repo, tizen_arch), '{}/{}'.format(base_repo, 'noarch'), + '{}/{}'.format(unified_repo, tizen_arch), '{}/{}'.format(unified_repo, 'noarch')]: + request = urllib.request.Request(url) + with urllib.request.urlopen(request) as response: + documents[url] = response.read().decode('utf-8') + + # Download packages. + download_path = sysroot / '.rpms' + download_path.mkdir(exist_ok=True) + existing_rpms = [f for f in download_path.iterdir() if f.suffix == '.rpm'] + + for package in base_packages + unified_packages: + quoted = urllib.parse.quote(package) + pattern = re.escape(quoted) + '-\\d+\\.[\\d_\\.]+-[\\d\\.]+\\..+\\.rpm' + + if any([re.match(pattern, rpm.name) for rpm in existing_rpms]): + continue + + for parent, doc in documents.items(): + match = re.search('.+?'.format(pattern), doc) + if match: + rpm_url = '{}/{}'.format(parent, match.group(1)) + break + + if match: + if not quiet: + print('Downloading ' + rpm_url) + urllib.request.urlretrieve(rpm_url, download_path / match.group(1)) + else: + sys.exit('Could not find a package named ' + package) + + # Extract files. + for rpm in [f for f in download_path.iterdir() if f.suffix == '.rpm']: + command = 'rpm2cpio {} | cpio -idum --quiet'.format(rpm) + subprocess.run(command, shell=True, cwd=sysroot, check=True) + + # Create symbolic links. + asm = sysroot / 'usr' / 'include' / 'asm' + if not asm.exists(): + os.symlink('asm-' + arch, asm) + pkgconfig = sysroot / 'usr' / 'lib' / 'pkgconfig' + if arch == 'arm64' and not pkgconfig.exists(): + os.symlink('../lib64/pkgconfig', pkgconfig) + + # Copy objects required by the linker, such as crtbeginS.o and libgcc.a. + if arch == 'arm64': + libpath = sysroot / 'usr' / 'lib64' + else: + libpath = sysroot / 'usr' / 'lib' + subprocess.run('cp gcc/*/*/*.o gcc/*/*/*.a .', shell=True, cwd=libpath, check=True) + + # Apply a patch if applicable. + patch = Path(__file__).parent / '{}.patch'.format(arch) + if patch.is_file(): + command = 'patch -p1 -s -d {} < {}'.format(sysroot, patch) + subprocess.run(command, shell=True, check=True) + + +def main(): + # Check dependencies. + for dep in ['rpm2cpio', 'cpio', 'git']: + if not shutil.which(dep): + sys.exit('{} is not installed. To install, run:\n' + ' sudo apt install {}'.format(dep, dep)) + + # Parse arguments. + parser = argparse.ArgumentParser(description='Tizen sysroot generator') + parser.add_argument('-o', '--out', metavar='PATH', type=str, help='Path to the output directory') + parser.add_argument('-f', '--force', action='store_true', help='Force re-downloading of packages') + parser.add_argument('-q', '--quiet', action='store_true', help='Suppress log output') + parser.add_argument( + '--api-version', + metavar='VER', + default=6.0, + type=float, + help='Target API version (defaults to 6.0)' + ) + args = parser.parse_args() + + if args.out: + outpath = Path(args.out) + else: + outpath = Path(__file__).parent / 'sysroot' + outpath.mkdir(exist_ok=True) + + for arch in ['arm', 'arm64', 'x86']: + sysroot = outpath / arch + if args.force and sysroot.is_dir(): + shutil.rmtree(sysroot) + sysroot.mkdir(exist_ok=True) + + if not args.quiet: + print('Generating sysroot for {}...'.format(arch)) + generate_sysroot(sysroot.resolve(), args.api_version, arch, args.quiet) + + +# Execute only if run as a script. +if __name__ == '__main__': + main() From 465e8c4ebaf4825549ef9bd0ba77e22d12343b0c Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 12 Mar 2025 12:56:12 +0900 Subject: [PATCH 03/28] [Tizen][impeller] Disable gles3 compile Stop generating the GLES3 impeller shader variants in shaders.gni. Flutter 3.44 changed shell/platform/embedder/embedder_surface_gl_impeller.cc to include the impeller/entity/gles3/* headers and select GLES3 shaders at runtime via GetShaderMappings(is_gles3). With GLES3 generation disabled those headers/symbols do not exist, so adapt that consumer here: drop the gles3 includes and always use the GLES2 shader set (valid on GLES3 contexts too). This keeps the disable-gles3 patch self-consistent. --- engine/src/flutter/impeller/tools/shaders.gni | 23 +---------------- .../embedder/embedder_surface_gl_impeller.cc | 25 ++++--------------- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/engine/src/flutter/impeller/tools/shaders.gni b/engine/src/flutter/impeller/tools/shaders.gni index 98aa5a7932170..ddacd814a83bb 100644 --- a/engine/src/flutter/impeller/tools/shaders.gni +++ b/engine/src/flutter/impeller/tools/shaders.gni @@ -122,22 +122,6 @@ template("impeller_shaders") { analyze = analyze } } - - gles3_shaders = "gles3_$target_name" - - impeller_shaders_gles(gles3_shaders) { - name = invoker.name - require_framebuffer_fetch = require_framebuffer_fetch - gles_language_version = 300 - is_300 = true - analyze = false - if (defined(invoker.gles_exclusions)) { - shaders = invoker.shaders - invoker.gles_exclusions - } else { - shaders = invoker.shaders - } - analyze = analyze - } } if (impeller_enable_vulkan) { @@ -171,12 +155,7 @@ template("impeller_shaders") { } if (enable_opengles) { - public_deps += [ ":$gles3_shaders" ] - - # wasm builds only use GLES3, no need to build GLES2 shaders - if (!is_wasm) { - public_deps += [ ":$gles_shaders" ] - } + public_deps += [ ":$gles_shaders" ] } if (impeller_enable_vulkan) { diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc index d73ae89f95ba8..3832a69e3f3fc 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc @@ -10,29 +10,16 @@ #include "impeller/entity/gles/entity_shaders_gles.h" #include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" #include "impeller/entity/gles/modern_shaders_gles.h" -#include "impeller/entity/gles3/entity_shaders_gles.h" -#include "impeller/entity/gles3/framebuffer_blend_shaders_gles.h" -#include "impeller/entity/gles3/modern_shaders_gles.h" #include "impeller/renderer/backend/gles/context_gles.h" #include "impeller/renderer/backend/gles/proc_table_gles.h" namespace flutter { namespace { -std::vector> GetShaderMappings(bool is_gles3) { - if (is_gles3) { - return { - std::make_shared( - impeller_entity_shaders_gles3_data, - impeller_entity_shaders_gles3_length), - std::make_shared( - impeller_modern_shaders_gles3_data, - impeller_modern_shaders_gles3_length), - std::make_shared( - impeller_framebuffer_blend_shaders_gles3_data, - impeller_framebuffer_blend_shaders_gles3_length), - }; - } +std::vector> GetShaderMappings() { + // Tizen: GLES3 impeller shaders are not compiled (see + // impeller/tools/shaders.gni). Always use the GLES2 shader set, which is + // also valid on GLES3 contexts. return { std::make_shared( impeller_entity_shaders_gles_data, @@ -101,9 +88,7 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( return; } - const auto is_gles3 = - gl->GetDescription()->GetGlVersion().IsAtLeast(impeller::Version(3)); - const auto shader_mappings = GetShaderMappings(is_gles3); + const auto shader_mappings = GetShaderMappings(); impeller_context_ = impeller::ContextGLES::Create( impeller::Flags{}, std::move(gl), shader_mappings, From 6546fe9d6dfffc35c10c9f2bff667d8e5dea2a43 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 23 May 2025 17:47:47 +0900 Subject: [PATCH 04/28] [Tizen] Remove unnecessary CI --- .github/workflows/mirror.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/mirror.yml diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml deleted file mode 100644 index a568c78265469..0000000000000 --- a/.github/workflows/mirror.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Mirror master to main branches in the flutter repository. -on: - push: - branches: - - 'master' - -# Declare default permissions as read only. -permissions: read-all - -jobs: - mirror_job: - permissions: - pull-requests: write - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/flutter' }} - name: Mirror master branch to main branch - steps: - - name: Mirror action step - id: mirror - uses: google/mirror-branch-action@30c52ee21f5d3bd7fb28b95501c11aae7f17eebb - with: - github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} - source: 'master' - dest: 'main' From a682001c4a388ea303f796cf03f720f8332930ea Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 23 May 2025 17:51:34 +0900 Subject: [PATCH 05/28] [Tizen] Upgrade llvm 17 to 18 --- .github/workflows/build-docker.yml | 4 ++-- .github/workflows/build.yml | 15 ++++++++------- engine/src/flutter/ci/tizen/build_llvm.sh | 3 ++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 62267c3bbf820..d15fb0f88456b 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -15,7 +15,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@v5 with: - context: ci/tizen - file: ci/tizen/Dockerfile + context: engine/src/flutter/ci/tizen + file: engine/src/flutter/ci/tizen/Dockerfile push: true tags: ghcr.io/${{ github.repository_owner }}/build-engine:latest diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 34d2bdd939119..61fd921e209ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,7 +119,11 @@ jobs: run: | mkdir C:\workspace\flutter cd C:\workspace\flutter - git config --global core.autocrlf true + git config --global core.autocrlf false + git config --global core.filemode false + git config --global core.fscache true + git config --global core.preloadindex true + git config --global depot-tools.allowGlobalGitConfig true git init --quiet git remote add origin https://github.com/${{ github.repository }} git fetch --depth 1 origin ${{ github.sha }} @@ -158,8 +162,7 @@ jobs: --runtime-mode=${{ matrix.mode }} ` --no-goma ` --target-dir build - # TODO(jsuya) : https://github.com/flutter/flutter/issues/163487 - C:\workspace\flutter\engine\src\flutter/third_party/ninja/ninja -C .\out\build gen_snapshot + ninja -C .\out\build gen_snapshot - uses: actions/upload-artifact@v4 with: @@ -216,8 +219,7 @@ jobs: --disable-desktop-embeddings \ --target-dir build cd engine/src - # TODO(jsuya) : https://github.com/flutter/flutter/issues/163487 - flutter/third_party/ninja/ninja -C out/build clang_arm64/gen_snapshot + ninja -C out/build clang_arm64/gen_snapshot - uses: actions/upload-artifact@v4 with: @@ -274,8 +276,7 @@ jobs: --disable-desktop-embeddings \ --target-dir build cd engine/src - # TODO(jsuya) : https://github.com/flutter/flutter/issues/163487 - flutter/third_party/ninja/ninja -C out/build clang_x64/gen_snapshot + ninja -C out/build clang_x64/gen_snapshot - uses: actions/upload-artifact@v4 with: diff --git a/engine/src/flutter/ci/tizen/build_llvm.sh b/engine/src/flutter/ci/tizen/build_llvm.sh index 507395cea8363..7d75d9c6a4252 100755 --- a/engine/src/flutter/ci/tizen/build_llvm.sh +++ b/engine/src/flutter/ci/tizen/build_llvm.sh @@ -18,7 +18,7 @@ else cd llvm-project git init git remote add origin https://github.com/llvm/llvm-project.git - git fetch --depth=1 origin llvmorg-17.0.6 + git fetch --depth=1 origin llvmorg-18.1.8 git checkout FETCH_HEAD fi @@ -29,6 +29,7 @@ cmake -G Ninja \ -DLLVM_ENABLE_PROJECTS="clang" \ -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \ -DCMAKE_C_COMPILER=clang-11 \ + -DCMAKE_CXX_FLAGS="-mssse3" \ -DCMAKE_CXX_COMPILER=clang++-11 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="$OUTPUT_DIR" \ From cb0237107d4fa30d1c0766f32058fbde1bbce9ad Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 23 Jun 2025 10:54:44 +0900 Subject: [PATCH 06/28] [Tizen] Temporary fix to Microsoft STL compiler compatibility checks --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61fd921e209ed..0beee16550bbb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -150,6 +150,10 @@ jobs: (Get-Content ".gclient") | ForEach-Object { $_ -replace "'flutter'","'.'" } | Set-Content ".gclient" # TODO(jsuya) : pipes deprecated in python 3.13. (https://dart-review.googlesource.com/c/sdk/+/307620) (Get-Content "engine/src/build/vs_toolchain.py") | ForEach-Object { $_ -replace 'import pipes','' } | Set-Content "engine/src/build/vs_toolchain.py" + + # TODO(jsuya) : Temporary fix to Microsoft STL compiler compatibility checks(https://github.com/flutter-tizen/flutter-tizen/pull/635#issuecomment-2990206946) + (Get-Content "engine/src/build/config/compiler/BUILD.gn") | ForEach-Object { $_; if ($_.Trim() -eq '"__STD_C",') { ' "_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH",' } } | Set-Content "engine/src/build/config/compiler/BUILD.gn" + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS gclient sync -v --no-history --shallow From 83245d88dfa78c20811ac5cf759d2357ba1e1922 Mon Sep 17 00:00:00 2001 From: xiaowei guan <60122246+xiaowei-guan@users.noreply.github.com> Date: Fri, 29 Aug 2025 09:21:53 +0800 Subject: [PATCH 07/28] [Tizen] Enable vulkan (#11) --- engine/src/build_overrides/vulkan_headers.gni | 2 +- engine/src/flutter/impeller/tools/args.gni | 2 +- engine/src/flutter/shell/config.gni | 2 +- engine/src/flutter/tools/gn | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/src/build_overrides/vulkan_headers.gni b/engine/src/build_overrides/vulkan_headers.gni index 8ef7d23936589..8a4144db207b6 100644 --- a/engine/src/build_overrides/vulkan_headers.gni +++ b/engine/src/build_overrides/vulkan_headers.gni @@ -6,6 +6,6 @@ # set anything. if (is_linux) { - vulkan_use_x11 = !is_minimal_linux + vulkan_use_x11 = false vulkan_use_wayland = true } diff --git a/engine/src/flutter/impeller/tools/args.gni b/engine/src/flutter/impeller/tools/args.gni index 2759a69136c9f..4be3a3d64eb8c 100644 --- a/engine/src/flutter/impeller/tools/args.gni +++ b/engine/src/flutter/impeller/tools/args.gni @@ -18,7 +18,7 @@ declare_args() { enable_unittests) && target_os != "fuchsia" # Whether the Vulkan backend is enabled. - impeller_enable_vulkan = (is_win || is_android || is_mac || + impeller_enable_vulkan = (is_linux || is_win || is_android || is_mac || enable_unittests) && target_os != "fuchsia" } diff --git a/engine/src/flutter/shell/config.gni b/engine/src/flutter/shell/config.gni index 9ad466439ee2e..8aa62e02417ba 100644 --- a/engine/src/flutter/shell/config.gni +++ b/engine/src/flutter/shell/config.gni @@ -7,7 +7,7 @@ declare_args() { # The logic for enabling Vulkan and Metal is in tools/gn. shell_enable_metal = false - shell_enable_vulkan = false + shell_enable_vulkan = true shell_enable_software = true } diff --git a/engine/src/flutter/tools/gn b/engine/src/flutter/tools/gn index e5e01f2237875..9aa027c060261 100755 --- a/engine/src/flutter/tools/gn +++ b/engine/src/flutter/tools/gn @@ -649,7 +649,7 @@ def to_gn_args(args): # Enable Vulkan on all platforms except for iOS. This is just # to save on mobile binary size, as there's no reason the Vulkan embedder # features can't work on these platforms. - if gn_args['target_os'] not in ['ios', 'mac', 'linux']: + if gn_args['target_os'] not in ['ios', 'mac']: gn_args['skia_use_vulkan'] = True gn_args['skia_use_vma'] = False gn_args['shell_enable_vulkan'] = True @@ -793,12 +793,12 @@ def to_gn_args(args): # gen_snapshot, but the build defines otherwise make it look like the build is # for a host Windows build and make GN think we will be building ANGLE. # Angle is not used on Mac hosts as there are no tests for the OpenGL backend. - if (is_host_build(args) and gn_args['host_os'] != 'mac') or (args.target_os == 'linux' and - get_host_os() == 'win'): + if (is_host_build(args) and gn_args['host_os'] != 'mac') or args.target_os == 'linux': # Don't include git commit information. gn_args['angle_enable_commit_id'] = False # Do not build unnecessary parts of the ANGLE tree. - gn_args['angle_build_all'] = False + if is_host_build(args) or args.target_os == 'linux': + gn_args['angle_build_all'] = False gn_args['angle_has_astc_encoder'] = False gn_args['angle_enable_libdrm'] = False # Force ANGLE context checks on Windows to prevent crashes. From 078db53506ce6a49ea52f90e3f6a506e77787b36 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 12 Sep 2025 12:06:23 +0900 Subject: [PATCH 08/28] [Tizen] Add x64 release (#13) flutter-tizen/flutter-tizen#554 Added x64 artifacts for x64 emulator support (Currently, x64 Release mode is only supported on Linux hosts.) --- .github/workflows/build.yml | 41 +++++++++++++++++++++-- engine/src/flutter/ci/tizen/build_llvm.sh | 1 + engine/src/flutter/testing/testing.gni | 3 +- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0beee16550bbb..e828f82a2b2a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - arch: [arm, arm64, x86] + arch: [arm, arm64, x86, x64] mode: [debug, release, profile] include: - arch: arm @@ -21,6 +21,8 @@ jobs: triple: aarch64-linux-gnu - arch: x86 triple: i686-linux-gnu + - arch: x64 + triple: x86_64-linux-gnu exclude: - arch: x86 mode: release @@ -58,6 +60,7 @@ jobs: run: flutter/engine/src/flutter/ci/tizen/generate_sysroot.py --out /tizen_tools/sysroot - name: Build + if: ${{ matrix.arch != 'x64' }} run: | cd flutter # Ignore unsupported linker option. @@ -78,12 +81,37 @@ jobs: ninja -C out/build flutter_engine_library cp -f flutter/third_party/icu/flutter/icudtl.dat out/build + - name: Build(x64) + if: ${{ matrix.arch == 'x64' }} + run: | + cd flutter + + engine/src/flutter/tools/gn \ + --target-os linux \ + --linux-cpu ${{ matrix.arch }} \ + --no-goma \ + --target-triple ${{ matrix.triple }} \ + --runtime-mode ${{ matrix.mode }} \ + --enable-fontconfig \ + --disable-desktop-embeddings \ + --target-dir build + + cd engine/src + ninja -C out/build flutter_engine_library + cp -f flutter/third_party/icu/flutter/icudtl.dat out/build + - name: Build gen_snapshot - if: ${{ matrix.mode != 'debug' }} + if: ${{ matrix.mode != 'debug' && matrix.arch != 'x64'}} run: | cd flutter ninja -C engine/src/out/build clang_x64/gen_snapshot + - name: Build gen_snapshot(x64) + if: ${{ matrix.mode != 'debug' && matrix.arch == 'x64'}} + run: | + cd flutter + ninja -C engine/src/out/build gen_snapshot + - uses: actions/upload-artifact@v4 with: name: tizen-${{ matrix.arch }}-${{ matrix.mode }} @@ -100,12 +128,19 @@ jobs: if-no-files-found: error - uses: actions/upload-artifact@v4 - if: ${{ matrix.mode != 'debug' }} + if: ${{ matrix.mode != 'debug' && matrix.arch != 'x64'}} with: name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_linux-x64 path: flutter/engine/src/out/build/clang_x64/gen_snapshot if-no-files-found: error + - uses: actions/upload-artifact@v4 + if: ${{ matrix.mode != 'debug' && matrix.arch == 'x64'}} + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_linux-x64 + path: flutter/engine/src/out/build/gen_snapshot + if-no-files-found: error + windows-build: runs-on: windows-latest diff --git a/engine/src/flutter/ci/tizen/build_llvm.sh b/engine/src/flutter/ci/tizen/build_llvm.sh index 7d75d9c6a4252..de27efb73a9b6 100755 --- a/engine/src/flutter/ci/tizen/build_llvm.sh +++ b/engine/src/flutter/ci/tizen/build_llvm.sh @@ -43,4 +43,5 @@ for name in ar readelf nm strip; do ln -sf llvm-$name arm-linux-gnueabi-$name ln -sf llvm-$name aarch64-linux-gnu-$name ln -sf llvm-$name i686-linux-gnu-$name + ln -sf llvm-$name x86_64-linux-gnu-$name done diff --git a/engine/src/flutter/testing/testing.gni b/engine/src/flutter/testing/testing.gni index 5ead3d22e922c..c25746c779cec 100644 --- a/engine/src/flutter/testing/testing.gni +++ b/engine/src/flutter/testing/testing.gni @@ -15,7 +15,8 @@ declare_args() { # * host_toolchain: non-cross-compile. We can run tests on the host. # * Fuchsia: build unittests for testing on device. # * macOS: arm64 builds can run x64 binaries. - enable_unittests = current_toolchain == host_toolchain || is_fuchsia || is_mac + # Disable unit test + enable_unittests = false # Build iOS target device unit tests. # From 2face0fed8899327dfd1744cf97e5b29a7a300d5 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 17 Sep 2025 13:02:27 +0900 Subject: [PATCH 09/28] [Tizen] Disable x64 symbol generation temporary (#14) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e828f82a2b2a3..0eb01ef09b086 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,7 +121,7 @@ jobs: if-no-files-found: error - uses: actions/upload-artifact@v4 - if: ${{ github.event_name == 'push' }} + if: ${{ github.event_name == 'push' && matrix.arch != 'x64' }} with: name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_symbols path: flutter/engine/src/out/build/so.unstripped/libflutter_engine.so From b44979dee38795ad17537e4c006e402cf1794252 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 18 Sep 2025 17:48:28 +0900 Subject: [PATCH 10/28] [Tizen] Update sysroot download URL (#15) https://github.com/flutter-tizen/embedder/pull/118#discussion_r2357867428 --- engine/src/flutter/ci/tizen/generate_sysroot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/ci/tizen/generate_sysroot.py b/engine/src/flutter/ci/tizen/generate_sysroot.py index c4bdc51f347cf..210f7e9ecf9e7 100755 --- a/engine/src/flutter/ci/tizen/generate_sysroot.py +++ b/engine/src/flutter/ci/tizen/generate_sysroot.py @@ -41,10 +41,10 @@ def generate_sysroot(sysroot: Path, api_version: float, arch: str, quiet=False): else: sys.exit('Unknown arch: ' + arch) - base_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Base/latest/repos/standard/packages'.format( + base_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Base/reference/repos/standard/packages'.format( api_version, api_version ) - unified_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Unified/latest/repos/standard/packages'.format( + unified_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Unified/reference/repos/standard/packages'.format( api_version, api_version ) From 68aec63c95c4109d0ab9979f5c2fe0007898ce9b Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 19 Sep 2025 18:15:14 +0900 Subject: [PATCH 11/28] [ci] Fix window version to 2022 flutter engine's window build uses Window SDKs 10.0.22621.0. window-2025 runner in gitHub actions no longer supports the 10.0.22621.0 SDK. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0eb01ef09b086..cb97449ae2e30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -142,7 +142,7 @@ jobs: if-no-files-found: error windows-build: - runs-on: windows-latest + runs-on: windows-2022 strategy: matrix: From eb937c40be3ce702dee25b20438993c5bf4f3274 Mon Sep 17 00:00:00 2001 From: xiaowei guan <60122246+xiaowei-guan@users.noreply.github.com> Date: Sat, 11 Oct 2025 10:42:02 +0800 Subject: [PATCH 12/28] [Tizen] Support render texture for vulkan + impeller (#17) Add new struct FlutterVulkanTexture for embedder : ``` typedef struct { /// Handle to the VkImage that is owned by the embedder. The engine will /// bind this image for writing the frame. FlutterVulkanImageHandle image; /// The VkDeviceMemory that backs the iamge. FlutterVulkanDeviceMemoryHandle image_memory; /// The VkFormat of the image (for example: VK_FORMAT_R8G8B8A8_UNORM). uint32_t format; /// User data to be returned on the invocation of the destruction callback. void* user_data; /// Callback invoked (on an engine managed thread) that asks the embedder to /// collect the texture. VoidCallback destruction_callback; /// Optional parameters for texture height/width, default is 0, non-zero means /// the texture has the specified width/height. /// Width of the texture. size_t width; /// Height of the texture. size_t height; } FlutterVulkanTexture; ``` The implement of [texture source](https://github.com/flutter-tizen/flutter/pull/17/files#diff-7955a8522a753162869f2e8ca0017a83f4854b60800c844202e70c3aa00ff0c9R5-R204) refer to the solution of android platform(https://github.com/flutter-tizen/flutter/blob/flutter-3.35.3/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.cc) and I have submitted the code [Support render texture for embedder](https://github.com/xiaowei-guan/embedder/commit/6a94746b5a006730e6f5280d5a4fecf8eef53b4d), I will create a new PR after[ support vulkan backend](https://github.com/flutter-tizen/embedder/pull/110) PR released. --- .../flutter/shell/platform/embedder/BUILD.gn | 4 + .../shell/platform/embedder/embedder.cc | 21 ++ .../shell/platform/embedder/embedder.h | 39 +++- .../embedder_external_texture_resolver.cc | 19 ++ .../embedder_external_texture_resolver.h | 12 ++ ...embedder_external_texture_source_vulkan.cc | 204 ++++++++++++++++++ .../embedder_external_texture_source_vulkan.h | 70 ++++++ .../embedder_external_texture_vulkan.cc | 195 +++++++++++++++++ .../embedder_external_texture_vulkan.h | 64 ++++++ 9 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc create mode 100644 engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h create mode 100644 engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc create mode 100644 engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.h diff --git a/engine/src/flutter/shell/platform/embedder/BUILD.gn b/engine/src/flutter/shell/platform/embedder/BUILD.gn index 61abe64a16f90..91cb17c7254ab 100644 --- a/engine/src/flutter/shell/platform/embedder/BUILD.gn +++ b/engine/src/flutter/shell/platform/embedder/BUILD.gn @@ -167,6 +167,8 @@ template("embedder_source_set") { if (embedder_enable_vulkan) { sources += [ + "embedder_external_texture_vulkan.cc", + "embedder_external_texture_vulkan.h", "embedder_surface_vulkan.cc", "embedder_surface_vulkan.h", ] @@ -175,6 +177,8 @@ template("embedder_source_set") { sources += [ "embedder_surface_vulkan_impeller.cc", "embedder_surface_vulkan_impeller.h", + "embedder_external_texture_source_vulkan.cc", + "embedder_external_texture_source_vulkan.h", ] } diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index a9a4b21350f37..190eb63969f04 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -2373,6 +2373,27 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, external_texture_metal_callback); } } +#endif +#ifdef SHELL_ENABLE_VULKAN + if (config->type == kVulkan) { + const FlutterVulkanRendererConfig* vulkan_config = &config->vulkan; + if (auto callback = SAFE_ACCESS(vulkan_config, + external_texture_frame_callback, nullptr)) { + auto external_texture_vulkan_callback = + [ptr = callback, user_data]( + int64_t texture_identifier, size_t width, + size_t height) -> std::unique_ptr { + std::unique_ptr texture = + std::make_unique(); + if (!ptr(user_data, texture_identifier, width, height, texture.get())) { + return nullptr; + } + return texture; + }; + external_texture_resolver = std::make_unique( + external_texture_vulkan_callback); + } + } #endif auto custom_task_runners = SAFE_ACCESS(args, custom_task_runners, nullptr); auto thread_config_callback = [&custom_task_runners]( diff --git a/engine/src/flutter/shell/platform/embedder/embedder.h b/engine/src/flutter/shell/platform/embedder/embedder.h index 232bb8e51ce10..5475f275ed112 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.h +++ b/engine/src/flutter/shell/platform/embedder/embedder.h @@ -930,6 +930,9 @@ typedef void* FlutterVulkanQueueHandle; /// Alias for VkImage. typedef uint64_t FlutterVulkanImageHandle; +/// Alias for VkDeviceMemory. +typedef uint64_t FlutterVulkanDeviceMemoryHandle; + typedef struct { /// The size of this struct. Must be sizeof(FlutterVulkanImage). size_t struct_size; @@ -958,6 +961,36 @@ typedef bool (*FlutterVulkanPresentCallback)( void* /* user data */, const FlutterVulkanImage* /* image */); +typedef struct { + /// Handle to the VkImage that is owned by the embedder. The engine will + /// bind this image for writing the frame. + FlutterVulkanImageHandle image; + /// The VkDeviceMemory that backs the iamge. + FlutterVulkanDeviceMemoryHandle image_memory; + /// The VkFormat of the image (for example: VK_FORMAT_R8G8B8A8_UNORM). + uint32_t format; + /// User data to be returned on the invocation of the destruction callback. + void* user_data; + /// Callback invoked (on an engine managed thread) that asks the embedder to + /// collect the texture. + VoidCallback destruction_callback; + /// Optional parameters for texture height/width, default is 0, non-zero means + /// the texture has the specified width/height. + /// Width of the texture. + size_t width; + /// Height of the texture. + size_t height; +} FlutterVulkanTexture; + +/// Callback to provide an external texture for a given texture_id. +/// See: external_texture_frame_callback. +typedef bool (*FlutterVulkanTextureFrameCallback)( + void* /* user data */, + int64_t /* texture identifier */, + size_t /* width */, + size_t /* height */, + FlutterVulkanTexture* /* texture out */); + typedef struct { /// The size of this struct. Must be sizeof(FlutterVulkanRendererConfig). size_t struct_size; @@ -1021,7 +1054,11 @@ typedef struct { /// without any additional synchronization. /// Not used if a FlutterCompositor is supplied in FlutterProjectArgs. FlutterVulkanPresentCallback present_image_callback; - + /// When the embedder specifies that a texture has a frame available, the + /// engine will call this method (on an internal engine managed thread) so + /// that external texture details can be supplied to the engine for subsequent + /// composition. + FlutterVulkanTextureFrameCallback external_texture_frame_callback; } FlutterVulkanRendererConfig; typedef struct { diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.cc index 4e4b41b8d4ad2..2dcef86b2061c 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.cc @@ -21,6 +21,12 @@ EmbedderExternalTextureResolver::EmbedderExternalTextureResolver( : metal_callback_(std::move(metal_callback)) {} #endif +#ifdef SHELL_ENABLE_VULKAN +EmbedderExternalTextureResolver::EmbedderExternalTextureResolver( + EmbedderExternalTextureVulkan::ExternalTextureCallback vulkan_callback) + : vulkan_callback_(std::move(vulkan_callback)) {} +#endif + std::unique_ptr EmbedderExternalTextureResolver::ResolveExternalTexture(int64_t texture_id) { #ifdef SHELL_ENABLE_GL @@ -37,6 +43,13 @@ EmbedderExternalTextureResolver::ResolveExternalTexture(int64_t texture_id) { } #endif +#ifdef SHELL_ENABLE_VULKAN + if (vulkan_callback_) { + return std::make_unique(texture_id, + vulkan_callback_); + } +#endif + return nullptr; } @@ -53,6 +66,12 @@ bool EmbedderExternalTextureResolver::SupportsExternalTextures() { } #endif +#ifdef SHELL_ENABLE_VULKAN + if (vulkan_callback_) { + return true; + } +#endif + return false; } diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.h index 412301736e566..1079a006d6359 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_resolver.h @@ -17,6 +17,10 @@ #include "flutter/shell/platform/embedder/embedder_external_texture_metal.h" #endif +#ifdef SHELL_ENABLE_VULKAN +#include "flutter/shell/platform/embedder/embedder_external_texture_vulkan.h" +#endif + namespace flutter { class EmbedderExternalTextureResolver { public: @@ -34,6 +38,11 @@ class EmbedderExternalTextureResolver { EmbedderExternalTextureMetal::ExternalTextureCallback metal_callback); #endif +#ifdef SHELL_ENABLE_VULKAN + explicit EmbedderExternalTextureResolver( + EmbedderExternalTextureVulkan::ExternalTextureCallback vulkan_callback); +#endif + std::unique_ptr ResolveExternalTexture(int64_t texture_id); bool SupportsExternalTextures(); @@ -47,6 +56,9 @@ class EmbedderExternalTextureResolver { EmbedderExternalTextureMetal::ExternalTextureCallback metal_callback_; #endif +#ifdef SHELL_ENABLE_VULKAN + EmbedderExternalTextureVulkan::ExternalTextureCallback vulkan_callback_; +#endif FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureResolver); }; } // namespace flutter diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc new file mode 100644 index 0000000000000..31a959f9ba0c5 --- /dev/null +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc @@ -0,0 +1,204 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h" + +#include "impeller/renderer/backend/vulkan/allocator_vk.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/texture_source_vk.h" +#include "impeller/renderer/backend/vulkan/yuv_conversion_library_vk.h" +#include "vulkan/vulkan.hpp" + +namespace flutter { + +bool RequiresYCBCRConversion(impeller::vk::Format format) { + switch (format) { + case impeller::vk::Format::eG8B8R83Plane420Unorm: + case impeller::vk::Format::eG8B8R82Plane420Unorm: + case impeller::vk::Format::eG8B8R83Plane422Unorm: + case impeller::vk::Format::eG8B8R82Plane422Unorm: + case impeller::vk::Format::eG8B8R83Plane444Unorm: + return true; + default: + // NOTE: NOT EXHAUSTIVE. + break; + } + return false; +} + +EmbedderExternalTextureSourceVulkan::EmbedderExternalTextureSourceVulkan( + const std::shared_ptr& p_context, + FlutterVulkanTexture* embedder_desc) + : TextureSourceVK(ToTextureDescriptor(embedder_desc)) { + const auto& context = impeller::ContextVK::Cast(*p_context); + const auto& device = context.GetDevice(); + texture_image_ = + impeller::vk::Image(reinterpret_cast(embedder_desc->image)); + texture_device_memory_ = impeller::vk::DeviceMemory( + reinterpret_cast(embedder_desc->image_memory)); + + needs_yuv_conversion_ = RequiresYCBCRConversion( + static_cast(embedder_desc->format)); + std::shared_ptr yuv_conversion; + if (needs_yuv_conversion_) { + // Figure out how to perform YUV conversions. + yuv_conversion = CreateYUVConversion(context, embedder_desc); + if (!yuv_conversion || !yuv_conversion->IsValid()) { + VALIDATION_LOG << "Fail to create yuv conversion"; + return; + } + } + + // Create image view for the newly created image. + if (!CreateTextureImageView(device, embedder_desc, yuv_conversion)) { + VALIDATION_LOG << "Fail to create texture image view"; + return; + } + + yuv_conversion_ = std::move(yuv_conversion); + is_valid_ = true; +} + +impeller::PixelFormat ToPixelFormat(int32_t vk_format) { + switch (vk_format) { + case VK_FORMAT_UNDEFINED: + return impeller::PixelFormat::kUnknown; + case VK_FORMAT_R8G8B8A8_UNORM: + return impeller::PixelFormat::kR8G8B8A8UNormInt; + case VK_FORMAT_R8G8B8A8_SRGB: + return impeller::PixelFormat::kR8G8B8A8UNormIntSRGB; + case VK_FORMAT_B8G8R8A8_UNORM: + return impeller::PixelFormat::kB8G8R8A8UNormInt; + case VK_FORMAT_B8G8R8A8_SRGB: + return impeller::PixelFormat::kB8G8R8A8UNormIntSRGB; + case VK_FORMAT_R32G32B32A32_SFLOAT: + return impeller::PixelFormat::kR32G32B32A32Float; + case VK_FORMAT_R16G16B16A16_SFLOAT: + return impeller::PixelFormat::kR16G16B16A16Float; + case VK_FORMAT_S8_UINT: + return impeller::PixelFormat::kS8UInt; + case VK_FORMAT_D24_UNORM_S8_UINT: + return impeller::PixelFormat::kD24UnormS8Uint; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + return impeller::PixelFormat::kD32FloatS8UInt; + case VK_FORMAT_R8_UNORM: + return impeller::PixelFormat::kR8UNormInt; + case VK_FORMAT_R8G8_UNORM: + return impeller::PixelFormat::kR8G8UNormInt; + default: + return impeller::PixelFormat::kUnknown; + } +} + +impeller::TextureDescriptor +EmbedderExternalTextureSourceVulkan::ToTextureDescriptor( + FlutterVulkanTexture* embedder_desc) { + const auto size = + impeller::ISize{static_cast(embedder_desc->width), + static_cast(embedder_desc->height)}; + impeller::TextureDescriptor desc; + desc.storage_mode = impeller::StorageMode::kDevicePrivate; + desc.format = ToPixelFormat(embedder_desc->format); + desc.size = size; + desc.type = impeller::TextureType::kTexture2D; + desc.sample_count = impeller::SampleCount::kCount1; + desc.compression_type = impeller::CompressionType::kLossless; + desc.mip_count = 1u; + desc.usage = impeller::TextureUsage::kRenderTarget; + return desc; +} + +std::shared_ptr +EmbedderExternalTextureSourceVulkan::CreateYUVConversion( + const impeller::ContextVK& context, + FlutterVulkanTexture* embedder_desc) { + impeller::YUVConversionDescriptorVK conversion_chain; + auto& conversion_info = conversion_chain.get(); + + conversion_info.format = + static_cast(embedder_desc->format); + conversion_info.ycbcrModel = + impeller::vk::SamplerYcbcrModelConversion::eYcbcr709; + conversion_info.ycbcrRange = impeller::vk::SamplerYcbcrRange::eItuFull; + conversion_info.components = {impeller::vk::ComponentSwizzle::eIdentity, + impeller::vk::ComponentSwizzle::eIdentity, + impeller::vk::ComponentSwizzle::eIdentity, + impeller::vk::ComponentSwizzle::eIdentity}; + conversion_info.xChromaOffset = impeller::vk::ChromaLocation::eCositedEven; + conversion_info.yChromaOffset = impeller::vk::ChromaLocation::eCositedEven; + conversion_info.chromaFilter = impeller::vk::Filter::eNearest; + conversion_info.forceExplicitReconstruction = false; + return context.GetYUVConversionLibrary()->GetConversion(conversion_chain); +} + +bool EmbedderExternalTextureSourceVulkan::CreateTextureImageView( + const impeller::vk::Device& device, + FlutterVulkanTexture* embedder_desc, + const std::shared_ptr& yuv_conversion_wrapper) { + impeller::vk::StructureChain + view_chain; + auto& view_info = view_chain.get(); + view_info.image = texture_image_; + view_info.viewType = impeller::vk::ImageViewType::e2D; + view_info.format = static_cast(embedder_desc->format); + view_info.subresourceRange.aspectMask = + impeller::vk::ImageAspectFlagBits::eColor; + view_info.subresourceRange.baseMipLevel = 0u; + view_info.subresourceRange.baseArrayLayer = 0u; + view_info.subresourceRange.levelCount = 1; + view_info.subresourceRange.layerCount = 1; + + if (RequiresYCBCRConversion( + static_cast(embedder_desc->format))) { + view_chain.get().conversion = + yuv_conversion_wrapper->GetConversion(); + } else { + view_chain.unlink(); + } + auto image_view = device.createImageViewUnique(view_info); + if (image_view.result != impeller::vk::Result::eSuccess) { + return false; + } + texture_image_view_ = std::move(image_view.value); + return true; +} + +// |TextureSourceVK| +EmbedderExternalTextureSourceVulkan::~EmbedderExternalTextureSourceVulkan() = + default; + +bool EmbedderExternalTextureSourceVulkan::IsValid() const { + return is_valid_; +} + +// |TextureSourceVK| +impeller::vk::Image EmbedderExternalTextureSourceVulkan::GetImage() const { + return texture_image_; +} + +// |TextureSourceVK| +impeller::vk::ImageView EmbedderExternalTextureSourceVulkan::GetImageView() + const { + return texture_image_view_.get(); +} + +// |TextureSourceVK| +impeller::vk::ImageView +EmbedderExternalTextureSourceVulkan::GetRenderTargetView() const { + return texture_image_view_.get(); +} + +// |TextureSourceVK| +bool EmbedderExternalTextureSourceVulkan::IsSwapchainImage() const { + return is_swapchain_image_; +} + +// |TextureSourceVK| +std::shared_ptr +EmbedderExternalTextureSourceVulkan::GetYUVConversion() const { + return needs_yuv_conversion_ ? yuv_conversion_ : nullptr; +} + +} // namespace flutter diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h new file mode 100644 index 0000000000000..57bad4034fe59 --- /dev/null +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_SOURCE_VULKAN_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_SOURCE_VULKAN_H_ + +#include "flutter/shell/platform/embedder/embedder.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/texture_source_vk.h" +#include "impeller/renderer/backend/vulkan/vk.h" +#include "impeller/renderer/backend/vulkan/yuv_conversion_vk.h" + +namespace flutter { + +class ContextVK; + +class EmbedderExternalTextureSourceVulkan final + : public impeller::TextureSourceVK { + public: + EmbedderExternalTextureSourceVulkan( + const std::shared_ptr& context, + FlutterVulkanTexture* embedder_desc); + + // |TextureSourceVK| + ~EmbedderExternalTextureSourceVulkan() override; + + // |TextureSourceVK| + impeller::vk::Image GetImage() const override; + + // |TextureSourceVK| + impeller::vk::ImageView GetImageView() const override; + + // |TextureSourceVK| + impeller::vk::ImageView GetRenderTargetView() const override; + + bool IsValid() const; + + // |TextureSourceVK| + bool IsSwapchainImage() const override; + + // |TextureSourceVK| + std::shared_ptr GetYUVConversion() const override; + + private: + bool CreateTextureImageView( + const impeller::vk::Device& device, + FlutterVulkanTexture* embedder_desc, + const std::shared_ptr& yuv_conversion_wrapper); + impeller::TextureDescriptor ToTextureDescriptor( + FlutterVulkanTexture* embedder_desc); + std::shared_ptr CreateYUVConversion( + const impeller::ContextVK& context, + FlutterVulkanTexture* embedder_desc); + std::shared_ptr yuv_conversion_ = {}; + bool needs_yuv_conversion_ = false; + bool is_swapchain_image_ = false; + bool is_valid_ = false; + impeller::vk::Image texture_image_; + impeller::vk::DeviceMemory texture_device_memory_; + impeller::vk::UniqueImageView texture_image_view_ = {}; + EmbedderExternalTextureSourceVulkan( + const EmbedderExternalTextureSourceVulkan&) = delete; + EmbedderExternalTextureSourceVulkan& operator=( + const EmbedderExternalTextureSourceVulkan&) = delete; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_SOURCE_VULKAN_H_ diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc new file mode 100644 index 0000000000000..ced35eb6d5e85 --- /dev/null +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc @@ -0,0 +1,195 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/embedder_external_texture_vulkan.h" + +#include "flutter/fml/logging.h" +#include "flutter/impeller/display_list/dl_image_impeller.h" +#include "flutter/impeller/renderer/backend/vulkan/command_buffer_vk.h" +#include "flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h" +#include "impeller/core/texture_descriptor.h" +#include "impeller/display_list/aiks_context.h" +#include "impeller/renderer/backend/vulkan/texture_vk.h" +#include "impeller/renderer/context.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkPaint.h" +#include "third_party/skia/include/core/SkAlphaType.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h" +#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h" +#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h" + +namespace flutter { +EmbedderExternalTextureVulkan::EmbedderExternalTextureVulkan( + int64_t texture_identifier, + const ExternalTextureCallback& callback) + : Texture(texture_identifier), external_texture_callback_(callback) { + FML_DCHECK(external_texture_callback_); +} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::Paint(PaintContext& context, + const DlRect& bounds, + bool freeze, + const DlImageSampling sampling) { + if (last_image_ == nullptr) { + last_image_ = + ResolveTexture(Id(), context.gr_context, context.aiks_context, + SkISize::Make(bounds.GetWidth(), bounds.GetHeight())); + } + + DlCanvas* canvas = context.canvas; + const DlPaint* paint = context.paint; + + if (last_image_) { + DlRect image_bounds = DlRect::Make(last_image_->GetBounds()); + if (bounds != image_bounds) { + canvas->DrawImageRect(last_image_, image_bounds, bounds, sampling, paint); + } else { + canvas->DrawImage(last_image_, bounds.GetOrigin(), sampling, paint); + } + } +} + +sk_sp EmbedderExternalTextureVulkan::ResolveTexture( + int64_t texture_id, + GrDirectContext* context, + impeller::AiksContext* aiks_context, + const SkISize& size) { + if (!!aiks_context) { + return ResolveTextureImpeller(texture_id, aiks_context, size); + } else { + return ResolveTextureSkia(texture_id, context, size); + } +} + +sk_sp EmbedderExternalTextureVulkan::ResolveTextureSkia( + int64_t texture_id, + GrDirectContext* context, + const SkISize& size) { + context->flushAndSubmit(); + context->resetContext(kAll_GrBackendState); + std::unique_ptr texture = + external_texture_callback_(texture_id, size.width(), size.height()); + + if (!texture) { + return nullptr; + } + + size_t width = size.width(); + size_t height = size.height(); + + if (texture->width != 0 && texture->height != 0) { + width = texture->width; + height = texture->height; + } + + GrVkImageInfo image_info = { + .fImage = reinterpret_cast(texture->image), + .fImageTiling = VK_IMAGE_TILING_OPTIMAL, + .fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .fFormat = static_cast(texture->format), + .fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT, + .fSampleCount = 1, + .fLevelCount = 1, + }; + + auto gr_backend_texture = + GrBackendTextures::MakeVk(width, height, image_info); + SkImages::TextureReleaseProc release_proc = texture->destruction_callback; + auto image = + SkImages::BorrowTextureFrom(context, // context + gr_backend_texture, // texture handle + kTopLeft_GrSurfaceOrigin, // origin + kRGB_888x_SkColorType, // color type + kPremul_SkAlphaType, // alpha type + nullptr, // colorspace + release_proc, // texture release proc + texture->user_data // texture release context + ); + + if (!image) { + // In case Skia rejects the image, call the release proc so that + // embedders can perform collection of intermediates. + if (release_proc) { + release_proc(texture->user_data); + } + return nullptr; + } + + return DlImage::Make(std::move(image)); +} + +sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( + int64_t texture_id, + impeller::AiksContext* aiks_context, + const SkISize& size) { + std::unique_ptr texture_desc = + external_texture_callback_(texture_id, size.width(), size.height()); + if (!texture_desc) { + return nullptr; + } + + const auto& impeller_context = + impeller::ContextVK::Cast(*aiks_context->GetContext()); + + auto texture_source = std::make_shared( + aiks_context->GetContext(), texture_desc.get()); + + auto texture = std::make_shared( + aiks_context->GetContext(), texture_source); + // Transition the layout to shader read. + { + auto buffer = impeller_context.CreateCommandBuffer(); + impeller::CommandBufferVK& buffer_vk = + impeller::CommandBufferVK::Cast(*buffer); + + impeller::BarrierVK barrier; + barrier.cmd_buffer = buffer_vk.GetCommandBuffer(); + barrier.src_access = impeller::vk::AccessFlagBits::eColorAttachmentWrite | + impeller::vk::AccessFlagBits::eTransferWrite; + barrier.src_stage = + impeller::vk::PipelineStageFlagBits::eColorAttachmentOutput | + impeller::vk::PipelineStageFlagBits::eTransfer; + barrier.dst_access = impeller::vk::AccessFlagBits::eShaderRead; + barrier.dst_stage = impeller::vk::PipelineStageFlagBits::eFragmentShader; + + barrier.new_layout = impeller::vk::ImageLayout::eShaderReadOnlyOptimal; + + if (!texture_source->SetLayout(barrier).ok()) { + return nullptr; + } + if (!impeller_context.GetCommandQueue()->Submit({buffer}).ok()) { + return nullptr; + } + } + + return impeller::DlImageImpeller::Make(texture); +} + +EmbedderExternalTextureVulkan::~EmbedderExternalTextureVulkan() = default; + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::OnGrContextCreated() {} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::OnGrContextDestroyed() {} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::MarkNewFrameAvailable() { + last_image_ = nullptr; +} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::OnTextureUnregistered() {} + +} // namespace flutter diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.h new file mode 100644 index 0000000000000..07611b4b729bf --- /dev/null +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.h @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_VULKAN_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_VULKAN_H_ + +#include "flutter/common/graphics/texture.h" +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "include/core/SkTypes.h" +#include "include/gpu/vk/VulkanTypes.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flutter { + +class EmbedderExternalTextureVulkan : public flutter::Texture { + public: + using ExternalTextureCallback = std::function< + std::unique_ptr(int64_t, size_t, size_t)>; + EmbedderExternalTextureVulkan(int64_t texture_identifier, + const ExternalTextureCallback& callback); + + ~EmbedderExternalTextureVulkan(); + + private: + const ExternalTextureCallback& external_texture_callback_; + + sk_sp last_image_; + + sk_sp ResolveTexture(int64_t texture_id, + GrDirectContext* context, + impeller::AiksContext* aiks_context, + const SkISize& size); + sk_sp ResolveTextureSkia(int64_t texture_id, + GrDirectContext* context, + const SkISize& size); + sk_sp ResolveTextureImpeller(int64_t texture_id, + impeller::AiksContext* aiks_context, + const SkISize& size); + + // |flutter::Texture| + void Paint(PaintContext& context, + const DlRect& bounds, + bool freeze, + const DlImageSampling sampling) override; + + // |flutter::Texture| + void OnGrContextCreated() override; + + // |flutter::Texture| + void OnGrContextDestroyed() override; + + // |flutter::Texture| + void MarkNewFrameAvailable() override; + + // |flutter::Texture| + void OnTextureUnregistered() override; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureVulkan); +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_VULKAN_H_ From c99b78f8437ed716cc0df5fab13874cc34bcdcf6 Mon Sep 17 00:00:00 2001 From: xiaowei guan <60122246+xiaowei-guan@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:57:50 +0800 Subject: [PATCH 13/28] [Tizen] Fix external texture gl crash issue (#18) --- .../shell/platform/embedder/embedder_external_texture_gl.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc index d6279fee66992..aa6bd62588686 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc @@ -70,8 +70,10 @@ sk_sp EmbedderExternalTextureGL::ResolveTexture( const SkISize& size) { if (!!aiks_context) { return ResolveTextureImpeller(texture_id, aiks_context, size); - } else { + } else if (!!context) { return ResolveTextureSkia(texture_id, context, size); + } else { + return nullptr; } } From 2730988624b8e9f24b3d907d0a1b4febfb9be8ba Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 5 Dec 2025 14:47:59 +0900 Subject: [PATCH 14/28] [Tizen][ci] Add ignore linker option --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb97449ae2e30..2d4597630f465 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,6 +65,7 @@ jobs: cd flutter # Ignore unsupported linker option. sed -i "/-Wl,--undefined-version/d" engine/src/build/config/compiler/BUILD.gn + sed -i "s/Wno-nontrivial-memcall/Wno-nontrivial-memaccess/g" engine/src/build/config/compiler/BUILD.gn engine/src/flutter/tools/gn \ --target-os linux \ From aeb7ddfb76cdcd92ba839ef0a95ca50c4cc8b1b9 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 5 Dec 2025 14:55:30 +0900 Subject: [PATCH 15/28] [Tizen] Remove unnecessary CI --- .github/dependabot.yml | 23 --- .github/labeler.yml | 289 ----------------------------- .github/release.yml | 68 ------- .github/workflows/freeze.yml | 72 ------- .github/workflows/labeler.yml | 22 --- .github/workflows/no-response.yaml | 44 ----- 6 files changed, 518 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 .github/labeler.yml delete mode 100644 .github/release.yml delete mode 100644 .github/workflows/freeze.yml delete mode 100644 .github/workflows/labeler.yml delete mode 100644 .github/workflows/no-response.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index d0f70fdd13cea..0000000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,23 +0,0 @@ -# See Dependabot documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - groups: - all-github-actions: - patterns: [ "*" ] - reviewers: - - "christopherfujino" - - "jmagman" - labels: - - "team-infra" - - "autosubmit" - # Updating patch versions for "github-actions" is too chatty. - # See https://github.com/flutter/flutter/issues/158350. - ignore: - - dependency-name: "*" - update-types: ["version-update:semver-patch"] diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index 086c3ddc428a6..0000000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# See https://github.com/actions/labeler/blob/main/README.md for docs. -# Use git ls-files '' to see the list of matching files. - -'a: accessibility': - - changed-files: - - any-glob-to-any-file: - - '**/accessibility/*' - - '**/*accessibility*' - - '**/semantics/*' - - '**/*semantics*' - -'a: animation': - - changed-files: - - any-glob-to-any-file: - - '**/animation/*' - - '**/*animation*' - -'a: desktop': - - changed-files: - - any-glob-to-any-file: - - '**/linux/**/*' - - '**/macos/**/*' - - '**/windows/**/*' - - engine/src/flutter/shell/platform/darwin/common/**/* - -'a: internationalization': - - changed-files: - - any-glob-to-any-file: - - packages/flutter_localizations/**/* - -'a: tests': - - changed-files: - - any-glob-to-any-file: - - packages/flutter_driver/**/* - - packages/flutter_goldens/**/* - - packages/flutter_test/**/* - - packages/integration_test/**/* - -'a: text input': - - changed-files: - - all-globs-to-any-file: - - '**/*[tT]ext*' - - '!**/*[cC]ontext*' - - '!**/*[tT]exture*' - -'d: api docs': - - changed-files: - - any-glob-to-any-file: - - examples/api/**/* - -'d: docs/': - - changed-files: - - any-glob-to-any-file: - - docs/**/* - -'d: examples': - - changed-files: - - any-glob-to-any-file: - - examples/**/* - -'e: embedder': - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/embedder - -'e: impeller': - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/impeller/**/* - -engine: - - changed-files: - - any-glob-to-any-file: - - DEPS - - engine/**/* - - docs/engine/**/* - -'f: cupertino': - - changed-files: - - any-glob-to-any-file: - - '**/cupertino/*' - - '**/*cupertino*' - - docs/libraries/cupertino/**/* - -'f: focus': - - changed-files: - - any-glob-to-any-file: - - '**/focus/*' - - '**/*focus*' - -'f: gestures': - - changed-files: - - any-glob-to-any-file: - - '**/gestures/*' - - '**/*gestures*' - -'f: material design': - - changed-files: - - any-glob-to-any-file: - - '**/material/*' - - '**/*material*' - - docs/libraries/material/**/* - -'f: routes': - - changed-files: - - any-glob-to-any-file: - - '**/navigator/*' - - '**/*navigator*' - - '**/route/*' - - '**/*route*' - -'f: scrolling': - - changed-files: - - any-glob-to-any-file: - - '**/*scroll*' - - '**/scroll/*' - - '**/*sliver*' - - '**/sliver/*' - - '**/*viewport*' - - '**/viewport/*' - -framework: - - changed-files: - - any-glob-to-any-file: - - packages/flutter/**/* - - packages/flutter_driver/**/* - - packages/flutter_goldens/**/* - - packages/flutter_localizations/**/* - - packages/flutter_test/**/* - - packages/integration_test/**/* - - examples/api/**/* - - docs/about/**/* - - docs/contributing/**/* - - docs/libraries/**/* - -'f: integration_test': - - changed-files: - - any-glob-to-any-file: - - packages/integration_test/**/* - -package: - - changed-files: - - any-glob-to-any-file: - - docs/ecosystem/**/* - -platform-android: - - changed-files: - - any-glob-to-any-file: - - docs/platform/android/**/* - - engine/src/flutter/shell/platform/android/**/* - - packages/flutter_tools/*android* - - packages/flutter_tools/gradle/**/* - -platform-ios: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/ios/**/* - - packages/flutter_tools/lib/src/ios/**/* - -platform-fuchsia: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/fuchsia/**/* - -platform-linux: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/linux/**/* - - packages/flutter_tools/lib/src/linux/**/* - -platform-macos: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/macos/**/* - - packages/flutter_tools/lib/src/macos/**/* - -platform-web: - - changed-files: - - any-glob-to-any-file: - - '**/web_sdk/**/*' - - engine/src/flutter/lib/web_ui/**/* - - packages/flutter_web_plugins - -platform-windows: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/windows/**/* - - packages/flutter_tools/lib/src/windows/**/* - -'customer: gallery': - - changed-files: - - any-glob-to-any-file: - - examples/flutter_gallery/**/* - -'c: tech-debt': - - changed-files: - - any-glob-to-any-file: - - '**/fix_data.yaml' - - '**/*.expect' - - '**/*test_fixes*' -'c: contributor-productivity': - - changed-files: - - any-glob-to-any-file: - - docs/contributing/**/* - -# Keep this synced with CODEOWNERS. -team-android: - - changed-files: - - any-glob-to-any-file: - - docs/platform/android/**/* - - engine/src/flutter/shell/platform/android/**/* - - packages/flutter_tools/**/*android* - - packages/flutter_tools/**/*gradle* - -team-ecosystem: - - changed-files: - - any-glob-to-any-file: - - docs/ecosystem/**/* - -team-engine: - - changed-files: - - any-glob-to-any-file: - - docs/engine/**/* - -team-infra: - - changed-files: - - any-glob-to-any-file: - - docs/infra/**/* - - docs/releases/**/* - -# Keep this synced with CODEOWNERS. -team-ios: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/ios/framework/**/* - - packages/flutter_tools/**/ios/* - - packages/flutter_tools/**/macos/* - - packages/flutter_tools/**/*xcode* - - packages/flutter_tools/**/*ios* - - packages/flutter_tools/**/*macos* - -# Keep this synced with CODEOWNERS. -team-linux: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/linux/**/* - - packages/flutter_tools/**/linux/* - - packages/flutter_tools/**/*linux* - -# Keep this synced with CODEOWNERS. -team-macos: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/macos/**/* - - packages/flutter_tools/**/macos/* - - packages/flutter_tools/**/*macos* - -# Keep this synced with CODEOWNERS. -team-windows: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/windows/**/* - - packages/flutter_tools/**/windows/* - - packages/flutter_tools/**/*windows* - -team-tool: - - changed-files: - - any-glob-to-any-file: - - docs/tool/**/* - -team-web: - - changed-files: - - any-glob-to-any-file: - - docs/platforms/web/**/* - -tool: - - changed-files: - - any-glob-to-any-file: - - packages/flutter_tools/**/* - - packages/fuchsia_remote_debug_protocol/**/* - - docs/tool/**/* diff --git a/.github/release.yml b/.github/release.yml deleted file mode 100644 index 5c6decc52930e..0000000000000 --- a/.github/release.yml +++ /dev/null @@ -1,68 +0,0 @@ -changelog: - exclude: - authors: - - engine-flutter-autoroll - - fluttergithubbot - labels: - - passed first triage - - passed secondary triage - - "team: flakes" - - revert - categories: - - title: Framework - labels: - - framework - - "a: text input" - - "f: scrolling" - - "f: routes" - - "f: selection" - - "f: gestures" - exclude: - labels: - - "f: material design" - - title: Material - labels: - - "f: material design" - # Mobile - - title: iOS - labels: - - platform-ios - - "f: cupertino" - - title: Android - labels: - - platform-android - # Desktop - - title: macOS - labels: - - platform-mac - - title: Windows - labels: - - platform-windows - - title: Linux - labels: - - platform-linux - # Web - - title: Web - labels: - - platform-web - - "browser: chrome-desktop" - - "browser: edge" - - "browser: firefox" - - "browser: safari-macos" - # Misc - - title: Tooling - labels: - - tool - - title: DevTools - labels: - - "d: devtools" - - "d: intellij" - - "d: tools_metadata" - - "f: inspector" - - title: Documentation - labels: - - "d: examples" - - "d: api docs" - - title: Other Changes - labels: - - '*' diff --git a/.github/workflows/freeze.yml b/.github/workflows/freeze.yml deleted file mode 100644 index 11c904b149de7..0000000000000 --- a/.github/workflows/freeze.yml +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: Material and Cupertino Code Freeze - -on: - pull_request_target: - types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled] - branches: - - master - merge_group: - branches: - - master - -permissions: read-all - -jobs: - check_freeze: - name: Check Code Freeze - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/flutter' }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Check for changes in frozen folders - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - frozen: - - 'packages/flutter/lib/src/material/**' - - 'packages/flutter/lib/src/cupertino/**' - - 'packages/flutter/test/material/**' - - 'packages/flutter/test/cupertino/**' - - 'examples/api/lib/material/**' - - 'examples/api/lib/cupertino/**' - - 'examples/api/test/material/**' - - 'examples/api/test/cupertino/**' - - 'packages/flutter/lib/fix_data/fix_cupertino.yaml' - - 'packages/flutter/lib/fix_data/fix_material/**' - - 'packages/flutter/test_fixes/material/**' - - 'packages/flutter/test_fixes/cupertino/**' - - - name: Fail on frozen changes - # This step only runs if the 'frozen' filter matched AND the override label is NOT present - if: | - steps.filter.outputs.frozen == 'true' && - !contains(github.event.pull_request.labels.*.name, 'override: code freeze') - run: | - echo "Error: Code changes detected during the current code freeze." - echo "The following paths are currently frozen:" - echo " - packages/flutter/lib/src/material/" - echo " - packages/flutter/lib/src/cupertino/" - echo " - (and associated tests/examples)" - echo "" - echo "If this is a critical fix that must land during the freeze, please file an issue for team-design." - echo "Information on this code freeze: https://github.com/flutter/flutter/issues/184093" - exit 1 - - - name: Pass if overridden - if: | - steps.filter.outputs.frozen == 'true' && - contains(github.event.pull_request.labels.*.name, 'override: code freeze') - run: echo "Code freeze overridden by override: code freeze label." - - - name: Pass if no frozen changes - if: steps.filter.outputs.frozen == 'false' - run: echo "No changes to frozen code." diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index 193049ebb9400..0000000000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: "Pull Request Labeler" -on: -- pull_request_target - -# Declare default permissions as read only. -permissions: read-all - -jobs: - triage: - if: ${{ github.repository == 'flutter/flutter' }} - permissions: - pull-requests: write - runs-on: ubuntu-latest - steps: - # Source available at https://github.com/actions/labeler/blob/main/README.md - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b - with: - sync-labels: true diff --git a/.github/workflows/no-response.yaml b/.github/workflows/no-response.yaml deleted file mode 100644 index 94d4d8fc988ce..0000000000000 --- a/.github/workflows/no-response.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: No Response - -# Both `issue_comment` and `scheduled` event types are required for this Action -# to work properly. -on: - issue_comment: - types: [created] - schedule: - # Schedule for five minutes after the hour, every hour - - cron: '5 * * * *' - -# By specifying the access of one of the scopes, all of those that are not -# specified are set to 'none'. -permissions: - issues: write - -jobs: - noResponse: - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/flutter' }} - steps: - - uses: godofredoc/no-response@0ce2dc0e63e1c7d2b87752ceed091f6d32c9df09 - with: - token: ${{ github.token }} - # Comment to post when closing an Issue for lack of response. Set to `false` to disable - closeComment: > - Without additional information, we are unfortunately not sure how to - resolve this issue. We are therefore reluctantly going to close this - bug for now. - - If you find this problem please file a new issue with the same description, - what happens, logs and the output of 'flutter doctor -v'. All system setups - can be slightly different so it's always better to open new issues and reference - the related ones. - - Thanks for your contribution. - # Number of days of inactivity before an issue is closed for lack of response. - daysUntilClose: 21 - # Label requiring a response. - responseRequiredLabel: "waiting for customer response" From 5a6890f1cc1945fa4226f5f60b9ca54142d59cfa Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 5 Dec 2025 17:07:40 +0900 Subject: [PATCH 16/28] [Tizen] Disable lto option --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d4597630f465..0fee350a485cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,7 @@ jobs: --target-os linux \ --linux-cpu ${{ matrix.arch }} \ --no-goma \ + --no-lto \ --target-toolchain /tizen_tools/toolchains \ --target-sysroot /tizen_tools/sysroot/${{ matrix.arch }} \ --target-triple ${{ matrix.triple }} \ From cffd0c1197d9cd6d6d24cba10bd6c9bcf4a8cb68 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 12 Dec 2025 19:02:34 +0900 Subject: [PATCH 17/28] [Tizen][ci] Update macos runnner for intel mac Replaced macos-13 with macos-15-intel https://github.com/actions/runner-images/issues/13046 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0fee350a485cd..67a4a1cfcec32 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -269,7 +269,7 @@ jobs: if-no-files-found: error macos-intel-build: - runs-on: macos-13 + runs-on: macos-15-intel strategy: matrix: From 14fced10766f8b5c4ee51e568eec3f779a141838 Mon Sep 17 00:00:00 2001 From: xiaowei guan <60122246+xiaowei-guan@users.noreply.github.com> Date: Tue, 23 Dec 2025 16:30:24 +0800 Subject: [PATCH 18/28] [Tizen] Fix GPU memory leak issue (#21) Because Tizen platform don't support hardware key, so we can't create a cache source to store the gpu resource. so we should reset the resouce before the frame end. --- .../platform/embedder/embedder_external_texture_vulkan.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc index ced35eb6d5e85..b50d9644b914a 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc @@ -139,7 +139,7 @@ sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( return nullptr; } - const auto& impeller_context = + auto& impeller_context = impeller::ContextVK::Cast(*aiks_context->GetContext()); auto texture_source = std::make_shared( @@ -172,7 +172,7 @@ sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( return nullptr; } } - + impeller_context.DisposeThreadLocalCachedResources(); return impeller::DlImageImpeller::Make(texture); } From ca9afd2a8a0d6d9413f9e8b9c5826a65eed465db Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 11 Mar 2026 17:18:27 +0900 Subject: [PATCH 19/28] [Tizen] Add syscall compat header for custom Linux toolchains Inject a small compat header into the custom cross-toolchain command line so Linux targets built with Tizen sysroots still see __NR_getrandom even when the sysroot headers do not export it. This keeps third_party/dart untouched and fixes builds across common Tizen architectures: - x86: 355 - x64: 318 - arm: 384 - arm64: 278 --- engine/src/build/toolchain/custom/BUILD.gn | 9 +++-- .../toolchain/custom/tizen_syscall_compat.h | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 engine/src/build/toolchain/custom/tizen_syscall_compat.h diff --git a/engine/src/build/toolchain/custom/BUILD.gn b/engine/src/build/toolchain/custom/BUILD.gn index c5d2a60af752b..5011fa2ae6d63 100644 --- a/engine/src/build/toolchain/custom/BUILD.gn +++ b/engine/src/build/toolchain/custom/BUILD.gn @@ -25,6 +25,9 @@ toolchain("custom") { target_triple_flags = "--target=${custom_target_triple}" sysroot_flags = "--sysroot ${custom_sysroot}" + syscall_compat_header = + rebase_path("//build/toolchain/custom/tizen_syscall_compat.h", root_build_dir) + syscall_compat_flags = "-include ${syscall_compat_header}" custom_lib_flags = "-L${custom_toolchain}/lib" @@ -34,7 +37,7 @@ toolchain("custom") { tool("cc") { depfile = "{{output}}.d" - command = "$cc -MD -MF $depfile $target_triple_flags $sysroot_flags {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + command = "$cc -MD -MF $depfile $target_triple_flags $sysroot_flags $syscall_compat_flags {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" depsformat = "gcc" description = "CC {{output}}" outputs = @@ -43,7 +46,7 @@ toolchain("custom") { tool("cxx") { depfile = "{{output}}.d" - command = "$cxx -MD -MF $depfile $target_triple_flags $sysroot_flags {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + command = "$cxx -MD -MF $depfile $target_triple_flags $sysroot_flags $syscall_compat_flags {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" depsformat = "gcc" description = "CXX {{output}}" outputs = @@ -52,7 +55,7 @@ toolchain("custom") { tool("asm") { depfile = "{{output}}.d" - command = "$cc -MD -MF $depfile $target_triple_flags $sysroot_flags {{defines}} {{include_dirs}} {{asmflags}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + command = "$cc -MD -MF $depfile $target_triple_flags $sysroot_flags $syscall_compat_flags {{defines}} {{include_dirs}} {{asmflags}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" depsformat = "gcc" description = "ASM {{output}}" outputs = diff --git a/engine/src/build/toolchain/custom/tizen_syscall_compat.h b/engine/src/build/toolchain/custom/tizen_syscall_compat.h new file mode 100644 index 0000000000000..b6de9aeb0265e --- /dev/null +++ b/engine/src/build/toolchain/custom/tizen_syscall_compat.h @@ -0,0 +1,39 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BUILD_TOOLCHAIN_CUSTOM_TIZEN_SYSCALL_COMPAT_H_ +#define BUILD_TOOLCHAIN_CUSTOM_TIZEN_SYSCALL_COMPAT_H_ + +// Some Tizen cross-compilation sysroots do not expose __NR_getrandom even +// though the target Linux ABI supports the syscall. Dart's crypto_linux.cc +// references the syscall number directly, so provide the missing arch-specific +// values for custom Linux toolchains without modifying third_party sources. +#if defined(__linux__) + +#if defined(__i386__) && !defined(__NR_getrandom) +#define __NR_getrandom 355 +#endif + +#if defined(__x86_64__) && !defined(__NR_getrandom) +#define __NR_getrandom 318 +#endif + +#if defined(__arm__) && !defined(__NR_getrandom) +#define __NR_getrandom 384 +#endif + +#if defined(__aarch64__) && !defined(__NR_getrandom) +#define __NR_getrandom 278 +#endif + +// Some Tizen sysroots ship a pre-3.17 UAPI header that +// predates the getrandom(2) flags. BoringSSL's urandom.cc references +// GRND_NONBLOCK directly, so provide the missing flag for custom toolchains. +#if !defined(GRND_NONBLOCK) +#define GRND_NONBLOCK 0x0001 +#endif + +#endif // defined(__linux__) + +#endif // BUILD_TOOLCHAIN_CUSTOM_TIZEN_SYSCALL_COMPAT_H_ From 86ecc2200d8a0a664d47460f78149ce91288aaf2 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Tue, 24 Mar 2026 16:10:20 +0900 Subject: [PATCH 20/28] [Tizen] Remove unnecessary CI (#28) Remove unnecessary CI --- .github/workflows/content-aware-hash.yml | 26 ------------------ .github/workflows/coverage.yml | 29 -------------------- .github/workflows/lock.yaml | 35 ------------------------ .github/workflows/tool-test-general.yml | 30 -------------------- 4 files changed, 120 deletions(-) delete mode 100644 .github/workflows/content-aware-hash.yml delete mode 100644 .github/workflows/coverage.yml delete mode 100644 .github/workflows/lock.yaml delete mode 100644 .github/workflows/tool-test-general.yml diff --git a/.github/workflows/content-aware-hash.yml b/.github/workflows/content-aware-hash.yml deleted file mode 100644 index f4fdf557b8f2c..0000000000000 --- a/.github/workflows/content-aware-hash.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: Generate a content aware hash for the Flutter Engine - -on: workflow_dispatch - -permissions: - contents: read - -jobs: - generate-engine-content-hash: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - - - name: Generate Hash - run: | - # Script updated to handle gh-readonly-queue/ branches - engine_content_hash=$(bin/internal/content_aware_hash.sh) - # test notice annotation for retrival from api - echo "::notice ::{\"engine_content_hash\": \"${engine_content_hash}\"}" - # test summary writing - echo "{\"engine_content_hash\": \"${engine_content_hash}\"" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index b23675261f2ec..0000000000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: coverage - -on: - push: - branches: - - master - paths: - - 'packages/flutter/**' - -permissions: read-all - -jobs: - build: - name: coverage - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/flutter' }} - steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 - - name: ./bin/flutter test --coverage - run: pushd packages/flutter;../../bin/flutter test --coverage -j 1;popd - - name: upload coverage - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 - with: - files: packages/flutter/coverage/lcov.info - verbose: true diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml deleted file mode 100644 index e1b9355be94dd..0000000000000 --- a/.github/workflows/lock.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Configuration for Lock Threads - https://github.com/dessant/lock-threads - -name: 'Lock Threads' - -# By specifying the access of one of the scopes, all of those that are not -# specified are set to 'none'. -permissions: - issues: write - -on: - schedule: - - cron: '0 * * * *' - -jobs: - lock: - permissions: - issues: write - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/flutter' }} - steps: - - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 - with: - process-only: 'issues' - github-token: ${{ github.token }} - # Number of days of inactivity before a closed issue is locked. - issue-inactive-days: '14' - issue-comment: > - This thread has been automatically locked since there has not been - any recent activity after it was closed. If you are still experiencing a - similar issue, please open a new bug, including the output of - `flutter doctor -v` and a minimal reproduction of the issue. diff --git a/.github/workflows/tool-test-general.yml b/.github/workflows/tool-test-general.yml deleted file mode 100644 index d326db08ebeac..0000000000000 --- a/.github/workflows/tool-test-general.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Tool tests general - experiment - -on: - pull_request: - branches: [master] - paths: - - '.github/workflows/tool-test-general.yml' - - 'dev/**' - - 'packages/flutter_tools/**' - - 'bin/**' - - '.ci.yaml' - - 'engine/**' - - 'DEPS' - push: - branches: [master] - -jobs: - Linux_tool-tests-general: - permissions: - contents: read - runs-on: ubuntu-latest - - steps: - # Note: we must check out the tree for the composite action to be available - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - - uses: ./.github/actions/composite-flutter-setup - - - name: Tool Test - run: | - SHARD=tool_tests SUBSHARD=general dart --enable-asserts dev/bots/test.dart From 4c947134e2e11d9ba90b4d0567ff3e8388e48023 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 27 Mar 2026 13:37:14 +0900 Subject: [PATCH 21/28] [Tizen] CI host artifact builds for macOS (#29) Adds artifact generation for x64 builds in a macOS environment. I tested the build on macOS 26.3 (Tahoe) and M4 Pro (Arm64) using the x64 lib --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67a4a1cfcec32..d18770b52b733 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -216,7 +216,7 @@ jobs: strategy: matrix: - arch: [arm, arm64] + arch: [arm, arm64, x64] mode: [release, profile] steps: @@ -273,7 +273,7 @@ jobs: strategy: matrix: - arch: [arm, arm64] + arch: [arm, arm64, x64] mode: [release, profile] steps: @@ -326,7 +326,7 @@ jobs: if-no-files-found: error release: - needs: [build] + needs: [build, macos-build, macos-intel-build] if: ${{ github.event_name == 'push' }} runs-on: ubuntu-latest From 21c6e4ef77fa7e50931453cc9ff8f15c73df9ade Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 3 Apr 2026 16:42:45 +0900 Subject: [PATCH 22/28] [Tizen] CI host artifact builds for Windows x64 (#30) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d18770b52b733..7c4027fcfcda0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -148,7 +148,7 @@ jobs: strategy: matrix: - arch: [arm, arm64] + arch: [arm, arm64, x64] mode: [release, profile] steps: @@ -326,7 +326,7 @@ jobs: if-no-files-found: error release: - needs: [build, macos-build, macos-intel-build] + needs: [build, windows-build, macos-build, macos-intel-build] if: ${{ github.event_name == 'push' }} runs-on: ubuntu-latest From b5deae90addd776581d714640859b4c88fbf2eef Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 8 Jun 2026 12:15:12 +0900 Subject: [PATCH 23/28] [Tizen] Fix VkImage leak in EmbedderExternalTextureSourceVulkan Store the embedder's destruction_callback / user_data and invoke it in the destructor to reclaim the VkImage; the Impeller path never released it. Reset the image view before the callback frees the image. --- .../embedder_external_texture_source_vulkan.cc | 12 +++++++++--- .../embedder_external_texture_source_vulkan.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc index 31a959f9ba0c5..818fe07da71f2 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc @@ -30,7 +30,9 @@ bool RequiresYCBCRConversion(impeller::vk::Format format) { EmbedderExternalTextureSourceVulkan::EmbedderExternalTextureSourceVulkan( const std::shared_ptr& p_context, FlutterVulkanTexture* embedder_desc) - : TextureSourceVK(ToTextureDescriptor(embedder_desc)) { + : TextureSourceVK(ToTextureDescriptor(embedder_desc)), + destruction_callback_(embedder_desc->destruction_callback), + destruction_callback_user_data_(embedder_desc->user_data) { const auto& context = impeller::ContextVK::Cast(*p_context); const auto& device = context.GetDevice(); texture_image_ = @@ -166,8 +168,12 @@ bool EmbedderExternalTextureSourceVulkan::CreateTextureImageView( } // |TextureSourceVK| -EmbedderExternalTextureSourceVulkan::~EmbedderExternalTextureSourceVulkan() = - default; +EmbedderExternalTextureSourceVulkan::~EmbedderExternalTextureSourceVulkan() { + texture_image_view_.reset(); + if (destruction_callback_) { + destruction_callback_(destruction_callback_user_data_); + } +} bool EmbedderExternalTextureSourceVulkan::IsValid() const { return is_valid_; diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h index 57bad4034fe59..4f28ea2825238 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.h @@ -59,6 +59,8 @@ class EmbedderExternalTextureSourceVulkan final impeller::vk::Image texture_image_; impeller::vk::DeviceMemory texture_device_memory_; impeller::vk::UniqueImageView texture_image_view_ = {}; + VoidCallback destruction_callback_ = nullptr; + void* destruction_callback_user_data_ = nullptr; EmbedderExternalTextureSourceVulkan( const EmbedderExternalTextureSourceVulkan&) = delete; EmbedderExternalTextureSourceVulkan& operator=( From 346b691526879ae422020ebb35bd1b620fc1a503 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 8 Jun 2026 14:07:42 +0900 Subject: [PATCH 24/28] [Tizen] Validate external Vulkan texture before use Reject a null embedder image handle in the texture source constructor and skip wrapping an invalid source into a TextureVK, returning nullptr instead of rendering with a half-initialized texture. --- .../embedder/embedder_external_texture_source_vulkan.cc | 4 ++++ .../platform/embedder/embedder_external_texture_vulkan.cc | 3 +++ 2 files changed, 7 insertions(+) diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc index 818fe07da71f2..ab77d3afdefc4 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_source_vulkan.cc @@ -33,6 +33,10 @@ EmbedderExternalTextureSourceVulkan::EmbedderExternalTextureSourceVulkan( : TextureSourceVK(ToTextureDescriptor(embedder_desc)), destruction_callback_(embedder_desc->destruction_callback), destruction_callback_user_data_(embedder_desc->user_data) { + if (!embedder_desc->image) { + VALIDATION_LOG << "Vulkan image handle is null"; + return; + } const auto& context = impeller::ContextVK::Cast(*p_context); const auto& device = context.GetDevice(); texture_image_ = diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc index b50d9644b914a..50157012b2986 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc @@ -144,6 +144,9 @@ sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( auto texture_source = std::make_shared( aiks_context->GetContext(), texture_desc.get()); + if (!texture_source->IsValid()) { + return nullptr; + } auto texture = std::make_shared( aiks_context->GetContext(), texture_source); From a6768c9aa9202f86a225262cb8dbc4d3a2adb3e6 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Tue, 9 Jun 2026 19:55:41 +0900 Subject: [PATCH 25/28] [Tizen] Support GL_TEXTURE_EXTERNAL_OES for impeller external texture The flutter-tizen embedder passes target=GL_TEXTURE_EXTERNAL_OES, so set the descriptor type to kTextureExternalOES instead of the default 2D. --- .../shell/platform/embedder/embedder_external_texture_gl.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc index aa6bd62588686..d1437ad0d164e 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc @@ -155,6 +155,9 @@ sk_sp EmbedderExternalTextureGL::ResolveTextureImpeller( impeller::TextureDescriptor desc; desc.size = impeller::ISize(texture->width, texture->height); desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt; + if (texture->target == GL_TEXTURE_EXTERNAL_OES) { + desc.type = impeller::TextureType::kTextureExternalOES; + } impeller::ContextGLES& context = impeller::ContextGLES::Cast(*aiks_context->GetContext()); From b487c1e4bdbdac3f906c8bd3aaa590c63fd0778d Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 2 Jul 2026 20:35:33 +0900 Subject: [PATCH 26/28] [Tizen] Use HTTPS and avoid shell=True in sysroot generator Download packages from download.tizen.org over HTTPS instead of plain HTTP, and run the patch command with an argument list and stdin redirection instead of shell=True string interpolation. --- engine/src/flutter/ci/tizen/generate_sysroot.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/engine/src/flutter/ci/tizen/generate_sysroot.py b/engine/src/flutter/ci/tizen/generate_sysroot.py index 210f7e9ecf9e7..8aa3ccd211396 100755 --- a/engine/src/flutter/ci/tizen/generate_sysroot.py +++ b/engine/src/flutter/ci/tizen/generate_sysroot.py @@ -41,10 +41,10 @@ def generate_sysroot(sysroot: Path, api_version: float, arch: str, quiet=False): else: sys.exit('Unknown arch: ' + arch) - base_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Base/reference/repos/standard/packages'.format( + base_repo = 'https://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Base/reference/repos/standard/packages'.format( api_version, api_version ) - unified_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Unified/reference/repos/standard/packages'.format( + unified_repo = 'https://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Unified/reference/repos/standard/packages'.format( api_version, api_version ) @@ -104,8 +104,9 @@ def generate_sysroot(sysroot: Path, api_version: float, arch: str, quiet=False): # Apply a patch if applicable. patch = Path(__file__).parent / '{}.patch'.format(arch) if patch.is_file(): - command = 'patch -p1 -s -d {} < {}'.format(sysroot, patch) - subprocess.run(command, shell=True, check=True) + with open(patch) as file: + subprocess.run(['patch', '-p1', '-s', '-d', str(sysroot)], + stdin=file, check=True) def main(): From 9526dc832fe775bdf34423000adfbcfda80e627d Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 2 Jul 2026 20:35:51 +0900 Subject: [PATCH 27/28] [Tizen] Harden Vulkan external texture resolution - Null-check the command buffer returned by CreateCommandBuffer() before dereferencing it in the Impeller path. It can be null when the context is being torn down. - Fall back to the engine-provided size when the embedder does not populate the texture width/height, matching the Skia path. --- .../platform/embedder/embedder_external_texture_vulkan.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc index 50157012b2986..e10ba4c8f1a90 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_vulkan.cc @@ -139,6 +139,11 @@ sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( return nullptr; } + if (texture_desc->width == 0 || texture_desc->height == 0) { + texture_desc->width = size.width(); + texture_desc->height = size.height(); + } + auto& impeller_context = impeller::ContextVK::Cast(*aiks_context->GetContext()); @@ -153,6 +158,9 @@ sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( // Transition the layout to shader read. { auto buffer = impeller_context.CreateCommandBuffer(); + if (!buffer) { + return nullptr; + } impeller::CommandBufferVK& buffer_vk = impeller::CommandBufferVK::Cast(*buffer); From 7b52043b30638faecd2ef52fa4f77b8f731b24b8 Mon Sep 17 00:00:00 2001 From: xiaowei guan <60122246+xiaowei-guan@users.noreply.github.com> Date: Fri, 10 Jul 2026 08:42:54 +0800 Subject: [PATCH 28/28] [Impeller] Use the IO context for OpenGL program setup (#37) Fix https://github.com/flutter-tizen/embedder/issues/157 Impeller eagerly sets up the entire set of pipeline state objects (PSOs) it may need during renderer setup. This works fine on mid to high-end devices as the setup completes well before the first frame is rendered. However on low-end devices, not all PSOs may be ready before the first frame is rendered. Low-end device usually don't have as much available concurrency and are slower to boot. On these devices, the rendering thread had to wait for the background compile job to be completed. It was also observed that the relatively higher priority render thread was waiting on a background thread to finish a PSO compile job. The PSO compile job could also be stuck behind another job that was not immediately needed. This made the situation on low end devices less than ideal. https://github.com/flutter/flutter/pull/180022, this PR fix PSO stuck issue for Vulkan on low end devices. Current PR can fix the PSO stuck issue for GLES on low end devices. Related issue : https://github.com/flutter/flutter/issues/176657 This PR can improve app startup time for impeller+GLES on low-end devices. https://github.com/flutter/flutter/pull/180022 has provided solution for impeller + vulkan. Impeller+GLES has a special case: resource loading cannot be arbitrarily placed on other threads; it needs to be done on the I/O thread because this thread shares the context with the GPU thread. Therefore, this PR introduces the I/O task runner into PipelineCompileQueueGLES. However, since the I/O task runner may handle other tasks, we cannot put all jobs into the I/O thread at once. My current solution is to wait for the previous job to finish before putting the next one into the I/O thread. Main changes: Introduce fml::RefPtrfml::TaskRunner into PipelineCompileQueueGLES. Extract new class PipelineCompileQueueVulkan for vulkan, because the runners are on different threads. (cherry picked from commit 92a84309326c027c621dddd962485a920a9a5a2a) --- engine/src/flutter/impeller/renderer/BUILD.gn | 1 + .../impeller/renderer/backend/gles/BUILD.gn | 4 + .../renderer/backend/gles/context_gles.cc | 15 +- .../renderer/backend/gles/context_gles.h | 6 +- .../gles/pipeline_compile_queue_gles.cc | 72 +++++++ .../gles/pipeline_compile_queue_gles.h | 63 ++++++ .../backend/gles/pipeline_library_gles.cc | 51 +++-- .../backend/gles/pipeline_library_gles.h | 8 +- .../pipeline_compile_queue_gles_unittests.cc | 185 ++++++++++++++++++ .../impeller/renderer/backend/vulkan/BUILD.gn | 3 + .../vulkan/pipeline_compile_queue_vulkan.cc | 45 +++++ .../vulkan/pipeline_compile_queue_vulkan.h | 56 ++++++ .../backend/vulkan/pipeline_library_vk.cc | 2 +- .../backend/vulkan/pipeline_library_vk.h | 4 +- ...pipeline_compile_queue_vulkan_unittests.cc | 181 +++++++++++++++++ .../renderer/pipeline_compile_queue.cc | 50 ++--- .../renderer/pipeline_compile_queue.h | 71 +++++-- .../pipeline_compile_queue_unittests.cc | 96 +++++++++ .../common/shell_test_platform_view_gl.cc | 3 +- .../android_context_dynamic_impeller.cc | 8 +- .../android_context_dynamic_impeller.h | 4 +- .../android/android_context_gl_impeller.cc | 21 +- .../android/android_context_gl_impeller.h | 5 +- .../platform/android/platform_view_android.cc | 13 +- .../shell/platform/embedder/embedder.cc | 3 +- .../embedder/embedder_surface_gl_impeller.cc | 5 +- .../embedder/embedder_surface_gl_impeller.h | 3 +- 27 files changed, 885 insertions(+), 93 deletions(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc create mode 100644 engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc diff --git a/engine/src/flutter/impeller/renderer/BUILD.gn b/engine/src/flutter/impeller/renderer/BUILD.gn index 0340164b956f2..861f2b5f5435f 100644 --- a/engine/src/flutter/impeller/renderer/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/BUILD.gn @@ -104,6 +104,7 @@ template("renderer_unittests_component") { "blit_pass_unittests.cc", "capabilities_unittests.cc", "device_buffer_unittests.cc", + "pipeline_compile_queue_unittests.cc", "pipeline_descriptor_unittests.cc", "pipeline_library_unittests.cc", "pool_unittests.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn index 38c9771b4be9e..93bad0d441972 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn @@ -24,6 +24,7 @@ impeller_component("gles_unittests") { "test/mock_gles.cc", "test/mock_gles.h", "test/mock_gles_unittests.cc", + "test/pipeline_compile_queue_gles_unittests.cc", "test/pipeline_library_gles_unittests.cc", "test/proc_table_gles_unittests.cc", "test/reactor_unittests.cc", @@ -34,6 +35,7 @@ impeller_component("gles_unittests") { ] deps = [ ":gles", + "//flutter/fml", "//flutter/impeller/playground:playground_test", "//flutter/testing:testing_lib", ] @@ -68,6 +70,8 @@ impeller_component("gles") { "gpu_tracer_gles.h", "handle_gles.cc", "handle_gles.h", + "pipeline_compile_queue_gles.cc", + "pipeline_compile_queue_gles.h", "pipeline_gles.cc", "pipeline_gles.h", "pipeline_library_gles.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc index 084b74d4e0ec5..070591eba3a7e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc @@ -22,16 +22,19 @@ std::shared_ptr ContextGLES::Create( const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, - bool enable_gpu_tracing) { - return std::shared_ptr(new ContextGLES( - flags, std::move(gl), shader_libraries, enable_gpu_tracing)); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) { + return std::shared_ptr( + new ContextGLES(flags, std::move(gl), shader_libraries, + enable_gpu_tracing, std::move(io_task_runner))); } ContextGLES::ContextGLES( const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries_mappings, - bool enable_gpu_tracing) + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) : Context(flags) { reactor_ = std::make_shared(std::move(gl)); if (!reactor_->IsValid()) { @@ -52,8 +55,8 @@ ContextGLES::ContextGLES( // Create the pipeline library. { - pipeline_library_ = - std::shared_ptr(new PipelineLibraryGLES(reactor_)); + pipeline_library_ = std::shared_ptr( + new PipelineLibraryGLES(reactor_, std::move(io_task_runner))); } // Create allocators. diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h index 44f2f873bb23e..a35d4ab9342d1 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h @@ -28,7 +28,8 @@ class ContextGLES final : public Context, const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, - bool enable_gpu_tracing); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner = nullptr); // |Context| ~ContextGLES() override; @@ -64,7 +65,8 @@ class ContextGLES final : public Context, const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, - bool enable_gpu_tracing); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner = nullptr); // |Context| std::string DescribeGpuModel() const override; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc new file mode 100644 index 0000000000000..eeb971ab09751 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h" + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" + +namespace impeller { + +std::shared_ptr PipelineCompileQueueGLES::Create( + fml::RefPtr worker_task_runner) { + if (!worker_task_runner) { + return nullptr; + } + return std::shared_ptr( + new PipelineCompileQueueGLES(std::move(worker_task_runner))); +} + +PipelineCompileQueueGLES::PipelineCompileQueueGLES( + fml::RefPtr worker_task_runner) + : worker_task_runner_(std::move(worker_task_runner)) {} + +PipelineCompileQueueGLES::~PipelineCompileQueueGLES() = default; + +void PipelineCompileQueueGLES::OnJobAdded() { + // To prevent potential deadlocks and reduce lock contention, avoid calling + // external or virtual methods (such as DrainPendingJobs, which posts tasks + // to the task runner) while holding a mutex. Instead, minimize the scope of + // the lock by using a local boolean flag to trigger the draining process + // outside the lock block. + bool should_drain = false; + { + Lock lock(processing_mutex_); + if (!is_processing_) { + is_processing_ = true; + should_drain = true; + } + } + if (should_drain) { + DrainPendingJobs(); + } +} + +void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { + if (!job) { + return; + } + + worker_task_runner_->PostTask(job); +} + +void PipelineCompileQueueGLES::DrainPendingJobs() { + PostJob([weak_queue = weak_from_this()]() { + if (auto queue = std::static_pointer_cast( + weak_queue.lock())) { + queue->DoOneJob(); + { + Lock lock(queue->processing_mutex_); + if (!queue->HasPendingJobs()) { + queue->is_processing_ = false; + return; + } + } + queue->DrainPendingJobs(); + } + }); +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h new file mode 100644 index 0000000000000..138b5398ea301 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -0,0 +1,63 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ + +#include "flutter/fml/closure.h" +#include "flutter/fml/task_runner.h" +#include "impeller/base/thread.h" +#include "impeller/renderer/pipeline_compile_queue.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A task queue designed for managing compilation of pipeline state +/// objects for OpenGL ES backend. +/// +/// This subclass uses a fml::TaskRunner as the worker task runner +/// and implements a sequential job processing mechanism to prevent +/// blocking the IO task runner. +/// +/// Key characteristics: +/// - Uses fml::RefPtr for worker_task_runner_ +/// - Processes jobs sequentially: loads one job at a time before +/// proceeding to the next, preventing IO task runner blocking +/// - Uses DrainPendingJobs() to recursively process jobs one by one +/// - Employs is_processing_ flag and processing_mutex_ to control +/// sequential processing +/// +/// The sequential processing ensures that pipeline compilation jobs +/// do not overwhelm the task runner, which is particularly +/// important for GLES backend where resource loading patterns +/// differ from Vulkan. +/// +class PipelineCompileQueueGLES : public PipelineCompileQueue { + public: + static std::shared_ptr Create( + fml::RefPtr worker_task_runner); + + ~PipelineCompileQueueGLES() override; + + PipelineCompileQueueGLES(const PipelineCompileQueueGLES&) = delete; + + PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete; + + void PostJob(const fml::closure& job) override; + + void OnJobAdded() override; + + private: + explicit PipelineCompileQueueGLES( + fml::RefPtr worker_task_runner); + void DrainPendingJobs(); + + fml::RefPtr worker_task_runner_; + Mutex processing_mutex_; + bool is_processing_ IPLR_GUARDED_BY(processing_mutex_) = false; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index 240891dfc4b60..3cad5620e143c 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -16,8 +16,12 @@ namespace impeller { -PipelineLibraryGLES::PipelineLibraryGLES(std::shared_ptr reactor) - : reactor_(std::move(reactor)) {} +PipelineLibraryGLES::PipelineLibraryGLES( + std::shared_ptr reactor, + fml::RefPtr io_task_runner) + : reactor_(std::move(reactor)), + compile_queue_( + PipelineCompileQueueGLES::Create(std::move(io_task_runner))) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; @@ -296,17 +300,34 @@ PipelineFuture PipelineLibraryGLES::GetPipeline( PipelineFuture{descriptor, promise->get_future()}; pipelines_[descriptor] = pipeline_future; - const auto result = reactor_->AddOperation([promise, // - weak_this = weak_from_this(), // - descriptor, // - vert_function, // - frag_function, // - threadsafe // - ](const ReactorGLES& reactor) { - promise->set_value(CreatePipeline(weak_this, descriptor, vert_function, - frag_function, threadsafe)); - }); - FML_CHECK(result); + auto weak_this = weak_from_this(); + auto reactor = reactor_; + auto generation_task = [promise, weak_this, descriptor, vert_function, + frag_function, threadsafe, reactor]() { + auto thiz = weak_this.lock(); + if (!thiz) { + promise->set_value(nullptr); + return; + } + const auto result = reactor->AddOperation([promise, // + weak_this, // + descriptor, // + vert_function, // + frag_function, // + threadsafe // + ](const ReactorGLES& reactor) { + promise->set_value(CreatePipeline(weak_this, descriptor, vert_function, + frag_function, threadsafe)); + }); + FML_CHECK(result); + }; + + if (async && compile_queue_) { + compile_queue_->PostJobForDescriptor(descriptor, + std::move(generation_task)); + } else { + generation_task(); + } return pipeline_future; } @@ -373,4 +394,8 @@ void PipelineLibraryGLES::SetProgramForKey( programs_[key] = std::move(program); } +PipelineCompileQueue* PipelineLibraryGLES::GetPipelineCompileQueue() const { + return compile_queue_.get(); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h index 25c51c8113d38..765cff60c7152 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -9,7 +9,9 @@ #include #include "flutter/fml/hash_combine.h" +#include "flutter/fml/task_runner.h" #include "impeller/base/thread.h" +#include "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h" #include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/backend/gles/unique_handle_gles.h" #include "impeller/renderer/pipeline_library.h" @@ -91,8 +93,10 @@ class PipelineLibraryGLES final PipelineMap pipelines_; Mutex programs_mutex_; ProgramMap programs_ IPLR_GUARDED_BY(programs_mutex_); + std::shared_ptr compile_queue_; - explicit PipelineLibraryGLES(std::shared_ptr reactor); + explicit PipelineLibraryGLES(std::shared_ptr reactor, + fml::RefPtr io_task_runner); // |PipelineLibrary| bool IsValid() const override; @@ -127,6 +131,8 @@ class PipelineLibraryGLES final void SetProgramForKey(const ProgramKey& key, std::shared_ptr program); + // |PipelineLibrary| + PipelineCompileQueue* GetPipelineCompileQueue() const override; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc b/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc new file mode 100644 index 0000000000000..325254cadf02d --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc @@ -0,0 +1,185 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h" + +#include +#include +#include + +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/thread.h" +#include "flutter/testing/testing.h" +#include "impeller/renderer/pipeline_descriptor.h" + +namespace impeller { +namespace testing { + +TEST(PipelineCompileQueueGLESTest, CreateReturnsNullWithNullTaskRunner) { + auto queue = PipelineCompileQueueGLES::Create(nullptr); + EXPECT_EQ(queue, nullptr); +} + +TEST(PipelineCompileQueueGLESTest, CreateSucceedsWithValidTaskRunner) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + EXPECT_NE(queue, nullptr); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, PostJobDoesNothingWithNullClosure) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + queue->PostJob(nullptr); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, OnJobAddedProcessesJobsSequentially) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic completed_jobs{0}; + fml::CountDownLatch latch(3); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + queue->PostJobForDescriptor(desc1, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + completed_jobs++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(completed_jobs, 3); + + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, + PostJobForDescriptorWithDuplicateRunsEagerly) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic first_job_count{0}; + std::atomic second_job_count{0}; + fml::CountDownLatch latch(2); + + PipelineDescriptor desc; + + queue->PostJobForDescriptor(desc, [&]() { + first_job_count++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc, [&]() { + second_job_count++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(first_job_count, 1); + EXPECT_EQ(second_job_count, 1); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, IsProcessingResetsAfterAllJobsComplete) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + fml::CountDownLatch latch(1); + + queue->PostJobForDescriptor(PipelineDescriptor{}, + [&]() { latch.CountDown(); }); + + latch.Wait(); + + fml::CountDownLatch latch2(1); + queue->PostJobForDescriptor(PipelineDescriptor{}, + [&]() { latch2.CountDown(); }); + + latch2.Wait(); + + SUCCEED(); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, DestroyQueueWithPendingTasks) { + fml::Thread thread; + std::atomic completed_jobs{0}; + fml::CountDownLatch latch(3); + + { + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + queue->PostJobForDescriptor(desc1, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + completed_jobs++; + latch.CountDown(); + }); + + // Queue will be destroyed here with pending jobs. + // The destructor should block and wait for all jobs to complete. + } + + // All jobs should be completed before the queue is destroyed. + EXPECT_EQ(completed_jobs, 3); + thread.Join(); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn index 12fbcd6aea5ef..4e8604f724f4b 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn @@ -28,6 +28,7 @@ impeller_component("vulkan_unittests") { "test/mock_vulkan.cc", "test/mock_vulkan.h", "test/mock_vulkan_unittests.cc", + "test/pipeline_compile_queue_vulkan_unittests.cc", "test/sampler_library_vk_unittests.cc", "test/swapchain_unittests.cc", ] @@ -80,6 +81,8 @@ impeller_component("vulkan") { "pipeline_cache_data_vk.h", "pipeline_cache_vk.cc", "pipeline_cache_vk.h", + "pipeline_compile_queue_vulkan.cc", + "pipeline_compile_queue_vulkan.h", "pipeline_library_vk.cc", "pipeline_library_vk.h", "pipeline_vk.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc new file mode 100644 index 0000000000000..aed83a4cebdeb --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h" + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" + +namespace impeller { + +std::shared_ptr PipelineCompileQueueVulkan::Create( + std::shared_ptr worker_task_runner) { + if (!worker_task_runner) { + return nullptr; + } + return std::shared_ptr( + new PipelineCompileQueueVulkan(std::move(worker_task_runner))); +} + +PipelineCompileQueueVulkan::PipelineCompileQueueVulkan( + std::shared_ptr worker_task_runner) + : PipelineCompileQueue(), + worker_task_runner_(std::move(worker_task_runner)) {} + +PipelineCompileQueueVulkan::~PipelineCompileQueueVulkan() = default; + +void PipelineCompileQueueVulkan::OnJobAdded() { + PostJob([weak_queue = weak_from_this()]() { + if (auto queue = std::static_pointer_cast( + weak_queue.lock())) { + queue->DoOneJob(); + } + }); +} + +void PipelineCompileQueueVulkan::PostJob(const fml::closure& job) { + if (!job) { + return; + } + + worker_task_runner_->PostTask(job); +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h new file mode 100644 index 0000000000000..5a65c0ebc4997 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_COMPILE_QUEUE_VULKAN_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_COMPILE_QUEUE_VULKAN_H_ + +#include "flutter/fml/closure.h" +#include "flutter/fml/task_runner.h" +#include "impeller/renderer/pipeline_compile_queue.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A task queue designed for managing compilation of pipeline state +/// objects for Vulkan backend. +/// +/// This subclass uses a fml::BasicTaskRunner as the worker task +/// runner and dispatches compile jobs directly without sequential +/// processing constraints. +/// +/// Key characteristics: +/// - Uses std::shared_ptr for +/// worker_task_runner_ +/// - Dispatches jobs directly to the task runner in OnJobAdded() +/// - Does not implement sequential processing like GLES version +/// +/// The Vulkan backend benefits from the parallel nature of pipeline +/// compilation, allowing multiple compile jobs to be processed +/// concurrently through the task runner. +/// +class PipelineCompileQueueVulkan : public PipelineCompileQueue { + public: + static std::shared_ptr Create( + std::shared_ptr worker_task_runner); + + ~PipelineCompileQueueVulkan() override; + + PipelineCompileQueueVulkan(const PipelineCompileQueueVulkan&) = delete; + + PipelineCompileQueueVulkan& operator=(const PipelineCompileQueueVulkan&) = + delete; + + void PostJob(const fml::closure& job) override; + + void OnJobAdded() override; + + private: + explicit PipelineCompileQueueVulkan( + std::shared_ptr worker_task_runner); + std::shared_ptr worker_task_runner_; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_COMPILE_QUEUE_VULKAN_H_ diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 0cbef862c2bd1..ed4c2d5bc39e2 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -27,7 +27,7 @@ PipelineLibraryVK::PipelineLibraryVK( device_holder, std::move(cache_directory))), worker_task_runner_(std::move(worker_task_runner)), - compile_queue_(PipelineCompileQueue::Create(worker_task_runner_)) { + compile_queue_(PipelineCompileQueueVulkan::Create(worker_task_runner_)) { FML_DCHECK(worker_task_runner_); if (!pso_cache_->IsValid() || !worker_task_runner_) { return; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h index 3ead3c7dc700d..4304db9b58293 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h @@ -13,10 +13,10 @@ #include "impeller/base/thread.h" #include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h" #include "impeller/renderer/backend/vulkan/pipeline_cache_vk.h" +#include "impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h" #include "impeller/renderer/backend/vulkan/pipeline_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/pipeline.h" -#include "impeller/renderer/pipeline_compile_queue.h" #include "impeller/renderer/pipeline_library.h" namespace impeller { @@ -49,7 +49,7 @@ class PipelineLibraryVK final PipelineKey pipeline_key_ IPLR_GUARDED_BY(pipelines_mutex_) = 1; bool is_valid_ = false; bool cache_dirty_ = false; - std::shared_ptr compile_queue_; + std::shared_ptr compile_queue_; PipelineLibraryVK( const std::shared_ptr& device_holder, diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc new file mode 100644 index 0000000000000..6ee373535fd39 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc @@ -0,0 +1,181 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h" + +#include +#include +#include +#include + +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/task_runner.h" +#include "flutter/testing/testing.h" +#include "impeller/renderer/pipeline_descriptor.h" + +namespace impeller { +namespace testing { + +TEST(PipelineCompileQueueVulkanTest, CreateSucceedsWithValidTaskRunner) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + EXPECT_NE(queue, nullptr); +} + +TEST(PipelineCompileQueueVulkanTest, PostJobDoesNothingWithNullClosure) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + queue->PostJob(nullptr); +} + +TEST(PipelineCompileQueueVulkanTest, OnJobAddedProcessesJobsInParallel) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic concurrent_jobs{0}; + std::atomic max_concurrent{0}; + fml::CountDownLatch latch(3); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + queue->PostJobForDescriptor(desc1, [&]() { + int current = ++concurrent_jobs; + int prev_max = max_concurrent.load(); + while (current > prev_max && + !max_concurrent.compare_exchange_weak(prev_max, current)) { + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + concurrent_jobs--; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + int current = ++concurrent_jobs; + int prev_max = max_concurrent.load(); + while (current > prev_max && + !max_concurrent.compare_exchange_weak(prev_max, current)) { + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + concurrent_jobs--; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + int current = ++concurrent_jobs; + int prev_max = max_concurrent.load(); + while (current > prev_max && + !max_concurrent.compare_exchange_weak(prev_max, current)) { + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + concurrent_jobs--; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_GE(max_concurrent.load(), 1); +} + +TEST(PipelineCompileQueueVulkanTest, + PostJobForDescriptorWithDuplicateRunsEagerly) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic first_job_count{0}; + std::atomic second_job_count{0}; + fml::CountDownLatch latch(2); + + PipelineDescriptor desc; + + queue->PostJobForDescriptor(desc, [&]() { + first_job_count++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc, [&]() { + second_job_count++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(first_job_count, 1); + EXPECT_EQ(second_job_count, 1); +} + +TEST(PipelineCompileQueueVulkanTest, MultipleJobsCompleteSuccessfully) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic completed_jobs{0}; + fml::CountDownLatch latch(5); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + PipelineDescriptor desc4; + desc4.SetSampleCount(SampleCount::kCount4); + desc4.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc5; + desc5.SetSampleCount(SampleCount::kCount4); + desc5.SetCullMode(CullMode::kFrontFace); + + // Post 5 jobs with distinct descriptors + queue->PostJobForDescriptor(desc1, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc4, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc5, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(completed_jobs, 5); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index 124e11e177346..da10b9be100b8 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -9,16 +9,6 @@ namespace impeller { -std::shared_ptr PipelineCompileQueue::Create( - std::shared_ptr worker_task_runner) { - return std::shared_ptr( - new PipelineCompileQueue(std::move(worker_task_runner))); -} - -PipelineCompileQueue::PipelineCompileQueue( - std::shared_ptr worker_task_runner) - : worker_task_runner_(std::move(worker_task_runner)) {} - PipelineCompileQueue::~PipelineCompileQueue() { FinishAllJobs(); } @@ -29,31 +19,33 @@ bool PipelineCompileQueue::PostJobForDescriptor(const PipelineDescriptor& desc, return false; } - { - Lock lock(pending_jobs_mutex_); - auto insertion_result = pending_jobs_.insert(std::make_pair(desc, job)); - if (!insertion_result.second) { - // This bit is being extremely conservative. If insertion did not take - // place, someone gave the compile queue a job for the same description. - // This is highly unusual but technically not impossible. Just run the job - // eagerly. - FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " + if (!AddJob(desc, job)) { + // This bit is being extremely conservative. If insertion did not take + // place, someone gave the compile queue a job for the same description. + // This is highly unusual but technically not impossible. Just run the job + // eagerly. + FML_LOG(WARNING) << "Got multiple compile jobs for the same descriptor. " "Running eagerly."; - // Don't invoke the job here has there are we have currently acquired a - // mutex. - worker_task_runner_->PostTask(job); - return true; - } + PostJob(job); + return true; } - worker_task_runner_->PostTask([weak_queue = weak_from_this()]() { - if (auto queue = weak_queue.lock()) { - queue->DoOneJob(); - } - }); + OnJobAdded(); return true; } +bool PipelineCompileQueue::AddJob(const PipelineDescriptor& desc, + const fml::closure& job) { + Lock lock(pending_jobs_mutex_); + auto insertion_result = pending_jobs_.insert(std::make_pair(desc, job)); + return insertion_result.second; +} + +bool PipelineCompileQueue::HasPendingJobs() { + Lock lock(pending_jobs_mutex_); + return !pending_jobs_.empty(); +} + fml::closure PipelineCompileQueue::TakeNextJob() { Lock lock(pending_jobs_mutex_); if (pending_jobs_.empty()) { diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index d166679b3798f..901ac76977431 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -39,13 +39,12 @@ namespace impeller { /// entirely optional. The queue skipping mechanism all assume the /// optional availability of a compile queue. /// -class PipelineCompileQueue final +class PipelineCompileQueue : public std::enable_shared_from_this { public: - static std::shared_ptr Create( - std::shared_ptr worker_task_runner); + PipelineCompileQueue() = default; - ~PipelineCompileQueue(); + virtual ~PipelineCompileQueue(); PipelineCompileQueue(const PipelineCompileQueue&) = delete; @@ -72,26 +71,68 @@ class PipelineCompileQueue final /// void PerformJobEagerly(const PipelineDescriptor& desc); + protected: + //---------------------------------------------------------------------------- + /// @brief Post a compilation job to the worker task runner. + /// + /// This is a pure virtual function that must be implemented by + /// subclasses. It is responsible for actually dispatching the + /// job closure to the appropriate task runner for execution. + /// + /// @param[in] job The compilation job closure to post + /// + virtual void PostJob(const fml::closure& job) = 0; + + //---------------------------------------------------------------------------- + /// @brief Called by PostJobForDescriptor after a job has been + /// successfully added to the queue. Subclasses must implement + /// this to define their scheduling strategy. + /// + /// The default implementation for duplicate descriptors is to + /// run the job eagerly. Subclasses can override this behavior + /// by checking for duplicates before calling the base class. + /// + virtual void OnJobAdded() = 0; + + //---------------------------------------------------------------------------- + /// @brief Execute one pending compilation job from the queue. + /// + /// This method retrieves and executes a single job from the + /// pending jobs queue. It is typically called by subclasses + /// when they are ready to process the next job in the queue. + /// + void DoOneJob(); + + //---------------------------------------------------------------------------- + /// @brief Add a compilation job to the pending queue for the specified + /// descriptor. + /// + /// @param[in] desc The pipeline descriptor that identifies the job + /// @param[in] job The compilation job closure to add + /// + /// @return True if the job was successfully added to the queue, false + /// if a job for this descriptor already exists. + /// + bool AddJob(const PipelineDescriptor& desc, const fml::closure& job); + + //---------------------------------------------------------------------------- + /// @brief Check if there are any pending compilation jobs in the queue. + /// + /// @return True if there are pending jobs waiting to be processed, + /// false otherwise. + /// + bool HasPendingJobs(); + private: - std::shared_ptr worker_task_runner_; Mutex pending_jobs_mutex_; - size_t priorities_elevated_ = {}; - std::unordered_map, ComparableEqual> pending_jobs_ IPLR_GUARDED_BY(pending_jobs_mutex_); - - explicit PipelineCompileQueue( - std::shared_ptr worker_task_runner); - + size_t priorities_elevated_ = {}; fml::closure TakeJob(const PipelineDescriptor& desc); - fml::closure TakeNextJob(); - - void DoOneJob(); - void FinishAllJobs(); }; diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc new file mode 100644 index 0000000000000..14be51dcd1556 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/pipeline_compile_queue.h" + +#include + +#include "flutter/fml/closure.h" +#include "flutter/testing/testing.h" + +namespace impeller { +namespace testing { + +class TestPipelineCompileQueue : public PipelineCompileQueue { + public: + void PostJob(const fml::closure& job) override { + if (job) { + job(); + } + } + + void OnJobAdded() override {} + + bool AddJobForTest(const PipelineDescriptor& desc, const fml::closure& job) { + return AddJob(desc, job); + } + + bool HasPendingJobsForTest() { return HasPendingJobs(); } +}; + +TEST(PipelineCompileQueueTest, AddJobReturnsTrueForNewDescriptor) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + bool job_executed = false; + fml::closure job = [&job_executed]() { job_executed = true; }; + + bool result = queue.AddJobForTest(desc, job); + EXPECT_TRUE(result); +} + +TEST(PipelineCompileQueueTest, AddJobReturnsFalseForDuplicateDescriptor) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + bool job1_executed = false; + bool job2_executed = false; + fml::closure job1 = [&job1_executed]() { job1_executed = true; }; + fml::closure job2 = [&job2_executed]() { job2_executed = true; }; + + bool result1 = queue.AddJobForTest(desc, job1); + bool result2 = queue.AddJobForTest(desc, job2); + + EXPECT_TRUE(result1); + EXPECT_FALSE(result2); +} + +TEST(PipelineCompileQueueTest, HasPendingJobsReturnsCorrectState) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + fml::closure job = []() {}; + + EXPECT_FALSE(queue.HasPendingJobsForTest()); + + queue.AddJobForTest(desc, job); + EXPECT_TRUE(queue.HasPendingJobsForTest()); +} + +TEST(PipelineCompileQueueTest, PerformJobEagerlyExecutesJob) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + bool job_executed = false; + fml::closure job = [&job_executed]() { job_executed = true; }; + + queue.AddJobForTest(desc, job); + queue.PerformJobEagerly(desc); + + EXPECT_TRUE(job_executed); + EXPECT_FALSE(queue.HasPendingJobsForTest()); +} + +TEST(PipelineCompileQueueTest, FinishAllJobsDrainsQueue) { + auto queue = std::make_shared(); + PipelineDescriptor desc; + bool job_executed = false; + fml::closure job = [&job_executed]() { job_executed = true; }; + + queue->AddJobForTest(desc, job); + EXPECT_TRUE(queue->HasPendingJobsForTest()); + + queue.reset(); + + EXPECT_TRUE(job_executed); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc b/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc index 5ee5e7b18a935..c7da40ba45d1b 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc +++ b/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc @@ -61,7 +61,8 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( return; } impeller_context_ = impeller::ContextGLES::Create( - impeller::Flags{}, std::move(gl), ShaderLibraryMappings(), true); + impeller::Flags{}, std::move(gl), ShaderLibraryMappings(), true, + task_runners.GetIOTaskRunner()); } } diff --git a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc index 76d54efec8466..66898fc3e9330 100644 --- a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc @@ -139,9 +139,11 @@ GetActualRenderingAPIForImpeller( } // namespace AndroidContextDynamicImpeller::AndroidContextDynamicImpeller( - const AndroidContext::ContextSettings& settings) + const AndroidContext::ContextSettings& settings, + fml::RefPtr io_task_runner) : AndroidContext(AndroidRenderingAPI::kImpellerVulkan), - settings_(settings) {} + settings_(settings), + io_task_runner_(std::move(io_task_runner)) {} AndroidContextDynamicImpeller::~AndroidContextDynamicImpeller() = default; @@ -185,7 +187,7 @@ void AndroidContextDynamicImpeller::SetupImpellerContext() { if (!vk_context_) { gl_context_ = std::make_shared( std::make_unique(), - settings_.enable_gpu_tracing); + settings_.enable_gpu_tracing, io_task_runner_); } } diff --git a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h index 4de037bfa77ba..b6e59b65d0dae 100644 --- a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h +++ b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h @@ -23,7 +23,8 @@ namespace flutter { class AndroidContextDynamicImpeller : public AndroidContext { public: explicit AndroidContextDynamicImpeller( - const AndroidContext::ContextSettings& settings); + const AndroidContext::ContextSettings& settings, + fml::RefPtr io_task_runner); ~AndroidContextDynamicImpeller(); @@ -51,6 +52,7 @@ class AndroidContextDynamicImpeller : public AndroidContext { const AndroidContext::ContextSettings settings_; std::shared_ptr gl_context_; std::shared_ptr vk_context_; + fml::RefPtr io_task_runner_; FML_DISALLOW_COPY_AND_ASSIGN(AndroidContextDynamicImpeller); }; diff --git a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc index 79c0ec66bd25b..ce7bb0bf23eba 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc @@ -51,7 +51,8 @@ class AndroidContextGLImpeller::ReactorWorker final static std::shared_ptr CreateImpellerContext( const std::shared_ptr& worker, - bool enable_gpu_tracing) { + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) { auto proc_table = std::make_unique( impeller::egl::CreateProcAddressResolver()); @@ -85,11 +86,11 @@ static std::shared_ptr CreateImpellerContext( auto context = impeller::ContextGLES::Create( impeller::Flags{}, std::move(proc_table), is_gles3 ? gles3_shader_mappings : gles2_shader_mappings, - enable_gpu_tracing); + enable_gpu_tracing, io_task_runner); #else - auto context = - impeller::ContextGLES::Create(impeller::Flags{}, std::move(proc_table), - gles2_shader_mappings, enable_gpu_tracing); + auto context = impeller::ContextGLES::Create( + impeller::Flags{}, std::move(proc_table), gles2_shader_mappings, + enable_gpu_tracing, io_task_runner); #endif // !SLIMPELLER if (!context) { @@ -107,10 +108,12 @@ static std::shared_ptr CreateImpellerContext( AndroidContextGLImpeller::AndroidContextGLImpeller( std::unique_ptr display, - bool enable_gpu_tracing) + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) : AndroidContext(AndroidRenderingAPI::kImpellerOpenGLES), reactor_worker_(std::shared_ptr(new ReactorWorker())), - display_(std::move(display)) { + display_(std::move(display)), + io_task_runner_(std::move(io_task_runner)) { if (!display_ || !display_->IsValid()) { FML_LOG(ERROR) << "Could not create context with invalid EGL display."; return; @@ -174,8 +177,8 @@ AndroidContextGLImpeller::AndroidContextGLImpeller( return; } - auto impeller_context = - CreateImpellerContext(reactor_worker_, enable_gpu_tracing); + auto impeller_context = CreateImpellerContext( + reactor_worker_, enable_gpu_tracing, io_task_runner_); if (!impeller_context) { FML_LOG(ERROR) << "Could not create Impeller context."; diff --git a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h index e6d68d8444e38..792c8236bb106 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h +++ b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_CONTEXT_GL_IMPELLER_H_ #include "flutter/fml/macros.h" +#include "flutter/fml/task_runner.h" #include "flutter/impeller/toolkit/egl/display.h" #include "flutter/shell/platform/android/context/android_context.h" @@ -14,7 +15,8 @@ namespace flutter { class AndroidContextGLImpeller : public AndroidContext { public: AndroidContextGLImpeller(std::unique_ptr display, - bool enable_gpu_tracing); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner); ~AndroidContextGLImpeller(); @@ -42,6 +44,7 @@ class AndroidContextGLImpeller : public AndroidContext { std::unique_ptr onscreen_context_; std::unique_ptr offscreen_context_; bool is_valid_ = false; + fml::RefPtr io_task_runner_; FML_DISALLOW_COPY_AND_ASSIGN(AndroidContextGLImpeller); }; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.cc b/engine/src/flutter/shell/platform/android/platform_view_android.cc index 02ed4df41e5c1..466c4d916edcc 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -108,7 +108,8 @@ static std::shared_ptr CreateAndroidContext( const flutter::TaskRunners& task_runners, AndroidRenderingAPI android_rendering_api, bool enable_opengl_gpu_tracing, - const AndroidContext::ContextSettings& settings) { + const AndroidContext::ContextSettings& settings, + const fml::RefPtr& io_task_runner) { switch (android_rendering_api) { #if !SLIMPELLER case AndroidRenderingAPI::kSoftware: @@ -123,11 +124,12 @@ static std::shared_ptr CreateAndroidContext( return std::make_unique(settings); case AndroidRenderingAPI::kImpellerOpenGLES: return std::make_unique( - std::make_unique(), - enable_opengl_gpu_tracing); + std::make_unique(), enable_opengl_gpu_tracing, + io_task_runner); case AndroidRenderingAPI::kImpellerAutoselect: // Determine if we're using GL or Vulkan. - return std::make_unique(settings); + return std::make_unique(settings, + io_task_runner); } FML_UNREACHABLE(); } @@ -145,7 +147,8 @@ PlatformViewAndroid::PlatformViewAndroid( task_runners, rendering_api, delegate.OnPlatformViewGetSettings().enable_opengl_gpu_tracing, - CreateContextSettings(delegate.OnPlatformViewGetSettings()))) {} + CreateContextSettings(delegate.OnPlatformViewGetSettings()), + task_runners.GetIOTaskRunner())) {} PlatformViewAndroid::PlatformViewAndroid( PlatformView::Delegate& delegate, diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 190eb63969f04..41970ce55c6d6 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -492,7 +492,8 @@ InferOpenGLPlatformViewCreationCallback( shell.GetTaskRunners(), // task runners std::make_unique( gl_dispatch_table, fbo_reset_after_present, - view_embedder), // embedder_surface + view_embedder, // embedder_surface + shell.GetTaskRunners().GetIOTaskRunner()), // io_task_runner platform_dispatch_table, // embedder platform dispatch table view_embedder // external view embedder ); diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc index 3832a69e3f3fc..42494a5575ce9 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc @@ -64,7 +64,8 @@ class ReactorWorker final : public impeller::ReactorGLES::Worker { EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( EmbedderSurfaceGLSkia::GLDispatchTable gl_dispatch_table, bool fbo_reset_after_present, - std::shared_ptr external_view_embedder) + std::shared_ptr external_view_embedder, + fml::RefPtr io_task_runner) : gl_dispatch_table_(std::move(gl_dispatch_table)), fbo_reset_after_present_(fbo_reset_after_present), external_view_embedder_(std::move(external_view_embedder)), @@ -92,7 +93,7 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( impeller_context_ = impeller::ContextGLES::Create( impeller::Flags{}, std::move(gl), shader_mappings, - /*enable_gpu_tracing=*/false); + /*enable_gpu_tracing=*/false, std::move(io_task_runner)); if (!impeller_context_) { FML_LOG(ERROR) << "Could not create Impeller context."; diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h index 274e1ccb38f70..73db083573347 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h @@ -30,7 +30,8 @@ class EmbedderSurfaceGLImpeller final : public EmbedderSurface, EmbedderSurfaceGLImpeller( EmbedderSurfaceGLSkia::GLDispatchTable gl_dispatch_table, bool fbo_reset_after_present, - std::shared_ptr external_view_embedder); + std::shared_ptr external_view_embedder, + fml::RefPtr io_task_runner); ~EmbedderSurfaceGLImpeller() override;