diff --git a/.ci.yaml b/.ci.yaml index 484dabfa97843..1f7129b80bd3a 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -161,7 +161,6 @@ targets: - name: Linux Fuchsia recipe: engine/engine properties: - add_recipes_cq: "true" build_fuchsia: "true" fuchsia_ctl_version: version:0.0.27 # ensure files from pre-production Fuchsia SDK tests are purged from cache @@ -361,7 +360,7 @@ targets: {"download_emsdk": true} dependencies: >- [ - {"dependency": "chrome_and_driver", "version": "version:115.0"}, + {"dependency": "chrome_and_driver", "version": "version:119.0.6045.9"}, {"dependency": "curl", "version": "version:7.64.0"} ] framework: "true" diff --git a/.clang-tidy b/.clang-tidy index f6ee71473d917..0e68ce7e4b3c1 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,8 +1,6 @@ # A YAML format of https://clang.llvm.org/extra/clang-tidy/. # Prefix check with "-" to ignore. -# Note: Some of the checks here are used as errors selectively, see -# //ci/lint.sh Checks: >- bugprone-use-after-move, bugprone-unchecked-optional-access, @@ -41,3 +39,11 @@ CheckOptions: value: "lower_case" - key: readability-identifier-naming.PrivateMemberSuffix value: "_" + +# Lint headers within paths that contain "/flutter/" but not: +# - gen (generated code, the fact it compiles is good enough™) +# - third_party (we didn't author most of the code, and can't fix the lints) +# +# Note this is because of our buildroot setup, so the full path of a lint is: +# "../../flutter/impeller/core/runtime_types.h:1:1" as reported. +HeaderFilterRegex: "(!third_party/)(!gen/).*/flutter/.*" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5010e1b1b248a..9688ddae25af1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,7 +16,7 @@ updates: - dependency-name: "github/codeql-action" update-types: ["version-update:semver-minor"] - package-ecosystem: "pub" - directory: "/lib/web_ui/pubspec.yaml" + directory: "/lib/web_ui" schedule: interval: "daily" labels: diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 56a3e054e3dcb..f74e8d5c75f29 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -22,11 +22,11 @@ jobs: id-token: write steps: - name: "Checkout code" - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af + uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 with: results_file: results.sarif results_format: sarif diff --git a/.github/workflows/third_party_scan.yml b/.github/workflows/third_party_scan.yml index cab8acc162744..bd5c36e995806 100644 --- a/.github/workflows/third_party_scan.yml +++ b/.github/workflows/third_party_scan.yml @@ -25,11 +25,11 @@ jobs: id-token: write steps: - name: "Checkout code" - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 with: persist-credentials: false - name: "setup python" - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 with: python-version: '3.7.7' # install the python version needed - name: "extract and flatten deps" diff --git a/.gitignore b/.gitignore index c66ae6504bef8..fcd681e7bd9c5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,6 @@ pubspec.lock docs/doxygen/ xcuserdata -third_party/gn/ -third_party/ninja/ninja* - # Miscellaneous *.class *.lock @@ -133,3 +130,4 @@ app.*.symbols # Prebuilt binaries. /prebuilts/ +/build/secondary/third_party/protobuf diff --git a/DEPS b/DEPS index b3193f8abbdc6..325870f067a22 100644 --- a/DEPS +++ b/DEPS @@ -18,7 +18,7 @@ vars = { 'llvm_git': 'https://llvm.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e39cf360ea93daeb8e0997908a44f8d298730ce8', + 'skia_revision': '73e9879f06e74615f94e7a91827b591ceb388878', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. @@ -39,7 +39,7 @@ vars = { # The list of revisions for these tools comes from Fuchsia, here: # https://fuchsia.googlesource.com/integration/+/HEAD/toolchain # If there are problems with the toolchain, contact fuchsia-toolchain@. - 'clang_version': 'git_revision:576b184d6e3b633f51b908b61ebd281d2ecbf66f', + 'clang_version': 'git_revision:00396e6a1a0b79fda008cb4e86b616d7952b33c8', # The goma version and the clang version can be tightly coupled. If goma # stops working on a clang roll, this may need to be updated using the value @@ -57,28 +57,28 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1ee7ef8bbc6543b851f805027e1ca64f4e7909bd', + 'dart_revision': '901e92d1062758dece51a524b523df105328c47b', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py - 'dart_binaryen_rev': 'cdb7aeab40b4c522de20b242019f7e88641445d5', + 'dart_binaryen_rev': 'a51bd6df919a5b79574f0996a760cc20cb05697e', 'dart_boringssl_gen_rev': 'a468ba9fec3f59edf46a7db98caaca893e1e4d96', 'dart_boringssl_rev': '74646566e93de7551bfdfc5f49de7462f13d1d05', - 'dart_browser_launcher_rev': '1f69393d63a2f8d36d00b86cdd20df70c347af82', - 'dart_clock_rev': '1e75f08d3428bcd6f4b7cf70e788f24fc9b661e1', - 'dart_collection_rev': '91afde43f488eef618454b896301c6ff59af72e0', - 'dart_devtools_rev': 'adfb08c95c626248b28cc60df3b085c12ad8758e', + 'dart_browser_launcher_rev': 'c2871b2e03aa4490caf476c274996e2bc20c5451', + 'dart_clock_rev': '200a0209927ea7c4737309e7e9076ec8b97e9c4b', + 'dart_collection_rev': 'd27bfaf7994ee690be6ed424b8ee288c7aa672f6', + 'dart_devtools_rev': '11ec4ae1036408018143b58d80d6feadbee56a6c', 'dart_libprotobuf_rev': '24487dd1045c7f3d64a21f38a3f0c06cc4cf2edb', 'dart_perfetto_rev': 'b8da07095979310818f0efde2ef3c69ea70d62c5', 'dart_protobuf_gn_rev': 'ca669f79945418f6229e4fef89b666b2a88cbb10', - 'dart_protobuf_rev': 'c16bc891978a1764f0d6d8eca54f420242c78a6a', - 'dart_pub_rev': 'be6868ba132c782d9835b1638a634ecb73428579', + 'dart_protobuf_rev': 'c559fe52734ef6e2389e26ec3901eaf23fd76543', + 'dart_pub_rev': 'fca927ae2662204805e1646c0c0687369001a41a', 'dart_root_certificates_rev': '692f6d6488af68e0121317a9c2c9eb393eb0ee50', - 'dart_tools_rev': '2c8cbd63b96d94ef4fbc0c7df6fd5eff65fc9a85', - 'dart_watcher_rev': '1aed03e2a8005f45083fdb38cbd4b684cd23082f', - 'dart_webdev_rev': 'fc876cb0de59526160ed17efaa920557a6e2ba32', - 'dart_webkit_inspection_protocol_rev': '39a3c297ff573635e7936b015ce4f3466e4739d6', - 'dart_yaml_edit_rev': '4a9734dda12f63ef9eee4121f87ff4401e25a607', + 'dart_tools_rev': '15cc9c789359f13a637472817b64b42d827cd879', + 'dart_watcher_rev': '6ad58dcbbf328fbecf18d6ad2357c67be300b489', + 'dart_webdev_rev': '1bd434b66b58927c6aaf6e6fcbba075d86b82cfd', + 'dart_webkit_inspection_protocol_rev': '82f0c1c46dfdba5edf7c5fa84456233121dd69e1', + 'dart_yaml_edit_rev': 'a7e7fbad5ee263cc681681c1a6eb9e6df5336ad6', 'dart_zlib_rev': '14dd4c4455602c9b71a1a89b5cafd1f4030d2e3f', 'ocmock_rev': 'c4ec0e3a7a9f56cfdbd0aa01f4f97bb4b75c5ef8', # v3.7.1 @@ -125,7 +125,7 @@ vars = { # Upstream URLs for third party dependencies, used in # determining common ancestor commit for vulnerability scanning # prefixed with 'upstream_' in order to be identified by parsing tool. - # The vulnerabiity database being used in this scan can be browsed + # The vulnerability database being used in this scan can be browsed # using this UI https://osv.dev/list # If a new dependency needs to be added, the upstream (non-mirrored) # git URL for that dependency should be added to this list @@ -221,6 +221,7 @@ vars = { "upstream_stream_channel": "https://github.com/dart-lang/stream_channel.git", "upstream_string_scanner": "https://github.com/dart-lang/string_scanner.git", "upstream_SwiftShader": "https://swiftshader.googlesource.com/SwiftShader.git", + "upstream_tar": "https://github.com/simolus3/tar.git", "upstream_term_glyph": "https://github.com/dart-lang/term_glyph.git", "upstream_test_reflective_loader": "https://github.com/dart-lang/test_reflective_loader.git", "upstream_test": "https://github.com/dart-lang/test.git", @@ -261,13 +262,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '9780f253156165c515962f5f0c56235d34617689', - - # Fuchsia compatibility - # - # The dependencies in this section should match the layout in the Fuchsia gn - # build. Eventually, we'll manage these dependencies together with Fuchsia - # and not have to specific hashes. + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '5ced4367fee2721f6eda34802dc28b1335637c63', 'src/third_party/rapidjson': Var('fuchsia_git') + '/third_party/rapidjson' + '@' + 'ef3564c5c8824989393b87df25355baf35ff544b', @@ -281,20 +276,20 @@ deps = { 'src/third_party/libcxxabi': Var('llvm_git') + '/llvm-project/libcxxabi' + '@' + '2ce528fb5e0f92e57c97ec3ff53b75359d33af12', - 'src/third_party/glfw': + 'src/flutter/third_party/glfw': Var('fuchsia_git') + '/third_party/glfw' + '@' + 'dd8a678a66f1967372e5a5e3deac41ebf65ee127', 'src/third_party/shaderc': - Var('github_git') + '/google/shaderc.git' + '@' + '7ea834ecc59258a5c13c3d3e6fa0582bdde7c543', + Var('chromium_git') + '/external/github.com/google/shaderc' + '@' + '7ea834ecc59258a5c13c3d3e6fa0582bdde7c543', 'src/third_party/vulkan-deps': Var('chromium_git') + '/vulkan-deps' + '@' + '40b75117a60b11c42a1fb87bf14c0f49bcdb8b3d', 'src/third_party/flatbuffers': - Var('github_git') + '/google/flatbuffers.git' + '@' + '0a80646371179f8a7a5c1f42c31ee1d44dcf6709', + Var('chromium_git') + '/external/github.com/google/flatbuffers' + '@' + '0a80646371179f8a7a5c1f42c31ee1d44dcf6709', 'src/third_party/icu': - Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '985b9a6f70e13f3db741fed121e4dcc3046ad494', + Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'a622de35ac311c5ad390a7af80724634e5dc61ed', 'src/third_party/khronos': Var('chromium_git') + '/chromium/src/third_party/khronos.git' + '@' + '676d544d2b8f48903b7da9fceffaa534a5613978', @@ -303,13 +298,13 @@ deps = { Var('chromium_git') + '/external/github.com/google/gtest-parallel' + '@' + '38191e2733d7cbaeaef6a3f1a942ddeb38a2ad14', 'src/third_party/benchmark': - Var('github_git') + '/google/benchmark' + '@' + '431abd149fd76a072f821913c0340137cc755f36', + Var('chromium_git') + '/external/github.com/google/benchmark' + '@' + '431abd149fd76a072f821913c0340137cc755f36', 'src/third_party/googletest': - Var('github_git') + '/google/googletest' + '@' + '054a986a8513149e8374fc669a5fe40117ca6b41', + Var('chromium_git') + '/external/github.com/google/googletest' + '@' + '054a986a8513149e8374fc669a5fe40117ca6b41', 'src/third_party/boringssl': - Var('github_git') + '/dart-lang/boringssl_gen.git' + '@' + Var('dart_boringssl_gen_rev'), + Var('dart_git') + '/boringssl_gen.git' + '@' + Var('dart_boringssl_gen_rev'), 'src/third_party/yapf': Var('github_git') + '/google/yapf' + '@' + '212c5b5ad8e172d2d914ae454c121c89cccbcb35', @@ -324,7 +319,7 @@ deps = { 'src/third_party/protobuf': Var('fuchsia_git') + '/third_party/protobuf' + '@' + Var('dart_libprotobuf_rev'), - 'src/build/secondary/third_party/protobuf': + 'src/flutter/build/secondary/third_party/protobuf': Var('fuchsia_git') + '/protobuf-gn' + '@' + Var('dart_protobuf_gn_rev'), 'src/third_party/dart': @@ -333,28 +328,28 @@ deps = { # WARNING: Unused Dart dependencies in the list below till "WARNING:" marker are removed automatically - see create_updated_flutter_deps.py. 'src/third_party/dart/third_party/binaryen/src': - Var('chromium_git') + '/external/github.com/WebAssembly/binaryen.git@cdb7aeab40b4c522de20b242019f7e88641445d5', + Var('chromium_git') + '/external/github.com/WebAssembly/binaryen.git@a51bd6df919a5b79574f0996a760cc20cb05697e', 'src/third_party/dart/third_party/devtools': - {'dep_type': 'cipd', 'packages': [{'package': 'dart/third_party/flutter/devtools', 'version': 'git_revision:adfb08c95c626248b28cc60df3b085c12ad8758e'}]}, + {'dep_type': 'cipd', 'packages': [{'package': 'dart/third_party/flutter/devtools', 'version': 'git_revision:11ec4ae1036408018143b58d80d6feadbee56a6c'}]}, 'src/third_party/dart/third_party/pkg/args': - Var('dart_git') + '/args.git@5a4e16f1e4c08b01498a9dce8aeda1a60161cd52', + Var('dart_git') + '/args.git@df9b428e53e889835257c8475538e09834ffd022', 'src/third_party/dart/third_party/pkg/async': - Var('dart_git') + '/async.git@75efa6cc08b2fc906fac4b6fdfdbcf6da7d0a2da', + Var('dart_git') + '/async.git@def44823a35fc13312d3147cbbd5425a73e7e243', 'src/third_party/dart/third_party/pkg/bazel_worker': - Var('dart_git') + '/bazel_worker.git@159e67182044b2d5edd89d12a873487d1d1343c1', + Var('dart_git') + '/bazel_worker.git@b1b6a6605f0255eb1bf4aaf5aaf36f0d635e1b20', 'src/third_party/dart/third_party/pkg/boolean_selector': - Var('dart_git') + '/boolean_selector.git@f255921c7155da2167e8c96e04e527180787aafb', + Var('dart_git') + '/boolean_selector.git@479e1c110355a6c13f88922c9fdec353225fc825', 'src/third_party/dart/third_party/pkg/browser_launcher': Var('dart_git') + '/browser_launcher.git' + '@' + Var('dart_browser_launcher_rev'), 'src/third_party/dart/third_party/pkg/cli_util': - Var('dart_git') + '/cli_util.git@44118e35e55c75f84fbc5ead051424ee1e73e406', + Var('dart_git') + '/cli_util.git@56c1235ba516dbabb3e2b1d4fe76603630f9f5d1', 'src/third_party/dart/third_party/pkg/clock': Var('dart_git') + '/clock.git' + '@' + Var('dart_clock_rev'), @@ -363,79 +358,79 @@ deps = { Var('dart_git') + '/collection.git' + '@' + Var('dart_collection_rev'), 'src/third_party/dart/third_party/pkg/convert': - Var('dart_git') + '/convert.git@c058c8f4ebfdc09a5122db7988acd9e117a7da48', + Var('dart_git') + '/convert.git@03242b2058af45456e07a5648fe9b9ef40ca57d9', 'src/third_party/dart/third_party/pkg/crypto': - Var('dart_git') + '/crypto.git@1e26879c8f166850288e8722c590a465b4461f1f', + Var('dart_git') + '/crypto.git@36ead7c6f748448cde9149bc96292adb844c4601', 'src/third_party/dart/third_party/pkg/csslib': - Var('dart_git') + '/csslib.git@bd30a1a773ec66d3e435dfc53fc140f1967716da', + Var('dart_git') + '/csslib.git@f6b68dd9ed9da297f5df4cd31a39787bf35432b3', 'src/third_party/dart/third_party/pkg/dart_style': - Var('dart_git') + '/dart_style.git@2956b1a705953f880a5dae9d3a0969df0fc45e99', + Var('dart_git') + '/dart_style.git@1a2def95a3c04dafd27b85d17e6e828bd4afa1a3', 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@a32ba3a16baba6d4c785b0424a1cc3a0e83124ff', + Var('dart_git') + '/dartdoc.git@f7e9b17897e69dd5e43d3f0a785bb5b6e5c973ed', 'src/third_party/dart/third_party/pkg/ffi': - Var('dart_git') + '/ffi.git@d36e05af55293bcc511d6b3a99ea4b8cb69f6323', + Var('dart_git') + '/ffi.git@2faec288966d8f564049adb86a7ca43fd6e01fbf', 'src/third_party/dart/third_party/pkg/file': - Var('dart_git') + '/external/github.com/google/file.dart@a18ad1ce88eaeb5a11a13ef8fc25d1e78b546c59', + Var('dart_git') + '/external/github.com/google/file.dart@7418131cfe3c5e063166bc3d7cca98985a6d8eeb', 'src/third_party/dart/third_party/pkg/fixnum': - Var('dart_git') + '/fixnum.git@87ed0658f32f992dc7360b77513eadfa7056aa9d', + Var('dart_git') + '/fixnum.git@ef45eb556524eadcd72ecdbbed87951288bcd9e7', 'src/third_party/dart/third_party/pkg/glob': - Var('dart_git') + '/glob.git@9c1996f9f9326d776fe151f292912113b8b64aa3', + Var('dart_git') + '/glob.git@00465333cc4110e077cb256b4fa7eff4797bc856', 'src/third_party/dart/third_party/pkg/html': - Var('dart_git') + '/html.git@a1b193e95f13c995e7f7200ce0d363de5952e383', + Var('dart_git') + '/html.git@49e2c8e9b3bc9fcf25a8eb290c026d3c94c5d175', 'src/third_party/dart/third_party/pkg/http': - Var('dart_git') + '/http.git@7fb6fd686f6ce60af300be71a84defcc336d272e', + Var('dart_git') + '/http.git@7240d0a26c17f06f5e6d704082ea95d10a7a730d', 'src/third_party/dart/third_party/pkg/http_multi_server': - Var('dart_git') + '/http_multi_server.git@9d62ea396d7d282592edf994378f67fcde982ce8', + Var('dart_git') + '/http_multi_server.git@03041aabc9ffa4c730c4221bf6ff1ef8bcd27cef', 'src/third_party/dart/third_party/pkg/http_parser': - Var('dart_git') + '/http_parser.git@d2d03e7dfa3b7a99515b16f827650d6e210799b5', + Var('dart_git') + '/http_parser.git@c557f570fd53fed11914fe98c9dc20872e6eeca6', 'src/third_party/dart/third_party/pkg/intl': Var('dart_git') + '/intl.git@5d65e3808ce40e6282e40881492607df4e35669f', 'src/third_party/dart/third_party/pkg/json_rpc_2': - Var('dart_git') + '/json_rpc_2.git@50a37863be221f43ef07533c0c154ae676fc5df0', + Var('dart_git') + '/json_rpc_2.git@0521afb58b9aeb90beda8fa5b00b98b998ec9ba6', 'src/third_party/dart/third_party/pkg/leak_tracker': Var('dart_git') + '/leak_tracker.git@098bafcf99a5220e3c352d895d991e163568ee03', 'src/third_party/dart/third_party/pkg/logging': - Var('dart_git') + '/logging.git@bcaad0f781a889d6e5cf8fc564fd0722c446b96e', + Var('dart_git') + '/logging.git@642ed2124f7ef7abc819a0e22ae0c7afdb5398d3', 'src/third_party/dart/third_party/pkg/markdown': - Var('dart_git') + '/markdown.git@6cfd6f17651a8ba31b5a268f1139bb2c039dd4d4', + Var('dart_git') + '/markdown.git@4e2e9701d87058311857d06fd7f5df54e8f86c53', 'src/third_party/dart/third_party/pkg/matcher': - Var('dart_git') + '/matcher.git@80910d6698576ba486ace6e5fdf0e27324f138db', + Var('dart_git') + '/matcher.git@356e5f68d3484d44b9ef3b814ed95f9de17c7afd', 'src/third_party/dart/third_party/pkg/mime': - Var('dart_git') + '/mime.git@37ef637c35896e289fdd37c0ea4680df4ab9f543', + Var('dart_git') + '/mime.git@af3e5fe753b957e95f03838f8a63782582c413ca', 'src/third_party/dart/third_party/pkg/mockito': - Var('dart_git') + '/mockito.git@412c0beb51a12ed4a8833db7f542558ab92c0c65', + Var('dart_git') + '/mockito.git@4edf86ffe358462eafcb94fa3cbcb578d7cc6c6b', 'src/third_party/dart/third_party/pkg/native': - Var('dart_git') + '/native.git@a2dfedc35960711eb24cb04dcb906793d2222295', + Var('dart_git') + '/native.git@757f5034bb3cb9cf9db1b268fccea028d6b0f6a6', 'src/third_party/dart/third_party/pkg/package_config': - Var('dart_git') + '/package_config.git@ae7ad83de97aba507fd05e97cc372bc6695c1759', + Var('dart_git') + '/package_config.git@100533d2f836583f281c9dfa11a00d6842c176d4', 'src/third_party/dart/third_party/pkg/path': - Var('dart_git') + '/path.git@96d9183ad4f9e48109fa8d4b8269cf75f13922dd', + Var('dart_git') + '/path.git@4ca27d4e88d47f2d96c3113940a97321b6aa7175', 'src/third_party/dart/third_party/pkg/pool': - Var('dart_git') + '/pool.git@a5bee3540a2b5b3a3c34038667e7cd7bb514dc62', + Var('dart_git') + '/pool.git@5ccef15fcd4690d96e22e60c3962f4c97d9430f9', 'src/third_party/dart/third_party/pkg/protobuf': Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_rev'), @@ -444,55 +439,58 @@ deps = { Var('dart_git') + '/pub.git' + '@' + Var('dart_pub_rev'), 'src/third_party/dart/third_party/pkg/pub_semver': - Var('dart_git') + '/pub_semver.git@f0be74a446f971db478e68b59ea62e393d6df3bd', + Var('dart_git') + '/pub_semver.git@8e5a58fd4231854b35ac585ff81c242885334843', 'src/third_party/dart/third_party/pkg/shelf': - Var('dart_git') + '/shelf.git@2926f76dc0f713b46ba5d9dbc391e29c6d1521a9', + Var('dart_git') + '/shelf.git@c15fc6f6ae11079d7796b0bf8c93135a5a112d82', 'src/third_party/dart/third_party/pkg/source_map_stack_trace': - Var('dart_git') + '/source_map_stack_trace.git@196d7bfa58ef307687907c323ab8e5fb1f382afa', + Var('dart_git') + '/source_map_stack_trace.git@73d449cb90f9faf3ccacde0635f55230c6060024', 'src/third_party/dart/third_party/pkg/source_maps': - Var('dart_git') + '/source_maps.git@eb3d40a6193adc63da958ed9451e3218bd6e95a0', + Var('dart_git') + '/source_maps.git@fc6aa16cc3548dec5642057a7fbbce01d64f4a19', 'src/third_party/dart/third_party/pkg/source_span': - Var('dart_git') + '/source_span.git@48d0f574ee0a92a241c865d47f461803a664b5ba', + Var('dart_git') + '/source_span.git@92e50bf0c15bea00218e5fdb2881d2570de1932b', 'src/third_party/dart/third_party/pkg/sse': - Var('dart_git') + '/sse.git@eeb2588ce56a5b2f1e4bbd88c2b35c910c505b71', + Var('dart_git') + '/sse.git@37df57d7d09503c12f6200122ab0cb7e92ad50d1', 'src/third_party/dart/third_party/pkg/stack_trace': - Var('dart_git') + '/stack_trace.git@bcf2a0b1b7d4abaeedcb8b18ff41e4994aea1b17', + Var('dart_git') + '/stack_trace.git@634589f915f7b236dba8aca0f581cf792e5a6e03', 'src/third_party/dart/third_party/pkg/stream_channel': - Var('dart_git') + '/stream_channel.git@0ce7ab69c3a2ab83cdeb9dc60e1bacbb83abc165', + Var('dart_git') + '/stream_channel.git@ffdb20840d05a276699b50fdfc70cf668bfed6e2', 'src/third_party/dart/third_party/pkg/string_scanner': - Var('dart_git') + '/string_scanner.git@da9142cf9809e7e1364144b8193ec60d87f0a4b8', + Var('dart_git') + '/string_scanner.git@9c525f78fbc4189ee4dc3171a5c79e925b58f58b', + + 'src/third_party/dart/third_party/pkg/tar': + Var('dart_git') + '/external/github.com/simolus3/tar.git@748f6e680206752cc8e7a3c30af78a86da9830bd', 'src/third_party/dart/third_party/pkg/term_glyph': - Var('dart_git') + '/term_glyph.git@1b28285a7e818b8e87c4d2119d968c5b36d73c7a', + Var('dart_git') + '/term_glyph.git@cff80de129b2e69c11e3a9d7c6ea0447fc37865b', 'src/third_party/dart/third_party/pkg/test': - Var('dart_git') + '/test.git@27dcae11f6630c0d980f521cf372e962d286e9d3', + Var('dart_git') + '/test.git@4341470a2b844cd9a6692647639d652f617dd302', 'src/third_party/dart/third_party/pkg/test_reflective_loader': - Var('dart_git') + '/test_reflective_loader.git@45c57d62fb08471681cd0b0a1c3b131bf0122929', + Var('dart_git') + '/test_reflective_loader.git@8593eb160f796179f77c8edb6fde050433810211', 'src/third_party/dart/third_party/pkg/tools': Var('dart_git') + '/tools.git' + '@' + Var('dart_tools_rev'), 'src/third_party/dart/third_party/pkg/typed_data': - Var('dart_git') + '/typed_data.git@80e8943524a627f7ff421ace824f38105983e89a', + Var('dart_git') + '/typed_data.git@d1c15ed29d10568cd713fba77d01c4d79b03ccf8', 'src/third_party/dart/third_party/pkg/usage': - Var('dart_git') + '/usage.git@7b12d510b5abde8a216437b8430ccfd02273625c', + Var('dart_git') + '/usage.git@d7d2964433f26b9a3c60dc9c6677f00c005ee9fb', 'src/third_party/dart/third_party/pkg/watcher': Var('dart_git') + '/watcher.git' + '@' + Var('dart_watcher_rev'), 'src/third_party/dart/third_party/pkg/web_socket_channel': - Var('dart_git') + '/web_socket_channel.git@af945f1ad3ac4193ed70b4ebfbdcba3b9f0198bc', + Var('dart_git') + '/web_socket_channel.git@f3ac1bf2bd3c93eb6d5d78646ff7de31797f4cf6', 'src/third_party/dart/third_party/pkg/webdev': Var('dart_git') + '/webdev.git' + '@' + Var('dart_webdev_rev'), @@ -501,13 +499,13 @@ deps = { Var('dart_git') + '/external/github.com/google/webkit_inspection_protocol.dart.git' + '@' + Var('dart_webkit_inspection_protocol_rev'), 'src/third_party/dart/third_party/pkg/yaml': - Var('dart_git') + '/yaml.git@ae001879aa377afee2e70cf11b8716d6cc3e2658', + Var('dart_git') + '/yaml.git@9f0d64934c07bc27438074616455618b7103582d', 'src/third_party/dart/third_party/pkg/yaml_edit': Var('dart_git') + '/yaml_edit.git' + '@' + Var('dart_yaml_edit_rev'), 'src/third_party/dart/tools/sdks/dart-sdk': - {'dep_type': 'cipd', 'packages': [{'package': 'dart/dart-sdk/${{platform}}', 'version': 'version:3.1.0-298.0.dev'}]}, + {'dep_type': 'cipd', 'packages': [{'package': 'dart/dart-sdk/${{platform}}', 'version': 'version:3.2.0-150.0.dev'}]}, # WARNING: end of dart dependencies list that is cleaned up automatically - see create_updated_flutter_deps.py. @@ -628,10 +626,10 @@ deps = { Var('fuchsia_git') + '/third_party/pyyaml.git' + '@' + '25e97546488eee166b1abb229a27856cecd8b7ac', 'src/third_party/swiftshader': - Var('swiftshader_git') + '/SwiftShader.git' + '@' + '5f9ed9b16931c7155171d31f75004f73f0a3abc8', + Var('swiftshader_git') + '/SwiftShader.git' + '@' + '5b6f768198ce6a6485da31e1be06b5a17a2bf0a0', 'src/third_party/angle': - Var('chromium_git') + '/angle/angle.git' + '@' + '48e2c605adcd5bcc1622b18f357c7a73ebfb3543', + Var('chromium_git') + '/angle/angle.git' + '@' + '6a09e41ce6ea8c93524faae1a925eb01562f53b1', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator' + '@' + '7de5cc00de50e71a3aab22dea52fbb7ff4efceb6', @@ -650,7 +648,7 @@ deps = { Var('dart_git') + '/external/github.com/google/file.dart.git' + '@' + 'b2e31cb6ef40b223701dbfa0b907fe58468484d7', # 6.1.4 'src/third_party/pkg/flutter_packages': - Var('github_git') + '/flutter/packages.git' + '@' + '25454e63851fe7933f04d025606e68c1eac4fe0f', # various + Var('flutter_git') + '/mirrors/packages' + '@' + '25454e63851fe7933f04d025606e68c1eac4fe0f', # various 'src/third_party/pkg/gcloud': Var('github_git') + '/dart-lang/gcloud.git' + '@' + 'a5276b85c4714378e84b1fb478b8feeeb686ac26', # 0.8.6-dev @@ -659,21 +657,21 @@ deps = { Var('github_git') + '/google/googleapis.dart.git' + '@' + '526011f56d98eab183cc6075ee1392e8303e43e2', # various 'src/third_party/pkg/platform': - Var('github_git') + '/google/platform.dart.git' + '@' + '1ffad63428bbd1b3ecaa15926bacfb724023648c', # 3.1.0 + Var('dart_git') + '/platform.dart' + '@' + '1ffad63428bbd1b3ecaa15926bacfb724023648c', # 3.1.0 'src/third_party/pkg/process': - Var('github_git') + '/google/process.dart.git' + '@' + '0c9aeac86dcc4e3a6cf760b76fed507107e244d5', # 4.2.1 + Var('dart_git') + '/process.dart' + '@' + '0c9aeac86dcc4e3a6cf760b76fed507107e244d5', # 4.2.1 'src/third_party/pkg/process_runner': Var('github_git') + '/google/process_runner.git' + '@' + 'f24c69efdcaf109168f23d381fa281453d2bc9b1', # 4.1.2 'src/third_party/pkg/quiver': - Var('github_git') + '/google/quiver-dart.git' + '@' + '90b92bee895e507d435012356a8b5c5f17eafa52', # 3.2.1 + Var('chromium_git') + '/external/github.com/google/quiver-dart' + '@' + '90b92bee895e507d435012356a8b5c5f17eafa52', # 3.2.1 'src/third_party/pkg/vector_math': Var('dart_git') + '/external/github.com/google/vector_math.dart.git' + '@' + '0a5fd95449083d404df9768bc1b321b88a7d2eef', # 2.1.0 - 'src/third_party/imgui': + 'src/flutter/third_party/imgui': Var('github_git') + '/ocornut/imgui.git' + '@' + '3ea0fad204e994d669f79ed29dcaf61cd5cb571d', 'src/third_party/tinygltf': @@ -914,7 +912,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'gHhOd3d6V3Zv6CzK2s7BcyuC4jOjrn9t1vfN-QWP3nMC' + 'version': 'a3CcHQHJIAEr-O2R058jFagcmGvry9ngm-HgLq7RY_YC' } ], 'condition': 'host_os == "mac" and not download_fuchsia_sdk', @@ -924,7 +922,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'ftcEc93bjtc0q2a6skMN1MORORVVwcAJIW8gUyhP_Q4C' + 'version': 'uW96BsI9g7PF2on-4cgq0IQ0YqZXi1JaKSfvjMYMuSUC' } ], 'condition': 'host_os == "linux" and not download_fuchsia_sdk', @@ -932,7 +930,7 @@ deps = { }, 'src/third_party/impeller-cmake-example': { - 'url': Var('github_git') + '/bdero/impeller-cmake-example.git' + '@' + '1f606c40651029fd944236e5be64710a6ddb75de', + 'url': Var('github_git') + '/bdero/impeller-cmake-example.git' + '@' + 'ad78e80741349dda91c8a17fd16919655406e7d8', 'condition': 'download_impeller_cmake_example', }, @@ -952,7 +950,7 @@ deps = { 'packages': [ { 'package': 'flutter/flutter_font_fallbacks', - 'version': 'ba9a3d16939f9576afa67273198d779270cd768ae2867209ff3d72bab9acd3f6' + 'version': '14d38cffa1a2c9d4380094957aa63aee811b613b0dc53fd909557f61f6a9068c' } ], 'dep_type': 'cipd', @@ -983,17 +981,6 @@ hooks = [ 'pattern': '.', 'action': ['python3', 'src/build/vs_toolchain.py', 'update'], }, - { - # Ensure that we don't accidentally reference any .pyc files whose - # corresponding .py files have already been deleted. - 'name': 'remove_stale_pyc_files', - 'pattern': 'src/tools/.*\\.py', - 'action': [ - 'python3', - 'src/tools/remove_stale_pyc_files.py', - 'src/tools', - ], - }, { 'name': 'dia_dll', 'pattern': '.', diff --git a/analysis_options.yaml b/analysis_options.yaml index c33c15e544991..f4e9378bdf958 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,6 +7,7 @@ analyzer: language: strict-casts: true + strict-inference: true strict-raw-types: true errors: # allow self-reference to deprecated members (we do this because otherwise we have diff --git a/build/bin_to_obj.gni b/build/bin_to_obj.gni new file mode 100644 index 0000000000000..1885db6ba2663 --- /dev/null +++ b/build/bin_to_obj.gni @@ -0,0 +1,103 @@ +# 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. + +# Generates an assembly file defining a given symbol with the bytes from a +# binary file. Places the symbol in a text section if 'executable' is true, +# otherwise places the symbol in a read-only data section. +template("bin_to_assembly") { + assert(defined(invoker.deps), "Must define deps") + assert(defined(invoker.input), "Must define input binary file") + assert(defined(invoker.symbol), "Must define symbol name") + assert(defined(invoker.executable), "Must define boolean executable") + + action(target_name) { + deps = invoker.deps + script = "//third_party/dart/runtime/tools/bin_to_assembly.py" + output = "$target_gen_dir/${invoker.input}.S" + args = [ + "--input", + rebase_path(invoker.input), + "--output", + rebase_path(output), + "--symbol_name", + invoker.symbol, + "--target_os", + current_os, + ] + if (defined(invoker.size_symbol)) { + args += [ + "--size_symbol_name", + invoker.size_symbol, + "--target_arch", + current_cpu, + ] + } + if (invoker.executable) { + args += [ "--executable" ] + } + if (current_os != "win") { + args += [ "--incbin" ] + } + inputs = [ + script, + invoker.input, + ] + outputs = [ output ] + } +} + +# Generates an object file defining a given symbol with the bytes from a +# binary file. Places the symbol in the read-only data section. +template("bin_to_coff") { + assert(defined(invoker.deps), "Must define deps") + assert(defined(invoker.input), "Must define input binary file") + assert(defined(invoker.symbol), "Must define symbol name") + assert(defined(invoker.executable), "Must define executable") + + action(target_name) { + deps = invoker.deps + script = "//third_party/dart/runtime/tools/bin_to_coff.py" + output = "$target_gen_dir/${invoker.input}.o" + args = [ + "--input", + rebase_path(invoker.input), + "--output", + rebase_path(output), + "--symbol_name", + invoker.symbol, + ] + + if (defined(invoker.size_symbol)) { + args += [ + "--size_symbol_name", + invoker.size_symbol, + ] + } + + if (invoker.executable) { + args += [ "--executable" ] + } + + args += [ "--arch=$current_cpu" ] + inputs = [ invoker.input ] + outputs = [ output ] + } +} + +# Generates a linkable output file defining the specified symbol with the bytes +# from the binary file. Emits a COFF object file when targeting Windows, +# otherwise assembly. +template("bin_to_linkable") { + assert(defined(invoker.deps), "Must define deps") + assert(defined(invoker.input), "Must define input binary file") + assert(defined(invoker.symbol), "Must define symbol name") + target_type = "bin_to_assembly" + if (is_win) { + target_type = "bin_to_coff" + } + + target(target_type, target_name) { + forward_variables_from(invoker, "*") + } +} diff --git a/build/dart/rules.gni b/build/dart/rules.gni index 00b801b2c204f..55c1220df506f 100644 --- a/build/dart/rules.gni +++ b/build/dart/rules.gni @@ -10,21 +10,113 @@ import("//flutter/common/config.gni") import("//third_party/dart/build/dart/dart_action.gni") import("//third_party/dart/sdk_args.gni") -frontend_server_files = - exec_script("//third_party/dart/tools/list_dart_files.py", - [ - "absolute", - rebase_path("//flutter/flutter_frontend_server"), - ], - "list lines") - -frontend_server_files += - exec_script("//third_party/dart/tools/list_dart_files.py", - [ - "absolute", - rebase_path("//third_party/dart/pkg"), - ], - "list lines") +# Generates a Dart kernel snapshot using flutter_frontend_server. +# +# Arguments +# main_dart (required): +# The Dart entrypoint file. +# +# kernel_output (required): +# The path to the output kernel snapshot in the out directory. +# +# package_config (optional): +# The path to the package_config.json file. +# +# deps (optional): +# Additional dependencies. Dependencies on the frontend server and +# Flutter's platform.dill are included by default. This rule creates and +# uses a depfile, so listing all Dart sources is not necessary. +# +# extra_args (optional): +# Additional frontend server command line arguments. +template("flutter_frontend_server") { + assert(defined(invoker.main_dart), "The Dart test file must be specified.") + assert(defined(invoker.kernel_output), + "The Dart Kernel file location must be specified.") + + kernel_output = invoker.kernel_output + + common_deps = [ "//flutter/lib/snapshot:strong_platform" ] + if (defined(invoker.deps)) { + common_deps += invoker.deps + } + + extra_args = [] + if (defined(invoker.extra_args)) { + extra_args += invoker.extra_args + } + + packages_args = [] + if (defined(invoker.package_config)) { + packages_args += [ + "--packages", + rebase_path(invoker.package_config, root_build_dir), + ] + } + + snapshot_depfile = "$kernel_output.d" + + common_vm_args = [ "--disable-dart-dev" ] + + flutter_patched_sdk = + rebase_path("$root_out_dir/flutter_patched_sdk", root_build_dir) + + common_args = extra_args + packages_args + [ + "--sdk-root", + flutter_patched_sdk, + "--target=flutter", + "--depfile", + rebase_path(snapshot_depfile, root_build_dir), + "--output-dill", + rebase_path(invoker.kernel_output, root_build_dir), + rebase_path(invoker.main_dart, root_build_dir), + ] + + if (flutter_prebuilt_dart_sdk) { + common_deps += [ "//flutter/flutter_frontend_server:frontend_server" ] + action(target_name) { + forward_variables_from(invoker, + [ + "visibility", + "testonly", + ], + [ "pool" ]) + deps = common_deps + pool = "//flutter/build/dart:dart_pool" + script = "//build/gn_run_binary.py" + inputs = [ invoker.main_dart ] + outputs = [ invoker.kernel_output ] + depfile = snapshot_depfile + + ext = "" + if (is_win) { + ext = ".exe" + } + dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext", root_out_dir) + frontend_server = + rebase_path("$root_gen_dir/frontend_server.dart.snapshot") + + args = [ dart ] + common_vm_args + [ frontend_server ] + common_args + } + } else { + prebuilt_dart_action(target_name) { + forward_variables_from(invoker, + [ + "visibility", + "testonly", + ], + [ "pool" ]) + deps = common_deps + pool = "//flutter/build/dart:dart_pool" + script = "//third_party/dart/pkg/frontend_server/bin/frontend_server_starter.dart" + inputs = [ invoker.main_dart ] + outputs = [ invoker.kernel_output ] + depfile = snapshot_depfile + vm_args = common_vm_args + args = common_args + } + } +} # Creates a dart kernel (dill) file suitable for use with gen_snapshot, as well # as the app-jit, aot-elf, or aot-assembly snapshot for targeting Flutter on @@ -48,61 +140,38 @@ template("flutter_snapshot") { kernel_output = "$target_gen_dir/kernel_blob.bin" - prebuilt_dart_action(kernel_target) { - script = "//flutter/flutter_frontend_server/bin/starter.dart" - - main_dart = rebase_path(invoker.main_dart) - package_config = rebase_path(invoker.package_config) - flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") - - deps = [ "//flutter/lib/snapshot:strong_platform" ] - - inputs = [ - main_dart, - package_config, - ] + frontend_server_files - - outputs = [ kernel_output ] - - depfile = "$kernel_output.d" - abs_depfile = rebase_path(depfile) - vm_args = [ "--disable-dart-dev" ] - - args = [ - "--depfile=$abs_depfile", - "--packages=" + rebase_path(package_config), - "--target=flutter", - "--sdk-root=" + flutter_patched_sdk, - "--output-dill=" + rebase_path(kernel_output, root_build_dir), + extra_frontend_server_args = [] + if (is_aot) { + extra_frontend_server_args += [ + "--aot", + "--tfa", ] + } else { + # --no-link-platform is only valid when --aot isn't specified + extra_frontend_server_args += [ "--no-link-platform" ] + } - if (is_aot) { - args += [ - "--aot", - "--tfa", - ] - } else { - # --no-link-platform is only valid when --aot isn't specified - args += [ "--no-link-platform" ] - } - - if (defined(invoker.product) && invoker.product) { - # Setting this flag in a non-product release build for AOT (a "profile" - # build) causes the vm service isolate code to be tree-shaken from an app. - # See the pragma on the entrypoint here: - # - # https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/bin/vmservice_io.dart#L240 - # - # Also, this define excludes debugging and profiling code from Flutter. - args += [ "-Ddart.vm.product=true" ] - } else { - if (flutter_runtime_mode == "profile") { - # The following define excludes debugging code from Flutter. - args += [ "-Ddart.vm.profile=true" ] - } + if (defined(invoker.product) && invoker.product) { + # Setting this flag in a non-product release build for AOT (a "profile" + # build) causes the vm service isolate code to be tree-shaken from an app. + # See the pragma on the entrypoint here: + # + # https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/bin/vmservice_io.dart#L240 + # + # Also, this define excludes debugging and profiling code from Flutter. + extra_frontend_server_args += [ "-Ddart.vm.product=true" ] + } else { + if (flutter_runtime_mode == "profile") { + # The following define excludes debugging code from Flutter. + extra_frontend_server_args += [ "-Ddart.vm.profile=true" ] } + } - args += [ rebase_path(main_dart) ] + flutter_frontend_server(kernel_target) { + main_dart = invoker.main_dart + package_config = invoker.package_config + kernel_output = kernel_output + extra_args = extra_frontend_server_args } compiled_action(snapshot_target) { @@ -271,7 +340,7 @@ template("application_snapshot") { # Ensure the compiled appliation (e.g. frontend-server, ...) will use this # Dart SDK hash when consuming/producing kernel. # - # (Instead of ensuring every user of the "application_snapshot" passes it's + # (Instead of ensuring every user of the "application_snapshot" passes its # own) snapshot_vm_args += [ "-Dsdk_hash=$sdk_hash" ] diff --git a/build/secondary/flutter/third_party/glfw/BUILD.gn b/build/secondary/flutter/third_party/glfw/BUILD.gn new file mode 100644 index 0000000000000..98c3fe43e7eda --- /dev/null +++ b/build/secondary/flutter/third_party/glfw/BUILD.gn @@ -0,0 +1,125 @@ +# 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. + +import("glfw_args.gni") + +_checkout_dir = "//flutter/third_party/glfw" + +config("relative_glfw_headers") { + include_dirs = [ + "$_checkout_dir/include", + "$_checkout_dir/include/GLFW", + ] +} + +source_set("glfw") { + public = [ + "$_checkout_dir/include/GLFW/glfw3.h", + "$_checkout_dir/include/GLFW/glfw3native.h", + ] + + sources = [ + "$_checkout_dir/src/context.c", + "$_checkout_dir/src/egl_context.c", + "$_checkout_dir/src/init.c", + "$_checkout_dir/src/input.c", + "$_checkout_dir/src/monitor.c", + "$_checkout_dir/src/null_init.c", + "$_checkout_dir/src/null_joystick.c", + "$_checkout_dir/src/null_joystick.h", + "$_checkout_dir/src/null_monitor.c", + "$_checkout_dir/src/null_platform.h", + "$_checkout_dir/src/null_window.c", + "$_checkout_dir/src/osmesa_context.c", + "$_checkout_dir/src/platform.c", + "$_checkout_dir/src/vulkan.c", + "$_checkout_dir/src/window.c", + ] + + include_dirs = [ "$_checkout_dir/src" ] + + public_configs = [ ":relative_glfw_headers" ] + + if (is_win) { + sources += [ + "$_checkout_dir/src/wgl_context.c", + "$_checkout_dir/src/win32_init.c", + "$_checkout_dir/src/win32_joystick.c", + "$_checkout_dir/src/win32_joystick.h", + "$_checkout_dir/src/win32_module.c", + "$_checkout_dir/src/win32_monitor.c", + "$_checkout_dir/src/win32_platform.h", + "$_checkout_dir/src/win32_thread.c", + "$_checkout_dir/src/win32_time.c", + "$_checkout_dir/src/win32_window.c", + ] + + defines = [ "_GLFW_WIN32" ] + } else if (is_linux) { + sources += [ + "$_checkout_dir/src/glx_context.c", + "$_checkout_dir/src/linux_joystick.c", + "$_checkout_dir/src/linux_joystick.h", + "$_checkout_dir/src/posix_module.c", + "$_checkout_dir/src/posix_poll.c", + "$_checkout_dir/src/posix_poll.h", + "$_checkout_dir/src/posix_thread.c", + "$_checkout_dir/src/posix_thread.h", + "$_checkout_dir/src/posix_time.c", + "$_checkout_dir/src/posix_time.h", + "$_checkout_dir/src/x11_init.c", + "$_checkout_dir/src/x11_monitor.c", + "$_checkout_dir/src/x11_platform.h", + "$_checkout_dir/src/x11_window.c", + "$_checkout_dir/src/xkb_unicode.c", + "$_checkout_dir/src/xkb_unicode.h", + ] + + defines = [ + "_GLFW_X11", + "_GLFW_HAS_XF86VM", + ] + + libs = [ + "X11", + "Xcursor", + "Xinerama", + "Xrandr", + "Xxf86vm", + ] + } else if (is_mac) { + sources += [ + "$_checkout_dir/src/cocoa_init.m", + "$_checkout_dir/src/cocoa_joystick.h", + "$_checkout_dir/src/cocoa_joystick.m", + "$_checkout_dir/src/cocoa_monitor.m", + "$_checkout_dir/src/cocoa_platform.h", + "$_checkout_dir/src/cocoa_time.c", + "$_checkout_dir/src/cocoa_window.m", + "$_checkout_dir/src/nsgl_context.m", + "$_checkout_dir/src/posix_module.c", + "$_checkout_dir/src/posix_thread.c", + "$_checkout_dir/src/posix_thread.h", + ] + + defines = [ "_GLFW_COCOA" ] + + cflags = [ + "-Wno-deprecated-declarations", + "-Wno-objc-multiple-method-names", + ] + + frameworks = [ + "CoreVideo.framework", + "IOKit.framework", + ] + } + + if (glfw_vulkan_library != "") { + defines += [ "_GLFW_VULKAN_LIBRARY=" + glfw_vulkan_library ] + } + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} diff --git a/build/secondary/flutter/third_party/glfw/README b/build/secondary/flutter/third_party/glfw/README new file mode 100644 index 0000000000000..404ef3cae2eec --- /dev/null +++ b/build/secondary/flutter/third_party/glfw/README @@ -0,0 +1,13 @@ +Name: GLFW +License: zlib/libpng (BSD-like) +Upstream Git: https://github.com/glfw/glfw +Version: 3.2.1 +Description: + +GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and +Vulkan development on the desktop. + +To update: +- Advance the pinned hash in DEPS, which should map the repository to + //third_party/glfw +- Update BUILD.gn if necessary for changes. diff --git a/build/secondary/flutter/third_party/glfw/glfw_args.gni b/build/secondary/flutter/third_party/glfw/glfw_args.gni new file mode 100644 index 0000000000000..c5903ad2078c8 --- /dev/null +++ b/build/secondary/flutter/third_party/glfw/glfw_args.gni @@ -0,0 +1,7 @@ +# 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. + +declare_args() { + glfw_vulkan_library = "" +} diff --git a/build/secondary/flutter/third_party/imgui/BUILD.gn b/build/secondary/flutter/third_party/imgui/BUILD.gn new file mode 100644 index 0000000000000..9a93a12eaf719 --- /dev/null +++ b/build/secondary/flutter/third_party/imgui/BUILD.gn @@ -0,0 +1,48 @@ +# 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. + +source_root = "//flutter/third_party/imgui" + +source_set("imgui") { + public = [ + "$source_root/imgui.h", + "$source_root/imgui_internal.h", + "$source_root/imstb_rectpack.h", + "$source_root/imstb_textedit.h", + "$source_root/imstb_truetype.h", + ] + + include_dirs = [ "$source_root" ] + + sources = [ + "$source_root/imgui.cpp", + "$source_root/imgui.h", + "$source_root/imgui_demo.cpp", + "$source_root/imgui_draw.cpp", + "$source_root/imgui_internal.h", + "$source_root/imgui_tables.cpp", + "$source_root/imgui_widgets.cpp", + "$source_root/imstb_rectpack.h", + "$source_root/imstb_textedit.h", + "$source_root/imstb_truetype.h", + ] +} + +config("imgui_headers") { + include_dirs = [ "$source_root" ] +} + +source_set("imgui_glfw") { + public_deps = [ + ":imgui", + "//flutter/third_party/glfw", + ] + + public_configs = [ ":imgui_headers" ] + + sources = [ + "$source_root/backends/imgui_impl_glfw.cpp", + "$source_root/backends/imgui_impl_glfw.h", + ] +} diff --git a/build/secondary/testing/gmock/BUILD.gn b/build/secondary/testing/gmock/BUILD.gn new file mode 100644 index 0000000000000..ecf49f0ff7f6f --- /dev/null +++ b/build/secondary/testing/gmock/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("gmock_config") { + # Gmock headers need to be able to find themselves. + include_dirs = [ "include" ] +} + +static_library("gmock") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ + # Sources based on files in r173 of gmock. + "include/gmock/gmock-actions.h", + "include/gmock/gmock-cardinalities.h", + "include/gmock/gmock-generated-actions.h", + "include/gmock/gmock-generated-function-mockers.h", + "include/gmock/gmock-generated-matchers.h", + "include/gmock/gmock-generated-nice-strict.h", + "include/gmock/gmock-matchers.h", + "include/gmock/gmock-spec-builders.h", + "include/gmock/gmock.h", + "include/gmock/internal/gmock-generated-internal-utils.h", + "include/gmock/internal/gmock-internal-utils.h", + "include/gmock/internal/gmock-port.h", + + #"src/gmock-all.cc", # Not needed by our build. + "src/gmock-cardinalities.cc", + "src/gmock-internal-utils.cc", + "src/gmock-matchers.cc", + "src/gmock-spec-builders.cc", + "src/gmock.cc", + ] + + # This project includes some stuff form gtest's guts. + include_dirs = [ "../gtest/include" ] + + public_configs = [ + ":gmock_config", + "//testing/gtest:gtest_config", + ] +} + +static_library("gmock_main") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ "src/gmock_main.cc" ] + deps = [ ":gmock" ] +} diff --git a/build/secondary/testing/gtest/BUILD.gn b/build/secondary/testing/gtest/BUILD.gn new file mode 100644 index 0000000000000..dad1b7d697b32 --- /dev/null +++ b/build/secondary/testing/gtest/BUILD.gn @@ -0,0 +1,132 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("gtest_config") { + visibility = [ + ":*", + "//testing/gmock:*", # gmock also shares this config. + ] + + defines = [ + # In order to allow regex matches in gtest to be shared between Windows + # and other systems, we tell gtest to always use it's internal engine. + "GTEST_HAS_POSIX_RE=0", + + # Chrome doesn't support / require C++11, yet. + "GTEST_LANG_CXX11=0", + ] + + # Gtest headers need to be able to find themselves. + include_dirs = [ "include" ] + + if (is_win) { + cflags = [ "/wd4800" ] # Unused variable warning. + } + + if (is_posix) { + defines += [ + # gtest isn't able to figure out when RTTI is disabled for gcc + # versions older than 4.3.2, and assumes it's enabled. Our Mac + # and Linux builds disable RTTI, and cannot guarantee that the + # compiler will be 4.3.2. or newer. The Mac, for example, uses + # 4.2.1 as that is the latest available on that platform. gtest + # must be instructed that RTTI is disabled here, and for any + # direct dependents that might include gtest headers. + "GTEST_HAS_RTTI=0", + ] + } + + if (is_android) { + defines += [ + # We want gtest features that use tr1::tuple, but we currently + # don't support the variadic templates used by libstdc++'s + # implementation. gtest supports this scenario by providing its + # own implementation but we must opt in to it. + "GTEST_USE_OWN_TR1_TUPLE=1", + + # GTEST_USE_OWN_TR1_TUPLE only works if GTEST_HAS_TR1_TUPLE is set. + # gtest r625 made it so that GTEST_HAS_TR1_TUPLE is set to 0 + # automatically on android, so it has to be set explicitly here. + "GTEST_HAS_TR1_TUPLE=1", + ] + } +} + +config("gtest_direct_config") { + visibility = [ ":*" ] + defines = [ "UNIT_TEST" ] +} + +config("gtest_warnings") { + visibility = [ ":*" ] + if (is_win && is_clang) { + # The Mutex constructor initializer list in gtest-port.cc is incorrectly + # ordered. See + # https://groups.google.com/d/msg/googletestframework/S5uSV8L2TX8/U1FaTDa6J6sJ. + cflags = [ "-Wno-reorder" ] + } +} + +static_library("gtest") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ + "include/gtest/gtest-death-test.h", + "include/gtest/gtest-message.h", + "include/gtest/gtest-param-test.h", + "include/gtest/gtest-printers.h", + "include/gtest/gtest-spi.h", + "include/gtest/gtest-test-part.h", + "include/gtest/gtest-typed-test.h", + "include/gtest/gtest.h", + "include/gtest/gtest_pred_impl.h", + "include/gtest/internal/gtest-death-test-internal.h", + "include/gtest/internal/gtest-filepath.h", + "include/gtest/internal/gtest-internal.h", + "include/gtest/internal/gtest-linked_ptr.h", + "include/gtest/internal/gtest-param-util-generated.h", + "include/gtest/internal/gtest-param-util.h", + "include/gtest/internal/gtest-port.h", + "include/gtest/internal/gtest-string.h", + "include/gtest/internal/gtest-tuple.h", + "include/gtest/internal/gtest-type-util.h", + + #"gtest/src/gtest-all.cc", # Not needed by our build. + "../multiprocess_func_list.cc", + "../multiprocess_func_list.h", + "../platform_test.h", + "src/gtest-death-test.cc", + "src/gtest-filepath.cc", + "src/gtest-internal-inl.h", + "src/gtest-port.cc", + "src/gtest-printers.cc", + "src/gtest-test-part.cc", + "src/gtest-typed-test.cc", + "src/gtest.cc", + ] + + if (is_mac) { + sources += [ + "../gtest_mac.h", + "../gtest_mac.mm", + "../platform_test_mac.mm", + ] + } + + include_dirs = [ "." ] + + all_dependent_configs = [ ":gtest_config" ] + public_configs = [ ":gtest_direct_config" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + configs += [ ":gtest_warnings" ] +} + +source_set("gtest_main") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ "src/gtest_main.cc" ] + deps = [ ":gtest" ] +} diff --git a/build/secondary/testing/libfuzzer/fuzzer_test.gni b/build/secondary/testing/libfuzzer/fuzzer_test.gni new file mode 100644 index 0000000000000..8f8f3e750feec --- /dev/null +++ b/build/secondary/testing/libfuzzer/fuzzer_test.gni @@ -0,0 +1,11 @@ +# Copyright 2019 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. + +# This is a dummy implementation to satisfy the ANGLE build, using the no-op +# implementation from the real (Chromium) fuzzer_test.gni. +template("fuzzer_test") { + not_needed(invoker, "*") + group(target_name) { + } +} diff --git a/build/secondary/testing/test.gni b/build/secondary/testing/test.gni new file mode 100644 index 0000000000000..ff724df08962a --- /dev/null +++ b/build/secondary/testing/test.gni @@ -0,0 +1,6 @@ +# Copyright 2019 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. + +# This is a dummy file to satisfy the ANGLE build. Flutter's use of ANGLE +# doesn't actually require any of the real content. diff --git a/build/secondary/third_party/BUILD.gn b/build/secondary/third_party/BUILD.gn new file mode 100644 index 0000000000000..c44ca40e88832 --- /dev/null +++ b/build/secondary/third_party/BUILD.gn @@ -0,0 +1,9 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("fontconfig") { + if (is_linux) { + libs = [ "fontconfig" ] + } +} diff --git a/build/secondary/third_party/android_ndk/BUILD.gn b/build/secondary/third_party/android_ndk/BUILD.gn new file mode 100644 index 0000000000000..697d2873a5e39 --- /dev/null +++ b/build/secondary/third_party/android_ndk/BUILD.gn @@ -0,0 +1,8 @@ +# 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. + +source_set("cpu_features") { + public_configs = [ "//third_party/android_tools:cpu_features_include" ] + deps = [ "//third_party/android_tools:cpu_features" ] +} diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn new file mode 100644 index 0000000000000..574472afdb27a --- /dev/null +++ b/build/secondary/third_party/android_tools/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +config("cpu_features_include") { + include_dirs = [ "ndk/sources/android/cpufeatures" ] +} + +# This is the GN version of +# //build/android/ndk.gyp:cpu_features +source_set("cpu_features") { + sources = [ "ndk/sources/android/cpufeatures/cpu-features.c" ] + public_configs = [ ":cpu_features_include" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} diff --git a/build/secondary/third_party/benchmark/BUILD.gn b/build/secondary/third_party/benchmark/BUILD.gn new file mode 100644 index 0000000000000..f88d6bf0c6898 --- /dev/null +++ b/build/secondary/third_party/benchmark/BUILD.gn @@ -0,0 +1,60 @@ +# Copyright 2016 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +config("benchmark_config") { + visibility = [ ":*" ] + include_dirs = [ "include" ] +} +static_library("benchmark") { + testonly = true + + sources = [ + "//third_party/benchmark/src/arraysize.h", + "//third_party/benchmark/src/benchmark.cc", + "//third_party/benchmark/src/benchmark_api_internal.cc", + "//third_party/benchmark/src/benchmark_api_internal.h", + "//third_party/benchmark/src/benchmark_main.cc", + "//third_party/benchmark/src/benchmark_name.cc", + "//third_party/benchmark/src/benchmark_register.cc", + "//third_party/benchmark/src/benchmark_register.h", + "//third_party/benchmark/src/benchmark_runner.cc", + "//third_party/benchmark/src/benchmark_runner.h", + "//third_party/benchmark/src/check.h", + "//third_party/benchmark/src/colorprint.cc", + "//third_party/benchmark/src/colorprint.h", + "//third_party/benchmark/src/commandlineflags.cc", + "//third_party/benchmark/src/commandlineflags.h", + "//third_party/benchmark/src/complexity.cc", + "//third_party/benchmark/src/complexity.h", + "//third_party/benchmark/src/console_reporter.cc", + "//third_party/benchmark/src/counter.cc", + "//third_party/benchmark/src/counter.h", + "//third_party/benchmark/src/csv_reporter.cc", + "//third_party/benchmark/src/cycleclock.h", + "//third_party/benchmark/src/internal_macros.h", + "//third_party/benchmark/src/json_reporter.cc", + "//third_party/benchmark/src/log.h", + "//third_party/benchmark/src/mutex.h", + "//third_party/benchmark/src/perf_counters.cc", + "//third_party/benchmark/src/perf_counters.h", + "//third_party/benchmark/src/re.h", + "//third_party/benchmark/src/reporter.cc", + "//third_party/benchmark/src/sleep.cc", + "//third_party/benchmark/src/sleep.h", + "//third_party/benchmark/src/statistics.cc", + "//third_party/benchmark/src/statistics.h", + "//third_party/benchmark/src/string_util.cc", + "//third_party/benchmark/src/string_util.h", + "//third_party/benchmark/src/sysinfo.cc", + "//third_party/benchmark/src/sysinfo.h", + "//third_party/benchmark/src/thread_manager.h", + "//third_party/benchmark/src/thread_timer.h", + "//third_party/benchmark/src/timers.cc", + "//third_party/benchmark/src/timers.h", + ] + public_configs = [ ":benchmark_config" ] + defines = [ + "HAVE_STD_REGEX", + "HAVE_THREAD_SAFETY_ATTRIBUTES", + ] +} diff --git a/build/secondary/third_party/cpu-features/BUILD.gn b/build/secondary/third_party/cpu-features/BUILD.gn new file mode 100644 index 0000000000000..dadee56df8cff --- /dev/null +++ b/build/secondary/third_party/cpu-features/BUILD.gn @@ -0,0 +1,8 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("cpu-features") { + public_configs = [ "//third_party/android_tools:cpu_features_include" ] + deps = [ "//third_party/android_tools:cpu_features" ] +} diff --git a/build/secondary/third_party/dart/pkg/meta/BUILD.gn b/build/secondary/third_party/dart/pkg/meta/BUILD.gn new file mode 100644 index 0000000000000..3ad0e03f66bbe --- /dev/null +++ b/build/secondary/third_party/dart/pkg/meta/BUILD.gn @@ -0,0 +1,26 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("meta") { + package_name = "meta" + + language_version = "2.12" + + deps = [] + + sources = [ + "dart2js.dart", + "meta.dart", + "meta_meta.dart", + ] +} diff --git a/build/secondary/third_party/dart/third_party/pkg/args/BUILD.gn b/build/secondary/third_party/dart/third_party/pkg/args/BUILD.gn new file mode 100644 index 0000000000000..e25404362f6a1 --- /dev/null +++ b/build/secondary/third_party/dart/third_party/pkg/args/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("args") { + package_name = "args" + + language_version = "2.12" + + deps = [] + + sources = [ + "args.dart", + "command_runner.dart", + "src/allow_anything_parser.dart", + "src/arg_parser.dart", + "src/arg_parser_exception.dart", + "src/arg_results.dart", + "src/help_command.dart", + "src/option.dart", + "src/parser.dart", + "src/usage.dart", + "src/usage_exception.dart", + "src/utils.dart", + ] +} diff --git a/build/secondary/third_party/dart/third_party/pkg/collection/BUILD.gn b/build/secondary/third_party/dart/third_party/pkg/collection/BUILD.gn new file mode 100644 index 0000000000000..092624f4bed1a --- /dev/null +++ b/build/secondary/third_party/dart/third_party/pkg/collection/BUILD.gn @@ -0,0 +1,51 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("collection") { + package_name = "collection" + + language_version = "2.12" + + deps = [] + + sources = [ + "algorithms.dart", + "collection.dart", + "equality.dart", + "iterable_zip.dart", + "priority_queue.dart", + "src/algorithms.dart", + "src/canonicalized_map.dart", + "src/combined_wrappers/combined_iterable.dart", + "src/combined_wrappers/combined_iterator.dart", + "src/combined_wrappers/combined_list.dart", + "src/combined_wrappers/combined_map.dart", + "src/comparators.dart", + "src/empty_unmodifiable_set.dart", + "src/equality.dart", + "src/equality_map.dart", + "src/equality_set.dart", + "src/functions.dart", + "src/iterable_extensions.dart", + "src/iterable_zip.dart", + "src/list_extensions.dart", + "src/priority_queue.dart", + "src/queue_list.dart", + "src/union_set.dart", + "src/union_set_controller.dart", + "src/unmodifiable_wrappers.dart", + "src/utils.dart", + "src/wrappers.dart", + "wrappers.dart", + ] +} diff --git a/build/secondary/third_party/dart/third_party/pkg/logging/BUILD.gn b/build/secondary/third_party/dart/third_party/pkg/logging/BUILD.gn new file mode 100644 index 0000000000000..73baa07636161 --- /dev/null +++ b/build/secondary/third_party/dart/third_party/pkg/logging/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("logging") { + package_name = "logging" + + language_version = "2.12" + + deps = [] + + sources = [ + "logging.dart", + "src/level.dart", + "src/log_record.dart", + "src/logger.dart", + ] +} diff --git a/build/secondary/third_party/dart/third_party/pkg/matcher/BUILD.gn b/build/secondary/third_party/dart/third_party/pkg/matcher/BUILD.gn new file mode 100644 index 0000000000000..9bd84e15f70d5 --- /dev/null +++ b/build/secondary/third_party/dart/third_party/pkg/matcher/BUILD.gn @@ -0,0 +1,55 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("matcher") { + package_name = "matcher" + + language_version = "2.12" + + deps = [ "//third_party/dart/third_party/pkg/stack_trace" ] + + sources = [ + "expect.dart", + "matcher.dart", + "mirror_matchers.dart", + "src/core_matchers.dart", + "src/custom_matcher.dart", + "src/description.dart", + "src/equals_matcher.dart", + "src/error_matchers.dart", + "src/expect/async_matcher.dart", + "src/expect/expect.dart", + "src/expect/expect_async.dart", + "src/expect/future_matchers.dart", + "src/expect/never_called.dart", + "src/expect/prints_matcher.dart", + "src/expect/stream_matcher.dart", + "src/expect/stream_matchers.dart", + "src/expect/throws_matcher.dart", + "src/expect/throws_matchers.dart", + "src/expect/util/placeholder.dart", + "src/expect/util/pretty_print.dart", + "src/feature_matcher.dart", + "src/having_matcher.dart", + "src/interfaces.dart", + "src/iterable_matchers.dart", + "src/map_matchers.dart", + "src/numeric_matchers.dart", + "src/operator_matchers.dart", + "src/order_matchers.dart", + "src/pretty_print.dart", + "src/string_matchers.dart", + "src/type_matcher.dart", + "src/util.dart", + ] +} diff --git a/build/secondary/third_party/dart/third_party/pkg/path/BUILD.gn b/build/secondary/third_party/dart/third_party/pkg/path/BUILD.gn new file mode 100644 index 0000000000000..2e692b66eb920 --- /dev/null +++ b/build/secondary/third_party/dart/third_party/pkg/path/BUILD.gn @@ -0,0 +1,36 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("path") { + package_name = "path" + + language_version = "2.12" + + deps = [] + + sources = [ + "path.dart", + "src/characters.dart", + "src/context.dart", + "src/internal_style.dart", + "src/parsed_path.dart", + "src/path_exception.dart", + "src/path_map.dart", + "src/path_set.dart", + "src/style.dart", + "src/style/posix.dart", + "src/style/url.dart", + "src/style/windows.dart", + "src/utils.dart", + ] +} diff --git a/build/secondary/third_party/dart/third_party/pkg/stack_trace/BUILD.gn b/build/secondary/third_party/dart/third_party/pkg/stack_trace/BUILD.gn new file mode 100644 index 0000000000000..10dd2983dd05a --- /dev/null +++ b/build/secondary/third_party/dart/third_party/pkg/stack_trace/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("stack_trace") { + package_name = "stack_trace" + + language_version = "2.12" + + deps = [ "//third_party/dart/third_party/pkg/path" ] + + sources = [ + "src/chain.dart", + "src/frame.dart", + "src/lazy_chain.dart", + "src/lazy_trace.dart", + "src/stack_zone_specification.dart", + "src/trace.dart", + "src/unparsed_frame.dart", + "src/utils.dart", + "src/vm_trace.dart", + "stack_trace.dart", + ] +} diff --git a/build/secondary/third_party/expat/BUILD.gn b/build/secondary/third_party/expat/BUILD.gn new file mode 100644 index 0000000000000..316ec274443fa --- /dev/null +++ b/build/secondary/third_party/expat/BUILD.gn @@ -0,0 +1,26 @@ +# 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. + +config("expat_config") { + include_dirs = [ + "expat/lib", + "//flutter/build/secondary/third_party/expat/expat_config", + ] + + defines = [ + "XML_STATIC", + "XML_DEV_URANDOM", + ] +} + +static_library("expat") { + sources = [ + "expat/lib/expat.h", + "expat/lib/xmlparse.c", + "expat/lib/xmlrole.c", + "expat/lib/xmltok.c", + ] + + public_configs = [ ":expat_config" ] +} diff --git a/build/secondary/third_party/expat/expat_config/expat_config.h b/build/secondary/third_party/expat/expat_config/expat_config.h new file mode 100644 index 0000000000000..d02d0f6005c0e --- /dev/null +++ b/build/secondary/third_party/expat/expat_config/expat_config.h @@ -0,0 +1,14 @@ +// 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. + +#define BYTEORDER 1234 +#define HAVE_INTTYPES_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define STDC_HEADERS 1 +#define XML_CONTEXT_BYTES 1024 +#define XML_DTD 1 +#define XML_NS 1 diff --git a/build/secondary/third_party/flatbuffers/BUILD.gn b/build/secondary/third_party/flatbuffers/BUILD.gn new file mode 100644 index 0000000000000..502be760175b3 --- /dev/null +++ b/build/secondary/third_party/flatbuffers/BUILD.gn @@ -0,0 +1,100 @@ +# 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. + +_src_root = "//third_party/flatbuffers" + +config("flatbuffers_public_configs") { + include_dirs = [ "$_src_root/include" ] + + cflags = [ "-Wno-newline-eof" ] +} + +source_set("flatbuffers") { + sources = [ + "$_src_root/include/flatbuffers/allocator.h", + "$_src_root/include/flatbuffers/array.h", + "$_src_root/include/flatbuffers/base.h", + "$_src_root/include/flatbuffers/bfbs_generator.h", + "$_src_root/include/flatbuffers/buffer.h", + "$_src_root/include/flatbuffers/buffer_ref.h", + "$_src_root/include/flatbuffers/default_allocator.h", + "$_src_root/include/flatbuffers/detached_buffer.h", + "$_src_root/include/flatbuffers/flatbuffer_builder.h", + "$_src_root/include/flatbuffers/flatbuffers.h", + "$_src_root/include/flatbuffers/flex_flat_util.h", + "$_src_root/include/flatbuffers/flexbuffers.h", + "$_src_root/include/flatbuffers/hash.h", + "$_src_root/include/flatbuffers/idl.h", + "$_src_root/include/flatbuffers/minireflect.h", + "$_src_root/include/flatbuffers/reflection.h", + "$_src_root/include/flatbuffers/reflection_generated.h", + "$_src_root/include/flatbuffers/registry.h", + "$_src_root/include/flatbuffers/stl_emulation.h", + "$_src_root/include/flatbuffers/string.h", + "$_src_root/include/flatbuffers/struct.h", + "$_src_root/include/flatbuffers/table.h", + "$_src_root/include/flatbuffers/util.h", + "$_src_root/include/flatbuffers/vector.h", + "$_src_root/include/flatbuffers/vector_downward.h", + "$_src_root/include/flatbuffers/verifier.h", + "$_src_root/src/idl_gen_text.cpp", + "$_src_root/src/idl_parser.cpp", + "$_src_root/src/reflection.cpp", + "$_src_root/src/util.cpp", + ] + + public_configs = [ ":flatbuffers_public_configs" ] +} + +executable("flatc") { + sources = [ + "$_src_root/grpc/src/compiler/cpp_generator.cc", + "$_src_root/grpc/src/compiler/cpp_generator.h", + "$_src_root/grpc/src/compiler/go_generator.cc", + "$_src_root/grpc/src/compiler/go_generator.h", + "$_src_root/grpc/src/compiler/java_generator.cc", + "$_src_root/grpc/src/compiler/java_generator.h", + "$_src_root/grpc/src/compiler/python_generator.cc", + "$_src_root/grpc/src/compiler/python_generator.h", + "$_src_root/grpc/src/compiler/schema_interface.h", + "$_src_root/grpc/src/compiler/swift_generator.cc", + "$_src_root/grpc/src/compiler/swift_generator.h", + "$_src_root/grpc/src/compiler/ts_generator.cc", + "$_src_root/grpc/src/compiler/ts_generator.h", + "$_src_root/include/flatbuffers/code_generators.h", + "$_src_root/src/annotated_binary_text_gen.cpp", + "$_src_root/src/annotated_binary_text_gen.h", + "$_src_root/src/bfbs_gen.h", + "$_src_root/src/bfbs_gen_lua.cpp", + "$_src_root/src/bfbs_gen_lua.h", + "$_src_root/src/bfbs_namer.h", + "$_src_root/src/binary_annotator.cpp", + "$_src_root/src/binary_annotator.h", + "$_src_root/src/code_generators.cpp", + "$_src_root/src/flatc.cpp", + "$_src_root/src/flatc_main.cpp", + "$_src_root/src/idl_gen_cpp.cpp", + "$_src_root/src/idl_gen_csharp.cpp", + "$_src_root/src/idl_gen_dart.cpp", + "$_src_root/src/idl_gen_fbs.cpp", + "$_src_root/src/idl_gen_go.cpp", + "$_src_root/src/idl_gen_grpc.cpp", + "$_src_root/src/idl_gen_java.cpp", + "$_src_root/src/idl_gen_json_schema.cpp", + "$_src_root/src/idl_gen_kotlin.cpp", + "$_src_root/src/idl_gen_lobster.cpp", + "$_src_root/src/idl_gen_lua.cpp", + "$_src_root/src/idl_gen_php.cpp", + "$_src_root/src/idl_gen_python.cpp", + "$_src_root/src/idl_gen_rust.cpp", + "$_src_root/src/idl_gen_swift.cpp", + "$_src_root/src/idl_gen_ts.cpp", + "$_src_root/src/idl_namer.h", + "$_src_root/src/namer.h", + ] + + include_dirs = [ "$_src_root/grpc" ] + + deps = [ ":flatbuffers" ] +} diff --git a/build/secondary/third_party/flatbuffers/flatbuffers.gni b/build/secondary/third_party/flatbuffers/flatbuffers.gni new file mode 100644 index 0000000000000..ae636d64aee9d --- /dev/null +++ b/build/secondary/third_party/flatbuffers/flatbuffers.gni @@ -0,0 +1,51 @@ +# 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. + +import("//build/compiled_action.gni") + +template("flatbuffers") { + assert(defined(invoker.flatbuffers), + "Flatbuffer schema files must be specified.") + + flatc_target_name = "flatc_$target_name" + compiled_action_foreach(flatc_target_name) { + tool = "//third_party/flatbuffers:flatc" + sources = invoker.flatbuffers + outputs = [ "$target_gen_dir/{{source_name_part}}_flatbuffers.h" ] + args = [ + "--warnings-as-errors", + "--cpp", + "--cpp-std", + "c++17", + "--cpp-static-reflection", + "--gen-object-api", + "--filename-suffix", + "_flatbuffers", + "-o", + rebase_path(target_gen_dir, root_build_dir), + "{{source}}", + ] + } + + source_set(target_name) { + forward_variables_from(invoker, + "*", + [ + "flatbuffers", + "sources", + "deps", + ]) + sources = get_target_outputs(":$flatc_target_name") + if (defined(invoker.sources)) { + sources += invoker.sources + } + deps = [ + ":$flatc_target_name", + "//third_party/flatbuffers", + ] + if (defined(invoker.deps)) { + deps += invoker.deps + } + } +} diff --git a/build/secondary/third_party/googletest/BUILD.gn b/build/secondary/third_party/googletest/BUILD.gn new file mode 100644 index 0000000000000..72f87cc4749e9 --- /dev/null +++ b/build/secondary/third_party/googletest/BUILD.gn @@ -0,0 +1,319 @@ +# Copyright 2018 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (is_fuchsia) { + import("//build/fuchsia/sdk.gni") +} + +config("gtest_private_config") { + visibility = [ ":*" ] + include_dirs = [ "googletest" ] +} + +config("gtest_config") { + include_dirs = [ "googletest/include" ] +} + +static_library("gtest") { + testonly = true + public = [ + "googletest/include/gtest/gtest-spi.h", + "googletest/include/gtest/gtest.h", + ] + sources = [ + "googletest/include/gtest/gtest-death-test.h", + "googletest/include/gtest/gtest-message.h", + "googletest/include/gtest/gtest-param-test.h", + "googletest/include/gtest/gtest-printers.h", + "googletest/include/gtest/gtest-test-part.h", + "googletest/include/gtest/gtest-typed-test.h", + "googletest/include/gtest/gtest_pred_impl.h", + "googletest/include/gtest/gtest_prod.h", + "googletest/include/gtest/internal/custom/gtest-port.h", + "googletest/include/gtest/internal/custom/gtest-printers.h", + "googletest/include/gtest/internal/custom/gtest.h", + "googletest/include/gtest/internal/gtest-death-test-internal.h", + "googletest/include/gtest/internal/gtest-filepath.h", + "googletest/include/gtest/internal/gtest-internal.h", + "googletest/include/gtest/internal/gtest-linked_ptr.h", + "googletest/include/gtest/internal/gtest-param-util-generated.h", + "googletest/include/gtest/internal/gtest-param-util.h", + "googletest/include/gtest/internal/gtest-port-arch.h", + "googletest/include/gtest/internal/gtest-port.h", + "googletest/include/gtest/internal/gtest-string.h", + "googletest/include/gtest/internal/gtest-tuple.h", + "googletest/include/gtest/internal/gtest-type-util.h", + "googletest/src/gtest-all.cc", + "googletest/src/gtest-death-test.cc", + "googletest/src/gtest-filepath.cc", + "googletest/src/gtest-internal-inl.h", + "googletest/src/gtest-matchers.cc", + "googletest/src/gtest-port.cc", + "googletest/src/gtest-printers.cc", + "googletest/src/gtest-test-part.cc", + "googletest/src/gtest-typed-test.cc", + "googletest/src/gtest.cc", + ] + sources -= [ "googletest/src/gtest-all.cc" ] + public_configs = [ ":gtest_config" ] + configs += [ ":gtest_private_config" ] + + if (is_fuchsia) { + if (using_fuchsia_sdk) { + deps = [ + "$fuchsia_sdk_root/pkg:fdio", + "$fuchsia_sdk_root/pkg:zx", + ] + } else { + deps = [ + "//zircon/public/lib/fdio", + "//zircon/public/lib/zx", + ] + } + } +} + +# Library that defines the FRIEND_TEST macro. +source_set("gtest_prod") { + testonly = false + public = [ "googletest/include/gtest/gtest_prod.h" ] + public_configs = [ ":gtest_config" ] +} + +static_library("gtest_main") { + testonly = true + sources = [ "googletest/src/gtest_main.cc" ] + public_deps = [ ":gtest" ] +} + +executable("gtest_all_test") { + testonly = true + sources = [ + "googletest/test/gtest-death-test_test.cc", + "googletest/test/gtest-filepath_test.cc", + "googletest/test/gtest-linked_ptr_test.cc", + "googletest/test/gtest-message_test.cc", + "googletest/test/gtest-options_test.cc", + "googletest/test/gtest-port_test.cc", + "googletest/test/gtest-printers_test.cc", + "googletest/test/gtest-test-part_test.cc", + "googletest/test/gtest-typed-test2_test.cc", + "googletest/test/gtest-typed-test_test.cc", + "googletest/test/gtest-typed-test_test.h", + "googletest/test/gtest_main_unittest.cc", + "googletest/test/gtest_pred_impl_unittest.cc", + "googletest/test/gtest_prod_test.cc", + "googletest/test/gtest_unittest.cc", + "googletest/test/production.cc", + "googletest/test/production.h", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ":gtest_main", + ] +} + +executable("gtest_environment_test") { + testonly = true + sources = [ "googletest/test/gtest_environment_test.cc" ] + configs += [ ":gtest_private_config" ] + deps = [ ":gtest" ] +} + +executable("gtest_listener_test") { + testonly = true + sources = [ "googletest/test/gtest-listener_test.cc" ] + deps = [ ":gtest" ] +} + +executable("gtest_no_test") { + testonly = true + sources = [ "googletest/test/gtest_no_test_unittest.cc" ] + deps = [ ":gtest" ] +} + +executable("gtest_param_test") { + testonly = true + sources = [ + "googletest/test/gtest-param-test2_test.cc", + "googletest/test/gtest-param-test_test.cc", + "googletest/test/gtest-param-test_test.h", + ] + configs += [ ":gtest_private_config" ] + deps = [ ":gtest" ] +} + +executable("gtest_premature_exit_test") { + testonly = true + sources = [ "googletest/test/gtest_premature_exit_test.cc" ] + deps = [ ":gtest" ] +} + +executable("gtest_repeat_test") { + testonly = true + sources = [ "googletest/test/gtest_repeat_test.cc" ] + configs += [ ":gtest_private_config" ] + deps = [ ":gtest" ] +} + +executable("gtest_sole_header_test") { + testonly = true + sources = [ "googletest/test/gtest_sole_header_test.cc" ] + deps = [ + ":gtest", + ":gtest_main", + ] +} + +executable("gtest_stress_test") { + testonly = true + sources = [ "googletest/test/gtest_stress_test.cc" ] + configs += [ ":gtest_private_config" ] + deps = [ ":gtest" ] +} + +executable("gtest_unittest_api_test") { + testonly = true + sources = [ "googletest/test/gtest-unittest-api_test.cc" ] + deps = [ ":gtest" ] +} + +group("gtest_all_tests") { + testonly = true + deps = [ + ":gtest_all_test", + ":gtest_environment_test", + ":gtest_listener_test", + ":gtest_no_test", + ":gtest_param_test", + ":gtest_premature_exit_test", + ":gtest_repeat_test", + ":gtest_sole_header_test", + ":gtest_stress_test", + ":gtest_unittest_api_test", + ] +} + +config("gmock_private_config") { + visibility = [ ":*" ] + include_dirs = [ "googlemock" ] +} + +config("gmock_config") { + include_dirs = [ "googlemock/include" ] + + cflags_cc = [ + # The MOCK_METHODn() macros do not specify "override", which triggers this + # warning in users: "error: 'Method' overrides a member function but is not + # marked 'override' [-Werror,-Winconsistent-missing-override]". Suppress + # these warnings until https://github.com/google/googletest/issues/533 is + # fixed. + "-Wno-inconsistent-missing-override", + ] +} + +static_library("gmock") { + testonly = true + public = [ "googlemock/include/gmock/gmock.h" ] + sources = [ + "googlemock/include/gmock/gmock-actions.h", + "googlemock/include/gmock/gmock-cardinalities.h", + "googlemock/include/gmock/gmock-generated-actions.h", + "googlemock/include/gmock/gmock-generated-function-mockers.h", + "googlemock/include/gmock/gmock-generated-matchers.h", + "googlemock/include/gmock/gmock-generated-nice-strict.h", + "googlemock/include/gmock/gmock-matchers.h", + "googlemock/include/gmock/gmock-more-actions.h", + "googlemock/include/gmock/gmock-more-matchers.h", + "googlemock/include/gmock/gmock-spec-builders.h", + "googlemock/include/gmock/internal/custom/gmock-generated-actions.h", + "googlemock/include/gmock/internal/custom/gmock-matchers.h", + "googlemock/include/gmock/internal/custom/gmock-port.h", + "googlemock/include/gmock/internal/gmock-generated-internal-utils.h", + "googlemock/include/gmock/internal/gmock-internal-utils.h", + "googlemock/include/gmock/internal/gmock-port.h", + "googlemock/src/gmock-all.cc", + "googlemock/src/gmock-cardinalities.cc", + "googlemock/src/gmock-internal-utils.cc", + "googlemock/src/gmock-matchers.cc", + "googlemock/src/gmock-spec-builders.cc", + "googlemock/src/gmock.cc", + ] + sources -= [ "googlemock/src/gmock-all.cc" ] + public_configs = [ ":gmock_config" ] + configs += [ ":gmock_private_config" ] + deps = [ ":gtest" ] +} + +static_library("gmock_main") { + testonly = true + sources = [ "googlemock/src/gmock_main.cc" ] + public_deps = [ + ":gmock", + ":gtest", + ] +} + +executable("gmock_all_test") { + testonly = true + sources = [ + "googlemock/test/gmock-actions_test.cc", + "googlemock/test/gmock-cardinalities_test.cc", + "googlemock/test/gmock-generated-actions_test.cc", + "googlemock/test/gmock-generated-function-mockers_test.cc", + "googlemock/test/gmock-generated-internal-utils_test.cc", + "googlemock/test/gmock-generated-matchers_test.cc", + "googlemock/test/gmock-internal-utils_test.cc", + "googlemock/test/gmock-matchers_test.cc", + "googlemock/test/gmock-more-actions_test.cc", + "googlemock/test/gmock-nice-strict_test.cc", + "googlemock/test/gmock-port_test.cc", + "googlemock/test/gmock-spec-builders_test.cc", + "googlemock/test/gmock_test.cc", + ] + configs += [ + ":gmock_private_config", + ":gtest_private_config", + ] + deps = [ + ":gmock", + ":gmock_main", + ":gtest", + ] +} + +executable("gmock_link_test") { + testonly = true + sources = [ + "googlemock/test/gmock_link2_test.cc", + "googlemock/test/gmock_link_test.cc", + "googlemock/test/gmock_link_test.h", + ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gmock", + ":gmock_main", + ":gtest", + ] +} + +executable("gmock_stress_test") { + testonly = true + sources = [ "googlemock/test/gmock_stress_test.cc" ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gmock", + ":gtest", + ] +} + +group("gmock_all_tests") { + testonly = true + deps = [ + ":gmock_all_test", + ":gmock_link_test", + ":gmock_stress_test", + ] +} diff --git a/build/secondary/third_party/inja/BUILD.gn b/build/secondary/third_party/inja/BUILD.gn new file mode 100644 index 0000000000000..59345eb31ce8c --- /dev/null +++ b/build/secondary/third_party/inja/BUILD.gn @@ -0,0 +1,43 @@ +# 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. + +source_root = "//third_party/inja" + +config("inja_public_config") { + include_dirs = [ "$source_root/include" ] + + if (is_clang) { + cflags_cc = [ + "-Wno-unused-variable", + "-Wno-newline-eof", + ] + } + + defines = [ "INJA_NOEXCEPTION=1" ] +} + +source_set("inja") { + public_configs = [ ":inja_public_config" ] + + public = [ "$source_root/include/inja/inja.hpp" ] + + sources = [ + "$source_root/include/inja/config.hpp", + "$source_root/include/inja/environment.hpp", + "$source_root/include/inja/exceptions.hpp", + "$source_root/include/inja/function_storage.hpp", + "$source_root/include/inja/inja.hpp", + "$source_root/include/inja/lexer.hpp", + "$source_root/include/inja/node.hpp", + "$source_root/include/inja/parser.hpp", + "$source_root/include/inja/renderer.hpp", + "$source_root/include/inja/statistics.hpp", + "$source_root/include/inja/string_view.hpp", + "$source_root/include/inja/template.hpp", + "$source_root/include/inja/token.hpp", + "$source_root/include/inja/utils.hpp", + ] + + public_deps = [ "//third_party/json" ] +} diff --git a/build/secondary/third_party/json/BUILD.gn b/build/secondary/third_party/json/BUILD.gn new file mode 100644 index 0000000000000..bf24ebf119380 --- /dev/null +++ b/build/secondary/third_party/json/BUILD.gn @@ -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. + +source_root = "//third_party/json" + +config("json_public_config") { + include_dirs = [ "$source_root/include" ] +} + +source_set("json") { + public_configs = [ ":json_public_config" ] + + public = [ "$source_root/include/nlohmann/json.hpp" ] + + sources = [ + "$source_root/include/nlohmann/adl_serializer.hpp", + "$source_root/include/nlohmann/byte_container_with_subtype.hpp", + "$source_root/include/nlohmann/detail/abi_macros.hpp", + "$source_root/include/nlohmann/detail/conversions/from_json.hpp", + "$source_root/include/nlohmann/detail/conversions/to_chars.hpp", + "$source_root/include/nlohmann/detail/conversions/to_json.hpp", + "$source_root/include/nlohmann/detail/exceptions.hpp", + "$source_root/include/nlohmann/detail/hash.hpp", + "$source_root/include/nlohmann/detail/input/binary_reader.hpp", + "$source_root/include/nlohmann/detail/input/input_adapters.hpp", + "$source_root/include/nlohmann/detail/input/json_sax.hpp", + "$source_root/include/nlohmann/detail/input/lexer.hpp", + "$source_root/include/nlohmann/detail/input/parser.hpp", + "$source_root/include/nlohmann/detail/input/position_t.hpp", + "$source_root/include/nlohmann/detail/iterators/internal_iterator.hpp", + "$source_root/include/nlohmann/detail/iterators/iter_impl.hpp", + "$source_root/include/nlohmann/detail/iterators/iteration_proxy.hpp", + "$source_root/include/nlohmann/detail/iterators/iterator_traits.hpp", + "$source_root/include/nlohmann/detail/iterators/json_reverse_iterator.hpp", + "$source_root/include/nlohmann/detail/iterators/primitive_iterator.hpp", + "$source_root/include/nlohmann/detail/json_custom_base_class.hpp", + "$source_root/include/nlohmann/detail/json_pointer.hpp", + "$source_root/include/nlohmann/detail/json_ref.hpp", + "$source_root/include/nlohmann/detail/macro_scope.hpp", + "$source_root/include/nlohmann/detail/macro_unscope.hpp", + "$source_root/include/nlohmann/detail/meta/call_std/begin.hpp", + "$source_root/include/nlohmann/detail/meta/call_std/end.hpp", + "$source_root/include/nlohmann/detail/meta/cpp_future.hpp", + "$source_root/include/nlohmann/detail/meta/detected.hpp", + "$source_root/include/nlohmann/detail/meta/identity_tag.hpp", + "$source_root/include/nlohmann/detail/meta/is_sax.hpp", + "$source_root/include/nlohmann/detail/meta/std_fs.hpp", + "$source_root/include/nlohmann/detail/meta/type_traits.hpp", + "$source_root/include/nlohmann/detail/meta/void_t.hpp", + "$source_root/include/nlohmann/detail/output/binary_writer.hpp", + "$source_root/include/nlohmann/detail/output/output_adapters.hpp", + "$source_root/include/nlohmann/detail/output/serializer.hpp", + "$source_root/include/nlohmann/detail/string_concat.hpp", + "$source_root/include/nlohmann/detail/string_escape.hpp", + "$source_root/include/nlohmann/detail/value_t.hpp", + "$source_root/include/nlohmann/json.hpp", + "$source_root/include/nlohmann/json_fwd.hpp", + "$source_root/include/nlohmann/ordered_map.hpp", + "$source_root/include/nlohmann/thirdparty/hedley/hedley.hpp", + "$source_root/include/nlohmann/thirdparty/hedley/hedley_undef.hpp", + ] +} diff --git a/build/secondary/third_party/jsoncpp/BUILD.gn b/build/secondary/third_party/jsoncpp/BUILD.gn new file mode 100644 index 0000000000000..ff255b0add118 --- /dev/null +++ b/build/secondary/third_party/jsoncpp/BUILD.gn @@ -0,0 +1,11 @@ +# Copyright 2019 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. + +# The ANGLE build rules have a target that depends on jsoncpp, but the Flutter +# engine never actually builds that target, so just this provides empty dummy +# dependencies to satisfy the generation-time resolution. +config("jsoncpp_config") { +} +group("jsoncpp") { +} diff --git a/build/secondary/third_party/libcxx/BUILD.gn b/build/secondary/third_party/libcxx/BUILD.gn new file mode 100644 index 0000000000000..710ebe465d1b2 --- /dev/null +++ b/build/secondary/third_party/libcxx/BUILD.gn @@ -0,0 +1,117 @@ +# 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. + +config("libcxx_config") { + defines = [ "_LIBCPP_DISABLE_AVAILABILITY=1" ] + include_dirs = [ "//flutter/build/secondary/third_party/libcxx/config" ] +} + +config("src_include") { + include_dirs = [ "src" ] +} + +source_set("libcxx") { + sources = [ + "src/algorithm.cpp", + "src/any.cpp", + "src/bind.cpp", + "src/charconv.cpp", + "src/chrono.cpp", + "src/condition_variable.cpp", + "src/condition_variable_destructor.cpp", + "src/debug.cpp", + "src/exception.cpp", + "src/filesystem/directory_iterator.cpp", + "src/filesystem/filesystem_common.h", + "src/filesystem/int128_builtins.cpp", + "src/filesystem/operations.cpp", + "src/functional.cpp", + "src/future.cpp", + "src/hash.cpp", + "src/ios.cpp", + "src/ios.instantiations.cpp", + "src/iostream.cpp", + "src/locale.cpp", + "src/memory.cpp", + "src/mutex.cpp", + "src/mutex_destructor.cpp", + "src/new.cpp", + "src/optional.cpp", + "src/random.cpp", + "src/regex.cpp", + "src/ryu/d2fixed.cpp", + "src/ryu/d2s.cpp", + "src/ryu/f2s.cpp", + "src/shared_mutex.cpp", + "src/stdexcept.cpp", + "src/string.cpp", + "src/strstream.cpp", + "src/system_error.cpp", + "src/thread.cpp", + "src/typeinfo.cpp", + "src/utility.cpp", + "src/valarray.cpp", + "src/variant.cpp", + "src/vector.cpp", + ] + + deps = [ "//third_party/libcxxabi" ] + + # TODO(goderbauer): remove when all sources build with LTO for android_arm64 and android_x64. + if (is_android && (current_cpu == "arm64" || current_cpu == "x64")) { + sources -= [ "src/new.cpp" ] + deps += [ ":libcxx_nolto" ] + } + + public_configs = [ + ":libcxx_config", + "//third_party/libcxxabi:libcxxabi_config", + ] + + defines = [ + "_LIBCPP_NO_EXCEPTIONS", + "_LIBCPP_NO_RTTI", + "_LIBCPP_BUILDING_LIBRARY", + "LIBCXX_BUILDING_LIBCXXABI", + ] + + # While no translation units in Flutter engine enable RTTI, it may be enabled + # in one of the third party dependencies. This mirrors the configuration in + # libcxxabi. + configs -= [ "//build/config/compiler:no_rtti" ] + configs += [ "//build/config/compiler:rtti" ] + + # libcxx requires C++20 + configs -= [ "//build/config/compiler:cxx_version_default" ] + configs += [ "//build/config/compiler:cxx_version_20" ] + + configs += [ ":src_include" ] + + if (is_clang) { + # shared_mutex.cpp and debug.cpp are purposefully in violation. + cflags_cc = [ "-Wno-thread-safety-analysis" ] + } +} + +source_set("libcxx_nolto") { + visibility = [ ":*" ] + + sources = [ "src/new.cpp" ] + + cflags_cc = [ "-fno-lto" ] + + deps = [ "//third_party/libcxxabi" ] + + public_configs = [ + ":libcxx_config", + "//third_party/libcxxabi:libcxxabi_config", + ] + + defines = [ + "_LIBCPP_NO_EXCEPTIONS", + "_LIBCPP_NO_RTTI", + "_LIBCPP_BUILDING_LIBRARY", + "LIBCXX_BUILDING_LIBCXXABI", + ] +} diff --git a/build/secondary/third_party/libcxx/config/__config_site b/build/secondary/third_party/libcxx/config/__config_site new file mode 100644 index 0000000000000..106c24fc3613b --- /dev/null +++ b/build/secondary/third_party/libcxx/config/__config_site @@ -0,0 +1,39 @@ +#ifndef _LIBCPP_CONFIG_SITE +#define _LIBCPP_CONFIG_SITE + +/* #undef _LIBCPP_ABI_VERSION */ +/* #undef _LIBCPP_ABI_UNSTABLE */ +/* #undef _LIBCPP_ABI_FORCE_ITANIUM */ +/* #undef _LIBCPP_ABI_FORCE_MICROSOFT */ +/* #undef _LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT */ +/* #undef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE */ +/* #undef _LIBCPP_HAS_NO_STDIN */ +/* #undef _LIBCPP_HAS_NO_STDOUT */ +/* #undef _LIBCPP_HAS_NO_THREADS */ +/* #undef _LIBCPP_HAS_NO_MONOTONIC_CLOCK */ +/* #undef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS */ +/* #undef _LIBCPP_HAS_MUSL_LIBC */ +/* #undef _LIBCPP_HAS_THREAD_API_PTHREAD */ +/* #undef _LIBCPP_HAS_THREAD_API_EXTERNAL */ +/* #undef _LIBCPP_HAS_THREAD_API_WIN32 */ +/* #undef _LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL */ +/* #undef _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS */ +#define _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS +/* #undef _LIBCPP_NO_VCRUNTIME */ +/* #undef _LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION */ +/* #undef _LIBCPP_ABI_NAMESPACE */ +/* #undef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY */ +/* #undef _LIBCPP_HAS_PARALLEL_ALGORITHMS */ +/* #undef _LIBCPP_HAS_NO_RANDOM_DEVICE */ +/* #undef _LIBCPP_HAS_NO_LOCALIZATION */ + +#define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES + +// This is a workaround for BoringSSL, which is compiled in C11 mode +// and includes stdatomic.h. Defining this macro will cause stdatomic.h +// to redirect to the next version of that header in the include path. +#if !defined(__cplusplus) && defined(__clang__) +#define _LIBCPP_COMPILER_CLANG_BASED +#endif + +#endif // _LIBCPP_CONFIG_SITE diff --git a/build/secondary/third_party/libcxxabi/BUILD.gn b/build/secondary/third_party/libcxxabi/BUILD.gn new file mode 100644 index 0000000000000..10e51e78ac8b8 --- /dev/null +++ b/build/secondary/third_party/libcxxabi/BUILD.gn @@ -0,0 +1,82 @@ +# 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. + +config("libcxxabi_config") { + common_cc_flags = [ + "-nostdinc++", + "-fvisibility=hidden", + ] + + cflags_cc = common_cc_flags + cflags_objcc = common_cc_flags + + include_dirs = [ "include" ] + + if (is_ios) { + ldflags = [ "-Wl,-unexported_symbols_list," + + rebase_path("lib/new-delete.exp", root_build_dir) ] + } +} + +source_set("libcxxabi") { + visibility = [ "../libcxx:*" ] + + public_configs = [ ":libcxxabi_config" ] + + defines = [ + "_LIBCPP_BUILDING_LIBRARY", + "_LIBCXXABI_BUILDING_LIBRARY", + "LIBCXXABI_SILENT_TERMINATE", + "_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS", + ] + + sources = [] + + # Compile libcxx ABI using C++11. This replicates the rule in the + # CMakeLists on the "cxx_abiobjects" target. + configs -= [ "//build/config/compiler:cxx_version_default" ] + configs += [ "//build/config/compiler:cxx_version_20" ] + + configs += [ "//third_party/libcxx:src_include" ] + + # No translation units in the engine are built with exceptions. But, using + # Objective-C exceptions requires some infrastructure setup for exceptions. + # Build support for the same in cxxabi on Darwin. + if (is_mac || is_ios) { + configs -= [ "//build/config/gcc:no_exceptions" ] + sources += [ + "src/cxa_exception.cpp", + "src/cxa_personality.cpp", + ] + } else { + if (!is_tsan) { + sources += [ "src/cxa_noexception.cpp" ] + } + } + + # Third party dependencies may depend on RTTI. Add support for the same in + # cxxabi. + configs -= [ "//build/config/compiler:no_rtti" ] + configs += [ "//build/config/compiler:rtti" ] + + sources += [ + "src/abort_message.cpp", + "src/cxa_aux_runtime.cpp", + "src/cxa_default_handlers.cpp", + "src/cxa_demangle.cpp", + "src/cxa_exception_storage.cpp", + "src/cxa_handlers.cpp", + "src/cxa_vector.cpp", + "src/cxa_virtual.cpp", + "src/fallback_malloc.cpp", + "src/private_typeinfo.cpp", + "src/stdlib_exception.cpp", + "src/stdlib_stdexcept.cpp", + "src/stdlib_typeinfo.cpp", + ] + + if (!(is_tsan && is_linux)) { + sources += [ "src/cxa_guard.cpp" ] + } +} diff --git a/build/secondary/third_party/libjpeg-turbo/BUILD.gn b/build/secondary/third_party/libjpeg-turbo/BUILD.gn new file mode 100644 index 0000000000000..55f346e49c8d3 --- /dev/null +++ b/build/secondary/third_party/libjpeg-turbo/BUILD.gn @@ -0,0 +1,81 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("libjpeg_config") { + include_dirs = [ "." ] +} + +source_set("libjpeg") { + public_configs = [ ":libjpeg_config" ] + + defines = [] + + if (!is_win) { + cflags_c = [ + "-Wno-unused-variable", + "-Wno-unused-function", + ] + } else { + defines += [ "TURBO_FOR_WINDOWS" ] + } + + sources = [ + "//third_party/libjpeg-turbo/jcapimin.c", + "//third_party/libjpeg-turbo/jcapistd.c", + "//third_party/libjpeg-turbo/jccoefct.c", + "//third_party/libjpeg-turbo/jccolor.c", + "//third_party/libjpeg-turbo/jcdctmgr.c", + "//third_party/libjpeg-turbo/jchuff.c", + "//third_party/libjpeg-turbo/jcinit.c", + "//third_party/libjpeg-turbo/jcmainct.c", + "//third_party/libjpeg-turbo/jcmarker.c", + "//third_party/libjpeg-turbo/jcmaster.c", + "//third_party/libjpeg-turbo/jcomapi.c", + "//third_party/libjpeg-turbo/jcparam.c", + "//third_party/libjpeg-turbo/jcphuff.c", + "//third_party/libjpeg-turbo/jcprepct.c", + "//third_party/libjpeg-turbo/jcsample.c", + "//third_party/libjpeg-turbo/jdapimin.c", + "//third_party/libjpeg-turbo/jdapistd.c", + "//third_party/libjpeg-turbo/jdcoefct.c", + "//third_party/libjpeg-turbo/jdcolor.c", + "//third_party/libjpeg-turbo/jddctmgr.c", + "//third_party/libjpeg-turbo/jdhuff.c", + "//third_party/libjpeg-turbo/jdinput.c", + "//third_party/libjpeg-turbo/jdmainct.c", + "//third_party/libjpeg-turbo/jdmarker.c", + "//third_party/libjpeg-turbo/jdmaster.c", + "//third_party/libjpeg-turbo/jdmerge.c", + "//third_party/libjpeg-turbo/jdphuff.c", + "//third_party/libjpeg-turbo/jdpostct.c", + "//third_party/libjpeg-turbo/jdsample.c", + "//third_party/libjpeg-turbo/jerror.c", + "//third_party/libjpeg-turbo/jfdctflt.c", + "//third_party/libjpeg-turbo/jfdctfst.c", + "//third_party/libjpeg-turbo/jfdctint.c", + "//third_party/libjpeg-turbo/jidctflt.c", + "//third_party/libjpeg-turbo/jidctfst.c", + "//third_party/libjpeg-turbo/jidctint.c", + "//third_party/libjpeg-turbo/jidctred.c", + "//third_party/libjpeg-turbo/jmemmgr.c", + "//third_party/libjpeg-turbo/jmemnobs.c", + "//third_party/libjpeg-turbo/jquant1.c", + "//third_party/libjpeg-turbo/jquant2.c", + "//third_party/libjpeg-turbo/jutils.c", + ] + + if (current_cpu == "arm" && !is_ios) { + sources += [ + "//third_party/libjpeg-turbo/simd/jsimd_arm.c", + "//third_party/libjpeg-turbo/simd/jsimd_arm_neon.S", + ] + } else if (current_cpu == "arm64") { + sources += [ + "//third_party/libjpeg-turbo/simd/jsimd_arm64.c", + "//third_party/libjpeg-turbo/simd/jsimd_arm64_neon.S", + ] + } else { + sources += [ "//third_party/libjpeg-turbo/jsimd_none.c" ] + } +} diff --git a/build/secondary/third_party/libsrtp/BUILD.gn b/build/secondary/third_party/libsrtp/BUILD.gn new file mode 100644 index 0000000000000..5697e3175f0bb --- /dev/null +++ b/build/secondary/third_party/libsrtp/BUILD.gn @@ -0,0 +1,345 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + use_system_libsrtp = false + use_srtp_boringssl = true +} + +config("libsrtp_config") { + defines = [ + "HAVE_CONFIG_H", + "HAVE_STDLIB_H", + "HAVE_STRING_H", + "TESTAPP_SOURCE", + ] + + include_dirs = [ + "config", + "srtp/include", + "srtp/crypto/include", + ] + + if (use_srtp_boringssl) { + defines += [ "OPENSSL" ] + } + + if (is_posix) { + defines += [ + "HAVE_INT16_T", + "HAVE_INT32_T", + "HAVE_INT8_T", + "HAVE_UINT16_T", + "HAVE_UINT32_T", + "HAVE_UINT64_T", + "HAVE_UINT8_T", + "HAVE_STDINT_H", + "HAVE_INTTYPES_H", + "HAVE_NETINET_IN_H", + "HAVE_ARPA_INET_H", + "HAVE_UNISTD_H", + ] + cflags = [ "-Wno-unused-variable" ] + } + + if (is_win) { + defines += [ + "HAVE_BYTESWAP_METHODS_H", + + # All Windows architectures are this way. + "SIZEOF_UNSIGNED_LONG=4", + "SIZEOF_UNSIGNED_LONG_LONG=8", + ] + } + + if (current_cpu == "x64" || current_cpu == "x86" || current_cpu == "arm") { + defines += [ + # TODO(leozwang): CPU_RISC doesn"t work properly on android/arm + # platform for unknown reasons, need to investigate the root cause + # of it. CPU_RISC is used for optimization only, and CPU_CISC should + # just work just fine, it has been tested on android/arm with srtp + # test applications and libjingle. + "CPU_CISC", + ] + } +} + +config("system_libsrtp_config") { + defines = [ "USE_SYSTEM_LIBSRTP" ] + include_dirs = [ "/usr/include/srtp" ] +} + +if (use_system_libsrtp) { + group("libsrtp") { + public_configs = [ + ":libsrtp_config", + ":system_libsrtp_config", + ] + libs = [ "-lsrtp" ] + } +} else { + static_library("libsrtp") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + public_configs = [ ":libsrtp_config" ] + + sources = [ + # includes + "srtp/include/ekt.h", + "srtp/include/getopt_s.h", + "srtp/include/rtp.h", + "srtp/include/rtp_priv.h", + "srtp/include/srtp.h", + "srtp/include/srtp_priv.h", + "srtp/include/ut_sim.h", + + # headers + "srtp/crypto/include/aes.h", + "srtp/crypto/include/aes_cbc.h", + "srtp/crypto/include/aes_icm.h", + "srtp/crypto/include/alloc.h", + "srtp/crypto/include/auth.h", + "srtp/crypto/include/cipher.h", + "srtp/crypto/include/crypto.h", + "srtp/crypto/include/crypto_kernel.h", + "srtp/crypto/include/crypto_math.h", + "srtp/crypto/include/crypto_types.h", + "srtp/crypto/include/cryptoalg.h", + "srtp/crypto/include/datatypes.h", + "srtp/crypto/include/err.h", + "srtp/crypto/include/gf2_8.h", + "srtp/crypto/include/hmac.h", + "srtp/crypto/include/integers.h", + "srtp/crypto/include/kernel_compat.h", + "srtp/crypto/include/key.h", + "srtp/crypto/include/null_auth.h", + "srtp/crypto/include/null_cipher.h", + "srtp/crypto/include/prng.h", + "srtp/crypto/include/rand_source.h", + "srtp/crypto/include/rdb.h", + "srtp/crypto/include/rdbx.h", + "srtp/crypto/include/sha1.h", + "srtp/crypto/include/stat.h", + "srtp/crypto/include/xfm.h", + + # sources + "srtp/crypto/cipher/aes.c", + "srtp/crypto/cipher/aes_cbc.c", + "srtp/crypto/cipher/aes_icm.c", + "srtp/crypto/cipher/cipher.c", + "srtp/crypto/cipher/null_cipher.c", + "srtp/crypto/hash/auth.c", + "srtp/crypto/hash/hmac.c", + "srtp/crypto/hash/null_auth.c", + "srtp/crypto/hash/sha1.c", + "srtp/crypto/kernel/alloc.c", + "srtp/crypto/kernel/crypto_kernel.c", + "srtp/crypto/kernel/err.c", + "srtp/crypto/kernel/key.c", + "srtp/crypto/math/datatypes.c", + "srtp/crypto/math/gf2_8.c", + "srtp/crypto/math/stat.c", + "srtp/crypto/replay/rdb.c", + "srtp/crypto/replay/rdbx.c", + "srtp/crypto/replay/ut_sim.c", + "srtp/crypto/rng/ctr_prng.c", + "srtp/crypto/rng/prng.c", + "srtp/crypto/rng/rand_source.c", + "srtp/srtp/ekt.c", + "srtp/srtp/srtp.c", + ] + + if (is_clang) { + cflags = [ "-Wno-implicit-function-declaration" ] + } + + if (use_srtp_boringssl) { + deps = [ "//third_party/boringssl:boringssl" ] + public_deps = [ "//third_party/boringssl:boringssl" ] + sources -= [ + "srtp/crypto/cipher/aes_cbc.c", + "srtp/crypto/cipher/aes_icm.c", + "srtp/crypto/hash/hmac.c", + "srtp/crypto/hash/sha1.c", + "srtp/crypto/rng/ctr_prng.c", + "srtp/crypto/rng/prng.c", + ] + sources += [ + "srtp/crypto/cipher/aes_gcm_ossl.c", + "srtp/crypto/cipher/aes_icm_ossl.c", + "srtp/crypto/hash/hmac_ossl.c", + "srtp/crypto/include/aes_gcm_ossl.h", + "srtp/crypto/include/aes_icm_ossl.h", + ] + } + } + + # TODO(GYP): A bunch of these tests don't compile (in gyp either). They're + # not very broken, so could probably be made to work if it's useful. + if (!is_win) { + executable("rdbx_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + "srtp/test/rdbx_driver.c", + ] + } + + executable("srtp_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/include/getopt_s.h", + "srtp/include/srtp_priv.h", + "srtp/test/getopt_s.c", + "srtp/test/srtp_driver.c", + ] + } + + executable("roc_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/include/rdbx.h", + "srtp/include/ut_sim.h", + "srtp/test/roc_driver.c", + ] + } + + executable("replay_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/include/rdbx.h", + "srtp/include/ut_sim.h", + "srtp/test/replay_driver.c", + ] + } + + executable("rtpw") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/include/datatypes.h", + "srtp/include/getopt_s.h", + "srtp/include/rtp.h", + "srtp/include/srtp.h", + "srtp/test/getopt_s.c", + "srtp/test/rtp.c", + "srtp/test/rtpw.c", + ] + if (is_android) { + defines = [ "HAVE_SYS_SOCKET_H" ] + } + if (is_clang) { + cflags = [ "-Wno-implicit-function-declaration" ] + } + } + + executable("srtp_test_cipher_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/test/cipher_driver.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_datatypes_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ "srtp/crypto/test/datatypes_driver.c" ] + } + + executable("srtp_test_stat_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ "srtp/crypto/test/stat_driver.c" ] + } + + executable("srtp_test_sha1_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ "srtp/crypto/test/sha1_driver.c" ] + } + + executable("srtp_test_kernel_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/test/kernel_driver.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_aes_calc") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ "srtp/crypto/test/aes_calc.c" ] + } + + executable("srtp_test_rand_gen") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/test/rand_gen.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_rand_gen_soak") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ + "srtp/crypto/test/rand_gen_soak.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_env") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ ":libsrtp" ] + sources = [ "srtp/crypto/test/env.c" ] + } + + group("srtp_runtest") { + deps = [ + ":rdbx_driver", + ":replay_driver", + ":roc_driver", + ":rtpw", + ":srtp_driver", + ":srtp_test_aes_calc", + ":srtp_test_cipher_driver", + ":srtp_test_datatypes_driver", + ":srtp_test_env", + ":srtp_test_kernel_driver", + ":srtp_test_rand_gen", + ":srtp_test_rand_gen_soak", + ":srtp_test_sha1_driver", + ":srtp_test_stat_driver", + ] + } + } +} diff --git a/build/secondary/third_party/libtess2/BUILD.gn b/build/secondary/third_party/libtess2/BUILD.gn new file mode 100644 index 0000000000000..4d9ac96f8b038 --- /dev/null +++ b/build/secondary/third_party/libtess2/BUILD.gn @@ -0,0 +1,29 @@ +# 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. + +source_set("libtess2") { + public = [ "//third_party/libtess2/Include/tesselator.h" ] + + include_dirs = [ "//third_party/libtess2/Include/" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + sources = [ + "//third_party/libtess2/Source/bucketalloc.c", + "//third_party/libtess2/Source/bucketalloc.h", + "//third_party/libtess2/Source/dict.c", + "//third_party/libtess2/Source/dict.h", + "//third_party/libtess2/Source/geom.c", + "//third_party/libtess2/Source/geom.h", + "//third_party/libtess2/Source/mesh.c", + "//third_party/libtess2/Source/mesh.h", + "//third_party/libtess2/Source/priorityq.c", + "//third_party/libtess2/Source/priorityq.h", + "//third_party/libtess2/Source/sweep.c", + "//third_party/libtess2/Source/sweep.h", + "//third_party/libtess2/Source/tess.c", + "//third_party/libtess2/Source/tess.h", + ] +} diff --git a/build/secondary/third_party/libwebp/BUILD.gn b/build/secondary/third_party/libwebp/BUILD.gn new file mode 100644 index 0000000000000..4a25106223451 --- /dev/null +++ b/build/secondary/third_party/libwebp/BUILD.gn @@ -0,0 +1,178 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file is based on: +# https://skia.googlesource.com/skia/+/main/third_party/libwebp/BUILD.gn +config("libwebp_config") { + include_dirs = [ + "//third_party/libwebp/src", + "//third_party/libwebp", + ] +} + +config("libwebp_defines") { + defines = [ + # WebP naturally decodes to RGB_565, Skia with BGR_565. + # This makes WebP decode to BGR_565 when we ask for RGB_565. + # (It also swaps the color order for 4444, but we don't care today.) + "WEBP_SWAP_16BIT_CSP", + + # Prevent WebP symbols from being exposed. + "WEBP_EXTERN=extern", + ] +} + +source_set("libwebp_sse41") { + include_dirs = [ + "//third_party/libwebp/src", + "//third_party/libwebp", + ] + configs += [ ":libwebp_defines" ] + sources = [ + "//third_party/libwebp/src/dsp/alpha_processing_sse41.c", + "//third_party/libwebp/src/dsp/dec_sse41.c", + "//third_party/libwebp/src/dsp/enc_sse41.c", + "//third_party/libwebp/src/dsp/lossless_enc_sse41.c", + "//third_party/libwebp/src/dsp/lossless_sse41.c", + "//third_party/libwebp/src/dsp/upsampling_sse41.c", + "//third_party/libwebp/src/dsp/yuv_sse41.c", + ] + if ((current_cpu == "x86" || current_cpu == "x64") && (!is_win || is_clang)) { + cflags_c = [ "-msse4.1" ] + } +} + +source_set("libwebp") { + public_configs = [ ":libwebp_config" ] + include_dirs = [ + "//third_party/libwebp/src", + "//third_party/libwebp", + ] + + deps = [ ":libwebp_sse41" ] + + if (is_android) { + deps += [ "//third_party/cpu-features" ] + } + + configs += [ ":libwebp_defines" ] + + sources = [ + "//third_party/libwebp/sharpyuv/sharpyuv.c", + "//third_party/libwebp/sharpyuv/sharpyuv_cpu.c", + "//third_party/libwebp/sharpyuv/sharpyuv_csp.c", + "//third_party/libwebp/sharpyuv/sharpyuv_dsp.c", + "//third_party/libwebp/sharpyuv/sharpyuv_gamma.c", + "//third_party/libwebp/sharpyuv/sharpyuv_neon.c", + "//third_party/libwebp/sharpyuv/sharpyuv_sse2.c", + "//third_party/libwebp/src/dec/alpha_dec.c", + "//third_party/libwebp/src/dec/buffer_dec.c", + "//third_party/libwebp/src/dec/frame_dec.c", + "//third_party/libwebp/src/dec/idec_dec.c", + "//third_party/libwebp/src/dec/io_dec.c", + "//third_party/libwebp/src/dec/quant_dec.c", + "//third_party/libwebp/src/dec/tree_dec.c", + "//third_party/libwebp/src/dec/vp8_dec.c", + "//third_party/libwebp/src/dec/vp8l_dec.c", + "//third_party/libwebp/src/dec/webp_dec.c", + "//third_party/libwebp/src/demux/anim_decode.c", + "//third_party/libwebp/src/demux/demux.c", + "//third_party/libwebp/src/dsp/alpha_processing.c", + "//third_party/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/alpha_processing_neon.c", + "//third_party/libwebp/src/dsp/alpha_processing_sse2.c", + "//third_party/libwebp/src/dsp/cost.c", + "//third_party/libwebp/src/dsp/cost_mips32.c", + "//third_party/libwebp/src/dsp/cost_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/cost_neon.c", + "//third_party/libwebp/src/dsp/cost_sse2.c", + "//third_party/libwebp/src/dsp/cpu.c", + "//third_party/libwebp/src/dsp/dec.c", + "//third_party/libwebp/src/dsp/dec_clip_tables.c", + "//third_party/libwebp/src/dsp/dec_mips32.c", + "//third_party/libwebp/src/dsp/dec_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/dec_msa.c", + "//third_party/libwebp/src/dsp/dec_neon.c", + "//third_party/libwebp/src/dsp/dec_sse2.c", + "//third_party/libwebp/src/dsp/enc.c", + "//third_party/libwebp/src/dsp/enc_mips32.c", + "//third_party/libwebp/src/dsp/enc_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/enc_msa.c", + "//third_party/libwebp/src/dsp/enc_neon.c", + "//third_party/libwebp/src/dsp/enc_sse2.c", + "//third_party/libwebp/src/dsp/filters.c", + "//third_party/libwebp/src/dsp/filters_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/filters_msa.c", + "//third_party/libwebp/src/dsp/filters_neon.c", + "//third_party/libwebp/src/dsp/filters_sse2.c", + "//third_party/libwebp/src/dsp/lossless.c", + "//third_party/libwebp/src/dsp/lossless_enc.c", + "//third_party/libwebp/src/dsp/lossless_enc_mips32.c", + "//third_party/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/lossless_enc_msa.c", + "//third_party/libwebp/src/dsp/lossless_enc_neon.c", + "//third_party/libwebp/src/dsp/lossless_enc_sse2.c", + "//third_party/libwebp/src/dsp/lossless_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/lossless_msa.c", + "//third_party/libwebp/src/dsp/lossless_neon.c", + "//third_party/libwebp/src/dsp/lossless_sse2.c", + "//third_party/libwebp/src/dsp/rescaler.c", + "//third_party/libwebp/src/dsp/rescaler_mips32.c", + "//third_party/libwebp/src/dsp/rescaler_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/rescaler_msa.c", + "//third_party/libwebp/src/dsp/rescaler_neon.c", + "//third_party/libwebp/src/dsp/rescaler_sse2.c", + "//third_party/libwebp/src/dsp/ssim.c", + "//third_party/libwebp/src/dsp/ssim_sse2.c", + "//third_party/libwebp/src/dsp/upsampling.c", + "//third_party/libwebp/src/dsp/upsampling_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/upsampling_msa.c", + "//third_party/libwebp/src/dsp/upsampling_neon.c", + "//third_party/libwebp/src/dsp/upsampling_sse2.c", + "//third_party/libwebp/src/dsp/yuv.c", + "//third_party/libwebp/src/dsp/yuv_mips32.c", + "//third_party/libwebp/src/dsp/yuv_mips_dsp_r2.c", + "//third_party/libwebp/src/dsp/yuv_neon.c", + "//third_party/libwebp/src/dsp/yuv_sse2.c", + "//third_party/libwebp/src/enc/alpha_enc.c", + "//third_party/libwebp/src/enc/analysis_enc.c", + "//third_party/libwebp/src/enc/backward_references_cost_enc.c", + "//third_party/libwebp/src/enc/backward_references_enc.c", + "//third_party/libwebp/src/enc/config_enc.c", + "//third_party/libwebp/src/enc/cost_enc.c", + "//third_party/libwebp/src/enc/filter_enc.c", + "//third_party/libwebp/src/enc/frame_enc.c", + "//third_party/libwebp/src/enc/histogram_enc.c", + "//third_party/libwebp/src/enc/iterator_enc.c", + "//third_party/libwebp/src/enc/near_lossless_enc.c", + "//third_party/libwebp/src/enc/picture_csp_enc.c", + "//third_party/libwebp/src/enc/picture_enc.c", + "//third_party/libwebp/src/enc/picture_psnr_enc.c", + "//third_party/libwebp/src/enc/picture_rescale_enc.c", + "//third_party/libwebp/src/enc/picture_tools_enc.c", + "//third_party/libwebp/src/enc/predictor_enc.c", + "//third_party/libwebp/src/enc/quant_enc.c", + "//third_party/libwebp/src/enc/syntax_enc.c", + "//third_party/libwebp/src/enc/token_enc.c", + "//third_party/libwebp/src/enc/tree_enc.c", + "//third_party/libwebp/src/enc/vp8l_enc.c", + "//third_party/libwebp/src/enc/webp_enc.c", + "//third_party/libwebp/src/mux/anim_encode.c", + "//third_party/libwebp/src/mux/muxedit.c", + "//third_party/libwebp/src/mux/muxinternal.c", + "//third_party/libwebp/src/mux/muxread.c", + "//third_party/libwebp/src/utils/bit_reader_utils.c", + "//third_party/libwebp/src/utils/bit_writer_utils.c", + "//third_party/libwebp/src/utils/color_cache_utils.c", + "//third_party/libwebp/src/utils/filters_utils.c", + "//third_party/libwebp/src/utils/huffman_encode_utils.c", + "//third_party/libwebp/src/utils/huffman_utils.c", + "//third_party/libwebp/src/utils/quant_levels_dec_utils.c", + "//third_party/libwebp/src/utils/quant_levels_utils.c", + "//third_party/libwebp/src/utils/random_utils.c", + "//third_party/libwebp/src/utils/rescaler_utils.c", + "//third_party/libwebp/src/utils/thread_utils.c", + "//third_party/libwebp/src/utils/utils.c", + ] +} diff --git a/build/secondary/third_party/nss/BUILD.gn b/build/secondary/third_party/nss/BUILD.gn new file mode 100644 index 0000000000000..95e6347c75305 --- /dev/null +++ b/build/secondary/third_party/nss/BUILD.gn @@ -0,0 +1,1205 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/linux/pkg_config.gni") + +if (is_linux) { + # This is a dependency on NSS with no libssl. On Linux we use a built-in SSL + # library but the system NSS libraries. Non-Linux platforms using NSS use the + # hermetic one in //third_party/nss. + # + # Generally you should depend on //crypto:platform instead of using this + # config since that will properly pick up NSS or OpenSSL depending on + # platform and build config. + pkg_config("system_nss_no_ssl_config") { + packages = [ "nss" ] + extra_args = [ + "-v", + "-lssl3", + ] + } +} else { + include_nss_root_certs = is_ios + include_nss_libpkix = is_ios + + config("nspr_config") { + defines = [ "NO_NSPR_10_SUPPORT" ] + include_dirs = [ + "nspr/pr/include", + "nspr/lib/ds", + "nspr/lib/libc/include", + ] + + if (component_mode != "shared_library") { + defines += [ "NSPR_STATIC" ] + } + } + + component("nspr") { + output_name = "crnspr" + sources = [ + "nspr/lib/ds/plarena.c", + "nspr/lib/ds/plarena.h", + "nspr/lib/ds/plarenas.h", + "nspr/lib/ds/plhash.c", + "nspr/lib/ds/plhash.h", + "nspr/lib/libc/include/plbase64.h", + "nspr/lib/libc/include/plerror.h", + "nspr/lib/libc/include/plgetopt.h", + "nspr/lib/libc/include/plstr.h", + "nspr/lib/libc/src/base64.c", + "nspr/lib/libc/src/plerror.c", + "nspr/lib/libc/src/plgetopt.c", + "nspr/lib/libc/src/strcase.c", + "nspr/lib/libc/src/strcat.c", + "nspr/lib/libc/src/strchr.c", + "nspr/lib/libc/src/strcmp.c", + "nspr/lib/libc/src/strcpy.c", + "nspr/lib/libc/src/strdup.c", + "nspr/lib/libc/src/strlen.c", + "nspr/lib/libc/src/strpbrk.c", + "nspr/lib/libc/src/strstr.c", + "nspr/lib/libc/src/strtok.c", + "nspr/pr/include/md/_darwin.cfg", + "nspr/pr/include/md/_darwin.h", + "nspr/pr/include/md/_pcos.h", + "nspr/pr/include/md/_pth.h", + "nspr/pr/include/md/_unix_errors.h", + "nspr/pr/include/md/_unixos.h", + "nspr/pr/include/md/_win32_errors.h", + "nspr/pr/include/md/_win95.cfg", + "nspr/pr/include/md/_win95.h", + "nspr/pr/include/md/prosdep.h", + "nspr/pr/include/nspr.h", + "nspr/pr/include/obsolete/pralarm.h", + "nspr/pr/include/obsolete/probslet.h", + "nspr/pr/include/obsolete/protypes.h", + "nspr/pr/include/obsolete/prsem.h", + "nspr/pr/include/pratom.h", + "nspr/pr/include/prbit.h", + "nspr/pr/include/prclist.h", + "nspr/pr/include/prcmon.h", + "nspr/pr/include/prcountr.h", + "nspr/pr/include/prcpucfg.h", + "nspr/pr/include/prcvar.h", + "nspr/pr/include/prdtoa.h", + "nspr/pr/include/prenv.h", + "nspr/pr/include/prerr.h", + "nspr/pr/include/prerror.h", + "nspr/pr/include/prinet.h", + "nspr/pr/include/prinit.h", + "nspr/pr/include/prinrval.h", + "nspr/pr/include/prio.h", + "nspr/pr/include/pripcsem.h", + "nspr/pr/include/private/pprio.h", + "nspr/pr/include/private/pprmwait.h", + "nspr/pr/include/private/pprthred.h", + "nspr/pr/include/private/primpl.h", + "nspr/pr/include/private/prpriv.h", + "nspr/pr/include/prlink.h", + "nspr/pr/include/prlock.h", + "nspr/pr/include/prlog.h", + "nspr/pr/include/prlong.h", + "nspr/pr/include/prmem.h", + "nspr/pr/include/prmon.h", + "nspr/pr/include/prmwait.h", + "nspr/pr/include/prnetdb.h", + "nspr/pr/include/prolock.h", + "nspr/pr/include/prpdce.h", + "nspr/pr/include/prprf.h", + "nspr/pr/include/prproces.h", + "nspr/pr/include/prrng.h", + "nspr/pr/include/prrwlock.h", + "nspr/pr/include/prshm.h", + "nspr/pr/include/prshma.h", + "nspr/pr/include/prsystem.h", + "nspr/pr/include/prthread.h", + "nspr/pr/include/prtime.h", + "nspr/pr/include/prtpool.h", + "nspr/pr/include/prtrace.h", + "nspr/pr/include/prtypes.h", + "nspr/pr/include/prvrsion.h", + "nspr/pr/include/prwin16.h", + "nspr/pr/src/io/prdir.c", + "nspr/pr/src/io/prfdcach.c", + "nspr/pr/src/io/prfile.c", + "nspr/pr/src/io/prio.c", + "nspr/pr/src/io/priometh.c", + "nspr/pr/src/io/pripv6.c", + "nspr/pr/src/io/prlayer.c", + "nspr/pr/src/io/prlog.c", + "nspr/pr/src/io/prmapopt.c", + "nspr/pr/src/io/prmmap.c", + "nspr/pr/src/io/prmwait.c", + "nspr/pr/src/io/prpolevt.c", + "nspr/pr/src/io/prprf.c", + "nspr/pr/src/io/prscanf.c", + "nspr/pr/src/io/prsocket.c", + "nspr/pr/src/io/prstdio.c", + "nspr/pr/src/linking/prlink.c", + "nspr/pr/src/malloc/prmalloc.c", + "nspr/pr/src/malloc/prmem.c", + "nspr/pr/src/md/prosdep.c", + "nspr/pr/src/md/unix/darwin.c", + "nspr/pr/src/md/unix/os_Darwin.s", + "nspr/pr/src/md/unix/unix.c", + "nspr/pr/src/md/unix/unix_errors.c", + "nspr/pr/src/md/unix/uxproces.c", + "nspr/pr/src/md/unix/uxrng.c", + "nspr/pr/src/md/unix/uxshm.c", + "nspr/pr/src/md/unix/uxwrap.c", + "nspr/pr/src/md/windows/ntgc.c", + "nspr/pr/src/md/windows/ntinrval.c", + "nspr/pr/src/md/windows/ntmisc.c", + "nspr/pr/src/md/windows/ntsec.c", + "nspr/pr/src/md/windows/ntsem.c", + "nspr/pr/src/md/windows/w32ipcsem.c", + "nspr/pr/src/md/windows/w32poll.c", + "nspr/pr/src/md/windows/w32rng.c", + "nspr/pr/src/md/windows/w32shm.c", + "nspr/pr/src/md/windows/w95cv.c", + "nspr/pr/src/md/windows/w95dllmain.c", + "nspr/pr/src/md/windows/w95io.c", + "nspr/pr/src/md/windows/w95sock.c", + "nspr/pr/src/md/windows/w95thred.c", + "nspr/pr/src/md/windows/win32_errors.c", + "nspr/pr/src/memory/prseg.c", + "nspr/pr/src/memory/prshm.c", + "nspr/pr/src/memory/prshma.c", + "nspr/pr/src/misc/pralarm.c", + "nspr/pr/src/misc/pratom.c", + "nspr/pr/src/misc/praton.c", + "nspr/pr/src/misc/prcountr.c", + "nspr/pr/src/misc/prdtoa.c", + "nspr/pr/src/misc/prenv.c", + "nspr/pr/src/misc/prerr.c", + "nspr/pr/src/misc/prerror.c", + "nspr/pr/src/misc/prerrortable.c", + "nspr/pr/src/misc/prinit.c", + "nspr/pr/src/misc/prinrval.c", + "nspr/pr/src/misc/pripc.c", + "nspr/pr/src/misc/pripcsem.c", + "nspr/pr/src/misc/prlog2.c", + "nspr/pr/src/misc/prlong.c", + "nspr/pr/src/misc/prnetdb.c", + "nspr/pr/src/misc/prolock.c", + "nspr/pr/src/misc/prrng.c", + "nspr/pr/src/misc/prsystem.c", + "nspr/pr/src/misc/prthinfo.c", + "nspr/pr/src/misc/prtime.c", + "nspr/pr/src/misc/prtpool.c", + "nspr/pr/src/misc/prtrace.c", + "nspr/pr/src/pthreads/ptio.c", + "nspr/pr/src/pthreads/ptmisc.c", + "nspr/pr/src/pthreads/ptsynch.c", + "nspr/pr/src/pthreads/ptthread.c", + "nspr/pr/src/threads/combined/prucpu.c", + "nspr/pr/src/threads/combined/prucv.c", + "nspr/pr/src/threads/combined/prulock.c", + "nspr/pr/src/threads/combined/prustack.c", + "nspr/pr/src/threads/combined/pruthr.c", + "nspr/pr/src/threads/prcmon.c", + "nspr/pr/src/threads/prcthr.c", + "nspr/pr/src/threads/prdump.c", + "nspr/pr/src/threads/prmon.c", + "nspr/pr/src/threads/prrwlock.c", + "nspr/pr/src/threads/prsem.c", + "nspr/pr/src/threads/prtpd.c", + ] + + public_configs = [ ":nspr_config" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + if (is_win) { + configs -= [ + "//build/config/win:unicode", # Requires 8-bit mode. + "//build/config/win:lean_and_mean", # Won"t compile with lean and mean. + ] + } + configs += [ + "//build/config/compiler:no_chromium_code", + "//build/config/compiler:no_size_t_to_int_warning", + ] + + cflags = [] + defines = [ + "_NSPR_BUILD_", + "FORCE_PR_LOG", + ] + + include_dirs = [ "nspr/pr/include/private" ] + + if (is_win) { + cflags = [ "/wd4554" ] # Check precidence. + defines += [ + "XP_PC", + "WIN32", + "WIN95", + "_PR_GLOBAL_THREADS_ONLY", + "_CRT_SECURE_NO_WARNINGS", + ] + } else { + sources -= [ + "nspr/pr/src/md/windows/ntgc.c", + "nspr/pr/src/md/windows/ntinrval.c", + "nspr/pr/src/md/windows/ntmisc.c", + "nspr/pr/src/md/windows/ntsec.c", + "nspr/pr/src/md/windows/ntsem.c", + "nspr/pr/src/md/windows/w32ipcsem.c", + "nspr/pr/src/md/windows/w32poll.c", + "nspr/pr/src/md/windows/w32rng.c", + "nspr/pr/src/md/windows/w32shm.c", + "nspr/pr/src/md/windows/w95cv.c", + "nspr/pr/src/md/windows/w95dllmain.c", + "nspr/pr/src/md/windows/w95io.c", + "nspr/pr/src/md/windows/w95sock.c", + "nspr/pr/src/md/windows/w95thred.c", + "nspr/pr/src/md/windows/win32_errors.c", + "nspr/pr/src/threads/combined/prucpu.c", + "nspr/pr/src/threads/combined/prucv.c", + "nspr/pr/src/threads/combined/prulock.c", + "nspr/pr/src/threads/combined/prustack.c", + "nspr/pr/src/threads/combined/pruthr.c", + ] + } + + if (!is_posix) { + sources -= [ + "nspr/pr/src/md/unix/darwin.c", + "nspr/pr/src/md/unix/os_Darwin.s", + "nspr/pr/src/md/unix/unix.c", + "nspr/pr/src/md/unix/unix_errors.c", + "nspr/pr/src/md/unix/uxproces.c", + "nspr/pr/src/md/unix/uxrng.c", + "nspr/pr/src/md/unix/uxshm.c", + "nspr/pr/src/md/unix/uxwrap.c", + "nspr/pr/src/pthreads/ptio.c", + "nspr/pr/src/pthreads/ptmisc.c", + "nspr/pr/src/pthreads/ptsynch.c", + "nspr/pr/src/pthreads/ptthread.c", + ] + } + + if (current_cpu == "x86") { + defines += [ "_X86_" ] + } else if (current_cpu == "x64") { + defines += [ "_AMD64_" ] + } + + if (is_mac || is_ios) { + sources -= [ + "nspr/pr/src/io/prdir.c", + "nspr/pr/src/io/prfile.c", + "nspr/pr/src/io/prio.c", + "nspr/pr/src/io/prsocket.c", + "nspr/pr/src/misc/pripcsem.c", + "nspr/pr/src/threads/prcthr.c", + "nspr/pr/src/threads/prdump.c", + "nspr/pr/src/threads/prmon.c", + "nspr/pr/src/threads/prsem.c", + ] + defines += [ + "XP_UNIX", + "DARWIN", + "XP_MACOSX", + "_PR_PTHREADS", + "HAVE_BSD_FLOCK", + "HAVE_DLADDR", + "HAVE_LCHOWN", + "HAVE_SOCKLEN_T", + "HAVE_STRERROR", + ] + } + + if (is_mac) { + defines += [ "HAVE_CRT_EXTERNS_H" ] + libs = [ + "CoreFoundation.framework", + "CoreServices.framework", + ] + } + + if (is_clang) { + cflags += [ + # nspr uses a bunch of deprecated functions (NSLinkModule etc) in + # prlink.c on mac. + "-Wno-deprecated-declarations", + + # nspr passes "const char*" through "void*". + "-Wno-incompatible-pointer-types", + + # nspr passes "int*" through "unsigned int*". + "-Wno-pointer-sign", + ] + + # nspr uses assert(!"foo") instead of assert(false && "foo"). + configs -= [ "//build/config/clang:extra_warnings" ] + } + } + + component("nss") { + output_name = "crnss" + sources = [ + # Ensure at least one object file is produced, so that MSVC does not + # warn when creating the static/shared library. See the note for + # the "nssckbi" target for why the "nss" target was split as such. + "nss/lib/nss/nssver.c", + ] + + public_deps = [ ":nss_static" ] + + if (include_nss_root_certs) { + public_deps += [ ":nssckbi" ] + } + + if (component_mode == "shared_library") { + if (is_mac) { + ldflags = [ "-all_load" ] + } else if (is_win) { + # Pass the def file to the linker. + ldflags = + [ "/DEF:" + rebase_path("nss/exports_win.def", root_build_dir) ] + } + } + } + + config("nssckbi_config") { + include_dirs = [ "nss/lib/ckfw/builtins" ] + } + + # This is really more of a pseudo-target to work around the fact that + # a single static_library target cannot contain two object files of the + # same name (hash.o / hash.obj). Logically, this is part of the + # "nss_static" target. By separating it out, it creates a possible + # circular dependency between "nss_static" and "nssckbi" when + # "exclude_nss_root_certs" is not specified, as "nss_static" depends on + # the "builtinsC_GetFunctionList" exported by this target. This is an + # artifact of how NSS is being statically built, which is not an + # officially supported configuration - normally, "nssckbi.dll/so" would + # depend on libnss3.dll/so, and the higher layer caller would instruct + # libnss3.dll to dynamically load nssckbi.dll, breaking the circle. + # + # TODO(rsleevi): http://crbug.com/128134 - Break the circular dependency + # without requiring nssckbi to be built as a shared library. + source_set("nssckbi") { + visibility = [ ":nss" ] # This target is internal implementation detail. + + sources = [ + "nss/lib/ckfw/builtins/anchor.c", + "nss/lib/ckfw/builtins/bfind.c", + "nss/lib/ckfw/builtins/binst.c", + "nss/lib/ckfw/builtins/bobject.c", + "nss/lib/ckfw/builtins/bsession.c", + "nss/lib/ckfw/builtins/bslot.c", + "nss/lib/ckfw/builtins/btoken.c", + "nss/lib/ckfw/builtins/builtins.h", + "nss/lib/ckfw/builtins/certdata.c", + "nss/lib/ckfw/builtins/ckbiver.c", + "nss/lib/ckfw/builtins/constants.c", + "nss/lib/ckfw/builtins/nssckbi.h", + "nss/lib/ckfw/ck.h", + "nss/lib/ckfw/ckfw.h", + "nss/lib/ckfw/ckfwm.h", + "nss/lib/ckfw/ckfwtm.h", + "nss/lib/ckfw/ckmd.h", + "nss/lib/ckfw/ckt.h", + "nss/lib/ckfw/crypto.c", + "nss/lib/ckfw/find.c", + "nss/lib/ckfw/hash.c", + "nss/lib/ckfw/instance.c", + "nss/lib/ckfw/mechanism.c", + "nss/lib/ckfw/mutex.c", + "nss/lib/ckfw/nssck.api", + "nss/lib/ckfw/nssckepv.h", + "nss/lib/ckfw/nssckft.h", + "nss/lib/ckfw/nssckfw.h", + "nss/lib/ckfw/nssckfwc.h", + "nss/lib/ckfw/nssckfwt.h", + "nss/lib/ckfw/nssckg.h", + "nss/lib/ckfw/nssckmdt.h", + "nss/lib/ckfw/nssckt.h", + "nss/lib/ckfw/object.c", + "nss/lib/ckfw/session.c", + "nss/lib/ckfw/sessobj.c", + "nss/lib/ckfw/slot.c", + "nss/lib/ckfw/token.c", + "nss/lib/ckfw/wrap.c", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + + if (is_win) { + configs -= [ "//build/config/win:unicode" ] # Requires 8-bit mode. + } + configs += [ "//build/config/compiler:no_chromium_code" ] + + include_dirs = [ "nss/lib/ckfw" ] + public_configs = [ ":nssckbi_config" ] + + public_deps = [ ":nss_static" ] + } + + config("nss_static_config") { + defines = [ + "NSS_STATIC", + "NSS_USE_STATIC_LIBS", + "USE_UTIL_DIRECTLY", + ] + if (is_win) { + defines += [ "_WINDOWS" ] + } + include_dirs = [ + "nspr/pr/include", + "nspr/lib/ds", + "nspr/lib/libc/include", + "nss/lib/base", + "nss/lib/certdb", + "nss/lib/certhigh", + "nss/lib/cryptohi", + "nss/lib/dev", + "nss/lib/freebl", + "nss/lib/freebl/ecl", + "nss/lib/nss", + "nss/lib/pk11wrap", + "nss/lib/pkcs7", + "nss/lib/pki", + "nss/lib/smime", + "nss/lib/softoken", + "nss/lib/util", + ] + } + + if (is_win && current_cpu == "x86") { + source_set("nss_static_avx") { + sources = [ + "nss/lib/freebl/intel-gcm-wrap.c", + "nss/lib/freebl/intel-gcm-x86-masm.asm", + "nss/lib/freebl/intel-gcm.h", + ] + defines = [ + "_WINDOWS", + "_X86_", + "INTEL_GCM", + "MP_API_COMPATIBLE", + "MP_ASSEMBLY_DIV_2DX1D", + "MP_ASSEMBLY_MULTIPLY", + "MP_ASSEMBLY_SQUARE", + "MP_NO_MP_WORD", + "MP_USE_UINT_DIGIT", + "NSS_DISABLE_DBM", + "NSS_STATIC", + "NSS_USE_STATIC_LIBS", + "NSS_X86", + "NSS_X86_OR_X64", + "RIJNDAEL_INCLUDE_TABLES", + "SHLIB_PREFIX=\"\"", + "SHLIB_SUFFIX=\"dll\"", + "SHLIB_VERSION=\"3\"", + "SOFTOKEN_LIB_NAME=\"softokn3.dll\"", + "SOFTOKEN_SHLIB_VERSION=\"3\"", + "USE_HW_AES", + "USE_UTIL_DIRECTLY", + "WIN32", + "WIN95", + "XP_PC", + ] + include_dirs = [ + "nspr/pr/include", + "nspr/lib/ds", + "nspr/lib/libc/include", + "nss/lib/freebl/ecl", + "nss/lib/util", + ] + } + } + + source_set("nss_static") { + visibility = [ ":*" ] # Internal implementation detail. + + sources = [ + "nss/lib/base/arena.c", + "nss/lib/base/base.h", + "nss/lib/base/baset.h", + "nss/lib/base/error.c", + "nss/lib/base/errorval.c", + "nss/lib/base/hash.c", + "nss/lib/base/hashops.c", + "nss/lib/base/item.c", + "nss/lib/base/libc.c", + "nss/lib/base/list.c", + "nss/lib/base/nssbase.h", + "nss/lib/base/nssbaset.h", + "nss/lib/base/nssutf8.c", + "nss/lib/base/tracker.c", + "nss/lib/certdb/alg1485.c", + "nss/lib/certdb/cert.h", + "nss/lib/certdb/certdb.c", + "nss/lib/certdb/certdb.h", + "nss/lib/certdb/certi.h", + "nss/lib/certdb/certt.h", + "nss/lib/certdb/certv3.c", + "nss/lib/certdb/certxutl.c", + "nss/lib/certdb/certxutl.h", + "nss/lib/certdb/crl.c", + "nss/lib/certdb/genname.c", + "nss/lib/certdb/genname.h", + "nss/lib/certdb/polcyxtn.c", + "nss/lib/certdb/secname.c", + "nss/lib/certdb/stanpcertdb.c", + "nss/lib/certdb/xauthkid.c", + "nss/lib/certdb/xbsconst.c", + "nss/lib/certdb/xconst.c", + "nss/lib/certdb/xconst.h", + "nss/lib/certhigh/certhigh.c", + "nss/lib/certhigh/certhtml.c", + "nss/lib/certhigh/certreq.c", + "nss/lib/certhigh/certvfy.c", + "nss/lib/certhigh/crlv2.c", + "nss/lib/certhigh/ocsp.c", + "nss/lib/certhigh/ocsp.h", + "nss/lib/certhigh/ocspi.h", + "nss/lib/certhigh/ocspsig.c", + "nss/lib/certhigh/ocspt.h", + "nss/lib/certhigh/ocspti.h", + "nss/lib/certhigh/xcrldist.c", + "nss/lib/cryptohi/cryptohi.h", + "nss/lib/cryptohi/cryptoht.h", + "nss/lib/cryptohi/dsautil.c", + "nss/lib/cryptohi/key.h", + "nss/lib/cryptohi/keyhi.h", + "nss/lib/cryptohi/keyi.h", + "nss/lib/cryptohi/keyt.h", + "nss/lib/cryptohi/keythi.h", + "nss/lib/cryptohi/sechash.c", + "nss/lib/cryptohi/sechash.h", + "nss/lib/cryptohi/seckey.c", + "nss/lib/cryptohi/secsign.c", + "nss/lib/cryptohi/secvfy.c", + "nss/lib/dev/ckhelper.c", + "nss/lib/dev/ckhelper.h", + "nss/lib/dev/dev.h", + "nss/lib/dev/devm.h", + "nss/lib/dev/devslot.c", + "nss/lib/dev/devt.h", + "nss/lib/dev/devtm.h", + "nss/lib/dev/devtoken.c", + "nss/lib/dev/devutil.c", + "nss/lib/dev/nssdev.h", + "nss/lib/dev/nssdevt.h", + "nss/lib/freebl/aeskeywrap.c", + "nss/lib/freebl/alg2268.c", + "nss/lib/freebl/alghmac.c", + "nss/lib/freebl/alghmac.h", + "nss/lib/freebl/arcfive.c", + "nss/lib/freebl/arcfour.c", + "nss/lib/freebl/blapi.h", + "nss/lib/freebl/blapii.h", + "nss/lib/freebl/blapit.h", + "nss/lib/freebl/camellia.c", + "nss/lib/freebl/camellia.h", + "nss/lib/freebl/chacha20/chacha20.c", + "nss/lib/freebl/chacha20/chacha20.h", + "nss/lib/freebl/chacha20/chacha20_vec.c", + "nss/lib/freebl/chacha20poly1305.c", + "nss/lib/freebl/chacha20poly1305.h", + "nss/lib/freebl/ctr.c", + "nss/lib/freebl/ctr.h", + "nss/lib/freebl/cts.c", + "nss/lib/freebl/cts.h", + "nss/lib/freebl/des.c", + "nss/lib/freebl/des.h", + "nss/lib/freebl/desblapi.c", + "nss/lib/freebl/dh.c", + "nss/lib/freebl/drbg.c", + "nss/lib/freebl/dsa.c", + "nss/lib/freebl/ec.c", + "nss/lib/freebl/ec.h", + "nss/lib/freebl/ecdecode.c", + "nss/lib/freebl/ecl/ec2.h", + "nss/lib/freebl/ecl/ec_naf.c", + "nss/lib/freebl/ecl/ecl-curve.h", + "nss/lib/freebl/ecl/ecl-exp.h", + "nss/lib/freebl/ecl/ecl-priv.h", + "nss/lib/freebl/ecl/ecl.c", + "nss/lib/freebl/ecl/ecl.h", + "nss/lib/freebl/ecl/ecl_curve.c", + "nss/lib/freebl/ecl/ecl_gf.c", + "nss/lib/freebl/ecl/ecl_mult.c", + "nss/lib/freebl/ecl/ecp.h", + "nss/lib/freebl/ecl/ecp_256.c", + "nss/lib/freebl/ecl/ecp_256_32.c", + "nss/lib/freebl/ecl/ecp_384.c", + "nss/lib/freebl/ecl/ecp_521.c", + "nss/lib/freebl/ecl/ecp_aff.c", + "nss/lib/freebl/ecl/ecp_jac.c", + "nss/lib/freebl/ecl/ecp_jm.c", + "nss/lib/freebl/ecl/ecp_mont.c", + "nss/lib/freebl/gcm.c", + "nss/lib/freebl/gcm.h", + "nss/lib/freebl/hmacct.c", + "nss/lib/freebl/hmacct.h", + "nss/lib/freebl/intel-aes-x86-masm.asm", + "nss/lib/freebl/intel-aes.h", + "nss/lib/freebl/jpake.c", + "nss/lib/freebl/md2.c", + "nss/lib/freebl/md5.c", + "nss/lib/freebl/mpi/logtab.h", + "nss/lib/freebl/mpi/mp_gf2m-priv.h", + "nss/lib/freebl/mpi/mp_gf2m.c", + "nss/lib/freebl/mpi/mp_gf2m.h", + "nss/lib/freebl/mpi/mpcpucache.c", + "nss/lib/freebl/mpi/mpi-config.h", + "nss/lib/freebl/mpi/mpi-priv.h", + "nss/lib/freebl/mpi/mpi.c", + "nss/lib/freebl/mpi/mpi.h", + "nss/lib/freebl/mpi/mpi_amd64.c", + "nss/lib/freebl/mpi/mpi_arm.c", + "nss/lib/freebl/mpi/mpi_arm_mac.c", + "nss/lib/freebl/mpi/mpi_x86_asm.c", + "nss/lib/freebl/mpi/mplogic.c", + "nss/lib/freebl/mpi/mplogic.h", + "nss/lib/freebl/mpi/mpmontg.c", + "nss/lib/freebl/mpi/mpprime.c", + "nss/lib/freebl/mpi/mpprime.h", + "nss/lib/freebl/mpi/primes.c", + "nss/lib/freebl/nss_build_config_mac.h", + "nss/lib/freebl/poly1305/poly1305-donna-x64-sse2-incremental-source.c", + "nss/lib/freebl/poly1305/poly1305.c", + "nss/lib/freebl/poly1305/poly1305.h", + "nss/lib/freebl/pqg.c", + "nss/lib/freebl/pqg.h", + "nss/lib/freebl/rawhash.c", + "nss/lib/freebl/rijndael.c", + "nss/lib/freebl/rijndael.h", + "nss/lib/freebl/rijndael32.tab", + "nss/lib/freebl/rsa.c", + "nss/lib/freebl/rsapkcs.c", + "nss/lib/freebl/secmpi.h", + "nss/lib/freebl/secrng.h", + "nss/lib/freebl/seed.c", + "nss/lib/freebl/seed.h", + "nss/lib/freebl/sha256.h", + "nss/lib/freebl/sha512.c", + "nss/lib/freebl/sha_fast.c", + "nss/lib/freebl/sha_fast.h", + "nss/lib/freebl/shsign.h", + "nss/lib/freebl/shvfy.c", + "nss/lib/freebl/sysrand.c", + "nss/lib/freebl/tlsprfalg.c", + "nss/lib/freebl/unix_rand.c", + "nss/lib/freebl/win_rand.c", + "nss/lib/nss/nss.h", + "nss/lib/nss/nssinit.c", + "nss/lib/nss/nssrenam.h", + "nss/lib/nss/utilwrap.c", + "nss/lib/pk11wrap/debug_module.c", + "nss/lib/pk11wrap/dev3hack.c", + "nss/lib/pk11wrap/dev3hack.h", + "nss/lib/pk11wrap/pk11akey.c", + "nss/lib/pk11wrap/pk11auth.c", + "nss/lib/pk11wrap/pk11cert.c", + "nss/lib/pk11wrap/pk11cxt.c", + "nss/lib/pk11wrap/pk11err.c", + "nss/lib/pk11wrap/pk11func.h", + "nss/lib/pk11wrap/pk11kea.c", + "nss/lib/pk11wrap/pk11list.c", + "nss/lib/pk11wrap/pk11load.c", + "nss/lib/pk11wrap/pk11mech.c", + "nss/lib/pk11wrap/pk11merge.c", + "nss/lib/pk11wrap/pk11nobj.c", + "nss/lib/pk11wrap/pk11obj.c", + "nss/lib/pk11wrap/pk11pars.c", + "nss/lib/pk11wrap/pk11pbe.c", + "nss/lib/pk11wrap/pk11pk12.c", + "nss/lib/pk11wrap/pk11pqg.c", + "nss/lib/pk11wrap/pk11pqg.h", + "nss/lib/pk11wrap/pk11priv.h", + "nss/lib/pk11wrap/pk11pub.h", + "nss/lib/pk11wrap/pk11sdr.c", + "nss/lib/pk11wrap/pk11sdr.h", + "nss/lib/pk11wrap/pk11skey.c", + "nss/lib/pk11wrap/pk11slot.c", + "nss/lib/pk11wrap/pk11util.c", + "nss/lib/pk11wrap/secmod.h", + "nss/lib/pk11wrap/secmodi.h", + "nss/lib/pk11wrap/secmodt.h", + "nss/lib/pk11wrap/secmodti.h", + "nss/lib/pk11wrap/secpkcs5.h", + "nss/lib/pkcs7/certread.c", + "nss/lib/pkcs7/p7common.c", + "nss/lib/pkcs7/p7create.c", + "nss/lib/pkcs7/p7decode.c", + "nss/lib/pkcs7/p7encode.c", + "nss/lib/pkcs7/p7local.c", + "nss/lib/pkcs7/p7local.h", + "nss/lib/pkcs7/pkcs7t.h", + "nss/lib/pkcs7/secmime.c", + "nss/lib/pkcs7/secmime.h", + "nss/lib/pkcs7/secpkcs7.h", + "nss/lib/pki/asymmkey.c", + "nss/lib/pki/certdecode.c", + "nss/lib/pki/certificate.c", + "nss/lib/pki/cryptocontext.c", + "nss/lib/pki/nsspki.h", + "nss/lib/pki/nsspkit.h", + "nss/lib/pki/pki.h", + "nss/lib/pki/pki3hack.c", + "nss/lib/pki/pki3hack.h", + "nss/lib/pki/pkibase.c", + "nss/lib/pki/pkim.h", + "nss/lib/pki/pkistore.c", + "nss/lib/pki/pkistore.h", + "nss/lib/pki/pkit.h", + "nss/lib/pki/pkitm.h", + "nss/lib/pki/symmkey.c", + "nss/lib/pki/tdcache.c", + "nss/lib/pki/trustdomain.c", + "nss/lib/smime/cms.h", + "nss/lib/smime/cmslocal.h", + "nss/lib/smime/cmsreclist.h", + "nss/lib/smime/cmst.h", + "nss/lib/smime/smime.h", + "nss/lib/softoken/fipsaudt.c", + "nss/lib/softoken/fipstest.c", + "nss/lib/softoken/fipstokn.c", + "nss/lib/softoken/jpakesftk.c", + "nss/lib/softoken/lgglue.c", + "nss/lib/softoken/lgglue.h", + "nss/lib/softoken/lowkey.c", + "nss/lib/softoken/lowkeyi.h", + "nss/lib/softoken/lowkeyti.h", + "nss/lib/softoken/lowpbe.c", + "nss/lib/softoken/lowpbe.h", + "nss/lib/softoken/padbuf.c", + "nss/lib/softoken/pkcs11.c", + "nss/lib/softoken/pkcs11c.c", + "nss/lib/softoken/pkcs11i.h", + "nss/lib/softoken/pkcs11ni.h", + "nss/lib/softoken/pkcs11u.c", + "nss/lib/softoken/sdb.c", + "nss/lib/softoken/sdb.h", + "nss/lib/softoken/sftkdb.c", + "nss/lib/softoken/sftkdb.h", + "nss/lib/softoken/sftkdbt.h", + "nss/lib/softoken/sftkdbti.h", + "nss/lib/softoken/sftkhmac.c", + "nss/lib/softoken/sftkpars.c", + "nss/lib/softoken/sftkpars.h", + "nss/lib/softoken/sftkpwd.c", + "nss/lib/softoken/softkver.c", + "nss/lib/softoken/softkver.h", + "nss/lib/softoken/softoken.h", + "nss/lib/softoken/softoknt.h", + "nss/lib/softoken/tlsprf.c", + "nss/lib/ssl/sslerr.h", + "nss/lib/util/SECerrs.h", + "nss/lib/util/base64.h", + "nss/lib/util/ciferfam.h", + "nss/lib/util/derdec.c", + "nss/lib/util/derenc.c", + "nss/lib/util/dersubr.c", + "nss/lib/util/dertime.c", + "nss/lib/util/errstrs.c", + "nss/lib/util/hasht.h", + "nss/lib/util/nssb64.h", + "nss/lib/util/nssb64d.c", + "nss/lib/util/nssb64e.c", + "nss/lib/util/nssb64t.h", + "nss/lib/util/nssilckt.h", + "nss/lib/util/nssilock.c", + "nss/lib/util/nssilock.h", + "nss/lib/util/nsslocks.h", + "nss/lib/util/nssrwlk.c", + "nss/lib/util/nssrwlk.h", + "nss/lib/util/nssrwlkt.h", + "nss/lib/util/nssutil.h", + "nss/lib/util/oidstring.c", + "nss/lib/util/pkcs11.h", + "nss/lib/util/pkcs11f.h", + "nss/lib/util/pkcs11n.h", + "nss/lib/util/pkcs11p.h", + "nss/lib/util/pkcs11t.h", + "nss/lib/util/pkcs11u.h", + "nss/lib/util/pkcs1sig.c", + "nss/lib/util/pkcs1sig.h", + "nss/lib/util/portreg.c", + "nss/lib/util/portreg.h", + "nss/lib/util/quickder.c", + "nss/lib/util/secalgid.c", + "nss/lib/util/secasn1.h", + "nss/lib/util/secasn1d.c", + "nss/lib/util/secasn1e.c", + "nss/lib/util/secasn1t.h", + "nss/lib/util/secasn1u.c", + "nss/lib/util/seccomon.h", + "nss/lib/util/secder.h", + "nss/lib/util/secdert.h", + "nss/lib/util/secdig.c", + "nss/lib/util/secdig.h", + "nss/lib/util/secdigt.h", + "nss/lib/util/secerr.h", + "nss/lib/util/secitem.c", + "nss/lib/util/secitem.h", + "nss/lib/util/secoid.c", + "nss/lib/util/secoid.h", + "nss/lib/util/secoidt.h", + "nss/lib/util/secport.c", + "nss/lib/util/secport.h", + "nss/lib/util/sectime.c", + "nss/lib/util/templates.c", + "nss/lib/util/utf8.c", + "nss/lib/util/utilmod.c", + "nss/lib/util/utilmodt.h", + "nss/lib/util/utilpars.c", + "nss/lib/util/utilpars.h", + "nss/lib/util/utilparst.h", + "nss/lib/util/utilrename.h", + ] + + sources -= [ + # mpi_arm.c is included by mpi_arm_mac.c. + # NOTE: mpi_arm.c can be used directly on Linux. mpi_arm.c will need + # to be excluded conditionally if we start to build NSS on Linux. + "nss/lib/freebl/mpi/mpi_arm.c", + + # primes.c is included by mpprime.c. + "nss/lib/freebl/mpi/primes.c", + + # unix_rand.c and win_rand.c are included by sysrand.c. + "nss/lib/freebl/unix_rand.c", + "nss/lib/freebl/win_rand.c", + + # debug_module.c is included by pk11load.c. + "nss/lib/pk11wrap/debug_module.c", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + if (is_win) { + configs -= [ "//build/config/win:unicode" ] # Requires 8-bit mode. + } + configs += [ + "//build/config/compiler:no_chromium_code", + "//build/config/compiler:no_size_t_to_int_warning", + ] + public_configs = [ ":nss_static_config" ] + + cflags = [] + + # Only need the defines and includes not in nss_static_config. + defines = [ + "MP_API_COMPATIBLE", + "NSS_DISABLE_DBM", + "RIJNDAEL_INCLUDE_TABLES", + "SHLIB_VERSION=\"3\"", + "SOFTOKEN_SHLIB_VERSION=\"3\"", + ] + include_dirs = [ + "nss/lib/freebl/mpi", + "nss/lib/ssl", + ] + + if (is_win) { + cflags += [ "/wd4101" ] # Unreferenced local variable. + } + + if (include_nss_libpkix) { + sources += [ + "nss/lib/certhigh/certvfypkix.c", + "nss/lib/certhigh/certvfypkixprint.c", + "nss/lib/libpkix/include/pkix.h", + "nss/lib/libpkix/include/pkix_certsel.h", + "nss/lib/libpkix/include/pkix_certstore.h", + "nss/lib/libpkix/include/pkix_checker.h", + "nss/lib/libpkix/include/pkix_crlsel.h", + "nss/lib/libpkix/include/pkix_errorstrings.h", + "nss/lib/libpkix/include/pkix_params.h", + "nss/lib/libpkix/include/pkix_pl_pki.h", + "nss/lib/libpkix/include/pkix_pl_system.h", + "nss/lib/libpkix/include/pkix_results.h", + "nss/lib/libpkix/include/pkix_revchecker.h", + "nss/lib/libpkix/include/pkix_sample_modules.h", + "nss/lib/libpkix/include/pkix_util.h", + "nss/lib/libpkix/include/pkixt.h", + "nss/lib/libpkix/pkix/certsel/pkix_certselector.c", + "nss/lib/libpkix/pkix/certsel/pkix_certselector.h", + "nss/lib/libpkix/pkix/certsel/pkix_comcertselparams.c", + "nss/lib/libpkix/pkix/certsel/pkix_comcertselparams.h", + "nss/lib/libpkix/pkix/checker/pkix_basicconstraintschecker.c", + "nss/lib/libpkix/pkix/checker/pkix_basicconstraintschecker.h", + "nss/lib/libpkix/pkix/checker/pkix_certchainchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_certchainchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_crlchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_crlchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_ekuchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_ekuchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_expirationchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_expirationchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_namechainingchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_namechainingchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_nameconstraintschecker.c", + "nss/lib/libpkix/pkix/checker/pkix_nameconstraintschecker.h", + "nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_ocspchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_policychecker.c", + "nss/lib/libpkix/pkix/checker/pkix_policychecker.h", + "nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_revocationchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_revocationmethod.c", + "nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h", + "nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c", + "nss/lib/libpkix/pkix/checker/pkix_signaturechecker.h", + "nss/lib/libpkix/pkix/checker/pkix_targetcertchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_targetcertchecker.h", + "nss/lib/libpkix/pkix/crlsel/pkix_comcrlselparams.c", + "nss/lib/libpkix/pkix/crlsel/pkix_comcrlselparams.h", + "nss/lib/libpkix/pkix/crlsel/pkix_crlselector.c", + "nss/lib/libpkix/pkix/crlsel/pkix_crlselector.h", + "nss/lib/libpkix/pkix/params/pkix_procparams.c", + "nss/lib/libpkix/pkix/params/pkix_procparams.h", + "nss/lib/libpkix/pkix/params/pkix_resourcelimits.c", + "nss/lib/libpkix/pkix/params/pkix_resourcelimits.h", + "nss/lib/libpkix/pkix/params/pkix_trustanchor.c", + "nss/lib/libpkix/pkix/params/pkix_trustanchor.h", + "nss/lib/libpkix/pkix/params/pkix_valparams.c", + "nss/lib/libpkix/pkix/params/pkix_valparams.h", + "nss/lib/libpkix/pkix/results/pkix_buildresult.c", + "nss/lib/libpkix/pkix/results/pkix_buildresult.h", + "nss/lib/libpkix/pkix/results/pkix_policynode.c", + "nss/lib/libpkix/pkix/results/pkix_policynode.h", + "nss/lib/libpkix/pkix/results/pkix_valresult.c", + "nss/lib/libpkix/pkix/results/pkix_valresult.h", + "nss/lib/libpkix/pkix/results/pkix_verifynode.c", + "nss/lib/libpkix/pkix/results/pkix_verifynode.h", + "nss/lib/libpkix/pkix/store/pkix_store.c", + "nss/lib/libpkix/pkix/store/pkix_store.h", + "nss/lib/libpkix/pkix/top/pkix_build.c", + "nss/lib/libpkix/pkix/top/pkix_build.h", + "nss/lib/libpkix/pkix/top/pkix_lifecycle.c", + "nss/lib/libpkix/pkix/top/pkix_lifecycle.h", + "nss/lib/libpkix/pkix/top/pkix_validate.c", + "nss/lib/libpkix/pkix/top/pkix_validate.h", + "nss/lib/libpkix/pkix/util/pkix_error.c", + "nss/lib/libpkix/pkix/util/pkix_error.h", + "nss/lib/libpkix/pkix/util/pkix_errpaths.c", + "nss/lib/libpkix/pkix/util/pkix_list.c", + "nss/lib/libpkix/pkix/util/pkix_list.h", + "nss/lib/libpkix/pkix/util/pkix_logger.c", + "nss/lib/libpkix/pkix/util/pkix_logger.h", + "nss/lib/libpkix/pkix/util/pkix_tools.c", + "nss/lib/libpkix/pkix/util/pkix_tools.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_colcertstore.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_colcertstore.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpcertstore.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpcertstore.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpdefaultclient.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpdefaultclient.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_nsscontext.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_nsscontext.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_pk11certstore.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_pk11certstore.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_socket.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_socket.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_basicconstraints.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_basicconstraints.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyinfo.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyinfo.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicymap.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicymap.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_generalname.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_generalname.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_nameconstraints.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_nameconstraints.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_publickey.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_publickey.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bigint.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bigint.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bytearray.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bytearray.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_error.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_hashtable.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_hashtable.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_monitorlock.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_monitorlock.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_object.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_object.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_primhash.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_primhash.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_rwlock.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_rwlock.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_string.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_string.h", + ] + + # Disable the LDAP code in libpkix. + defines += [ "NSS_PKIX_NO_LDAP" ] + + include_dirs += [ + "nss/lib/libpkix/include", + "nss/lib/libpkix/pkix/certsel", + "nss/lib/libpkix/pkix/checker", + "nss/lib/libpkix/pkix/crlsel", + "nss/lib/libpkix/pkix/params", + "nss/lib/libpkix/pkix/results", + "nss/lib/libpkix/pkix/store", + "nss/lib/libpkix/pkix/top", + "nss/lib/libpkix/pkix/util", + "nss/lib/libpkix/pkix_pl_nss/module", + "nss/lib/libpkix/pkix_pl_nss/pki", + "nss/lib/libpkix/pkix_pl_nss/system", + ] + } else { + defines += [ "NSS_DISABLE_LIBPKIX" ] + } + + if (!include_nss_root_certs) { + defines += [ "NSS_DISABLE_ROOT_CERTS" ] + } + + if (current_cpu == "x64" && !is_win) { + sources -= [ + "nss/lib/freebl/chacha20/chacha20.c", + "nss/lib/freebl/poly1305/poly1305.c", + ] + } else { + sources -= [ + "nss/lib/freebl/chacha20/chacha20_vec.c", + "nss/lib/freebl/poly1305/poly1305-donna-x64-sse2-incremental-source.c", + ] + } + + if (is_mac || is_ios) { + sources -= [ "nss/lib/freebl/mpi/mpi_amd64.c" ] + cflags += [ + "-include", + rebase_path("//third_party/nss/nss/lib/freebl/nss_build_config_mac.h", + root_build_dir), + ] + defines += [ + "XP_UNIX", + "DARWIN", + "HAVE_STRERROR", + "HAVE_BSD_FLOCK", + "SHLIB_SUFFIX=\"dylib\"", + "SHLIB_PREFIX=\"lib\"", + "SOFTOKEN_LIB_NAME=\"libsoftokn3.dylib\"", + ] + + configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] + } else { + # Not Mac/iOS. + sources -= [ "nss/lib/freebl/mpi/mpi_arm_mac.c" ] + } + + if (is_win) { + defines += [ + "SHLIB_SUFFIX=\"dll\"", + "SHLIB_PREFIX=\"\"", + "SOFTOKEN_LIB_NAME=\"softokn3.dll\"", + "XP_PC", + "WIN32", + "WIN95", + ] + + if (current_cpu == "x86") { + defines += [ + "NSS_X86_OR_X64", + "NSS_X86", + "_X86_", + "MP_ASSEMBLY_MULTIPLY", + "MP_ASSEMBLY_SQUARE", + "MP_ASSEMBLY_DIV_2DX1D", + "MP_USE_UINT_DIGIT", + "MP_NO_MP_WORD", + "USE_HW_AES", + "INTEL_GCM", + ] + sources -= [ "nss/lib/freebl/mpi/mpi_amd64.c" ] + } else if (current_cpu == "x64") { + sources -= [ + "nss/lib/freebl/intel-aes-x86-masm.asm", + "nss/lib/freebl/mpi/mpi_amd64.c", + "nss/lib/freebl/mpi/mpi_x86_asm.c", + ] + defines += [ + "NSS_USE_64", + "NSS_X86_OR_X64", + "NSS_X64", + "_AMD64_", + "MP_CHAR_STORE_SLOW", + "MP_IS_LITTLE_ENDIAN", + "WIN64", + ] + } + } else { + # Not Windows. + sources -= [ + # mpi_x86_asm.c contains MSVC inline assembly code. + "nss/lib/freebl/mpi/mpi_x86_asm.c", + ] + } + + if (is_clang) { + cflags += [ + # nss doesn"t explicitly cast between different enum types. + "-Wno-conversion", + + # nss passes "const char*" through "void*". + "-Wno-incompatible-pointer-types", + + # nss prefers `a && b || c` over `(a && b) || c`. + "-Wno-logical-op-parentheses", + + # nss doesn"t use exhaustive switches on enums + "-Wno-switch", + + # nss has some `unsigned < 0` checks. + "-Wno-tautological-compare", + ] + } + + public_deps = [ ":nspr" ] + deps = [ + ":nspr", + "//third_party/sqlite", + ] + + if (is_win && current_cpu == "x86") { + deps += [ ":nss_static_avx" ] + } + } +} # Windows/Mac/iOS. diff --git a/build/secondary/third_party/ocmock/BUILD.gn b/build/secondary/third_party/ocmock/BUILD.gn new file mode 100644 index 0000000000000..66f2e49f30f8c --- /dev/null +++ b/build/secondary/third_party/ocmock/BUILD.gn @@ -0,0 +1,121 @@ +# 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. + +ocmock_path = "//third_party/ocmock/Source" + +# OCMock headers use `#import `. +config("ocmock_config") { + include_dirs = [ "$ocmock_path" ] +} + +# Target that compiles all sources to .o files but does not produce a static +# library for use in macOS desktop tests. +source_set("ocmock_src") { + configs -= [ + "//build/config/compiler:chromium_code", + "//build/config/gcc:symbol_visibility_hidden", + "//build/config:symbol_visibility_hidden", + ] + all_dependent_configs = [ ":ocmock_config" ] + cflags = [ + "-fvisibility=default", + "-Wno-misleading-indentation", + ] + if (is_ios) { + cflags += [ "-mios-simulator-version-min=$ios_testing_deployment_target" ] + } + + sources = [ + "$ocmock_path/OCMock/NSInvocation+OCMAdditions.h", + "$ocmock_path/OCMock/NSInvocation+OCMAdditions.m", + "$ocmock_path/OCMock/NSMethodSignature+OCMAdditions.h", + "$ocmock_path/OCMock/NSMethodSignature+OCMAdditions.m", + "$ocmock_path/OCMock/NSNotificationCenter+OCMAdditions.h", + "$ocmock_path/OCMock/NSNotificationCenter+OCMAdditions.m", + "$ocmock_path/OCMock/NSObject+OCMAdditions.h", + "$ocmock_path/OCMock/NSObject+OCMAdditions.m", + "$ocmock_path/OCMock/NSValue+OCMAdditions.h", + "$ocmock_path/OCMock/NSValue+OCMAdditions.m", + "$ocmock_path/OCMock/OCClassMockObject.h", + "$ocmock_path/OCMock/OCClassMockObject.m", + "$ocmock_path/OCMock/OCMArg.h", + "$ocmock_path/OCMock/OCMArg.m", + "$ocmock_path/OCMock/OCMArgAction.h", + "$ocmock_path/OCMock/OCMArgAction.m", + "$ocmock_path/OCMock/OCMBlockArgCaller.h", + "$ocmock_path/OCMock/OCMBlockArgCaller.m", + "$ocmock_path/OCMock/OCMBlockCaller.h", + "$ocmock_path/OCMock/OCMBlockCaller.m", + "$ocmock_path/OCMock/OCMBoxedReturnValueProvider.h", + "$ocmock_path/OCMock/OCMBoxedReturnValueProvider.m", + "$ocmock_path/OCMock/OCMConstraint.h", + "$ocmock_path/OCMock/OCMConstraint.m", + "$ocmock_path/OCMock/OCMExceptionReturnValueProvider.h", + "$ocmock_path/OCMock/OCMExceptionReturnValueProvider.m", + "$ocmock_path/OCMock/OCMExpectationRecorder.h", + "$ocmock_path/OCMock/OCMExpectationRecorder.m", + "$ocmock_path/OCMock/OCMFunctions.h", + "$ocmock_path/OCMock/OCMFunctions.m", + "$ocmock_path/OCMock/OCMFunctionsPrivate.h", + "$ocmock_path/OCMock/OCMIndirectReturnValueProvider.h", + "$ocmock_path/OCMock/OCMIndirectReturnValueProvider.m", + "$ocmock_path/OCMock/OCMInvocationExpectation.h", + "$ocmock_path/OCMock/OCMInvocationExpectation.m", + "$ocmock_path/OCMock/OCMInvocationMatcher.h", + "$ocmock_path/OCMock/OCMInvocationMatcher.m", + "$ocmock_path/OCMock/OCMInvocationStub.h", + "$ocmock_path/OCMock/OCMInvocationStub.m", + "$ocmock_path/OCMock/OCMLocation.h", + "$ocmock_path/OCMock/OCMLocation.m", + "$ocmock_path/OCMock/OCMMacroState.h", + "$ocmock_path/OCMock/OCMMacroState.m", + "$ocmock_path/OCMock/OCMNonRetainingObjectReturnValueProvider.h", + "$ocmock_path/OCMock/OCMNonRetainingObjectReturnValueProvider.m", + "$ocmock_path/OCMock/OCMNotificationPoster.h", + "$ocmock_path/OCMock/OCMNotificationPoster.m", + "$ocmock_path/OCMock/OCMObjectReturnValueProvider.h", + "$ocmock_path/OCMock/OCMObjectReturnValueProvider.m", + "$ocmock_path/OCMock/OCMObserverRecorder.h", + "$ocmock_path/OCMock/OCMObserverRecorder.m", + "$ocmock_path/OCMock/OCMPassByRefSetter.h", + "$ocmock_path/OCMock/OCMPassByRefSetter.m", + "$ocmock_path/OCMock/OCMQuantifier.h", + "$ocmock_path/OCMock/OCMQuantifier.m", + "$ocmock_path/OCMock/OCMRealObjectForwarder.h", + "$ocmock_path/OCMock/OCMRealObjectForwarder.m", + "$ocmock_path/OCMock/OCMRecorder.h", + "$ocmock_path/OCMock/OCMRecorder.m", + "$ocmock_path/OCMock/OCMStubRecorder.h", + "$ocmock_path/OCMock/OCMStubRecorder.m", + "$ocmock_path/OCMock/OCMVerifier.h", + "$ocmock_path/OCMock/OCMVerifier.m", + "$ocmock_path/OCMock/OCMock.h", + "$ocmock_path/OCMock/OCMockObject.h", + "$ocmock_path/OCMock/OCMockObject.m", + "$ocmock_path/OCMock/OCObserverMockObject.h", + "$ocmock_path/OCMock/OCObserverMockObject.m", + "$ocmock_path/OCMock/OCPartialMockObject.h", + "$ocmock_path/OCMock/OCPartialMockObject.m", + "$ocmock_path/OCMock/OCProtocolMockObject.h", + "$ocmock_path/OCMock/OCProtocolMockObject.m", + ] + + frameworks = [ "Foundation.framework" ] +} + +shared_library("ocmock_shared") { + deps = [ ":ocmock_src" ] + cflags = [ "-fvisibility=default" ] + ldflags = [ "-Wl,-install_name,@rpath/Frameworks/libocmock_shared.dylib" ] +} + +# Generates a static library, used in iOS unit test targets +static_library("ocmock") { + # Force the static lib to include code from dependencies + complete_static_lib = true + if (is_ios) { + cflags = [ "-mios-simulator-version-min=$ios_testing_deployment_target" ] + } + public_deps = [ ":ocmock_src" ] +} diff --git a/build/secondary/third_party/pkg/quiver/BUILD.gn b/build/secondary/third_party/pkg/quiver/BUILD.gn new file mode 100644 index 0000000000000..8917550b30844 --- /dev/null +++ b/build/secondary/third_party/pkg/quiver/BUILD.gn @@ -0,0 +1,83 @@ +# Copyright 2021 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. + +# TODO(flutter/flutter#85356): This file was originally generated by the +# fuchsia.git script: `package_importer.py`. The generated `BUILD.gn` files were +# copied to the flutter repo to support `dart_library` targets used for +# Flutter-Fuchsia integration tests. This file can be maintained by hand, but it +# would be better to implement a script for Flutter, to either generate these +# BUILD.gn files or dynamically generate the GN targets. + +import("//flutter/tools/fuchsia/dart/dart_library.gni") + +dart_library("quiver") { + package_name = "quiver" + + # The current version of this library is not null safe + language_version = "2.0" + + deps = [ + "//third_party/dart/pkg/meta", + "//third_party/dart/third_party/pkg/matcher", + ] + + sources = [ + "async.dart", + "cache.dart", + "check.dart", + "collection.dart", + "core.dart", + "io.dart", + "iterables.dart", + "mirrors.dart", + "pattern.dart", + "src/async/collect.dart", + "src/async/concat.dart", + "src/async/countdown_timer.dart", + "src/async/enumerate.dart", + "src/async/future_stream.dart", + "src/async/iteration.dart", + "src/async/metronome.dart", + "src/async/stream_buffer.dart", + "src/async/stream_router.dart", + "src/async/string.dart", + "src/cache/cache.dart", + "src/cache/map_cache.dart", + "src/collection/bimap.dart", + "src/collection/delegates/iterable.dart", + "src/collection/delegates/list.dart", + "src/collection/delegates/map.dart", + "src/collection/delegates/queue.dart", + "src/collection/delegates/set.dart", + "src/collection/lru_map.dart", + "src/collection/multimap.dart", + "src/collection/treeset.dart", + "src/core/hash.dart", + "src/core/optional.dart", + "src/iterables/concat.dart", + "src/iterables/count.dart", + "src/iterables/cycle.dart", + "src/iterables/enumerate.dart", + "src/iterables/generating_iterable.dart", + "src/iterables/infinite_iterable.dart", + "src/iterables/merge.dart", + "src/iterables/min_max.dart", + "src/iterables/partition.dart", + "src/iterables/range.dart", + "src/iterables/zip.dart", + "src/time/clock.dart", + "src/time/duration_unit_constants.dart", + "src/time/util.dart", + "strings.dart", + "testing/async.dart", + "testing/equality.dart", + "testing/runtime.dart", + "testing/src/async/fake_async.dart", + "testing/src/equality/equality.dart", + "testing/src/runtime/checked_mode.dart", + "testing/src/time/time.dart", + "testing/time.dart", + "time.dart", + ] +} diff --git a/build/secondary/third_party/shaderc_flutter/BUILD.gn b/build/secondary/third_party/shaderc_flutter/BUILD.gn new file mode 100644 index 0000000000000..ec41f7ac753db --- /dev/null +++ b/build/secondary/third_party/shaderc_flutter/BUILD.gn @@ -0,0 +1,83 @@ +# 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. + +shaderc_base = "//third_party/shaderc" + +config("shaderc_util_config") { + include_dirs = [ "$shaderc_base/libshaderc_util/include/" ] +} + +source_set("shaderc_util_flutter") { + public_configs = [ ":shaderc_util_config" ] + + configs += + [ "//third_party/vulkan-deps/spirv-tools/src:spvtools_public_config" ] + + public_deps = [ + "//third_party/vulkan-deps/glslang/src:glslang_sources", + "//third_party/vulkan-deps/spirv-tools/src:spvtools", + ] + + defines = [ "ENABLE_HLSL=1" ] + + if (is_clang) { + cflags_cc = [ + "-Wno-deprecated-copy", + "-Wno-unknown-warning-option", + ] + } + + sources = [ + "$shaderc_base/libshaderc_util/include/libshaderc_util/counting_includer.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/exceptions.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/file_finder.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/format.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/io_shaderc.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/message.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/mutex.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/resources.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/string_piece.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/universal_unistd.h", + "$shaderc_base/libshaderc_util/include/libshaderc_util/version_profile.h", + "$shaderc_base/libshaderc_util/src/compiler.cc", + "$shaderc_base/libshaderc_util/src/file_finder.cc", + "$shaderc_base/libshaderc_util/src/io_shaderc.cc", + "$shaderc_base/libshaderc_util/src/message.cc", + "$shaderc_base/libshaderc_util/src/resources.cc", + "$shaderc_base/libshaderc_util/src/shader_stage.cc", + "$shaderc_base/libshaderc_util/src/spirv_tools_wrapper.cc", + "$shaderc_base/libshaderc_util/src/version_profile.cc", + ] +} + +config("shaderc_config") { + include_dirs = [ "$shaderc_base/libshaderc/include/" ] +} + +source_set("shaderc_flutter") { + defines = [ "SHADERC_IMPLEMENTATION" ] + + public_configs = [ ":shaderc_config" ] + + configs += + [ "//third_party/vulkan-deps/spirv-tools/src:spvtools_public_config" ] + + deps = [ ":shaderc_util_flutter" ] + + public_deps = [ + "//third_party/vulkan-deps/glslang/src:glslang_sources", + "//third_party/vulkan-deps/spirv-tools/src:spvtools", + ] + + sources = [ + "$shaderc_base/libshaderc/include/shaderc/env.h", + "$shaderc_base/libshaderc/include/shaderc/shaderc.h", + "$shaderc_base/libshaderc/include/shaderc/shaderc.hpp", + "$shaderc_base/libshaderc/include/shaderc/status.h", + "$shaderc_base/libshaderc/include/shaderc/visibility.h", + "$shaderc_base/libshaderc/src/shaderc.cc", + "$shaderc_base/libshaderc/src/shaderc_private.h", + ] +} diff --git a/build/secondary/third_party/spirv_cross_flutter/BUILD.gn b/build/secondary/third_party/spirv_cross_flutter/BUILD.gn new file mode 100644 index 0000000000000..2429fefe6bed3 --- /dev/null +++ b/build/secondary/third_party/spirv_cross_flutter/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_root = "//third_party/vulkan-deps/spirv-cross/src" + +config("spirv_cross_public") { + include_dirs = [ source_root ] + + defines = [ "SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS" ] +} + +source_set("spirv_cross_flutter") { + public_configs = [ ":spirv_cross_public" ] + + sources = [ + "$source_root/GLSL.std.450.h", + "$source_root/spirv.hpp", + "$source_root/spirv_cfg.cpp", + "$source_root/spirv_cfg.hpp", + "$source_root/spirv_common.hpp", + "$source_root/spirv_cross.cpp", + "$source_root/spirv_cross.hpp", + "$source_root/spirv_cross_containers.hpp", + "$source_root/spirv_cross_error_handling.hpp", + "$source_root/spirv_cross_parsed_ir.cpp", + "$source_root/spirv_cross_parsed_ir.hpp", + "$source_root/spirv_cross_util.cpp", + "$source_root/spirv_cross_util.hpp", + "$source_root/spirv_glsl.cpp", + "$source_root/spirv_glsl.hpp", + "$source_root/spirv_hlsl.cpp", + "$source_root/spirv_hlsl.hpp", + "$source_root/spirv_msl.cpp", + "$source_root/spirv_msl.hpp", + "$source_root/spirv_parser.cpp", + "$source_root/spirv_parser.hpp", + "$source_root/spirv_reflect.cpp", + "$source_root/spirv_reflect.hpp", + ] +} diff --git a/build/secondary/third_party/stb/BUILD.gn b/build/secondary/third_party/stb/BUILD.gn new file mode 100644 index 0000000000000..68be14ffb5d40 --- /dev/null +++ b/build/secondary/third_party/stb/BUILD.gn @@ -0,0 +1,15 @@ +# 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. + +source_root = "//third_party/stb" + +source_set("stb_truetype") { + testonly = true + + public = [ "$source_root/stb_truetype.h" ] + + include_dirs = [ "$source_root" ] + + sources = [ "//flutter/build/secondary/third_party/stb/stb_truetype_stub.cc" ] +} diff --git a/build/secondary/third_party/stb/stb_truetype_stub.cc b/build/secondary/third_party/stb/stb_truetype_stub.cc new file mode 100644 index 0000000000000..eaca9020be07c --- /dev/null +++ b/build/secondary/third_party/stb/stb_truetype_stub.cc @@ -0,0 +1,6 @@ +// 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. + +#define STB_TRUETYPE_IMPLEMENTATION +#include "third_party/stb/stb_truetype.h" diff --git a/build/secondary/third_party/tinygltf/BUILD.gn b/build/secondary/third_party/tinygltf/BUILD.gn new file mode 100644 index 0000000000000..d5f6b6cfff4b7 --- /dev/null +++ b/build/secondary/third_party/tinygltf/BUILD.gn @@ -0,0 +1,23 @@ +# 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. + +source_root = "//third_party/tinygltf" + +source_set("tinygltf") { + public = [ "$source_root/tiny_gltf.h" ] + + if (is_clang) { + cflags_cc = [ "-Wno-sign-compare" ] + } + + include_dirs = [ + "$source_root", + "$source_root/third_party/include", + ] + + sources = + [ "//flutter/build/secondary/third_party/tinygltf/tinygltf_stub.cc" ] + + deps = [ "//third_party/json" ] +} diff --git a/build/secondary/third_party/tinygltf/tinygltf_stub.cc b/build/secondary/third_party/tinygltf/tinygltf_stub.cc new file mode 100644 index 0000000000000..88e369d44f5d5 --- /dev/null +++ b/build/secondary/third_party/tinygltf/tinygltf_stub.cc @@ -0,0 +1,11 @@ +// 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 "nlohmann/json.hpp" + +#define TINYGLTF_IMPLEMENTATION +#define TINYGLTF_NO_INCLUDE_JSON +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "third_party/tinygltf/tiny_gltf.h" diff --git a/build/secondary/third_party/vulkan_memory_allocator/BUILD.gn b/build/secondary/third_party/vulkan_memory_allocator/BUILD.gn new file mode 100644 index 0000000000000..8d4587d91306e --- /dev/null +++ b/build/secondary/third_party/vulkan_memory_allocator/BUILD.gn @@ -0,0 +1,40 @@ +# Copyright 2020 The ANGLE Project Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Custom GN integration for VulkanMemoryAllocator. + +declare_args() { + vma_vulkan_headers_dir = "//third_party/vulkan-deps/vulkan-headers/src" +} + +config("vulkan_memory_allocator_config") { + include_dirs = [ "include" ] + if (is_clang) { + cflags_cc = [ + "-Wno-c++98-compat-extra-semi", + "-Wno-deprecated-copy", + "-Wno-implicit-fallthrough", + "-Wno-nullability-completeness", + "-Wno-suggest-destructor-override", + "-Wno-suggest-override", + "-Wno-unused-private-field", + "-Wno-unused-variable", + ] + } + if (is_win && !is_clang) { + cflags_cc = [ + "/wd4189", # local variable is initialized but not referenced + ] + } + defines = [ + "VMA_DYNAMIC_VULKAN_FUNCTIONS=0", + "VMA_STATIC_VULKAN_FUNCTIONS=0", + ] +} + +source_set("vulkan_memory_allocator") { + sources = [ "include/vk_mem_alloc.h" ] + deps = [ "${vma_vulkan_headers_dir}:vulkan_headers" ] + public_configs = [ ":vulkan_memory_allocator_config" ] +} diff --git a/build/secondary/third_party/vulkan_validation_layers/BUILD.gn b/build/secondary/third_party/vulkan_validation_layers/BUILD.gn new file mode 100644 index 0000000000000..688ba45fda98e --- /dev/null +++ b/build/secondary/third_party/vulkan_validation_layers/BUILD.gn @@ -0,0 +1,493 @@ +# 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. + +################################################################################ +# This is a temporary secondary build file until the rtti issue is solved +# in vulkan-deps +################################################################################ + +import("//build_overrides/vulkan_validation_layers.gni") + +_checkout_dir = "//third_party/vulkan-deps/vulkan-validation-layers/src" + +vulkan_undefine_configs = [] +if (is_win) { + vulkan_undefine_configs += [ "//build/config/win:unicode" ] +} + +vulkan_gen_dir = "$target_gen_dir/$vulkan_gen_subdir" +raw_vulkan_gen_dir = rebase_path(vulkan_gen_dir, root_build_dir) + +vulkan_data_dir = "$root_out_dir/$vulkan_data_subdir" +raw_vulkan_data_dir = rebase_path(vulkan_data_dir, root_build_dir) + +raw_root_out_dir = rebase_path(root_out_dir, root_build_dir) + +# This special action is needed to remove old VVL objects that are now renamed. +action("vulkan_clean_old_validation_layer_objects") { + script = "$_checkout_dir/build-gn/remove_files.py" + + # inputs is a (random) new file since the vvl roll, used to ensure the cleanup is done only once + inputs = [ "$_checkout_dir/layers/gpu_validation/gpu_validation.cpp" ] + outputs = [ "$vulkan_gen_dir/old_vvl_files_are_removed" ] + args = [ + "$raw_vulkan_gen_dir/old_vvl_files_are_removed", + "$raw_root_out_dir/libVkLayer*", + "$raw_root_out_dir/VkLayer*", + "$raw_vulkan_data_dir/VkLayer*.json", + ] +} + +config("generated_layers_config") { + if (is_clang) { + cflags = [ + "-Wno-conversion", + "-Wno-deprecated-copy", + "-Wno-extra-semi", + "-Wno-implicit-fallthrough", + "-Wno-missing-field-initializers", + "-Wno-newline-eof", + "-Wno-sign-compare", + "-Wno-unused-const-variable", + ] + } +} + +config("vulkan_internal_config") { + defines = [ + "VULKAN_NON_CMAKE_BUILD", + "VK_ENABLE_BETA_EXTENSIONS", + ] + + if (!is_win) { + cflags_cc = [ "-std=c++17" ] + } else { + cflags_cc = [ "/std:c++17" ] + } + + cflags = [] + if (is_clang || !is_win) { + cflags += [ "-Wno-unused-function" ] + } + if (is_clang && is_mac) { + cflags += [ "-Wno-unguarded-availability-new" ] + } + if (is_linux) { + defines += [ + "SYSCONFDIR=\"/etc\"", + "FALLBACK_CONFIG_DIRS=\"/etc/xdg\"", + "FALLBACK_DATA_DIRS=\"/usr/local/share:/usr/share\"", + ] + } + + # Suppress warnings the vulkan code doesn't comply with. + if (is_fuchsia) { + configs = [ "//build/config:Wno-unused-but-set-variable" ] + } + if (is_clang) { + cflags += [ "-Wno-extra-semi" ] + } +} + +# The validation layers +# --------------------- + +config("vulkan_layer_config") { + include_dirs = [ + "$_checkout_dir/layers", + "$_checkout_dir/layers/external", + "$_checkout_dir/layers/generated", + ] + if (is_clang) { + cflags = [ "-Wno-extra-semi" ] + } +} + +core_validation_sources = [ + "$_checkout_dir/layers/containers/qfo_transfer.h", + "$_checkout_dir/layers/containers/range_vector.h", + "$_checkout_dir/layers/containers/subresource_adapter.cpp", + "$_checkout_dir/layers/containers/subresource_adapter.h", + "$_checkout_dir/layers/core_checks/cc_android.cpp", + "$_checkout_dir/layers/core_checks/cc_buffer.cpp", + "$_checkout_dir/layers/core_checks/cc_buffer_address.h", + "$_checkout_dir/layers/core_checks/cc_cmd_buffer.cpp", + "$_checkout_dir/layers/core_checks/cc_cmd_buffer_dynamic.cpp", + "$_checkout_dir/layers/core_checks/cc_copy_blit_resolve.cpp", + "$_checkout_dir/layers/core_checks/cc_descriptor.cpp", + "$_checkout_dir/layers/core_checks/cc_device.cpp", + "$_checkout_dir/layers/core_checks/cc_device_memory.cpp", + "$_checkout_dir/layers/core_checks/cc_drawdispatch.cpp", + "$_checkout_dir/layers/core_checks/cc_external_object.cpp", + "$_checkout_dir/layers/core_checks/cc_image.cpp", + "$_checkout_dir/layers/core_checks/cc_image_layout.cpp", + "$_checkout_dir/layers/core_checks/cc_pipeline.cpp", + "$_checkout_dir/layers/core_checks/cc_pipeline_compute.cpp", + "$_checkout_dir/layers/core_checks/cc_pipeline_graphics.cpp", + "$_checkout_dir/layers/core_checks/cc_pipeline_ray_tracing.cpp", + "$_checkout_dir/layers/core_checks/cc_query.cpp", + "$_checkout_dir/layers/core_checks/cc_queue.cpp", + "$_checkout_dir/layers/core_checks/cc_ray_tracing.cpp", + "$_checkout_dir/layers/core_checks/cc_render_pass.cpp", + "$_checkout_dir/layers/core_checks/cc_shader.cpp", + "$_checkout_dir/layers/core_checks/cc_shader.h", + "$_checkout_dir/layers/core_checks/cc_synchronization.cpp", + "$_checkout_dir/layers/core_checks/cc_video.cpp", + "$_checkout_dir/layers/core_checks/cc_wsi.cpp", + "$_checkout_dir/layers/core_checks/cc_ycbcr.cpp", + "$_checkout_dir/layers/core_checks/core_validation.h", + "$_checkout_dir/layers/error_message/core_error_location.cpp", + "$_checkout_dir/layers/error_message/core_error_location.h", + "$_checkout_dir/layers/error_message/validation_error_enums.h", + "$_checkout_dir/layers/external/vma/vk_mem_alloc.h", + "$_checkout_dir/layers/external/vma/vma.cpp", + "$_checkout_dir/layers/external/vma/vma.h", + "$_checkout_dir/layers/generated/command_validation.cpp", + "$_checkout_dir/layers/generated/command_validation.h", + "$_checkout_dir/layers/generated/gpu_as_inspection_comp.h", + "$_checkout_dir/layers/generated/gpu_pre_dispatch_comp.h", + "$_checkout_dir/layers/generated/gpu_pre_draw_vert.h", + "$_checkout_dir/layers/generated/spirv_grammar_helper.cpp", + "$_checkout_dir/layers/generated/spirv_grammar_helper.h", + "$_checkout_dir/layers/generated/spirv_validation_helper.cpp", + "$_checkout_dir/layers/generated/sync_validation_types.cpp", + "$_checkout_dir/layers/generated/sync_validation_types.h", + "$_checkout_dir/layers/gpu_shaders/gpu_shaders_constants.h", + "$_checkout_dir/layers/gpu_validation/gpu_utils.cpp", + "$_checkout_dir/layers/gpu_validation/gpu_utils.h", + "$_checkout_dir/layers/gpu_validation/gpu_validation.cpp", + "$_checkout_dir/layers/gpu_validation/gpu_validation.h", + "$_checkout_dir/layers/gpu_validation/gpu_vuids.h", + "$_checkout_dir/layers/state_tracker/base_node.cpp", + "$_checkout_dir/layers/state_tracker/base_node.h", + "$_checkout_dir/layers/state_tracker/buffer_state.cpp", + "$_checkout_dir/layers/state_tracker/buffer_state.h", + "$_checkout_dir/layers/state_tracker/cmd_buffer_state.cpp", + "$_checkout_dir/layers/state_tracker/cmd_buffer_state.h", + "$_checkout_dir/layers/state_tracker/descriptor_sets.cpp", + "$_checkout_dir/layers/state_tracker/descriptor_sets.h", + "$_checkout_dir/layers/state_tracker/device_memory_state.cpp", + "$_checkout_dir/layers/state_tracker/device_memory_state.h", + "$_checkout_dir/layers/state_tracker/device_state.h", + "$_checkout_dir/layers/state_tracker/image_layout_map.cpp", + "$_checkout_dir/layers/state_tracker/image_layout_map.h", + "$_checkout_dir/layers/state_tracker/image_state.cpp", + "$_checkout_dir/layers/state_tracker/image_state.h", + "$_checkout_dir/layers/state_tracker/pipeline_layout_state.cpp", + "$_checkout_dir/layers/state_tracker/pipeline_layout_state.h", + "$_checkout_dir/layers/state_tracker/pipeline_state.cpp", + "$_checkout_dir/layers/state_tracker/pipeline_state.h", + "$_checkout_dir/layers/state_tracker/pipeline_sub_state.cpp", + "$_checkout_dir/layers/state_tracker/pipeline_sub_state.h", + "$_checkout_dir/layers/state_tracker/query_state.h", + "$_checkout_dir/layers/state_tracker/queue_state.cpp", + "$_checkout_dir/layers/state_tracker/queue_state.h", + "$_checkout_dir/layers/state_tracker/ray_tracing_state.h", + "$_checkout_dir/layers/state_tracker/render_pass_state.cpp", + "$_checkout_dir/layers/state_tracker/render_pass_state.h", + "$_checkout_dir/layers/state_tracker/sampler_state.h", + "$_checkout_dir/layers/state_tracker/shader_instruction.cpp", + "$_checkout_dir/layers/state_tracker/shader_instruction.h", + "$_checkout_dir/layers/state_tracker/shader_module.cpp", + "$_checkout_dir/layers/state_tracker/shader_module.h", + "$_checkout_dir/layers/state_tracker/state_tracker.cpp", + "$_checkout_dir/layers/state_tracker/state_tracker.h", + "$_checkout_dir/layers/state_tracker/video_session_state.cpp", + "$_checkout_dir/layers/state_tracker/video_session_state.h", + "$_checkout_dir/layers/sync/sync_utils.cpp", + "$_checkout_dir/layers/sync/sync_utils.h", + "$_checkout_dir/layers/sync/sync_validation.cpp", + "$_checkout_dir/layers/sync/sync_validation.h", + "$_checkout_dir/layers/sync/sync_vuid_maps.cpp", + "$_checkout_dir/layers/sync/sync_vuid_maps.h", + "$_checkout_dir/layers/utils/android_ndk_types.h", + "$_checkout_dir/layers/utils/convert_to_renderpass2.cpp", + "$_checkout_dir/layers/utils/convert_to_renderpass2.h", +] + +object_lifetimes_sources = [ + "$_checkout_dir/layers/generated/object_tracker.cpp", + "$_checkout_dir/layers/generated/object_tracker.h", + "$_checkout_dir/layers/object_tracker/object_lifetime_validation.h", + "$_checkout_dir/layers/object_tracker/object_tracker_utils.cpp", +] + +stateless_validation_sources = [ + "$_checkout_dir/layers/generated/enum_flag_bits.h", + "$_checkout_dir/layers/generated/parameter_validation.cpp", + "$_checkout_dir/layers/generated/parameter_validation.h", + "$_checkout_dir/layers/stateless/parameter_name.h", + "$_checkout_dir/layers/stateless/sl_buffer.cpp", + "$_checkout_dir/layers/stateless/sl_cmd_buffer.cpp", + "$_checkout_dir/layers/stateless/sl_cmd_buffer_dynamic.cpp", + "$_checkout_dir/layers/stateless/sl_descriptor.cpp", + "$_checkout_dir/layers/stateless/sl_device_memory.cpp", + "$_checkout_dir/layers/stateless/sl_external_object.cpp", + "$_checkout_dir/layers/stateless/sl_framebuffer.cpp", + "$_checkout_dir/layers/stateless/sl_image.cpp", + "$_checkout_dir/layers/stateless/sl_instance_device.cpp", + "$_checkout_dir/layers/stateless/sl_pipeline.cpp", + "$_checkout_dir/layers/stateless/sl_ray_tracing.cpp", + "$_checkout_dir/layers/stateless/sl_render_pass.cpp", + "$_checkout_dir/layers/stateless/sl_synchronization.cpp", + "$_checkout_dir/layers/stateless/sl_wsi.cpp", + "$_checkout_dir/layers/stateless/stateless_validation.h", +] + +thread_safety_sources = [ + "$_checkout_dir/layers/generated/thread_safety.cpp", + "$_checkout_dir/layers/generated/thread_safety.h", +] + +unique_objects_sources = [] + +best_practices_sources = [ + "$_checkout_dir/layers/best_practices/best_practices_error_enums.h", + "$_checkout_dir/layers/best_practices/best_practices_utils.cpp", + "$_checkout_dir/layers/best_practices/best_practices_validation.h", + "$_checkout_dir/layers/best_practices/bp_buffer.cpp", + "$_checkout_dir/layers/best_practices/bp_cmd_buffer.cpp", + "$_checkout_dir/layers/best_practices/bp_copy_blit_resolve.cpp", + "$_checkout_dir/layers/best_practices/bp_descriptor.cpp", + "$_checkout_dir/layers/best_practices/bp_device_memory.cpp", + "$_checkout_dir/layers/best_practices/bp_drawdispatch.cpp", + "$_checkout_dir/layers/best_practices/bp_framebuffer.cpp", + "$_checkout_dir/layers/best_practices/bp_image.cpp", + "$_checkout_dir/layers/best_practices/bp_instance_device.cpp", + "$_checkout_dir/layers/best_practices/bp_pipeline.cpp", + "$_checkout_dir/layers/best_practices/bp_ray_tracing.cpp", + "$_checkout_dir/layers/best_practices/bp_render_pass.cpp", + "$_checkout_dir/layers/best_practices/bp_synchronization.cpp", + "$_checkout_dir/layers/best_practices/bp_video.cpp", + "$_checkout_dir/layers/best_practices/bp_wsi.cpp", + "$_checkout_dir/layers/generated/best_practices.cpp", + "$_checkout_dir/layers/generated/best_practices.h", +] + +debug_printf_sources = [ + "$_checkout_dir/layers/gpu_validation/debug_printf.cpp", + "$_checkout_dir/layers/gpu_validation/debug_printf.h", +] + +chassis_sources = [ + "$_checkout_dir/layers/generated/chassis.cpp", + "$_checkout_dir/layers/generated/chassis.h", + "$_checkout_dir/layers/generated/chassis_dispatch_helper.h", + "$_checkout_dir/layers/generated/layer_chassis_dispatch.cpp", + "$_checkout_dir/layers/generated/layer_chassis_dispatch.h", + "$_checkout_dir/layers/generated/valid_param_values.cpp", + "$_checkout_dir/layers/generated/valid_param_values.h", + "$_checkout_dir/layers/generated/vk_dispatch_table_helper.h", + "$_checkout_dir/layers/generated/vk_extension_helper.h", + "$_checkout_dir/layers/generated/vk_safe_struct.cpp", + "$_checkout_dir/layers/layer_options.cpp", + "$_checkout_dir/layers/layer_options.h", + "$_checkout_dir/layers/vk_layer_settings_ext.h", + "$vulkan_headers_dir/include/vulkan/vk_layer.h", + "$vulkan_headers_dir/include/vulkan/vulkan.h", +] + +layers = [ [ + "khronos_validation", + core_validation_sources + object_lifetimes_sources + + stateless_validation_sources + thread_safety_sources + + unique_objects_sources + best_practices_sources + + debug_printf_sources + chassis_sources, + [ ":vulkan_core_validation_glslang" ], + [], + ] ] + +if (!is_android) { + action("vulkan_gen_json_files") { + script = "$_checkout_dir/build-gn/generate_vulkan_layers_json.py" + + deps = [ "$vulkan_headers_dir:vulkan_headers" ] + if (!is_fuchsia) { + # Make sure that the cleanup of old layer JSON files happens before the new ones are generated. + deps += [ ":vulkan_clean_old_validation_layer_objects" ] + } + + sources = [ "$_checkout_dir/layers/VkLayer_khronos_validation.json.in" ] + outputs = [ "$vulkan_data_dir/VkLayer_khronos_validation.json" ] + + if (is_linux) { + _platform = "Linux" + } else if (is_win) { + _platform = "Windows" + } else if (is_mac) { + _platform = "Darwin" + } else if (is_fuchsia) { + _platform = "Fuchsia" + } else { + _platform = "Other" + } + + args = [ + "--platform", + _platform, + rebase_path("$_checkout_dir/layers/", root_build_dir), + rebase_path(vulkan_data_dir, root_build_dir), + ] + rebase_path(sources, root_build_dir) + if (is_fuchsia) { + args += [ "--no-path-prefix" ] + } + + # The layer JSON files are part of the necessary data deps. + data = outputs + } +} + +config("vulkan_memory_allocator_config") { + if (is_clang) { + cflags_cc = [ "-Wno-nullability-completeness" ] + } +} + +source_set("vulkan_layer_utils") { + include_dirs = [ + "$_checkout_dir/layers", + "$_checkout_dir/layers/external", + "$_checkout_dir/layers/generated", + ] + sources = [ + "$_checkout_dir/layers/containers/custom_containers.h", + "$_checkout_dir/layers/containers/sparse_containers.h", + "$_checkout_dir/layers/error_message/logging.cpp", + "$_checkout_dir/layers/error_message/logging.h", + "$_checkout_dir/layers/external/xxhash.cpp", + "$_checkout_dir/layers/external/xxhash.h", + "$_checkout_dir/layers/generated/vk_enum_string_helper.h", + "$_checkout_dir/layers/generated/vk_extension_helper.h", + "$_checkout_dir/layers/generated/vk_format_utils.cpp", + "$_checkout_dir/layers/generated/vk_format_utils.h", + "$_checkout_dir/layers/generated/vk_layer_dispatch_table.h", + "$_checkout_dir/layers/generated/vk_object_types.h", + "$_checkout_dir/layers/generated/vk_safe_struct.h", + "$_checkout_dir/layers/generated/vk_typemap_helper.h", + "$_checkout_dir/layers/generated/vk_validation_error_messages.h", + "$_checkout_dir/layers/utils/android_ndk_types.h", + "$_checkout_dir/layers/utils/cast_utils.h", + "$_checkout_dir/layers/utils/hash_util.h", + "$_checkout_dir/layers/utils/hash_vk_types.h", + "$_checkout_dir/layers/utils/vk_layer_extension_utils.cpp", + "$_checkout_dir/layers/utils/vk_layer_extension_utils.h", + "$_checkout_dir/layers/utils/vk_layer_utils.cpp", + "$_checkout_dir/layers/utils/vk_layer_utils.h", + "$_checkout_dir/layers/vk_layer_config.cpp", + "$_checkout_dir/layers/vk_layer_config.h", + "$vulkan_headers_dir/include/vulkan/vk_layer.h", + "$vulkan_headers_dir/include/vulkan/vulkan.h", + ] + defines = [ "XXH_NO_LONG_LONG" ] + public_configs = [ + ":vulkan_internal_config", + ":vulkan_memory_allocator_config", + ] + public_deps = [ "$vulkan_headers_dir:vulkan_headers" ] + + configs -= vulkan_undefine_configs + if (!is_fuchsia) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } +} + +config("vulkan_core_validation_config") { + include_dirs = [ "$vvl_glslang_dir" ] +} + +source_set("vulkan_core_validation_glslang") { + public_deps = [ + "${vvl_spirv_tools_dir}:spvtools", + "${vvl_spirv_tools_dir}:spvtools_opt", + "${vvl_spirv_tools_dir}:spvtools_val", + ] + public_configs = [ + "$vulkan_headers_dir:vulkan_headers_config", + ":vulkan_core_validation_config", + ] +} + +config("vulkan_stateless_validation_config") { + if (is_clang) { + cflags_cc = [ "-Wno-unused-const-variable" ] + } +} + +if (is_fuchsia) { + library_type = "loadable_module" +} else { + library_type = "shared_library" +} + +foreach(layer_info, layers) { + name = layer_info[0] + target(library_type, "VkLayer_$name") { + defines = [] + ldflags = [] + if (is_fuchsia) { + configs -= [ "//build/config:thread_safety_annotations" ] + ldflags += [ "-static-libstdc++" ] + configs += [ "//build/config:rtti" ] + } else { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + # The following line is what has to change to fix chromium compilation. + configs -= [ "//build/config/compiler:no_rtti" ] + configs += [ "//build/config/compiler:rtti" ] + } + configs -= vulkan_undefine_configs + configs += [ ":generated_layers_config" ] + public_configs = [ ":vulkan_layer_config" ] + deps = [ ":vulkan_layer_utils" ] + if (!is_fuchsia) { + deps += [ + # Make sure the cleanup of old layers happen before the new ones are compiled. + ":vulkan_clean_old_validation_layer_objects", + ] + } + if (layer_info[2] != "") { + deps += layer_info[2] + } + sources = layer_info[1] + if (is_win) { + defines += [ "NOMINMAX" ] + sources += [ "$_checkout_dir/layers/VkLayer_$name.def" ] + } + if (is_linux || is_android || is_fuchsia) { + ldflags += [ "-Wl,-Bsymbolic,--exclude-libs,ALL" ] + } + if (defined(ozone_platform_x11) && ozone_platform_x11) { + defines += [ "VK_USE_PLATFORM_XLIB_KHR" ] + } + if (is_android) { + libs = [ + "c++", # Note: C++ added by Flutter. + "log", + "nativewindow", + ] + # Note: config edit removed by Flutter + # configs -= [ "//build/config/android:hide_all_but_jni_onload" ] + } + defines += layer_info[3] + } +} + +group("vulkan_validation_layers") { + public_deps = [] + data_deps = [] + foreach(layer_info, layers) { + name = layer_info[0] + if (is_fuchsia) { + public_deps += [ ":VkLayer_$name" ] + } else { + data_deps += [ ":VkLayer_$name" ] + } + } +} + +group("tests") { + # TODO(fxbug.dev/13288) +} diff --git a/build/secondary/third_party/wuffs/BUILD.gn b/build/secondary/third_party/wuffs/BUILD.gn new file mode 100644 index 0000000000000..a78200b1e8feb --- /dev/null +++ b/build/secondary/third_party/wuffs/BUILD.gn @@ -0,0 +1,59 @@ +# 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. + +config("wuffs_public") { + include_dirs = [ "release/c" ] + + cflags = [] + + if (is_clang) { + cflags += [ + "-Wno-unused-function", + "-Wno-c++11-narrowing", + ] + } +} + +source_set("wuffs") { + public_configs = [ ":wuffs_public" ] + + defines = [ + # Copy/pasting from "../externals/wuffs/release/c/wuffs-*.c": + # + # ---- + # + # Wuffs ships as a "single file C library" or "header file library" as per + # https://github.com/nothings/stb/blob/master/docs/stb_howto.txt + # + # To use that single file as a "foo.c"-like implementation, instead of a + # "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or + # compiling it. + # + # ---- + "WUFFS_IMPLEMENTATION", + + # Continuing to copy/paste: + # + # ---- + # + # Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users + # of Wuffs' .c file explicitly include which parts of Wuffs to build. That + # file contains the entire Wuffs standard library, implementing a variety of + # codecs and file formats. Without this macro definition, an optimizing + # compiler or linker may very well discard Wuffs code for unused codecs, + # but listing the Wuffs modules we use makes that process explicit. + # Preprocessing means that such code simply isn't compiled. + # + # ---- + # + # For Skia, we're only interested in particular image codes (e.g. GIF) and + # their dependencies (e.g. BASE, LZW). + "WUFFS_CONFIG__MODULES", + "WUFFS_CONFIG__MODULE__BASE", + "WUFFS_CONFIG__MODULE__GIF", + "WUFFS_CONFIG__MODULE__LZW", + ] + + sources = [ "release/c/wuffs-v0.3.c" ] +} diff --git a/ci/bin/format.dart b/ci/bin/format.dart index 1e4de1455edfd..8e1c1b1d6b3c6 100644 --- a/ci/bin/format.dart +++ b/ci/bin/format.dart @@ -99,7 +99,7 @@ Future _runGit( return result.stdout; } -typedef MessageCallback = Function(String? message, {MessageType type}); +typedef MessageCallback = void Function(String? message, {MessageType type}); /// Base class for format checkers. /// diff --git a/ci/binary_size_treemap.sh b/ci/binary_size_treemap.sh new file mode 100755 index 0000000000000..b144c4bc153cb --- /dev/null +++ b/ci/binary_size_treemap.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# 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. + +# Run a tool that generates a treemap showing the contribution of each +# component to the size of a binary. +# +# Usage: +# binary_size_treemap.sh [binary_path] [output_dir] + +set -e + +INPUT_PATH="$(cd $(dirname "$1"); pwd -P)/$(basename "$1")" +DEST_DIR="$(cd $(dirname "$2"); pwd -P)/$(basename "$2")" +CI_DIRECTORY=$(cd $(dirname "${BASH_SOURCE[0]}"); pwd -P) +ENGINE_BUILDROOT=$(cd "$CI_DIRECTORY/../.."; pwd -P) + +if [ "$(uname)" == "Darwin" ]; then + NDK_PLATFORM="darwin-x86_64" +else + NDK_PLATFORM="linux-x86_64" +fi +ADDR2LINE="third_party/android_tools/ndk/toolchains/aarch64-linux-android-4.9/prebuilt/$NDK_PLATFORM/bin/aarch64-linux-android-addr2line" + +# Run the binary size script from the buildroot directory so the treemap path +# navigation will start from there. +cd "$ENGINE_BUILDROOT" +python3 third_party/dart/runtime/third_party/binary_size/src/run_binary_size_analysis.py --library "$INPUT_PATH" --destdir "$DEST_DIR" --addr2line-binary "$ADDR2LINE" diff --git a/ci/builders/README.md b/ci/builders/README.md index 645cc2d731ff5..73fcf14d41dd1 100644 --- a/ci/builders/README.md +++ b/ci/builders/README.md @@ -105,6 +105,26 @@ A configuration file defines a top-level builder that will show up as a column in the [Flutter Dashboard](https://flutter-dashboard.appspot.com/#/build?repo=engine&branch=master). + +### Magic variables + +Magic variables are special environment variables that can be used as parameters +for generators and test commands in the local and global contexts. + +Magic environment variables have the following limitations: +only `${FLUTTER_LOGS_DIR}` is currently supported and it needs to be used +alone within the parameter string(e.g. `["${FLUTTER_LOGS_DIR}"]` is OK +but `["path=${FLUTTER_LOGS_DIR}"]` is not). + +The current list of supported magic variables is: + +* `${FLUTTER_LOGS_DIR}` - translated to the path of the temporary + folder where logs are being placed. +* `${LUCI_WORKDIR}` - translated to the LUCI chroot working directory. +* `${LUCI_CLEANUP}` - translated to the LUCI chroot temp directory. +* `${REVISION}` - translated to the engine commit in postsubmit. In presubmit + it is translated to an empty string. + ### Build A build is a dictionary with a gn command, a ninja command, zero or more @@ -299,10 +319,7 @@ permissions to run in the target platform. * **name** - the name of the step running the script. * **parameters** - flags or parameters passed to the script. Parameters accept magic environment variables(placeholders replaced before executing -the test). Magic environment variables have the following limitations: -only `${FLUTTER_LOGS_DIR}` is currently supported and it needs to be used -alone within the parameter string(e.g. `["${FLUTTER_LOGS_DIR}"]` is OK -but `["path=${FLUTTER_LOGS_DIR}"]` is not). +the test). * **Script** - the path to the script to execute relative to the checkout directory. * **contexts** - a list of available contexts to add to the text execution step. @@ -448,7 +465,7 @@ Engine test example: { "tests": [ { - "name": "test: lint android_debug_arm64", + "name": "test: clang_tidy android_debug_arm64", "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", @@ -460,7 +477,7 @@ Engine test example: ], "tasks": [ { - "name": "test: lint android_debug_arm64", + "name": "test: clang_tidy android_debug_arm64", "parameters": [ "--variant", "android_debug_arm64", @@ -469,7 +486,7 @@ Engine test example: "--shard-variants=host_debug" ], "max_attempts": 1, - "script": "flutter/ci/lint.sh" + "script": "flutter/ci/clang_tidy.sh" } ] } @@ -497,7 +514,7 @@ Example task configuration: ```json { - "name": "test: lint android_debug_arm64", + "name": "test: clang_tidy android_debug_arm64", "parameters": [ "--variant", "android_debug_arm64", @@ -506,7 +523,7 @@ Example task configuration: "--shard-variants=host_debug" ], "max_attempts": 1, - "script": "flutter/ci/lint.sh" + "script": "flutter/ci/clang_tidy.sh" } ``` diff --git a/ci/builders/linux_android_aot_engine.json b/ci/builders/linux_android_aot_engine.json index 5065d6781d2e2..595e440ffa0f3 100644 --- a/ci/builders/linux_android_aot_engine.json +++ b/ci/builders/linux_android_aot_engine.json @@ -114,15 +114,11 @@ "tests": [ { "name": "Generate treemap for android_release_arm64", - "language": "python3", - "script": "third_party/dart/runtime/third_party/binary_size/src/run_binary_size_analysis.py", + "language": "bash", + "script": "flutter/ci/binary_size_treemap.sh", "parameters": [ - "--library", "../../src/out/android_release_arm64/libflutter.so", - "--destdir", - "${FLUTTER_LOGS_DIR}", - "--addr2line-binary", - "../../src/third_party/android_tools/ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-addr2line" + "${FLUTTER_LOGS_DIR}" ] } ] diff --git a/ci/builders/linux_android_debug_engine.json b/ci/builders/linux_android_debug_engine.json index 0b6f20399e8f8..4fcbfe5e9d427 100644 --- a/ci/builders/linux_android_debug_engine.json +++ b/ci/builders/linux_android_debug_engine.json @@ -36,6 +36,7 @@ { "language": "python3", "name": "Host Tests for android_jit_release_x86", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "android_jit_release_x86", @@ -44,8 +45,7 @@ "--engine-capture-core-dump", "--android-variant", "android_jit_release_x86" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -89,6 +89,7 @@ { "language": "python3", "name": "Host Tests for android_debug", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "android_debug", @@ -97,8 +98,7 @@ "--engine-capture-core-dump", "--android-variant", "android_debug" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, diff --git a/ci/builders/linux_fuchsia.json b/ci/builders/linux_fuchsia.json index 8af5dc025e426..72a72490e303c 100644 --- a/ci/builders/linux_fuchsia.json +++ b/ci/builders/linux_fuchsia.json @@ -152,8 +152,9 @@ "name": "Upload fuchsia artifacts", "parameters": [ "--engine-version", - "HEAD", - "--skip-build" + "${REVISION}", + "--skip-build", + "--upload" ], "script": "flutter/tools/fuchsia/build_fuchsia_artifacts.py", "language": "python3" @@ -162,11 +163,12 @@ "name": "Upload to CIPD for arch: arm64", "parameters": [ "--engine-version", - "HEAD", + "${REVISION}", + "--upload", "--target-arch", "arm64", "--out-dir", - "/b/s/w/ir/x/w/recipe_cleanup/tmppqs4ecj7", + "${LUCI_CLEANUP}", "--symbol-dirs", "out/fuchsia_debug_arm64/.build-id", "out/fuchsia_profile_arm64/.build-id", @@ -179,11 +181,12 @@ "name": "Upload to CIPD for arch: x64", "parameters": [ "--engine-version", - "HEAD", + "${REVISION}", + "--upload", "--target-arch", "x64", "--out-dir", - "/b/s/w/ir/x/w/recipe_cleanup/tmppqs4ecj7", + "${LUCI_CLEANUP}", "--symbol-dirs", "out/fuchsia_debug_x64/.build-id", "out/fuchsia_profile_x64/.build-id", diff --git a/ci/builders/linux_host_engine.json b/ci/builders/linux_host_engine.json index 97f535aa1a0bb..aae0ac2835190 100644 --- a/ci/builders/linux_host_engine.json +++ b/ci/builders/linux_host_engine.json @@ -53,14 +53,14 @@ { "language": "python3", "name": "Host Tests for host_debug_impeller_vulkan", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug_impeller_vulkan", "--type", "impeller-vulkan", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -110,14 +110,14 @@ { "language": "python3", "name": "Host Tests for host_debug", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug", "--type", - "dart", + "dart,dart-host", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -158,14 +158,14 @@ { "language": "python3", "name": "Host Tests for host_profile", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_profile", "--type", - "dart,engine", + "dart,dart-host,engine", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -216,14 +216,14 @@ { "language": "python3", "name": "Host Tests for host_release", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_release", "--type", - "dart,engine,benchmarks", + "dart,dart-host,engine,benchmarks", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] }, { "language": "bash", @@ -233,10 +233,10 @@ { "language": "bash", "name": "Upload metrics dry-run", - "parameters": [ + "script": "flutter/testing/benchmark/upload_metrics.sh", + "parameters": [ "--no-upload" - ], - "script": "flutter/testing/benchmark/upload_metrics.sh" + ] } ] } diff --git a/ci/builders/linux_unopt.json b/ci/builders/linux_unopt.json index 2570f491dbf1e..bdc93f0816438 100644 --- a/ci/builders/linux_unopt.json +++ b/ci/builders/linux_unopt.json @@ -13,7 +13,9 @@ "--prebuilt-dart-sdk", "--asan", "--lsan", - "--dart-debug" + "--dart-debug", + "--rbe", + "--no-goma" ], "name": "host_debug_unopt", "ninja": { @@ -38,15 +40,15 @@ { "language": "python3", "name": "test: Host_Tests_for_host_debug_unopt", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug_unopt", "--type", - "dart,engine", + "dart,dart-host,engine", "--engine-capture-core-dump", "--use-sanitizer-suppressions" - ], - "script": "flutter/testing/run_tests.py" + ] }, { "name": "analyze_dart_ui", @@ -89,7 +91,9 @@ ], "gn": [ "--android", - "--unoptimized" + "--unoptimized", + "--rbe", + "--no-goma" ], "name": "android_debug_unopt", "ninja": { @@ -104,6 +108,7 @@ { "language": "python3", "name": "test: Host Tests for android_debug_unopt", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "android_debug_unopt", @@ -112,20 +117,19 @@ "--engine-capture-core-dump", "--android-variant", "android_debug_unopt" - ], - "script": "flutter/testing/run_tests.py" + ] }, { "language": "python3", "name": "malioc diff", + "script": "flutter/impeller/tools/malioc_diff.py", "parameters": [ "--before-relative-to-src", "flutter/impeller/tools/malioc.json", "--after-relative-to-src", "out/android_debug_unopt/gen/malioc", "--print-diff" - ], - "script": "flutter/impeller/tools/malioc_diff.py" + ] } ] } diff --git a/ci/builders/linux_web_engine.json b/ci/builders/linux_web_engine.json index 3063262c05fed..518284ec47dc7 100644 --- a/ci/builders/linux_web_engine.json +++ b/ci/builders/linux_web_engine.json @@ -1,6 +1,6 @@ { "_comment": "THIS IS A GENERATED FILE. Do not edit this file directly.", - "_comment2": "See `generated_builder_json.dart` for the generator code", + "_comment2": "See `generate_builder_json.dart` for the generator code", "builds": [ { "name": "web_tests/artifacts", @@ -351,11 +351,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -387,11 +387,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -423,11 +423,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -459,11 +459,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -495,11 +495,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -531,11 +531,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -567,11 +567,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -603,11 +603,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -639,7 +639,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "firefox", @@ -675,7 +675,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "firefox", @@ -711,7 +711,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "firefox", @@ -747,7 +747,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "firefox", @@ -783,7 +783,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "firefox", @@ -819,11 +819,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -855,11 +855,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -891,11 +891,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -927,11 +927,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -963,11 +963,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -999,11 +999,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1035,11 +1035,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1071,11 +1071,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1095,7 +1095,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac" + "os=Mac-12" ], "gclient_variables": { "download_android_deps": false @@ -1107,7 +1107,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "tasks": [ @@ -1127,7 +1127,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac" + "os=Mac-12" ], "gclient_variables": { "download_android_deps": false @@ -1139,7 +1139,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "tasks": [ @@ -1159,7 +1159,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac" + "os=Mac-12" ], "gclient_variables": { "download_android_deps": false @@ -1171,7 +1171,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "tasks": [ @@ -1191,7 +1191,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac" + "os=Mac-12" ], "gclient_variables": { "download_android_deps": false @@ -1203,7 +1203,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "tasks": [ @@ -1223,7 +1223,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac" + "os=Mac-12" ], "gclient_variables": { "download_android_deps": false @@ -1235,7 +1235,7 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "tasks": [ @@ -1267,11 +1267,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1303,11 +1303,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1339,11 +1339,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1375,11 +1375,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1411,11 +1411,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1447,11 +1447,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1483,11 +1483,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ @@ -1519,11 +1519,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:115.0" + "version": "119.0.6045.9" } ], "tasks": [ diff --git a/ci/builders/mac_android_aot_engine.json b/ci/builders/mac_android_aot_engine.json index 7f2f8861ab2dd..f59354f1078b5 100644 --- a/ci/builders/mac_android_aot_engine.json +++ b/ci/builders/mac_android_aot_engine.json @@ -9,7 +9,7 @@ "out/android_profile/zip_archives/android-arm-profile/darwin-x64.zip" ], "name": "android_profile", - "realm": "production" + "realm": "production" } ], "drone_dimensions": [ @@ -48,7 +48,7 @@ "out/android_profile_arm64/zip_archives/android-arm64-profile/darwin-x64.zip" ], "name": "android_profile_arm64", - "realm": "production" + "realm": "production" } ], "drone_dimensions": [ @@ -88,7 +88,7 @@ "out/android_profile_x64/zip_archives/android-x64-profile/darwin-x64.zip" ], "name": "android_profile_x64", - "realm": "production" + "realm": "production" } ], "drone_dimensions": [ @@ -128,7 +128,7 @@ "out/android_release/zip_archives/android-arm-release/darwin-x64.zip" ], "name": "android_release", - "realm": "production" + "realm": "production" } ], "drone_dimensions": [ @@ -167,7 +167,7 @@ "out/android_release_arm64/zip_archives/android-arm64-release/darwin-x64.zip" ], "name": "android_release_arm64", - "realm": "production" + "realm": "production" } ], "drone_dimensions": [ @@ -207,7 +207,7 @@ "out/android_release_x64/zip_archives/android-x64-release/darwin-x64.zip" ], "name": "android_release_x64", - "realm": "production" + "realm": "production" } ], "drone_dimensions": [ diff --git a/ci/builders/mac_clang_tidy.json b/ci/builders/mac_clang_tidy.json index aadf3721911ca..f68aa4028f3b8 100644 --- a/ci/builders/mac_clang_tidy.json +++ b/ci/builders/mac_clang_tidy.json @@ -50,7 +50,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { @@ -72,7 +72,7 @@ "--shard-id=0", "--shard-variants=host_debug,host_debug,host_debug" ], - "max_attempts": 1, + "max_attempts": 1, "script": "flutter/ci/clang_tidy.sh" } ] @@ -82,7 +82,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { @@ -114,7 +114,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { @@ -146,7 +146,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { @@ -178,7 +178,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { @@ -201,7 +201,7 @@ "--shard-id=0", "--shard-variants=host_debug" ], - "max_attempts": 1, + "max_attempts": 1, "script": "flutter/ci/clang_tidy.sh" } ] diff --git a/ci/builders/mac_clang_tidy_presubmit.json b/ci/builders/mac_clang_tidy_presubmit.json index 8fa1e9f4ebd29..19f030435649e 100644 --- a/ci/builders/mac_clang_tidy_presubmit.json +++ b/ci/builders/mac_clang_tidy_presubmit.json @@ -50,7 +50,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { @@ -82,7 +82,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac", + "os=Mac-12", "cpu=arm64" ], "gclient_variables": { diff --git a/ci/builders/mac_host_engine.json b/ci/builders/mac_host_engine.json index cc3a336302a50..658074a91eb7e 100644 --- a/ci/builders/mac_host_engine.json +++ b/ci/builders/mac_host_engine.json @@ -54,14 +54,14 @@ { "language": "python3", "name": "Host Tests for host_debug", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug", "--type", - "dart,engine", + "dart,dart-host,engine", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -113,14 +113,14 @@ { "language": "python3", "name": "Host Tests for host_profile", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_profile", "--type", - "dart,engine", + "dart,dart-host,engine", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -146,7 +146,7 @@ "dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "gclient_variables": { @@ -183,13 +183,13 @@ { "language": "python3", "name": "Impeller-golden, dart and engine tests for host_release", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_release", "--type", - "dart,engine,impeller-golden" - ], - "script": "flutter/testing/run_tests.py" + "dart,dart-host,engine,impeller-golden" + ] } ] }, diff --git a/ci/builders/mac_ios_engine.json b/ci/builders/mac_ios_engine.json index a8a4bb926462e..3dfc5c4d559b4 100644 --- a/ci/builders/mac_ios_engine.json +++ b/ci/builders/mac_ios_engine.json @@ -3,8 +3,8 @@ { "drone_dimensions": [ "device_type=none", - "mac_model=Macmini8,1", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -26,7 +26,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -49,7 +50,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -72,7 +74,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -96,7 +99,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -120,8 +124,8 @@ { "drone_dimensions": [ "device_type=none", - "mac_model=Macmini8,1", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -144,7 +148,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -168,7 +173,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -192,7 +198,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -217,7 +224,8 @@ { "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac-12", + "cpu=x86" ], "gn": [ "--ios", @@ -289,53 +297,6 @@ "script": "flutter/sky/tools/create_full_ios_framework.py", "language": "python3" }, - { - "name": "Debug-ios-Flutter-Extension-Safe.xcframework", - "parameters": [ - "--dst", - "out/debug_extension_safe", - "--arm64-out-dir", - "out/ios_debug_extension_safe", - "--simulator-x64-out-dir", - "out/ios_debug_sim_extension_safe", - "--simulator-arm64-out-dir", - "out/ios_debug_sim_arm64_extension_safe" - ], - "script": "flutter/sky/tools/create_full_ios_framework.py", - "language": "python3" - }, - { - "name": "Profile-ios-Flutter-Extension-Safe.xcframework", - "parameters": [ - "--dst", - "out/profile_extension_safe", - "--arm64-out-dir", - "out/ios_profile_extension_safe", - "--simulator-x64-out-dir", - "out/ios_debug_sim_extension_safe", - "--simulator-arm64-out-dir", - "out/ios_debug_sim_arm64_extension_safe" - ], - "script": "flutter/sky/tools/create_full_ios_framework.py", - "language": "python3" - }, - { - "name": "Release-ios-Flutter-Extension-Safe.xcframework", - "parameters": [ - "--dst", - "out/release_extension_safe", - "--arm64-out-dir", - "out/ios_release_extension_safe", - "--simulator-x64-out-dir", - "out/ios_debug_sim_extension_safe", - "--simulator-arm64-out-dir", - "out/ios_debug_sim_arm64_extension_safe", - "--dsym", - "--strip" - ], - "script": "flutter/sky/tools/create_full_ios_framework.py", - "language": "python3" - }, { "name": "Release-macos-gen-snapshots", "parameters": [ @@ -379,23 +340,8 @@ "realm": "production" }, { - "source": "out/debug_extension_safe/artifacts.zip", - "destination": "ios-extension-safe/artifacts.zip", - "realm": "production" - }, - { - "source": "out/profile_extension_safe/artifacts.zip", - "destination": "ios-profile-extension-safe/artifacts.zip", - "realm": "production" - }, - { - "source": "out/release_extension_safe/artifacts.zip", - "destination": "ios-release-extension-safe/artifacts.zip", - "realm": "production" - }, - { - "source": "out/release_extension_safe/Flutter.dSYM.zip", - "destination": "ios-release-extension-safe/Flutter.dSYM.zip", + "source": "out/release/extension_safe_Flutter.dSYM.zip", + "destination": "ios-release/extension_safe_Flutter.dSYM.zip", "realm": "production" } ] diff --git a/ci/builders/mac_unopt.json b/ci/builders/mac_unopt.json index 4d372bc615424..480b96a5c699d 100644 --- a/ci/builders/mac_unopt.json +++ b/ci/builders/mac_unopt.json @@ -40,14 +40,14 @@ { "language": "python3", "name": "Host Tests for host_debug_unopt", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug_unopt", "--type", - "dart,engine", + "dart,dart-host,engine", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, @@ -96,6 +96,7 @@ { "language": "python3", "name": "Tests for ios_debug_sim", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "ios_debug_sim", @@ -104,8 +105,7 @@ "--engine-capture-core-dump", "--ios-variant", "ios_debug_sim" - ], - "script": "flutter/testing/run_tests.py" + ] }, { "name": "Scenario App Integration Tests", @@ -205,6 +205,7 @@ { "language": "python3", "name": "Tests for ios_debug_sim_arm64", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "ios_debug_sim_arm64", @@ -213,8 +214,7 @@ "--engine-capture-core-dump", "--ios-variant", "ios_debug_sim_arm64" - ], - "script": "flutter/testing/run_tests.py" + ] }, { "name": "Scenario App Integration Tests", @@ -276,6 +276,7 @@ { "language": "python3", "name": "Tests for ios_debug_sim_arm64_extension_safe", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "ios_debug_sim_arm64_extension_safe", @@ -284,8 +285,7 @@ "--engine-capture-core-dump", "--ios-variant", "ios_debug_sim_arm64_extension_safe" - ], - "script": "flutter/testing/run_tests.py" + ] }, { "name": "Scenario App Integration Tests", diff --git a/ci/builders/standalone/linux_android_emulator.json b/ci/builders/standalone/linux_android_emulator.json index e8c392e56ae1e..b2fe54629a9b0 100644 --- a/ci/builders/standalone/linux_android_emulator.json +++ b/ci/builders/standalone/linux_android_emulator.json @@ -6,7 +6,9 @@ "gn": [ "--android", "--android-cpu=x64", - "--no-lto" + "--no-lto", + "--rbe", + "--no-goma" ], "name": "android_debug_x64", "ninja": { @@ -23,13 +25,13 @@ "contexts": [ "android_virtual_device" ], - "parameters": [ - "--android-variant", - "android_debug_x64", - "--type", - "android" - ], - "script": "flutter/testing/run_tests.py" + "script": "flutter/testing/run_tests.py", + "parameters": [ + "--android-variant", + "android_debug_x64", + "--type", + "android" + ] }, { "language": "bash", @@ -37,10 +39,10 @@ "contexts": [ "android_virtual_device" ], - "parameters": [ - "android_debug_x64" - ], - "script": "flutter/testing/scenario_app/run_android_tests.sh" + "script": "flutter/testing/scenario_app/run_android_tests.sh", + "parameters": [ + "android_debug_x64" + ] } ] } diff --git a/ci/builders/standalone/linux_benchmarks.json b/ci/builders/standalone/linux_benchmarks.json index fdec72e47ae7a..53b6a460db062 100644 --- a/ci/builders/standalone/linux_benchmarks.json +++ b/ci/builders/standalone/linux_benchmarks.json @@ -10,7 +10,9 @@ "--runtime-mode", "release", "--prebuilt-dart-sdk", - "--build-embedder-examples" + "--build-embedder-examples", + "--rbe", + "--no-goma" ], "name": "host_release", "ninja": { diff --git a/ci/builders/standalone/linux_license.json b/ci/builders/standalone/linux_license.json index 15a792c79b89b..494d401cccee2 100644 --- a/ci/builders/standalone/linux_license.json +++ b/ci/builders/standalone/linux_license.json @@ -11,7 +11,7 @@ "tests": [ { "name": "licenses check", - "max_attempts": 1, + "max_attempts": 1, "script": "flutter/ci/licenses.sh" } ] diff --git a/ci/builders/standalone/windows_unopt.json b/ci/builders/standalone/windows_unopt.json index 70839194ae0d9..2508f4507d663 100644 --- a/ci/builders/standalone/windows_unopt.json +++ b/ci/builders/standalone/windows_unopt.json @@ -26,14 +26,14 @@ { "language": "python3", "name": "test: Host Tests for host_debug_unopt", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug_unopt", "--type", "engine", "--engine-capture-core-dump" - ], - "script": "flutter/testing/run_tests.py" + ] } ] } diff --git a/ci/builders/windows_host_engine.json b/ci/builders/windows_host_engine.json index 729d62bdcfec9..12c4542b85496 100644 --- a/ci/builders/windows_host_engine.json +++ b/ci/builders/windows_host_engine.json @@ -46,13 +46,13 @@ { "language": "python3", "name": "Host Tests for host_debug", + "script": "flutter/testing/run_tests.py", "parameters": [ "--variant", "host_debug", "--type", "engine" - ], - "script": "flutter/testing/run_tests.py" + ] } ] }, diff --git a/ci/clang_tidy.sh b/ci/clang_tidy.sh index cacaebb62630a..80cb7030fbd55 100755 --- a/ci/clang_tidy.sh +++ b/ci/clang_tidy.sh @@ -36,10 +36,14 @@ DART="${DART_BIN}/dart" # FLUTTER_LINT_PRINT_FIX will make it so that fix is executed and the generated # diff is printed to stdout if clang-tidy fails. This is helpful for enabling # new lints. + +# To run on CI, just uncomment the following line: +# FLUTTER_LINT_PRINT_FIX=1 + if [[ -z "${FLUTTER_LINT_PRINT_FIX}" ]]; then fix_flag="" else - fix_flag="--fix" + fix_flag="--fix --lint-all" fi COMPILE_COMMANDS="$SRC_DIR/out/host_debug/compile_commands.json" diff --git a/ci/licenses.sh b/ci/licenses.sh index c38b08126996d..2989505ae17ce 100755 --- a/ci/licenses.sh +++ b/ci/licenses.sh @@ -156,7 +156,7 @@ function verify_licenses() ( local actualLicenseCount actualLicenseCount="$(tail -n 1 flutter/ci/licenses_golden/licenses_flutter | tr -dc '0-9')" - local expectedLicenseCount=20 # When changing this number: Update the error message below as well describing the newly expected license types. + local expectedLicenseCount=39 # When changing this number: Update the error message below as well describing the newly expected license types. if [[ $actualLicenseCount -ne $expectedLicenseCount ]]; then echo "=============================== ERROR ===============================" diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 9b1f50e0670f3..36119e952ad9b 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -4,7 +4,6 @@ ../../../.github ../../../.gitignore ../../../.gn -../../../.ycm_extra_conf.py ../../../AUTHORS ../../../CODEOWNERS ../../../README.md @@ -83,8 +82,10 @@ ../../../flutter/fml/ascii_trie_unittests.cc ../../../flutter/fml/backtrace_unittests.cc ../../../flutter/fml/base32_unittest.cc +../../../flutter/fml/closure_unittests.cc ../../../flutter/fml/command_line_unittest.cc ../../../flutter/fml/container_unittests.cc +../../../flutter/fml/cpu_affinity_unittests.cc ../../../flutter/fml/endianness_unittests.cc ../../../flutter/fml/file_unittest.cc ../../../flutter/fml/hash_combine_unittests.cc @@ -95,7 +96,6 @@ ../../../flutter/fml/memory/ref_counted_unittest.cc ../../../flutter/fml/memory/task_runner_checker_unittest.cc ../../../flutter/fml/memory/weak_ptr_unittest.cc -../../../flutter/fml/message_loop_impl_unittests.cc ../../../flutter/fml/message_loop_task_queues_merge_unmerge_unittests.cc ../../../flutter/fml/message_loop_task_queues_unittests.cc ../../../flutter/fml/message_loop_unittests.cc @@ -120,8 +120,10 @@ ../../../flutter/impeller/.gitignore ../../../flutter/impeller/README.md ../../../flutter/impeller/aiks/aiks_unittests.cc +../../../flutter/impeller/aiks/canvas_recorder_unittests.cc ../../../flutter/impeller/aiks/canvas_unittests.cc ../../../flutter/impeller/aiks/testing +../../../flutter/impeller/aiks/trace_serializer_unittests.cc ../../../flutter/impeller/archivist/archivist_unittests.cc ../../../flutter/impeller/base/README.md ../../../flutter/impeller/base/base_unittests.cc @@ -150,9 +152,12 @@ ../../../flutter/impeller/golden_tests_harvester/test ../../../flutter/impeller/image/README.md ../../../flutter/impeller/playground +../../../flutter/impeller/renderer/backend/gles/test ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/test @@ -225,6 +230,7 @@ ../../../flutter/runtime/no_dart_plugin_registrant_unittests.cc ../../../flutter/runtime/type_conversions_unittests.cc ../../../flutter/shell/common/animator_unittests.cc +../../../flutter/shell/common/base64_unittests.cc ../../../flutter/shell/common/context_options_unittests.cc ../../../flutter/shell/common/dl_op_spy_unittests.cc ../../../flutter/shell/common/engine_unittests.cc @@ -381,8 +387,10 @@ ../../../flutter/sky/tools/create_xcframework.py ../../../flutter/sky/tools/dist_dart_pkg.py ../../../flutter/sky/tools/install_framework_headers.py -../../../flutter/sky/tools/objcopy.py ../../../flutter/testing +../../../flutter/third_party/.clang-tidy +../../../flutter/third_party/.gitignore +../../../flutter/third_party/README.md ../../../flutter/third_party/accessibility/README.md ../../../flutter/third_party/accessibility/ax/ax_enum_util_unittest.cc ../../../flutter/third_party/accessibility/ax/ax_event_generator_unittest.cc @@ -420,7 +428,23 @@ ../../../flutter/third_party/accessibility/gfx/geometry/vector2d_unittest.cc ../../../flutter/third_party/accessibility/gfx/range/range_unittest.cc ../../../flutter/third_party/accessibility/gfx/test +../../../flutter/third_party/glfw/.appveyor.yml +../../../flutter/third_party/glfw/.git +../../../flutter/third_party/glfw/.gitattributes +../../../flutter/third_party/glfw/.github +../../../flutter/third_party/glfw/.gitignore +../../../flutter/third_party/glfw/.mailmap +../../../flutter/third_party/glfw/CMake +../../../flutter/third_party/glfw/CMakeLists.txt +../../../flutter/third_party/glfw/CONTRIBUTORS.md +../../../flutter/third_party/glfw/README.md +../../../flutter/third_party/glfw/deps +../../../flutter/third_party/glfw/docs +../../../flutter/third_party/glfw/examples +../../../flutter/third_party/glfw/src/CMakeLists.txt +../../../flutter/third_party/glfw/tests ../../../flutter/third_party/gn +../../../flutter/third_party/imgui ../../../flutter/third_party/ninja ../../../flutter/third_party/spring_animation/README.md ../../../flutter/third_party/test_shaders @@ -466,6 +490,10 @@ ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/dist/lib/ld.so.1 ../../../fuchsia/sdk/linux/arch/x64/sysroot/dist/lib/asan/ld.so.1 ../../../fuchsia/sdk/linux/arch/x64/sysroot/dist/lib/ld.so.1 +../../../fuchsia/sdk/linux/bind/fuchsia.arm.platform/meta.json +../../../fuchsia/sdk/linux/bind/fuchsia.nxp.platform/meta.json +../../../fuchsia/sdk/linux/bind/fuchsia.platform/meta.json +../../../fuchsia/sdk/linux/bind/fuchsia/meta.json ../../../fuchsia/sdk/linux/dart/sl4f/meta.json ../../../fuchsia/sdk/linux/data/config/symbol_index/meta.json ../../../fuchsia/sdk/linux/docs @@ -631,6 +659,7 @@ ../../../fuchsia/sdk/linux/fidl/zx/meta.json ../../../fuchsia/sdk/linux/meta ../../../fuchsia/sdk/linux/packages/blobs +../../../fuchsia/sdk/linux/packages/heapdump-collector/meta.json ../../../fuchsia/sdk/linux/packages/realm_builder_server/meta.json ../../../fuchsia/sdk/linux/pkg/async-cpp/meta.json ../../../fuchsia/sdk/linux/pkg/async-default/meta.json @@ -671,9 +700,11 @@ ../../../fuchsia/sdk/linux/pkg/fidl_cpp_v2/meta.json ../../../fuchsia/sdk/linux/pkg/fidl_cpp_wire/meta.json ../../../fuchsia/sdk/linux/pkg/fidl_driver/meta.json +../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/meta.json ../../../fuchsia/sdk/linux/pkg/fidl_driver_transport/meta.json ../../../fuchsia/sdk/linux/pkg/fit-promise/meta.json ../../../fuchsia/sdk/linux/pkg/fit/meta.json +../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/meta.json ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json ../../../fuchsia/sdk/linux/pkg/input_report_reader/meta.json ../../../fuchsia/sdk/linux/pkg/inspect/meta.json @@ -710,7 +741,6 @@ ../../../fuchsia/sdk/linux/pkg/zx/meta.json ../../../fuchsia/sdk/linux/tools ../../../fuchsia/sdk/linux/version_history.json -../../../gpu/README.md ../../../out ../../../third_party/abseil-cpp/.git ../../../third_party/abseil-cpp/.github @@ -988,7 +1018,6 @@ ../../../third_party/angle/.git ../../../third_party/angle/.gitattributes ../../../third_party/angle/.gitignore -../../../third_party/angle/.gitmodules ../../../third_party/angle/.gn ../../../third_party/angle/.style.yapf ../../../third_party/angle/.vpython @@ -1127,7 +1156,6 @@ ../../../third_party/angle/src/third_party/ceval/package.json ../../../third_party/angle/src/third_party/libXNVCtrl/README.chromium ../../../third_party/angle/src/third_party/volk -../../../third_party/angle/testing ../../../third_party/angle/third_party ../../../third_party/angle/tools ../../../third_party/angle/util @@ -1578,22 +1606,14 @@ ../../../third_party/dart/runtime/observatory/tests ../../../third_party/dart/runtime/observatory/update_sources.py ../../../third_party/dart/runtime/observatory/web/third_party/README.md -../../../third_party/dart/runtime/observatory_2/.gitignore -../../../third_party/dart/runtime/observatory_2/HACKING.md -../../../third_party/dart/runtime/observatory_2/analysis_options.yaml -../../../third_party/dart/runtime/observatory_2/lib/src/elements/css/shared.css -../../../third_party/dart/runtime/observatory_2/pubspec.yaml -../../../third_party/dart/runtime/observatory_2/tests -../../../third_party/dart/runtime/observatory_2/update_sources.py -../../../third_party/dart/runtime/observatory_2/web/third_party/README.md ../../../third_party/dart/runtime/tests ../../../third_party/dart/runtime/third_party/binary_size ../../../third_party/dart/runtime/third_party/d3 ../../../third_party/dart/runtime/third_party/double-conversion/.gitignore ../../../third_party/dart/runtime/third_party/double-conversion/AUTHORS ../../../third_party/dart/runtime/third_party/double-conversion/Changelog -../../../third_party/dart/runtime/third_party/double-conversion/README ../../../third_party/dart/runtime/third_party/double-conversion/README.dart +../../../third_party/dart/runtime/third_party/double-conversion/README.md ../../../third_party/dart/runtime/tools/.gitignore ../../../third_party/dart/runtime/tools/android_finder.py ../../../third_party/dart/runtime/tools/benchmark.py @@ -1654,6 +1674,7 @@ ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32_test.cc ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_riscv_test.cc ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_test.cc +../../../third_party/dart/runtime/vm/compiler/assembler/assembler_test.h ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64_test.cc ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_test.cc ../../../third_party/dart/runtime/vm/compiler/backend/bce_test.cc @@ -2005,21 +2026,6 @@ ../../../third_party/freetype2/src/winfonts/rules.mk ../../../third_party/freetype2/tests ../../../third_party/freetype2/vms_make.com -../../../third_party/glfw/.appveyor.yml -../../../third_party/glfw/.git -../../../third_party/glfw/.gitattributes -../../../third_party/glfw/.github -../../../third_party/glfw/.gitignore -../../../third_party/glfw/.mailmap -../../../third_party/glfw/CMake -../../../third_party/glfw/CMakeLists.txt -../../../third_party/glfw/CONTRIBUTORS.md -../../../third_party/glfw/README.md -../../../third_party/glfw/deps -../../../third_party/glfw/docs -../../../third_party/glfw/examples -../../../third_party/glfw/src/CMakeLists.txt -../../../third_party/glfw/tests ../../../third_party/google_fonts_for_unit_tests ../../../third_party/googletest ../../../third_party/gradle @@ -2225,7 +2231,6 @@ ../../../third_party/icu/source/tools/toolutil/sources.txt ../../../third_party/icu/source/tools/tzcode/Makefile.in ../../../third_party/icu/source/tools/tzcode/readme.txt -../../../third_party/imgui ../../../third_party/inja/.clang-format ../../../third_party/inja/.git ../../../third_party/inja/.github @@ -2634,10 +2639,7 @@ ../../../third_party/skia/experimental ../../../third_party/skia/fuzz/README.md ../../../third_party/skia/gm/BUILD.bazel -../../../third_party/skia/gm/android_gm_test.bzl ../../../third_party/skia/gm/png_codec.bzl -../../../third_party/skia/gm/surface_manager/BUILD.bazel -../../../third_party/skia/gm/vias/BUILD.bazel ../../../third_party/skia/gn/BUILD.bazel ../../../third_party/skia/gn/__init__.py ../../../third_party/skia/gn/bazel_build.py @@ -2847,6 +2849,7 @@ ../../../third_party/skia/src/gpu/mtl/BUILD.bazel ../../../third_party/skia/src/gpu/tessellate/BUILD.bazel ../../../third_party/skia/src/gpu/vk/BUILD.bazel +../../../third_party/skia/src/gpu/vk/vulkanmemoryallocator/BUILD.bazel ../../../third_party/skia/src/image/BUILD.bazel ../../../third_party/skia/src/lazy/BUILD.bazel ../../../third_party/skia/src/opts/BUILD.bazel diff --git a/ci/licenses_golden/licenses_dart b/ci/licenses_golden/licenses_dart index 2433a1a934b1b..4409b01315149 100644 --- a/ci/licenses_golden/licenses_dart +++ b/ci/licenses_golden/licenses_dart @@ -1,4 +1,4 @@ -Signature: 2842791947e56be69f6d021381e26966 +Signature: 5885ccbd7975a54702b26a3184e769aa ==================================================================================================== LIBRARY: dart @@ -1354,58 +1354,6 @@ ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/type_argu ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/unknown_ref.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_view.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/tracer.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/app/application.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/app/location_manager.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_instances.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/code_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/code_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/context_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/context_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile/virtual_tree.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile_table.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/curly_block.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/error_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/eval_box.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/field_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/field_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/flag_list.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/function_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/function_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_snapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/icdata_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/icdata_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/instance_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/instance_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/json_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/library_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/library_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/local_var_descriptors_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/megamorphiccache_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/native_memory_profiler.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/object_common.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/object_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectpool_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectstore_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/observatory_application.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/pc_descriptors_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sample_buffer_control.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_inset.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sentinel_value.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sentinel_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/source_inset.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/stack_trace_tree_config.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/type_arguments_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unknown_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/tracer.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/atomic.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/signal_blocker.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/allocation.h + ../../../third_party/dart/LICENSE @@ -1694,58 +1642,6 @@ FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/type_argume FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unknown_ref.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_view.dart FILE: ../../../third_party/dart/runtime/observatory/lib/tracer.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/application.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/location_manager.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_instances.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/code_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/code_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/context_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/context_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile/virtual_tree.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile_table.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/curly_block.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/error_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/eval_box.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/field_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/field_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/flag_list.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/function_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/function_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/icdata_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/icdata_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/instance_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/instance_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/json_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/library_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/library_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/local_var_descriptors_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/megamorphiccache_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/native_memory_profiler.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/object_common.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/object_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectpool_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectstore_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/observatory_application.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/pc_descriptors_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sample_buffer_control.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_inset.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sentinel_value.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sentinel_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/source_inset.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/stack_trace_tree_config.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/type_arguments_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unknown_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/tracer.dart FILE: ../../../third_party/dart/runtime/platform/atomic.h FILE: ../../../third_party/dart/runtime/platform/signal_blocker.h FILE: ../../../third_party/dart/runtime/vm/allocation.h @@ -1973,10 +1869,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: dart ORIGIN: ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js + http://polymer.github.io/LICENSE.txt referenced by ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js -ORIGIN: ../../../third_party/dart/runtime/observatory_2/web/third_party/webcomponents.min.js + http://polymer.github.io/LICENSE.txt referenced by ../../../third_party/dart/runtime/observatory_2/web/third_party/webcomponents.min.js TYPE: LicenseType.bsd FILE: ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js -FILE: ../../../third_party/dart/runtime/observatory_2/web/third_party/webcomponents.min.js ---------------------------------------------------------------------------------------------------- Copyright (c) 2014 The Polymer Project Authors. All rights reserved. @@ -2030,26 +1924,6 @@ ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_connec ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/service/object.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/utils.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/web/main.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/bin/shell.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/app.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/object_graph.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/service.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/service_common.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/service_html.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/service_io.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/app/page.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/app/settings.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/app/view_model.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_tree.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/debugger.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_map.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_reconnect.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metrics.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_connect.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/service/object.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/utils.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/web/main.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/address_sanitizer.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/memory_sanitizer.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/safe_stack.h + ../../../third_party/dart/LICENSE @@ -2111,9 +1985,9 @@ ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/pream ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/jsshell.js + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/linked_hash_map.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/synced/embedded_names.dart + ../../../third_party/dart/LICENSE +ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/convert_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/lib_prefix.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/profiler.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm_shared/lib/convert_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/collection/set.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/core/sink.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/developer/profiler.dart + ../../../third_party/dart/LICENSE @@ -2142,26 +2016,6 @@ FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_connect. FILE: ../../../third_party/dart/runtime/observatory/lib/src/service/object.dart FILE: ../../../third_party/dart/runtime/observatory/lib/utils.dart FILE: ../../../third_party/dart/runtime/observatory/web/main.dart -FILE: ../../../third_party/dart/runtime/observatory_2/bin/shell.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/app.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/object_graph.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/service.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/service_common.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/service_html.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/service_io.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/page.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/settings.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/view_model.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_tree.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/debugger.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_map.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_reconnect.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metrics.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_connect.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/service/object.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/utils.dart -FILE: ../../../third_party/dart/runtime/observatory_2/web/main.dart FILE: ../../../third_party/dart/runtime/platform/address_sanitizer.h FILE: ../../../third_party/dart/runtime/platform/memory_sanitizer.h FILE: ../../../third_party/dart/runtime/platform/safe_stack.h @@ -2223,9 +2077,9 @@ FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambl FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/jsshell.js FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/linked_hash_map.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/synced/embedded_names.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/convert_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/lib_prefix.dart FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/profiler.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm_shared/lib/convert_patch.dart FILE: ../../../third_party/dart/sdk/lib/collection/set.dart FILE: ../../../third_party/dart/sdk/lib/core/sink.dart FILE: ../../../third_party/dart/sdk/lib/developer/profiler.dart @@ -2289,24 +2143,6 @@ ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/ports.dar ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/timeline_page.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/sample_profile/sample_profile.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/web/timeline.js + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/cli.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/debugger.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/sample_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/allocation_profile/allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/cli/command.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/debugger/debugger.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/debugger/debugger_location.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_snapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/logging.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/logging_list.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/megamorphiccache_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectpool_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/persistent_handles.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/ports.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/timeline_page.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/sample_profile/sample_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/web/timeline.js + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.cc + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/log.cc + ../../../third_party/dart/LICENSE @@ -2387,24 +2223,6 @@ FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/ports.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/timeline_page.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/sample_profile/sample_profile.dart FILE: ../../../third_party/dart/runtime/observatory/web/timeline.js -FILE: ../../../third_party/dart/runtime/observatory_2/lib/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/cli.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/debugger.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/allocation_profile/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/cli/command.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/debugger/debugger.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/debugger/debugger_location.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/logging.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/logging_list.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/megamorphiccache_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectpool_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/ports.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/timeline_page.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/sample_profile/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/web/timeline.js FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.cc FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.h FILE: ../../../third_party/dart/runtime/vm/log.cc @@ -2656,151 +2474,6 @@ ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/stron ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/target.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/type_arguments.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/web/timeline_message_handler.js + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/event.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/models.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/repositories.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/app/notification.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/virtual_collection.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/virtual_tree.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/error_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/general_error.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/custom_element.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/nav_bar.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/nav_menu.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/rendering_queue.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/rendering_scheduler.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/tag.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/uris.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/inbound_references.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/counter_chart.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/location.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/run_state.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/shared_summary.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/summary.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metric/details.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metric/graph.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/class_menu.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/isolate_menu.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/library_menu.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/menu_item.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify_event.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify_exception.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/refresh.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/top_menu.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/vm_menu.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/retaining_path.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/source_link.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/strongly_reachable_instances.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_connect_target.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/exceptions.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/breakpoint.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/class.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/code.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/context.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/error.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/event.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/extension_data.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/field.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/flag.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/frame.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/function.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/guarded.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/heap_space.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/icdata.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/inbound_references.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/instance.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/isolate.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/library.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/local_var_descriptors.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/map_association.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/megamorphiccache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/metric.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/notification.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/object.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/objectpool.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/objectstore.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/pc_descriptors.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/persistent_handles.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/ports.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/retaining_path.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/sample_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/script.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/sentinel.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/source_location.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/target.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/timeline_event.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/type_arguments.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/unknown.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/vm.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/breakpoint.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/class.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/context.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/editor.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/eval.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/event.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/field.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/flag.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/function.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/heap_snapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/icdata.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/inbound_references.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/instance.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/isolate.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/library.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/megamorphiccache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/metric.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/notification.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/object.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/objectpool.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/objectstore.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/persistent_handles.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/ports.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/reachable_size.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/retained_size.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/retaining_path.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/sample_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/script.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/strongly_reachable_instances.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/target.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/type_arguments.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/allocation_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/breakpoint.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/class.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/context.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/editor.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/eval.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/event.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/field.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/flag.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/function.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/heap_snapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/icdata.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/inbound_references.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/instance.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/isolate.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/library.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/megamorphiccache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/metric.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/notification.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/object.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/objectpool.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/objectstore.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/persistent_handles.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/ports.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/reachable_size.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/retained_size.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/retaining_path.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/sample_profile.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/script.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/settings.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/strongly_reachable_instances.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/target.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/type_arguments.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/web/timeline_message_handler.js + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/syslog_fuchsia.cc + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/utils_fuchsia.cc + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/utils_fuchsia.h + ../../../third_party/dart/LICENSE @@ -3011,151 +2684,6 @@ FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/strongl FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/target.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/type_arguments.dart FILE: ../../../third_party/dart/runtime/observatory/web/timeline_message_handler.js -FILE: ../../../third_party/dart/runtime/observatory_2/lib/event.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/models.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/repositories.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/notification.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/virtual_collection.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/virtual_tree.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/error_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/general_error.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/custom_element.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/nav_bar.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/nav_menu.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/rendering_queue.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/rendering_scheduler.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/tag.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/uris.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/counter_chart.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/location.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/run_state.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/shared_summary.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/summary.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metric/details.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metric/graph.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/class_menu.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/isolate_menu.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/library_menu.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/menu_item.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify_event.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify_exception.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/refresh.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/top_menu.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/vm_menu.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/source_link.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/strongly_reachable_instances.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_connect_target.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/exceptions.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/breakpoint.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/class.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/code.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/context.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/error.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/event.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/extension_data.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/field.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/flag.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/frame.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/function.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/guarded.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/heap_space.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/icdata.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/instance.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/isolate.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/library.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/local_var_descriptors.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/map_association.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/megamorphiccache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/metric.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/notification.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/object.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/objectpool.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/objectstore.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/pc_descriptors.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/ports.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/script.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/sentinel.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/source_location.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/target.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/timeline_event.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/type_arguments.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/unknown.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/vm.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/breakpoint.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/class.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/context.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/editor.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/eval.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/event.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/field.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/flag.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/function.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/icdata.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/instance.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/isolate.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/library.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/megamorphiccache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/metric.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/notification.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/object.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/objectpool.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/objectstore.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/ports.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/reachable_size.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/retained_size.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/script.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/strongly_reachable_instances.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/target.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/type_arguments.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/breakpoint.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/class.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/context.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/editor.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/eval.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/event.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/field.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/flag.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/function.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/icdata.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/instance.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/isolate.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/library.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/megamorphiccache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/metric.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/notification.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/object.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/objectpool.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/objectstore.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/ports.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/reachable_size.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/retained_size.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/script.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/settings.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/strongly_reachable_instances.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/target.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/type_arguments.dart -FILE: ../../../third_party/dart/runtime/observatory_2/web/timeline_message_handler.js FILE: ../../../third_party/dart/runtime/platform/syslog_fuchsia.cc FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.cc FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.h @@ -3297,30 +2825,6 @@ ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/subty ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/unlinked_call.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/vm.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/search_bar.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/reload.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/singletargetcache_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/singletargetcache_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/subtypetestcache_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/subtypetestcache_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/timeline/dashboard.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unlinkedcall_ref.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unlinkedcall_view.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/service.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/single_target_cache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/subtype_test_cache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/timeline.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/unlinked_call.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/single_target_cache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/subtype_test_cache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/timeline.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/unlinked_call.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/vm.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/single_target_cache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/subtype_test_cache.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/timeline.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/unlinked_call.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/vm.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/allocation.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/growable_array.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_riscv.cc + ../../../third_party/dart/LICENSE @@ -3444,30 +2948,6 @@ FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/subtype FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/unlinked_call.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/vm.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/search_bar.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/reload.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/singletargetcache_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/singletargetcache_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/subtypetestcache_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/subtypetestcache_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/timeline/dashboard.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unlinkedcall_ref.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unlinkedcall_view.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/service.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/single_target_cache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/subtype_test_cache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/timeline.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/unlinked_call.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/single_target_cache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/subtype_test_cache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/timeline.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/unlinked_call.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/vm.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/single_target_cache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/subtype_test_cache.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/timeline.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/unlinked_call.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/vm.dart FILE: ../../../third_party/dart/runtime/platform/allocation.h FILE: ../../../third_party/dart/runtime/platform/growable_array.h FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_riscv.cc @@ -3736,11 +3216,6 @@ ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/iso ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/isolate_group.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/isolate_group.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline_base.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/tree_map.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/isolate_group.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/isolate_group.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/isolate_group.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/timeline_base.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/elf.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/thread_sanitizer.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_api_table.dart + ../../../third_party/dart/LICENSE @@ -3829,11 +3304,6 @@ FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/isola FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/isolate_group.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/isolate_group.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline_base.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/tree_map.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/isolate_group.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/isolate_group.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/isolate_group.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/timeline_base.dart FILE: ../../../third_party/dart/runtime/platform/elf.h FILE: ../../../third_party/dart/runtime/platform/thread_sanitizer.h FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_api_table.dart @@ -3988,8 +3458,6 @@ ORIGIN: ../../../third_party/dart/runtime/include/dart_version.h + ../../../thir ORIGIN: ../../../third_party/dart/runtime/include/internal/dart_api_dl_impl.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/bin/heap_snapshot.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/observatory/lib/src/elements/process_snapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/bin/heap_snapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/process_snapshot.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/allocation.cc + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/leak_sanitizer.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/platform/priority_queue.h + ../../../third_party/dart/LICENSE @@ -4060,8 +3528,6 @@ FILE: ../../../third_party/dart/runtime/include/dart_version.h FILE: ../../../third_party/dart/runtime/include/internal/dart_api_dl_impl.h FILE: ../../../third_party/dart/runtime/observatory/bin/heap_snapshot.dart FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/process_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory_2/bin/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/process_snapshot.dart FILE: ../../../third_party/dart/runtime/platform/allocation.cc FILE: ../../../third_party/dart/runtime/platform/leak_sanitizer.h FILE: ../../../third_party/dart/runtime/platform/priority_queue.h @@ -4278,14 +3744,10 @@ ORIGIN: ../../../third_party/dart/runtime/platform/mach_o.h + ../../../third_par ORIGIN: ../../../third_party/dart/runtime/platform/pe.h + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/bin/download.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/bin/explore.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/heapsnapshot.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/analysis.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/cli.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/completion.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/console.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/expression.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/format.dart + ../../../third_party/dart/LICENSE -ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/intset.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/load.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/compiler/backend/il_serializer.cc + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/runtime/vm/compiler/backend/il_serializer.h + ../../../third_party/dart/LICENSE @@ -4358,14 +3820,10 @@ FILE: ../../../third_party/dart/runtime/platform/mach_o.h FILE: ../../../third_party/dart/runtime/platform/pe.h FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/bin/download.dart FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/bin/explore.dart -FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/heapsnapshot.dart -FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/analysis.dart FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/cli.dart FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/completion.dart FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/console.dart FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/expression.dart -FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/format.dart -FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/intset.dart FILE: ../../../third_party/dart/runtime/tools/heapsnapshot/lib/src/load.dart FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_serializer.cc FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_serializer.h @@ -4477,6 +3935,7 @@ ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_allo ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/debugger.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/records.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_allow_interop_patch.dart + ../../../third_party/dart/LICENSE +ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/preambles/seal_native_object.js + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/records.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_shared/lib/js_interop_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/js_shared/lib/js_interop_unsafe_patch.dart + ../../../third_party/dart/LICENSE @@ -4501,7 +3960,9 @@ ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/string_helper.dart ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/string_stringref_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/sync_star_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/typed_data.dart + ../../../third_party/dart/LICENSE +ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/wasm_types_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/weak_patch.dart + ../../../third_party/dart/LICENSE +ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm_js_compatibility/lib/convert_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm_js_compatibility/lib/typed_data.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/_internal/wasm_js_compatibility/lib/typed_data_patch.dart + ../../../third_party/dart/LICENSE ORIGIN: ../../../third_party/dart/sdk/lib/async/future_extensions.dart + ../../../third_party/dart/LICENSE @@ -4525,6 +3986,7 @@ FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_allow_ FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/debugger.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/records.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_allow_interop_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/preambles/seal_native_object.js FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/records.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_shared/lib/js_interop_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_shared/lib/js_interop_unsafe_patch.dart @@ -4549,7 +4011,9 @@ FILE: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/string_helper.dart FILE: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/string_stringref_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/sync_star_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/typed_data.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/wasm_types_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/wasm/lib/weak_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/wasm_js_compatibility/lib/convert_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/wasm_js_compatibility/lib/typed_data.dart FILE: ../../../third_party/dart/sdk/lib/_internal/wasm_js_compatibility/lib/typed_data_patch.dart FILE: ../../../third_party/dart/sdk/lib/async/future_extensions.dart @@ -4688,12 +4152,12 @@ ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/bign ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.h ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.h -ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.h -ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.cc +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-to-string.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.h ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.h +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/string-to-double.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.h ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/utils.h @@ -4703,12 +4167,12 @@ FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.h FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-to-string.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.h FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/string-to-double.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.h FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/utils.h @@ -4745,12 +4209,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: double-conversion ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-to-string.h ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.cc ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/ieee.h +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/string-to-double.h TYPE: LicenseType.bsd FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-to-string.h FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.cc FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/ieee.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/string-to-double.h ---------------------------------------------------------------------------------------------------- Copyright 2012 the V8 project authors. All rights reserved. @@ -4793,14 +4261,6 @@ FILE: ../../../third_party/dart/runtime/observatory/web/favicon.ico FILE: ../../../third_party/dart/runtime/observatory/web/index.html FILE: ../../../third_party/dart/runtime/observatory/web/third_party/trace_viewer_full.html FILE: ../../../third_party/dart/runtime/observatory/web/timeline.html -FILE: ../../../third_party/dart/runtime/observatory_2/lib/elements.dart -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/img/chromium_icon.png -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/img/dart_icon.png -FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/img/isolate_icon.png -FILE: ../../../third_party/dart/runtime/observatory_2/web/favicon.ico -FILE: ../../../third_party/dart/runtime/observatory_2/web/index.html -FILE: ../../../third_party/dart/runtime/observatory_2/web/third_party/trace_viewer_full.html -FILE: ../../../third_party/dart/runtime/observatory_2/web/timeline.html FILE: ../../../third_party/dart/runtime/tools/wiki/styles/style.scss FILE: ../../../third_party/dart/runtime/tools/wiki/templates/includes/auto-refresh.html FILE: ../../../third_party/dart/runtime/tools/wiki/templates/page.html diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f37b0279e3c8d..5d47269235ca8 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -277,6 +277,331 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================================================== +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/mappings.h +ORIGIN: ../../../flutter/third_party/glfw/src/mappings.h.in +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/mappings.h +FILE: ../../../flutter/third_party/glfw/src/mappings.h.in +---------------------------------------------------------------------------------------------------- +Copyright (C) 1997-2013 Sam Lantinga + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/LICENSE.md +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/glfw.rc.in +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard + +Copyright (c) 2006-2019 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/context.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/context.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2016 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/linux_joystick.c +ORIGIN: ../../../flutter/third_party/glfw/src/posix_thread.c +ORIGIN: ../../../flutter/third_party/glfw/src/posix_thread.h +ORIGIN: ../../../flutter/third_party/glfw/src/posix_time.c +ORIGIN: ../../../flutter/third_party/glfw/src/posix_time.h +ORIGIN: ../../../flutter/third_party/glfw/src/win32_thread.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_thread.h +ORIGIN: ../../../flutter/third_party/glfw/src/win32_time.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_time.h +ORIGIN: ../../../flutter/third_party/glfw/src/xkb_unicode.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/linux_joystick.c +FILE: ../../../flutter/third_party/glfw/src/posix_thread.c +FILE: ../../../flutter/third_party/glfw/src/posix_thread.h +FILE: ../../../flutter/third_party/glfw/src/posix_time.c +FILE: ../../../flutter/third_party/glfw/src/posix_time.h +FILE: ../../../flutter/third_party/glfw/src/win32_thread.c +FILE: ../../../flutter/third_party/glfw/src/win32_thread.h +FILE: ../../../flutter/third_party/glfw/src/win32_time.c +FILE: ../../../flutter/third_party/glfw/src/win32_time.h +FILE: ../../../flutter/third_party/glfw/src/xkb_unicode.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2017 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/include/GLFW/glfw3native.h +ORIGIN: ../../../flutter/third_party/glfw/src/init.c +ORIGIN: ../../../flutter/third_party/glfw/src/platform.c +ORIGIN: ../../../flutter/third_party/glfw/src/platform.h +ORIGIN: ../../../flutter/third_party/glfw/src/vulkan.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/include/GLFW/glfw3native.h +FILE: ../../../flutter/third_party/glfw/src/init.c +FILE: ../../../flutter/third_party/glfw/src/platform.c +FILE: ../../../flutter/third_party/glfw/src/platform.h +FILE: ../../../flutter/third_party/glfw/src/vulkan.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2018 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/include/GLFW/glfw3.h +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_monitor.m +ORIGIN: ../../../flutter/third_party/glfw/src/egl_context.c +ORIGIN: ../../../flutter/third_party/glfw/src/glx_context.c +ORIGIN: ../../../flutter/third_party/glfw/src/input.c +ORIGIN: ../../../flutter/third_party/glfw/src/internal.h +ORIGIN: ../../../flutter/third_party/glfw/src/monitor.c +ORIGIN: ../../../flutter/third_party/glfw/src/wgl_context.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_init.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_joystick.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_monitor.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_platform.h +ORIGIN: ../../../flutter/third_party/glfw/src/win32_window.c +ORIGIN: ../../../flutter/third_party/glfw/src/x11_init.c +ORIGIN: ../../../flutter/third_party/glfw/src/x11_monitor.c +ORIGIN: ../../../flutter/third_party/glfw/src/x11_platform.h +ORIGIN: ../../../flutter/third_party/glfw/src/x11_window.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/include/GLFW/glfw3.h +FILE: ../../../flutter/third_party/glfw/src/cocoa_monitor.m +FILE: ../../../flutter/third_party/glfw/src/egl_context.c +FILE: ../../../flutter/third_party/glfw/src/glx_context.c +FILE: ../../../flutter/third_party/glfw/src/input.c +FILE: ../../../flutter/third_party/glfw/src/internal.h +FILE: ../../../flutter/third_party/glfw/src/monitor.c +FILE: ../../../flutter/third_party/glfw/src/wgl_context.c +FILE: ../../../flutter/third_party/glfw/src/win32_init.c +FILE: ../../../flutter/third_party/glfw/src/win32_joystick.c +FILE: ../../../flutter/third_party/glfw/src/win32_monitor.c +FILE: ../../../flutter/third_party/glfw/src/win32_platform.h +FILE: ../../../flutter/third_party/glfw/src/win32_window.c +FILE: ../../../flutter/third_party/glfw/src/x11_init.c +FILE: ../../../flutter/third_party/glfw/src/x11_monitor.c +FILE: ../../../flutter/third_party/glfw/src/x11_platform.h +FILE: ../../../flutter/third_party/glfw/src/x11_window.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2019 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/window.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/window.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2019 Camilla Löwy +Copyright (c) 2012 Torsten Walluhn + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_joystick.h +ORIGIN: ../../../flutter/third_party/glfw/src/null_joystick.h +ORIGIN: ../../../flutter/third_party/glfw/src/win32_joystick.h +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/cocoa_joystick.h +FILE: ../../../flutter/third_party/glfw/src/null_joystick.h +FILE: ../../../flutter/third_party/glfw/src/win32_joystick.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2006-2017 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/mappings.h +ORIGIN: ../../../flutter/third_party/glfw/src/mappings.h.in +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/mappings.h +FILE: ../../../flutter/third_party/glfw/src/mappings.h.in +---------------------------------------------------------------------------------------------------- +Copyright (c) 2006-2018 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + ==================================================================================================== LIBRARY: accessibility ORIGIN: ../../../flutter/third_party/accessibility/gfx/geometry/insets.cc + ../../../flutter/third_party/accessibility/LICENSE @@ -312,6 +637,125 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_time.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/cocoa_time.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2016 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_init.m +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_platform.h +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_window.m +ORIGIN: ../../../flutter/third_party/glfw/src/nsgl_context.m +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/cocoa_init.m +FILE: ../../../flutter/third_party/glfw/src/cocoa_platform.h +FILE: ../../../flutter/third_party/glfw/src/cocoa_window.m +FILE: ../../../flutter/third_party/glfw/src/nsgl_context.m +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2019 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_joystick.m +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/cocoa_joystick.m +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2019 Camilla Löwy +Copyright (c) 2012 Torsten Walluhn + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/cocoa_time.h +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/cocoa_time.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2021 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + ==================================================================================================== LIBRARY: accessibility ORIGIN: ../../../flutter/third_party/accessibility/base/win/scoped_bstr.cc + ../../../flutter/third_party/accessibility/LICENSE @@ -530,6 +974,44 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/linux_joystick.h +ORIGIN: ../../../flutter/third_party/glfw/src/wl_init.c +ORIGIN: ../../../flutter/third_party/glfw/src/wl_monitor.c +ORIGIN: ../../../flutter/third_party/glfw/src/wl_platform.h +ORIGIN: ../../../flutter/third_party/glfw/src/wl_window.c +ORIGIN: ../../../flutter/third_party/glfw/src/xkb_unicode.h +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/linux_joystick.h +FILE: ../../../flutter/third_party/glfw/src/wl_init.c +FILE: ../../../flutter/third_party/glfw/src/wl_monitor.c +FILE: ../../../flutter/third_party/glfw/src/wl_platform.h +FILE: ../../../flutter/third_party/glfw/src/wl_window.c +FILE: ../../../flutter/third_party/glfw/src/xkb_unicode.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014 Jonas Ådahl + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + ==================================================================================================== LIBRARY: accessibility ORIGIN: ../../../flutter/third_party/accessibility/base/win/windows_types.h + ../../../flutter/third_party/accessibility/LICENSE @@ -565,6 +1047,158 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/null_init.c +ORIGIN: ../../../flutter/third_party/glfw/src/null_platform.h +ORIGIN: ../../../flutter/third_party/glfw/src/osmesa_context.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/null_init.c +FILE: ../../../flutter/third_party/glfw/src/null_platform.h +FILE: ../../../flutter/third_party/glfw/src/osmesa_context.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2016 Google Inc. +Copyright (c) 2016-2017 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/null_monitor.c +ORIGIN: ../../../flutter/third_party/glfw/src/null_window.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/null_monitor.c +FILE: ../../../flutter/third_party/glfw/src/null_window.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2016 Google Inc. +Copyright (c) 2016-2019 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/null_joystick.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/null_joystick.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2016-2017 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/posix_module.c +ORIGIN: ../../../flutter/third_party/glfw/src/win32_module.c +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/posix_module.c +FILE: ../../../flutter/third_party/glfw/src/win32_module.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2021 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../flutter/third_party/glfw/src/posix_poll.c +ORIGIN: ../../../flutter/third_party/glfw/src/posix_poll.h +TYPE: LicenseType.unknown +FILE: ../../../flutter/third_party/glfw/src/posix_poll.c +FILE: ../../../flutter/third_party/glfw/src/posix_poll.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2022 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + ==================================================================================================== LIBRARY: web_locale_keymap ORIGIN: ../../../flutter/third_party/web_locale_keymap/lib/web_locale_keymap.dart + ../../../flutter/third_party/web_locale_keymap/License.txt @@ -870,6 +1504,8 @@ ORIGIN: ../../../flutter/fml/concurrent_message_loop.cc + ../../../flutter/LICEN ORIGIN: ../../../flutter/fml/concurrent_message_loop.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/concurrent_message_loop_factory.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/container.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/fml/cpu_affinity.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/fml/cpu_affinity.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/dart/dart_converter.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/dart/dart_converter.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/delayed_task.cc + ../../../flutter/LICENSE @@ -916,6 +1552,8 @@ ORIGIN: ../../../flutter/fml/message_loop_task_queues_benchmark.cc + ../../../fl ORIGIN: ../../../flutter/fml/native_library.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/paths.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/paths.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/fml/platform/android/cpu_affinity.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/fml/platform/android/cpu_affinity.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/jni_util.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/jni_util.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/jni_weak_ref.cc + ../../../flutter/LICENSE @@ -979,6 +1617,7 @@ ORIGIN: ../../../flutter/fml/shared_thread_merger.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/shared_thread_merger.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/size.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/status.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/fml/status_or.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/string_conversion.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/string_conversion.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/synchronization/atomic_object.h + ../../../flutter/LICENSE @@ -1023,6 +1662,8 @@ ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc + ../../../f ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/canvas.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/canvas.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/canvas_recorder.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/canvas_type.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/color_filter.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/color_filter.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/color_source.cc + ../../../flutter/LICENSE @@ -1039,6 +1680,8 @@ ORIGIN: ../../../flutter/impeller/aiks/picture.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/picture.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/picture_recorder.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/picture_recorder.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/trace_serializer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/trace_serializer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/archivist/archivable.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/archivist/archivable.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/archivist/archive.cc + ../../../flutter/LICENSE @@ -1409,8 +2052,8 @@ ORIGIN: ../../../flutter/impeller/golden_tests/golden_tests.cc + ../../../flutte ORIGIN: ../../../flutter/impeller/golden_tests/main.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/golden_tests/metal_screenshot.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/golden_tests/metal_screenshot.mm + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/golden_tests/metal_screenshoter.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/golden_tests/metal_screenshoter.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/golden_tests/metal_screenshotter.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/golden_tests/metal_screenshotter.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/golden_tests/working_directory.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/golden_tests/working_directory.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/golden_tests_harvester/bin/golden_tests_harvester.dart + ../../../flutter/LICENSE @@ -1443,6 +2086,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h + . ORIGIN: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gles.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/gles/handle_gles.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/gles/handle_gles.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc + ../../../flutter/LICENSE @@ -1485,6 +2130,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.h + . ORIGIN: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/pipeline_mtl.h + ../../../flutter/LICENSE @@ -1542,6 +2189,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.cc + . ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc + ../../../flutter/LICENSE @@ -1970,10 +2619,11 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.da ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/renderer.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/shader.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart + ../../../flutter/LICENSE @@ -2038,7 +2688,9 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/key_map.g.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/layers.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/mouse/context_menu.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/mouse/cursor.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/mouse/prevent_default.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/noto_font.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/noto_font_encoding.dart + ../../../flutter/LICENSE @@ -2069,6 +2721,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart + .. ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/image.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/incrementable.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/label_and_value.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/link.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/live_region.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/platform_view.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/scrollable.dart + ../../../flutter/LICENSE @@ -2236,6 +2889,8 @@ ORIGIN: ../../../flutter/runtime/test_font_data.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/runtime/test_font_data.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/animator.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/animator.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/common/base64.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/common/base64.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/context_options.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/context_options.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/dart_native_benchmarks.cc + ../../../flutter/LICENSE @@ -2361,10 +3016,10 @@ ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/surface_p ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/flutter_main.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/flutter_main.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/android/hardware_buffer_external_texture.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_gl.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_gl.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/image_external_texture.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_gl.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_gl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/Log.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java + ../../../flutter/LICENSE @@ -2445,6 +3100,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/syst ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/RestorationChannel.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java + ../../../flutter/LICENSE @@ -2487,6 +3143,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platf ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java + ../../../flutter/LICENSE @@ -2691,6 +3348,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextI ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelay.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelay.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelayTest.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m + ../../../flutter/LICENSE @@ -2712,6 +3370,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/KeyCodeMap_I ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest_mrc.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController_FlutterScreenAndSceneIfLoadedTest.mm + ../../../flutter/LICENSE @@ -2849,6 +3509,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterVie ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/embedder/embedder.cc + ../../../flutter/LICENSE @@ -3382,6 +4043,7 @@ ORIGIN: ../../../flutter/third_party/txt/src/txt/platform.h + ../../../flutter/L ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_android.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_fuchsia.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_linux.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_mac.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_mac.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_windows.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/web_test_fonts/lib/web_test_fonts.dart + ../../../flutter/LICENSE @@ -3394,6 +4056,7 @@ ORIGIN: ../../../flutter/vulkan/procs/vulkan_interface.cc + ../../../flutter/LIC ORIGIN: ../../../flutter/vulkan/procs/vulkan_interface.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/vulkan/procs/vulkan_proc_table.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/vulkan/procs/vulkan_proc_table.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/vulkan/swiftshader_path.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/vulkan/vulkan_application.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/vulkan/vulkan_application.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/vulkan/vulkan_backbuffer.cc + ../../../flutter/LICENSE @@ -3614,6 +4277,8 @@ FILE: ../../../flutter/fml/concurrent_message_loop.cc FILE: ../../../flutter/fml/concurrent_message_loop.h FILE: ../../../flutter/fml/concurrent_message_loop_factory.cc FILE: ../../../flutter/fml/container.h +FILE: ../../../flutter/fml/cpu_affinity.cc +FILE: ../../../flutter/fml/cpu_affinity.h FILE: ../../../flutter/fml/dart/dart_converter.cc FILE: ../../../flutter/fml/dart/dart_converter.h FILE: ../../../flutter/fml/delayed_task.cc @@ -3660,6 +4325,8 @@ FILE: ../../../flutter/fml/message_loop_task_queues_benchmark.cc FILE: ../../../flutter/fml/native_library.h FILE: ../../../flutter/fml/paths.cc FILE: ../../../flutter/fml/paths.h +FILE: ../../../flutter/fml/platform/android/cpu_affinity.cc +FILE: ../../../flutter/fml/platform/android/cpu_affinity.h FILE: ../../../flutter/fml/platform/android/jni_util.cc FILE: ../../../flutter/fml/platform/android/jni_util.h FILE: ../../../flutter/fml/platform/android/jni_weak_ref.cc @@ -3723,6 +4390,7 @@ FILE: ../../../flutter/fml/shared_thread_merger.cc FILE: ../../../flutter/fml/shared_thread_merger.h FILE: ../../../flutter/fml/size.h FILE: ../../../flutter/fml/status.h +FILE: ../../../flutter/fml/status_or.h FILE: ../../../flutter/fml/string_conversion.cc FILE: ../../../flutter/fml/string_conversion.h FILE: ../../../flutter/fml/synchronization/atomic_object.h @@ -3767,6 +4435,8 @@ FILE: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc FILE: ../../../flutter/impeller/aiks/aiks_playground_inspector.h FILE: ../../../flutter/impeller/aiks/canvas.cc FILE: ../../../flutter/impeller/aiks/canvas.h +FILE: ../../../flutter/impeller/aiks/canvas_recorder.h +FILE: ../../../flutter/impeller/aiks/canvas_type.h FILE: ../../../flutter/impeller/aiks/color_filter.cc FILE: ../../../flutter/impeller/aiks/color_filter.h FILE: ../../../flutter/impeller/aiks/color_source.cc @@ -3783,6 +4453,8 @@ FILE: ../../../flutter/impeller/aiks/picture.cc FILE: ../../../flutter/impeller/aiks/picture.h FILE: ../../../flutter/impeller/aiks/picture_recorder.cc FILE: ../../../flutter/impeller/aiks/picture_recorder.h +FILE: ../../../flutter/impeller/aiks/trace_serializer.cc +FILE: ../../../flutter/impeller/aiks/trace_serializer.h FILE: ../../../flutter/impeller/archivist/archivable.cc FILE: ../../../flutter/impeller/archivist/archivable.h FILE: ../../../flutter/impeller/archivist/archive.cc @@ -4153,8 +4825,8 @@ FILE: ../../../flutter/impeller/golden_tests/golden_tests.cc FILE: ../../../flutter/impeller/golden_tests/main.cc FILE: ../../../flutter/impeller/golden_tests/metal_screenshot.h FILE: ../../../flutter/impeller/golden_tests/metal_screenshot.mm -FILE: ../../../flutter/impeller/golden_tests/metal_screenshoter.h -FILE: ../../../flutter/impeller/golden_tests/metal_screenshoter.mm +FILE: ../../../flutter/impeller/golden_tests/metal_screenshotter.h +FILE: ../../../flutter/impeller/golden_tests/metal_screenshotter.mm FILE: ../../../flutter/impeller/golden_tests/working_directory.cc FILE: ../../../flutter/impeller/golden_tests/working_directory.h FILE: ../../../flutter/impeller/golden_tests_harvester/bin/golden_tests_harvester.dart @@ -4187,6 +4859,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/gles.h +FILE: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/handle_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/handle_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc @@ -4229,6 +4903,8 @@ FILE: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.mm +FILE: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.h +FILE: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/pipeline_mtl.h @@ -4286,6 +4962,8 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/limits_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.h @@ -4719,10 +5397,11 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/renderer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/shader.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart @@ -4787,7 +5466,9 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/key_map.g.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/layers.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse/context_menu.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse/cursor.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse/prevent_default.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/noto_font.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/noto_font_encoding.dart @@ -4818,6 +5499,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/image.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/incrementable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/label_and_value.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/link.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/live_region.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/platform_view.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -4985,6 +5667,8 @@ FILE: ../../../flutter/runtime/test_font_data.cc FILE: ../../../flutter/runtime/test_font_data.h FILE: ../../../flutter/shell/common/animator.cc FILE: ../../../flutter/shell/common/animator.h +FILE: ../../../flutter/shell/common/base64.cc +FILE: ../../../flutter/shell/common/base64.h FILE: ../../../flutter/shell/common/context_options.cc FILE: ../../../flutter/shell/common/context_options.h FILE: ../../../flutter/shell/common/dart_native_benchmarks.cc @@ -5110,12 +5794,12 @@ FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_poo FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool.h FILE: ../../../flutter/shell/platform/android/flutter_main.cc FILE: ../../../flutter/shell/platform/android/flutter_main.h -FILE: ../../../flutter/shell/platform/android/hardware_buffer_external_texture.cc -FILE: ../../../flutter/shell/platform/android/hardware_buffer_external_texture.h -FILE: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_gl.cc -FILE: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_gl.h -FILE: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_vk.cc -FILE: ../../../flutter/shell/platform/android/hardware_buffer_external_texture_vk.h +FILE: ../../../flutter/shell/platform/android/image_external_texture.cc +FILE: ../../../flutter/shell/platform/android/image_external_texture.h +FILE: ../../../flutter/shell/platform/android/image_external_texture_gl.cc +FILE: ../../../flutter/shell/platform/android/image_external_texture_gl.h +FILE: ../../../flutter/shell/platform/android/image_external_texture_vk.cc +FILE: ../../../flutter/shell/platform/android/image_external_texture_vk.h FILE: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java FILE: ../../../flutter/shell/platform/android/io/flutter/Log.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java @@ -5200,6 +5884,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/system FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/RestorationChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java @@ -5247,6 +5932,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java @@ -5453,6 +6139,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInp FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelay.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelay.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelayTest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m @@ -5474,6 +6161,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/KeyCodeMap_Int FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest_mrc.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController_FlutterScreenAndSceneIfLoadedTest.mm @@ -5613,6 +6302,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewP FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/module.modulemap @@ -6152,6 +6842,7 @@ FILE: ../../../flutter/third_party/txt/src/txt/platform.h FILE: ../../../flutter/third_party/txt/src/txt/platform_android.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_fuchsia.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_linux.cc +FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.h FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.mm FILE: ../../../flutter/third_party/txt/src/txt/platform_windows.cc FILE: ../../../flutter/third_party/web_test_fonts/lib/web_test_fonts.dart @@ -6164,6 +6855,7 @@ FILE: ../../../flutter/vulkan/procs/vulkan_interface.cc FILE: ../../../flutter/vulkan/procs/vulkan_interface.h FILE: ../../../flutter/vulkan/procs/vulkan_proc_table.cc FILE: ../../../flutter/vulkan/procs/vulkan_proc_table.h +FILE: ../../../flutter/vulkan/swiftshader_path.h FILE: ../../../flutter/vulkan/vulkan_application.cc FILE: ../../../flutter/vulkan/vulkan_application.h FILE: ../../../flutter/vulkan/vulkan_backbuffer.cc @@ -6819,4 +7511,4 @@ use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. ==================================================================================================== -Total license count: 20 +Total license count: 39 diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index b21c0d70c5f40..4f90cb18fe7d5 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 75eb720a09615537eaca04d3a0afb82f +Signature: 0fe3ee87896948e29956db2de14cc16e ==================================================================================================== LIBRARY: fuchsia_sdk @@ -9,6 +9,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libtrace-engine.so @@ -18,6 +19,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsync.a @@ -186,6 +188,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libtrace-engine.so @@ -195,6 +198,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libsync.a @@ -363,6 +367,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libtrace-engine.so @@ -372,6 +377,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsync.a @@ -536,15 +542,22 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libzircon.so FILE: ../../../fuchsia/sdk/linux/data/config/symbol_index/config.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64/release/package_manifest.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64/release/package_manifest.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/arm64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/arm64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/riscv64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/riscv64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/x64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/x64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64-api15/release/package_manifest.json FILE: ../../../fuchsia/sdk/linux/pkg/async-default/async-default.ifs FILE: ../../../fuchsia/sdk/linux/pkg/driver_runtime_shared_lib/driver_runtime.ifs FILE: ../../../fuchsia/sdk/linux/pkg/fdio/fdio.ifs +FILE: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/heapdump_instrumentation.ifs FILE: ../../../fuchsia/sdk/linux/pkg/inspect/inspect.json FILE: ../../../fuchsia/sdk/linux/pkg/svc/svc.ifs FILE: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_shard_sdk.json @@ -1821,6 +1834,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libtrace-engine.so @@ -1830,6 +1844,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsync.a @@ -1998,6 +2013,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libtrace-engine.so @@ -2007,6 +2023,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libsync.a @@ -2175,6 +2192,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libtrace-engine.so @@ -2184,6 +2202,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsync.a @@ -2348,15 +2367,22 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libzircon.so FILE: ../../../fuchsia/sdk/linux/data/config/symbol_index/config.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64/release/package_manifest.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64/release/package_manifest.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/arm64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/arm64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/riscv64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/riscv64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/x64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/x64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64-api15/release/package_manifest.json FILE: ../../../fuchsia/sdk/linux/pkg/async-default/async-default.ifs FILE: ../../../fuchsia/sdk/linux/pkg/driver_runtime_shared_lib/driver_runtime.ifs FILE: ../../../fuchsia/sdk/linux/pkg/fdio/fdio.ifs +FILE: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/heapdump_instrumentation.ifs FILE: ../../../fuchsia/sdk/linux/pkg/inspect/inspect.json FILE: ../../../fuchsia/sdk/linux/pkg/svc/svc.ifs FILE: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_shard_sdk.json @@ -2421,6 +2447,7 @@ ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/lookup.h + .. ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/clock.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/scheduler.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/utc.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/bind/fuchsia.platform/fuchsia.platform.bind + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/sl4f.dart + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/audio.dart + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device_log.dart + ../../../fuchsia/sdk/linux/LICENSE @@ -2719,6 +2746,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/lookup.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/clock.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/scheduler.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/utc.h +FILE: ../../../fuchsia/sdk/linux/bind/fuchsia.platform/fuchsia.platform.bind FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/sl4f.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/audio.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device_log.dart @@ -3039,6 +3067,8 @@ ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/crash-re ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/string_view.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls-next.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/testonly-syscalls.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/bind/fuchsia.arm.platform/fuchsia.arm.platform.bind + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/bind/fuchsia.nxp.platform/fuchsia.nxp.platform.bind + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/component.dart + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device.dart + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/diagnostics.dart + ../../../fuchsia/sdk/linux/LICENSE @@ -3213,6 +3243,8 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/crash-reas FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/string_view.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls-next.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/testonly-syscalls.h +FILE: ../../../fuchsia/sdk/linux/bind/fuchsia.arm.platform/fuchsia.arm.platform.bind +FILE: ../../../fuchsia/sdk/linux/bind/fuchsia.nxp.platform/fuchsia.nxp.platform.bind FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/component.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/diagnostics.dart @@ -3726,10 +3758,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LIBRARY: fuchsia_sdk ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/errors.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/internal/cdecls.inc + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/iob.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/errors.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/syscalls/internal/cdecls.inc + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/syscalls/iob.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/errors.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/internal/cdecls.inc + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/iob.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/bind/fuchsia/fuchsia.bind + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/performance_publish.dart + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/power_metrics.dart + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/overview.fidl + ../../../fuchsia/sdk/linux/LICENSE @@ -3755,6 +3791,7 @@ ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.resolution/overview.fi ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.resolution/package.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.resolution/resolver.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.sandbox/sandbox.fidl + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/namespace.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/overview.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.debugdata/publisher.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/overview.fidl + ../../../fuchsia/sdk/linux/LICENSE @@ -3944,6 +3981,13 @@ ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver/include/lib/fidl_driver/cpp/u ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver/include/lib/fidl_driver/cpp/wire_client.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver/include/lib/fidl_driver/cpp/wire_messaging_declarations.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver/unknown_interactions.cc + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/internal/endpoint_conversions.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/internal/natural_client_details.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_client.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_messaging.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_types.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/unified_messaging_declarations.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/natural_messaging.cc + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/inline_any.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/internal/inline_any.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/input_report_reader/reader.cc + ../../../fuchsia/sdk/linux/LICENSE @@ -3974,10 +4018,14 @@ ORIGIN: ../../../fuchsia/sdk/linux/pkg/vulkan/client.shard.cml + ../../../fuchsi TYPE: LicenseType.bsd FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/errors.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/internal/cdecls.inc +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/iob.h FILE: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/errors.h FILE: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/syscalls/internal/cdecls.inc +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/syscalls/iob.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/errors.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/internal/cdecls.inc +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/iob.h +FILE: ../../../fuchsia/sdk/linux/bind/fuchsia/fuchsia.bind FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/performance_publish.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/power_metrics.dart FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/overview.fidl @@ -4003,6 +4051,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.resolution/overview.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.resolution/package.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.resolution/resolver.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.sandbox/sandbox.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/namespace.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/overview.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.debugdata/publisher.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/overview.fidl @@ -4192,6 +4241,13 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver/include/lib/fidl_driver/cpp/unk FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver/include/lib/fidl_driver/cpp/wire_client.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver/include/lib/fidl_driver/cpp/wire_messaging_declarations.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver/unknown_interactions.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/internal/endpoint_conversions.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/internal/natural_client_details.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_client.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_messaging.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_types.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/unified_messaging_declarations.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/natural_messaging.cc FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/inline_any.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/internal/inline_any.h FILE: ../../../fuchsia/sdk/linux/pkg/input_report_reader/reader.cc @@ -4254,6 +4310,7 @@ ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.sandbox/overview.fidl ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/controller.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.device.fs/names.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.driver.framework/driver.fidl + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.driver.framework/driver_info.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/graphical_presenter.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio.signalprocessing/endpoint.fidl + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/clock.fidl + ../../../fuchsia/sdk/linux/LICENSE @@ -4287,6 +4344,7 @@ ORIGIN: ../../../fuchsia/sdk/linux/pkg/component_outgoing_cpp/include/lib/compon ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/driver_base.cc + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/driver_export.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/internal/basic_factory.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/internal/driver_server.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/internal/lifecycle.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/prepare_stop_completer.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/start_completer.h + ../../../fuchsia/sdk/linux/LICENSE @@ -4304,10 +4362,16 @@ ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/driver_runtime.cc + .. ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/environment_variables.cc + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/test_environment.cc + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/test_node.cc + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_ostream.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/include/heapdump/bind.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/include/heapdump/snapshot.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/include/heapdump/stats.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/inspect/offer.shard.cml + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/inspect/use.shard.cml + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/sync_cpp/include/lib/sync/cpp/mutex.h + ../../../fuchsia/sdk/linux/LICENSE ORIGIN: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service_handler.cc + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/iob.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/pkg/zx/iob.cc + ../../../fuchsia/sdk/linux/LICENSE TYPE: LicenseType.bsd FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/flatland_example.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/media_session.dart @@ -4315,6 +4379,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.sandbox/overview.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/controller.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.device.fs/names.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.driver.framework/driver.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.driver.framework/driver_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/graphical_presenter.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio.signalprocessing/endpoint.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/clock.fidl @@ -4348,6 +4413,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/component_outgoing_cpp/include/lib/componen FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/driver_base.cc FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/driver_export.h FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/internal/basic_factory.h +FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/internal/driver_server.h FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/internal/lifecycle.h FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/prepare_stop_completer.h FILE: ../../../fuchsia/sdk/linux/pkg/driver_component_cpp/include/lib/driver/component/cpp/start_completer.h @@ -4365,10 +4431,16 @@ FILE: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/driver_runtime.cc FILE: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/environment_variables.cc FILE: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/test_environment.cc FILE: ../../../fuchsia/sdk/linux/pkg/driver_testing_cpp/test_node.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_driver_natural/include/lib/fidl_driver/cpp/natural_ostream.h +FILE: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/include/heapdump/bind.h +FILE: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/include/heapdump/snapshot.h +FILE: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/include/heapdump/stats.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/offer.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/inspect/use.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/sync_cpp/include/lib/sync/cpp/mutex.h FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service_handler.cc +FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/iob.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/iob.cc ---------------------------------------------------------------------------------------------------- Copyright 2023 The Fuchsia Authors. All rights reserved. @@ -4405,6 +4477,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libtrace-engine.so @@ -4414,6 +4487,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsync.a @@ -4582,6 +4656,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/dist/libtrace-engine.so @@ -4591,6 +4666,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/riscv64/lib/libsync.a @@ -4759,6 +4835,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsyslog.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libtrace-engine.so @@ -4768,6 +4845,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libdriver_runtime.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libfdio.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libheapdump_instrumentation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libmagma_client.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsync.a @@ -4932,15 +5010,22 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libzircon.so FILE: ../../../fuchsia/sdk/linux/data/config/symbol_index/config.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64/release/package_manifest.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64/release/package_manifest.json -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64/release/content_checklist_path -FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/arm64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/arm64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/riscv64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/riscv64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/x64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/heapdump-collector/x64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/arm64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/riscv64-api15/release/package_manifest.json +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64-api15/release/content_checklist_path +FILE: ../../../fuchsia/sdk/linux/packages/realm_builder_server/x64-api15/release/package_manifest.json FILE: ../../../fuchsia/sdk/linux/pkg/async-default/async-default.ifs FILE: ../../../fuchsia/sdk/linux/pkg/driver_runtime_shared_lib/driver_runtime.ifs FILE: ../../../fuchsia/sdk/linux/pkg/fdio/fdio.ifs +FILE: ../../../fuchsia/sdk/linux/pkg/heapdump_instrumentation/heapdump_instrumentation.ifs FILE: ../../../fuchsia/sdk/linux/pkg/inspect/inspect.json FILE: ../../../fuchsia/sdk/linux/pkg/svc/svc.ifs FILE: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_shard_sdk.json diff --git a/ci/licenses_golden/licenses_gpu b/ci/licenses_golden/licenses_gpu deleted file mode 100644 index dcffbcface07a..0000000000000 --- a/ci/licenses_golden/licenses_gpu +++ /dev/null @@ -1,38 +0,0 @@ -Signature: ffe64a3daaf0ad982854594ad155dd56 - -==================================================================================================== -LIBRARY: engine -ORIGIN: ../../../gpu/GLES2/gl2chromium.h + ../../../gpu/LICENSE -ORIGIN: ../../../gpu/command_buffer/client/gles2_c_lib_export.h + ../../../gpu/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../gpu/GLES2/gl2chromium.h -FILE: ../../../gpu/command_buffer/client/gles2_c_lib_export.h ----------------------------------------------------------------------------------------------------- -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -Total license count: 1 diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 6fb15209df5f1..341af48f6b66f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8ef10e4ab0d396fd7a4dde6d2a3971dc +Signature: c3073afa753b36b5069f1287dbbcd21d ==================================================================================================== LIBRARY: etc1 @@ -317,6 +317,7 @@ ORIGIN: ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/.bazelignore FILE: ../../../third_party/skia/.bazelproject +FILE: ../../../third_party/skia/Cargo.toml FILE: ../../../third_party/skia/RELEASE_NOTES.md FILE: ../../../third_party/skia/go.mod FILE: ../../../third_party/skia/go.sum @@ -385,9 +386,18 @@ FILE: ../../../third_party/skia/modules/pathkit/perf/pathops.bench.js FILE: ../../../third_party/skia/modules/pathkit/perf/perfReporter.js FILE: ../../../third_party/skia/modules/skparagraph/test.html FILE: ../../../third_party/skia/package-lock.json -FILE: ../../../third_party/skia/relnotes/directcontext_submit.md -FILE: ../../../third_party/skia/relnotes/shadowflags.md +FILE: ../../../third_party/skia/relnotes/base64.md +FILE: ../../../third_party/skia/relnotes/font-default.md +FILE: ../../../third_party/skia/relnotes/glbackendsemaphore.md +FILE: ../../../third_party/skia/relnotes/grsurface-info.md +FILE: ../../../third_party/skia/relnotes/mesh.md +FILE: ../../../third_party/skia/relnotes/readbuffer-deserial.md +FILE: ../../../third_party/skia/relnotes/recorder-static-member.md +FILE: ../../../third_party/skia/relnotes/typeface.md +FILE: ../../../third_party/skia/relnotes/vk-directcontext.md +FILE: ../../../third_party/skia/relnotes/waitSemaphore.md FILE: ../../../third_party/skia/src/gpu/gpu_workaround_list.txt +FILE: ../../../third_party/skia/src/ports/fontations/Cargo.toml FILE: ../../../third_party/skia/src/sksl/generated/sksl_compute.minified.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_compute.unoptimized.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_frag.minified.sksl @@ -641,12 +651,14 @@ ORIGIN: ../../../third_party/skia/include/private/base/SkNoncopyable.h + ../../. ORIGIN: ../../../third_party/skia/include/private/base/SkPoint_impl.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/base/SkTDArray.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/base/SkTemplates.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/utils/SkBase64.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/utils/SkCamera.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/utils/SkParse.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/utils/SkParsePath.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/base/SkBase64.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/base/SkBase64.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkBuffer.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkBuffer.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/base/SkDebug.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkDeque.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkEndian.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkRandom.h + ../../../third_party/skia/LICENSE @@ -671,7 +683,6 @@ ORIGIN: ../../../third_party/skia/src/core/SkBlurMaskFilterImpl.cpp + ../../../t ORIGIN: ../../../third_party/skia/src/core/SkColor.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkColorFilter.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkCoreBlitters.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/core/SkDebug.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkDescriptor.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkDraw.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkDraw.h + ../../../third_party/skia/LICENSE @@ -737,7 +748,6 @@ ORIGIN: ../../../third_party/skia/src/ports/SkTypeface_mac_ct.cpp + ../../../thi ORIGIN: ../../../third_party/skia/src/shaders/SkBitmapProcShader.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/shaders/SkBlendShader.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/shaders/SkShader.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/utils/SkBase64.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/utils/SkCamera.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/utils/SkParse.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/utils/SkParseColor.cpp + ../../../third_party/skia/LICENSE @@ -787,12 +797,14 @@ FILE: ../../../third_party/skia/include/private/base/SkNoncopyable.h FILE: ../../../third_party/skia/include/private/base/SkPoint_impl.h FILE: ../../../third_party/skia/include/private/base/SkTDArray.h FILE: ../../../third_party/skia/include/private/base/SkTemplates.h -FILE: ../../../third_party/skia/include/utils/SkBase64.h FILE: ../../../third_party/skia/include/utils/SkCamera.h FILE: ../../../third_party/skia/include/utils/SkParse.h FILE: ../../../third_party/skia/include/utils/SkParsePath.h +FILE: ../../../third_party/skia/src/base/SkBase64.cpp +FILE: ../../../third_party/skia/src/base/SkBase64.h FILE: ../../../third_party/skia/src/base/SkBuffer.cpp FILE: ../../../third_party/skia/src/base/SkBuffer.h +FILE: ../../../third_party/skia/src/base/SkDebug.cpp FILE: ../../../third_party/skia/src/base/SkDeque.cpp FILE: ../../../third_party/skia/src/base/SkEndian.h FILE: ../../../third_party/skia/src/base/SkRandom.h @@ -817,7 +829,6 @@ FILE: ../../../third_party/skia/src/core/SkBlurMaskFilterImpl.cpp FILE: ../../../third_party/skia/src/core/SkColor.cpp FILE: ../../../third_party/skia/src/core/SkColorFilter.cpp FILE: ../../../third_party/skia/src/core/SkCoreBlitters.h -FILE: ../../../third_party/skia/src/core/SkDebug.cpp FILE: ../../../third_party/skia/src/core/SkDescriptor.h FILE: ../../../third_party/skia/src/core/SkDraw.cpp FILE: ../../../third_party/skia/src/core/SkDraw.h @@ -883,7 +894,6 @@ FILE: ../../../third_party/skia/src/ports/SkTypeface_mac_ct.cpp FILE: ../../../third_party/skia/src/shaders/SkBitmapProcShader.h FILE: ../../../third_party/skia/src/shaders/SkBlendShader.cpp FILE: ../../../third_party/skia/src/shaders/SkShader.cpp -FILE: ../../../third_party/skia/src/utils/SkBase64.cpp FILE: ../../../third_party/skia/src/utils/SkCamera.cpp FILE: ../../../third_party/skia/src/utils/SkParse.cpp FILE: ../../../third_party/skia/src/utils/SkParseColor.cpp @@ -3877,8 +3887,6 @@ ORIGIN: ../../../third_party/skia/src/core/SkRecordedDrawable.h + ../../../third ORIGIN: ../../../third_party/skia/src/core/SkScaleToSides.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkSpecialImage.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkSpecialImage.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/core/SkSpecialSurface.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/core/SkSpecialSurface.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkSwizzle.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkSwizzlePriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/imagefilters/SkShaderImageFilter.cpp + ../../../third_party/skia/LICENSE @@ -4184,8 +4192,6 @@ FILE: ../../../third_party/skia/src/core/SkRecordedDrawable.h FILE: ../../../third_party/skia/src/core/SkScaleToSides.h FILE: ../../../third_party/skia/src/core/SkSpecialImage.cpp FILE: ../../../third_party/skia/src/core/SkSpecialImage.h -FILE: ../../../third_party/skia/src/core/SkSpecialSurface.cpp -FILE: ../../../third_party/skia/src/core/SkSpecialSurface.h FILE: ../../../third_party/skia/src/core/SkSwizzle.cpp FILE: ../../../third_party/skia/src/core/SkSwizzlePriv.h FILE: ../../../third_party/skia/src/effects/imagefilters/SkShaderImageFilter.cpp @@ -4963,43 +4969,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: vulkanmemoryallocator -ORIGIN: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.cpp + ../../../third_party/skia/third_party/vulkanmemoryallocator/LICENSE -ORIGIN: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.h + ../../../third_party/skia/third_party/vulkanmemoryallocator/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.cpp -FILE: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.h ----------------------------------------------------------------------------------------------------- -Copyright 2018 Google Inc. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - ==================================================================================================== LIBRARY: skia ORIGIN: ../../../third_party/skia/fuzz/FuzzCommon.cpp + ../../../third_party/skia/LICENSE @@ -5050,7 +5019,6 @@ ORIGIN: ../../../third_party/skia/include/core/SkFontMetrics.h + ../../../third_ ORIGIN: ../../../third_party/skia/include/core/SkFontParameters.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkFontTypes.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkSpan.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/effects/SkOpPathEffect.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/effects/SkShaderMaskFilter.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/effects/SkTrimPathEffect.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/GrBackendDrawableInfo.h + ../../../third_party/skia/LICENSE @@ -5061,7 +5029,6 @@ ORIGIN: ../../../third_party/skia/include/private/base/SkMacros.h + ../../../thi ORIGIN: ../../../third_party/skia/include/private/base/SkSafe32.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/base/SkSpan_impl.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/base/SkTo.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrVkTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/gpu/vk/SkiaVulkan.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/utils/SkAnimCodecPlayer.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/utils/SkTextUtils.h + ../../../third_party/skia/LICENSE @@ -5123,8 +5090,6 @@ ORIGIN: ../../../third_party/skia/src/core/SkSafeRange.h + ../../../third_party/ ORIGIN: ../../../third_party/skia/src/core/SkStrikeCache.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkTextBlobPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkTypeface_remote.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/effects/SkOpPE.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/effects/SkOpPathEffect.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/SkShaderMaskFilterImpl.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/SkTrimPE.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/SkTrimPathEffect.cpp + ../../../third_party/skia/LICENSE @@ -5192,8 +5157,11 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkImageLayout.h + ../../.. ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkSamplerYcbcrConversion.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkSamplerYcbcrConversion.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkTypesPriv.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/vk/VulkanAMDMemoryAllocator.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/vk/VulkanAMDMemoryAllocator.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/vk/vulkanmemoryallocator/VulkanMemoryAllocatorWrapper.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/vk/vulkanmemoryallocator/VulkanMemoryAllocatorWrapper.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/image/SkImage_Lazy.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/opts/SkBitmapProcState_opts.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/opts/SkOpts_hsw.cpp + ../../../third_party/skia/LICENSE @@ -5265,7 +5233,6 @@ FILE: ../../../third_party/skia/include/core/SkFontMetrics.h FILE: ../../../third_party/skia/include/core/SkFontParameters.h FILE: ../../../third_party/skia/include/core/SkFontTypes.h FILE: ../../../third_party/skia/include/core/SkSpan.h -FILE: ../../../third_party/skia/include/effects/SkOpPathEffect.h FILE: ../../../third_party/skia/include/effects/SkShaderMaskFilter.h FILE: ../../../third_party/skia/include/effects/SkTrimPathEffect.h FILE: ../../../third_party/skia/include/gpu/GrBackendDrawableInfo.h @@ -5276,7 +5243,6 @@ FILE: ../../../third_party/skia/include/private/base/SkMacros.h FILE: ../../../third_party/skia/include/private/base/SkSafe32.h FILE: ../../../third_party/skia/include/private/base/SkSpan_impl.h FILE: ../../../third_party/skia/include/private/base/SkTo.h -FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrVkTypesPriv.h FILE: ../../../third_party/skia/include/private/gpu/vk/SkiaVulkan.h FILE: ../../../third_party/skia/include/utils/SkAnimCodecPlayer.h FILE: ../../../third_party/skia/include/utils/SkTextUtils.h @@ -5338,8 +5304,6 @@ FILE: ../../../third_party/skia/src/core/SkSafeRange.h FILE: ../../../third_party/skia/src/core/SkStrikeCache.cpp FILE: ../../../third_party/skia/src/core/SkTextBlobPriv.h FILE: ../../../third_party/skia/src/core/SkTypeface_remote.h -FILE: ../../../third_party/skia/src/effects/SkOpPE.h -FILE: ../../../third_party/skia/src/effects/SkOpPathEffect.cpp FILE: ../../../third_party/skia/src/effects/SkShaderMaskFilterImpl.cpp FILE: ../../../third_party/skia/src/effects/SkTrimPE.h FILE: ../../../third_party/skia/src/effects/SkTrimPathEffect.cpp @@ -5407,8 +5371,11 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkImageLayout.h FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkSamplerYcbcrConversion.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkSamplerYcbcrConversion.h FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkTypesPriv.cpp +FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkTypesPriv.h FILE: ../../../third_party/skia/src/gpu/vk/VulkanAMDMemoryAllocator.cpp FILE: ../../../third_party/skia/src/gpu/vk/VulkanAMDMemoryAllocator.h +FILE: ../../../third_party/skia/src/gpu/vk/vulkanmemoryallocator/VulkanMemoryAllocatorWrapper.cpp +FILE: ../../../third_party/skia/src/gpu/vk/vulkanmemoryallocator/VulkanMemoryAllocatorWrapper.h FILE: ../../../third_party/skia/src/image/SkImage_Lazy.h FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts.h FILE: ../../../third_party/skia/src/opts/SkOpts_hsw.cpp @@ -5748,6 +5715,7 @@ ORIGIN: ../../../third_party/skia/include/ports/SkCFObject.h + ../../../third_pa ORIGIN: ../../../third_party/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrContext_Base.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrImageContext.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/skottie/include/TextShaper.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/Composition.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/Composition.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/Layer.cpp + ../../../third_party/skia/LICENSE @@ -5776,12 +5744,12 @@ ORIGIN: ../../../third_party/skia/modules/skottie/src/layers/NullLayer.cpp + ../ ORIGIN: ../../../third_party/skia/modules/skottie/src/layers/SolidLayer.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextShaper.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextValue.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/text/TextValue.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/sksg/include/SkSGRenderEffect.h + ../../../third_party/skia/LICENSE @@ -5866,6 +5834,7 @@ FILE: ../../../third_party/skia/include/ports/SkCFObject.h FILE: ../../../third_party/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrContext_Base.h FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrImageContext.h +FILE: ../../../third_party/skia/modules/skottie/include/TextShaper.h FILE: ../../../third_party/skia/modules/skottie/src/Composition.cpp FILE: ../../../third_party/skia/modules/skottie/src/Composition.h FILE: ../../../third_party/skia/modules/skottie/src/Layer.cpp @@ -5894,12 +5863,12 @@ FILE: ../../../third_party/skia/modules/skottie/src/layers/NullLayer.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/SolidLayer.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.h -FILE: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.h FILE: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.h FILE: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.h +FILE: ../../../third_party/skia/modules/skottie/src/text/TextShaper.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/TextValue.cpp FILE: ../../../third_party/skia/modules/skottie/src/text/TextValue.h FILE: ../../../third_party/skia/modules/sksg/include/SkSGRenderEffect.h @@ -6002,7 +5971,6 @@ ORIGIN: ../../../third_party/skia/include/effects/SkImageFilters.h + ../../../th ORIGIN: ../../../third_party/skia/include/effects/SkRuntimeEffect.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/base/SkThreadAnnotations.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrGLTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/canvaskit/WasmCommon.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/canvaskit/debugger_bindings.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp + ../../../third_party/skia/LICENSE @@ -6043,6 +6011,7 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLAssembleGLInterfaceAutog ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLAssembleHelpers.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLAssembleWebGLInterfaceAutogen.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLTypesPriv.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/image/SkSpecialImage_Ganesh.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mock/GrMockCaps.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mock/GrMockTypes.cpp + ../../../third_party/skia/LICENSE @@ -6062,7 +6031,6 @@ FILE: ../../../third_party/skia/include/effects/SkImageFilters.h FILE: ../../../third_party/skia/include/effects/SkRuntimeEffect.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h FILE: ../../../third_party/skia/include/private/base/SkThreadAnnotations.h -FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrGLTypesPriv.h FILE: ../../../third_party/skia/modules/canvaskit/WasmCommon.h FILE: ../../../third_party/skia/modules/canvaskit/debugger_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp @@ -6103,6 +6071,7 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLAssembleGLInterfaceAutogen FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLAssembleHelpers.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLAssembleWebGLInterfaceAutogen.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLTypesPriv.cpp +FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLTypesPriv.h FILE: ../../../third_party/skia/src/gpu/ganesh/image/SkSpecialImage_Ganesh.h FILE: ../../../third_party/skia/src/gpu/ganesh/mock/GrMockCaps.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/mock/GrMockTypes.cpp @@ -6428,12 +6397,9 @@ ORIGIN: ../../../third_party/skia/gm/exoticformats.cpp + ../../../third_party/sk ORIGIN: ../../../third_party/skia/gm/rsxtext.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/skbug_9819.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/strokerect_anisotropic.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/verifiers/gmverifier.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/verifiers/gmverifier.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/widebuttcaps.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkM44.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkSamplingOptions.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/effects/SkStrokeAndFillPathEffect.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/GrDirectContext.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/mtl/GrMtlBackendContext.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/base/SkTPin.h + ../../../third_party/skia/LICENSE @@ -6562,12 +6528,9 @@ FILE: ../../../third_party/skia/gm/exoticformats.cpp FILE: ../../../third_party/skia/gm/rsxtext.cpp FILE: ../../../third_party/skia/gm/skbug_9819.cpp FILE: ../../../third_party/skia/gm/strokerect_anisotropic.cpp -FILE: ../../../third_party/skia/gm/verifiers/gmverifier.cpp -FILE: ../../../third_party/skia/gm/verifiers/gmverifier.h FILE: ../../../third_party/skia/gm/widebuttcaps.cpp FILE: ../../../third_party/skia/include/core/SkM44.h FILE: ../../../third_party/skia/include/core/SkSamplingOptions.h -FILE: ../../../third_party/skia/include/effects/SkStrokeAndFillPathEffect.h FILE: ../../../third_party/skia/include/gpu/GrDirectContext.h FILE: ../../../third_party/skia/include/gpu/mtl/GrMtlBackendContext.h FILE: ../../../third_party/skia/include/private/base/SkTPin.h @@ -7145,7 +7108,6 @@ ORIGIN: ../../../third_party/skia/gm/drawglyphs.cpp + ../../../third_party/skia/ ORIGIN: ../../../third_party/skia/gm/largeclippedpath.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/skbug_12212.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/slug.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrMtlTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/gpu/graphite/MtlGraphiteTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/utils/SkOrderedFontMgr.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/jetski/src/Canvas.cpp + ../../../third_party/skia/LICENSE @@ -7199,6 +7161,7 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlFramebuffer.h + ../../ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlFramebuffer.mm + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlPipeline.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlRenderCommandEncoder.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/ops/AtlasRenderTask.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/ops/AtlasRenderTask.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/Buffer.cpp + ../../../third_party/skia/LICENSE @@ -7229,7 +7192,6 @@ FILE: ../../../third_party/skia/gm/drawglyphs.cpp FILE: ../../../third_party/skia/gm/largeclippedpath.cpp FILE: ../../../third_party/skia/gm/skbug_12212.cpp FILE: ../../../third_party/skia/gm/slug.cpp -FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrMtlTypesPriv.h FILE: ../../../third_party/skia/include/private/gpu/graphite/MtlGraphiteTypesPriv.h FILE: ../../../third_party/skia/include/utils/SkOrderedFontMgr.h FILE: ../../../third_party/skia/modules/jetski/src/Canvas.cpp @@ -7283,6 +7245,7 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlFramebuffer.h FILE: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlFramebuffer.mm FILE: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlPipeline.h FILE: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlRenderCommandEncoder.h +FILE: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlTypesPriv.h FILE: ../../../third_party/skia/src/gpu/ganesh/ops/AtlasRenderTask.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/ops/AtlasRenderTask.h FILE: ../../../third_party/skia/src/gpu/graphite/Buffer.cpp @@ -7354,7 +7317,6 @@ ORIGIN: ../../../third_party/skia/gm/mesh.cpp + ../../../third_party/skia/LICENS ORIGIN: ../../../third_party/skia/include/core/SkBlender.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkMesh.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/effects/SkBlenders.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/gpu/GrSurfaceInfo.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ShaderErrorHandler.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/gl/egl/GrGLMakeEGLInterface.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/gl/glx/GrGLMakeGLXInterface.h + ../../../third_party/skia/LICENSE @@ -7367,7 +7329,6 @@ ORIGIN: ../../../third_party/skia/include/gpu/graphite/TextureInfo.h + ../../../ ORIGIN: ../../../third_party/skia/include/gpu/graphite/mtl/MtlBackendContext.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/chromium/Slug.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrMockTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/canvaskit/paragraph_bindings_gen.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkEnumBitMask.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBlendModeBlender.cpp + ../../../third_party/skia/LICENSE @@ -7380,7 +7341,6 @@ ORIGIN: ../../../third_party/skia/src/core/SkMeshPriv.h + ../../../third_party/s ORIGIN: ../../../third_party/skia/src/core/SkYUVAInfoLocation.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/SkBlenders.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/imagefilters/SkCropImageFilter.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/effects/imagefilters/SkCropImageFilter.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/KeyBuilder.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ResourceKey.cpp + ../../../third_party/skia/LICENSE @@ -7392,7 +7352,6 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrMeshDrawTarget.cpp + ../../.. ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrMeshDrawTarget.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrOpsTypes.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrPersistentCacheUtils.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrSurfaceInfo.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrWritePixelsRenderTask.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrWritePixelsRenderTask.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrYUVATextureProxies.cpp + ../../../third_party/skia/LICENSE @@ -7403,6 +7362,7 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/d3d/GrD3DTypesPriv.cpp + ../../ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/geometry/GrInnerFanTriangulator.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/egl/GrGLMakeNativeInterface_egl.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/glx/GrGLMakeNativeInterface_glx.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mock/GrMockTypesPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlTypesPriv.mm + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/ops/DrawMeshOp.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/ops/DrawMeshOp.h + ../../../third_party/skia/LICENSE @@ -7572,7 +7532,6 @@ FILE: ../../../third_party/skia/gm/mesh.cpp FILE: ../../../third_party/skia/include/core/SkBlender.h FILE: ../../../third_party/skia/include/core/SkMesh.h FILE: ../../../third_party/skia/include/effects/SkBlenders.h -FILE: ../../../third_party/skia/include/gpu/GrSurfaceInfo.h FILE: ../../../third_party/skia/include/gpu/ShaderErrorHandler.h FILE: ../../../third_party/skia/include/gpu/gl/egl/GrGLMakeEGLInterface.h FILE: ../../../third_party/skia/include/gpu/gl/glx/GrGLMakeGLXInterface.h @@ -7585,7 +7544,6 @@ FILE: ../../../third_party/skia/include/gpu/graphite/TextureInfo.h FILE: ../../../third_party/skia/include/gpu/graphite/mtl/MtlBackendContext.h FILE: ../../../third_party/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h FILE: ../../../third_party/skia/include/private/chromium/Slug.h -FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrMockTypesPriv.h FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings_gen.cpp FILE: ../../../third_party/skia/src/base/SkEnumBitMask.h FILE: ../../../third_party/skia/src/core/SkBlendModeBlender.cpp @@ -7598,7 +7556,6 @@ FILE: ../../../third_party/skia/src/core/SkMeshPriv.h FILE: ../../../third_party/skia/src/core/SkYUVAInfoLocation.h FILE: ../../../third_party/skia/src/effects/SkBlenders.cpp FILE: ../../../third_party/skia/src/effects/imagefilters/SkCropImageFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkCropImageFilter.h FILE: ../../../third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.cpp FILE: ../../../third_party/skia/src/gpu/KeyBuilder.h FILE: ../../../third_party/skia/src/gpu/ResourceKey.cpp @@ -7610,7 +7567,6 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/GrMeshDrawTarget.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/GrMeshDrawTarget.h FILE: ../../../third_party/skia/src/gpu/ganesh/GrOpsTypes.h FILE: ../../../third_party/skia/src/gpu/ganesh/GrPersistentCacheUtils.cpp -FILE: ../../../third_party/skia/src/gpu/ganesh/GrSurfaceInfo.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/GrWritePixelsRenderTask.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/GrWritePixelsRenderTask.h FILE: ../../../third_party/skia/src/gpu/ganesh/GrYUVATextureProxies.cpp @@ -7621,6 +7577,7 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/d3d/GrD3DTypesPriv.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/geometry/GrInnerFanTriangulator.h FILE: ../../../third_party/skia/src/gpu/ganesh/gl/egl/GrGLMakeNativeInterface_egl.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/gl/glx/GrGLMakeNativeInterface_glx.cpp +FILE: ../../../third_party/skia/src/gpu/ganesh/mock/GrMockTypesPriv.h FILE: ../../../third_party/skia/src/gpu/ganesh/mtl/GrMtlTypesPriv.mm FILE: ../../../third_party/skia/src/gpu/ganesh/ops/DrawMeshOp.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/ops/DrawMeshOp.h @@ -8655,19 +8612,20 @@ LIBRARY: skia ORIGIN: ../../../third_party/skia/gm/coordclampshader.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/fontations.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/imagefiltersunpremul.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/surface_manager/SurfaceManager.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/vias/Draw.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/ports/SkFontMgr_data.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/ports/SkTypeface_fontations.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/include/private/SkExif.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/SkGainmapInfo.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/SkGainmapShader.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/SkJpegGainmapEncoder.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/SkXmp.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/include/SlotManager.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skottie/src/SlotManager.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/skshaper/src/SkShaper_skunicode.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skunicode/src/SkUnicode_hardcoded.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skunicode/src/SkUnicode_hardcoded.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/skunicode/src/SkUnicode_icu_bidi.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/codec/SkExif.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/codec/SkJpegConstants.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/codec/SkJpegMultiPicture.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/codec/SkJpegMultiPicture.h + ../../../third_party/skia/LICENSE @@ -8677,6 +8635,8 @@ ORIGIN: ../../../third_party/skia/src/codec/SkJpegSourceMgr.cpp + ../../../third ORIGIN: ../../../third_party/skia/src/codec/SkJpegSourceMgr.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/codec/SkJpegXmp.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/codec/SkJpegXmp.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/codec/SkTiffUtility.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/codec/SkTiffUtility.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/codec/SkXmp.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkRasterPipelineContextUtils.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkRasterPipelineOpContexts.h + ../../../third_party/skia/LICENSE @@ -8696,19 +8656,20 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/gm/coordclampshader.cpp FILE: ../../../third_party/skia/gm/fontations.cpp FILE: ../../../third_party/skia/gm/imagefiltersunpremul.cpp -FILE: ../../../third_party/skia/gm/surface_manager/SurfaceManager.h -FILE: ../../../third_party/skia/gm/vias/Draw.h FILE: ../../../third_party/skia/include/ports/SkFontMgr_data.h FILE: ../../../third_party/skia/include/ports/SkTypeface_fontations.h +FILE: ../../../third_party/skia/include/private/SkExif.h FILE: ../../../third_party/skia/include/private/SkGainmapInfo.h FILE: ../../../third_party/skia/include/private/SkGainmapShader.h FILE: ../../../third_party/skia/include/private/SkJpegGainmapEncoder.h FILE: ../../../third_party/skia/include/private/SkXmp.h FILE: ../../../third_party/skia/modules/skottie/include/SlotManager.h FILE: ../../../third_party/skia/modules/skottie/src/SlotManager.cpp +FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_skunicode.cpp FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_hardcoded.cpp FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_hardcoded.h FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_icu_bidi.h +FILE: ../../../third_party/skia/src/codec/SkExif.cpp FILE: ../../../third_party/skia/src/codec/SkJpegConstants.h FILE: ../../../third_party/skia/src/codec/SkJpegMultiPicture.cpp FILE: ../../../third_party/skia/src/codec/SkJpegMultiPicture.h @@ -8718,6 +8679,8 @@ FILE: ../../../third_party/skia/src/codec/SkJpegSourceMgr.cpp FILE: ../../../third_party/skia/src/codec/SkJpegSourceMgr.h FILE: ../../../third_party/skia/src/codec/SkJpegXmp.cpp FILE: ../../../third_party/skia/src/codec/SkJpegXmp.h +FILE: ../../../third_party/skia/src/codec/SkTiffUtility.cpp +FILE: ../../../third_party/skia/src/codec/SkTiffUtility.h FILE: ../../../third_party/skia/src/codec/SkXmp.cpp FILE: ../../../third_party/skia/src/core/SkRasterPipelineContextUtils.h FILE: ../../../third_party/skia/src/core/SkRasterPipelineOpContexts.h @@ -8770,21 +8733,17 @@ LIBRARY: skia ORIGIN: ../../../third_party/skia/fuzz/FuzzCubicRoots.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/fuzz/FuzzPrecompile.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/fuzz/FuzzQuadRoots.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzColorspace.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzCubicRoots.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzPrecompile.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzQuadRoots.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/BazelGMRunner.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/BazelNoopRunner.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/gm/fontations_ft_compare.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/graphite_replay.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/hello_bazel_world.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/png_codec.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/rippleshadergm.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/gm/scaledrects.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/surface_manager/GaneshGLSurfaceManager.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/surface_manager/GaneshVulkanSurfaceManager.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/surface_manager/RasterSurfaceManager.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/surface_manager/SurfaceManager.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/gm/vias/SimpleVias.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/gm/workingspace.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/android/SkCanvasAndroid.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/android/SkHeifDecoder.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/android/SkImageAndroid.h + ../../../third_party/skia/LICENSE @@ -8801,18 +8760,19 @@ ORIGIN: ../../../third_party/skia/include/codec/SkRawDecoder.h + ../../../third_ ORIGIN: ../../../third_party/skia/include/codec/SkWbmpDecoder.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/codec/SkWebpDecoder.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkColorTable.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/core/SkEncodedImageFormat.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/include/core/SkICC.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkPoint.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkTextureCompressionType.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/core/SkTiledImageUtils.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/include/docs/SkMultiPictureDocument.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/GrExternalTextureGenerator.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/SkImageGanesh.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/SkMeshGanesh.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/mtl/SkSurfaceMetal.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/graphite/BackendSemaphore.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/graphite/Image.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/gpu/graphite/Surface.h + ../../../third_party/skia/LICENSE @@ -8825,12 +8785,16 @@ ORIGIN: ../../../third_party/skia/include/private/chromium/GrPromiseImageTexture ORIGIN: ../../../third_party/skia/include/private/chromium/GrSurfaceCharacterization.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/chromium/SkImageChromium.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/include/private/gpu/ganesh/GrTextureGenerator.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/include/private/gpu/graphite/ContextOptionsPriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/BentleyOttmann1.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/BruteForceCrossings.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/EventQueue.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/EventQueueInterface.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/Int96.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/Point.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/include/Segment.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/src/BentleyOttmann1.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/bentleyottmann/src/BruteForceCrossings.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/src/EventQueue.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/src/Int96.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/modules/bentleyottmann/src/Point.cpp + ../../../third_party/skia/LICENSE @@ -8845,6 +8809,7 @@ ORIGIN: ../../../third_party/skia/src/base/SkRectMemcpy.h + ../../../third_party ORIGIN: ../../../third_party/skia/src/base/SkSafeMath.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkTime.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/base/SkTime.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/codec/SkImageGenerator_FromEncoded.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBitmapProcState_opts.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBitmapProcState_opts_hsw.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBitmapProcState_opts_ssse3.cpp + ../../../third_party/skia/LICENSE @@ -8854,6 +8819,7 @@ ORIGIN: ../../../third_party/skia/src/core/SkBlitMask_opts_ssse3.cpp + ../../../ ORIGIN: ../../../third_party/skia/src/core/SkBlitRow_opts.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBlitRow_opts_hsw.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBlitter_A8.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/core/SkBlurEngine.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkBlurMaskFilterImpl.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkCanvas_Raster.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/core/SkChecksum.cpp + ../../../third_party/skia/LICENSE @@ -8905,6 +8871,7 @@ ORIGIN: ../../../third_party/skia/src/gpu/PipelineUtils.h + ../../../third_party ORIGIN: ../../../third_party/skia/src/gpu/TiledTextureUtils.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/TiledTextureUtils.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/dawn/DawnUtilsPriv.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrBackendSemaphorePriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrBackendSurfacePriv.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrCanvas.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/GrCanvas.h + ../../../third_party/skia/LICENSE @@ -8923,6 +8890,7 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/effects/GrPerlinNoise2Effect.h ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/AHardwareBufferGL.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLBackendSurface.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLBackendSurfacePriv.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLDirectContext.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/image/GrImageUtils.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/image/GrImageUtils.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/image/GrTextureGenerator.cpp + ../../../third_party/skia/LICENSE @@ -8936,6 +8904,7 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/surface/SkSurface_AndroidFactor ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/AHardwareBufferVk.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkBackendSurface.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkBackendSurfacePriv.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkDirectContext.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/AtlasProvider.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/AtlasProvider.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/BackendSemaphore.cpp + ../../../third_party/skia/LICENSE @@ -8949,6 +8918,8 @@ ORIGIN: ../../../third_party/skia/src/gpu/graphite/PathAtlas.cpp + ../../../thir ORIGIN: ../../../third_party/skia/src/gpu/graphite/PathAtlas.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/ProxyCache.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/ProxyCache.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/graphite/RasterPathAtlas.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/graphite/RasterPathAtlas.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/ReadSwizzle.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/YUVABackendTextures.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/YUVATextureProxies.cpp + ../../../third_party/skia/LICENSE @@ -8967,9 +8938,11 @@ ORIGIN: ../../../third_party/skia/src/gpu/graphite/dawn/DawnErrorChecker.cpp + . ORIGIN: ../../../third_party/skia/src/gpu/graphite/dawn/DawnErrorChecker.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/geom/CoverageMaskShape.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/geom/EdgeAAQuad.h + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/AtlasShapeRenderStep.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/AtlasShapeRenderStep.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/CoverageMaskRenderStep.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/CoverageMaskRenderStep.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/GraphiteVertexFiller.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/PerEdgeAAQuadRenderStep.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/graphite/render/PerEdgeAAQuadRenderStep.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/vk/VulkanDescriptorPool.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/vk/VulkanDescriptorPool.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/gpu/graphite/vk/VulkanDescriptorSet.cpp + ../../../third_party/skia/LICENSE @@ -9007,6 +8980,8 @@ ORIGIN: ../../../third_party/skia/src/shaders/SkRuntimeShader.h + ../../../third ORIGIN: ../../../third_party/skia/src/shaders/SkShaderBase.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/shaders/SkTriColorShader.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/shaders/SkTriColorShader.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/shaders/SkWorkingColorSpaceShader.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/shaders/SkWorkingColorSpaceShader.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/shaders/gradients/SkRadialGradient.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/shaders/gradients/SkSweepGradient.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/sksl/analysis/SkSLGetLoopControlFlowInfo.cpp + ../../../third_party/skia/LICENSE @@ -9018,9 +8993,7 @@ ORIGIN: ../../../third_party/skia/src/sksl/tracing/SkSLTraceHook.cpp + ../../../ ORIGIN: ../../../third_party/skia/src/sksl/tracing/SkSLTraceHook.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/sksl/transform/SkSLHoistSwitchVarDeclarationsAtTopLevel.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/sksl/transform/SkSLRewriteIndexedSwizzle.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/text/EmptyMailboxImpl.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/text/EmptySlugImpl.cpp + ../../../third_party/skia/LICENSE -ORIGIN: ../../../third_party/skia/src/text/TextBlobMailbox.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/text/SlugFromBuffer.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/text/gpu/SlugImpl.cpp + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/text/gpu/SlugImpl.h + ../../../third_party/skia/LICENSE ORIGIN: ../../../third_party/skia/src/text/gpu/VertexFiller.cpp + ../../../third_party/skia/LICENSE @@ -9031,21 +9004,17 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/fuzz/FuzzCubicRoots.cpp FILE: ../../../third_party/skia/fuzz/FuzzPrecompile.cpp FILE: ../../../third_party/skia/fuzz/FuzzQuadRoots.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzColorspace.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzCubicRoots.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzPrecompile.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzQuadRoots.cpp -FILE: ../../../third_party/skia/gm/BazelGMRunner.cpp -FILE: ../../../third_party/skia/gm/BazelNoopRunner.cpp +FILE: ../../../third_party/skia/gm/fontations_ft_compare.cpp FILE: ../../../third_party/skia/gm/graphite_replay.cpp FILE: ../../../third_party/skia/gm/hello_bazel_world.cpp FILE: ../../../third_party/skia/gm/png_codec.cpp FILE: ../../../third_party/skia/gm/rippleshadergm.cpp FILE: ../../../third_party/skia/gm/scaledrects.cpp -FILE: ../../../third_party/skia/gm/surface_manager/GaneshGLSurfaceManager.cpp -FILE: ../../../third_party/skia/gm/surface_manager/GaneshVulkanSurfaceManager.cpp -FILE: ../../../third_party/skia/gm/surface_manager/RasterSurfaceManager.cpp -FILE: ../../../third_party/skia/gm/surface_manager/SurfaceManager.cpp -FILE: ../../../third_party/skia/gm/vias/SimpleVias.cpp +FILE: ../../../third_party/skia/gm/workingspace.cpp FILE: ../../../third_party/skia/include/android/SkCanvasAndroid.h FILE: ../../../third_party/skia/include/android/SkHeifDecoder.h FILE: ../../../third_party/skia/include/android/SkImageAndroid.h @@ -9062,18 +9031,19 @@ FILE: ../../../third_party/skia/include/codec/SkRawDecoder.h FILE: ../../../third_party/skia/include/codec/SkWbmpDecoder.h FILE: ../../../third_party/skia/include/codec/SkWebpDecoder.h FILE: ../../../third_party/skia/include/core/SkColorTable.h -FILE: ../../../third_party/skia/include/core/SkEncodedImageFormat.h -FILE: ../../../third_party/skia/include/core/SkICC.h FILE: ../../../third_party/skia/include/core/SkPoint.h FILE: ../../../third_party/skia/include/core/SkTextureCompressionType.h FILE: ../../../third_party/skia/include/core/SkTiledImageUtils.h +FILE: ../../../third_party/skia/include/docs/SkMultiPictureDocument.h FILE: ../../../third_party/skia/include/gpu/ganesh/GrExternalTextureGenerator.h FILE: ../../../third_party/skia/include/gpu/ganesh/SkImageGanesh.h FILE: ../../../third_party/skia/include/gpu/ganesh/SkMeshGanesh.h FILE: ../../../third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h FILE: ../../../third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h +FILE: ../../../third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h FILE: ../../../third_party/skia/include/gpu/ganesh/mtl/SkSurfaceMetal.h FILE: ../../../third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h +FILE: ../../../third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h FILE: ../../../third_party/skia/include/gpu/graphite/BackendSemaphore.h FILE: ../../../third_party/skia/include/gpu/graphite/Image.h FILE: ../../../third_party/skia/include/gpu/graphite/Surface.h @@ -9086,12 +9056,16 @@ FILE: ../../../third_party/skia/include/private/chromium/GrPromiseImageTexture.h FILE: ../../../third_party/skia/include/private/chromium/GrSurfaceCharacterization.h FILE: ../../../third_party/skia/include/private/chromium/SkImageChromium.h FILE: ../../../third_party/skia/include/private/gpu/ganesh/GrTextureGenerator.h +FILE: ../../../third_party/skia/include/private/gpu/graphite/ContextOptionsPriv.h FILE: ../../../third_party/skia/modules/bentleyottmann/include/BentleyOttmann1.h +FILE: ../../../third_party/skia/modules/bentleyottmann/include/BruteForceCrossings.h FILE: ../../../third_party/skia/modules/bentleyottmann/include/EventQueue.h +FILE: ../../../third_party/skia/modules/bentleyottmann/include/EventQueueInterface.h FILE: ../../../third_party/skia/modules/bentleyottmann/include/Int96.h FILE: ../../../third_party/skia/modules/bentleyottmann/include/Point.h FILE: ../../../third_party/skia/modules/bentleyottmann/include/Segment.h FILE: ../../../third_party/skia/modules/bentleyottmann/src/BentleyOttmann1.cpp +FILE: ../../../third_party/skia/modules/bentleyottmann/src/BruteForceCrossings.cpp FILE: ../../../third_party/skia/modules/bentleyottmann/src/EventQueue.cpp FILE: ../../../third_party/skia/modules/bentleyottmann/src/Int96.cpp FILE: ../../../third_party/skia/modules/bentleyottmann/src/Point.cpp @@ -9106,6 +9080,7 @@ FILE: ../../../third_party/skia/src/base/SkRectMemcpy.h FILE: ../../../third_party/skia/src/base/SkSafeMath.cpp FILE: ../../../third_party/skia/src/base/SkTime.cpp FILE: ../../../third_party/skia/src/base/SkTime.h +FILE: ../../../third_party/skia/src/codec/SkImageGenerator_FromEncoded.cpp FILE: ../../../third_party/skia/src/core/SkBitmapProcState_opts.cpp FILE: ../../../third_party/skia/src/core/SkBitmapProcState_opts_hsw.cpp FILE: ../../../third_party/skia/src/core/SkBitmapProcState_opts_ssse3.cpp @@ -9115,6 +9090,7 @@ FILE: ../../../third_party/skia/src/core/SkBlitMask_opts_ssse3.cpp FILE: ../../../third_party/skia/src/core/SkBlitRow_opts.cpp FILE: ../../../third_party/skia/src/core/SkBlitRow_opts_hsw.cpp FILE: ../../../third_party/skia/src/core/SkBlitter_A8.h +FILE: ../../../third_party/skia/src/core/SkBlurEngine.h FILE: ../../../third_party/skia/src/core/SkBlurMaskFilterImpl.h FILE: ../../../third_party/skia/src/core/SkCanvas_Raster.cpp FILE: ../../../third_party/skia/src/core/SkChecksum.cpp @@ -9166,6 +9142,7 @@ FILE: ../../../third_party/skia/src/gpu/PipelineUtils.h FILE: ../../../third_party/skia/src/gpu/TiledTextureUtils.cpp FILE: ../../../third_party/skia/src/gpu/TiledTextureUtils.h FILE: ../../../third_party/skia/src/gpu/dawn/DawnUtilsPriv.h +FILE: ../../../third_party/skia/src/gpu/ganesh/GrBackendSemaphorePriv.h FILE: ../../../third_party/skia/src/gpu/ganesh/GrBackendSurfacePriv.h FILE: ../../../third_party/skia/src/gpu/ganesh/GrCanvas.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/GrCanvas.h @@ -9184,6 +9161,7 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/effects/GrPerlinNoise2Effect.h FILE: ../../../third_party/skia/src/gpu/ganesh/gl/AHardwareBufferGL.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLBackendSurface.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLBackendSurfacePriv.h +FILE: ../../../third_party/skia/src/gpu/ganesh/gl/GrGLDirectContext.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/image/GrImageUtils.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/image/GrImageUtils.h FILE: ../../../third_party/skia/src/gpu/ganesh/image/GrTextureGenerator.cpp @@ -9197,6 +9175,7 @@ FILE: ../../../third_party/skia/src/gpu/ganesh/surface/SkSurface_AndroidFactorie FILE: ../../../third_party/skia/src/gpu/ganesh/vk/AHardwareBufferVk.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkBackendSurface.cpp FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkBackendSurfacePriv.h +FILE: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkDirectContext.cpp FILE: ../../../third_party/skia/src/gpu/graphite/AtlasProvider.cpp FILE: ../../../third_party/skia/src/gpu/graphite/AtlasProvider.h FILE: ../../../third_party/skia/src/gpu/graphite/BackendSemaphore.cpp @@ -9210,6 +9189,8 @@ FILE: ../../../third_party/skia/src/gpu/graphite/PathAtlas.cpp FILE: ../../../third_party/skia/src/gpu/graphite/PathAtlas.h FILE: ../../../third_party/skia/src/gpu/graphite/ProxyCache.cpp FILE: ../../../third_party/skia/src/gpu/graphite/ProxyCache.h +FILE: ../../../third_party/skia/src/gpu/graphite/RasterPathAtlas.cpp +FILE: ../../../third_party/skia/src/gpu/graphite/RasterPathAtlas.h FILE: ../../../third_party/skia/src/gpu/graphite/ReadSwizzle.h FILE: ../../../third_party/skia/src/gpu/graphite/YUVABackendTextures.cpp FILE: ../../../third_party/skia/src/gpu/graphite/YUVATextureProxies.cpp @@ -9228,9 +9209,11 @@ FILE: ../../../third_party/skia/src/gpu/graphite/dawn/DawnErrorChecker.cpp FILE: ../../../third_party/skia/src/gpu/graphite/dawn/DawnErrorChecker.h FILE: ../../../third_party/skia/src/gpu/graphite/geom/CoverageMaskShape.h FILE: ../../../third_party/skia/src/gpu/graphite/geom/EdgeAAQuad.h -FILE: ../../../third_party/skia/src/gpu/graphite/render/AtlasShapeRenderStep.cpp -FILE: ../../../third_party/skia/src/gpu/graphite/render/AtlasShapeRenderStep.h +FILE: ../../../third_party/skia/src/gpu/graphite/render/CoverageMaskRenderStep.cpp +FILE: ../../../third_party/skia/src/gpu/graphite/render/CoverageMaskRenderStep.h FILE: ../../../third_party/skia/src/gpu/graphite/render/GraphiteVertexFiller.cpp +FILE: ../../../third_party/skia/src/gpu/graphite/render/PerEdgeAAQuadRenderStep.cpp +FILE: ../../../third_party/skia/src/gpu/graphite/render/PerEdgeAAQuadRenderStep.h FILE: ../../../third_party/skia/src/gpu/graphite/vk/VulkanDescriptorPool.cpp FILE: ../../../third_party/skia/src/gpu/graphite/vk/VulkanDescriptorPool.h FILE: ../../../third_party/skia/src/gpu/graphite/vk/VulkanDescriptorSet.cpp @@ -9268,6 +9251,8 @@ FILE: ../../../third_party/skia/src/shaders/SkRuntimeShader.h FILE: ../../../third_party/skia/src/shaders/SkShaderBase.cpp FILE: ../../../third_party/skia/src/shaders/SkTriColorShader.cpp FILE: ../../../third_party/skia/src/shaders/SkTriColorShader.h +FILE: ../../../third_party/skia/src/shaders/SkWorkingColorSpaceShader.cpp +FILE: ../../../third_party/skia/src/shaders/SkWorkingColorSpaceShader.h FILE: ../../../third_party/skia/src/shaders/gradients/SkRadialGradient.h FILE: ../../../third_party/skia/src/shaders/gradients/SkSweepGradient.h FILE: ../../../third_party/skia/src/sksl/analysis/SkSLGetLoopControlFlowInfo.cpp @@ -9279,9 +9264,7 @@ FILE: ../../../third_party/skia/src/sksl/tracing/SkSLTraceHook.cpp FILE: ../../../third_party/skia/src/sksl/tracing/SkSLTraceHook.h FILE: ../../../third_party/skia/src/sksl/transform/SkSLHoistSwitchVarDeclarationsAtTopLevel.cpp FILE: ../../../third_party/skia/src/sksl/transform/SkSLRewriteIndexedSwizzle.cpp -FILE: ../../../third_party/skia/src/text/EmptyMailboxImpl.cpp -FILE: ../../../third_party/skia/src/text/EmptySlugImpl.cpp -FILE: ../../../third_party/skia/src/text/TextBlobMailbox.h +FILE: ../../../third_party/skia/src/text/SlugFromBuffer.cpp FILE: ../../../third_party/skia/src/text/gpu/SlugImpl.cpp FILE: ../../../third_party/skia/src/text/gpu/SlugImpl.h FILE: ../../../third_party/skia/src/text/gpu/VertexFiller.cpp @@ -9357,6 +9340,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: skia +ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2WGSL.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkRuntimeBlender.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkRuntimeColorFilter.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2WGSL.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkRuntimeBlender.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkRuntimeColorFilter.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2023 Google, LLC + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + ==================================================================================================== LIBRARY: skia ORIGIN: ../../../third_party/skia/src/shaders/SkCoordClampShader.cpp + ../../../third_party/skia/LICENSE diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 8570dd9030482..fb01a55066e6b 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: d18c418247db94f3fad4810fc4ab16af +Signature: f30b8b554d159fc0dc6b3c823881e008 ==================================================================================================== LIBRARY: angle @@ -11944,35 +11944,6 @@ use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/mappings.h -ORIGIN: ../../../third_party/glfw/src/mappings.h.in -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/mappings.h -FILE: ../../../third_party/glfw/src/mappings.h.in ----------------------------------------------------------------------------------------------------- -Copyright (C) 1997-2013 Sam Lantinga - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the -use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: icu ORIGIN: ../../../third_party/icu/source/common/locavailable.cpp + ../../../third_party/icu/LICENSE @@ -32647,240 +32618,6 @@ use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/LICENSE.md -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/glfw.rc.in ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard - -Copyright (c) 2006-2019 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/context.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/context.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2016 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/linux_joystick.c -ORIGIN: ../../../third_party/glfw/src/posix_thread.c -ORIGIN: ../../../third_party/glfw/src/posix_thread.h -ORIGIN: ../../../third_party/glfw/src/posix_time.c -ORIGIN: ../../../third_party/glfw/src/posix_time.h -ORIGIN: ../../../third_party/glfw/src/win32_thread.c -ORIGIN: ../../../third_party/glfw/src/win32_thread.h -ORIGIN: ../../../third_party/glfw/src/win32_time.c -ORIGIN: ../../../third_party/glfw/src/win32_time.h -ORIGIN: ../../../third_party/glfw/src/xkb_unicode.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/linux_joystick.c -FILE: ../../../third_party/glfw/src/posix_thread.c -FILE: ../../../third_party/glfw/src/posix_thread.h -FILE: ../../../third_party/glfw/src/posix_time.c -FILE: ../../../third_party/glfw/src/posix_time.h -FILE: ../../../third_party/glfw/src/win32_thread.c -FILE: ../../../third_party/glfw/src/win32_thread.h -FILE: ../../../third_party/glfw/src/win32_time.c -FILE: ../../../third_party/glfw/src/win32_time.h -FILE: ../../../third_party/glfw/src/xkb_unicode.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2017 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/include/GLFW/glfw3native.h -ORIGIN: ../../../third_party/glfw/src/init.c -ORIGIN: ../../../third_party/glfw/src/platform.c -ORIGIN: ../../../third_party/glfw/src/platform.h -ORIGIN: ../../../third_party/glfw/src/vulkan.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/include/GLFW/glfw3native.h -FILE: ../../../third_party/glfw/src/init.c -FILE: ../../../third_party/glfw/src/platform.c -FILE: ../../../third_party/glfw/src/platform.h -FILE: ../../../third_party/glfw/src/vulkan.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2018 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/include/GLFW/glfw3.h -ORIGIN: ../../../third_party/glfw/src/cocoa_monitor.m -ORIGIN: ../../../third_party/glfw/src/egl_context.c -ORIGIN: ../../../third_party/glfw/src/glx_context.c -ORIGIN: ../../../third_party/glfw/src/input.c -ORIGIN: ../../../third_party/glfw/src/internal.h -ORIGIN: ../../../third_party/glfw/src/monitor.c -ORIGIN: ../../../third_party/glfw/src/wgl_context.c -ORIGIN: ../../../third_party/glfw/src/win32_init.c -ORIGIN: ../../../third_party/glfw/src/win32_joystick.c -ORIGIN: ../../../third_party/glfw/src/win32_monitor.c -ORIGIN: ../../../third_party/glfw/src/win32_platform.h -ORIGIN: ../../../third_party/glfw/src/win32_window.c -ORIGIN: ../../../third_party/glfw/src/x11_init.c -ORIGIN: ../../../third_party/glfw/src/x11_monitor.c -ORIGIN: ../../../third_party/glfw/src/x11_platform.h -ORIGIN: ../../../third_party/glfw/src/x11_window.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/include/GLFW/glfw3.h -FILE: ../../../third_party/glfw/src/cocoa_monitor.m -FILE: ../../../third_party/glfw/src/egl_context.c -FILE: ../../../third_party/glfw/src/glx_context.c -FILE: ../../../third_party/glfw/src/input.c -FILE: ../../../third_party/glfw/src/internal.h -FILE: ../../../third_party/glfw/src/monitor.c -FILE: ../../../third_party/glfw/src/wgl_context.c -FILE: ../../../third_party/glfw/src/win32_init.c -FILE: ../../../third_party/glfw/src/win32_joystick.c -FILE: ../../../third_party/glfw/src/win32_monitor.c -FILE: ../../../third_party/glfw/src/win32_platform.h -FILE: ../../../third_party/glfw/src/win32_window.c -FILE: ../../../third_party/glfw/src/x11_init.c -FILE: ../../../third_party/glfw/src/x11_monitor.c -FILE: ../../../third_party/glfw/src/x11_platform.h -FILE: ../../../third_party/glfw/src/x11_window.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2019 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/window.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/window.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2019 Camilla Löwy -Copyright (c) 2012 Torsten Walluhn - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: boringssl ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes.c @@ -34450,68 +34187,6 @@ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_joystick.h -ORIGIN: ../../../third_party/glfw/src/null_joystick.h -ORIGIN: ../../../third_party/glfw/src/win32_joystick.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/cocoa_joystick.h -FILE: ../../../third_party/glfw/src/null_joystick.h -FILE: ../../../third_party/glfw/src/win32_joystick.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2006-2017 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/mappings.h -ORIGIN: ../../../third_party/glfw/src/mappings.h.in -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/mappings.h -FILE: ../../../third_party/glfw/src/mappings.h.in ----------------------------------------------------------------------------------------------------- -Copyright (c) 2006-2018 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: khronos ORIGIN: ../../../third_party/khronos/noninclude/GL/glxext.h @@ -35170,97 +34845,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_time.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/cocoa_time.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2016 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_init.m -ORIGIN: ../../../third_party/glfw/src/cocoa_platform.h -ORIGIN: ../../../third_party/glfw/src/cocoa_window.m -ORIGIN: ../../../third_party/glfw/src/nsgl_context.m -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/cocoa_init.m -FILE: ../../../third_party/glfw/src/cocoa_platform.h -FILE: ../../../third_party/glfw/src/cocoa_window.m -FILE: ../../../third_party/glfw/src/nsgl_context.m ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2019 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_joystick.m -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/cocoa_joystick.m ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2019 Camilla Löwy -Copyright (c) 2012 Torsten Walluhn - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: libcxx LIBRARY: libcxxabi @@ -35332,34 +34916,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_time.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/cocoa_time.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2021 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: libXNVCtrl ORIGIN: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.h @@ -36120,44 +35676,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/linux_joystick.h -ORIGIN: ../../../third_party/glfw/src/wl_init.c -ORIGIN: ../../../third_party/glfw/src/wl_monitor.c -ORIGIN: ../../../third_party/glfw/src/wl_platform.h -ORIGIN: ../../../third_party/glfw/src/wl_window.c -ORIGIN: ../../../third_party/glfw/src/xkb_unicode.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/linux_joystick.h -FILE: ../../../third_party/glfw/src/wl_init.c -FILE: ../../../third_party/glfw/src/wl_monitor.c -FILE: ../../../third_party/glfw/src/wl_platform.h -FILE: ../../../third_party/glfw/src/wl_window.c -FILE: ../../../third_party/glfw/src/xkb_unicode.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014 Jonas Ådahl - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: boringssl ORIGIN: ../../../third_party/boringssl/src/crypto/bio/socket_helper.c @@ -36621,70 +36139,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/null_init.c -ORIGIN: ../../../third_party/glfw/src/null_platform.h -ORIGIN: ../../../third_party/glfw/src/osmesa_context.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/null_init.c -FILE: ../../../third_party/glfw/src/null_platform.h -FILE: ../../../third_party/glfw/src/osmesa_context.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2016 Google Inc. -Copyright (c) 2016-2017 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/null_monitor.c -ORIGIN: ../../../third_party/glfw/src/null_window.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/null_monitor.c -FILE: ../../../third_party/glfw/src/null_window.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2016 Google Inc. -Copyright (c) 2016-2019 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: boringssl ORIGIN: ../../../third_party/boringssl/src/crypto/bytestring/asn1_compat.c @@ -36746,34 +36200,6 @@ OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/null_joystick.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/null_joystick.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2016-2017 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: boringssl ORIGIN: ../../../third_party/boringssl/crypto_test_data.cc @@ -37380,36 +36806,6 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/posix_module.c -ORIGIN: ../../../third_party/glfw/src/win32_module.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/posix_module.c -FILE: ../../../third_party/glfw/src/win32_module.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2021 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: ceval ORIGIN: ../../../third_party/angle/src/third_party/ceval/LICENSE @@ -37504,36 +36900,6 @@ FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ==================================================================================================== -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/posix_poll.c -ORIGIN: ../../../third_party/glfw/src/posix_poll.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/glfw/src/posix_poll.c -FILE: ../../../third_party/glfw/src/posix_poll.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2022 Camilla Löwy - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - ==================================================================================================== LIBRARY: boringssl ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/posix_time.c @@ -42632,8 +41998,12 @@ ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.cpp + ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.h + ../../../third_party/angle/LICENSE +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.cpp + ../../../third_party/angle/LICENSE +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.h + ../../../third_party/angle/LICENSE +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.cpp + ../../../third_party/angle/LICENSE +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.mm + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/CompilerMtl.h + ../../../third_party/angle/LICENSE @@ -42819,8 +42189,12 @@ FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.h FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.h FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.h FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.h FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.mm FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/CompilerMtl.h @@ -43218,6 +42592,8 @@ ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfac ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.mm + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.h + ../../../third_party/angle/LICENSE +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.cpp + ../../../third_party/angle/LICENSE +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.mm + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PixmapSurfaceGLX.cpp + ../../../third_party/angle/LICENSE @@ -43232,7 +42608,6 @@ ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/TransformFeedback ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/TransformFeedbackMtl.mm + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/file_hooking/shader_cache_file_hooking.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_autogen.metal + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_ios_autogen.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_macos_autogen.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_src_autogen.h + ../../../third_party/angle/LICENSE @@ -43492,6 +42867,8 @@ FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceE FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.mm FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.h FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.mm FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PixmapSurfaceGLX.cpp @@ -43506,7 +42883,6 @@ FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TransformFeedbackMt FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TransformFeedbackMtl.mm FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/file_hooking/shader_cache_file_hooking.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_autogen.metal FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_ios_autogen.h FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_macos_autogen.h FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_src_autogen.h @@ -44468,8 +43844,6 @@ LIBRARY: angle ORIGIN: ../../../third_party/angle/src/common/FixedQueue.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/common/platform_helpers.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/common/platform_helpers.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/compiler/translator/tree_ops/RescopeGlobalVariables.cpp + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/compiler/translator/tree_ops/RescopeGlobalVariables.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/compiler/translator/tree_ops/msl/RewriteInterpolants.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/compiler/translator/tree_ops/msl/RewriteInterpolants.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/compiler/translator/tree_ops/spirv/EmulateFramebufferFetch.cpp + ../../../third_party/angle/LICENSE @@ -44484,13 +43858,6 @@ ORIGIN: ../../../third_party/angle/src/libANGLE/context_private_call_gl.cpp + .. ORIGIN: ../../../third_party/angle/src/libANGLE/context_private_call_gl_autogen.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/context_private_call_gles.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/context_private_call_gles_autogen.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/ProgramExecutableImpl.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramExecutableD3D.cpp + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramExecutableD3D.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramExecutableGL.cpp + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramExecutableGL.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramExecutableMtl.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramExecutableMtl.mm + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/blocklayoutMetal.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/blocklayoutMetal.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_library_cache.h + ../../../third_party/angle/LICENSE @@ -44501,17 +43868,12 @@ ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/process.cpp + ../ ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/process.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/renderermtl_utils.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/renderermtl_utils.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_metallib.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramExecutableNULL.cpp + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramExecutableNULL.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryTracking.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryTracking.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandPool.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandPool.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShareGroupVk.cpp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShareGroupVk.h + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.cpp + ../../../third_party/angle/LICENSE -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.h + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/CopyImageToBuffer.comp + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/CopyImageToBuffer.comp.json + ../../../third_party/angle/LICENSE ORIGIN: ../../../third_party/angle/src/libGLESv2/egl_context_lock_autogen.h + ../../../third_party/angle/LICENSE @@ -44520,8 +43882,6 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/angle/src/common/FixedQueue.h FILE: ../../../third_party/angle/src/common/platform_helpers.cpp FILE: ../../../third_party/angle/src/common/platform_helpers.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RescopeGlobalVariables.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RescopeGlobalVariables.h FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/msl/RewriteInterpolants.cpp FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/msl/RewriteInterpolants.h FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/spirv/EmulateFramebufferFetch.cpp @@ -44536,13 +43896,6 @@ FILE: ../../../third_party/angle/src/libANGLE/context_private_call_gl.cpp FILE: ../../../third_party/angle/src/libANGLE/context_private_call_gl_autogen.h FILE: ../../../third_party/angle/src/libANGLE/context_private_call_gles.cpp FILE: ../../../third_party/angle/src/libANGLE/context_private_call_gles_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramExecutableImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramExecutableD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramExecutableD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramExecutableGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramExecutableGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramExecutableMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramExecutableMtl.mm FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/blocklayoutMetal.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/blocklayoutMetal.h FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_library_cache.h @@ -44553,17 +43906,12 @@ FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/process.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/process.h FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/renderermtl_utils.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/renderermtl_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_internal_shaders_metallib.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramExecutableNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramExecutableNULL.h FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryTracking.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryTracking.h FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandPool.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandPool.h FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShareGroupVk.cpp FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShareGroupVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.h FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/CopyImageToBuffer.comp FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/CopyImageToBuffer.comp.json FILE: ../../../third_party/angle/src/libGLESv2/egl_context_lock_autogen.h @@ -49927,6 +49275,7 @@ FILE: ../../../third_party/icu/patches/configure.patch FILE: ../../../third_party/icu/patches/data_symb.patch FILE: ../../../third_party/icu/patches/fuchsia.patch FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/gmt24.patch FILE: ../../../third_party/icu/patches/include-utility.patch FILE: ../../../third_party/icu/patches/iso2022jp.patch FILE: ../../../third_party/icu/patches/isvalidenum.patch @@ -49935,6 +49284,7 @@ FILE: ../../../third_party/icu/patches/locale1.patch FILE: ../../../third_party/icu/patches/locale_google.patch FILE: ../../../third_party/icu/patches/name_5_langs.patch FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/revert_realpath.patch FILE: ../../../third_party/icu/patches/ultag_parse-double-free.patch FILE: ../../../third_party/icu/patches/wordbrk.patch FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan-icu4x.icu @@ -61917,4 +61267,4 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. ==================================================================================================== -Total license count: 868 +Total license count: 849 diff --git a/ci/licenses_golden/tool_signature b/ci/licenses_golden/tool_signature index bb1142c385e9a..0772863aa69bd 100644 --- a/ci/licenses_golden/tool_signature +++ b/ci/licenses_golden/tool_signature @@ -1,2 +1,2 @@ -Signature: 575f2a41ff152394b7e539742fa248e6 +Signature: f161ef7a408768b3ded7494d69263d9b diff --git a/ci/lint.sh b/ci/lint.sh deleted file mode 100755 index e7625fd7fdca1..0000000000000 --- a/ci/lint.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# -# 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. - -set -e - -# Needed because if it is set, cd may print the path it changed to. -unset CDPATH - -# On Mac OS, readlink -f doesn't work, so follow_links traverses the path one -# link at a time, and then cds into the link destination and find out where it -# ends up. -# -# The function is enclosed in a subshell to avoid changing the working directory -# of the caller. -function follow_links() ( - cd -P "$(dirname -- "$1")" - file="$PWD/$(basename -- "$1")" - while [[ -h "$file" ]]; do - cd -P "$(dirname -- "$file")" - file="$(readlink -- "$file")" - cd -P "$(dirname -- "$file")" - file="$PWD/$(basename -- "$file")" - done - echo "$file" -) - -SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")") -PYLINT="${SCRIPT_DIR}/pylint.sh" -CLANG_TIDY="${SCRIPT_DIR}/clang_tidy.sh" - -"${PYLINT}" "$@" -"${CLANG_TIDY}" "$@" diff --git a/ci/pubspec.yaml b/ci/pubspec.yaml index 85639d87f8160..8a662bdd57134 100644 --- a/ci/pubspec.yaml +++ b/ci/pubspec.yaml @@ -5,7 +5,7 @@ name: ci_scripts publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/ci/pylint.sh b/ci/pylint.sh index a3e3422152b00..e94f5248cb8e6 100755 --- a/ci/pylint.sh +++ b/ci/pylint.sh @@ -34,7 +34,6 @@ echo "$(date +%T) Running pylint" cd "$FLUTTER_DIR" pylint-2.7 --rcfile=.pylintrc \ - "build/" \ "ci/" \ "impeller/" \ "sky/" \ diff --git a/common/config.gni b/common/config.gni index 56035f10a3961..41439fed9dc82 100644 --- a/common/config.gni +++ b/common/config.gni @@ -113,18 +113,27 @@ if (flutter_prebuilt_dart_sdk) { _host_os_name = "windows" } + # When building 32-bit Android development artifacts for Windows host (like + # gen_snapshot), the host_cpu is set to x86. However, the correct prebuilt + # Dart SDK to use during this build is still the 64-bit one. + _host_cpu = host_cpu + if (host_os == "win" && host_cpu == "x86") { + _host_cpu = "x64" + } + _target_prebuilt_dart_sdk_config = "$_target_os_name-$target_cpu" - _host_prebuilt_dart_sdk_config = "$_host_os_name-$host_cpu" + _host_prebuilt_dart_sdk_config = "$_host_os_name-$_host_cpu" target_prebuilt_dart_sdk = "//flutter/prebuilts/$_target_prebuilt_dart_sdk_config/dart-sdk" host_prebuilt_dart_sdk = "//flutter/prebuilts/$_host_prebuilt_dart_sdk_config/dart-sdk" - # There is no prebuilt Dart SDK targeting Fuchsia, but we also don't need - # one, so even when the build is targeting Fuchsia, use the prebuilt - # Dart SDK for the host. - if (current_toolchain == host_toolchain || is_fuchsia || is_wasm) { + # There is no prebuilt Dart SDK targeting Fuchsia, iOS, and Android, but we + # also don't need one, so even when the build is targeting one of these + # platforms, we use the prebuilt Dart SDK for the host. + if (current_toolchain == host_toolchain || target_os == "android" || + target_os == "fuchsia" || target_os == "ios" || target_os == "wasm") { prebuilt_dart_sdk = host_prebuilt_dart_sdk prebuilt_dart_sdk_config = _host_prebuilt_dart_sdk_config } else { diff --git a/common/graphics/BUILD.gn b/common/graphics/BUILD.gn index 70fbc1ad5a614..61d410d4f864d 100644 --- a/common/graphics/BUILD.gn +++ b/common/graphics/BUILD.gn @@ -24,6 +24,7 @@ source_set("graphics") { "//flutter/assets", "//flutter/display_list", "//flutter/fml", + "//flutter/shell/common:base64", "//flutter/shell/version:version", "//third_party/boringssl", "//third_party/rapidjson", diff --git a/common/graphics/persistent_cache.cc b/common/graphics/persistent_cache.cc index 50d09bb8fcbc6..0d24125d1f00b 100644 --- a/common/graphics/persistent_cache.cc +++ b/common/graphics/persistent_cache.cc @@ -18,11 +18,11 @@ #include "flutter/fml/mapping.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/base64.h" #include "flutter/shell/version/version.h" #include "openssl/sha.h" #include "rapidjson/document.h" #include "third_party/skia/include/gpu/GrDirectContext.h" -#include "third_party/skia/include/utils/SkBase64.h" namespace flutter { @@ -169,21 +169,21 @@ sk_sp ParseBase32(const std::string& input) { } sk_sp ParseBase64(const std::string& input) { - SkBase64::Error error; + Base64::Error error; size_t output_len; - error = SkBase64::Decode(input.c_str(), input.length(), nullptr, &output_len); - if (error != SkBase64::Error::kNoError) { - FML_LOG(ERROR) << "Base64 decode error: " << error; + error = Base64::Decode(input.c_str(), input.length(), nullptr, &output_len); + if (error != Base64::Error::kNone) { + FML_LOG(ERROR) << "Base64 decode error: " << (int)error; FML_LOG(ERROR) << "Base64 can't decode: " << input; return nullptr; } sk_sp data = SkData::MakeUninitialized(output_len); void* output = data->writable_data(); - error = SkBase64::Decode(input.c_str(), input.length(), output, &output_len); - if (error != SkBase64::Error::kNoError) { - FML_LOG(ERROR) << "Base64 decode error: " << error; + error = Base64::Decode(input.c_str(), input.length(), output, &output_len); + if (error != Base64::Error::kNone) { + FML_LOG(ERROR) << "Base64 decode error: " << (int)error; FML_LOG(ERROR) << "Base64 can't decode: " << input; return nullptr; } diff --git a/common/graphics/texture.cc b/common/graphics/texture.cc index 07025ff085779..74d76c909f823 100644 --- a/common/graphics/texture.cc +++ b/common/graphics/texture.cc @@ -14,7 +14,7 @@ Texture::Texture(int64_t id) : id_(id) {} Texture::~Texture() = default; -TextureRegistry::TextureRegistry() : image_counter_(0) {} +TextureRegistry::TextureRegistry() = default; void TextureRegistry::RegisterTexture(const std::shared_ptr& texture) { if (!texture) { diff --git a/common/graphics/texture.h b/common/graphics/texture.h index 9b582a84da286..3f4ea4fcecfde 100644 --- a/common/graphics/texture.h +++ b/common/graphics/texture.h @@ -95,7 +95,7 @@ class TextureRegistry { private: std::map> mapping_; - size_t image_counter_; + size_t image_counter_ = 0; // This map keeps track of registered context listeners by their own // externally provided id. It indexes into ordered_images_. std::map image_indices_; diff --git a/common/settings.h b/common/settings.h index eb07585a33fb0..00dc5aeaf8d04 100644 --- a/common/settings.h +++ b/common/settings.h @@ -218,6 +218,9 @@ struct Settings { bool enable_impeller = false; #endif + // Indicates if image reader backed platform views are disabled. + bool disable_image_reader_platform_views = false; + // Requests a particular backend to be used (ex "opengles" or "vulkan") std::optional impeller_backend; @@ -225,6 +228,10 @@ struct Settings { // must be available to the application. bool enable_vulkan_validation = false; + // Enable GPU tracing in GLES backends. + // Some devices claim to support the required APIs but crash on their usage. + bool enable_opengl_gpu_tracing = false; + // Data set by platform-specific embedders for use in font initialization. uint32_t font_initialization_data = 0; diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 736e13dc5c7bb..1ec13109d0d83 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -155,6 +155,7 @@ if (enable_unittests) { "//flutter/common/graphics", "//flutter/display_list/testing:display_list_surface_provider", "//flutter/display_list/testing:display_list_testing", + "//flutter/impeller/typographer/backends/skia:typographer_skia_backend", "//flutter/testing", ] diff --git a/display_list/benchmarking/dl_benchmarks.cc b/display_list/benchmarking/dl_benchmarks.cc index 88efb5d69af15..2a03b60c7a8e9 100644 --- a/display_list/benchmarking/dl_benchmarks.cc +++ b/display_list/benchmarking/dl_benchmarks.cc @@ -710,15 +710,15 @@ std::shared_ptr GetTestVertices(SkPoint center, // the center point C, this should create a triangle fan with vertices // C, O_0, O_1, O_2, O_3, ... vertices.push_back(center); - colors.push_back(SK_ColorCYAN); + colors.push_back(DlColor(SK_ColorCYAN)); for (size_t i = 0; i <= outer_points.size(); i++) { vertices.push_back(outer_points[i % outer_points.size()]); if (i % 3 == 0) { - colors.push_back(SK_ColorRED); + colors.push_back(DlColor(SK_ColorRED)); } else if (i % 3 == 1) { - colors.push_back(SK_ColorGREEN); + colors.push_back(DlColor(SK_ColorGREEN)); } else { - colors.push_back(SK_ColorBLUE); + colors.push_back(DlColor(SK_ColorBLUE)); } } break; @@ -728,11 +728,11 @@ std::shared_ptr GetTestVertices(SkPoint center, // vertices O_0, O_1, C, O_1, O_2, C, O_2, O_3, C, ... for (size_t i = 0; i < outer_vertex_count; i++) { vertices.push_back(outer_points[i % outer_points.size()]); - colors.push_back(SK_ColorRED); + colors.push_back(DlColor(SK_ColorRED)); vertices.push_back(outer_points[(i + 1) % outer_points.size()]); - colors.push_back(SK_ColorGREEN); + colors.push_back(DlColor(SK_ColorGREEN)); vertices.push_back(center); - colors.push_back(SK_ColorBLUE); + colors.push_back(DlColor(SK_ColorBLUE)); } break; case DlVertexMode::kTriangleStrip: @@ -741,10 +741,10 @@ std::shared_ptr GetTestVertices(SkPoint center, // O_0, O_1, C, O_2, O_3, C, O_4, O_5, C, ... for (size_t i = 0; i <= outer_vertex_count; i++) { vertices.push_back(outer_points[i % outer_points.size()]); - colors.push_back(i % 2 ? SK_ColorRED : SK_ColorGREEN); + colors.push_back(i % 2 ? DlColor(SK_ColorRED) : DlColor(SK_ColorGREEN)); if (i % 2 == 1) { vertices.push_back(center); - colors.push_back(SK_ColorBLUE); + colors.push_back(DlColor(SK_ColorBLUE)); } } break; @@ -1270,7 +1270,8 @@ void BM_DrawShadow(benchmark::State& state, // We can hardcode dpr to 1.0f as we're varying elevation, and dpr is only // ever used in conjunction with elevation. - builder.DrawShadow(path, SK_ColorBLUE, elevation, transparent_occluder, 1.0f); + builder.DrawShadow(path, DlColor(SK_ColorBLUE), elevation, + transparent_occluder, 1.0f); auto display_list = builder.Build(); // We only want to time the actual rasterization. diff --git a/display_list/benchmarking/dl_benchmarks.h b/display_list/benchmarking/dl_benchmarks.h index 130c65c63259e..f43cd7c6731a6 100644 --- a/display_list/benchmarking/dl_benchmarks.h +++ b/display_list/benchmarking/dl_benchmarks.h @@ -102,7 +102,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawLine #define DRAW_LINE_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawLine, BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES) \ ->RangeMultiplier(2) \ ->Range(16, 2048) \ @@ -112,7 +112,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawRect #define DRAW_RECT_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawRect, BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES) \ ->RangeMultiplier(2) \ ->Range(16, 2048) \ @@ -122,7 +122,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawOval #define DRAW_OVAL_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawOval, BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES) \ ->RangeMultiplier(2) \ ->Range(16, 2048) \ @@ -132,7 +132,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawCircle #define DRAW_CIRCLE_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawCircle, BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES) \ ->RangeMultiplier(2) \ ->Range(16, 2048) \ @@ -142,7 +142,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawArc #define DRAW_ARC_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawArc, BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES) \ ->RangeMultiplier(2) \ ->Range(128, 2048) \ @@ -153,7 +153,7 @@ void BM_SaveLayer(benchmark::State& state, #define DRAW_PATH_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawPath, \ Lines/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkPath::Verb::kLine_Verb) \ ->RangeMultiplier(2) \ @@ -164,7 +164,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE(BM_DrawPath, \ Quads/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkPath::Verb::kQuad_Verb) \ ->RangeMultiplier(2) \ @@ -175,7 +175,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE(BM_DrawPath, \ Conics/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkPath::Verb::kConic_Verb) \ ->RangeMultiplier(2) \ @@ -186,7 +186,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE(BM_DrawPath, \ Cubics/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkPath::Verb::kCubic_Verb) \ ->RangeMultiplier(2) \ @@ -198,7 +198,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawPoints #define DRAW_POINTS_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawPoints, Points/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlCanvas::PointMode::kPoints) \ ->RangeMultiplier(2) \ @@ -207,7 +207,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawPoints, Lines/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlCanvas::PointMode::kLines) \ ->RangeMultiplier(2) \ @@ -216,7 +216,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawPoints, Polygon/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlCanvas::PointMode::kPolygon) \ ->RangeMultiplier(2) \ @@ -228,7 +228,7 @@ void BM_SaveLayer(benchmark::State& state, #define DRAW_VERTICES_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawVertices, \ TriangleStrip/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlVertexMode::kTriangleStrip) \ ->RangeMultiplier(2) \ @@ -239,7 +239,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE(BM_DrawVertices, \ TriangleFan/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlVertexMode::kTriangleFan) \ ->RangeMultiplier(2) \ @@ -250,7 +250,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE(BM_DrawVertices, \ Triangles/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlVertexMode::kTriangles) \ ->RangeMultiplier(2) \ @@ -262,7 +262,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawRRect #define DRAW_RRECT_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawRRect, Symmetric/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkRRect::Type::kSimple_Type) \ ->RangeMultiplier(2) \ @@ -271,7 +271,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawRRect, NinePatch/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkRRect::Type::kNinePatch_Type) \ ->RangeMultiplier(2) \ @@ -280,7 +280,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawRRect, Complex/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkRRect::Type::kComplex_Type) \ ->RangeMultiplier(2) \ @@ -291,7 +291,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawDRRect #define DRAW_DRRECT_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawDRRect, Symmetric/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkRRect::Type::kSimple_Type) \ ->RangeMultiplier(2) \ @@ -300,7 +300,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawDRRect, NinePatch/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkRRect::Type::kNinePatch_Type) \ ->RangeMultiplier(2) \ @@ -309,7 +309,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawDRRect, Complex/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ SkRRect::Type::kComplex_Type) \ ->RangeMultiplier(2) \ @@ -320,7 +320,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawImage #define DRAW_IMAGE_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawImage, Texture/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlImageSampling::kNearestNeighbor, false) \ ->RangeMultiplier(2) \ @@ -329,7 +329,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawImage, Upload/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlImageSampling::kNearestNeighbor, true) \ ->RangeMultiplier(2) \ @@ -341,7 +341,7 @@ void BM_SaveLayer(benchmark::State& state, #define DRAW_IMAGE_RECT_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE( \ BM_DrawImageRect, Texture/Strict/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlImageSampling::kNearestNeighbor, \ DlCanvas::SrcRectConstraint::kStrict, false) \ @@ -352,7 +352,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE( \ BM_DrawImageRect, Texture/Fast/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlImageSampling::kNearestNeighbor, \ DlCanvas::SrcRectConstraint::kFast, false) \ @@ -363,7 +363,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE( \ BM_DrawImageRect, Upload/Strict/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlImageSampling::kNearestNeighbor, \ DlCanvas::SrcRectConstraint::kStrict, true) \ @@ -374,7 +374,7 @@ void BM_SaveLayer(benchmark::State& state, \ BENCHMARK_CAPTURE( \ BM_DrawImageRect, Upload/Fast/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlImageSampling::kNearestNeighbor, \ DlCanvas::SrcRectConstraint::kFast, true) \ @@ -386,7 +386,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawImageNine #define DRAW_IMAGE_NINE_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawImageNine, Texture/Nearest/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlFilterMode::kNearest, false) \ ->RangeMultiplier(2) \ @@ -395,7 +395,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawImageNine, Upload/Nearest/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlFilterMode::kNearest, true) \ ->RangeMultiplier(2) \ @@ -404,7 +404,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawImageNine, Texture/Linear/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlFilterMode::kLinear, false) \ ->RangeMultiplier(2) \ @@ -413,7 +413,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawImageNine, Upload/Linear/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ DlFilterMode::kLinear, true) \ ->RangeMultiplier(2) \ @@ -424,7 +424,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawTextBlob #define DRAW_TEXT_BLOB_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawTextBlob, BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES) \ ->RangeMultiplier(2) \ ->Range(1, 256) \ @@ -435,7 +435,7 @@ void BM_SaveLayer(benchmark::State& state, // DrawShadow #define DRAW_SHADOW_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_DrawShadow, Lines/Transparent/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ true, \ SkPath::Verb::kLine_Verb) \ @@ -445,7 +445,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Quads/Transparent/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ true, \ SkPath::Verb::kQuad_Verb) \ @@ -455,7 +455,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Conics/Transparent/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ true, \ SkPath::Verb::kConic_Verb) \ @@ -465,7 +465,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Cubics/Transparent/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ true, \ SkPath::Verb::kCubic_Verb) \ @@ -475,7 +475,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Lines/Opaque/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ false, \ SkPath::Verb::kLine_Verb) \ @@ -485,7 +485,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Quads/Opaque/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ false, \ SkPath::Verb::kQuad_Verb) \ @@ -495,7 +495,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Conics/Opaque/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ false, \ SkPath::Verb::kConic_Verb) \ @@ -505,7 +505,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_DrawShadow, Cubics/Opaque/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ false, \ SkPath::Verb::kCubic_Verb) \ @@ -517,7 +517,7 @@ void BM_SaveLayer(benchmark::State& state, // SaveLayer #define SAVE_LAYER_BENCHMARKS(BACKEND, ATTRIBUTES) \ BENCHMARK_CAPTURE(BM_SaveLayer, Depth 1/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ 1) \ ->RangeMultiplier(2) \ @@ -526,7 +526,7 @@ void BM_SaveLayer(benchmark::State& state, ->Unit(benchmark::kMillisecond); \ \ BENCHMARK_CAPTURE(BM_SaveLayer, Depth 8/BACKEND, \ - BackendType::k##BACKEND##_Backend, \ + BackendType::k##BACKEND##Backend, \ ATTRIBUTES, \ 8) \ ->RangeMultiplier(2) \ diff --git a/display_list/benchmarking/dl_complexity_gl.h b/display_list/benchmarking/dl_complexity_gl.h index 5115bb4d6ecd6..61064e4cdef22 100644 --- a/display_list/benchmarking/dl_complexity_gl.h +++ b/display_list/benchmarking/dl_complexity_gl.h @@ -32,7 +32,7 @@ class DisplayListGLComplexityCalculator private: class GLHelper : public ComplexityCalculatorHelper { public: - GLHelper(unsigned int ceiling) + explicit GLHelper(unsigned int ceiling) : ComplexityCalculatorHelper(ceiling), save_layer_count_(0), draw_text_blob_count_(0) {} diff --git a/display_list/benchmarking/dl_complexity_helper.h b/display_list/benchmarking/dl_complexity_helper.h index 759d6e64460ed..871937a77a17c 100644 --- a/display_list/benchmarking/dl_complexity_helper.h +++ b/display_list/benchmarking/dl_complexity_helper.h @@ -96,8 +96,8 @@ class ComplexityCalculatorHelper public virtual IgnoreClipDispatchHelper, public virtual IgnoreTransformDispatchHelper { public: - ComplexityCalculatorHelper(unsigned int ceiling) - : is_complex_(false), ceiling_(ceiling), complexity_score_(0) {} + explicit ComplexityCalculatorHelper(unsigned int ceiling) + : ceiling_(ceiling) {} virtual ~ComplexityCalculatorHelper() = default; @@ -261,10 +261,10 @@ class ComplexityCalculatorHelper // If we exceed the ceiling (defaults to the largest number representable // by unsigned int), then set the is_complex_ bool and we no longer // accumulate. - bool is_complex_; + bool is_complex_ = false; unsigned int ceiling_; - unsigned int complexity_score_; + unsigned int complexity_score_ = 0; }; } // namespace flutter diff --git a/display_list/benchmarking/dl_complexity_metal.h b/display_list/benchmarking/dl_complexity_metal.h index dd068e2fa3243..90e7b9f69c663 100644 --- a/display_list/benchmarking/dl_complexity_metal.h +++ b/display_list/benchmarking/dl_complexity_metal.h @@ -32,10 +32,8 @@ class DisplayListMetalComplexityCalculator private: class MetalHelper : public ComplexityCalculatorHelper { public: - MetalHelper(unsigned int ceiling) - : ComplexityCalculatorHelper(ceiling), - save_layer_count_(0), - draw_text_blob_count_(0) {} + explicit MetalHelper(unsigned int ceiling) + : ComplexityCalculatorHelper(ceiling) {} void saveLayer(const SkRect* bounds, const SaveLayerOptions options, @@ -88,8 +86,8 @@ class DisplayListMetalComplexityCalculator unsigned int BatchedComplexity() override; private: - unsigned int save_layer_count_; - unsigned int draw_text_blob_count_; + unsigned int save_layer_count_ = 0; + unsigned int draw_text_blob_count_ = 0; }; DisplayListMetalComplexityCalculator() diff --git a/display_list/benchmarking/dl_complexity_unittests.cc b/display_list/benchmarking/dl_complexity_unittests.cc index eee77ac9bdf55..df4fc68497336 100644 --- a/display_list/benchmarking/dl_complexity_unittests.cc +++ b/display_list/benchmarking/dl_complexity_unittests.cc @@ -190,7 +190,7 @@ TEST(DisplayListComplexity, DrawShadow) { line_path.moveTo(SkPoint::Make(0, 0)); line_path.lineTo(SkPoint::Make(10, 10)); line_path.close(); - builder_line.DrawShadow(line_path, SK_ColorRED, 10.0f, false, 1.0f); + builder_line.DrawShadow(line_path, DlColor(SK_ColorRED), 10.0f, false, 1.0f); auto display_list_line = builder_line.Build(); DisplayListBuilder builder_quad; @@ -198,7 +198,7 @@ TEST(DisplayListComplexity, DrawShadow) { quad_path.moveTo(SkPoint::Make(0, 0)); quad_path.quadTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20)); quad_path.close(); - builder_quad.DrawShadow(quad_path, SK_ColorRED, 10.0f, false, 1.0f); + builder_quad.DrawShadow(quad_path, DlColor(SK_ColorRED), 10.0f, false, 1.0f); auto display_list_quad = builder_quad.Build(); DisplayListBuilder builder_conic; @@ -206,7 +206,8 @@ TEST(DisplayListComplexity, DrawShadow) { conic_path.moveTo(SkPoint::Make(0, 0)); conic_path.conicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), 1.5f); conic_path.close(); - builder_conic.DrawShadow(conic_path, SK_ColorRED, 10.0f, false, 1.0f); + builder_conic.DrawShadow(conic_path, DlColor(SK_ColorRED), 10.0f, false, + 1.0f); auto display_list_conic = builder_conic.Build(); DisplayListBuilder builder_cubic; @@ -214,7 +215,8 @@ TEST(DisplayListComplexity, DrawShadow) { cubic_path.moveTo(SkPoint::Make(0, 0)); cubic_path.cubicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), SkPoint::Make(20, 20)); - builder_cubic.DrawShadow(cubic_path, SK_ColorRED, 10.0f, false, 1.0f); + builder_cubic.DrawShadow(cubic_path, DlColor(SK_ColorRED), 10.0f, false, + 1.0f); auto display_list_cubic = builder_cubic.Build(); auto calculators = AccumulatorCalculators(); diff --git a/display_list/display_list.h b/display_list/display_list.h index 3d4a7acff5896..621338e59fd1e 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -160,7 +160,8 @@ class SaveLayerOptions { SaveLayerOptions() : flags_(0) {} SaveLayerOptions(const SaveLayerOptions& options) : flags_(options.flags_) {} - SaveLayerOptions(const SaveLayerOptions* options) : flags_(options->flags_) {} + explicit SaveLayerOptions(const SaveLayerOptions* options) + : flags_(options->flags_) {} SaveLayerOptions without_optimizations() const { SaveLayerOptions options; diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index bef888bcf8633..e5ce73e307258 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -932,7 +932,8 @@ TEST_F(DisplayListTest, NestedOpCountMetricsSameAsSkPicture) { DlOpReceiver& receiver = ToReceiver(builder); for (int y = 10; y <= 60; y += 10) { for (int x = 10; x <= 60; x += 10) { - receiver.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE); + receiver.setColor(((x + y) % 20) == 10 ? DlColor(SK_ColorRED) + : DlColor(SK_ColorBLUE)); receiver.drawRect(SkRect::MakeXYWH(x, y, 80, 80)); } } @@ -1059,15 +1060,15 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { }; #define RUN_TESTS(body) \ - run_tests( \ - #body, [](DlOpReceiver& receiver) { body }, true, false) + run_tests(#body, [](DlOpReceiver& receiver) { body }, true, false) #define RUN_TESTS2(body, expect) \ - run_tests( \ - #body, [](DlOpReceiver& receiver) { body }, expect, expect) + run_tests(#body, [](DlOpReceiver& receiver) { body }, expect, expect) RUN_TESTS(receiver.drawPaint();); - RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true); - RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrc);, false); + RUN_TESTS2(receiver.drawColor(DlColor(SK_ColorRED), DlBlendMode::kSrcOver); + , true); + RUN_TESTS2(receiver.drawColor(DlColor(SK_ColorRED), DlBlendMode::kSrc); + , false); RUN_TESTS(receiver.drawLine({0, 0}, {10, 10});); RUN_TESTS(receiver.drawRect({0, 0, 10, 10});); RUN_TESTS(receiver.drawOval({0, 0, 10, 10});); @@ -1119,8 +1120,9 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { RUN_TESTS2(receiver.drawDisplayList(display_list);, false); } RUN_TESTS2(receiver.drawTextBlob(TestBlob1, 0, 0);, false); - RUN_TESTS2(receiver.drawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0); - , false); + RUN_TESTS2( + receiver.drawShadow(kTestPath1, DlColor(SK_ColorBLACK), 1.0, false, 1.0); + , false); #undef RUN_TESTS2 #undef RUN_TESTS @@ -1250,7 +1252,7 @@ TEST_F(DisplayListTest, SaveLayerOneSimpleOpInheritsOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.drawRect({10, 10, 20, 20}); receiver.restore(); @@ -1280,7 +1282,7 @@ TEST_F(DisplayListTest, SaveLayerTwoOverlappingOpsDoesNotInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.drawRect({10, 10, 20, 20}); receiver.drawRect({15, 15, 25, 25}); @@ -1300,7 +1302,7 @@ TEST_F(DisplayListTest, NestedSaveLayersMightInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.drawRect({10, 10, 20, 20}); @@ -1323,7 +1325,7 @@ TEST_F(DisplayListTest, NestedSaveLayersCanBothSupportOpacityOptimization) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); receiver.drawRect({10, 10, 20, 20}); @@ -1340,7 +1342,7 @@ TEST_F(DisplayListTest, SaveLayerImageFilterDoesNotInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.setImageFilter(&kTestBlurImageFilter1); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.setImageFilter(nullptr); @@ -1357,7 +1359,7 @@ TEST_F(DisplayListTest, SaveLayerColorFilterDoesNotInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.setColorFilter(&kTestMatrixColorFilter1); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.setColorFilter(nullptr); @@ -1374,7 +1376,7 @@ TEST_F(DisplayListTest, SaveLayerSrcBlendDoesNotInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.setBlendMode(DlBlendMode::kSrc); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.setBlendMode(DlBlendMode::kSrcOver); @@ -1392,7 +1394,7 @@ TEST_F(DisplayListTest, SaveLayerImageFilterOnChildInheritsOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.setImageFilter(&kTestBlurImageFilter1); receiver.drawRect({10, 10, 20, 20}); @@ -1408,7 +1410,7 @@ TEST_F(DisplayListTest, SaveLayerColorFilterOnChildDoesNotInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.setColorFilter(&kTestMatrixColorFilter1); receiver.drawRect({10, 10, 20, 20}); @@ -1424,7 +1426,7 @@ TEST_F(DisplayListTest, SaveLayerSrcBlendOnChildDoesNotInheritOpacity) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255))); receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); receiver.setBlendMode(DlBlendMode::kSrc); receiver.drawRect({10, 10, 20, 20}); diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index f20b8fe82ec47..923d32230f632 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -351,7 +351,7 @@ void DisplayListBuilder::SetAttributesFromPaint( setDither(paint.isDither()); } if (flags.applies_alpha_or_color()) { - setColor(paint.getColor().argb); + setColor(paint.getColor()); } if (flags.applies_blend()) { setBlendMode(paint.getBlendMode()); diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h index 5cd06146c78c3..9ba279a15b590 100644 --- a/display_list/dl_canvas.h +++ b/display_list/dl_canvas.h @@ -85,8 +85,8 @@ class DlCanvas { virtual void TransformReset() = 0; virtual void Transform(const SkMatrix* matrix) = 0; virtual void Transform(const SkM44* matrix44) = 0; - virtual void Transform(const SkMatrix& matrix) { Transform(&matrix); } - virtual void Transform(const SkM44& matrix44) { Transform(&matrix44); } + void Transform(const SkMatrix& matrix) { Transform(&matrix); } + void Transform(const SkM44& matrix44) { Transform(&matrix44); } virtual void SetTransform(const SkMatrix* matrix) = 0; virtual void SetTransform(const SkM44* matrix44) = 0; virtual void SetTransform(const SkMatrix& matrix) { SetTransform(&matrix); } @@ -179,12 +179,11 @@ class DlCanvas { SrcRectConstraint constraint = SrcRectConstraint::kFast) { DrawImageRect(image, SkRect::Make(src), dst, sampling, paint, constraint); } - virtual void DrawImageRect( - const sk_sp& image, - const SkRect& dst, - DlImageSampling sampling, - const DlPaint* paint = nullptr, - SrcRectConstraint constraint = SrcRectConstraint::kFast) { + void DrawImageRect(const sk_sp& image, + const SkRect& dst, + DlImageSampling sampling, + const DlPaint* paint = nullptr, + SrcRectConstraint constraint = SrcRectConstraint::kFast) { DrawImageRect(image, image->bounds(), dst, sampling, paint, constraint); } virtual void DrawImageNine(const sk_sp& image, diff --git a/display_list/dl_color.h b/display_list/dl_color.h index 92a39150d2f2e..0b6bb2c21cb12 100644 --- a/display_list/dl_color.h +++ b/display_list/dl_color.h @@ -11,36 +11,34 @@ namespace flutter { struct DlColor { public: - constexpr DlColor() : argb(0xFF000000) {} - constexpr DlColor(uint32_t argb) : argb(argb) {} + constexpr DlColor() : argb_(0xFF000000) {} + constexpr explicit DlColor(uint32_t argb) : argb_(argb) {} static constexpr uint8_t toAlpha(SkScalar opacity) { return toC(opacity); } static constexpr SkScalar toOpacity(uint8_t alpha) { return toF(alpha); } // clang-format off - static constexpr DlColor kTransparent() {return 0x00000000;}; - static constexpr DlColor kBlack() {return 0xFF000000;}; - static constexpr DlColor kWhite() {return 0xFFFFFFFF;}; - static constexpr DlColor kRed() {return 0xFFFF0000;}; - static constexpr DlColor kGreen() {return 0xFF00FF00;}; - static constexpr DlColor kBlue() {return 0xFF0000FF;}; - static constexpr DlColor kCyan() {return 0xFF00FFFF;}; - static constexpr DlColor kMagenta() {return 0xFFFF00FF;}; - static constexpr DlColor kYellow() {return 0xFFFFFF00;}; - static constexpr DlColor kDarkGrey() {return 0xFF3F3F3F;}; - static constexpr DlColor kMidGrey() {return 0xFF808080;}; - static constexpr DlColor kLightGrey() {return 0xFFC0C0C0;}; + static constexpr DlColor kTransparent() {return DlColor(0x00000000);}; + static constexpr DlColor kBlack() {return DlColor(0xFF000000);}; + static constexpr DlColor kWhite() {return DlColor(0xFFFFFFFF);}; + static constexpr DlColor kRed() {return DlColor(0xFFFF0000);}; + static constexpr DlColor kGreen() {return DlColor(0xFF00FF00);}; + static constexpr DlColor kBlue() {return DlColor(0xFF0000FF);}; + static constexpr DlColor kCyan() {return DlColor(0xFF00FFFF);}; + static constexpr DlColor kMagenta() {return DlColor(0xFFFF00FF);}; + static constexpr DlColor kYellow() {return DlColor(0xFFFFFF00);}; + static constexpr DlColor kDarkGrey() {return DlColor(0xFF3F3F3F);}; + static constexpr DlColor kMidGrey() {return DlColor(0xFF808080);}; + static constexpr DlColor kLightGrey() {return DlColor(0xFFC0C0C0);}; // clang-format on - uint32_t argb; - constexpr bool isOpaque() const { return getAlpha() == 0xFF; } constexpr bool isTransparent() const { return getAlpha() == 0; } - constexpr int getAlpha() const { return argb >> 24; } - constexpr int getRed() const { return (argb >> 16) & 0xFF; } - constexpr int getGreen() const { return (argb >> 8) & 0xFF; } - constexpr int getBlue() const { return argb & 0xFF; } + constexpr int getAlpha() const { return argb_ >> 24; } + constexpr int getRed() const { return (argb_ >> 16) & 0xFF; } + constexpr int getGreen() const { return (argb_ >> 8) & 0xFF; } + constexpr int getBlue() const { return argb_ & 0xFF; } constexpr float getAlphaF() const { return toF(getAlpha()); } constexpr float getRedF() const { return toF(getRed()); } @@ -49,26 +47,26 @@ struct DlColor { constexpr uint32_t premultipliedArgb() const { if (isOpaque()) { - return argb; + return argb_; } float f = getAlphaF(); - return (argb & 0xFF000000) | // + return (argb_ & 0xFF000000) | // toC(getRedF() * f) << 16 | // toC(getGreenF() * f) << 8 | // toC(getBlueF() * f); } constexpr DlColor withAlpha(uint8_t alpha) const { // - return (argb & 0x00FFFFFF) | (alpha << 24); + return DlColor((argb_ & 0x00FFFFFF) | (alpha << 24)); } constexpr DlColor withRed(uint8_t red) const { // - return (argb & 0xFF00FFFF) | (red << 16); + return DlColor((argb_ & 0xFF00FFFF) | (red << 16)); } constexpr DlColor withGreen(uint8_t green) const { // - return (argb & 0xFFFF00FF) | (green << 8); + return DlColor((argb_ & 0xFFFF00FF) | (green << 8)); } constexpr DlColor withBlue(uint8_t blue) const { // - return (argb & 0xFFFFFF00) | (blue << 0); + return DlColor((argb_ & 0xFFFFFF00) | (blue << 0)); } constexpr DlColor modulateOpacity(float opacity) const { @@ -77,13 +75,16 @@ struct DlColor { : withAlpha(round(getAlpha() * opacity)); } - operator uint32_t() const { return argb; } - bool operator==(DlColor const& other) const { return argb == other.argb; } - bool operator!=(DlColor const& other) const { return argb != other.argb; } - bool operator==(uint32_t const& other) const { return argb == other; } - bool operator!=(uint32_t const& other) const { return argb != other; } + constexpr uint32_t argb() const { return argb_; } + + bool operator==(DlColor const& other) const { return argb_ == other.argb_; } + bool operator!=(DlColor const& other) const { return argb_ != other.argb_; } + bool operator==(uint32_t const& other) const { return argb_ == other; } + bool operator!=(uint32_t const& other) const { return argb_ != other; } private: + uint32_t argb_; + static float toF(uint8_t comp) { return comp * (1.0f / 255); } static uint8_t toC(float fComp) { return round(fComp * 255); } }; diff --git a/display_list/dl_color_unittests.cc b/display_list/dl_color_unittests.cc index 37d60b26c8fc7..7a6c6a15ed82d 100644 --- a/display_list/dl_color_unittests.cc +++ b/display_list/dl_color_unittests.cc @@ -14,7 +14,7 @@ static void arraysEqual(const uint32_t* ints, const DlColor* colors, int count) { for (int i = 0; i < count; i++) { - EXPECT_TRUE(ints[i] == colors[i]); + EXPECT_TRUE(ints[i] == colors[i].argb()); } } diff --git a/display_list/dl_op_flags.cc b/display_list/dl_op_flags.cc index 8c444dd692554..9293b2adcdc7f 100644 --- a/display_list/dl_op_flags.cc +++ b/display_list/dl_op_flags.cc @@ -21,7 +21,7 @@ const DisplayListSpecialGeometryFlags DisplayListAttributeFlags::WithPathEffect( // end caps to areas that might not have had them before so all // we need to do is to indicate the potential for diagonal // end caps and move on. - return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_); + return special_flags_.with(kMayHaveCaps | kMayHaveDiagonalCaps); } } } diff --git a/display_list/dl_op_flags.h b/display_list/dl_op_flags.h index 9bb6c013f8203..2f6a4d936c192 100644 --- a/display_list/dl_op_flags.h +++ b/display_list/dl_op_flags.h @@ -23,36 +23,36 @@ class DlPathEffect; class DisplayListFlags { protected: // A drawing operation that is not geometric in nature (but which - // may still apply a MaskFilter - see |kUsesMaskFilter_| below). - static constexpr int kIsNonGeometric_ = 0; + // may still apply a MaskFilter - see |kUsesMaskFilter| below). + static constexpr int kIsNonGeometric = 0; // A geometric operation that is defined as a fill operation // regardless of what the current paint Style is set to. - // This flag will automatically assume |kUsesMaskFilter_|. - static constexpr int kIsFilledGeometry_ = 1 << 0; + // This flag will automatically assume |kUsesMaskFilter|. + static constexpr int kIsFilledGeometry = 1 << 0; // A geometric operation that is defined as a stroke operation // regardless of what the current paint Style is set to. - // This flag will automatically assume |kUsesMaskFilter_|. - static constexpr int kIsStrokedGeometry_ = 1 << 1; + // This flag will automatically assume |kUsesMaskFilter|. + static constexpr int kIsStrokedGeometry = 1 << 1; // A geometric operation that may be a stroke or fill operation // depending on the current state of the paint Style attribute. - // This flag will automatically assume |kUsesMaskFilter_|. - static constexpr int kIsDrawnGeometry_ = 1 << 2; + // This flag will automatically assume |kUsesMaskFilter|. + static constexpr int kIsDrawnGeometry = 1 << 2; - static constexpr int kIsAnyGeometryMask_ = // - kIsFilledGeometry_ | // - kIsStrokedGeometry_ | // - kIsDrawnGeometry_; + static constexpr int kIsAnyGeometryMask = // + kIsFilledGeometry | // + kIsStrokedGeometry | // + kIsDrawnGeometry; // A primitive that floods the surface (or clip) with no // natural bounds, such as |drawColor| or |drawPaint|. - static constexpr int kFloodsSurface_ = 1 << 3; + static constexpr int kFloodsSurface = 1 << 3; - static constexpr int kMayHaveCaps_ = 1 << 4; - static constexpr int kMayHaveJoins_ = 1 << 5; - static constexpr int kButtCapIsSquare_ = 1 << 6; + static constexpr int kMayHaveCaps = 1 << 4; + static constexpr int kMayHaveJoins = 1 << 5; + static constexpr int kButtCapIsSquare = 1 << 6; // A geometric operation which has a path that might have // end caps that are not rectilinear which means that square @@ -62,7 +62,7 @@ class DisplayListFlags { // diagonal end caps. |drawLine| might have diagonal end // caps depending on the angle of the line, and more likely // |drawPath| will often have such end caps. - static constexpr int kMayHaveDiagonalCaps_ = 1 << 7; + static constexpr int kMayHaveDiagonalCaps = 1 << 7; // A geometric operation which has joined vertices that are // not guaranteed to be smooth (angles of incoming and outgoing) @@ -73,23 +73,23 @@ class DisplayListFlags { // |drawRect|, |drawOval| and |drawRRect| all have well // behaved joins, but |drawPath| might have joins that cause // mitered extensions outside the pre-transformed bounding box. - static constexpr int kMayHaveAcuteJoins_ = 1 << 8; + static constexpr int kMayHaveAcuteJoins = 1 << 8; - static constexpr int kAnySpecialGeometryMask_ = // - kMayHaveCaps_ | kMayHaveJoins_ | kButtCapIsSquare_ | // - kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_; + static constexpr int kAnySpecialGeometryMask = // + kMayHaveCaps | kMayHaveJoins | kButtCapIsSquare | // + kMayHaveDiagonalCaps | kMayHaveAcuteJoins; // clang-format off - static constexpr int kUsesAntiAlias_ = 1 << 10; - static constexpr int kUsesDither_ = 1 << 11; - static constexpr int kUsesAlpha_ = 1 << 12; - static constexpr int kUsesColor_ = 1 << 13; - static constexpr int kUsesBlend_ = 1 << 14; - static constexpr int kUsesShader_ = 1 << 15; - static constexpr int kUsesColorFilter_ = 1 << 16; - static constexpr int kUsesPathEffect_ = 1 << 17; - static constexpr int kUsesMaskFilter_ = 1 << 18; - static constexpr int kUsesImageFilter_ = 1 << 19; + static constexpr int kUsesAntiAlias = 1 << 10; + static constexpr int kUsesDither = 1 << 11; + static constexpr int kUsesAlpha = 1 << 12; + static constexpr int kUsesColor = 1 << 13; + static constexpr int kUsesBlend = 1 << 14; + static constexpr int kUsesShader = 1 << 15; + static constexpr int kUsesColorFilter = 1 << 16; + static constexpr int kUsesPathEffect = 1 << 17; + static constexpr int kUsesMaskFilter = 1 << 18; + static constexpr int kUsesImageFilter = 1 << 19; // Some ops have an optional paint argument. If the version // stored in the DisplayList ignores the paint, but there @@ -98,13 +98,13 @@ class DisplayListFlags { // a default paint object can be constructed when rendering // the op to carry information imposed from outside the // DisplayList (for example, the opacity override). - static constexpr int kIgnoresPaint_ = 1 << 30; + static constexpr int kIgnoresPaint = 1 << 30; // clang-format on - static constexpr int kAnyAttributeMask_ = // - kUsesAntiAlias_ | kUsesDither_ | kUsesAlpha_ | kUsesColor_ | kUsesBlend_ | - kUsesShader_ | kUsesColorFilter_ | kUsesPathEffect_ | kUsesMaskFilter_ | - kUsesImageFilter_; + static constexpr int kAnyAttributeMask = // + kUsesAntiAlias | kUsesDither | kUsesAlpha | kUsesColor | kUsesBlend | + kUsesShader | kUsesColorFilter | kUsesPathEffect | kUsesMaskFilter | + kUsesImageFilter; }; class DisplayListFlagsBase : protected DisplayListFlags { @@ -126,21 +126,21 @@ class DisplayListFlagsBase : protected DisplayListFlags { class DisplayListSpecialGeometryFlags : DisplayListFlagsBase { public: /// The geometry may have segments that end without closing the path. - constexpr bool may_have_end_caps() const { return has_any(kMayHaveCaps_); } + constexpr bool may_have_end_caps() const { return has_any(kMayHaveCaps); } /// The geometry may have segments connect non-continuously. - constexpr bool may_have_joins() const { return has_any(kMayHaveJoins_); } + constexpr bool may_have_joins() const { return has_any(kMayHaveJoins); } /// Mainly for drawPoints(PointMode) where Butt caps are rendered as squares. constexpr bool butt_cap_becomes_square() const { - return has_any(kButtCapIsSquare_); + return has_any(kButtCapIsSquare); } /// The geometry may have segments that end on a diagonal /// such that their end caps extend further than the default /// |strokeWidth * 0.5| margin around the geometry. constexpr bool may_have_diagonal_caps() const { - return has_any(kMayHaveDiagonalCaps_); + return has_any(kMayHaveDiagonalCaps); } /// The geometry may have segments that meet at vertices at @@ -148,13 +148,13 @@ class DisplayListSpecialGeometryFlags : DisplayListFlagsBase { /// further than the default |strokeWidth * 0.5| margin around /// the geometry. constexpr bool may_have_acute_joins() const { - return has_any(kMayHaveAcuteJoins_); + return has_any(kMayHaveAcuteJoins); } private: explicit constexpr DisplayListSpecialGeometryFlags(int flags) : DisplayListFlagsBase(flags) { - FML_DCHECK((flags & kAnySpecialGeometryMask_) == flags); + FML_DCHECK((flags & kAnySpecialGeometryMask) == flags); } const DisplayListSpecialGeometryFlags with(int extra) const { @@ -170,19 +170,19 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { const DlPathEffect* effect, bool is_stroked) const; - constexpr bool ignores_paint() const { return has_any(kIgnoresPaint_); } + constexpr bool ignores_paint() const { return has_any(kIgnoresPaint); } - constexpr bool applies_anti_alias() const { return has_any(kUsesAntiAlias_); } - constexpr bool applies_dither() const { return has_any(kUsesDither_); } - constexpr bool applies_color() const { return has_any(kUsesColor_); } - constexpr bool applies_alpha() const { return has_any(kUsesAlpha_); } + constexpr bool applies_anti_alias() const { return has_any(kUsesAntiAlias); } + constexpr bool applies_dither() const { return has_any(kUsesDither); } + constexpr bool applies_color() const { return has_any(kUsesColor); } + constexpr bool applies_alpha() const { return has_any(kUsesAlpha); } constexpr bool applies_alpha_or_color() const { - return has_any(kUsesAlpha_ | kUsesColor_); + return has_any(kUsesAlpha | kUsesColor); } /// The primitive dynamically determines whether it is a stroke or fill /// operation (or both) based on the setting of the |Style| attribute. - constexpr bool applies_style() const { return has_any(kIsDrawnGeometry_); } + constexpr bool applies_style() const { return has_any(kIsDrawnGeometry); } /// The primitive can use any of the stroke attributes, such as /// StrokeWidth, StrokeMiter, StrokeCap, or StrokeJoin. This /// method will return if the primitive is defined as one that @@ -193,34 +193,34 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { /// the style. // bool applies_stroke_attributes() const { return is_stroked(); } - constexpr bool applies_shader() const { return has_any(kUsesShader_); } + constexpr bool applies_shader() const { return has_any(kUsesShader); } /// The primitive honors the current DlColorFilter, including /// the related attribute InvertColors constexpr bool applies_color_filter() const { - return has_any(kUsesColorFilter_); + return has_any(kUsesColorFilter); } /// The primitive honors the DlBlendMode - constexpr bool applies_blend() const { return has_any(kUsesBlend_); } + constexpr bool applies_blend() const { return has_any(kUsesBlend); } constexpr bool applies_path_effect() const { - return has_any(kUsesPathEffect_); + return has_any(kUsesPathEffect); } /// The primitive honors the DlMaskFilter whether set using the /// filter object or using the convenience method |setMaskBlurFilter| constexpr bool applies_mask_filter() const { - return has_any(kUsesMaskFilter_); + return has_any(kUsesMaskFilter); } constexpr bool applies_image_filter() const { - return has_any(kUsesImageFilter_); + return has_any(kUsesImageFilter); } - constexpr bool is_geometric() const { return has_any(kIsAnyGeometryMask_); } - constexpr bool always_stroked() const { return has_any(kIsStrokedGeometry_); } + constexpr bool is_geometric() const { return has_any(kIsAnyGeometryMask); } + constexpr bool always_stroked() const { return has_any(kIsStrokedGeometry); } constexpr bool is_stroked(DlDrawStyle style = DlDrawStyle::kStroke) const { - return (has_any(kIsStrokedGeometry_) || - (style != DlDrawStyle::kFill && has_any(kIsDrawnGeometry_))); + return (has_any(kIsStrokedGeometry) || + (style != DlDrawStyle::kFill && has_any(kIsDrawnGeometry))); } - constexpr bool is_flood() const { return has_any(kFloodsSurface_); } + constexpr bool is_flood() const { return has_any(kFloodsSurface); } constexpr bool operator==(DisplayListAttributeFlags const& other) const { return flags_ == other.flags_; @@ -229,15 +229,15 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { private: explicit constexpr DisplayListAttributeFlags(int flags) : DisplayListFlagsBase(flags), - special_flags_(flags & kAnySpecialGeometryMask_) { - FML_DCHECK((flags & kIsAnyGeometryMask_) == kIsNonGeometric_ || - (flags & kIsAnyGeometryMask_) == kIsFilledGeometry_ || - (flags & kIsAnyGeometryMask_) == kIsStrokedGeometry_ || - (flags & kIsAnyGeometryMask_) == kIsDrawnGeometry_); - FML_DCHECK(((flags & kAnyAttributeMask_) == 0) != - ((flags & kIgnoresPaint_) == 0)); - FML_DCHECK((flags & kIsAnyGeometryMask_) != 0 || - (flags & kAnySpecialGeometryMask_) == 0); + special_flags_(flags & kAnySpecialGeometryMask) { + FML_DCHECK((flags & kIsAnyGeometryMask) == kIsNonGeometric || + (flags & kIsAnyGeometryMask) == kIsFilledGeometry || + (flags & kIsAnyGeometryMask) == kIsStrokedGeometry || + (flags & kIsAnyGeometryMask) == kIsDrawnGeometry); + FML_DCHECK(((flags & kAnyAttributeMask) == 0) != + ((flags & kIgnoresPaint) == 0)); + FML_DCHECK((flags & kIsAnyGeometryMask) != 0 || + (flags & kAnySpecialGeometryMask) == 0); } constexpr DisplayListAttributeFlags operator+(int extra) const { @@ -257,171 +257,171 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { class DisplayListOpFlags : DisplayListFlags { private: // Flags common to all primitives that apply colors - static constexpr int kBASE_PaintFlags_ = (kUsesDither_ | // - kUsesColor_ | // - kUsesAlpha_ | // - kUsesBlend_ | // - kUsesShader_ | // - kUsesColorFilter_ | // - kUsesImageFilter_); + static constexpr int kBasePaintFlags = (kUsesDither | // + kUsesColor | // + kUsesAlpha | // + kUsesBlend | // + kUsesShader | // + kUsesColorFilter | // + kUsesImageFilter); // Flags common to all primitives that stroke or fill - static constexpr int kBASE_StrokeOrFillFlags_ = (kIsDrawnGeometry_ | // - kUsesAntiAlias_ | // - kUsesMaskFilter_ | // - kUsesPathEffect_); + static constexpr int kBaseStrokeOrFillFlags = (kIsDrawnGeometry | // + kUsesAntiAlias | // + kUsesMaskFilter | // + kUsesPathEffect); // Flags common to primitives that stroke geometry - static constexpr int kBASE_StrokeFlags_ = (kIsStrokedGeometry_ | // - kUsesAntiAlias_ | // - kUsesMaskFilter_ | // - kUsesPathEffect_); + static constexpr int kBaseStrokeFlags = (kIsStrokedGeometry | // + kUsesAntiAlias | // + kUsesMaskFilter | // + kUsesPathEffect); // Flags common to primitives that render an image with paint attributes - static constexpr int kBASE_ImageFlags_ = (kIsNonGeometric_ | // - kUsesAlpha_ | // - kUsesDither_ | // - kUsesBlend_ | // - kUsesColorFilter_ | // - kUsesImageFilter_); + static constexpr int kBaseImageFlags = (kIsNonGeometric | // + kUsesAlpha | // + kUsesDither | // + kUsesBlend | // + kUsesColorFilter | // + kUsesImageFilter); public: static constexpr DisplayListAttributeFlags kSaveLayerFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kSaveLayerWithPaintFlags{ - kIsNonGeometric_ | // - kUsesAlpha_ | // - kUsesBlend_ | // - kUsesColorFilter_ | // - kUsesImageFilter_ // + kIsNonGeometric | // + kUsesAlpha | // + kUsesBlend | // + kUsesColorFilter | // + kUsesImageFilter // }; static constexpr DisplayListAttributeFlags kDrawColorFlags{ - kFloodsSurface_ | // - kIgnoresPaint_ // + kFloodsSurface | // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kDrawPaintFlags{ - kBASE_PaintFlags_ | // - kFloodsSurface_ // + kBasePaintFlags | // + kFloodsSurface // }; // Special case flags for horizonal and vertical lines static constexpr DisplayListAttributeFlags kDrawHVLineFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeFlags_ | // - kMayHaveCaps_ // + kBasePaintFlags | // + kBaseStrokeFlags | // + kMayHaveCaps // }; static constexpr DisplayListAttributeFlags kDrawLineFlags{ - kDrawHVLineFlags // - + kMayHaveDiagonalCaps_ // + kDrawHVLineFlags // + + kMayHaveDiagonalCaps // }; static constexpr DisplayListAttributeFlags kDrawRectFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveJoins_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags | // + kMayHaveJoins // }; static constexpr DisplayListAttributeFlags kDrawOvalFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags // }; static constexpr DisplayListAttributeFlags kDrawCircleFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags // }; static constexpr DisplayListAttributeFlags kDrawRRectFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags // }; static constexpr DisplayListAttributeFlags kDrawDRRectFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags // }; static constexpr DisplayListAttributeFlags kDrawPathFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveCaps_ | // - kMayHaveDiagonalCaps_ | // - kMayHaveJoins_ | // - kMayHaveAcuteJoins_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags | // + kMayHaveCaps | // + kMayHaveDiagonalCaps | // + kMayHaveJoins | // + kMayHaveAcuteJoins // }; static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveCaps_ | // - kMayHaveDiagonalCaps_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags | // + kMayHaveCaps | // + kMayHaveDiagonalCaps // }; static constexpr DisplayListAttributeFlags kDrawArcWithCenterFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveJoins_ | // - kMayHaveAcuteJoins_ // + kBasePaintFlags | // + kBaseStrokeOrFillFlags | // + kMayHaveJoins | // + kMayHaveAcuteJoins // }; static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeFlags_ | // - kMayHaveCaps_ | // - kButtCapIsSquare_ // + kBasePaintFlags | // + kBaseStrokeFlags | // + kMayHaveCaps | // + kButtCapIsSquare // }; static constexpr DisplayListAttributeFlags kDrawPointsAsLinesFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeFlags_ | // - kMayHaveCaps_ | // - kMayHaveDiagonalCaps_ // + kBasePaintFlags | // + kBaseStrokeFlags | // + kMayHaveCaps | // + kMayHaveDiagonalCaps // }; // Polygon mode just draws (count-1) separate lines, no joins static constexpr DisplayListAttributeFlags kDrawPointsAsPolygonFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeFlags_ | // - kMayHaveCaps_ | // - kMayHaveDiagonalCaps_ // + kBasePaintFlags | // + kBaseStrokeFlags | // + kMayHaveCaps | // + kMayHaveDiagonalCaps // }; static constexpr DisplayListAttributeFlags kDrawVerticesFlags{ - kIsNonGeometric_ | // - kUsesDither_ | // - kUsesAlpha_ | // - kUsesShader_ | // - kUsesBlend_ | // - kUsesColorFilter_ | // - kUsesImageFilter_ // + kIsNonGeometric | // + kUsesDither | // + kUsesAlpha | // + kUsesShader | // + kUsesBlend | // + kUsesColorFilter | // + kUsesImageFilter // }; static constexpr DisplayListAttributeFlags kDrawImageFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kDrawImageWithPaintFlags{ - kBASE_ImageFlags_ | // - kUsesAntiAlias_ | // - kUsesMaskFilter_ // + kBaseImageFlags | // + kUsesAntiAlias | // + kUsesMaskFilter // }; static constexpr DisplayListAttributeFlags kDrawImageRectFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kDrawImageRectWithPaintFlags{ - kBASE_ImageFlags_ | // - kUsesAntiAlias_ | // - kUsesMaskFilter_ // + kBaseImageFlags | // + kUsesAntiAlias | // + kUsesMaskFilter // }; static constexpr DisplayListAttributeFlags kDrawImageNineFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kDrawImageNineWithPaintFlags{ - kBASE_ImageFlags_ // + kBaseImageFlags // }; static constexpr DisplayListAttributeFlags kDrawAtlasFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kDrawAtlasWithPaintFlags{ - kBASE_ImageFlags_ // + kBaseImageFlags // }; static constexpr DisplayListAttributeFlags kDrawDisplayListFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; static constexpr DisplayListAttributeFlags kDrawTextBlobFlags{ - DisplayListAttributeFlags(kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveJoins_) // - - kUsesAntiAlias_ // + DisplayListAttributeFlags(kBasePaintFlags | // + kBaseStrokeOrFillFlags | // + kMayHaveJoins) // + - kUsesAntiAlias // }; static constexpr DisplayListAttributeFlags kDrawShadowFlags{ - kIgnoresPaint_ // + kIgnoresPaint // }; }; diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index a1c7835e43df9..64c54f583cf70 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -236,7 +236,7 @@ DEFINE_SET_CLEAR_DLATTR_OP(PathEffect, PathEffect, effect) struct SetImageColorSourceOp : DLOp { static const auto kType = DisplayListOpType::kSetImageColorSource; - SetImageColorSourceOp(const DlImageColorSource* source) + explicit SetImageColorSourceOp(const DlImageColorSource* source) : source(source->image(), source->horizontal_tile_mode(), source->vertical_tile_mode(), @@ -255,7 +255,8 @@ struct SetImageColorSourceOp : DLOp { struct SetRuntimeEffectColorSourceOp : DLOp { static const auto kType = DisplayListOpType::kSetRuntimeEffectColorSource; - SetRuntimeEffectColorSourceOp(const DlRuntimeEffectColorSource* source) + explicit SetRuntimeEffectColorSourceOp( + const DlRuntimeEffectColorSource* source) : source(source->runtime_effect(), source->samplers(), source->uniform_data()) {} @@ -296,7 +297,7 @@ struct SetSceneColorSourceOp : DLOp { struct SetSharedImageFilterOp : DLOp { static const auto kType = DisplayListOpType::kSetSharedImageFilter; - SetSharedImageFilterOp(const DlImageFilter* filter) + explicit SetSharedImageFilterOp(const DlImageFilter* filter) : filter(filter->shared()) {} const std::shared_ptr filter; @@ -316,7 +317,7 @@ struct SetSharedImageFilterOp : DLOp { struct SaveOpBase : DLOp { SaveOpBase() : options(), restore_index(0) {} - SaveOpBase(const SaveLayerOptions options) + explicit SaveOpBase(const SaveLayerOptions& options) : options(options), restore_index(0) {} // options parameter is only used by saveLayer operations, but since @@ -348,7 +349,7 @@ struct SaveOp final : SaveOpBase { struct SaveLayerOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayer; - explicit SaveLayerOp(const SaveLayerOptions options) : SaveOpBase(options) {} + explicit SaveLayerOp(const SaveLayerOptions& options) : SaveOpBase(options) {} void dispatch(DispatchContext& ctx) const { if (save_needed(ctx)) { @@ -360,7 +361,7 @@ struct SaveLayerOp final : SaveOpBase { struct SaveLayerBoundsOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayerBounds; - SaveLayerBoundsOp(const SaveLayerOptions options, const SkRect& rect) + SaveLayerBoundsOp(const SaveLayerOptions& options, const SkRect& rect) : SaveOpBase(options), rect(rect) {} const SkRect rect; @@ -375,7 +376,7 @@ struct SaveLayerBoundsOp final : SaveOpBase { struct SaveLayerBackdropOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayerBackdrop; - explicit SaveLayerBackdropOp(const SaveLayerOptions options, + explicit SaveLayerBackdropOp(const SaveLayerOptions& options, const DlImageFilter* backdrop) : SaveOpBase(options), backdrop(backdrop->shared()) {} @@ -397,7 +398,7 @@ struct SaveLayerBackdropOp final : SaveOpBase { struct SaveLayerBackdropBoundsOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayerBackdropBounds; - SaveLayerBackdropBoundsOp(const SaveLayerOptions options, + SaveLayerBackdropBoundsOp(const SaveLayerOptions& options, const SkRect& rect, const DlImageFilter* backdrop) : SaveOpBase(options), rect(rect), backdrop(backdrop->shared()) {} @@ -604,7 +605,7 @@ DEFINE_CLIP_SHAPE_OP(RRect, Difference) struct Clip##clipop##PathOp final : TransformClipOpBase { \ static const auto kType = DisplayListOpType::kClip##clipop##Path; \ \ - Clip##clipop##PathOp(SkPath path, bool is_aa) \ + Clip##clipop##PathOp(const SkPath& path, bool is_aa) \ : is_aa(is_aa), path(path) {} \ \ const bool is_aa; \ @@ -690,7 +691,7 @@ DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect) struct DrawPathOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawPath; - explicit DrawPathOp(SkPath path) : path(path) {} + explicit DrawPathOp(const SkPath& path) : path(path) {} const SkPath path; @@ -788,7 +789,7 @@ DEFINE_DRAW_POINTS_OP(Polygon, kPolygon); struct DrawVerticesOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawVertices; - DrawVerticesOp(DlBlendMode mode) : mode(mode) {} + explicit DrawVerticesOp(DlBlendMode mode) : mode(mode) {} const DlBlendMode mode; @@ -807,7 +808,7 @@ struct DrawVerticesOp final : DrawOpBase { struct name##Op final : DrawOpBase { \ static const auto kType = DisplayListOpType::k##name; \ \ - name##Op(const sk_sp image, \ + name##Op(const sk_sp& image, \ const SkPoint& point, \ DlImageSampling sampling) \ : point(point), sampling(sampling), image(std::move(image)) {} \ @@ -838,7 +839,7 @@ DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true) struct DrawImageRectOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawImageRect; - DrawImageRectOp(const sk_sp image, + DrawImageRectOp(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -849,7 +850,7 @@ struct DrawImageRectOp final : DrawOpBase { sampling(sampling), render_with_attributes(render_with_attributes), constraint(constraint), - image(std::move(image)) {} + image(image) {} const SkRect src; const SkRect dst; @@ -880,7 +881,7 @@ struct DrawImageRectOp final : DrawOpBase { struct name##Op final : DrawOpBase { \ static const auto kType = DisplayListOpType::k##name; \ \ - name##Op(const sk_sp image, \ + name##Op(const sk_sp& image, \ const SkIRect& center, \ const SkRect& dst, \ DlFilterMode mode) \ @@ -917,7 +918,7 @@ DEFINE_DRAW_IMAGE_NINE_OP(DrawImageNineWithAttr, true) // DlColor list only packs well if the count is even, otherwise there // can be 4 unusued bytes at the end. struct DrawAtlasBaseOp : DrawOpBase { - DrawAtlasBaseOp(const sk_sp atlas, + DrawAtlasBaseOp(const sk_sp& atlas, int count, DlBlendMode mode, DlImageSampling sampling, @@ -928,7 +929,7 @@ struct DrawAtlasBaseOp : DrawOpBase { has_colors(has_colors), render_with_attributes(render_with_attributes), sampling(sampling), - atlas(std::move(atlas)) {} + atlas(atlas) {} const int count; const uint16_t mode_index; @@ -960,7 +961,7 @@ struct DrawAtlasBaseOp : DrawOpBase { struct DrawAtlasOp final : DrawAtlasBaseOp { static const auto kType = DisplayListOpType::kDrawAtlas; - DrawAtlasOp(const sk_sp atlas, + DrawAtlasOp(const sk_sp& atlas, int count, DlBlendMode mode, DlImageSampling sampling, @@ -1001,7 +1002,7 @@ struct DrawAtlasOp final : DrawAtlasBaseOp { struct DrawAtlasCulledOp final : DrawAtlasBaseOp { static const auto kType = DisplayListOpType::kDrawAtlasCulled; - DrawAtlasCulledOp(const sk_sp atlas, + DrawAtlasCulledOp(const sk_sp& atlas, int count, DlBlendMode mode, DlImageSampling sampling, @@ -1045,9 +1046,9 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp { struct DrawDisplayListOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawDisplayList; - explicit DrawDisplayListOp(const sk_sp display_list, + explicit DrawDisplayListOp(const sk_sp& display_list, SkScalar opacity) - : opacity(opacity), display_list(std::move(display_list)) {} + : opacity(opacity), display_list(display_list) {} SkScalar opacity; const sk_sp display_list; @@ -1071,8 +1072,8 @@ struct DrawDisplayListOp final : DrawOpBase { struct DrawTextBlobOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawTextBlob; - DrawTextBlobOp(const sk_sp blob, SkScalar x, SkScalar y) - : x(x), y(y), blob(std::move(blob)) {} + DrawTextBlobOp(const sk_sp& blob, SkScalar x, SkScalar y) + : x(x), y(y), blob(blob) {} const SkScalar x; const SkScalar y; diff --git a/display_list/dl_paint.cc b/display_list/dl_paint.cc index 1389125d8c197..2ce1ec763277a 100644 --- a/display_list/dl_paint.cc +++ b/display_list/dl_paint.cc @@ -7,33 +7,33 @@ namespace flutter { DlPaint::DlPaint(DlColor color) - : blendMode_(static_cast(DlBlendMode::kDefaultMode)), - drawStyle_(static_cast(DlDrawStyle::kDefaultStyle)), - strokeCap_(static_cast(DlStrokeCap::kDefaultCap)), - strokeJoin_(static_cast(DlStrokeJoin::kDefaultJoin)), - isAntiAlias_(false), - isDither_(false), - isInvertColors_(false), + : blend_mode_(static_cast(DlBlendMode::kDefaultMode)), + draw_style_(static_cast(DlDrawStyle::kDefaultStyle)), + stroke_cap_(static_cast(DlStrokeCap::kDefaultCap)), + stroke_join_(static_cast(DlStrokeJoin::kDefaultJoin)), + is_anti_alias_(false), + is_dither_(false), + is_invert_colors_(false), color_(color), - strokeWidth_(kDefaultWidth), - strokeMiter_(kDefaultMiter) {} + stroke_width_(kDefaultWidth), + stroke_miter_(kDefaultMiter) {} bool DlPaint::operator==(DlPaint const& other) const { - return blendMode_ == other.blendMode_ && // - drawStyle_ == other.drawStyle_ && // - strokeCap_ == other.strokeCap_ && // - strokeJoin_ == other.strokeJoin_ && // - isAntiAlias_ == other.isAntiAlias_ && // - isDither_ == other.isDither_ && // - isInvertColors_ == other.isInvertColors_ && // - color_ == other.color_ && // - strokeWidth_ == other.strokeWidth_ && // - strokeMiter_ == other.strokeMiter_ && // - Equals(colorSource_, other.colorSource_) && // - Equals(colorFilter_, other.colorFilter_) && // - Equals(imageFilter_, other.imageFilter_) && // - Equals(maskFilter_, other.maskFilter_) && // - Equals(pathEffect_, other.pathEffect_); + return blend_mode_ == other.blend_mode_ && // + draw_style_ == other.draw_style_ && // + stroke_cap_ == other.stroke_cap_ && // + stroke_join_ == other.stroke_join_ && // + is_anti_alias_ == other.is_anti_alias_ && // + is_dither_ == other.is_dither_ && // + is_invert_colors_ == other.is_invert_colors_ && // + color_ == other.color_ && // + stroke_width_ == other.stroke_width_ && // + stroke_miter_ == other.stroke_miter_ && // + Equals(color_source_, other.color_source_) && // + Equals(color_filter_, other.color_filter_) && // + Equals(image_filter_, other.image_filter_) && // + Equals(mask_filter_, other.mask_filter_) && // + Equals(path_effect_, other.path_effect_); } const DlPaint DlPaint::kDefault; diff --git a/display_list/dl_paint.h b/display_list/dl_paint.h index 3d9220f57e3d6..4d09a39ce67fa 100644 --- a/display_list/dl_paint.h +++ b/display_list/dl_paint.h @@ -6,6 +6,7 @@ #define FLUTTER_DISPLAY_LIST_DL_PAINT_H_ #include +#include #include "flutter/display_list/dl_blend_mode.h" #include "flutter/display_list/dl_color.h" #include "flutter/display_list/effects/dl_color_filter.h" @@ -52,23 +53,23 @@ class DlPaint { static const DlPaint kDefault; DlPaint() : DlPaint(DlColor::kBlack()) {} - DlPaint(DlColor color); + explicit DlPaint(DlColor color); - bool isAntiAlias() const { return isAntiAlias_; } + bool isAntiAlias() const { return is_anti_alias_; } DlPaint& setAntiAlias(bool isAntiAlias) { - isAntiAlias_ = isAntiAlias; + is_anti_alias_ = isAntiAlias; return *this; } - bool isDither() const { return isDither_; } + bool isDither() const { return is_dither_; } DlPaint& setDither(bool isDither) { - isDither_ = isDither; + is_dither_ = isDither; return *this; } - bool isInvertColors() const { return isInvertColors_; } + bool isInvertColors() const { return is_invert_colors_; } DlPaint& setInvertColors(bool isInvertColors) { - isInvertColors_ = isInvertColors; + is_invert_colors_ = isInvertColors; return *this; } @@ -78,11 +79,8 @@ class DlPaint { return *this; } - uint8_t getAlpha() const { return color_.argb >> 24; } - DlPaint& setAlpha(uint8_t alpha) { - color_.argb = alpha << 24 | (color_.argb & 0x00FFFFFF); - return *this; - } + uint8_t getAlpha() const { return color_.argb() >> 24; } + DlPaint& setAlpha(uint8_t alpha) { return setColor(color_.withAlpha(alpha)); } SkScalar getOpacity() const { return color_.getAlphaF(); } DlPaint& setOpacity(SkScalar opacity) { setAlpha(SkScalarRoundToInt(opacity * 0xff)); @@ -90,111 +88,111 @@ class DlPaint { } DlBlendMode getBlendMode() const { - return static_cast(blendMode_); + return static_cast(blend_mode_); } DlPaint& setBlendMode(DlBlendMode mode) { - blendMode_ = static_cast(mode); + blend_mode_ = static_cast(mode); return *this; } DlDrawStyle getDrawStyle() const { - return static_cast(drawStyle_); + return static_cast(draw_style_); } DlPaint& setDrawStyle(DlDrawStyle style) { - drawStyle_ = static_cast(style); + draw_style_ = static_cast(style); return *this; } DlStrokeCap getStrokeCap() const { - return static_cast(strokeCap_); + return static_cast(stroke_cap_); } DlPaint& setStrokeCap(DlStrokeCap cap) { - strokeCap_ = static_cast(cap); + stroke_cap_ = static_cast(cap); return *this; } DlStrokeJoin getStrokeJoin() const { - return static_cast(strokeJoin_); + return static_cast(stroke_join_); } DlPaint& setStrokeJoin(DlStrokeJoin join) { - strokeJoin_ = static_cast(join); + stroke_join_ = static_cast(join); return *this; } - float getStrokeWidth() const { return strokeWidth_; } + float getStrokeWidth() const { return stroke_width_; } DlPaint& setStrokeWidth(float width) { - strokeWidth_ = width; + stroke_width_ = width; return *this; } - float getStrokeMiter() const { return strokeMiter_; } + float getStrokeMiter() const { return stroke_miter_; } DlPaint& setStrokeMiter(float miter) { - strokeMiter_ = miter; + stroke_miter_ = miter; return *this; } std::shared_ptr getColorSource() const { - return colorSource_; + return color_source_; } - const DlColorSource* getColorSourcePtr() const { return colorSource_.get(); } + const DlColorSource* getColorSourcePtr() const { return color_source_.get(); } DlPaint& setColorSource(std::shared_ptr source) { - colorSource_ = source; + color_source_ = std::move(source); return *this; } DlPaint& setColorSource(const DlColorSource* source) { - colorSource_ = source ? source->shared() : nullptr; + color_source_ = source ? source->shared() : nullptr; return *this; } std::shared_ptr getColorFilter() const { - return colorFilter_; + return color_filter_; } - const DlColorFilter* getColorFilterPtr() const { return colorFilter_.get(); } - DlPaint& setColorFilter(const std::shared_ptr filter) { - colorFilter_ = filter; + const DlColorFilter* getColorFilterPtr() const { return color_filter_.get(); } + DlPaint& setColorFilter(const std::shared_ptr& filter) { + color_filter_ = filter; return *this; } DlPaint& setColorFilter(const DlColorFilter* filter) { - colorFilter_ = filter ? filter->shared() : nullptr; + color_filter_ = filter ? filter->shared() : nullptr; return *this; } std::shared_ptr getImageFilter() const { - return imageFilter_; + return image_filter_; } - const DlImageFilter* getImageFilterPtr() const { return imageFilter_.get(); } - DlPaint& setImageFilter(const std::shared_ptr filter) { - imageFilter_ = filter; + const DlImageFilter* getImageFilterPtr() const { return image_filter_.get(); } + DlPaint& setImageFilter(const std::shared_ptr& filter) { + image_filter_ = filter; return *this; } DlPaint& setImageFilter(const DlImageFilter* filter) { - imageFilter_ = filter ? filter->shared() : nullptr; + image_filter_ = filter ? filter->shared() : nullptr; return *this; } std::shared_ptr getMaskFilter() const { - return maskFilter_; + return mask_filter_; } - const DlMaskFilter* getMaskFilterPtr() const { return maskFilter_.get(); } - DlPaint& setMaskFilter(std::shared_ptr filter) { - maskFilter_ = filter; + const DlMaskFilter* getMaskFilterPtr() const { return mask_filter_.get(); } + DlPaint& setMaskFilter(const std::shared_ptr& filter) { + mask_filter_ = filter; return *this; } DlPaint& setMaskFilter(const DlMaskFilter* filter) { - maskFilter_ = filter ? filter->shared() : nullptr; + mask_filter_ = filter ? filter->shared() : nullptr; return *this; } std::shared_ptr getPathEffect() const { - return pathEffect_; + return path_effect_; } - const DlPathEffect* getPathEffectPtr() const { return pathEffect_.get(); } - DlPaint& setPathEffect(std::shared_ptr pathEffect) { - pathEffect_ = pathEffect; + const DlPathEffect* getPathEffectPtr() const { return path_effect_.get(); } + DlPaint& setPathEffect(const std::shared_ptr& pathEffect) { + path_effect_ = pathEffect; return *this; } DlPaint& setPathEffect(const DlPathEffect* effect) { - pathEffect_ = effect ? effect->shared() : nullptr; + path_effect_ = effect ? effect->shared() : nullptr; return *this; } @@ -219,25 +217,25 @@ class DlPaint { union { struct { - unsigned blendMode_ : kBlendModeBits; - unsigned drawStyle_ : kDrawStyleBits; - unsigned strokeCap_ : kStrokeCapBits; - unsigned strokeJoin_ : kStrokeJoinBits; - unsigned isAntiAlias_ : 1; - unsigned isDither_ : 1; - unsigned isInvertColors_ : 1; + unsigned blend_mode_ : kBlendModeBits; + unsigned draw_style_ : kDrawStyleBits; + unsigned stroke_cap_ : kStrokeCapBits; + unsigned stroke_join_ : kStrokeJoinBits; + unsigned is_anti_alias_ : 1; + unsigned is_dither_ : 1; + unsigned is_invert_colors_ : 1; }; }; DlColor color_; - float strokeWidth_; - float strokeMiter_; - - std::shared_ptr colorSource_; - std::shared_ptr colorFilter_; - std::shared_ptr imageFilter_; - std::shared_ptr maskFilter_; - std::shared_ptr pathEffect_; + float stroke_width_; + float stroke_miter_; + + std::shared_ptr color_source_; + std::shared_ptr color_filter_; + std::shared_ptr image_filter_; + std::shared_ptr mask_filter_; + std::shared_ptr path_effect_; }; } // namespace flutter diff --git a/display_list/dl_paint_unittests.cc b/display_list/dl_paint_unittests.cc index a92082d0fefb2..2a34c466f60d1 100644 --- a/display_list/dl_paint_unittests.cc +++ b/display_list/dl_paint_unittests.cc @@ -42,14 +42,14 @@ TEST(DisplayListPaint, ConstructorDefaults) { EXPECT_EQ(paint, DlPaint()); EXPECT_EQ(paint, DlPaint(DlColor::kBlack())); - EXPECT_EQ(paint, DlPaint(0xFF000000)); + EXPECT_EQ(paint, DlPaint(DlColor(0xFF000000))); EXPECT_NE(paint, DlPaint().setAntiAlias(true)); EXPECT_NE(paint, DlPaint().setDither(true)); EXPECT_NE(paint, DlPaint().setInvertColors(true)); EXPECT_NE(paint, DlPaint().setColor(DlColor::kGreen())); EXPECT_NE(paint, DlPaint(DlColor::kGreen())); - EXPECT_NE(paint, DlPaint(0xFF00FF00)); + EXPECT_NE(paint, DlPaint(DlColor(0xFF00FF00))); EXPECT_NE(paint, DlPaint().setAlpha(0x7f)); EXPECT_NE(paint, DlPaint().setBlendMode(DlBlendMode::kDstIn)); EXPECT_NE(paint, DlPaint().setDrawStyle(DlDrawStyle::kStrokeAndFill)); diff --git a/display_list/effects/dl_color_filter.cc b/display_list/effects/dl_color_filter.cc index 99f4a7cdd8210..686484421561c 100644 --- a/display_list/effects/dl_color_filter.cc +++ b/display_list/effects/dl_color_filter.cc @@ -190,11 +190,11 @@ bool DlMatrixColorFilter::can_commute_with_opacity() const { } const std::shared_ptr - DlSrgbToLinearGammaColorFilter::instance = + DlSrgbToLinearGammaColorFilter::kInstance = std::make_shared(); const std::shared_ptr - DlLinearToSrgbGammaColorFilter::instance = + DlLinearToSrgbGammaColorFilter::kInstance = std::make_shared(); } // namespace flutter diff --git a/display_list/effects/dl_color_filter.h b/display_list/effects/dl_color_filter.h index a96517bb110db..42a4845e0a90c 100644 --- a/display_list/effects/dl_color_filter.h +++ b/display_list/effects/dl_color_filter.h @@ -63,7 +63,7 @@ class DlBlendColorFilter final : public DlColorFilter { : color_(color), mode_(mode) {} DlBlendColorFilter(const DlBlendColorFilter& filter) : DlBlendColorFilter(filter.color_, filter.mode_) {} - DlBlendColorFilter(const DlBlendColorFilter* filter) + explicit DlBlendColorFilter(const DlBlendColorFilter* filter) : DlBlendColorFilter(filter->color_, filter->mode_) {} static std::shared_ptr Make(DlColor color, DlBlendMode mode); @@ -113,12 +113,12 @@ class DlBlendColorFilter final : public DlColorFilter { // pixel data, the necessary pre<->non-pre conversions must be performed. class DlMatrixColorFilter final : public DlColorFilter { public: - DlMatrixColorFilter(const float matrix[20]) { + explicit DlMatrixColorFilter(const float matrix[20]) { memcpy(matrix_, matrix, sizeof(matrix_)); } DlMatrixColorFilter(const DlMatrixColorFilter& filter) : DlMatrixColorFilter(filter.matrix_) {} - DlMatrixColorFilter(const DlMatrixColorFilter* filter) + explicit DlMatrixColorFilter(const DlMatrixColorFilter* filter) : DlMatrixColorFilter(filter->matrix_) {} static std::shared_ptr Make(const float matrix[20]); @@ -155,12 +155,13 @@ class DlMatrixColorFilter final : public DlColorFilter { // gamma curve to the rendered pixels. class DlSrgbToLinearGammaColorFilter final : public DlColorFilter { public: - static const std::shared_ptr instance; + static const std::shared_ptr kInstance; DlSrgbToLinearGammaColorFilter() {} DlSrgbToLinearGammaColorFilter(const DlSrgbToLinearGammaColorFilter& filter) : DlSrgbToLinearGammaColorFilter() {} - DlSrgbToLinearGammaColorFilter(const DlSrgbToLinearGammaColorFilter* filter) + explicit DlSrgbToLinearGammaColorFilter( + const DlSrgbToLinearGammaColorFilter* filter) : DlSrgbToLinearGammaColorFilter() {} DlColorFilterType type() const override { @@ -170,7 +171,7 @@ class DlSrgbToLinearGammaColorFilter final : public DlColorFilter { bool modifies_transparent_black() const override { return false; } bool can_commute_with_opacity() const override { return true; } - std::shared_ptr shared() const override { return instance; } + std::shared_ptr shared() const override { return kInstance; } protected: bool equals_(const DlColorFilter& other) const override { @@ -186,12 +187,13 @@ class DlSrgbToLinearGammaColorFilter final : public DlColorFilter { // to the rendered pixels. class DlLinearToSrgbGammaColorFilter final : public DlColorFilter { public: - static const std::shared_ptr instance; + static const std::shared_ptr kInstance; DlLinearToSrgbGammaColorFilter() {} DlLinearToSrgbGammaColorFilter(const DlLinearToSrgbGammaColorFilter& filter) : DlLinearToSrgbGammaColorFilter() {} - DlLinearToSrgbGammaColorFilter(const DlLinearToSrgbGammaColorFilter* filter) + explicit DlLinearToSrgbGammaColorFilter( + const DlLinearToSrgbGammaColorFilter* filter) : DlLinearToSrgbGammaColorFilter() {} DlColorFilterType type() const override { @@ -201,7 +203,7 @@ class DlLinearToSrgbGammaColorFilter final : public DlColorFilter { bool modifies_transparent_black() const override { return false; } bool can_commute_with_opacity() const override { return true; } - std::shared_ptr shared() const override { return instance; } + std::shared_ptr shared() const override { return kInstance; } protected: bool equals_(const DlColorFilter& other) const override { diff --git a/display_list/effects/dl_color_filter_unittests.cc b/display_list/effects/dl_color_filter_unittests.cc index c82ca6eafef97..157348f3cf3ae 100644 --- a/display_list/effects/dl_color_filter_unittests.cc +++ b/display_list/effects/dl_color_filter_unittests.cc @@ -135,7 +135,7 @@ TEST(DisplayListColorFilter, SrgbToLinearEquals) { DlSrgbToLinearGammaColorFilter filter1; DlSrgbToLinearGammaColorFilter filter2; TestEquals(filter1, filter2); - TestEquals(filter1, *DlSrgbToLinearGammaColorFilter::instance); + TestEquals(filter1, *DlSrgbToLinearGammaColorFilter::kInstance); } TEST(DisplayListColorFilter, LinearToSrgbConstructor) { @@ -152,7 +152,7 @@ TEST(DisplayListColorFilter, LinearToSrgbEquals) { DlLinearToSrgbGammaColorFilter filter1; DlLinearToSrgbGammaColorFilter filter2; TestEquals(filter1, filter2); - TestEquals(filter1, *DlLinearToSrgbGammaColorFilter::instance); + TestEquals(filter1, *DlLinearToSrgbGammaColorFilter::kInstance); } } // namespace testing diff --git a/display_list/effects/dl_color_source.h b/display_list/effects/dl_color_source.h index 11a141da9c905..094006eb3ef40 100644 --- a/display_list/effects/dl_color_source.h +++ b/display_list/effects/dl_color_source.h @@ -178,7 +178,7 @@ class DlColorSource : public DlAttribute { class DlColorColorSource final : public DlColorSource { public: - DlColorColorSource(DlColor color) : color_(color) {} + explicit DlColorColorSource(DlColor color) : color_(color) {} bool isUIThreadSafe() const override { return true; } @@ -191,7 +191,7 @@ class DlColorColorSource final : public DlColorSource { DlColorSourceType type() const override { return DlColorSourceType::kColor; } size_t size() const override { return sizeof(*this); } - bool is_opaque() const override { return (color_ >> 24) == 255; } + bool is_opaque() const override { return color_.getAlpha() == 255; } DlColor color() const { return color_; } @@ -216,7 +216,7 @@ class DlMatrixColorSourceBase : public DlColorSource { } protected: - DlMatrixColorSourceBase(const SkMatrix* matrix) + explicit DlMatrixColorSourceBase(const SkMatrix* matrix) : matrix_(matrix ? *matrix : SkMatrix::I()) {} private: @@ -232,7 +232,7 @@ class DlImageColorSource final : public SkRefCnt, DlImageSampling sampling = DlImageSampling::kLinear, const SkMatrix* matrix = nullptr) : DlMatrixColorSourceBase(matrix), - image_(image), + image_(std::move(image)), horizontal_tile_mode_(horizontal_tile_mode), vertical_tile_mode_(vertical_tile_mode), sampling_(sampling) {} @@ -290,7 +290,7 @@ class DlGradientColorSourceBase : public DlMatrixColorSourceBase { } const DlColor* my_colors = colors(); for (uint32_t i = 0; i < stop_count_; i++) { - if ((my_colors[i] >> 24) < 255) { + if (my_colors[i].getAlpha() < 255) { return false; } } @@ -405,7 +405,8 @@ class DlLinearGradientColorSource final : public DlGradientColorSourceBase { store_color_stops(this + 1, colors, stops); } - DlLinearGradientColorSource(const DlLinearGradientColorSource* source) + explicit DlLinearGradientColorSource( + const DlLinearGradientColorSource* source) : DlGradientColorSourceBase(source->stop_count(), source->tile_mode(), source->matrix_ptr()), @@ -468,7 +469,8 @@ class DlRadialGradientColorSource final : public DlGradientColorSourceBase { store_color_stops(this + 1, colors, stops); } - DlRadialGradientColorSource(const DlRadialGradientColorSource* source) + explicit DlRadialGradientColorSource( + const DlRadialGradientColorSource* source) : DlGradientColorSourceBase(source->stop_count(), source->tile_mode(), source->matrix_ptr()), @@ -540,7 +542,8 @@ class DlConicalGradientColorSource final : public DlGradientColorSourceBase { store_color_stops(this + 1, colors, stops); } - DlConicalGradientColorSource(const DlConicalGradientColorSource* source) + explicit DlConicalGradientColorSource( + const DlConicalGradientColorSource* source) : DlGradientColorSourceBase(source->stop_count(), source->tile_mode(), source->matrix_ptr()), @@ -610,7 +613,7 @@ class DlSweepGradientColorSource final : public DlGradientColorSourceBase { store_color_stops(this + 1, colors, stops); } - DlSweepGradientColorSource(const DlSweepGradientColorSource* source) + explicit DlSweepGradientColorSource(const DlSweepGradientColorSource* source) : DlGradientColorSourceBase(source->stop_count(), source->tile_mode(), source->matrix_ptr()), diff --git a/display_list/effects/dl_color_source_unittests.cc b/display_list/effects/dl_color_source_unittests.cc index 96b2cc0f6ad9c..6fc0aac4cddb4 100644 --- a/display_list/effects/dl_color_source_unittests.cc +++ b/display_list/effects/dl_color_source_unittests.cc @@ -5,6 +5,7 @@ #include #include +#include "display_list/dl_color.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/effects/dl_color_source.h" #include "flutter/display_list/effects/dl_runtime_effect.h" @@ -86,17 +87,17 @@ static constexpr SkPoint kTestPoints2[2] = { }; TEST(DisplayListColorSource, ColorConstructor) { - DlColorColorSource source(SK_ColorRED); + DlColorColorSource source(DlColor::kRed()); } TEST(DisplayListColorSource, ColorShared) { - DlColorColorSource source(SK_ColorRED); + DlColorColorSource source(DlColor::kRed()); ASSERT_NE(source.shared().get(), &source); ASSERT_EQ(*source.shared(), source); } TEST(DisplayListColorSource, ColorAsColor) { - DlColorColorSource source(SK_ColorRED); + DlColorColorSource source(DlColor::kRed()); ASSERT_NE(source.asColor(), nullptr); ASSERT_EQ(source.asColor(), &source); @@ -109,26 +110,26 @@ TEST(DisplayListColorSource, ColorAsColor) { } TEST(DisplayListColorSource, ColorContents) { - DlColorColorSource source(SK_ColorRED); - ASSERT_EQ(source.color(), SK_ColorRED); + DlColorColorSource source(DlColor::kRed()); + ASSERT_EQ(source.color(), DlColor::kRed()); ASSERT_EQ(source.is_opaque(), true); for (int i = 0; i < 255; i++) { SkColor alpha_color = SkColorSetA(SK_ColorRED, i); - DlColorColorSource alpha_source(alpha_color); + auto const alpha_source = DlColorColorSource(DlColor(alpha_color)); ASSERT_EQ(alpha_source.color(), alpha_color); ASSERT_EQ(alpha_source.is_opaque(), false); } } TEST(DisplayListColorSource, ColorEquals) { - DlColorColorSource source1(SK_ColorRED); - DlColorColorSource source2(SK_ColorRED); + DlColorColorSource source1(DlColor::kRed()); + DlColorColorSource source2(DlColor::kRed()); TestEquals(source1, source2); } TEST(DisplayListColorSource, ColorNotEquals) { - DlColorColorSource source1(SK_ColorRED); - DlColorColorSource source2(SK_ColorBLUE); + DlColorColorSource source1(DlColor::kRed()); + DlColorColorSource source2(DlColor::kBlue()); TestNotEquals(source1, source2, "Color differs"); } diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index f7350e8bd767d..802f619b4c5af 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_DISPLAY_LIST_EFFECTS_DL_IMAGE_FILTER_H_ #define FLUTTER_DISPLAY_LIST_EFFECTS_DL_IMAGE_FILTER_H_ +#include + #include "flutter/display_list/dl_attributes.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/dl_tile_mode.h" @@ -223,7 +225,7 @@ class DlBlurImageFilter final : public DlImageFilter { : DlBlurImageFilter(filter->sigma_x_, filter->sigma_y_, filter->tile_mode_) {} - explicit DlBlurImageFilter(const DlBlurImageFilter& filter) + DlBlurImageFilter(const DlBlurImageFilter& filter) : DlBlurImageFilter(&filter) {} static std::shared_ptr Make(SkScalar sigma_x, @@ -295,7 +297,7 @@ class DlDilateImageFilter final : public DlImageFilter { : radius_x_(radius_x), radius_y_(radius_y) {} explicit DlDilateImageFilter(const DlDilateImageFilter* filter) : DlDilateImageFilter(filter->radius_x_, filter->radius_y_) {} - explicit DlDilateImageFilter(const DlDilateImageFilter& filter) + DlDilateImageFilter(const DlDilateImageFilter& filter) : DlDilateImageFilter(&filter) {} static std::shared_ptr Make(SkScalar radius_x, @@ -359,7 +361,7 @@ class DlErodeImageFilter final : public DlImageFilter { : radius_x_(radius_x), radius_y_(radius_y) {} explicit DlErodeImageFilter(const DlErodeImageFilter* filter) : DlErodeImageFilter(filter->radius_x_, filter->radius_y_) {} - explicit DlErodeImageFilter(const DlErodeImageFilter& filter) + DlErodeImageFilter(const DlErodeImageFilter& filter) : DlErodeImageFilter(&filter) {} static std::shared_ptr Make(SkScalar radius_x, @@ -423,7 +425,7 @@ class DlMatrixImageFilter final : public DlImageFilter { : matrix_(matrix), sampling_(sampling) {} explicit DlMatrixImageFilter(const DlMatrixImageFilter* filter) : DlMatrixImageFilter(filter->matrix_, filter->sampling_) {} - explicit DlMatrixImageFilter(const DlMatrixImageFilter& filter) + DlMatrixImageFilter(const DlMatrixImageFilter& filter) : DlMatrixImageFilter(&filter) {} static std::shared_ptr Make(const SkMatrix& matrix, @@ -510,7 +512,7 @@ class DlComposeImageFilter final : public DlImageFilter { : DlComposeImageFilter(&outer, &inner) {} explicit DlComposeImageFilter(const DlComposeImageFilter* filter) : DlComposeImageFilter(filter->outer_, filter->inner_) {} - explicit DlComposeImageFilter(const DlComposeImageFilter& filter) + DlComposeImageFilter(const DlComposeImageFilter& filter) : DlComposeImageFilter(&filter) {} static std::shared_ptr Make( @@ -586,11 +588,11 @@ class DlColorFilterImageFilter final : public DlImageFilter { : color_filter_(filter.shared()) {} explicit DlColorFilterImageFilter(const DlColorFilterImageFilter* filter) : DlColorFilterImageFilter(filter->color_filter_) {} - explicit DlColorFilterImageFilter(const DlColorFilterImageFilter& filter) + DlColorFilterImageFilter(const DlColorFilterImageFilter& filter) : DlColorFilterImageFilter(&filter) {} static std::shared_ptr Make( - std::shared_ptr filter) { + const std::shared_ptr& filter) { if (filter) { return std::make_shared(filter); } @@ -664,7 +666,7 @@ class DlLocalMatrixImageFilter final : public DlImageFilter { public: explicit DlLocalMatrixImageFilter(const SkMatrix& matrix, std::shared_ptr filter) - : matrix_(matrix), image_filter_(filter) {} + : matrix_(matrix), image_filter_(std::move(filter)) {} explicit DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter* filter) : DlLocalMatrixImageFilter(filter->matrix_, filter->image_filter_) {} DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter& filter) diff --git a/display_list/effects/dl_mask_filter.h b/display_list/effects/dl_mask_filter.h index 0749048caec60..d39839565ad2e 100644 --- a/display_list/effects/dl_mask_filter.h +++ b/display_list/effects/dl_mask_filter.h @@ -46,7 +46,7 @@ class DlBlurMaskFilter final : public DlMaskFilter { : style_(style), sigma_(sigma), respect_ctm_(respect_ctm) {} DlBlurMaskFilter(const DlBlurMaskFilter& filter) : DlBlurMaskFilter(filter.style_, filter.sigma_, filter.respect_ctm_) {} - DlBlurMaskFilter(const DlBlurMaskFilter* filter) + explicit DlBlurMaskFilter(const DlBlurMaskFilter* filter) : DlBlurMaskFilter(filter->style_, filter->sigma_, filter->respect_ctm_) { } diff --git a/display_list/effects/dl_path_effect.h b/display_list/effects/dl_path_effect.h index 0f9d0be60fa54..9c874fc203b3f 100644 --- a/display_list/effects/dl_path_effect.h +++ b/display_list/effects/dl_path_effect.h @@ -104,7 +104,7 @@ class DlDashPathEffect final : public DlPathEffect { } } - DlDashPathEffect(const DlDashPathEffect* dash_effect) + explicit DlDashPathEffect(const DlDashPathEffect* dash_effect) : DlDashPathEffect(dash_effect->intervals(), dash_effect->count_, dash_effect->phase_) {} diff --git a/display_list/geometry/dl_rtree.cc b/display_list/geometry/dl_rtree.cc index a4a805957bcc3..1598f505e88e1 100644 --- a/display_list/geometry/dl_rtree.cc +++ b/display_list/geometry/dl_rtree.cc @@ -217,7 +217,7 @@ const SkRect& DlRTree::bounds() const { if (!nodes_.empty()) { return nodes_.back().bounds; } else { - return empty_; + return kEmpty; } } diff --git a/display_list/geometry/dl_rtree.h b/display_list/geometry/dl_rtree.h index f12486edcce76..efda1613e38b1 100644 --- a/display_list/geometry/dl_rtree.h +++ b/display_list/geometry/dl_rtree.h @@ -96,7 +96,7 @@ class DlRTree : public SkRefCnt { const SkRect& bounds(int result_index) const { return (result_index >= 0 && result_index < leaf_count_) ? nodes_[result_index].bounds - : empty_; + : kEmpty; } /// Returns the bytes used by the object and all of its node data. @@ -136,7 +136,7 @@ class DlRTree : public SkRefCnt { } private: - static constexpr SkRect empty_ = SkRect::MakeEmpty(); + static constexpr SkRect kEmpty = SkRect::MakeEmpty(); void search(const Node& parent, const SkRect& query, diff --git a/display_list/image/dl_image.h b/display_list/image/dl_image.h index bc4a8602545ad..137fe03ac7acb 100644 --- a/display_list/image/dl_image.h +++ b/display_list/image/dl_image.h @@ -131,7 +131,9 @@ class DlImage : public SkRefCnt { bool Equals(const DlImage& other) const { return Equals(&other); } - bool Equals(sk_sp other) const { return Equals(other.get()); } + bool Equals(const sk_sp& other) const { + return Equals(other.get()); + } protected: DlImage(); diff --git a/display_list/image/dl_image_skia.h b/display_list/image/dl_image_skia.h index a29985d095929..03bcfa144a83a 100644 --- a/display_list/image/dl_image_skia.h +++ b/display_list/image/dl_image_skia.h @@ -12,7 +12,7 @@ namespace flutter { class DlImageSkia final : public DlImage { public: - DlImageSkia(sk_sp image); + explicit DlImageSkia(sk_sp image); // |DlImage| ~DlImageSkia() override; diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc index 34270bd88018d..2699a46bd7078 100644 --- a/display_list/skia/dl_sk_canvas.cc +++ b/display_list/skia/dl_sk_canvas.cc @@ -187,7 +187,7 @@ void DlSkCanvasAdapter::DrawPaint(const DlPaint& paint) { } void DlSkCanvasAdapter::DrawColor(DlColor color, DlBlendMode mode) { - delegate_->drawColor(color, ToSk(mode)); + delegate_->drawColor(ToSk(color), ToSk(mode)); } void DlSkCanvasAdapter::DrawLine(const SkPoint& p0, diff --git a/display_list/skia/dl_sk_conversions.cc b/display_list/skia/dl_sk_conversions.cc index 16f5fc710a343..2e7a42f5f91a4 100644 --- a/display_list/skia/dl_sk_conversions.cc +++ b/display_list/skia/dl_sk_conversions.cc @@ -23,7 +23,7 @@ SkPaint ToSk(const DlPaint& paint, bool force_stroke) { SkPaint sk_paint; sk_paint.setAntiAlias(paint.isAntiAlias()); - sk_paint.setColor(paint.getColor()); + sk_paint.setColor(ToSk(paint.getColor())); sk_paint.setBlendMode(ToSk(paint.getBlendMode())); sk_paint.setStyle(force_stroke ? SkPaint::kStroke_Style : ToSk(paint.getDrawStyle())); @@ -76,7 +76,7 @@ sk_sp ToSk(const DlColorSource* source) { case DlColorSourceType::kColor: { const DlColorColorSource* color_source = source->asColor(); FML_DCHECK(color_source != nullptr); - return SkShaders::Color(color_source->color()); + return SkShaders::Color(ToSk(color_source->color())); } case DlColorSourceType::kImage: { const DlImageColorSource* image_source = source->asImage(); @@ -240,7 +240,7 @@ sk_sp ToSk(const DlColorFilter* filter) { case DlColorFilterType::kBlend: { const DlBlendColorFilter* blend_filter = filter->asBlend(); FML_DCHECK(blend_filter != nullptr); - return SkColorFilters::Blend(blend_filter->color(), + return SkColorFilters::Blend(ToSk(blend_filter->color()), ToSk(blend_filter->mode())); } case DlColorFilterType::kMatrix: { diff --git a/display_list/skia/dl_sk_conversions.h b/display_list/skia/dl_sk_conversions.h index a1a9f6ce838b8..a162f8e65d3a1 100644 --- a/display_list/skia/dl_sk_conversions.h +++ b/display_list/skia/dl_sk_conversions.h @@ -16,6 +16,12 @@ inline SkBlendMode ToSk(DlBlendMode mode) { return static_cast(mode); } +inline SkColor ToSk(DlColor color) { + // This is safe because both SkColor and DlColor are backed by ARGB uint32_t. + // See dl_sk_conversions_unittests.cc. + return reinterpret_cast(color); +} + inline SkPaint::Style ToSk(DlDrawStyle style) { return static_cast(style); } diff --git a/display_list/skia/dl_sk_conversions_unittests.cc b/display_list/skia/dl_sk_conversions_unittests.cc index 1bae18a3ca1dd..be782181d994e 100644 --- a/display_list/skia/dl_sk_conversions_unittests.cc +++ b/display_list/skia/dl_sk_conversions_unittests.cc @@ -26,6 +26,21 @@ TEST(DisplayListImageFilter, LocalImageSkiaNull) { ASSERT_EQ(ToSk(dl_local_matrix_filter), nullptr); } +TEST(DisplayListSkConversions, ToSkColor) { + // Red + ASSERT_EQ(ToSk(DlColor::kRed()), SK_ColorRED); + + // Green + ASSERT_EQ(ToSk(DlColor::kGreen()), SK_ColorGREEN); + + // Blue + ASSERT_EQ(ToSk(DlColor::kBlue()), SK_ColorBLUE); + + // Half transparent grey + auto const grey_hex_half_opaque = 0x7F999999; + ASSERT_EQ(ToSk(DlColor(grey_hex_half_opaque)), SkColor(grey_hex_half_opaque)); +} + TEST(DisplayListSkConversions, ToSkTileMode) { ASSERT_EQ(ToSk(DlTileMode::kClamp), SkTileMode::kClamp); ASSERT_EQ(ToSk(DlTileMode::kRepeat), SkTileMode::kRepeat); @@ -136,7 +151,8 @@ TEST(DisplayListSkConversions, ToSkBlendMode) { TEST(DisplayListSkConversions, BlendColorFilterModifiesTransparency) { auto test_mode_color = [](DlBlendMode mode, DlColor color) { std::stringstream desc_str; - desc_str << "blend[" << static_cast(mode) << ", " << color << "]"; + desc_str << "blend[" << static_cast(mode) << ", " << color.argb() + << "]"; std::string desc = desc_str.str(); DlBlendColorFilter filter(color, mode); if (filter.modifies_transparent_black()) { diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc index f8cc16d1151c1..c05d23ec1d376 100644 --- a/display_list/skia/dl_sk_dispatcher.cc +++ b/display_list/skia/dl_sk_dispatcher.cc @@ -141,7 +141,7 @@ void DlSkCanvasDispatcher::drawPaint() { void DlSkCanvasDispatcher::drawColor(DlColor color, DlBlendMode mode) { // SkCanvas::drawColor(SkColor) does the following conversion anyway // We do it here manually to increase precision on applying opacity - SkColor4f color4f = SkColor4f::FromColor(color); + SkColor4f color4f = SkColor4f::FromColor(ToSk(color)); color4f.fA *= opacity(); canvas_->drawColor(color4f, ToSk(mode)); } @@ -290,8 +290,9 @@ void DlSkCanvasDispatcher::DrawShadow(SkCanvas* canvas, ? SkShadowFlags::kTransparentOccluder_ShadowFlag : SkShadowFlags::kNone_ShadowFlag; flags |= SkShadowFlags::kDirectionalLight_ShadowFlag; - SkColor in_ambient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color)); - SkColor in_spot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color)); + SkColor in_ambient = + SkColorSetA(ToSk(color), kAmbientAlpha * color.getAlpha()); + SkColor in_spot = SkColorSetA(ToSk(color), kSpotAlpha * color.getAlpha()); SkColor ambient_color, spot_color; SkShadowUtils::ComputeTonalColors(in_ambient, in_spot, &ambient_color, &spot_color); diff --git a/display_list/skia/dl_sk_paint_dispatcher.cc b/display_list/skia/dl_sk_paint_dispatcher.cc index 992b5146a6c13..43562405d4e23 100644 --- a/display_list/skia/dl_sk_paint_dispatcher.cc +++ b/display_list/skia/dl_sk_paint_dispatcher.cc @@ -63,8 +63,8 @@ void DlSkPaintDispatchHelper::setStrokeMiter(SkScalar limit) { paint_.setStrokeMiter(limit); } void DlSkPaintDispatchHelper::setColor(DlColor color) { - current_color_ = color; - paint_.setColor(color); + current_color_ = ToSk(color); + paint_.setColor(ToSk(color)); if (has_opacity()) { paint_.setAlphaf(paint_.getAlphaf() * opacity()); } diff --git a/display_list/skia/dl_sk_paint_dispatcher.h b/display_list/skia/dl_sk_paint_dispatcher.h index 33c59ff29bb14..dcbde11fba764 100644 --- a/display_list/skia/dl_sk_paint_dispatcher.h +++ b/display_list/skia/dl_sk_paint_dispatcher.h @@ -6,6 +6,7 @@ #define FLUTTER_DISPLAY_LIST_SKIA_DL_SK_PAINT_DISPATCHER_H_ #include "flutter/display_list/dl_op_receiver.h" +#include "flutter/display_list/skia/dl_sk_types.h" namespace flutter { @@ -14,7 +15,7 @@ namespace flutter { // which can be accessed at any time via paint(). class DlSkPaintDispatchHelper : public virtual DlOpReceiver { public: - DlSkPaintDispatchHelper(SkScalar opacity = SK_Scalar1) + explicit DlSkPaintDispatchHelper(SkScalar opacity = SK_Scalar1) : current_color_(SK_ColorBLACK), opacity_(opacity) { if (opacity < SK_Scalar1) { paint_.setAlphaf(opacity); @@ -75,7 +76,7 @@ class DlSkPaintDispatchHelper : public virtual DlOpReceiver { sk_sp makeColorFilter() const; struct SaveInfo { - SaveInfo(SkScalar opacity) : opacity(opacity) {} + explicit SaveInfo(SkScalar opacity) : opacity(opacity) {} SkScalar opacity; }; @@ -84,7 +85,7 @@ class DlSkPaintDispatchHelper : public virtual DlOpReceiver { void set_opacity(SkScalar opacity) { if (opacity_ != opacity) { opacity_ = opacity; - setColor(current_color_); + setColor(DlColor(current_color_)); } } diff --git a/display_list/skia/dl_sk_paint_dispatcher_unittests.cc b/display_list/skia/dl_sk_paint_dispatcher_unittests.cc index 514a3a9d10018..2b638845757f9 100644 --- a/display_list/skia/dl_sk_paint_dispatcher_unittests.cc +++ b/display_list/skia/dl_sk_paint_dispatcher_unittests.cc @@ -22,7 +22,8 @@ class MockDispatchHelper final : public virtual DlOpReceiver, void restore() override { DlSkPaintDispatchHelper::restore_opacity(); } }; -static const DlColor kTestColors[2] = {0xFF000000, 0xFFFFFFFF}; +static const DlColor kTestColors[2] = {DlColor(0xFF000000), + DlColor(0xFFFFFFFF)}; static const float kTestStops[2] = {0.0f, 1.0f}; static const auto kTestLinearGradient = DlColorSource::MakeLinear(SkPoint::Make(0.0f, 0.0f), diff --git a/display_list/skia/dl_sk_types.h b/display_list/skia/dl_sk_types.h index bd359b64f5b34..6f66c0629e68a 100644 --- a/display_list/skia/dl_sk_types.h +++ b/display_list/skia/dl_sk_types.h @@ -15,6 +15,7 @@ #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPathEffect.h" #include "third_party/skia/include/core/SkPicture.h" diff --git a/display_list/testing/BUILD.gn b/display_list/testing/BUILD.gn index e1965e8c4ad8f..1839219b1e8e3 100644 --- a/display_list/testing/BUILD.gn +++ b/display_list/testing/BUILD.gn @@ -69,6 +69,10 @@ source_set("display_list_surface_provider") { "//flutter/testing:testing_lib", ] + if (is_mac) { + deps += [ "//flutter/impeller/golden_tests:metal_screenshot" ] + } + public_configs = [ ":surface_provider_config" ] if (is_android) { @@ -99,6 +103,9 @@ source_set("display_list_surface_provider") { "dl_test_surface_metal.cc", "dl_test_surface_metal.h", ] - deps += [ "//flutter/testing:metal" ] + deps += [ + "//flutter/impeller/display_list", + "//flutter/testing:metal", + ] } } diff --git a/display_list/testing/dl_rendering_unittests.cc b/display_list/testing/dl_rendering_unittests.cc index 9f25788384caf..26421f9e1efb3 100644 --- a/display_list/testing/dl_rendering_unittests.cc +++ b/display_list/testing/dl_rendering_unittests.cc @@ -13,16 +13,22 @@ #include "flutter/display_list/skia/dl_sk_dispatcher.h" #include "flutter/display_list/testing/dl_test_surface_provider.h" #include "flutter/display_list/utils/dl_comparable.h" +#include "flutter/fml/file.h" #include "flutter/fml/math.h" #include "flutter/testing/display_list_testing.h" #include "flutter/testing/testing.h" +#ifdef IMPELLER_SUPPORTS_RENDERING +#include "flutter/impeller/typographer/backends/skia/text_frame_skia.h" +#endif // IMPELLER_SUPPORTS_RENDERING #include "third_party/skia/include/core/SkBBHFactory.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkStream.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkImageFilters.h" +#include "third_party/skia/include/encode/SkPngEncoder.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/GrRecordingContext.h" #include "third_party/skia/include/gpu/GrTypes.h" @@ -156,6 +162,63 @@ class SkImageSampling { SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}); }; +static void DrawCheckerboard(DlCanvas* canvas) { + DlPaint p0, p1; + p0.setDrawStyle(DlDrawStyle::kFill); + p0.setColor(DlColor(0xff00fe00)); // off-green + p1.setDrawStyle(DlDrawStyle::kFill); + p1.setColor(DlColor::kBlue()); + // Some pixels need some transparency for DstIn testing + p1.setAlpha(128); + int cbdim = 5; + int width = canvas->GetBaseLayerSize().width(); + int height = canvas->GetBaseLayerSize().height(); + for (int y = 0; y < width; y += cbdim) { + for (int x = 0; x < height; x += cbdim) { + DlPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1; + canvas->DrawRect(SkRect::MakeXYWH(x, y, cbdim, cbdim), cellp); + } + } +} + +static void DrawCheckerboard(SkCanvas* canvas) { + DlSkCanvasAdapter dl_canvas(canvas); + DrawCheckerboard(&dl_canvas); +} + +static std::shared_ptr MakeColorSource( + const sk_sp& image) { + return std::make_shared(image, // + DlTileMode::kRepeat, // + DlTileMode::kRepeat, // + DlImageSampling::kLinear); +} + +static sk_sp MakeColorSource(const sk_sp& image) { + return image->makeShader(SkTileMode::kRepeat, // + SkTileMode::kRepeat, // + SkImageSampling::kLinear); +} + +// Used to show "INFO" warnings about tests that are omitted on certain +// backends, but only once for the entire test run to avoid warning spam +class OncePerBackendWarning { + public: + explicit OncePerBackendWarning(const std::string& warning) + : warning_(warning) {} + + void warn(const std::string& name) { + if (warnings_sent_.find(name) == warnings_sent_.end()) { + warnings_sent_.insert(name); + FML_LOG(INFO) << warning_ << " on " << name; + } + } + + private: + std::string warning_; + std::set warnings_sent_; +}; + // A class to specify how much tolerance to allow in bounds estimates. // For some attributes, the machinery must make some conservative // assumptions as to the extent of the bounds, but some of our test @@ -271,22 +334,46 @@ class BoundsTolerance { SkScalar discrete_offset_ = 0; }; -using SkSetup = const std::function; -using SkRenderer = const std::function; -using DlSetup = const std::function; -using DlRenderer = const std::function; -static const SkSetup kEmptySkSetup = [](SkCanvas*, SkPaint&) {}; -static const SkRenderer kEmptySkRenderer = [](SkCanvas*, const SkPaint&) {}; -static const DlSetup kEmptyDlSetup = [](DlCanvas*, DlPaint&) {}; -static const DlRenderer kEmptyDlRenderer = [](DlCanvas*, const DlPaint&) {}; +template +struct RenderContext { + C canvas; + P paint; + I image; +}; +using SkSetupContext = RenderContext>; +using DlSetupContext = RenderContext>; +using SkRenderContext = + RenderContext>; +using DlRenderContext = + RenderContext>; + +using SkSetup = const std::function; +using SkRenderer = const std::function; +using DlSetup = const std::function; +using DlRenderer = const std::function; +static const SkSetup kEmptySkSetup = [](const SkSetupContext&) {}; +static const SkRenderer kEmptySkRenderer = [](const SkRenderContext&) {}; +static const DlSetup kEmptyDlSetup = [](const DlSetupContext&) {}; +static const DlRenderer kEmptyDlRenderer = [](const DlRenderContext&) {}; using PixelFormat = DlSurfaceProvider::PixelFormat; using BackendType = DlSurfaceProvider::BackendType; class RenderResult { public: - explicit RenderResult(const sk_sp& surface, - bool take_snapshot = false) { + virtual ~RenderResult() = default; + + virtual sk_sp image() const = 0; + virtual int width() const = 0; + virtual int height() const = 0; + virtual const uint32_t* addr32(int x, int y) const = 0; + virtual void write(const std::string& path) const = 0; +}; + +class SkRenderResult final : public RenderResult { + public: + explicit SkRenderResult(const sk_sp& surface, + bool take_snapshot = false) { SkImageInfo info = surface->imageInfo(); info = SkImageInfo::MakeN32Premul(info.dimensions()); addr_ = malloc(info.computeMinByteSize() * info.height()); @@ -296,12 +383,20 @@ class RenderResult { image_ = surface->makeImageSnapshot(); } } - ~RenderResult() { free(addr_); } + ~SkRenderResult() override { free(addr_); } - sk_sp image() const { return image_; } - int width() const { return pixmap_.width(); } - int height() const { return pixmap_.height(); } - const uint32_t* addr32(int x, int y) const { return pixmap_.addr32(x, y); } + sk_sp image() const override { return image_; } + int width() const override { return pixmap_.width(); } + int height() const override { return pixmap_.height(); } + const uint32_t* addr32(int x, int y) const override { + return pixmap_.addr32(x, y); + } + void write(const std::string& path) const { + auto stream = SkFILEWStream(path.c_str()); + SkPngEncoder::Options options; + SkPngEncoder::Encode(&stream, pixmap_, options); + stream.flush(); + } private: sk_sp image_; @@ -309,6 +404,29 @@ class RenderResult { void* addr_ = nullptr; }; +class ImpellerRenderResult final : public RenderResult { + public: + explicit ImpellerRenderResult(sk_sp screenshot, + SkRect render_bounds) + : screenshot_(std::move(screenshot)), render_bounds_(render_bounds) {} + ~ImpellerRenderResult() override = default; + + sk_sp image() const override { return nullptr; }; + int width() const override { return screenshot_->width(); }; + int height() const override { return screenshot_->height(); } + const uint32_t* addr32(int x, int y) const override { + return screenshot_->addr32(x, y); + } + void write(const std::string& path) const override { + screenshot_->write(path); + } + const SkRect& render_bounds() const { return render_bounds_; } + + private: + const sk_sp screenshot_; + SkRect render_bounds_; +}; + struct RenderJobInfo { int width = kTestWidth; int height = kTestHeight; @@ -319,6 +437,7 @@ struct RenderJobInfo { struct JobRenderer { virtual void Render(SkCanvas* canvas, const RenderJobInfo& info) = 0; + virtual bool targets_impeller() const { return false; } }; struct MatrixClipJobRenderer : public JobRenderer { @@ -340,21 +459,25 @@ struct MatrixClipJobRenderer : public JobRenderer { }; struct SkJobRenderer : public MatrixClipJobRenderer { - explicit SkJobRenderer(const SkSetup& sk_setup = kEmptySkSetup, - const SkRenderer& sk_render = kEmptySkRenderer, - const SkRenderer& sk_restore = kEmptySkRenderer) - : sk_setup_(sk_setup), sk_render_(sk_render), sk_restore_(sk_restore) {} + explicit SkJobRenderer(const SkSetup& sk_setup, + const SkRenderer& sk_render, + const SkRenderer& sk_restore, + const sk_sp& sk_image) + : sk_setup_(sk_setup), + sk_render_(sk_render), + sk_restore_(sk_restore), + sk_image_(sk_image) {} void Render(SkCanvas* canvas, const RenderJobInfo& info) override { FML_DCHECK(info.opacity == SK_Scalar1); SkPaint paint; - sk_setup_(canvas, paint); + sk_setup_({canvas, paint, sk_image_}); setup_paint_ = paint; setup_matrix_ = canvas->getTotalMatrix(); setup_clip_bounds_ = canvas->getDeviceClipBounds(); is_setup_ = true; - sk_render_(canvas, paint); - sk_restore_(canvas, paint); + sk_render_({canvas, paint, sk_image_}); + sk_restore_({canvas, paint, sk_image_}); } sk_sp MakePicture(const RenderJobInfo& info) { @@ -374,14 +497,19 @@ struct SkJobRenderer : public MatrixClipJobRenderer { const SkSetup sk_setup_; const SkRenderer sk_render_; const SkRenderer sk_restore_; + sk_sp sk_image_; SkPaint setup_paint_; }; struct DlJobRenderer : public MatrixClipJobRenderer { - explicit DlJobRenderer(const DlSetup& dl_setup = kEmptyDlSetup, - const DlRenderer& dl_render = kEmptyDlRenderer, - const DlRenderer& dl_restore = kEmptyDlRenderer) - : dl_setup_(dl_setup), dl_render_(dl_render), dl_restore_(dl_restore) {} + explicit DlJobRenderer(const DlSetup& dl_setup, + const DlRenderer& dl_render, + const DlRenderer& dl_restore, + const sk_sp& dl_image) + : dl_setup_(dl_setup), + dl_render_(dl_render), + dl_restore_(dl_restore), + dl_image_(dl_image) {} void Render(SkCanvas* sk_canvas, const RenderJobInfo& info) override { DlSkCanvasAdapter canvas(sk_canvas); @@ -391,13 +519,13 @@ struct DlJobRenderer : public MatrixClipJobRenderer { void Render(DlCanvas* canvas, const RenderJobInfo& info) { FML_DCHECK(info.opacity == SK_Scalar1); DlPaint paint; - dl_setup_(canvas, paint); + dl_setup_({canvas, paint, dl_image_}); setup_paint_ = paint; setup_matrix_ = canvas->GetTransform(); setup_clip_bounds_ = canvas->GetDestinationClipBounds().roundOut(); is_setup_ = true; - dl_render_(canvas, paint); - dl_restore_(canvas, paint); + dl_render_({canvas, paint, dl_image_}); + dl_restore_({canvas, paint, dl_image_}); } sk_sp MakeDisplayList(const RenderJobInfo& info) { @@ -411,10 +539,15 @@ struct DlJobRenderer : public MatrixClipJobRenderer { return setup_paint_; } + bool targets_impeller() const override { + return dl_image_->impeller_texture() != nullptr; + } + private: const DlSetup dl_setup_; const DlRenderer dl_render_; const DlRenderer dl_restore_; + const sk_sp dl_image_; DlPaint setup_paint_; }; @@ -456,36 +589,37 @@ class RenderEnvironment { } static RenderEnvironment Make565(const DlSurfaceProvider* provider) { - return RenderEnvironment(provider, PixelFormat::k565_PixelFormat); + return RenderEnvironment(provider, PixelFormat::k565PixelFormat); } static RenderEnvironment MakeN32(const DlSurfaceProvider* provider) { - return RenderEnvironment(provider, PixelFormat::kN32Premul_PixelFormat); - } - - void init_ref(SkRenderer& sk_renderer, - DlRenderer& dl_renderer, - DlColor bg = DlColor::kTransparent()) { - init_ref(kEmptySkSetup, sk_renderer, kEmptyDlSetup, dl_renderer, bg); + return RenderEnvironment(provider, PixelFormat::kN32PremulPixelFormat); } void init_ref(SkSetup& sk_setup, SkRenderer& sk_renderer, DlSetup& dl_setup, DlRenderer& dl_renderer, + DlRenderer& imp_renderer, DlColor bg = DlColor::kTransparent()) { - SkJobRenderer sk_job(sk_setup, sk_renderer); + SkJobRenderer sk_job(sk_setup, sk_renderer, kEmptySkRenderer, kTestSkImage); RenderJobInfo info = { .bg = bg, }; ref_sk_result_ = getResult(info, sk_job); - DlJobRenderer dl_job(dl_setup, dl_renderer); + DlJobRenderer dl_job(dl_setup, dl_renderer, kEmptyDlRenderer, kTestDlImage); ref_dl_result_ = getResult(info, dl_job); ref_dl_paint_ = dl_job.setup_paint(); ref_matrix_ = dl_job.setup_matrix(); ref_clip_bounds_ = dl_job.setup_clip_bounds(); ASSERT_EQ(sk_job.setup_matrix(), ref_matrix_); ASSERT_EQ(sk_job.setup_clip_bounds(), ref_clip_bounds_); + if (provider_->supports_impeller()) { + test_impeller_image_ = makeTestImpellerImage(provider_); + DlJobRenderer imp_job(dl_setup, imp_renderer, kEmptyDlRenderer, + test_impeller_image_); + ref_impeller_result_ = getImpellerResult(info, imp_job); + } } std::unique_ptr getResult(const RenderJobInfo& info, @@ -493,7 +627,7 @@ class RenderEnvironment { auto surface = getSurface(info.width, info.height); FML_DCHECK(surface != nullptr); auto canvas = surface->getCanvas(); - canvas->clear(info.bg); + canvas->clear(ToSk(info.bg)); int restore_count = canvas->save(); canvas->scale(info.scale, info.scale); @@ -504,7 +638,7 @@ class RenderEnvironment { GrAsDirectContext(surface->recordingContext())) { dContext->flushAndSubmit(surface.get(), GrSyncCpu::kYes); } - return std::make_unique(surface); + return std::make_unique(surface); } std::unique_ptr getResult(sk_sp dl) const { @@ -513,9 +647,25 @@ class RenderEnvironment { return getResult(info, job); } + std::unique_ptr getImpellerResult( + const RenderJobInfo& info, + DlJobRenderer& renderer) const { + FML_DCHECK(info.scale == SK_Scalar1); + + DisplayListBuilder builder; + builder.Clear(info.bg); + auto render_dl = renderer.MakeDisplayList(info); + builder.DrawDisplayList(render_dl); + auto dl = builder.Build(); + auto snap = provider_->ImpellerSnapshot(dl, kTestWidth, kTestHeight); + return std::make_unique(std::move(snap), + render_dl->bounds()); + } + const DlSurfaceProvider* provider() const { return provider_; } bool valid() const { return provider_->supports(format_); } const std::string backend_name() const { return provider_->backend_name(); } + bool supports_impeller() const { return provider_->supports_impeller(); } PixelFormat format() const { return format_; } const DlPaint& ref_dl_paint() const { return ref_dl_paint_; } @@ -523,6 +673,13 @@ class RenderEnvironment { const SkIRect& ref_clip_bounds() const { return ref_clip_bounds_; } const RenderResult* ref_sk_result() const { return ref_sk_result_.get(); } const RenderResult* ref_dl_result() const { return ref_dl_result_.get(); } + const ImpellerRenderResult* ref_impeller_result() const { + return ref_impeller_result_.get(); + } + + const sk_sp sk_image() const { return kTestSkImage; } + const sk_sp dl_image() const { return kTestDlImage; } + const sk_sp impeller_image() const { return test_impeller_image_; } private: sk_sp getSurface(int width, int height) const { @@ -551,8 +708,31 @@ class RenderEnvironment { SkIRect ref_clip_bounds_; std::unique_ptr ref_sk_result_; std::unique_ptr ref_dl_result_; + std::unique_ptr ref_impeller_result_; + sk_sp test_impeller_image_; + + static const sk_sp kTestSkImage; + static const sk_sp kTestDlImage; + static const sk_sp makeTestSkImage() { + sk_sp surface = SkSurfaces::Raster( + SkImageInfo::MakeN32Premul(kRenderWidth, kRenderHeight)); + DrawCheckerboard(surface->getCanvas()); + return surface->makeImageSnapshot(); + } + static const sk_sp makeTestImpellerImage( + const DlSurfaceProvider* provider) { + FML_DCHECK(provider->supports_impeller()); + DisplayListBuilder builder(SkRect::MakeWH(kRenderWidth, kRenderHeight)); + DrawCheckerboard(&builder); + return provider->MakeImpellerImage(builder.Build(), // + kRenderWidth, kRenderHeight); + } }; +const sk_sp RenderEnvironment::kTestSkImage = makeTestSkImage(); +const sk_sp RenderEnvironment::kTestDlImage = + DlImage::Make(kTestSkImage); + class CaseParameters { public: explicit CaseParameters(std::string info) @@ -564,7 +744,7 @@ class CaseParameters { dl_setup, kEmptySkRenderer, kEmptyDlRenderer, - SK_ColorTRANSPARENT, + DlColor(SK_ColorTRANSPARENT), false, false, false) {} @@ -637,9 +817,33 @@ class TestParameters { TestParameters(const SkRenderer& sk_renderer, const DlRenderer& dl_renderer, const DisplayListAttributeFlags& flags) - : sk_renderer_(sk_renderer), dl_renderer_(dl_renderer), flags_(flags) {} + : TestParameters(sk_renderer, dl_renderer, dl_renderer, flags) {} + + TestParameters(const SkRenderer& sk_renderer, + const DlRenderer& dl_renderer, + const DlRenderer& imp_renderer, + const DisplayListAttributeFlags& flags) + : sk_renderer_(sk_renderer), + dl_renderer_(dl_renderer), + imp_renderer_(imp_renderer), + flags_(flags) {} bool uses_paint() const { return !flags_.ignores_paint(); } + bool uses_gradient() const { return flags_.applies_shader(); } + + bool impeller_compatible(const DlPaint& paint) const { + if (is_draw_text_blob()) { + // Non-color text is rendered as paths + if (paint.getColorSourcePtr() && !paint.getColorSourcePtr()->asColor()) { + return false; + } + // Non-filled text (stroke or stroke and fill) is rendered as paths + if (paint.getDrawStyle() != DlDrawStyle::kFill) { + return false; + } + } + return true; + } bool should_match(const RenderEnvironment& env, const CaseParameters& caseP, @@ -661,7 +865,12 @@ class TestParameters { const DlPaint& ref_attr = env.ref_dl_paint(); if (flags_.applies_anti_alias() && // ref_attr.isAntiAlias() != attr.isAntiAlias()) { - return false; + if (renderer.targets_impeller()) { + // Impeller only does MSAA, ignoring the AA attribute + // https://github.com/flutter/flutter/issues/104721 + } else { + return false; + } } if (flags_.applies_dither() && // ref_attr.isDither() != attr.isDither()) { @@ -701,10 +910,16 @@ class TestParameters { flags_.WithPathEffect(attr.getPathEffect().get(), is_stroked); if (flags_.applies_path_effect() && // ref_attr.getPathEffect() != attr.getPathEffect()) { - switch (attr.getPathEffect()->type()) { - case DlPathEffectType::kDash: { - if (is_stroked && !ignores_dashes()) { - return false; + if (renderer.targets_impeller()) { + // Impeller ignores DlPathEffect objects: + // https://github.com/flutter/flutter/issues/109736 + } else { + switch (attr.getPathEffect()->type()) { + case DlPathEffectType::kDash: { + if (is_stroked && !ignores_dashes()) { + return false; + } + break; } } } @@ -822,6 +1037,7 @@ class TestParameters { const SkRenderer& sk_renderer() const { return sk_renderer_; } const DlRenderer& dl_renderer() const { return dl_renderer_; } + const DlRenderer& imp_renderer() const { return imp_renderer_; } // Tests that call drawTextBlob with an sk_ref paint attribute will cause // those attributes to be stored in an internal Skia cache so we need @@ -872,6 +1088,7 @@ class TestParameters { private: const SkRenderer sk_renderer_; const DlRenderer dl_renderer_; + const DlRenderer imp_renderer_; const DisplayListAttributeFlags flags_; bool is_draw_text_blob_ = false; @@ -886,16 +1103,56 @@ class TestParameters { class CanvasCompareTester { public: - static std::vector> kTestProviders; + static std::vector TestBackends; + static std::string ImpellerFailureImageDirectory; + static bool SaveImpellerFailureImages; + static std::vector ImpellerFailureImages; + + static std::unique_ptr GetProvider(BackendType type) { + auto provider = DlSurfaceProvider::Create(type); + if (provider == nullptr) { + FML_LOG(ERROR) << "provider " << DlSurfaceProvider::BackendName(type) + << " not supported (ignoring)"; + return nullptr; + } + provider->InitializeSurface(kTestWidth, kTestHeight, + PixelFormat::kN32PremulPixelFormat); + return provider; + } + + static bool AddProvider(BackendType type) { + auto provider = GetProvider(type); + if (!provider) { + return false; + } + CanvasCompareTester::TestBackends.push_back(type); + return true; + } static BoundsTolerance DefaultTolerance; static void RenderAll(const TestParameters& params, const BoundsTolerance& tolerance = DefaultTolerance) { - for (auto& provider : kTestProviders) { + for (auto& back_end : TestBackends) { + auto provider = GetProvider(back_end); RenderEnvironment env = RenderEnvironment::MakeN32(provider.get()); - env.init_ref(params.sk_renderer(), params.dl_renderer()); + env.init_ref(kEmptySkSetup, params.sk_renderer(), // + kEmptyDlSetup, params.dl_renderer(), params.imp_renderer()); quickCompareToReference(env, "default"); + if (env.supports_impeller()) { + auto impeller_result = env.ref_impeller_result(); + if (!checkPixels(impeller_result, impeller_result->render_bounds(), + "Impeller reference")) { + std::string test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + save_to_png(impeller_result, test_name + " (Impeller reference)", + "base rendering was blank or out of bounds"); + } + } else { + static OncePerBackendWarning warnings("No Impeller output tests"); + warnings.warn(env.backend_name()); + } + RenderWithTransforms(params, env, tolerance); RenderWithClips(params, env, tolerance); RenderWithSaveRestore(params, env, tolerance); @@ -913,113 +1170,113 @@ class CanvasCompareTester { SkRect::MakeXYWH(kRenderCenterX - 1, kRenderCenterY - 1, 2, 2); SkRect rect = SkRect::MakeXYWH(kRenderCenterX, kRenderCenterY, 10, 10); DlColor alpha_layer_color = DlColor::kCyan().withAlpha(0x7f); - SkRenderer sk_safe_restore = [=](SkCanvas* cv, const SkPaint& p) { + SkRenderer sk_safe_restore = [=](const SkRenderContext& ctx) { // Draw another primitive to disable peephole optimizations - cv->drawRect(kRenderBounds.makeOffset(500, 500), SkPaint()); - cv->restore(); + ctx.canvas->drawRect(kRenderBounds.makeOffset(500, 500), SkPaint()); + ctx.canvas->restore(); }; - DlRenderer dl_safe_restore = [=](DlCanvas* cv, const DlPaint& p) { + DlRenderer dl_safe_restore = [=](const DlRenderContext& ctx) { // Draw another primitive to disable peephole optimizations // As the rendering op rejection in the DisplayList Builder // gets smarter and smarter, this operation has had to get // sneakier and sneakier about specifying an operation that // won't practically show up in the output, but technically // can't be culled. - cv->DrawRect( + ctx.canvas->DrawRect( SkRect::MakeXYWH(kRenderCenterX, kRenderCenterY, 0.0001, 0.0001), DlPaint()); - cv->Restore(); + ctx.canvas->Restore(); }; - SkRenderer sk_opt_restore = [=](SkCanvas* cv, const SkPaint& p) { + SkRenderer sk_opt_restore = [=](const SkRenderContext& ctx) { // Just a simple restore to allow peephole optimizations to occur - cv->restore(); + ctx.canvas->restore(); }; - DlRenderer dl_opt_restore = [=](DlCanvas* cv, const DlPaint& p) { + DlRenderer dl_opt_restore = [=](const DlRenderContext& ctx) { // Just a simple restore to allow peephole optimizations to occur - cv->Restore(); + ctx.canvas->Restore(); }; SkRect layer_bounds = kRenderBounds.makeInset(15, 15); RenderWith(testP, env, tolerance, CaseParameters( "With prior save/clip/restore", - [=](SkCanvas* cv, SkPaint& p) { - cv->save(); - cv->clipRect(clip, SkClipOp::kIntersect, false); + [=](const SkSetupContext& ctx) { + ctx.canvas->save(); + ctx.canvas->clipRect(clip, SkClipOp::kIntersect, false); SkPaint p2; - cv->drawRect(rect, p2); + ctx.canvas->drawRect(rect, p2); p2.setBlendMode(SkBlendMode::kClear); - cv->drawRect(rect, p2); - cv->restore(); + ctx.canvas->drawRect(rect, p2); + ctx.canvas->restore(); }, - [=](DlCanvas* cv, DlPaint& p) { - cv->Save(); - cv->ClipRect(clip, ClipOp::kIntersect, false); + [=](const DlSetupContext& ctx) { + ctx.canvas->Save(); + ctx.canvas->ClipRect(clip, ClipOp::kIntersect, false); DlPaint p2; - cv->DrawRect(rect, p2); + ctx.canvas->DrawRect(rect, p2); p2.setBlendMode(DlBlendMode::kClear); - cv->DrawRect(rect, p2); - cv->Restore(); + ctx.canvas->DrawRect(rect, p2); + ctx.canvas->Restore(); })); RenderWith(testP, env, tolerance, CaseParameters( "saveLayer no paint, no bounds", - [=](SkCanvas* cv, SkPaint& p) { // - cv->saveLayer(nullptr, nullptr); + [=](const SkSetupContext& ctx) { + ctx.canvas->saveLayer(nullptr, nullptr); }, - [=](DlCanvas* cv, DlPaint& p) { // - cv->SaveLayer(nullptr, nullptr); + [=](const DlSetupContext& ctx) { + ctx.canvas->SaveLayer(nullptr, nullptr); }) .with_restore(sk_safe_restore, dl_safe_restore, false)); RenderWith(testP, env, tolerance, CaseParameters( "saveLayer no paint, with bounds", - [=](SkCanvas* cv, SkPaint& p) { // - cv->saveLayer(layer_bounds, nullptr); + [=](const SkSetupContext& ctx) { + ctx.canvas->saveLayer(layer_bounds, nullptr); }, - [=](DlCanvas* cv, DlPaint& p) { // - cv->SaveLayer(&layer_bounds, nullptr); + [=](const DlSetupContext& ctx) { + ctx.canvas->SaveLayer(&layer_bounds, nullptr); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); RenderWith(testP, env, tolerance, CaseParameters( "saveLayer with alpha, no bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(nullptr, &save_p); + save_p.setColor(ToSk(alpha_layer_color)); + ctx.canvas->saveLayer(nullptr, &save_p); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setColor(alpha_layer_color); - cv->SaveLayer(nullptr, &save_p); + ctx.canvas->SaveLayer(nullptr, &save_p); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); RenderWith(testP, env, tolerance, CaseParameters( "saveLayer with peephole alpha, no bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(nullptr, &save_p); + save_p.setColor(ToSk(alpha_layer_color)); + ctx.canvas->saveLayer(nullptr, &save_p); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setColor(alpha_layer_color); - cv->SaveLayer(nullptr, &save_p); + ctx.canvas->SaveLayer(nullptr, &save_p); }) .with_restore(sk_opt_restore, dl_opt_restore, true, true)); RenderWith(testP, env, tolerance, CaseParameters( "saveLayer with alpha and bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(layer_bounds, &save_p); + save_p.setColor(ToSk(alpha_layer_color)); + ctx.canvas->saveLayer(layer_bounds, &save_p); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setColor(alpha_layer_color); - cv->SaveLayer(&layer_bounds, &save_p); + ctx.canvas->SaveLayer(&layer_bounds, &save_p); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); { @@ -1032,24 +1289,25 @@ class CanvasCompareTester { // a non-opaque color to avoid that problem. RenderEnvironment backdrop_env = RenderEnvironment::MakeN32(env.provider()); - SkSetup sk_backdrop_setup = [=](SkCanvas* cv, SkPaint& p) { + SkSetup sk_backdrop_setup = [=](const SkSetupContext& ctx) { SkPaint setup_p; - setup_p.setShader(kTestSkImageColorSource); - cv->drawPaint(setup_p); + setup_p.setShader(MakeColorSource(ctx.image)); + ctx.canvas->drawPaint(setup_p); }; - DlSetup dl_backdrop_setup = [=](DlCanvas* cv, DlPaint& p) { + DlSetup dl_backdrop_setup = [=](const DlSetupContext& ctx) { DlPaint setup_p; - setup_p.setColorSource(&kTestDlImageColorSource); - cv->DrawPaint(setup_p); + setup_p.setColorSource(MakeColorSource(ctx.image)); + ctx.canvas->DrawPaint(setup_p); }; - SkSetup sk_content_setup = [=](SkCanvas* cv, SkPaint& p) { - p.setAlpha(p.getAlpha() / 2); + SkSetup sk_content_setup = [=](const SkSetupContext& ctx) { + ctx.paint.setAlpha(ctx.paint.getAlpha() / 2); }; - DlSetup dl_content_setup = [=](DlCanvas* cv, DlPaint& p) { - p.setAlpha(p.getAlpha() / 2); + DlSetup dl_content_setup = [=](const DlSetupContext& ctx) { + ctx.paint.setAlpha(ctx.paint.getAlpha() / 2); }; backdrop_env.init_ref(sk_backdrop_setup, testP.sk_renderer(), - dl_backdrop_setup, testP.dl_renderer()); + dl_backdrop_setup, testP.dl_renderer(), + testP.imp_renderer()); quickCompareToReference(backdrop_env, "backdrop"); DlBlurImageFilter dl_backdrop(5, 5, DlTileMode::kDecal); @@ -1058,48 +1316,49 @@ class CanvasCompareTester { RenderWith(testP, backdrop_env, tolerance, CaseParameters( "saveLayer with backdrop", - [=](SkCanvas* cv, SkPaint& p) { - sk_backdrop_setup(cv, p); - cv->saveLayer(SkCanvas::SaveLayerRec( + [=](const SkSetupContext& ctx) { + sk_backdrop_setup(ctx); + ctx.canvas->saveLayer(SkCanvas::SaveLayerRec( nullptr, nullptr, sk_backdrop.get(), 0)); - sk_content_setup(cv, p); + sk_content_setup(ctx); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_backdrop_setup(cv, p); - cv->SaveLayer(nullptr, nullptr, &dl_backdrop); - dl_content_setup(cv, p); + [=](const DlSetupContext& ctx) { + dl_backdrop_setup(ctx); + ctx.canvas->SaveLayer(nullptr, nullptr, &dl_backdrop); + dl_content_setup(ctx); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); RenderWith(testP, backdrop_env, tolerance, CaseParameters( "saveLayer with bounds and backdrop", - [=](SkCanvas* cv, SkPaint& p) { - sk_backdrop_setup(cv, p); - cv->saveLayer(SkCanvas::SaveLayerRec( + [=](const SkSetupContext& ctx) { + sk_backdrop_setup(ctx); + ctx.canvas->saveLayer(SkCanvas::SaveLayerRec( &layer_bounds, nullptr, sk_backdrop.get(), 0)); - sk_content_setup(cv, p); + sk_content_setup(ctx); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_backdrop_setup(cv, p); - cv->SaveLayer(&layer_bounds, nullptr, &dl_backdrop); - dl_content_setup(cv, p); + [=](const DlSetupContext& ctx) { + dl_backdrop_setup(ctx); + ctx.canvas->SaveLayer(&layer_bounds, nullptr, + &dl_backdrop); + dl_content_setup(ctx); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); RenderWith(testP, backdrop_env, tolerance, CaseParameters( "clipped saveLayer with backdrop", - [=](SkCanvas* cv, SkPaint& p) { - sk_backdrop_setup(cv, p); - cv->clipRect(layer_bounds); - cv->saveLayer(SkCanvas::SaveLayerRec( + [=](const SkSetupContext& ctx) { + sk_backdrop_setup(ctx); + ctx.canvas->clipRect(layer_bounds); + ctx.canvas->saveLayer(SkCanvas::SaveLayerRec( nullptr, nullptr, sk_backdrop.get(), 0)); - sk_content_setup(cv, p); + sk_content_setup(ctx); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_backdrop_setup(cv, p); - cv->ClipRect(layer_bounds); - cv->SaveLayer(nullptr, nullptr, &dl_backdrop); - dl_content_setup(cv, p); + [=](const DlSetupContext& ctx) { + dl_backdrop_setup(ctx); + ctx.canvas->ClipRect(layer_bounds); + ctx.canvas->SaveLayer(nullptr, nullptr, &dl_backdrop); + dl_content_setup(ctx); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); } @@ -1120,17 +1379,17 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "saveLayer ColorFilter, no bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; save_p.setColorFilter(sk_alpha_rotate_filter); - cv->saveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->saveLayer(nullptr, &save_p); + ctx.paint.setStrokeWidth(5.0); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setColorFilter(&dl_alpha_rotate_filter); - cv->SaveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->SaveLayer(nullptr, &save_p); + ctx.paint.setStrokeWidth(5.0); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); } @@ -1138,17 +1397,17 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "saveLayer ColorFilter and bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; save_p.setColorFilter(sk_alpha_rotate_filter); - cv->saveLayer(kRenderBounds, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->saveLayer(kRenderBounds, &save_p); + ctx.paint.setStrokeWidth(5.0); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setColorFilter(&dl_alpha_rotate_filter); - cv->SaveLayer(&kRenderBounds, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->SaveLayer(&kRenderBounds, &save_p); + ctx.paint.setStrokeWidth(5.0); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); } @@ -1171,17 +1430,17 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "saveLayer ImageFilter, no bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; save_p.setImageFilter(sk_cf_image_filter); - cv->saveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->saveLayer(nullptr, &save_p); + ctx.paint.setStrokeWidth(5.0); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setImageFilter(&dl_cf_image_filter); - cv->SaveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->SaveLayer(nullptr, &save_p); + ctx.paint.setStrokeWidth(5.0); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); } @@ -1189,17 +1448,17 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "saveLayer ImageFilter and bounds", - [=](SkCanvas* cv, SkPaint& p) { + [=](const SkSetupContext& ctx) { SkPaint save_p; save_p.setImageFilter(sk_cf_image_filter); - cv->saveLayer(kRenderBounds, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->saveLayer(kRenderBounds, &save_p); + ctx.paint.setStrokeWidth(5.0); }, - [=](DlCanvas* cv, DlPaint& p) { + [=](const DlSetupContext& ctx) { DlPaint save_p; save_p.setImageFilter(&dl_cf_image_filter); - cv->SaveLayer(&kRenderBounds, &save_p); - p.setStrokeWidth(5.0); + ctx.canvas->SaveLayer(&kRenderBounds, &save_p); + ctx.paint.setStrokeWidth(5.0); }) .with_restore(sk_safe_restore, dl_safe_restore, true)); } @@ -1218,48 +1477,56 @@ class CanvasCompareTester { RenderEnvironment aa_env = RenderEnvironment::MakeN32(env.provider()); // Tweak the bounds tolerance for the displacement of 1/10 of a pixel const BoundsTolerance aa_tolerance = tolerance.addBoundsPadding(1, 1); - auto sk_aa_setup = [=](SkCanvas* cv, SkPaint& p, bool is_aa) { - cv->translate(0.1, 0.1); - p.setAntiAlias(is_aa); - p.setStrokeWidth(5.0); + auto sk_aa_setup = [=](SkSetupContext ctx, bool is_aa) { + ctx.canvas->translate(0.1, 0.1); + ctx.paint.setAntiAlias(is_aa); + ctx.paint.setStrokeWidth(5.0); }; - auto dl_aa_setup = [=](DlCanvas* cv, DlPaint& p, bool is_aa) { - cv->Translate(0.1, 0.1); - p.setAntiAlias(is_aa); - p.setStrokeWidth(5.0); + auto dl_aa_setup = [=](DlSetupContext ctx, bool is_aa) { + ctx.canvas->Translate(0.1, 0.1); + ctx.paint.setAntiAlias(is_aa); + ctx.paint.setStrokeWidth(5.0); }; aa_env.init_ref( - [=](SkCanvas* cv, SkPaint& p) { sk_aa_setup(cv, p, false); }, + [=](const SkSetupContext& ctx) { sk_aa_setup(ctx, false); }, testP.sk_renderer(), - [=](DlCanvas* cv, DlPaint& p) { dl_aa_setup(cv, p, false); }, - testP.dl_renderer()); + [=](const DlSetupContext& ctx) { dl_aa_setup(ctx, false); }, + testP.dl_renderer(), testP.imp_renderer()); quickCompareToReference(aa_env, "AntiAlias"); RenderWith( testP, aa_env, aa_tolerance, CaseParameters( "AntiAlias == True", - [=](SkCanvas* cv, SkPaint& p) { sk_aa_setup(cv, p, true); }, - [=](DlCanvas* cv, DlPaint& p) { dl_aa_setup(cv, p, true); })); + [=](const SkSetupContext& ctx) { sk_aa_setup(ctx, true); }, + [=](const DlSetupContext& ctx) { dl_aa_setup(ctx, true); })); RenderWith( testP, aa_env, aa_tolerance, CaseParameters( "AntiAlias == False", - [=](SkCanvas* cv, SkPaint& p) { sk_aa_setup(cv, p, false); }, - [=](DlCanvas* cv, DlPaint& p) { dl_aa_setup(cv, p, false); })); + [=](const SkSetupContext& ctx) { sk_aa_setup(ctx, false); }, + [=](const DlSetupContext& ctx) { dl_aa_setup(ctx, false); })); } - RenderWith( + RenderWith( // testP, env, tolerance, CaseParameters( "Color == Blue", - [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, - [=](DlCanvas*, DlPaint& p) { p.setColor(DlColor::kBlue()); })); - RenderWith( + [=](const SkSetupContext& ctx) { + ctx.paint.setColor(SK_ColorBLUE); + }, + [=](const DlSetupContext& ctx) { + ctx.paint.setColor(DlColor::kBlue()); + })); + RenderWith( // testP, env, tolerance, CaseParameters( "Color == Green", - [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, - [=](DlCanvas*, DlPaint& p) { p.setColor(DlColor::kGreen()); })); + [=](const SkSetupContext& ctx) { + ctx.paint.setColor(SK_ColorGREEN); + }, + [=](const DlSetupContext& ctx) { + ctx.paint.setColor(DlColor::kGreen()); + })); RenderWithStrokes(testP, env, tolerance); @@ -1271,25 +1538,25 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "Blend == SrcIn", - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kSrcIn); - p.setColor(blendable_color); + [=](const SkSetupContext& ctx) { + ctx.paint.setBlendMode(SkBlendMode::kSrcIn); + ctx.paint.setColor(blendable_color.argb()); }, - [=](DlCanvas*, DlPaint& p) { - p.setBlendMode(DlBlendMode::kSrcIn); - p.setColor(blendable_color); + [=](const DlSetupContext& ctx) { + ctx.paint.setBlendMode(DlBlendMode::kSrcIn); + ctx.paint.setColor(blendable_color); }) .with_bg(bg)); RenderWith(testP, env, tolerance, CaseParameters( "Blend == DstIn", - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kDstIn); - p.setColor(blendable_color); + [=](const SkSetupContext& ctx) { + ctx.paint.setBlendMode(SkBlendMode::kDstIn); + ctx.paint.setColor(blendable_color.argb()); }, - [=](DlCanvas*, DlPaint& p) { - p.setBlendMode(DlBlendMode::kDstIn); - p.setColor(blendable_color); + [=](const DlSetupContext& ctx) { + ctx.paint.setBlendMode(DlBlendMode::kDstIn); + ctx.paint.setColor(blendable_color); }) .with_bg(bg)); } @@ -1299,16 +1566,17 @@ class CanvasCompareTester { // like a non-trivial stroke width and a shader rather than a color // (for drawPaint) so we create a new environment for these tests. RenderEnvironment blur_env = RenderEnvironment::MakeN32(env.provider()); - SkSetup sk_blur_setup = [=](SkCanvas*, SkPaint& p) { - p.setShader(kTestSkImageColorSource); - p.setStrokeWidth(5.0); + SkSetup sk_blur_setup = [=](const SkSetupContext& ctx) { + ctx.paint.setShader(MakeColorSource(ctx.image)); + ctx.paint.setStrokeWidth(5.0); }; - DlSetup dl_blur_setup = [=](DlCanvas*, DlPaint& p) { - p.setColorSource(&kTestDlImageColorSource); - p.setStrokeWidth(5.0); + DlSetup dl_blur_setup = [=](const DlSetupContext& ctx) { + ctx.paint.setColorSource(MakeColorSource(ctx.image)); + ctx.paint.setStrokeWidth(5.0); }; blur_env.init_ref(sk_blur_setup, testP.sk_renderer(), // - dl_blur_setup, testP.dl_renderer()); + dl_blur_setup, testP.dl_renderer(), + testP.imp_renderer()); quickCompareToReference(blur_env, "blur"); DlBlurImageFilter dl_filter_decal_5(5.0, 5.0, DlTileMode::kDecal); auto sk_filter_decal_5 = @@ -1318,13 +1586,13 @@ class CanvasCompareTester { RenderWith(testP, blur_env, blur_5_tolerance, CaseParameters( "ImageFilter == Decal Blur 5", - [=](SkCanvas* cv, SkPaint& p) { - sk_blur_setup(cv, p); - p.setImageFilter(sk_filter_decal_5); + [=](const SkSetupContext& ctx) { + sk_blur_setup(ctx); + ctx.paint.setImageFilter(sk_filter_decal_5); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_blur_setup(cv, p); - p.setImageFilter(&dl_filter_decal_5); + [=](const DlSetupContext& ctx) { + dl_blur_setup(ctx); + ctx.paint.setImageFilter(&dl_filter_decal_5); })); } DlBlurImageFilter dl_filter_clamp_5(5.0, 5.0, DlTileMode::kClamp); @@ -1334,13 +1602,13 @@ class CanvasCompareTester { RenderWith(testP, blur_env, blur_5_tolerance, CaseParameters( "ImageFilter == Clamp Blur 5", - [=](SkCanvas* cv, SkPaint& p) { - sk_blur_setup(cv, p); - p.setImageFilter(sk_filter_clamp_5); + [=](const SkSetupContext& ctx) { + sk_blur_setup(ctx); + ctx.paint.setImageFilter(sk_filter_clamp_5); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_blur_setup(cv, p); - p.setImageFilter(&dl_filter_clamp_5); + [=](const DlSetupContext& ctx) { + dl_blur_setup(ctx); + ctx.paint.setImageFilter(&dl_filter_clamp_5); })); } } @@ -1350,29 +1618,30 @@ class CanvasCompareTester { // like a non-trivial stroke width and a shader rather than a color // (for drawPaint) so we create a new environment for these tests. RenderEnvironment dilate_env = RenderEnvironment::MakeN32(env.provider()); - SkSetup sk_dilate_setup = [=](SkCanvas*, SkPaint& p) { - p.setShader(kTestSkImageColorSource); - p.setStrokeWidth(5.0); + SkSetup sk_dilate_setup = [=](const SkSetupContext& ctx) { + ctx.paint.setShader(MakeColorSource(ctx.image)); + ctx.paint.setStrokeWidth(5.0); }; - DlSetup dl_dilate_setup = [=](DlCanvas*, DlPaint& p) { - p.setColorSource(&kTestDlImageColorSource); - p.setStrokeWidth(5.0); + DlSetup dl_dilate_setup = [=](const DlSetupContext& ctx) { + ctx.paint.setColorSource(MakeColorSource(ctx.image)); + ctx.paint.setStrokeWidth(5.0); }; dilate_env.init_ref(sk_dilate_setup, testP.sk_renderer(), // - dl_dilate_setup, testP.dl_renderer()); + dl_dilate_setup, testP.dl_renderer(), + testP.imp_renderer()); quickCompareToReference(dilate_env, "dilate"); DlDilateImageFilter dl_dilate_filter_5(5.0, 5.0); auto sk_dilate_filter_5 = SkImageFilters::Dilate(5.0, 5.0, nullptr); RenderWith(testP, dilate_env, tolerance, CaseParameters( "ImageFilter == Dilate 5", - [=](SkCanvas* cv, SkPaint& p) { - sk_dilate_setup(cv, p); - p.setImageFilter(sk_dilate_filter_5); + [=](const SkSetupContext& ctx) { + sk_dilate_setup(ctx); + ctx.paint.setImageFilter(sk_dilate_filter_5); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_dilate_setup(cv, p); - p.setImageFilter(&dl_dilate_filter_5); + [=](const DlSetupContext& ctx) { + dl_dilate_setup(ctx); + ctx.paint.setImageFilter(&dl_dilate_filter_5); })); } @@ -1381,16 +1650,17 @@ class CanvasCompareTester { // like a non-trivial stroke width and a shader rather than a color // (for drawPaint) so we create a new environment for these tests. RenderEnvironment erode_env = RenderEnvironment::MakeN32(env.provider()); - SkSetup sk_erode_setup = [=](SkCanvas*, SkPaint& p) { - p.setShader(kTestSkImageColorSource); - p.setStrokeWidth(6.0); + SkSetup sk_erode_setup = [=](const SkSetupContext& ctx) { + ctx.paint.setShader(MakeColorSource(ctx.image)); + ctx.paint.setStrokeWidth(6.0); }; - DlSetup dl_erode_setup = [=](DlCanvas*, DlPaint& p) { - p.setColorSource(&kTestDlImageColorSource); - p.setStrokeWidth(6.0); + DlSetup dl_erode_setup = [=](const DlSetupContext& ctx) { + ctx.paint.setColorSource(MakeColorSource(ctx.image)); + ctx.paint.setStrokeWidth(6.0); }; erode_env.init_ref(sk_erode_setup, testP.sk_renderer(), // - dl_erode_setup, testP.dl_renderer()); + dl_erode_setup, testP.dl_renderer(), + testP.imp_renderer()); quickCompareToReference(erode_env, "erode"); // do not erode too much, because some tests assert there are enough // pixels that are changed. @@ -1399,13 +1669,13 @@ class CanvasCompareTester { RenderWith(testP, erode_env, tolerance, CaseParameters( "ImageFilter == Erode 1", - [=](SkCanvas* cv, SkPaint& p) { - sk_erode_setup(cv, p); - p.setImageFilter(sk_erode_filter_1); + [=](const SkSetupContext& ctx) { + sk_erode_setup(ctx); + ctx.paint.setImageFilter(sk_erode_filter_1); }, - [=](DlCanvas* cv, DlPaint& p) { - dl_erode_setup(cv, p); - p.setImageFilter(&dl_erode_filter_1); + [=](const DlSetupContext& ctx) { + dl_erode_setup(ctx); + ctx.paint.setImageFilter(&dl_erode_filter_1); })); } @@ -1431,31 +1701,31 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "ColorFilter == RotateRGB", - [=](SkCanvas*, SkPaint& p) { - p.setColor(DlColor::kYellow()); - p.setColorFilter(sk_color_filter); + [=](const SkSetupContext& ctx) { + ctx.paint.setColor(SK_ColorYELLOW); + ctx.paint.setColorFilter(sk_color_filter); }, - [=](DlCanvas*, DlPaint& p) { - p.setColor(DlColor::kYellow()); - p.setColorFilter(&dl_color_filter); + [=](const DlSetupContext& ctx) { + ctx.paint.setColor(DlColor::kYellow()); + ctx.paint.setColorFilter(&dl_color_filter); }) .with_bg(bg)); } { DlColor bg = DlColor::kWhite(); - RenderWith( - testP, env, tolerance, - CaseParameters( - "ColorFilter == Invert", - [=](SkCanvas*, SkPaint& p) { - p.setColor(DlColor::kYellow()); - p.setColorFilter(SkColorFilters::Matrix(invert_color_matrix)); - }, - [=](DlCanvas*, DlPaint& p) { - p.setColor(DlColor::kYellow()); - p.setInvertColors(true); - }) - .with_bg(bg)); + RenderWith(testP, env, tolerance, + CaseParameters( + "ColorFilter == Invert", + [=](const SkSetupContext& ctx) { + ctx.paint.setColor(SK_ColorYELLOW); + ctx.paint.setColorFilter( + SkColorFilters::Matrix(invert_color_matrix)); + }, + [=](const DlSetupContext& ctx) { + ctx.paint.setColor(DlColor::kYellow()); + ctx.paint.setInvertColors(true); + }) + .with_bg(bg)); } } @@ -1468,13 +1738,13 @@ class CanvasCompareTester { RenderWith(testP, env, blur_5_tolerance, CaseParameters( "MaskFilter == Blur 5", - [=](SkCanvas*, SkPaint& p) { - p.setStrokeWidth(5.0); - p.setMaskFilter(sk_mask_filter); + [=](const SkSetupContext& ctx) { + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setMaskFilter(sk_mask_filter); }, - [=](DlCanvas*, DlPaint& p) { - p.setStrokeWidth(5.0); - p.setMaskFilter(&dl_mask_filter); + [=](const DlSetupContext& ctx) { + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setMaskFilter(&dl_mask_filter); })); } } @@ -1505,12 +1775,69 @@ class CanvasCompareTester { auto sk_gradient = SkGradientShader::MakeLinear( end_points, sk_colors, stops, 3, SkTileMode::kMirror, 0, nullptr); { - RenderWith( - testP, env, tolerance, - CaseParameters( - "LinearGradient GYB", - [=](SkCanvas*, SkPaint& p) { p.setShader(sk_gradient); }, - [=](DlCanvas*, DlPaint& p) { p.setColorSource(dl_gradient); })); + RenderWith(testP, env, tolerance, + CaseParameters( + "LinearGradient GYB", + [=](const SkSetupContext& ctx) { + ctx.paint.setShader(sk_gradient); + }, + [=](const DlSetupContext& ctx) { + ctx.paint.setColorSource(dl_gradient); + })); + } + + if (testP.uses_gradient()) { + // Dithering is only applied to gradients so we reuse the gradient + // created above in these setup methods. Also, thin stroked + // primitives (mainly drawLine and drawPoints) do not show much + // dithering so we use a non-trivial stroke width as well. + RenderEnvironment dither_env = + RenderEnvironment::Make565(env.provider()); + if (!dither_env.valid()) { + // Currently only happens on Metal backend + static OncePerBackendWarning warnings("Skipping Dithering tests"); + warnings.warn(dither_env.backend_name()); + } else { + DlColor dither_bg = DlColor::kBlack(); + SkSetup sk_dither_setup = [=](const SkSetupContext& ctx) { + ctx.paint.setShader(sk_gradient); + ctx.paint.setAlpha(0xf0); + ctx.paint.setStrokeWidth(5.0); + }; + DlSetup dl_dither_setup = [=](const DlSetupContext& ctx) { + ctx.paint.setColorSource(dl_gradient); + ctx.paint.setAlpha(0xf0); + ctx.paint.setStrokeWidth(5.0); + }; + dither_env.init_ref(sk_dither_setup, testP.sk_renderer(), + dl_dither_setup, testP.dl_renderer(), + testP.imp_renderer(), dither_bg); + quickCompareToReference(dither_env, "dither"); + RenderWith(testP, dither_env, tolerance, + CaseParameters( + "Dither == True", + [=](const SkSetupContext& ctx) { + sk_dither_setup(ctx); + ctx.paint.setDither(true); + }, + [=](const DlSetupContext& ctx) { + dl_dither_setup(ctx); + ctx.paint.setDither(true); + }) + .with_bg(dither_bg)); + RenderWith(testP, dither_env, tolerance, + CaseParameters( + "Dither = False", + [=](const SkSetupContext& ctx) { + sk_dither_setup(ctx); + ctx.paint.setDither(false); + }, + [=](const DlSetupContext& ctx) { + dl_dither_setup(ctx); + ctx.paint.setDither(false); + }) + .with_bg(dither_bg)); + } } } } @@ -1526,11 +1853,11 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "Fill", - [=](SkCanvas*, SkPaint& p) { // - p.setStyle(SkPaint::kFill_Style); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kFill_Style); }, - [=](DlCanvas*, DlPaint& p) { // - p.setDrawStyle(DlDrawStyle::kFill); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kFill); })); // Skia on HW produces a strong miter consistent with width=1.0 // for any width less than a pixel, but the bounds computations of @@ -1540,156 +1867,157 @@ class CanvasCompareTester { // See https://bugs.chromium.org/p/skia/issues/detail?id=14046 bool no_hairlines = testP.is_draw_path() && - env.provider()->backend_type() != BackendType::kSoftware_Backend; + env.provider()->backend_type() != BackendType::kSoftwareBackend; RenderWith(testP, env, tolerance, CaseParameters( "Stroke + defaults", - [=](SkCanvas*, SkPaint& p) { // + [=](const SkSetupContext& ctx) { if (no_hairlines) { - p.setStrokeWidth(1.0); + ctx.paint.setStrokeWidth(1.0); } - p.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStyle(SkPaint::kStroke_Style); }, - [=](DlCanvas*, DlPaint& p) { // + [=](const DlSetupContext& ctx) { if (no_hairlines) { - p.setStrokeWidth(1.0); + ctx.paint.setStrokeWidth(1.0); } - p.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); })); RenderWith(testP, env, tolerance, CaseParameters( "Fill + unnecessary StrokeWidth 10", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kFill_Style); - p.setStrokeWidth(10.0); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kFill_Style); + ctx.paint.setStrokeWidth(10.0); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kFill); - p.setStrokeWidth(10.0); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kFill); + ctx.paint.setStrokeWidth(10.0); })); RenderEnvironment stroke_base_env = RenderEnvironment::MakeN32(env.provider()); - SkSetup sk_stroke_setup = [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); + SkSetup sk_stroke_setup = [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); }; - DlSetup dl_stroke_setup = [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); + DlSetup dl_stroke_setup = [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); }; stroke_base_env.init_ref(sk_stroke_setup, testP.sk_renderer(), - dl_stroke_setup, testP.dl_renderer()); + dl_stroke_setup, testP.dl_renderer(), + testP.imp_renderer()); quickCompareToReference(stroke_base_env, "stroke"); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 10", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(10.0); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(10.0); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(10.0); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(10.0); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5, Square Cap", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeCap(SkPaint::kSquare_Cap); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeCap(SkPaint::kSquare_Cap); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); - p.setStrokeCap(DlStrokeCap::kSquare); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeCap(DlStrokeCap::kSquare); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5, Round Cap", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeCap(SkPaint::kRound_Cap); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeCap(SkPaint::kRound_Cap); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); - p.setStrokeCap(DlStrokeCap::kRound); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeCap(DlStrokeCap::kRound); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5, Bevel Join", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeJoin(SkPaint::kBevel_Join); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeJoin(SkPaint::kBevel_Join); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); - p.setStrokeJoin(DlStrokeJoin::kBevel); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeJoin(DlStrokeJoin::kBevel); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5, Round Join", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeJoin(SkPaint::kRound_Join); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeJoin(SkPaint::kRound_Join); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); - p.setStrokeJoin(DlStrokeJoin::kRound); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeJoin(DlStrokeJoin::kRound); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5, Miter 10", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeMiter(10.0); - p.setStrokeJoin(SkPaint::kMiter_Join); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeMiter(10.0); + ctx.paint.setStrokeJoin(SkPaint::kMiter_Join); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); - p.setStrokeMiter(10.0); - p.setStrokeJoin(DlStrokeJoin::kMiter); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeMiter(10.0); + ctx.paint.setStrokeJoin(DlStrokeJoin::kMiter); })); RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "Stroke Width 5, Miter 0", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeMiter(0.0); - p.setStrokeJoin(SkPaint::kMiter_Join); + [=](const SkSetupContext& ctx) { + ctx.paint.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeMiter(0.0); + ctx.paint.setStrokeJoin(SkPaint::kMiter_Join); }, - [=](DlCanvas*, DlPaint& p) { - p.setDrawStyle(DlDrawStyle::kStroke); - p.setStrokeWidth(5.0); - p.setStrokeMiter(0.0); - p.setStrokeJoin(DlStrokeJoin::kMiter); + [=](const DlSetupContext& ctx) { + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setStrokeMiter(0.0); + ctx.paint.setStrokeJoin(DlStrokeJoin::kMiter); })); { @@ -1701,34 +2029,34 @@ class CanvasCompareTester { RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "PathEffect without forced stroking == Dash-29-2", - [=](SkCanvas*, SkPaint& p) { + [=](const SkSetupContext& ctx) { // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(sk_dash_effect); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setPathEffect(sk_dash_effect); }, - [=](DlCanvas*, DlPaint& p) { + [=](const DlSetupContext& ctx) { // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(dl_dash_effect); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setPathEffect(dl_dash_effect); })); } { RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "PathEffect == Dash-29-2", - [=](SkCanvas*, SkPaint& p) { + [=](const SkSetupContext& ctx) { // Need stroke style to see dashing properly - p.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStyle(SkPaint::kStroke_Style); // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(sk_dash_effect); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setPathEffect(sk_dash_effect); }, - [=](DlCanvas*, DlPaint& p) { + [=](const DlSetupContext& ctx) { // Need stroke style to see dashing properly - p.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(dl_dash_effect); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setPathEffect(dl_dash_effect); })); } dl_dash_effect = DlDashPathEffect::Make(test_dashes_2, 2, 0.0f); @@ -1737,19 +2065,19 @@ class CanvasCompareTester { RenderWith(testP, stroke_base_env, tolerance, CaseParameters( "PathEffect == Dash-17-1.5", - [=](SkCanvas*, SkPaint& p) { + [=](const SkSetupContext& ctx) { // Need stroke style to see dashing properly - p.setStyle(SkPaint::kStroke_Style); + ctx.paint.setStyle(SkPaint::kStroke_Style); // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(sk_dash_effect); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setPathEffect(sk_dash_effect); }, - [=](DlCanvas*, DlPaint& p) { + [=](const DlSetupContext& ctx) { // Need stroke style to see dashing properly - p.setDrawStyle(DlDrawStyle::kStroke); + ctx.paint.setDrawStyle(DlDrawStyle::kStroke); // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(dl_dash_effect); + ctx.paint.setStrokeWidth(5.0); + ctx.paint.setPathEffect(dl_dash_effect); })); } } @@ -1762,26 +2090,30 @@ class CanvasCompareTester { // bounds, then the estimate under rotation or skewing will be off // so we scale the padding by about 5% to compensate. BoundsTolerance skewed_tolerance = tolerance.mulScale(1.05, 1.05); - RenderWith(testP, env, tolerance, - CaseParameters( - "Translate 5, 10", // - [=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, - [=](DlCanvas* c, DlPaint&) { c->Translate(5, 10); })); - RenderWith(testP, env, tolerance, - CaseParameters( - "Scale +5%", // - [=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); }, - [=](DlCanvas* c, DlPaint&) { c->Scale(1.05, 1.05); })); - RenderWith(testP, env, skewed_tolerance, - CaseParameters( - "Rotate 5 degrees", // - [=](SkCanvas* c, SkPaint&) { c->rotate(5); }, - [=](DlCanvas* c, DlPaint&) { c->Rotate(5); })); - RenderWith(testP, env, skewed_tolerance, - CaseParameters( - "Skew 5%", // - [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, - [=](DlCanvas* c, DlPaint&) { c->Skew(0.05, 0.05); })); + RenderWith( // + testP, env, tolerance, + CaseParameters( + "Translate 5, 10", // + [=](const SkSetupContext& ctx) { ctx.canvas->translate(5, 10); }, + [=](const DlSetupContext& ctx) { ctx.canvas->Translate(5, 10); })); + RenderWith( // + testP, env, tolerance, + CaseParameters( + "Scale +5%", // + [=](const SkSetupContext& ctx) { ctx.canvas->scale(1.05, 1.05); }, + [=](const DlSetupContext& ctx) { ctx.canvas->Scale(1.05, 1.05); })); + RenderWith( // + testP, env, skewed_tolerance, + CaseParameters( + "Rotate 5 degrees", // + [=](const SkSetupContext& ctx) { ctx.canvas->rotate(5); }, + [=](const DlSetupContext& ctx) { ctx.canvas->Rotate(5); })); + RenderWith( // + testP, env, skewed_tolerance, + CaseParameters( + "Skew 5%", // + [=](const SkSetupContext& ctx) { ctx.canvas->skew(0.05, 0.05); }, + [=](const DlSetupContext& ctx) { ctx.canvas->Skew(0.05, 0.05); })); { // This rather odd transform can cause slight differences in // computing in-bounds samples depending on which base rendering @@ -1794,11 +2126,12 @@ class CanvasCompareTester { SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, // tweak, 1.0 + tweak, 10, // 0, 0, 1); - RenderWith(testP, env, skewed_tolerance, - CaseParameters( - "Transform 2D Affine", - [=](SkCanvas* c, SkPaint&) { c->concat(tx); }, - [=](DlCanvas* c, DlPaint&) { c->Transform(tx); })); + RenderWith( // + testP, env, skewed_tolerance, + CaseParameters( + "Transform 2D Affine", + [=](const SkSetupContext& ctx) { ctx.canvas->concat(tx); }, + [=](const DlSetupContext& ctx) { ctx.canvas->Transform(tx); })); } { SkM44 m44 = SkM44(1, 0, 0, kRenderCenterX, // @@ -1810,11 +2143,12 @@ class CanvasCompareTester { m44.preConcat( SkM44::Rotate({0, 1, 0}, math::kPi / 45)); // 4 degrees around Y m44.preTranslate(-kRenderCenterX, -kRenderCenterY); - RenderWith(testP, env, skewed_tolerance, - CaseParameters( - "Transform Full Perspective", - [=](SkCanvas* c, SkPaint&) { c->concat(m44); }, - [=](DlCanvas* c, DlPaint&) { c->Transform(m44); })); + RenderWith( // + testP, env, skewed_tolerance, + CaseParameters( + "Transform Full Perspective", + [=](const SkSetupContext& ctx) { ctx.canvas->concat(m44); }, + [=](const DlSetupContext& ctx) { ctx.canvas->Transform(m44); })); } } @@ -1835,29 +2169,29 @@ class CanvasCompareTester { RenderWith(testP, env, intersect_tolerance, CaseParameters( "Hard ClipRect inset by 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kIntersect, false); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRect(r_clip, SkClipOp::kIntersect, false); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipRect(r_clip, ClipOp::kIntersect, false); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipRect(r_clip, ClipOp::kIntersect, false); })); RenderWith(testP, env, intersect_tolerance, CaseParameters( "AntiAlias ClipRect inset by 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kIntersect, true); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRect(r_clip, SkClipOp::kIntersect, true); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipRect(r_clip, ClipOp::kIntersect, true); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipRect(r_clip, ClipOp::kIntersect, true); })); RenderWith(testP, env, diff_tolerance, CaseParameters( "Hard ClipRect Diff, inset by 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kDifference, false); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRect(r_clip, SkClipOp::kDifference, false); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipRect(r_clip, ClipOp::kDifference, false); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipRect(r_clip, ClipOp::kDifference, false); }) .with_diff_clip()); // This test RR clip used to use very small radii, but due to @@ -1869,29 +2203,31 @@ class CanvasCompareTester { RenderWith(testP, env, intersect_tolerance, CaseParameters( "Hard ClipRRect with radius of 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kIntersect, false); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRRect(rr_clip, SkClipOp::kIntersect, + false); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipRRect(rr_clip, ClipOp::kIntersect, false); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipRRect(rr_clip, ClipOp::kIntersect, false); })); RenderWith(testP, env, intersect_tolerance, CaseParameters( "AntiAlias ClipRRect with radius of 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kIntersect, true); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRRect(rr_clip, SkClipOp::kIntersect, true); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipRRect(rr_clip, ClipOp::kIntersect, true); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipRRect(rr_clip, ClipOp::kIntersect, true); })); RenderWith(testP, env, diff_tolerance, CaseParameters( "Hard ClipRRect Diff, with radius of 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kDifference, false); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRRect(rr_clip, SkClipOp::kDifference, + false); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipRRect(rr_clip, ClipOp::kDifference, false); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipRRect(rr_clip, ClipOp::kDifference, false); }) .with_diff_clip()); SkPath path_clip = SkPath(); @@ -1901,38 +2237,115 @@ class CanvasCompareTester { RenderWith(testP, env, intersect_tolerance, CaseParameters( "Hard ClipPath inset by 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kIntersect, false); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipPath(path_clip, SkClipOp::kIntersect, + false); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipPath(path_clip, ClipOp::kIntersect, false); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipPath(path_clip, ClipOp::kIntersect, false); })); RenderWith(testP, env, intersect_tolerance, CaseParameters( "AntiAlias ClipPath inset by 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kIntersect, true); + [=](const SkSetupContext& ctx) { + ctx.canvas->clipPath(path_clip, SkClipOp::kIntersect, + true); }, - [=](DlCanvas* c, DlPaint&) { - c->ClipPath(path_clip, ClipOp::kIntersect, true); + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipPath(path_clip, ClipOp::kIntersect, true); })); - RenderWith(testP, env, diff_tolerance, - CaseParameters( - "Hard ClipPath Diff, inset by 15.4", - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kDifference, false); - }, - [=](DlCanvas* c, DlPaint&) { - c->ClipPath(path_clip, ClipOp::kDifference, false); - }) - .with_diff_clip()); + RenderWith( + testP, env, diff_tolerance, + CaseParameters( + "Hard ClipPath Diff, inset by 15.4", + [=](const SkSetupContext& ctx) { + ctx.canvas->clipPath(path_clip, SkClipOp::kDifference, false); + }, + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipPath(path_clip, ClipOp::kDifference, false); + }) + .with_diff_clip()); + } + + enum class DirectoryStatus { + kExisted, + kCreated, + kFailed, + }; + + static DirectoryStatus CheckDir(const std::string& dir) { + auto ret = + fml::OpenDirectory(dir.c_str(), false, fml::FilePermission::kRead); + if (ret.is_valid()) { + return DirectoryStatus::kExisted; + } + ret = + fml::OpenDirectory(dir.c_str(), true, fml::FilePermission::kReadWrite); + if (ret.is_valid()) { + return DirectoryStatus::kCreated; + } + FML_LOG(ERROR) << "Could not create directory (" << dir + << ") for impeller failure images" + << ", ret = " << ret.get() << ", errno = " << errno; + return DirectoryStatus::kFailed; + } + + static void SetupImpellerFailureImageDirectory() { + std::string base_dir = "./impeller_failure_images"; + if (CheckDir(base_dir) == DirectoryStatus::kFailed) { + return; + } + for (int i = 0; i < 10000; i++) { + std::string sub_dir = std::to_string(i); + while (sub_dir.length() < 4) { + sub_dir = "0" + sub_dir; + } + std::string try_dir = base_dir + "/" + sub_dir; + switch (CheckDir(try_dir)) { + case DirectoryStatus::kExisted: + break; + case DirectoryStatus::kCreated: + ImpellerFailureImageDirectory = try_dir; + return; + case DirectoryStatus::kFailed: + return; + } + } + FML_LOG(ERROR) << "Too many output directories for Impeller failure images"; + } + + static void save_to_png(const RenderResult* result, + const std::string& op_desc, + const std::string& reason) { + if (!SaveImpellerFailureImages) { + return; + } + if (ImpellerFailureImageDirectory.length() == 0) { + SetupImpellerFailureImageDirectory(); + if (ImpellerFailureImageDirectory.length() == 0) { + SaveImpellerFailureImages = false; + return; + } + } + + std::string filename = ImpellerFailureImageDirectory + "/"; + for (const char& ch : op_desc) { + filename += (ch == ':' || ch == ' ') ? '_' : ch; + } + filename = filename + ".png"; + result->write(filename); + ImpellerFailureImages.push_back(filename); + FML_LOG(ERROR) << reason << ": " << filename; } static void RenderWith(const TestParameters& testP, const RenderEnvironment& env, const BoundsTolerance& tolerance_in, const CaseParameters& caseP) { - const std::string info = env.backend_name() + ": " + caseP.info(); + std::string test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + const std::string info = + env.backend_name() + ": " + test_name + " (" + caseP.info() + ")"; const DlColor bg = caseP.bg(); RenderJobInfo base_info = { .bg = bg, @@ -1940,15 +2353,16 @@ class CanvasCompareTester { // sk_result is a direct rendering via SkCanvas to SkSurface // DisplayList mechanisms are not involved in this operation - // SkPaint sk_paint; SkJobRenderer sk_job(caseP.sk_setup(), // testP.sk_renderer(), // - caseP.sk_restore()); + caseP.sk_restore(), // + env.sk_image()); auto sk_result = env.getResult(base_info, sk_job); DlJobRenderer dl_job(caseP.dl_setup(), // testP.dl_renderer(), // - caseP.dl_restore()); + caseP.dl_restore(), // + env.dl_image()); auto dl_result = env.getResult(base_info, dl_job); EXPECT_EQ(sk_job.setup_matrix(), dl_job.setup_matrix()); @@ -1968,10 +2382,50 @@ class CanvasCompareTester { if (testP.should_match(env, caseP, dl_job.setup_paint(), dl_job)) { quickCompareToReference(env.ref_sk_result(), sk_result.get(), true, - info + " (attribute has no effect)"); + info + " (attribute should not have effect)"); } else { quickCompareToReference(env.ref_sk_result(), sk_result.get(), false, - info + " (attribute affects rendering)"); + info + " (attribute should affect rendering)"); + } + + // If either the reference setup or the test setup contain attributes + // that Impeller doesn't support, we skip the Impeller testing. This + // is mostly stroked or patterned text which is vectored through drawPath + // for Impeller. + if (env.supports_impeller() && + testP.impeller_compatible(dl_job.setup_paint()) && + testP.impeller_compatible(env.ref_dl_paint())) { + DlJobRenderer imp_job(caseP.dl_setup(), // + testP.imp_renderer(), // + caseP.dl_restore(), // + env.impeller_image()); + auto imp_result = env.getImpellerResult(base_info, imp_job); + std::string imp_info = info + " (Impeller)"; + bool success = checkPixels(imp_result.get(), imp_result->render_bounds(), + imp_info, bg); + if (testP.should_match(env, caseP, imp_job.setup_paint(), imp_job)) { + success = success && // + quickCompareToReference( // + env.ref_impeller_result(), imp_result.get(), true, + imp_info + " (attribute should not have effect)"); + } else { + success = success && // + quickCompareToReference( // + env.ref_impeller_result(), imp_result.get(), false, + imp_info + " (attribute should affect rendering)"); + } + if (SaveImpellerFailureImages && !success) { + FML_LOG(ERROR) << "Impeller issue encountered for: " + << *imp_job.MakeDisplayList(base_info); + save_to_png(imp_result.get(), info + " (Impeller Result)", + "output saved in"); + save_to_png(env.ref_impeller_result(), info + " (Impeller Reference)", + "compare to reference without attributes"); + save_to_png(sk_result.get(), info + " (Skia Result)", + "and to Skia reference with attributes"); + save_to_png(env.ref_sk_result(), info + " (Skia Reference)", + "and to Skia reference without attributes"); + } } quickCompareToReference(sk_result.get(), dl_result.get(), true, @@ -2082,10 +2536,10 @@ class CanvasCompareTester { } static int groupOpacityFudgeFactor(const RenderEnvironment& env) { - if (env.format() == PixelFormat::k565_PixelFormat) { + if (env.format() == PixelFormat::k565PixelFormat) { return 9; } - if (env.provider()->backend_type() == BackendType::kOpenGL_Backend) { + if (env.provider()->backend_type() == BackendType::kOpenGlBackend) { // OpenGL gets a little fuzzy at times. Still, "within 5" (aka +/-4) // for byte samples is not bad, though the other backends give +/-1 return 5; @@ -2130,11 +2584,11 @@ class CanvasCompareTester { for (int x = 0; x < kTestWidth; x++) { uint32_t ref_pixel = ref_row[x]; uint32_t test_pixel = test_row[x]; - if (ref_pixel != bg.argb || test_pixel != bg.argb) { + if (ref_pixel != bg.argb() || test_pixel != bg.argb()) { pixels_touched++; for (int i = 0; i < 32; i += 8) { int ref_comp = (ref_pixel >> i) & 0xff; - int bg_comp = (bg.argb >> i) & 0xff; + int bg_comp = (bg.argb() >> i) & 0xff; SkScalar faded_comp = bg_comp + (ref_comp - bg_comp) * opacity; int test_comp = (test_pixel >> i) & 0xff; if (std::abs(faded_comp - test_comp) > fudge) { @@ -2156,14 +2610,16 @@ class CanvasCompareTester { ASSERT_LE(pixels_different, 1) << info; } - static void checkPixels(const RenderResult* ref_result, + static bool checkPixels(const RenderResult* ref_result, const SkRect ref_bounds, const std::string& info, - const DlColor bg) { + const DlColor bg = DlColor::kTransparent()) { uint32_t untouched = bg.premultipliedArgb(); int pixels_touched = 0; int pixels_oob = 0; SkIRect i_bounds = ref_bounds.roundOut(); + EXPECT_EQ(ref_result->width(), kTestWidth) << info; + EXPECT_EQ(ref_result->height(), kTestWidth) << info; for (int y = 0; y < kTestHeight; y++) { const uint32_t* ref_row = ref_result->addr32(0, y); for (int x = 0; x < kTestWidth; x++) { @@ -2175,8 +2631,9 @@ class CanvasCompareTester { } } } - ASSERT_EQ(pixels_oob, 0) << info; - ASSERT_GT(pixels_touched, 0) << info; + EXPECT_EQ(pixels_oob, 0) << info; + EXPECT_GT(pixels_touched, 0) << info; + return pixels_oob == 0 && pixels_touched > 0; } static int countModifiedTransparentPixels(const RenderResult* ref_result, @@ -2202,14 +2659,14 @@ class CanvasCompareTester { info + " reference rendering"); } - static void quickCompareToReference(const RenderResult* ref_result, + static bool quickCompareToReference(const RenderResult* ref_result, const RenderResult* test_result, bool should_match, const std::string& info) { int w = test_result->width(); int h = test_result->height(); - ASSERT_EQ(w, ref_result->width()) << info; - ASSERT_EQ(h, ref_result->height()) << info; + EXPECT_EQ(w, ref_result->width()) << info; + EXPECT_EQ(h, ref_result->height()) << info; int pixels_different = 0; for (int y = 0; y < h; y++) { const uint32_t* ref_row = ref_result->addr32(0, y); @@ -2224,9 +2681,11 @@ class CanvasCompareTester { } } if (should_match) { - ASSERT_EQ(pixels_different, 0) << info; + EXPECT_EQ(pixels_different, 0) << info; + return pixels_different == 0; } else { - ASSERT_NE(pixels_different, 0) << info; + EXPECT_NE(pixels_different, 0) << info; + return pixels_different != 0; } } @@ -2343,31 +2802,6 @@ class CanvasCompareTester { } } - static const sk_sp kTestImage; - static const sk_sp makeTestImage() { - sk_sp surface = SkSurfaces::Raster( - SkImageInfo::MakeN32Premul(kRenderWidth, kRenderHeight)); - SkCanvas* canvas = surface->getCanvas(); - SkPaint p0, p1; - p0.setStyle(SkPaint::kFill_Style); - p0.setColor(SkColorSetARGB(0xff, 0x00, 0xfe, 0x00)); // off-green - p1.setStyle(SkPaint::kFill_Style); - p1.setColor(SK_ColorBLUE); - // Some pixels need some transparency for DstIn testing - p1.setAlpha(128); - int cbdim = 5; - for (int y = 0; y < kRenderHeight; y += cbdim) { - for (int x = 0; x < kRenderWidth; x += cbdim) { - SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1; - canvas->drawRect(SkRect::MakeXYWH(x, y, cbdim, cbdim), cellp); - } - } - return surface->makeImageSnapshot(); - } - - static const DlImageColorSource kTestDlImageColorSource; - static const sk_sp kTestSkImageColorSource; - static sk_sp MakeTextBlob(const std::string& string, SkScalar font_height) { SkFont font(SkTypeface::MakeFromName("ahem", SkFontStyle::Normal()), @@ -2377,30 +2811,22 @@ class CanvasCompareTester { } }; -std::vector> - CanvasCompareTester::kTestProviders; +std::vector CanvasCompareTester::TestBackends; +std::string CanvasCompareTester::ImpellerFailureImageDirectory = ""; +bool CanvasCompareTester::SaveImpellerFailureImages = false; +std::vector CanvasCompareTester::ImpellerFailureImages; BoundsTolerance CanvasCompareTester::DefaultTolerance = BoundsTolerance().addAbsolutePadding(1, 1); -const sk_sp CanvasCompareTester::kTestImage = makeTestImage(); -const DlImageColorSource CanvasCompareTester::kTestDlImageColorSource( - DlImage::Make(kTestImage), - DlTileMode::kRepeat, - DlTileMode::kRepeat, - DlImageSampling::kLinear); -const sk_sp CanvasCompareTester::kTestSkImageColorSource = - kTestImage->makeShader(SkTileMode::kRepeat, - SkTileMode::kRepeat, - SkImageSampling::kLinear); - // Eventually this bare bones testing::Test fixture will subsume the // CanvasCompareTester and the TestParameters could then become just // configuration calls made upon the fixture. template -class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags { +class DisplayListRenderingTestBase : public BaseT, + protected DisplayListOpFlags { public: - DisplayListCanvasTestBase() = default; + DisplayListRenderingTestBase() = default; static bool StartsWith(std::string str, std::string prefix) { if (prefix.length() > str.length()) { @@ -2414,17 +2840,6 @@ class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags { return true; } - static void AddProvider(BackendType type, const std::string& name) { - auto provider = DlSurfaceProvider::Create(type); - if (provider == nullptr) { - FML_LOG(ERROR) << "provider " << name << " not supported (ignoring)"; - return; - } - provider->InitializeSurface(kTestWidth, kTestHeight, - PixelFormat::kN32Premul_PixelFormat); - CanvasCompareTester::kTestProviders.push_back(std::move(provider)); - } - static void SetUpTestSuite() { bool do_software = true; bool do_opengl = false; @@ -2433,6 +2848,10 @@ class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags { for (auto p_arg = std::next(args.begin()); p_arg != args.end(); p_arg++) { std::string arg = *p_arg; bool enable = true; + if (arg == "--save-impeller-failures") { + CanvasCompareTester::SaveImpellerFailureImages = true; + continue; + } if (StartsWith(arg, "--no")) { enable = false; arg = "-" + arg.substr(4); @@ -2446,84 +2865,90 @@ class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags { } } if (do_software) { - AddProvider(BackendType::kSoftware_Backend, "Software"); + CanvasCompareTester::AddProvider(BackendType::kSoftwareBackend); } if (do_opengl) { - AddProvider(BackendType::kOpenGL_Backend, "OpenGL"); + CanvasCompareTester::AddProvider(BackendType::kOpenGlBackend); } if (do_metal) { - AddProvider(BackendType::kMetal_Backend, "Metal"); + CanvasCompareTester::AddProvider(BackendType::kMetalBackend); } std::string providers = ""; - auto begin = CanvasCompareTester::kTestProviders.cbegin(); - auto end = CanvasCompareTester::kTestProviders.cend(); - while (begin != end) { - providers += " " + (*begin++)->backend_name(); + for (auto& back_end : CanvasCompareTester::TestBackends) { + providers += " " + DlSurfaceProvider::BackendName(back_end); } FML_LOG(INFO) << "Running tests on [" << providers << " ]"; } static void TearDownTestSuite() { - // Deleting these provider objects allows Metal to clean up its - // resources before the exit handler reports them as leaks. - CanvasCompareTester::kTestProviders.clear(); + if (CanvasCompareTester::ImpellerFailureImages.size() > 0) { + FML_LOG(INFO); + FML_LOG(INFO) << CanvasCompareTester::ImpellerFailureImages.size() + << " images saved in " + << CanvasCompareTester::ImpellerFailureImageDirectory; + for (auto filename : CanvasCompareTester::ImpellerFailureImages) { + FML_LOG(INFO) << " " << filename; + } + FML_LOG(INFO); + } } private: - FML_DISALLOW_COPY_AND_ASSIGN(DisplayListCanvasTestBase); + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListRenderingTestBase); }; -using DisplayListCanvas = DisplayListCanvasTestBase<::testing::Test>; +using DisplayListRendering = DisplayListRenderingTestBase<::testing::Test>; -TEST_F(DisplayListCanvas, DrawPaint) { +TEST_F(DisplayListRendering, DrawPaint) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawPaint(paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawPaint(ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawPaint(paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawPaint(ctx.paint); }, kDrawPaintFlags)); } -TEST_F(DisplayListCanvas, DrawOpaqueColor) { +TEST_F(DisplayListRendering, DrawOpaqueColor) { // We use a non-opaque color to avoid obliterating any backdrop filter output CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { + [=](const SkRenderContext& ctx) { // DrawColor is not tested against attributes because it is supposed // to ignore them. So, if the paint has an alpha, it is because we // are doing a saveLayer+backdrop test and we need to not flood over // the backdrop output with a solid color. So, we perform an alpha // drawColor for that case only. - SkColor color = SkColorSetA(SK_ColorMAGENTA, paint.getAlpha()); - canvas->drawColor(color); + SkColor color = SkColorSetA(SK_ColorMAGENTA, ctx.paint.getAlpha()); + ctx.canvas->drawColor(color); }, - [=](DlCanvas* canvas, const DlPaint& paint) { + [=](const DlRenderContext& ctx) { // DrawColor is not tested against attributes because it is supposed // to ignore them. So, if the paint has an alpha, it is because we // are doing a saveLayer+backdrop test and we need to not flood over // the backdrop output with a solid color. So, we transfer the alpha // from the paint for that case only. - canvas->DrawColor(DlColor::kMagenta().withAlpha(paint.getAlpha())); + ctx.canvas->DrawColor( + DlColor::kMagenta().withAlpha(ctx.paint.getAlpha())); }, kDrawColorFlags)); } -TEST_F(DisplayListCanvas, DrawAlphaColor) { +TEST_F(DisplayListRendering, DrawAlphaColor) { // We use a non-opaque color to avoid obliterating any backdrop filter output CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawColor(0x7FFF00FF); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawColor(0x7FFF00FF); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawColor(DlColor::kMagenta().withAlpha(0x7f)); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawColor(DlColor(0x7FFF00FF)); }, kDrawColorFlags)); } -TEST_F(DisplayListCanvas, DrawDiagonalLines) { +TEST_F(DisplayListRendering, DrawDiagonalLines) { SkPoint p1 = SkPoint::Make(kRenderLeft, kRenderTop); SkPoint p2 = SkPoint::Make(kRenderRight, kRenderBottom); SkPoint p3 = SkPoint::Make(kRenderLeft, kRenderBottom); @@ -2537,125 +2962,125 @@ TEST_F(DisplayListCanvas, DrawDiagonalLines) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // + [=](const SkRenderContext& ctx) { // // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; + SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); - canvas->drawLine(p1, p2, p); - canvas->drawLine(p3, p4, p); - canvas->drawLine(p5, p6, p); - canvas->drawLine(p7, p8, p); + ctx.canvas->drawLine(p1, p2, p); + ctx.canvas->drawLine(p3, p4, p); + ctx.canvas->drawLine(p5, p6, p); + ctx.canvas->drawLine(p7, p8, p); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawLine(p1, p2, paint); - canvas->DrawLine(p3, p4, paint); - canvas->DrawLine(p5, p6, paint); - canvas->DrawLine(p7, p8, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawLine(p1, p2, ctx.paint); + ctx.canvas->DrawLine(p3, p4, ctx.paint); + ctx.canvas->DrawLine(p5, p6, ctx.paint); + ctx.canvas->DrawLine(p7, p8, ctx.paint); }, kDrawLineFlags) .set_draw_line()); } -TEST_F(DisplayListCanvas, DrawHorizontalLine) { +TEST_F(DisplayListRendering, DrawHorizontalLine) { SkPoint p1 = SkPoint::Make(kRenderLeft, kRenderCenterY); SkPoint p2 = SkPoint::Make(kRenderRight, kRenderCenterY); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // + [=](const SkRenderContext& ctx) { // // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; + SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); - canvas->drawLine(p1, p2, p); + ctx.canvas->drawLine(p1, p2, p); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawLine(p1, p2, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawLine(p1, p2, ctx.paint); }, kDrawHVLineFlags) .set_draw_line() .set_horizontal_line()); } -TEST_F(DisplayListCanvas, DrawVerticalLine) { +TEST_F(DisplayListRendering, DrawVerticalLine) { SkPoint p1 = SkPoint::Make(kRenderCenterX, kRenderTop); SkPoint p2 = SkPoint::Make(kRenderCenterY, kRenderBottom); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // + [=](const SkRenderContext& ctx) { // // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; + SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); - canvas->drawLine(p1, p2, p); + ctx.canvas->drawLine(p1, p2, p); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawLine(p1, p2, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawLine(p1, p2, ctx.paint); }, kDrawHVLineFlags) .set_draw_line() .set_vertical_line()); } -TEST_F(DisplayListCanvas, DrawRect) { +TEST_F(DisplayListRendering, DrawRect) { // Bounds are offset by 0.5 pixels to induce AA CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawRect(kRenderBounds.makeOffset(0.5, 0.5), paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawRect(kRenderBounds.makeOffset(0.5, 0.5), ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawRect(kRenderBounds.makeOffset(0.5, 0.5), paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawRect(kRenderBounds.makeOffset(0.5, 0.5), ctx.paint); }, kDrawRectFlags)); } -TEST_F(DisplayListCanvas, DrawOval) { +TEST_F(DisplayListRendering, DrawOval) { SkRect rect = kRenderBounds.makeInset(0, 10); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawOval(rect, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawOval(rect, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawOval(rect, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawOval(rect, ctx.paint); }, kDrawOvalFlags)); } -TEST_F(DisplayListCanvas, DrawCircle) { +TEST_F(DisplayListRendering, DrawCircle) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawCircle(kTestCenter, kRenderRadius, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawCircle(kTestCenter, kRenderRadius, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawCircle(kTestCenter, kRenderRadius, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawCircle(kTestCenter, kRenderRadius, ctx.paint); }, kDrawCircleFlags)); } -TEST_F(DisplayListCanvas, DrawRRect) { +TEST_F(DisplayListRendering, DrawRRect) { SkRRect rrect = SkRRect::MakeRectXY(kRenderBounds, kRenderCornerRadius, kRenderCornerRadius); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawRRect(rrect, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawRRect(rrect, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawRRect(rrect, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawRRect(rrect, ctx.paint); }, kDrawRRectFlags)); } -TEST_F(DisplayListCanvas, DrawDRRect) { +TEST_F(DisplayListRendering, DrawDRRect) { SkRRect outer = SkRRect::MakeRectXY(kRenderBounds, kRenderCornerRadius, kRenderCornerRadius); SkRect inner_bounds = kRenderBounds.makeInset(30.0, 30.0); @@ -2663,16 +3088,16 @@ TEST_F(DisplayListCanvas, DrawDRRect) { kRenderCornerRadius); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawDRRect(outer, inner, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawDRRect(outer, inner, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawDRRect(outer, inner, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawDRRect(outer, inner, ctx.paint); }, kDrawDRRectFlags)); } -TEST_F(DisplayListCanvas, DrawPath) { +TEST_F(DisplayListRendering, DrawPath) { SkPath path; // unclosed lines to show some caps @@ -2697,29 +3122,29 @@ TEST_F(DisplayListCanvas, DrawPath) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawPath(path, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawPath(path, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawPath(path, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawPath(path, ctx.paint); }, kDrawPathFlags) .set_draw_path()); } -TEST_F(DisplayListCanvas, DrawArc) { +TEST_F(DisplayListRendering, DrawArc) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawArc(kRenderBounds, 60, 330, false, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawArc(kRenderBounds, 60, 330, false, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawArc(kRenderBounds, 60, 330, false, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawArc(kRenderBounds, 60, 330, false, ctx.paint); }, kDrawArcNoCenterFlags)); } -TEST_F(DisplayListCanvas, DrawArcCenter) { +TEST_F(DisplayListRendering, DrawArcCenter) { // Center arcs that inscribe nearly a whole circle except for a small // arc extent gap have 2 angles that may appear or disappear at the // various miter limits tested (0, 4, and 10). @@ -2732,17 +3157,17 @@ TEST_F(DisplayListCanvas, DrawArcCenter) { // Limit == 10, edge and center corners all miter CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawArc(kRenderBounds, 60, 360 - 12, true, paint); + [=](const SkRenderContext& ctx) { // + ctx.canvas->drawArc(kRenderBounds, 60, 360 - 12, true, ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawArc(kRenderBounds, 60, 360 - 12, true, paint); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawArc(kRenderBounds, 60, 360 - 12, true, ctx.paint); }, kDrawArcWithCenterFlags) .set_draw_arc_center()); } -TEST_F(DisplayListCanvas, DrawPointsAsPoints) { +TEST_F(DisplayListRendering, DrawPointsAsPoints) { // The +/- 16 points are designed to fall just inside the clips // that are tested against so we avoid lots of undrawn pixels // in the accumulated bounds. @@ -2777,23 +3202,25 @@ TEST_F(DisplayListCanvas, DrawPointsAsPoints) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // + [=](const SkRenderContext& ctx) { // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; + SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); - canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p); + auto mode = SkCanvas::kPoints_PointMode; + ctx.canvas->drawPoints(mode, count, points, p); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawPoints(PointMode::kPoints, count, points, paint); + [=](const DlRenderContext& ctx) { + auto mode = PointMode::kPoints; + ctx.canvas->DrawPoints(mode, count, points, ctx.paint); }, kDrawPointsAsPointsFlags) .set_draw_line() .set_ignores_dashes()); } -TEST_F(DisplayListCanvas, DrawPointsAsLines) { +TEST_F(DisplayListRendering, DrawPointsAsLines) { const SkScalar x0 = kRenderLeft + 1; const SkScalar x1 = kRenderLeft + 16; const SkScalar x2 = kRenderRight - 16; @@ -2827,21 +3254,23 @@ TEST_F(DisplayListCanvas, DrawPointsAsLines) { ASSERT_TRUE((count & 1) == 0); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // + [=](const SkRenderContext& ctx) { // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; + SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); - canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p); + auto mode = SkCanvas::kLines_PointMode; + ctx.canvas->drawPoints(mode, count, points, p); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawPoints(PointMode::kLines, count, points, paint); + [=](const DlRenderContext& ctx) { + auto mode = PointMode::kLines; + ctx.canvas->DrawPoints(mode, count, points, ctx.paint); }, kDrawPointsAsLinesFlags)); } -TEST_F(DisplayListCanvas, DrawPointsAsPolygon) { +TEST_F(DisplayListRendering, DrawPointsAsPolygon) { const SkPoint points1[] = { // RenderBounds box with a diamond SkPoint::Make(kRenderLeft, kRenderTop), @@ -2858,22 +3287,23 @@ TEST_F(DisplayListCanvas, DrawPointsAsPolygon) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // + [=](const SkRenderContext& ctx) { // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; + SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); - canvas->drawPoints(SkCanvas::kPolygon_PointMode, count1, points1, - p); + auto mode = SkCanvas::kPolygon_PointMode; + ctx.canvas->drawPoints(mode, count1, points1, p); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawPoints(PointMode::kPolygon, count1, points1, paint); + [=](const DlRenderContext& ctx) { + auto mode = PointMode::kPolygon; + ctx.canvas->DrawPoints(mode, count1, points1, ctx.paint); }, kDrawPointsAsPolygonFlags)); } -TEST_F(DisplayListCanvas, DrawVerticesWithColors) { +TEST_F(DisplayListRendering, DrawVerticesWithColors) { // Cover as many sides of the box with only 6 vertices: // +----------+ // |xxxxxxxxxx| @@ -2909,16 +3339,18 @@ TEST_F(DisplayListCanvas, DrawVerticesWithColors) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawVertices(sk_vertices, SkBlendMode::kSrcOver, paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawVertices(sk_vertices, SkBlendMode::kSrcOver, + ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawVertices(dl_vertices, DlBlendMode::kSrcOver, paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawVertices(dl_vertices, DlBlendMode::kSrcOver, + ctx.paint); }, kDrawVerticesFlags)); } -TEST_F(DisplayListCanvas, DrawVerticesWithImage) { +TEST_F(DisplayListRendering, DrawVerticesWithImage) { // Cover as many sides of the box with only 6 vertices: // +----------+ // |xxxxxxxxxx| @@ -2953,181 +3385,175 @@ TEST_F(DisplayListCanvas, DrawVerticesWithImage) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - SkPaint v_paint = paint; + [=](const SkRenderContext& ctx) { // + SkPaint v_paint = ctx.paint; if (v_paint.getShader() == nullptr) { - v_paint.setShader(CanvasCompareTester::kTestSkImageColorSource); + v_paint.setShader(MakeColorSource(ctx.image)); } - canvas->drawVertices(sk_vertices, SkBlendMode::kSrcOver, v_paint); + ctx.canvas->drawVertices(sk_vertices, SkBlendMode::kSrcOver, + v_paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - DlPaint v_paint = paint; + [=](const DlRenderContext& ctx) { // + DlPaint v_paint = ctx.paint; if (v_paint.getColorSource() == nullptr) { - v_paint.setColorSource( - &CanvasCompareTester::kTestDlImageColorSource); + v_paint.setColorSource(MakeColorSource(ctx.image)); } - canvas->DrawVertices(dl_vertices, DlBlendMode::kSrcOver, v_paint); + ctx.canvas->DrawVertices(dl_vertices, DlBlendMode::kSrcOver, + v_paint); }, kDrawVerticesFlags)); } -TEST_F(DisplayListCanvas, DrawImageNearest) { +TEST_F(DisplayListRendering, DrawImageNearest) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::kTestImage, // - kRenderLeft, kRenderTop, - SkImageSampling::kNearestNeighbor, &paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImage(ctx.image, kRenderLeft, kRenderTop, + SkImageSampling::kNearestNeighbor, + &ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawImage(DlImage::Make(CanvasCompareTester::kTestImage), - SkPoint::Make(kRenderLeft, kRenderTop), - DlImageSampling::kNearestNeighbor, &paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImage(ctx.image, // + SkPoint::Make(kRenderLeft, kRenderTop), + DlImageSampling::kNearestNeighbor, + &ctx.paint); }, kDrawImageWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawImageNearestNoPaint) { +TEST_F(DisplayListRendering, DrawImageNearestNoPaint) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::kTestImage, // - kRenderLeft, kRenderTop, - SkImageSampling::kNearestNeighbor, nullptr); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImage(ctx.image, kRenderLeft, kRenderTop, + SkImageSampling::kNearestNeighbor, nullptr); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawImage(DlImage::Make(CanvasCompareTester::kTestImage), - SkPoint::Make(kRenderLeft, kRenderTop), - DlImageSampling::kNearestNeighbor, nullptr); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImage(ctx.image, + SkPoint::Make(kRenderLeft, kRenderTop), + DlImageSampling::kNearestNeighbor, nullptr); }, kDrawImageFlags)); } -TEST_F(DisplayListCanvas, DrawImageLinear) { +TEST_F(DisplayListRendering, DrawImageLinear) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::kTestImage, // - kRenderLeft, kRenderTop, SkImageSampling::kLinear, - &paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImage(ctx.image, kRenderLeft, kRenderTop, + SkImageSampling::kLinear, &ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawImage(DlImage::Make(CanvasCompareTester::kTestImage), - SkPoint::Make(kRenderLeft, kRenderTop), - DlImageSampling::kLinear, &paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImage(ctx.image, + SkPoint::Make(kRenderLeft, kRenderTop), + DlImageSampling::kLinear, &ctx.paint); }, kDrawImageWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawImageRectNearest) { +TEST_F(DisplayListRendering, DrawImageRectNearest) { SkRect src = SkRect::MakeIWH(kRenderWidth, kRenderHeight).makeInset(5, 5); SkRect dst = kRenderBounds.makeInset(10.5, 10.5); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::kTestImage, src, dst, - SkImageSampling::kNearestNeighbor, &paint, - SkCanvas::kFast_SrcRectConstraint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImageRect( + ctx.image, src, dst, SkImageSampling::kNearestNeighbor, + &ctx.paint, SkCanvas::kFast_SrcRectConstraint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawImageRect( - DlImage::Make(CanvasCompareTester::kTestImage), src, dst, - DlImageSampling::kNearestNeighbor, &paint, - DlCanvas::SrcRectConstraint::kFast); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImageRect( + ctx.image, src, dst, DlImageSampling::kNearestNeighbor, + &ctx.paint, DlCanvas::SrcRectConstraint::kFast); }, kDrawImageRectWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawImageRectNearestNoPaint) { +TEST_F(DisplayListRendering, DrawImageRectNearestNoPaint) { SkRect src = SkRect::MakeIWH(kRenderWidth, kRenderHeight).makeInset(5, 5); SkRect dst = kRenderBounds.makeInset(10.5, 10.5); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::kTestImage, src, dst, - SkImageSampling::kNearestNeighbor, nullptr, - SkCanvas::kFast_SrcRectConstraint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImageRect( + ctx.image, src, dst, SkImageSampling::kNearestNeighbor, // + nullptr, SkCanvas::kFast_SrcRectConstraint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawImageRect( - DlImage::Make(CanvasCompareTester::kTestImage), src, dst, - DlImageSampling::kNearestNeighbor, nullptr, - DlCanvas::SrcRectConstraint::kFast); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImageRect( + ctx.image, src, dst, DlImageSampling::kNearestNeighbor, // + nullptr, DlCanvas::SrcRectConstraint::kFast); }, kDrawImageRectFlags)); } -TEST_F(DisplayListCanvas, DrawImageRectLinear) { +TEST_F(DisplayListRendering, DrawImageRectLinear) { SkRect src = SkRect::MakeIWH(kRenderWidth, kRenderHeight).makeInset(5, 5); SkRect dst = kRenderBounds.makeInset(10.5, 10.5); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::kTestImage, src, dst, - SkImageSampling::kLinear, &paint, - SkCanvas::kFast_SrcRectConstraint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImageRect( + ctx.image, src, dst, SkImageSampling::kLinear, // + &ctx.paint, SkCanvas::kFast_SrcRectConstraint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawImageRect( - DlImage::Make(CanvasCompareTester::kTestImage), src, dst, - DlImageSampling::kLinear, &paint, - DlCanvas::SrcRectConstraint::kFast); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawImageRect( + ctx.image, src, dst, DlImageSampling::kLinear, // + &ctx.paint, DlCanvas::SrcRectConstraint::kFast); }, kDrawImageRectWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawImageNineNearest) { +TEST_F(DisplayListRendering, DrawImageNineNearest) { SkIRect src = SkIRect::MakeWH(kRenderWidth, kRenderHeight).makeInset(25, 25); SkRect dst = kRenderBounds.makeInset(10.5, 10.5); - sk_sp image = CanvasCompareTester::kTestImage; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kNearest, - &paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImageNine(ctx.image.get(), src, dst, + SkFilterMode::kNearest, &ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawImageNine(DlImage::Make(image), src, dst, - DlFilterMode::kNearest, &paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImageNine(ctx.image, src, dst, + DlFilterMode::kNearest, &ctx.paint); }, kDrawImageNineWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawImageNineNearestNoPaint) { +TEST_F(DisplayListRendering, DrawImageNineNearestNoPaint) { SkIRect src = SkIRect::MakeWH(kRenderWidth, kRenderHeight).makeInset(25, 25); SkRect dst = kRenderBounds.makeInset(10.5, 10.5); - sk_sp image = CanvasCompareTester::kTestImage; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kNearest, - nullptr); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImageNine(ctx.image.get(), src, dst, + SkFilterMode::kNearest, nullptr); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawImageNine(DlImage::Make(image), src, dst, - DlFilterMode::kNearest, nullptr); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImageNine(ctx.image, src, dst, + DlFilterMode::kNearest, nullptr); }, kDrawImageNineFlags)); } -TEST_F(DisplayListCanvas, DrawImageNineLinear) { +TEST_F(DisplayListRendering, DrawImageNineLinear) { SkIRect src = SkIRect::MakeWH(kRenderWidth, kRenderHeight).makeInset(25, 25); SkRect dst = kRenderBounds.makeInset(10.5, 10.5); - sk_sp image = CanvasCompareTester::kTestImage; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kLinear, - &paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawImageNine(ctx.image.get(), src, dst, + SkFilterMode::kLinear, &ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawImageNine(DlImage::Make(image), src, dst, - DlFilterMode::kLinear, &paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawImageNine(ctx.image, src, dst, + DlFilterMode::kLinear, &ctx.paint); }, kDrawImageNineWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawAtlasNearest) { +TEST_F(DisplayListRendering, DrawAtlasNearest) { const SkRSXform xform[] = { // clang-format off { 1.2f, 0.0f, kRenderLeft, kRenderTop}, @@ -3156,25 +3582,24 @@ TEST_F(DisplayListCanvas, DrawAtlasNearest) { DlColor::kYellow(), DlColor::kMagenta(), }; - const sk_sp image = CanvasCompareTester::kTestImage; const DlImageSampling dl_sampling = DlImageSampling::kNearestNeighbor; const SkSamplingOptions sk_sampling = SkImageSampling::kNearestNeighbor; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawAtlas(image.get(), xform, tex, sk_colors, 4, - SkBlendMode::kSrcOver, sk_sampling, nullptr, - &paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawAtlas(ctx.image.get(), xform, tex, sk_colors, 4, + SkBlendMode::kSrcOver, sk_sampling, nullptr, + &ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawAtlas(DlImage::Make(image), xform, tex, dl_colors, 4, - DlBlendMode::kSrcOver, dl_sampling, nullptr, - &paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawAtlas(ctx.image, xform, tex, dl_colors, 4, + DlBlendMode::kSrcOver, dl_sampling, nullptr, + &ctx.paint); }, kDrawAtlasWithPaintFlags)); } -TEST_F(DisplayListCanvas, DrawAtlasNearestNoPaint) { +TEST_F(DisplayListRendering, DrawAtlasNearestNoPaint) { const SkRSXform xform[] = { // clang-format off { 1.2f, 0.0f, kRenderLeft, kRenderTop}, @@ -3203,25 +3628,24 @@ TEST_F(DisplayListCanvas, DrawAtlasNearestNoPaint) { DlColor::kYellow(), DlColor::kMagenta(), }; - const sk_sp image = CanvasCompareTester::kTestImage; const DlImageSampling dl_sampling = DlImageSampling::kNearestNeighbor; const SkSamplingOptions sk_sampling = SkImageSampling::kNearestNeighbor; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawAtlas(image.get(), xform, tex, sk_colors, 4, - SkBlendMode::kSrcOver, sk_sampling, // - nullptr, nullptr); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawAtlas(ctx.image.get(), xform, tex, sk_colors, 4, + SkBlendMode::kSrcOver, sk_sampling, // + nullptr, nullptr); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawAtlas(DlImage::Make(image), xform, tex, dl_colors, 4, - DlBlendMode::kSrcOver, dl_sampling, nullptr, - nullptr); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawAtlas(ctx.image, xform, tex, dl_colors, 4, + DlBlendMode::kSrcOver, dl_sampling, // + nullptr, nullptr); }, kDrawAtlasFlags)); } -TEST_F(DisplayListCanvas, DrawAtlasLinear) { +TEST_F(DisplayListRendering, DrawAtlasLinear) { const SkRSXform xform[] = { // clang-format off { 1.2f, 0.0f, kRenderLeft, kRenderTop}, @@ -3250,20 +3674,19 @@ TEST_F(DisplayListCanvas, DrawAtlasLinear) { DlColor::kYellow(), DlColor::kMagenta(), }; - const sk_sp image = CanvasCompareTester::kTestImage; const DlImageSampling dl_sampling = DlImageSampling::kLinear; const SkSamplingOptions sk_sampling = SkImageSampling::kLinear; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { - canvas->drawAtlas(image.get(), xform, tex, sk_colors, 2, // - SkBlendMode::kSrcOver, sk_sampling, nullptr, - &paint); + [=](const SkRenderContext& ctx) { + ctx.canvas->drawAtlas(ctx.image.get(), xform, tex, sk_colors, 2, + SkBlendMode::kSrcOver, sk_sampling, // + nullptr, &ctx.paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { - canvas->DrawAtlas(DlImage::Make(image), xform, tex, dl_colors, 2, - DlBlendMode::kSrcOver, dl_sampling, nullptr, - &paint); + [=](const DlRenderContext& ctx) { + ctx.canvas->DrawAtlas(ctx.image, xform, tex, dl_colors, 2, + DlBlendMode::kSrcOver, dl_sampling, // + nullptr, &ctx.paint); }, kDrawAtlasWithPaintFlags)); } @@ -3272,36 +3695,36 @@ sk_sp makeTestDisplayList() { DisplayListBuilder builder; DlPaint paint; paint.setDrawStyle(DlDrawStyle::kFill); - paint.setColor(SK_ColorRED); + paint.setColor(DlColor(SK_ColorRED)); builder.DrawRect({kRenderLeft, kRenderTop, kRenderCenterX, kRenderCenterY}, paint); - paint.setColor(SK_ColorBLUE); + paint.setColor(DlColor(SK_ColorBLUE)); builder.DrawRect({kRenderCenterX, kRenderTop, kRenderRight, kRenderCenterY}, paint); - paint.setColor(SK_ColorGREEN); + paint.setColor(DlColor(SK_ColorGREEN)); builder.DrawRect({kRenderLeft, kRenderCenterY, kRenderCenterX, kRenderBottom}, paint); - paint.setColor(SK_ColorYELLOW); + paint.setColor(DlColor(SK_ColorYELLOW)); builder.DrawRect( {kRenderCenterX, kRenderCenterY, kRenderRight, kRenderBottom}, paint); return builder.Build(); } -TEST_F(DisplayListCanvas, DrawDisplayList) { +TEST_F(DisplayListRendering, DrawDisplayList) { sk_sp display_list = makeTestDisplayList(); CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - DlSkCanvasAdapter(canvas).DrawDisplayList(display_list); + [=](const SkRenderContext& ctx) { // + DlSkCanvasAdapter(ctx.canvas).DrawDisplayList(display_list); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawDisplayList(display_list); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawDisplayList(display_list); }, kDrawDisplayListFlags) .set_draw_display_list()); } -TEST_F(DisplayListCanvas, DrawTextBlob) { +TEST_F(DisplayListRendering, DrawTextBlob) { // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the // performance overlay can use Fuchsia's font manager instead of the empty // default. @@ -3310,20 +3733,33 @@ TEST_F(DisplayListCanvas, DrawTextBlob) { #else sk_sp blob = CanvasCompareTester::MakeTextBlob("Testing", kRenderHeight * 0.33f); +#ifdef IMPELLER_SUPPORTS_RENDERING + auto frame = impeller::MakeTextFrameFromTextBlobSkia(blob); +#endif // IMPELLER_SUPPORTS_RENDERING SkScalar render_y_1_3 = kRenderTop + kRenderHeight * 0.3; SkScalar render_y_2_3 = kRenderTop + kRenderHeight * 0.6; CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - canvas->drawTextBlob(blob, kRenderLeft, render_y_1_3, paint); - canvas->drawTextBlob(blob, kRenderLeft, render_y_2_3, paint); - canvas->drawTextBlob(blob, kRenderLeft, kRenderBottom, paint); + [=](const SkRenderContext& ctx) { + auto paint = ctx.paint; + ctx.canvas->drawTextBlob(blob, kRenderLeft, render_y_1_3, paint); + ctx.canvas->drawTextBlob(blob, kRenderLeft, render_y_2_3, paint); + ctx.canvas->drawTextBlob(blob, kRenderLeft, kRenderBottom, paint); + }, + [=](const DlRenderContext& ctx) { + auto paint = ctx.paint; + ctx.canvas->DrawTextBlob(blob, kRenderLeft, render_y_1_3, paint); + ctx.canvas->DrawTextBlob(blob, kRenderLeft, render_y_2_3, paint); + ctx.canvas->DrawTextBlob(blob, kRenderLeft, kRenderBottom, paint); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawTextBlob(blob, kRenderLeft, render_y_1_3, paint); - canvas->DrawTextBlob(blob, kRenderLeft, render_y_2_3, paint); - canvas->DrawTextBlob(blob, kRenderLeft, kRenderBottom, paint); +#ifdef IMPELLER_SUPPORTS_RENDERING + [=](const DlRenderContext& ctx) { + auto paint = ctx.paint; + ctx.canvas->DrawTextFrame(frame, kRenderLeft, render_y_1_3, paint); + ctx.canvas->DrawTextFrame(frame, kRenderLeft, render_y_2_3, paint); + ctx.canvas->DrawTextFrame(frame, kRenderLeft, kRenderBottom, paint); }, +#endif // IMPELLER_SUPPORTS_RENDERING kDrawTextBlobFlags) .set_draw_text_blob(), // From examining the bounds differential for the "Default" case, the @@ -3335,7 +3771,7 @@ TEST_F(DisplayListCanvas, DrawTextBlob) { #endif // OS_FUCHSIA } -TEST_F(DisplayListCanvas, DrawShadow) { +TEST_F(DisplayListRendering, DrawShadow) { SkPath path; path.addRoundRect( { @@ -3350,18 +3786,18 @@ TEST_F(DisplayListCanvas, DrawShadow) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - DlSkCanvasDispatcher::DrawShadow(canvas, path, color, elevation, + [=](const SkRenderContext& ctx) { // + DlSkCanvasDispatcher::DrawShadow(ctx.canvas, path, color, elevation, false, 1.0); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawShadow(path, color, elevation, false, 1.0); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawShadow(path, color, elevation, false, 1.0); }, kDrawShadowFlags), CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } -TEST_F(DisplayListCanvas, DrawShadowTransparentOccluder) { +TEST_F(DisplayListRendering, DrawShadowTransparentOccluder) { SkPath path; path.addRoundRect( { @@ -3376,18 +3812,18 @@ TEST_F(DisplayListCanvas, DrawShadowTransparentOccluder) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - DlSkCanvasDispatcher::DrawShadow(canvas, path, color, elevation, + [=](const SkRenderContext& ctx) { // + DlSkCanvasDispatcher::DrawShadow(ctx.canvas, path, color, elevation, true, 1.0); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawShadow(path, color, elevation, true, 1.0); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawShadow(path, color, elevation, true, 1.0); }, kDrawShadowFlags), CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } -TEST_F(DisplayListCanvas, DrawShadowDpr) { +TEST_F(DisplayListRendering, DrawShadowDpr) { SkPath path; path.addRoundRect( { @@ -3402,18 +3838,18 @@ TEST_F(DisplayListCanvas, DrawShadowDpr) { CanvasCompareTester::RenderAll( // TestParameters( - [=](SkCanvas* canvas, const SkPaint& paint) { // - DlSkCanvasDispatcher::DrawShadow(canvas, path, color, elevation, + [=](const SkRenderContext& ctx) { // + DlSkCanvasDispatcher::DrawShadow(ctx.canvas, path, color, elevation, false, 1.5); }, - [=](DlCanvas* canvas, const DlPaint& paint) { // - canvas->DrawShadow(path, color, elevation, false, 1.5); + [=](const DlRenderContext& ctx) { // + ctx.canvas->DrawShadow(path, color, elevation, false, 1.5); }, kDrawShadowFlags), CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } -TEST_F(DisplayListCanvas, SaveLayerClippedContentStillFilters) { +TEST_F(DisplayListRendering, SaveLayerClippedContentStillFilters) { // draw rect is just outside of render bounds on the right const SkRect draw_rect = SkRect::MakeLTRB( // kRenderRight + 1, // @@ -3422,43 +3858,46 @@ TEST_F(DisplayListCanvas, SaveLayerClippedContentStillFilters) { kRenderBottom // ); TestParameters test_params( - [=](SkCanvas* canvas, const SkPaint& paint) { + [=](const SkRenderContext& ctx) { auto layer_filter = SkImageFilters::Blur(10.0f, 10.0f, SkTileMode::kDecal, nullptr); SkPaint layer_paint; layer_paint.setImageFilter(layer_filter); - canvas->save(); - canvas->clipRect(kRenderBounds, SkClipOp::kIntersect, false); - canvas->saveLayer(&kTestBounds, &layer_paint); - canvas->drawRect(draw_rect, paint); - canvas->restore(); - canvas->restore(); + ctx.canvas->save(); + ctx.canvas->clipRect(kRenderBounds, SkClipOp::kIntersect, false); + ctx.canvas->saveLayer(&kTestBounds, &layer_paint); + ctx.canvas->drawRect(draw_rect, ctx.paint); + ctx.canvas->restore(); + ctx.canvas->restore(); }, - [=](DlCanvas* canvas, const DlPaint& paint) { + [=](const DlRenderContext& ctx) { auto layer_filter = DlBlurImageFilter::Make(10.0f, 10.0f, DlTileMode::kDecal); DlPaint layer_paint; layer_paint.setImageFilter(layer_filter); - canvas->Save(); - canvas->ClipRect(kRenderBounds, ClipOp::kIntersect, false); - canvas->SaveLayer(&kTestBounds, &layer_paint); - canvas->DrawRect(draw_rect, paint); - canvas->Restore(); - canvas->Restore(); + ctx.canvas->Save(); + ctx.canvas->ClipRect(kRenderBounds, ClipOp::kIntersect, false); + ctx.canvas->SaveLayer(&kTestBounds, &layer_paint); + ctx.canvas->DrawRect(draw_rect, ctx.paint); + ctx.canvas->Restore(); + ctx.canvas->Restore(); }, kSaveLayerWithPaintFlags); CaseParameters case_params("Filtered SaveLayer with clipped content"); BoundsTolerance tolerance = BoundsTolerance().addAbsolutePadding(6.0f, 6.0f); - for (auto& provider : CanvasCompareTester::kTestProviders) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); RenderEnvironment env = RenderEnvironment::MakeN32(provider.get()); - env.init_ref(test_params.sk_renderer(), test_params.dl_renderer()); + env.init_ref(kEmptySkSetup, test_params.sk_renderer(), // + kEmptyDlSetup, test_params.dl_renderer(), + test_params.imp_renderer()); CanvasCompareTester::quickCompareToReference(env, "default"); CanvasCompareTester::RenderWith(test_params, env, tolerance, case_params); } } -TEST_F(DisplayListCanvas, SaveLayerConsolidation) { +TEST_F(DisplayListRendering, SaveLayerConsolidation) { float commutable_color_matrix[]{ // clang-format off 0, 1, 0, 0, 0, @@ -3488,8 +3927,8 @@ TEST_F(DisplayListCanvas, SaveLayerConsolidation) { DlBlendMode::kSrcATop), std::make_shared(commutable_color_matrix), std::make_shared(non_commutable_color_matrix), - DlSrgbToLinearGammaColorFilter::instance, - DlLinearToSrgbGammaColorFilter::instance, + DlSrgbToLinearGammaColorFilter::kInstance, + DlLinearToSrgbGammaColorFilter::kInstance, }; std::vector> image_filters = { std::make_shared(5.0f, 5.0f, DlTileMode::kDecal), @@ -3498,12 +3937,6 @@ TEST_F(DisplayListCanvas, SaveLayerConsolidation) { std::make_shared(contract_matrix, DlImageSampling::kLinear), }; - std::vector> environments; - for (auto& provider : CanvasCompareTester::kTestProviders) { - auto env = std::make_unique( - provider.get(), PixelFormat::kN32Premul_PixelFormat); - environments.push_back(std::move(env)); - } auto render_content = [](DisplayListBuilder& builder) { builder.DrawRect( @@ -3563,12 +3996,15 @@ TEST_F(DisplayListCanvas, SaveLayerConsolidation) { } }; - auto test_attributes = [test_attributes_env, &environments]( - DlPaint& paint1, DlPaint& paint2, - const DlPaint& paint_both, bool same, - bool rev_same, const std::string& desc1, - const std::string& desc2) { - for (auto& env : environments) { + auto test_attributes = [test_attributes_env](DlPaint& paint1, DlPaint& paint2, + const DlPaint& paint_both, + bool same, bool rev_same, + const std::string& desc1, + const std::string& desc2) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); + auto env = std::make_unique( + provider.get(), PixelFormat::kN32PremulPixelFormat); test_attributes_env(paint1, paint2, paint_both, // same, rev_same, desc1, desc2, env.get()); } @@ -3641,15 +4077,8 @@ TEST_F(DisplayListCanvas, SaveLayerConsolidation) { } } -TEST_F(DisplayListCanvas, MatrixColorFilterModifyTransparencyCheck) { - std::vector> environments; - for (auto& provider : CanvasCompareTester::kTestProviders) { - auto env = std::make_unique( - provider.get(), PixelFormat::kN32Premul_PixelFormat); - environments.push_back(std::move(env)); - } - - auto test_matrix = [&environments](int element, SkScalar value) { +TEST_F(DisplayListRendering, MatrixColorFilterModifyTransparencyCheck) { + auto test_matrix = [](int element, SkScalar value) { // clang-format off float matrix[] = { 1, 0, 0, 0, 0, @@ -3666,7 +4095,7 @@ TEST_F(DisplayListCanvas, MatrixColorFilterModifyTransparencyCheck) { auto dl_filter = DlMatrixColorFilter::Make(matrix); bool is_identity = (dl_filter == nullptr || original_value == value); - DlPaint paint(0x7f7f7f7f); + DlPaint paint(DlColor(0x7f7f7f7f)); DlPaint filter_save_paint = DlPaint().setColorFilter(&filter); DisplayListBuilder builder1; @@ -3685,7 +4114,10 @@ TEST_F(DisplayListCanvas, MatrixColorFilterModifyTransparencyCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); + auto env = std::make_unique( + provider.get(), PixelFormat::kN32PremulPixelFormat); auto results1 = env->getResult(display_list1); auto results2 = env->getResult(display_list2); CanvasCompareTester::quickCompareToReference( @@ -3715,15 +4147,8 @@ TEST_F(DisplayListCanvas, MatrixColorFilterModifyTransparencyCheck) { } } -TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) { - std::vector> environments; - for (auto& provider : CanvasCompareTester::kTestProviders) { - auto env = std::make_unique( - provider.get(), PixelFormat::kN32Premul_PixelFormat); - environments.push_back(std::move(env)); - } - - auto test_matrix = [&environments](int element, SkScalar value) { +TEST_F(DisplayListRendering, MatrixColorFilterOpacityCommuteCheck) { + auto test_matrix = [](int element, SkScalar value) { // clang-format off float matrix[] = { 1, 0, 0, 0, 0, @@ -3738,7 +4163,7 @@ TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) { auto filter = DlMatrixColorFilter::Make(matrix); EXPECT_EQ(SkScalarIsFinite(value), filter != nullptr); - DlPaint paint(0x80808080); + DlPaint paint(DlColor(0x80808080)); DlPaint opacity_save_paint = DlPaint().setOpacity(0.5); DlPaint filter_save_paint = DlPaint().setColorFilter(filter); @@ -3760,7 +4185,10 @@ TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); + auto env = std::make_unique( + provider.get(), PixelFormat::kN32PremulPixelFormat); auto results1 = env->getResult(display_list1); auto results2 = env->getResult(display_list2); if (!filter || filter->can_commute_with_opacity()) { @@ -3832,15 +4260,8 @@ static std::string BlendModeToString(DlBlendMode mode) { } } -TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) { - std::vector> environments; - for (auto& provider : CanvasCompareTester::kTestProviders) { - auto env = std::make_unique( - provider.get(), PixelFormat::kN32Premul_PixelFormat); - environments.push_back(std::move(env)); - } - - auto test_mode_color = [&environments](DlBlendMode mode, DlColor color) { +TEST_F(DisplayListRendering, BlendColorFilterModifyTransparencyCheck) { + auto test_mode_color = [](DlBlendMode mode, DlColor color) { std::stringstream desc_str; std::string mode_string = BlendModeToString(mode); desc_str << "blend[" << mode_string << ", " << color << "]"; @@ -3850,7 +4271,7 @@ TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) { ASSERT_NE(DlBlendColorFilter::Make(color, mode), nullptr) << desc; } - DlPaint paint(0x7f7f7f7f); + DlPaint paint(DlColor(0x7f7f7f7f)); DlPaint filter_save_paint = DlPaint().setColorFilter(&filter); DisplayListBuilder builder1; @@ -3869,7 +4290,10 @@ TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); + auto env = std::make_unique( + provider.get(), PixelFormat::kN32PremulPixelFormat); auto results1 = env->getResult(display_list1); auto results2 = env->getResult(display_list2); int modified_transparent_pixels = @@ -3894,15 +4318,8 @@ TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) { #undef TEST_MODE } -TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) { - std::vector> environments; - for (auto& provider : CanvasCompareTester::kTestProviders) { - auto env = std::make_unique( - provider.get(), PixelFormat::kN32Premul_PixelFormat); - environments.push_back(std::move(env)); - } - - auto test_mode_color = [&environments](DlBlendMode mode, DlColor color) { +TEST_F(DisplayListRendering, BlendColorFilterOpacityCommuteCheck) { + auto test_mode_color = [](DlBlendMode mode, DlColor color) { std::stringstream desc_str; std::string mode_string = BlendModeToString(mode); desc_str << "blend[" << mode_string << ", " << color << "]"; @@ -3915,7 +4332,7 @@ TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) { ASSERT_NE(DlBlendColorFilter::Make(color, mode), nullptr) << desc; } - DlPaint paint(0x80808080); + DlPaint paint(DlColor(0x80808080)); DlPaint opacity_save_paint = DlPaint().setOpacity(0.5); DlPaint filter_save_paint = DlPaint().setColorFilter(&filter); @@ -3937,7 +4354,10 @@ TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); + auto env = std::make_unique( + provider.get(), PixelFormat::kN32PremulPixelFormat); auto results1 = env->getResult(display_list1); auto results2 = env->getResult(display_list2); if (filter.can_commute_with_opacity()) { @@ -3964,11 +4384,11 @@ TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) { #undef TEST_MODE } -class DisplayListNopTest : public DisplayListCanvas { +class DisplayListNopTest : public DisplayListRendering { // The following code uses the acronym MTB for "modifies_transparent_black" protected: - DisplayListNopTest() : DisplayListCanvas() { + DisplayListNopTest() : DisplayListRendering() { test_src_colors = { DlColor::kBlack().withAlpha(0), // transparent black DlColor::kBlack().withAlpha(0x7f), // half transparent black @@ -4019,7 +4439,7 @@ class DisplayListNopTest : public DisplayListCanvas { int x = 0; for (DlColor color : test_dst_colors) { SkPaint paint; - paint.setColor(color); + paint.setColor(ToSk(color)); paint.setBlendMode(SkBlendMode::kSrc); canvas->drawRect(SkRect::MakeXYWH(x, 0, 1, 1), paint); x++; @@ -4086,7 +4506,7 @@ class DisplayListNopTest : public DisplayListCanvas { auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(w, h)); SkCanvas* canvas = surface->getCanvas(); renderer(canvas); - return std::make_unique(surface, snapshot); + return std::make_unique(surface, snapshot); } int check_color_result(DlColor dst_color, @@ -4121,8 +4541,8 @@ class DisplayListNopTest : public DisplayListCanvas { const uint32_t* dst_pixels = dst_data->addr32(0, y); const uint32_t* result_pixels = result_data->addr32(0, y); for (int x = 0; x < dst_data->width(); x++) { - all_flags |= - check_color_result(dst_pixels[x], result_pixels[x], dl, desc); + all_flags |= check_color_result(DlColor(dst_pixels[x]), + DlColor(result_pixels[x]), dl, desc); } } return all_flags; @@ -4158,11 +4578,11 @@ class DisplayListNopTest : public DisplayListCanvas { } auto sk_mode = static_cast(mode); - auto sk_color_filter = SkColorFilters::Blend(color, sk_mode); + auto sk_color_filter = SkColorFilters::Blend(ToSk(color), sk_mode); int all_flags = 0; if (sk_color_filter) { for (DlColor dst_color : test_dst_colors) { - DlColor result = sk_color_filter->filterColor(dst_color); + DlColor result = DlColor(sk_color_filter->filterColor(ToSk(dst_color))); all_flags |= check_color_result(dst_color, result, dl, desc); } if ((all_flags & kWasMTB) != 0) { @@ -4192,11 +4612,12 @@ class DisplayListNopTest : public DisplayListCanvas { auto sk_mode = static_cast(mode); SkPaint sk_paint; sk_paint.setBlendMode(sk_mode); - sk_paint.setColor(color); - for (auto& provider : CanvasCompareTester::kTestProviders) { + sk_paint.setColor(ToSk(color)); + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); auto result_surface = provider->MakeOffscreenSurface( test_image->width(), test_image->height(), - DlSurfaceProvider::kN32Premul_PixelFormat); + DlSurfaceProvider::kN32PremulPixelFormat); SkCanvas* result_canvas = result_surface->sk_surface()->getCanvas(); result_canvas->clear(SK_ColorTRANSPARENT); result_canvas->drawImage(test_image.get(), 0, 0); @@ -4207,8 +4628,8 @@ class DisplayListNopTest : public DisplayListCanvas { direct_context->flushAndSubmit(result_surface->sk_surface().get(), GrSyncCpu::kYes); } - auto result_pixels = - std::make_unique(result_surface->sk_surface()); + const std::unique_ptr result_pixels = + std::make_unique(result_surface->sk_surface()); int all_flags = check_image_result(test_data, result_pixels, dl, desc); report_results(all_flags, dl, desc); @@ -4252,12 +4673,13 @@ class DisplayListNopTest : public DisplayListCanvas { auto sk_mode = static_cast(mode); SkPaint sk_paint; sk_paint.setBlendMode(sk_mode); - sk_paint.setColor(color); + sk_paint.setColor(ToSk(color)); sk_paint.setColorFilter(ToSk(color_filter)); sk_paint.setImageFilter(ToSk(image_filter)); - for (auto& provider : CanvasCompareTester::kTestProviders) { + for (auto& back_end : CanvasCompareTester::TestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); auto result_surface = provider->MakeOffscreenSurface( - w, h, DlSurfaceProvider::kN32Premul_PixelFormat); + w, h, DlSurfaceProvider::kN32PremulPixelFormat); SkCanvas* result_canvas = result_surface->sk_surface()->getCanvas(); result_canvas->clear(SK_ColorTRANSPARENT); result_canvas->drawImage(test_image_dst_data->image(), 0, 0); @@ -4269,8 +4691,8 @@ class DisplayListNopTest : public DisplayListCanvas { direct_context->flushAndSubmit(result_surface->sk_surface().get(), GrSyncCpu::kYes); } - auto result_pixels = - std::make_unique(result_surface->sk_surface()); + std::unique_ptr result_pixels = + std::make_unique(result_surface->sk_surface()); int all_flags = check_image_result(test_image_dst_data, result_pixels, dl, desc); diff --git a/display_list/testing/dl_test_equality.h b/display_list/testing/dl_test_equality.h index 9d24d54306d4c..84dcaff5e2814 100644 --- a/display_list/testing/dl_test_equality.h +++ b/display_list/testing/dl_test_equality.h @@ -25,7 +25,7 @@ static void TestEquals(T& source1, T& source2) { } template -static void TestNotEquals(T& source1, T& source2, std::string label) { +static void TestNotEquals(T& source1, T& source2, const std::string& label) { ASSERT_FALSE(source1 == source2) << label; ASSERT_FALSE(source2 == source1) << label; ASSERT_TRUE(source1 != source2) << label; diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc index 0a53f0337afd9..714da0cf732b6 100644 --- a/display_list/testing/dl_test_snippets.cc +++ b/display_list/testing/dl_test_snippets.cc @@ -20,7 +20,8 @@ sk_sp GetSampleNestedDisplayList() { DlPaint paint; for (int y = 10; y <= 60; y += 10) { for (int x = 10; x <= 60; x += 10) { - paint.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE); + paint.setColor(((x + y) % 20) == 10 ? DlColor(SK_ColorRED) + : DlColor(SK_ColorBLUE)); builder.DrawRect(SkRect::MakeXYWH(x, y, 80, 80), paint); } } @@ -101,9 +102,12 @@ std::vector CreateAllAttributesOps() { }}, {"SetColor", { - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorGREEN); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLUE); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLACK); }}, + {0, 8, 0, 0, + [](DlOpReceiver& r) { r.setColor(DlColor(SK_ColorGREEN)); }}, + {0, 8, 0, 0, + [](DlOpReceiver& r) { r.setColor(DlColor(SK_ColorBLUE)); }}, + {0, 0, 0, 0, + [](DlOpReceiver& r) { r.setColor(DlColor(SK_ColorBLACK)); }}, }}, {"SetBlendMode", { @@ -212,11 +216,11 @@ std::vector CreateAllAttributesOps() { }}, {0, 16, 0, 0, [](DlOpReceiver& r) { - r.setColorFilter(DlSrgbToLinearGammaColorFilter::instance.get()); + r.setColorFilter(DlSrgbToLinearGammaColorFilter::kInstance.get()); }}, {0, 16, 0, 0, [](DlOpReceiver& r) { - r.setColorFilter(DlLinearToSrgbGammaColorFilter::instance.get()); + r.setColorFilter(DlLinearToSrgbGammaColorFilter::kInstance.get()); }}, {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorFilter(nullptr); }}, }}, @@ -513,15 +517,15 @@ std::vector CreateAllRenderingOps() { // cv.drawColor becomes cv.drawPaint(paint) {1, 16, 1, 24, [](DlOpReceiver& r) { - r.drawColor(SK_ColorBLUE, DlBlendMode::kSrcIn); + r.drawColor(DlColor(SK_ColorBLUE), DlBlendMode::kSrcIn); }}, {1, 16, 1, 24, [](DlOpReceiver& r) { - r.drawColor(SK_ColorBLUE, DlBlendMode::kDstOut); + r.drawColor(DlColor(SK_ColorBLUE), DlBlendMode::kDstOut); }}, {1, 16, 1, 24, [](DlOpReceiver& r) { - r.drawColor(SK_ColorCYAN, DlBlendMode::kSrcIn); + r.drawColor(DlColor(SK_ColorCYAN), DlBlendMode::kSrcIn); }}, }}, {"DrawLine", @@ -921,27 +925,27 @@ std::vector CreateAllRenderingOps() { // exposed {1, 32, -1, 32, [](DlOpReceiver& r) { - r.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, false, 1.0); + r.drawShadow(kTestPath1, DlColor(SK_ColorGREEN), 1.0, false, 1.0); }}, {1, 32, -1, 32, [](DlOpReceiver& r) { - r.drawShadow(kTestPath2, SK_ColorGREEN, 1.0, false, 1.0); + r.drawShadow(kTestPath2, DlColor(SK_ColorGREEN), 1.0, false, 1.0); }}, {1, 32, -1, 32, [](DlOpReceiver& r) { - r.drawShadow(kTestPath1, SK_ColorBLUE, 1.0, false, 1.0); + r.drawShadow(kTestPath1, DlColor(SK_ColorBLUE), 1.0, false, 1.0); }}, {1, 32, -1, 32, [](DlOpReceiver& r) { - r.drawShadow(kTestPath1, SK_ColorGREEN, 2.0, false, 1.0); + r.drawShadow(kTestPath1, DlColor(SK_ColorGREEN), 2.0, false, 1.0); }}, {1, 32, -1, 32, [](DlOpReceiver& r) { - r.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, true, 1.0); + r.drawShadow(kTestPath1, DlColor(SK_ColorGREEN), 1.0, true, 1.0); }}, {1, 32, -1, 32, [](DlOpReceiver& r) { - r.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, false, 2.5); + r.drawShadow(kTestPath1, DlColor(SK_ColorGREEN), 1.0, false, 2.5); }}, }}, }; diff --git a/display_list/testing/dl_test_snippets.h b/display_list/testing/dl_test_snippets.h index 536cb224e8ef9..64f54362cae7d 100644 --- a/display_list/testing/dl_test_snippets.h +++ b/display_list/testing/dl_test_snippets.h @@ -37,8 +37,6 @@ constexpr float kStops[] = { 0.5, 1.0, }; -static std::vector color_vector(kColors, kColors + 3); -static std::vector stops_vector(kStops, kStops + 3); // clang-format off constexpr float kRotateColorMatrix[20] = { @@ -216,7 +214,7 @@ static std::shared_ptr TestVertices2 = static sk_sp MakeTestDisplayList(int w, int h, SkColor color) { DisplayListBuilder builder; - builder.DrawRect(SkRect::MakeWH(w, h), DlPaint(color)); + builder.DrawRect(SkRect::MakeWH(w, h), DlPaint(DlColor(color))); return builder.Build(); } static sk_sp TestDisplayList1 = diff --git a/display_list/testing/dl_test_surface_gl.h b/display_list/testing/dl_test_surface_gl.h index 882e0fd6d243b..82677c9be749d 100644 --- a/display_list/testing/dl_test_surface_gl.h +++ b/display_list/testing/dl_test_surface_gl.h @@ -26,9 +26,9 @@ class DlOpenGLSurfaceProvider : public DlSurfaceProvider { size_t height, PixelFormat format) const override; const std::string backend_name() const override { return "OpenGL"; } - BackendType backend_type() const override { return kOpenGL_Backend; } + BackendType backend_type() const override { return kOpenGlBackend; } bool supports(PixelFormat format) const override { - return format == kN32Premul_PixelFormat; + return format == kN32PremulPixelFormat; } private: diff --git a/display_list/testing/dl_test_surface_metal.cc b/display_list/testing/dl_test_surface_metal.cc index 7c3617e8d63ca..2cce8f4e5ec4d 100644 --- a/display_list/testing/dl_test_surface_metal.cc +++ b/display_list/testing/dl_test_surface_metal.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "flutter/display_list/testing/dl_test_surface_metal.h" +#include "flutter/impeller/display_list/dl_dispatcher.h" +#include "flutter/impeller/display_list/dl_image_impeller.h" +#include "flutter/impeller/typographer/backends/skia/typographer_context_skia.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" @@ -28,7 +31,7 @@ class DlMetalSurfaceInstance : public DlSurfaceInstance { bool DlMetalSurfaceProvider::InitializeSurface(size_t width, size_t height, PixelFormat format) { - if (format != kN32Premul_PixelFormat) { + if (format != kN32PremulPixelFormat) { return false; } metal_context_ = std::make_unique(); @@ -54,5 +57,71 @@ std::shared_ptr DlMetalSurfaceProvider::MakeOffscreenSurface( return std::make_shared(std::move(surface)); } +class DlMetalPixelData : public DlPixelData { + using MetalScreenshot = impeller::testing::MetalScreenshot; + + public: + explicit DlMetalPixelData(std::unique_ptr screenshot) + : screenshot_(std::move(screenshot)), + addr_(reinterpret_cast(screenshot_->GetBytes())), + ints_per_row_(screenshot_->GetBytesPerRow() / 4) { + FML_DCHECK(screenshot_->GetBytesPerRow() == ints_per_row_ * 4); + } + ~DlMetalPixelData() override = default; + + const uint32_t* addr32(int x, int y) const override { + return addr_ + (y * ints_per_row_) + x; + } + size_t width() const override { return screenshot_->GetWidth(); } + size_t height() const override { return screenshot_->GetHeight(); } + void write(const std::string& path) const override { + screenshot_->WriteToPNG(path); + } + + private: + std::unique_ptr screenshot_; + const uint32_t* addr_; + const uint32_t ints_per_row_; +}; + +sk_sp DlMetalSurfaceProvider::ImpellerSnapshot( + const sk_sp& list, + int width, + int height) const { + InitScreenShotter(); + impeller::DlDispatcher dispatcher; + dispatcher.drawColor(flutter::DlColor::kTransparent(), + flutter::DlBlendMode::kSrc); + list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + return sk_make_sp(snapshotter_->MakeScreenshot( + *aiks_context_, picture, {width, height}, false)); +} + +sk_sp DlMetalSurfaceProvider::MakeImpellerImage( + const sk_sp& list, + int width, + int height) const { + InitScreenShotter(); + impeller::DlDispatcher dispatcher; + dispatcher.drawColor(flutter::DlColor::kTransparent(), + flutter::DlBlendMode::kSrc); + list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + std::shared_ptr image = + picture.ToImage(*aiks_context_, {width, height}); + std::shared_ptr texture = image->GetTexture(); + return impeller::DlImageImpeller::Make(texture); +} + +void DlMetalSurfaceProvider::InitScreenShotter() const { + if (!snapshotter_) { + snapshotter_.reset(new MetalScreenshotter()); + auto typographer = impeller::TypographerContextSkia::Make(); + aiks_context_.reset(new impeller::AiksContext( + snapshotter_->GetPlayground().GetContext(), typographer)); + } +} + } // namespace testing } // namespace flutter diff --git a/display_list/testing/dl_test_surface_metal.h b/display_list/testing/dl_test_surface_metal.h index c660c6dc95454..04843066bdc75 100644 --- a/display_list/testing/dl_test_surface_metal.h +++ b/display_list/testing/dl_test_surface_metal.h @@ -6,12 +6,15 @@ #define FLUTTER_DISPLAY_LIST_TESTING_DL_TEST_SURFACE_METAL_H_ #include "flutter/display_list/testing/dl_test_surface_provider.h" - +#include "flutter/fml/platform/darwin/scoped_nsautorelease_pool.h" +#include "flutter/impeller/golden_tests/metal_screenshotter.h" #include "flutter/testing/test_metal_surface.h" namespace flutter { namespace testing { +using MetalScreenshotter = impeller::testing::MetalScreenshotter; + class DlMetalSurfaceProvider : public DlSurfaceProvider { public: explicit DlMetalSurfaceProvider() : DlSurfaceProvider() {} @@ -26,14 +29,29 @@ class DlMetalSurfaceProvider : public DlSurfaceProvider { size_t height, PixelFormat format) const override; const std::string backend_name() const override { return "Metal"; } - BackendType backend_type() const override { return kMetal_Backend; } + BackendType backend_type() const override { return kMetalBackend; } bool supports(PixelFormat format) const override { - return format == kN32Premul_PixelFormat; + return format == kN32PremulPixelFormat; } + bool supports_impeller() const override { return true; } + sk_sp ImpellerSnapshot(const sk_sp& list, + int width, + int height) const override; + virtual sk_sp MakeImpellerImage(const sk_sp& list, + int width, + int height) const override; private: + // This must be placed before any other members that may use the + // autorelease pool. + fml::ScopedNSAutoreleasePool autorelease_pool_; + std::unique_ptr metal_context_; std::shared_ptr metal_surface_; + mutable std::unique_ptr snapshotter_; + mutable std::unique_ptr aiks_context_; + + void InitScreenShotter() const; }; } // namespace testing diff --git a/display_list/testing/dl_test_surface_provider.cc b/display_list/testing/dl_test_surface_provider.cc index 9615183d88059..ab7d75c0901e7 100644 --- a/display_list/testing/dl_test_surface_provider.cc +++ b/display_list/testing/dl_test_surface_provider.cc @@ -23,19 +23,30 @@ namespace flutter { namespace testing { +std::string DlSurfaceProvider::BackendName(BackendType type) { + switch (type) { + case kMetalBackend: + return "Metal"; + case kOpenGlBackend: + return "OpenGL"; + case kSoftwareBackend: + return "Software"; + } +} + std::unique_ptr DlSurfaceProvider::Create( BackendType backend_type) { switch (backend_type) { #ifdef ENABLE_SOFTWARE_BENCHMARKS - case kSoftware_Backend: + case kSoftwareBackend: return std::make_unique(); #endif #ifdef ENABLE_OPENGL_BENCHMARKS - case kOpenGL_Backend: + case kOpenGLBackend: return std::make_unique(); #endif #ifdef ENABLE_METAL_BENCHMARKS - case kMetal_Backend: + case kMetalBackend: return std::make_unique(); #endif default: diff --git a/display_list/testing/dl_test_surface_provider.h b/display_list/testing/dl_test_surface_provider.h index d3944aba21ae7..1ad4af789cb43 100644 --- a/display_list/testing/dl_test_surface_provider.h +++ b/display_list/testing/dl_test_surface_provider.h @@ -5,6 +5,10 @@ #ifndef FLUTTER_DISPLAY_LIST_TESTING_DL_TEST_SURFACE_PROVIDER_H_ #define FLUTTER_DISPLAY_LIST_TESTING_DL_TEST_SURFACE_PROVIDER_H_ +#include + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/image/dl_image.h" #include "flutter/fml/mapping.h" #include "flutter/testing/testing.h" @@ -13,6 +17,16 @@ namespace flutter { namespace testing { +class DlPixelData : public SkRefCnt { + public: + virtual ~DlPixelData() = default; + + virtual const uint32_t* addr32(int x, int y) const = 0; + virtual size_t width() const = 0; + virtual size_t height() const = 0; + virtual void write(const std::string& path) const = 0; +}; + class DlSurfaceInstance { public: virtual ~DlSurfaceInstance() = default; @@ -25,7 +39,8 @@ class DlSurfaceInstance { class DlSurfaceInstanceBase : public DlSurfaceInstance { public: - DlSurfaceInstanceBase(sk_sp surface) : surface_(surface) {} + explicit DlSurfaceInstanceBase(sk_sp surface) + : surface_(std::move(surface)) {} ~DlSurfaceInstanceBase() = default; sk_sp sk_surface() const override { return surface_; } @@ -36,41 +51,49 @@ class DlSurfaceInstanceBase : public DlSurfaceInstance { class DlSurfaceProvider { public: - typedef enum { kN32Premul_PixelFormat, k565_PixelFormat } PixelFormat; - typedef enum { - kSoftware_Backend, - kOpenGL_Backend, - kMetal_Backend - } BackendType; + typedef enum { kN32PremulPixelFormat, k565PixelFormat } PixelFormat; + typedef enum { kSoftwareBackend, kOpenGlBackend, kMetalBackend } BackendType; static SkImageInfo MakeInfo(PixelFormat format, int w, int h) { switch (format) { - case kN32Premul_PixelFormat: + case kN32PremulPixelFormat: return SkImageInfo::MakeN32Premul(w, h); - case k565_PixelFormat: + case k565PixelFormat: return SkImageInfo::Make(SkISize::Make(w, h), kRGB_565_SkColorType, kOpaque_SkAlphaType); } FML_DCHECK(false); } + static std::string BackendName(BackendType type); static std::unique_ptr Create(BackendType backend_type); virtual ~DlSurfaceProvider() = default; virtual const std::string backend_name() const = 0; virtual BackendType backend_type() const = 0; virtual bool supports(PixelFormat format) const = 0; + virtual bool supports_impeller() const { return false; } virtual bool InitializeSurface( size_t width, size_t height, - PixelFormat format = kN32Premul_PixelFormat) = 0; + PixelFormat format = kN32PremulPixelFormat) = 0; virtual std::shared_ptr GetPrimarySurface() const = 0; virtual std::shared_ptr MakeOffscreenSurface( size_t width, size_t height, - PixelFormat format = kN32Premul_PixelFormat) const = 0; + PixelFormat format = kN32PremulPixelFormat) const = 0; virtual bool Snapshot(std::string& filename) const; + virtual sk_sp ImpellerSnapshot(const sk_sp& list, + int width, + int height) const { + return nullptr; + } + virtual sk_sp MakeImpellerImage(const sk_sp& list, + int width, + int height) const { + return nullptr; + } protected: DlSurfaceProvider() = default; diff --git a/display_list/testing/dl_test_surface_software.h b/display_list/testing/dl_test_surface_software.h index 5fbbe19457d9f..d341079b0adaf 100644 --- a/display_list/testing/dl_test_surface_software.h +++ b/display_list/testing/dl_test_surface_software.h @@ -26,7 +26,7 @@ class DlSoftwareSurfaceProvider : public DlSurfaceProvider { size_t height, PixelFormat format) const override; const std::string backend_name() const override { return "Software"; } - BackendType backend_type() const override { return kSoftware_Backend; } + BackendType backend_type() const override { return kSoftwareBackend; } bool supports(PixelFormat format) const override { return true; } private: diff --git a/display_list/utils/dl_matrix_clip_tracker.h b/display_list/utils/dl_matrix_clip_tracker.h index f6d2736f0a1db..5b4a3fa763cb2 100644 --- a/display_list/utils/dl_matrix_clip_tracker.h +++ b/display_list/utils/dl_matrix_clip_tracker.h @@ -124,7 +124,7 @@ class DisplayListMatrixClipTracker { virtual void resetBounds(const SkRect& cull_rect); protected: - Data(const SkRect& rect) : cull_rect_(rect) {} + explicit Data(const SkRect& rect) : cull_rect_(rect) {} virtual bool has_perspective() const = 0; diff --git a/examples/glfw/BUILD.gn b/examples/glfw/BUILD.gn index 2ff312c0ee8b5..178bac61e4e05 100644 --- a/examples/glfw/BUILD.gn +++ b/examples/glfw/BUILD.gn @@ -12,7 +12,7 @@ if (build_embedder_examples) { deps = [ "//flutter/shell/platform/embedder:embedder", - "//third_party/glfw", + "//flutter/third_party/glfw", ] } } diff --git a/examples/glfw_drm/BUILD.gn b/examples/glfw_drm/BUILD.gn index 784e5654e5e69..afa204c76c709 100644 --- a/examples/glfw_drm/BUILD.gn +++ b/examples/glfw_drm/BUILD.gn @@ -12,7 +12,7 @@ if (build_embedder_examples) { deps = [ "//flutter/shell/platform/embedder:embedder", - "//third_party/glfw", + "//flutter/third_party/glfw", ] libs = [ "EGL" ] diff --git a/examples/vulkan_glfw/BUILD.gn b/examples/vulkan_glfw/BUILD.gn index 021d09f1efed9..cea552f66e2d5 100644 --- a/examples/vulkan_glfw/BUILD.gn +++ b/examples/vulkan_glfw/BUILD.gn @@ -9,7 +9,7 @@ executable("vulkan_glfw") { deps = [ "//flutter/shell/platform/embedder:embedder", - "//third_party/glfw", + "//flutter/third_party/glfw", "//third_party/vulkan-deps/vulkan-headers/src:vulkan_headers", ] } diff --git a/flow/BUILD.gn b/flow/BUILD.gn index a75605749c7aa..492126c2a3086 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -188,6 +188,7 @@ if (enable_unittests) { "//flutter/common/graphics", "//flutter/display_list/testing:display_list_testing", "//flutter/fml", + "//flutter/shell/common:base64", "//flutter/testing:skia", "//flutter/testing:testing_lib", "//third_party/dart/runtime:libdart_jit", # for tracing diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 04bb29a1f19e4..453259cd67279 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -23,18 +23,19 @@ namespace flutter { class LayerTree; +// The result status of CompositorContext::ScopedFrame::Raster. enum class RasterStatus { // Frame has been successfully rasterized. kSuccess, - // Frame is submitted twice. This is only used on Android when - // switching the background surface to FlutterImageView. + // Frame has been submited, but must be submitted again. This is only used + // on Android when switching the background surface to FlutterImageView. // // On Android, the first frame doesn't make the image available // to the ImageReader right away. The second frame does. // // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 kResubmit, - // Frame is dropped and a new frame with the same layer tree is + // Frame has be dropped and a new frame with the same layer tree must be // attempted. // // This is currently used to wait for the thread merger to merge @@ -44,18 +45,6 @@ enum class RasterStatus { // with separate threads for rasterization and platform tasks, // potentially leading to different performance characteristics. kSkipAndRetry, - // Frame has been successfully rasterized, but there are additional items in - // the pipeline waiting to be consumed. This is currently - // only used when thread configuration change occurs. - kEnqueuePipeline, - // Failed to rasterize the frame. - kFailed, - // Layer tree was discarded due to LayerTreeDiscardCallback or inability to - // access the GPU. - kDiscarded, - // Drawing was yielded to allow the correct thread to draw as a result of the - // RasterThreadMerger. - kYielded, }; class FrameDamage { diff --git a/flow/diff_context.cc b/flow/diff_context.cc index 612fe563fa9c6..54cbc5e80742b 100644 --- a/flow/diff_context.cc +++ b/flow/diff_context.cc @@ -123,12 +123,16 @@ Damage DiffContext::ComputeDamage(const SkIRect& accumulated_buffer_damage, SkRect frame_damage(damage_); for (const auto& r : readbacks_) { - SkRect rect = SkRect::Make(r.rect); - if (rect.intersects(frame_damage)) { - frame_damage.join(rect); - } - if (rect.intersects(buffer_damage)) { - buffer_damage.join(rect); + SkRect paint_rect = SkRect::Make(r.paint_rect); + SkRect readback_rect = SkRect::Make(r.readback_rect); + // Changes either in readback or paint rect require repainting both readback + // and paint rect. + if (paint_rect.intersects(frame_damage) || + readback_rect.intersects(frame_damage)) { + frame_damage.join(readback_rect); + frame_damage.join(paint_rect); + buffer_damage.join(readback_rect); + buffer_damage.join(paint_rect); } } @@ -220,9 +224,11 @@ void DiffContext::AddExistingPaintRegion(const PaintRegion& region) { } } -void DiffContext::AddReadbackRegion(const SkIRect& rect) { +void DiffContext::AddReadbackRegion(const SkIRect& paint_rect, + const SkIRect& readback_rect) { Readback readback; - readback.rect = rect; + readback.paint_rect = paint_rect; + readback.readback_rect = readback_rect; readback.position = rects_->size(); // Push empty rect as a placeholder for position in current subtree rects_->push_back(SkRect::MakeEmpty()); diff --git a/flow/diff_context.h b/flow/diff_context.h index 029fb49f933fc..0eff8ef5db4f6 100644 --- a/flow/diff_context.h +++ b/flow/diff_context.h @@ -123,8 +123,12 @@ class DiffContext { // The idea of readback region is that if any part of the readback region // needs to be repainted, then the whole readback region must be repainted; // - // Readback rect is in screen coordinates. - void AddReadbackRegion(const SkIRect& rect); + // paint_rect - rectangle where the filter paints contents (in screen + // coordinates) + // readback_rect - rectangle where the filter samples from (in screen + // coordinates) + void AddReadbackRegion(const SkIRect& paint_rect, + const SkIRect& readback_rect); // Returns the paint region for current subtree; Each rect in paint region is // in screen coordinates; Once a layer accumulates the paint regions of its @@ -261,8 +265,11 @@ class DiffContext { // determine if subtree has any readback size_t position; - // readback area, in screen coordinates - SkIRect rect; + // Paint region of the filter performing readback, in screen coordinates. + SkIRect paint_rect; + + // Readback area of the filter, in screen coordinates. + SkIRect readback_rect; }; std::vector readbacks_; diff --git a/flow/frame_timings.cc b/flow/frame_timings.cc index eed35fd4c755a..2943e850e17d0 100644 --- a/flow/frame_timings.cc +++ b/flow/frame_timings.cc @@ -254,4 +254,30 @@ const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const { return frame_number_trace_arg_val_.c_str(); } +static const char* StateToString(FrameTimingsRecorder::State state) { +#ifndef NDEBUG + switch (state) { + case FrameTimingsRecorder::State::kUninitialized: + return "kUninitialized"; + case FrameTimingsRecorder::State::kVsync: + return "kVsync"; + case FrameTimingsRecorder::State::kBuildStart: + return "kBuildStart"; + case FrameTimingsRecorder::State::kBuildEnd: + return "kBuildEnd"; + case FrameTimingsRecorder::State::kRasterStart: + return "kRasterStart"; + case FrameTimingsRecorder::State::kRasterEnd: + return "kRasterEnd"; + }; + FML_UNREACHABLE(); +#endif + return ""; +} + +void FrameTimingsRecorder::AssertInState(State state) const { + FML_DCHECK(state_ == state) << "Expected state " << StateToString(state) + << ", actual state " << StateToString(state_); +} + } // namespace flutter diff --git a/flow/frame_timings.h b/flow/frame_timings.h index e477a80b8f955..ac5a7e470215e 100644 --- a/flow/frame_timings.h +++ b/flow/frame_timings.h @@ -31,6 +31,7 @@ class FrameTimingsRecorder { public: /// Various states that the recorder can be in. When created the recorder is /// in an unitialized state and transtions in sequential order of the states. + // After adding an item to this enum, modify StateToString accordingly. enum class State : uint32_t { kUninitialized, kVsync, @@ -116,6 +117,15 @@ class FrameTimingsRecorder { /// Returns the recorded time from when `RecordRasterEnd` is called. FrameTiming GetRecordedTime() const; + /// Asserts in unopt builds that the recorder is current at the specified + /// state. + /// + /// Instead of adding a `GetState` method and asserting on the result, this + /// method prevents other logic from relying on the state. + /// + /// In opt builds, this call is a no-op. + void AssertInState(State state) const; + private: FML_FRIEND_TEST(FrameTimingsRecorderTest, ThrowWhenRecordBuildBeforeVsync); FML_FRIEND_TEST(FrameTimingsRecorderTest, diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index 44c3fca4a6acd..32707c4f77e6d 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -31,7 +31,7 @@ void BackdropFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { SkIRect filter_input_bounds; // in screen coordinates filter_->get_input_device_bounds( filter_target_bounds, context->GetTransform3x3(), filter_input_bounds); - context->AddReadbackRegion(filter_input_bounds); + context->AddReadbackRegion(filter_target_bounds, filter_input_bounds); } DiffChildren(context, prev); diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc index e73433889ff5d..5e38f74db9f44 100644 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -469,7 +469,6 @@ TEST_F(BackdropLayerDiffTest, BackdropLayer) { auto path1 = SkPath().addRect(SkRect::MakeLTRB(180, 180, 190, 190)); l4.root()->Add(std::make_shared(path1)); damage = DiffLayerTree(l4, l3); - EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(180, 180, 190, 190)); MockLayerTree l5; @@ -482,6 +481,31 @@ TEST_F(BackdropLayerDiffTest, BackdropLayer) { EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 190, 190)); } +TEST_F(BackdropLayerDiffTest, ReadbackOutsideOfPaintArea) { + auto filter = DlMatrixImageFilter(SkMatrix::Translate(50, 50), + DlImageSampling::kLinear); + + MockLayerTree l1(SkISize::Make(100, 100)); + + auto clip = std::make_shared(SkRect::MakeLTRB(60, 60, 80, 80), + Clip::hardEdge); + clip->Add(std::make_shared(filter.shared(), + DlBlendMode::kSrcOver)); + l1.root()->Add(clip); + auto damage = DiffLayerTree(l1, MockLayerTree(SkISize::Make(100, 100))); + + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(60 - 50, 60 - 50, 80, 80)); + + MockLayerTree l2(SkISize::Make(100, 100)); + // path inside readback area must trigger whole readback repaint + filter + // repaint. + auto path2 = SkPath().addRect(SkRect::MakeXYWH(60 - 50, 60 - 50, 10, 10)); + l2.root()->Add(clip); + l2.root()->Add(std::make_shared(path2)); + damage = DiffLayerTree(l2, l1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(60 - 50, 60 - 50, 80, 80)); +} + TEST_F(BackdropLayerDiffTest, BackdropLayerInvalidTransform) { auto filter = DlBlurImageFilter(10, 10, DlTileMode::kClamp); diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc index 98d6493ba7bb5..1c3672d7cc616 100644 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -595,51 +595,6 @@ TEST_F(ClipRRectLayerTest, EmptyClipDoesNotCullPlatformView) { EXPECT_EQ(embedder.painted_views(), std::vector({view_id})); } -TEST_F(ClipRRectLayerTest, AntiAliasWithSaveLayerIgnoresSaveLayerImpeller) { - enable_impeller(); - - auto path1 = SkPath().addRect({10, 10, 30, 30}); - auto mock1 = MockLayer::MakeOpacityCompatible(path1); - auto path2 = SkPath().addRect({20, 20, 40, 40}); - auto mock2 = MockLayer::MakeOpacityCompatible(path2); - auto children_bounds = path1.getBounds(); - children_bounds.join(path2.getBounds()); - SkRect clip_rect = SkRect::MakeWH(500, 500); - SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 20, 20); - auto clip_rrect_layer = std::make_shared( - clip_rrect, Clip::antiAliasWithSaveLayer); - clip_rrect_layer->Add(mock1); - clip_rrect_layer->Add(mock2); - - // ClipRectLayer will pass through compatibility from multiple - // non-overlapping compatible children - PrerollContext* context = preroll_context(); - clip_rrect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); - - DisplayListBuilder expected_builder; - /* OpacityLayer::Paint() */ { - expected_builder.Save(); - { - /* ClipRectLayer::Paint() */ { - expected_builder.Save(); - expected_builder.ClipRRect(clip_rrect, ClipOp::kIntersect, true); - /* child layer1 paint */ { - expected_builder.DrawPath(path1, DlPaint()); - } - /* child layer2 paint */ { // - expected_builder.DrawPath(path2, DlPaint()); - } - // expected_builder.Restore(); - } - } - expected_builder.Restore(); - } - - clip_rrect_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); -} - } // namespace testing } // namespace flutter diff --git a/flow/layers/clip_shape_layer.h b/flow/layers/clip_shape_layer.h index d33267853e30d..69c479fe61a23 100644 --- a/flow/layers/clip_shape_layer.h +++ b/flow/layers/clip_shape_layer.h @@ -32,8 +32,7 @@ class ClipShapeLayer : public CacheableContainerLayer { context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); } } - if (UsesSaveLayer(context->impeller_enabled()) && - context->has_raster_cache()) { + if (UsesSaveLayer() && context->has_raster_cache()) { context->WillPaintWithIntegralTransform(); } if (context->PushCullRect(clip_shape_bounds())) { @@ -43,7 +42,7 @@ class ClipShapeLayer : public CacheableContainerLayer { } void Preroll(PrerollContext* context) override { - bool uses_save_layer = UsesSaveLayer(context->impeller_enabled); + bool uses_save_layer = UsesSaveLayer(); // We can use the raster_cache for children only when the use_save_layer is // true so if use_save_layer is false we pass the layer_raster_item is @@ -53,8 +52,7 @@ class ClipShapeLayer : public CacheableContainerLayer { context, context->state_stack.transform_3x3()); Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create( - context, UsesSaveLayer(context->impeller_enabled)); + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); auto mutator = context->state_stack.save(); ApplyClip(mutator); @@ -80,7 +78,7 @@ class ClipShapeLayer : public CacheableContainerLayer { auto mutator = context.state_stack.save(); ApplyClip(mutator); - if (!UsesSaveLayer(context.impeller_enabled)) { + if (!UsesSaveLayer()) { PaintChildren(context); return; } @@ -101,10 +99,7 @@ class ClipShapeLayer : public CacheableContainerLayer { PaintChildren(context); } - bool UsesSaveLayer(bool enable_impeller) const { - if (enable_impeller) { - return false; - } + bool UsesSaveLayer() const { return clip_behavior_ == Clip::antiAliasWithSaveLayer; } diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc index 1ec1f35bd0bcc..209ca2b22733a 100644 --- a/flow/layers/color_filter_layer_unittests.cc +++ b/flow/layers/color_filter_layer_unittests.cc @@ -89,7 +89,7 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) { const SkPath child_path = SkPath().addRect(child_bounds); const DlPaint child_paint = DlPaint(DlColor::kYellow()); - auto dl_color_filter = DlLinearToSrgbGammaColorFilter::instance; + auto dl_color_filter = DlLinearToSrgbGammaColorFilter::kInstance; auto mock_layer = std::make_shared(child_path, child_paint); auto layer = std::make_shared(dl_color_filter); layer->Add(mock_layer); @@ -129,7 +129,7 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) { const DlPaint child_paint2 = DlPaint(DlColor::kCyan()); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto dl_color_filter = DlSrgbToLinearGammaColorFilter::instance; + auto dl_color_filter = DlSrgbToLinearGammaColorFilter::kInstance; auto layer = std::make_shared(dl_color_filter); layer->Add(mock_layer1); layer->Add(mock_layer2); @@ -179,7 +179,7 @@ TEST_F(ColorFilterLayerTest, Nested) { const DlPaint child_paint2 = DlPaint(DlColor::kCyan()); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto dl_color_filter = DlSrgbToLinearGammaColorFilter::instance; + auto dl_color_filter = DlSrgbToLinearGammaColorFilter::kInstance; auto layer1 = std::make_shared(dl_color_filter); auto layer2 = std::make_shared(dl_color_filter); @@ -239,7 +239,7 @@ TEST_F(ColorFilterLayerTest, Readback) { // ColorFilterLayer does not read from surface auto layer = std::make_shared( - DlLinearToSrgbGammaColorFilter::instance); + DlLinearToSrgbGammaColorFilter::kInstance); preroll_context()->surface_needs_readback = false; preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); @@ -255,7 +255,7 @@ TEST_F(ColorFilterLayerTest, Readback) { } TEST_F(ColorFilterLayerTest, CacheChild) { - auto layer_filter = DlSrgbToLinearGammaColorFilter::instance; + auto layer_filter = DlSrgbToLinearGammaColorFilter::kInstance; auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); @@ -296,7 +296,7 @@ TEST_F(ColorFilterLayerTest, CacheChild) { } TEST_F(ColorFilterLayerTest, CacheChildren) { - auto layer_filter = DlSrgbToLinearGammaColorFilter::instance; + auto layer_filter = DlSrgbToLinearGammaColorFilter::kInstance; auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); @@ -342,7 +342,7 @@ TEST_F(ColorFilterLayerTest, CacheChildren) { } TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { - auto layer_filter = DlSrgbToLinearGammaColorFilter::instance; + auto layer_filter = DlSrgbToLinearGammaColorFilter::kInstance; auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); @@ -439,11 +439,11 @@ TEST_F(ColorFilterLayerTest, OpacityInheritance) { expected_builder.Translate(offset.fX, offset.fY); /* ColorFilterLayer::Paint() */ { DlPaint dl_paint; - dl_paint.setColor(opacity_alpha << 24); + dl_paint.setColor(DlColor(opacity_alpha << 24)); dl_paint.setColorFilter(&layer_filter); expected_builder.SaveLayer(&child_path.getBounds(), &dl_paint); /* MockLayer::Paint() */ { - expected_builder.DrawPath(child_path, DlPaint(0xFF000000)); + expected_builder.DrawPath(child_path, DlPaint(DlColor(0xFF000000))); } expected_builder.Restore(); } diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index ee8f08c8491be..67cf817c0899f 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -603,7 +603,7 @@ TEST_F(ImageFilterLayerTest, OpacityInheritance) { expected_builder.Translate(offset.fX, offset.fY); /* ImageFilterLayer::Paint() */ { DlPaint image_filter_paint; - image_filter_paint.setColor(opacity_alpha << 24); + image_filter_paint.setColor(DlColor(opacity_alpha << 24)); image_filter_paint.setImageFilter(dl_image_filter.get()); expected_builder.SaveLayer(&child_path.getBounds(), &image_filter_paint); diff --git a/flow/layers/layer.h b/flow/layers/layer.h index d9864e82dcb3d..617f9221ebd0f 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -71,8 +71,6 @@ struct PrerollContext { // presence of a texture layer during Preroll. bool has_texture_layer = false; - bool impeller_enabled = false; - // The list of flags that describe which rendering state attributes // (such as opacity, ColorFilter, ImageFilter) a given layer can // render itself without requiring the parent to perform a protective diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index e8aea74efd78b..5f75aaf95f4fc 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -60,7 +60,6 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, .raster_time = frame.context().raster_time(), .ui_time = frame.context().ui_time(), .texture_registry = frame.context().texture_registry(), - .impeller_enabled = !frame.gr_context(), .raster_cached_entries = &raster_cache_items_, // clang-format on }; diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 5f573da2099a0..416096860d08c 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -70,6 +70,8 @@ class LayerTree { /// When `Paint` is called, if leaf layer tracing is enabled, additional /// metadata around raterization of leaf layers is collected. /// + /// This is not supported in the Impeller backend. + /// /// See: `LayerSnapshotStore` void enable_leaf_layer_tracing(bool enable) { enable_leaf_layer_tracing_ = enable; @@ -94,6 +96,27 @@ class LayerTree { FML_DISALLOW_COPY_AND_ASSIGN(LayerTree); }; +// The information to draw a layer tree to a specified view. +struct LayerTreeTask { + public: + LayerTreeTask(int64_t view_id, + std::unique_ptr layer_tree, + float device_pixel_ratio) + : view_id(view_id), + layer_tree(std::move(layer_tree)), + device_pixel_ratio(device_pixel_ratio) {} + + /// The target view to draw to. + int64_t view_id; + /// The target layer tree to be drawn. + std::unique_ptr layer_tree; + /// The pixel ratio of the target view. + float device_pixel_ratio; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(LayerTreeTask); +}; + } // namespace flutter #endif // FLUTTER_FLOW_LAYERS_LAYER_TREE_H_ diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 8b55dff69aa92..cf6138fd67045 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -36,8 +36,6 @@ void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) { } void OpacityLayer::Preroll(PrerollContext* context) { - FML_DCHECK(!layers().empty()); // We can't be a leaf. - auto mutator = context->state_stack.save(); mutator.translate(offset_); mutator.applyOpacity(SkRect(), DlColor::toOpacity(alpha_)); diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 71ccdc15c8476..f69ac4ec57f4f 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -27,14 +27,6 @@ namespace testing { using OpacityLayerTest = LayerTest; #ifndef NDEBUG -TEST_F(OpacityLayerTest, LeafLayer) { - auto layer = - std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), - "\\!layers\\(\\)\\.empty\\(\\)"); -} - TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { auto mock_layer = std::make_shared(SkPath()); auto layer = diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index 1c1ebb4566798..55d056476f029 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -52,15 +52,15 @@ void VisualizeStopWatch(DlCanvas* canvas, auto text = PerformanceOverlayLayer::MakeStatisticsText( stopwatch, label_prefix, font_path); // Historically SK_ColorGRAY (== 0xFF888888) was used here - DlPaint paint(0xFF888888); + DlPaint paint(DlColor(0xFF888888)); #ifdef IMPELLER_SUPPORTS_RENDERING if (impeller_enabled) { canvas->DrawTextFrame(impeller::MakeTextFrameFromTextBlobSkia(text), x + label_x, y + height + label_y, paint); + return; } #endif // IMPELLER_SUPPORTS_RENDERING canvas->DrawTextBlob(text, x + label_x, y + height + label_y, paint); - return; } } diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index b41143b863f6e..7537302d80fe7 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -10,6 +10,7 @@ #include "flutter/flow/flow_test_utils.h" #include "flutter/flow/raster_cache.h" #include "flutter/flow/testing/layer_test.h" +#include "flutter/shell/common/base64.h" #include "flutter/testing/mock_canvas.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkImage.h" @@ -18,7 +19,6 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/encode/SkPngEncoder.h" -#include "third_party/skia/include/utils/SkBase64.h" namespace flutter { namespace testing { @@ -111,11 +111,10 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { wstream.write(snapshot_data->data(), snapshot_data->size()); wstream.flush(); - size_t b64_size = - SkBase64::Encode(snapshot_data->data(), snapshot_data->size(), nullptr); + size_t b64_size = Base64::EncodedSize(snapshot_data->size()); sk_sp b64_data = SkData::MakeUninitialized(b64_size + 1); char* b64_char = static_cast(b64_data->writable_data()); - SkBase64::Encode(snapshot_data->data(), snapshot_data->size(), b64_char); + Base64::Encode(snapshot_data->data(), snapshot_data->size(), b64_char); b64_char[b64_size] = 0; // make it null terminated for printing EXPECT_TRUE(golden_data_matches) @@ -181,7 +180,7 @@ TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { paint_context().raster_time, "Raster", ""); auto overlay_text_data = overlay_text->serialize(SkSerialProcs{}); // Historically SK_ColorGRAY (== 0xFF888888) was used here - DlPaint text_paint(0xFF888888); + DlPaint text_paint(DlColor(0xFF888888)); SkPoint text_position = SkPoint::Make(16.0f, 22.0f); // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc index 4354f8715a1db..5588caa369478 100644 --- a/flow/layers/shader_mask_layer_unittests.cc +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -396,7 +396,7 @@ TEST_F(ShaderMaskLayerTest, OpacityInheritance) { { expected_builder.Translate(offset.fX, offset.fY); /* ShaderMaskLayer::Paint() */ { - DlPaint sl_paint = DlPaint(opacity_alpha << 24); + DlPaint sl_paint = DlPaint(DlColor(opacity_alpha << 24)); expected_builder.SaveLayer(&child_path.getBounds(), &sl_paint); { /* child layer paint */ { diff --git a/flow/paint_utils.cc b/flow/paint_utils.cc index 9b35291cffc70..eab62daad1d7a 100644 --- a/flow/paint_utils.cc +++ b/flow/paint_utils.cc @@ -49,7 +49,7 @@ void DrawCheckerboard(DlCanvas* canvas, const SkRect& rect) { // Stroke the drawn area DlPaint debug_paint; debug_paint.setStrokeWidth(8); - debug_paint.setColor(SkColorSetA(checkerboard_color, 255)); + debug_paint.setColor(DlColor(SkColorSetA(checkerboard_color, 255))); debug_paint.setDrawStyle(DlDrawStyle::kStroke); canvas->DrawRect(rect, debug_paint); } diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 1da2963ebffe7..e341e6cccc568 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -36,7 +36,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const sample_unit_width = (1.0 / kMaxSamples); // Provide a semi-transparent background for the graph. - painter.DrawRect(rect, 0x99FFFFFF); + painter.DrawRect(rect, DlColor(0x99FFFFFF)); // Prepare a path for the data; we start at the height of the last point so // it looks like we wrap around. @@ -54,7 +54,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, /*top=*/y + bar_height, /*right=*/bar_left + bar_width, /*bottom=*/bottom), - 0xAA0000FF); + DlColor(0xAA0000FF)); } } @@ -79,7 +79,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, /*top=*/y + frame_height, /*right=*/width, /*bottom=*/y + frame_height + 1), - 0xCC000000); + DlColor(0xCC000000)); } } } diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 178c307426c19..9f2cfa04a2d31 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -5,6 +5,7 @@ #ifndef FLOW_TESTING_LAYER_TEST_H_ #define FLOW_TESTING_LAYER_TEST_H_ +#include "display_list/dl_color.h" #include "flutter/flow/layer_snapshot_store.h" #include "flutter/flow/layers/layer.h" @@ -40,7 +41,7 @@ template class LayerTestBase : public CanvasTestBase { using TestT = CanvasTestBase; - const SkRect kDlBounds = SkRect::MakeWH(500, 500); + const SkRect k_dl_bounds_ = SkRect::MakeWH(500, 500); public: LayerTestBase() @@ -72,7 +73,7 @@ class LayerTestBase : public CanvasTestBase { .raster_cache = nullptr, // clang-format on }, - display_list_builder_(kDlBounds), + display_list_builder_(k_dl_bounds_), display_list_paint_context_{ // clang-format off .state_stack = display_list_state_stack_, @@ -103,7 +104,7 @@ class LayerTestBase : public CanvasTestBase { display_list_state_stack_.set_delegate(&display_list_builder_); checkerboard_state_stack_.set_delegate(&display_list_builder_); checkerboard_state_stack_.set_checkerboard_func(draw_checkerboard); - checkerboard_paint_.setColor(checkerboard_color_); + checkerboard_paint_.setColor(kCheckerboardColor); } /** @@ -195,12 +196,6 @@ class LayerTestBase : public CanvasTestBase { paint_context_.layer_snapshot_store = nullptr; } - void enable_impeller() { - preroll_context_.impeller_enabled = true; - paint_context_.impeller_enabled = true; - display_list_paint_context_.impeller_enabled = true; - } - private: void set_raster_cache_(std::unique_ptr raster_cache) { raster_cache_ = std::move(raster_cache); @@ -209,12 +204,12 @@ class LayerTestBase : public CanvasTestBase { display_list_paint_context_.raster_cache = raster_cache_.get(); } - static constexpr SkColor checkerboard_color_ = 0x42424242; + static constexpr DlColor kCheckerboardColor = DlColor(0x42424242); static void draw_checkerboard(DlCanvas* canvas, const SkRect& rect) { if (canvas) { DlPaint paint; - paint.setColor(checkerboard_color_); + paint.setColor(kCheckerboardColor); canvas->DrawRect(rect, paint); } } diff --git a/flutter_frontend_server/BUILD.gn b/flutter_frontend_server/BUILD.gn index 9e986066595ab..29a50e31b4cda 100644 --- a/flutter_frontend_server/BUILD.gn +++ b/flutter_frontend_server/BUILD.gn @@ -2,19 +2,17 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/build/dart/rules.gni") +import("//flutter/common/config.gni") -application_snapshot("frontend_server") { - main_dart = "bin/starter.dart" - deps = [ "//flutter/lib/snapshot:kernel_platform_files" ] +copy("frontend_server") { + if (flutter_prebuilt_dart_sdk) { + snapshot = + "$host_prebuilt_dart_sdk/bin/snapshots/frontend_server.dart.snapshot" + } else { + deps = [ "//third_party/dart/utils/kernel-service:frontend_server" ] + snapshot = "$root_out_dir/frontend_server.dart.snapshot" + } - package_config = rebase_path(".dart_tool/package_config.json") - flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") - training_args = [ - "--train", - "--sdk-root=$flutter_patched_sdk", - rebase_path(main_dart), - ] - - inputs = frontend_server_files + sources = [ snapshot ] + outputs = [ "$root_gen_dir/frontend_server.dart.snapshot" ] } diff --git a/flutter_frontend_server/README.md b/flutter_frontend_server/README.md deleted file mode 100644 index 3007831b924c6..0000000000000 --- a/flutter_frontend_server/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Frontend Server - -Frontend server is simple wrapper around Dart Frontend. It is a Dart application -that compiles Dart source into Dart Kernel binary (.dill-file). -Documentation on Dart Kernel (semantic, binary format, etc) can be found here: -https://github.com/dart-lang/sdk/wiki/Kernel-Documentation. - -Frontend server runs in two modes: - - immediate mode, where Dart source file name is provided as command line - argument; - - interactive mode, where communication is happening over stdin/stdout. - -## Interactive mode instructions - -### Compile/Recompile -``` -compile -``` - Compiles Dart source file with Dart Frontend. Replies with `result` response. - -``` -recompile - - -... - -``` - Incrementally recompiles Dart program previously compiled in current session, taking into account - changes in the listed files. Replies with `result` response. - - Relative paths should be relative to current working directory for the shell that launched - Frontend Server. - -### Accept/Reject -``` -accept -``` - Accepts results of incremental compilation, so that on next recompilation request Dart Frontend - will not include these recompiled files. -``` -reject -``` - Rejects results of incremental compilation, so that on next recompilation request Dart Frontend - will include compilation results from previously rejected recompilation in addition to what it - will recompile based on newly changed files. - Small technical detail is that Dart Frontend will not recompile files from previously rejected - recompilation attempts (unless they were changed since then), it will just include appropriate - kernel binaries it kept around from those previously rejected compilation requests. - - One use of `accept` and `reject` instructions is in the context of Dart VM hot-reload. Dart VM can - reject user-provided incremental change to what is currently running. It could happen for variety - of Dart VM internal reasons. For example, if incremental update changes some `class` to `enum`, - such update can not be hot-reloaded by VM at this point, will be rejected. - -### Quit -``` -quit -``` - Stops the server. - -## Response from the server - -``` -result - - [] -``` -Response from the Dart Frontend compiler is bracketed by `` tags. If the compiler -was able to produce a Dart Kernel file, the name of this file `` is provided too. -If the compiler encountered unrecoverable errors, there will be no output file name provided. - diff --git a/flutter_frontend_server/bin/starter.dart b/flutter_frontend_server/bin/starter.dart deleted file mode 100644 index e839087dd5d04..0000000000000 --- a/flutter_frontend_server/bin/starter.dart +++ /dev/null @@ -1,15 +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. - - -import 'dart:io'; - -import 'package:flutter_frontend_server/server.dart'; - -Future main(List args) async { - final int exitCode = await starter(args); - if (exitCode != 0) { - exit(exitCode); - } -} diff --git a/flutter_frontend_server/lib/server.dart b/flutter_frontend_server/lib/server.dart deleted file mode 100644 index 8733c5b7a9070..0000000000000 --- a/flutter_frontend_server/lib/server.dart +++ /dev/null @@ -1,91 +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. - -// ignore_for_file: avoid_print - - -import 'dart:async'; -import 'dart:io' hide FileSystemEntity; - -import 'package:args/args.dart'; -import 'package:frontend_server/frontend_server.dart' as frontend - show - CompilerInterface, - FrontendCompiler, - argParser, - listenAndCompile, - usage; -import 'package:path/path.dart' as path; - -/// Entry point for this module, that creates `FrontendCompiler` instance and -/// processes user input. -/// `compiler` is an optional parameter so it can be replaced with mocked -/// version for testing. -Future starter( - List args, { - frontend.CompilerInterface? compiler, - Stream>? input, - StringSink? output, -}) async { - ArgResults options; - try { - options = frontend.argParser.parse(args); - } catch (error) { - print('ERROR: $error\n'); - print(frontend.usage); - return 1; - } - - if (options['train'] as bool) { - if (!options.rest.isNotEmpty) { - throw Exception('Must specify input.dart'); - } - - final String input = options.rest[0]; - final String sdkRoot = options['sdk-root'] as String; - final Directory temp = - Directory.systemTemp.createTempSync('train_frontend_server'); - try { - for (int i = 0; i < 3; i++) { - final String outputTrainingDill = path.join(temp.path, 'app.dill'); - options = frontend.argParser.parse([ - '--incremental', - '--sdk-root=$sdkRoot', - '--output-dill=$outputTrainingDill', - '--target=flutter', - '--track-widget-creation', - '--enable-asserts', - ]); - compiler ??= frontend.FrontendCompiler(output); - - await compiler.compile(input, options); - compiler.acceptLastDelta(); - await compiler.recompileDelta(); - compiler.acceptLastDelta(); - compiler.resetIncrementalCompiler(); - await compiler.recompileDelta(); - compiler.acceptLastDelta(); - await compiler.recompileDelta(); - compiler.acceptLastDelta(); - } - return 0; - } finally { - temp.deleteSync(recursive: true); - } - } - - compiler ??= frontend.FrontendCompiler(output, - useDebuggerModuleNames: options['debugger-module-names'] as bool, - emitDebugMetadata: options['experimental-emit-debug-metadata'] as bool, - unsafePackageSerialization: - options['unsafe-package-serialization'] as bool); - - if (options.rest.isNotEmpty) { - return await compiler.compile(options.rest[0], options) ? 0 : 254; - } - - final Completer completer = Completer(); - frontend.listenAndCompile(compiler, input ?? stdin, options, completer); - return completer.future; -} diff --git a/flutter_frontend_server/pubspec.yaml b/flutter_frontend_server/pubspec.yaml index 8861b9d217fd8..16fd3ad7ee566 100644 --- a/flutter_frontend_server/pubspec.yaml +++ b/flutter_frontend_server/pubspec.yaml @@ -18,104 +18,22 @@ homepage: https://flutter.dev # relative to this directory into //third_party/dart environment: - sdk: '>=3.0.0-0 <4.0.0' - -dependencies: - args: any - frontend_server: any - path: any + sdk: '>=3.2.0-0 <4.0.0' dev_dependencies: litetest: any + path: any dependency_overrides: - _fe_analyzer_shared: - path: ../../third_party/dart/pkg/_fe_analyzer_shared - _js_interop_checks: - path: ../../third_party/dart/pkg/_js_interop_checks - args: - path: ../../third_party/dart/third_party/pkg/args - async: - path: ../../third_party/dart/third_party/pkg/async async_helper: path: ../../third_party/dart/pkg/async_helper - bazel_worker: - path: ../../third_party/dart/third_party/pkg/bazel_worker - build_integration: - path: ../../third_party/dart/pkg/build_integration - collection: - path: ../../third_party/dart/third_party/pkg/collection - compiler: - path: ../../third_party/dart/pkg/compiler - crypto: - path: ../../third_party/dart/third_party/pkg/crypto - dart_internal: - path: ../../third_party/dart/pkg/dart_internal - dart2js_info: - path: ../../third_party/dart/pkg/dart2js_info - dart2wasm: - path: ../../third_party/dart/pkg/dart2wasm - dev_compiler: - path: ../../third_party/dart/pkg/dev_compiler expect: path: ../../third_party/dart/pkg/expect - ffi: - path: ../../third_party/dart/third_party/pkg/ffi - fixnum: - path: ../../third_party/dart/third_party/pkg/fixnum - front_end: - path: ../../third_party/dart/pkg/front_end - frontend_server: - path: ../../third_party/dart/pkg/frontend_server - http_parser: - path: ../../third_party/dart/third_party/pkg/http_parser - js_ast: - path: ../../third_party/dart/pkg/js_ast - js_runtime: - path: ../../third_party/dart/pkg/js_runtime - js_shared: - path: ../../third_party/dart/pkg/js_shared - kernel: - path: ../../third_party/dart/pkg/kernel litetest: path: ../testing/litetest meta: path: ../../third_party/dart/pkg/meta - mmap: - path: ../../third_party/dart/pkg/mmap - package_config: - path: ../../third_party/dart/third_party/pkg/package_config path: path: ../../third_party/dart/third_party/pkg/path - protobuf: - path: ../../third_party/dart/third_party/pkg/protobuf/protobuf - shelf: - path: ../../third_party/dart/third_party/pkg/shelf/pkgs/shelf smith: path: ../../third_party/dart/pkg/smith - source_maps: - path: ../../third_party/dart/third_party/pkg/source_maps - source_span: - path: ../../third_party/dart/third_party/pkg/source_span - stack_trace: - path: ../../third_party/dart/third_party/pkg/stack_trace - stream_channel: - path: ../../third_party/dart/third_party/pkg/stream_channel - string_scanner: - path: ../../third_party/dart/third_party/pkg/string_scanner - term_glyph: - path: ../../third_party/dart/third_party/pkg/term_glyph - typed_data: - path: ../../third_party/dart/third_party/pkg/typed_data - usage: - path: ../../third_party/dart/third_party/pkg/usage - vm: - path: ../../third_party/dart/pkg/vm - vm_service: - path: ../../third_party/dart/pkg/vm_service - vm_snapshot_analysis: - path: ../../third_party/dart/pkg/vm_snapshot_analysis - wasm_builder: - path: ../../third_party/dart/pkg/wasm_builder - yaml: - path: ../../third_party/dart/third_party/pkg/yaml diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart index 53957fdae1b78..ded3b98362730 100644 --- a/flutter_frontend_server/test/to_string_test.dart +++ b/flutter_frontend_server/test/to_string_test.dart @@ -45,9 +45,7 @@ Future main(List args) async { ])); final ProcessResult runResult = Process.runSync(dart, [regularDill]); checkProcessResult(runResult); - // TODO(matanlurey): "dither: true" is now present by default, until it is - // remove entirely. See https://github.com/flutter/flutter/issues/112498. - String paintString = '"Paint.toString":"Paint(Color(0xffffffff); dither: true)"'; + String paintString = '"Paint.toString":"Paint(Color(0xffffffff))"'; if (buildDir.contains('release')) { paintString = '"Paint.toString":"Instance of \'Paint\'"'; } diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 7906c32968b4c..3faf7ceacbf78 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -19,6 +19,8 @@ source_set("fml") { "concurrent_message_loop.cc", "concurrent_message_loop.h", "container.h", + "cpu_affinity.cc", + "cpu_affinity.h", "delayed_task.cc", "delayed_task.h", "eintr_wrapper.h", @@ -67,6 +69,8 @@ source_set("fml") { "shared_thread_merger.cc", "shared_thread_merger.h", "size.h", + "status.h", + "status_or.h", "synchronization/atomic_object.h", "synchronization/count_down_latch.cc", "synchronization/count_down_latch.h", @@ -170,6 +174,8 @@ source_set("fml") { if (is_android) { sources += [ + "platform/android/cpu_affinity.cc", + "platform/android/cpu_affinity.h", "platform/android/jni_util.cc", "platform/android/jni_util.h", "platform/android/jni_weak_ref.cc", @@ -319,8 +325,10 @@ if (enable_unittests) { "ascii_trie_unittests.cc", "backtrace_unittests.cc", "base32_unittest.cc", + "closure_unittests.cc", "command_line_unittest.cc", "container_unittests.cc", + "cpu_affinity_unittests.cc", "endianness_unittests.cc", "file_unittest.cc", "hash_combine_unittests.cc", @@ -331,7 +339,6 @@ if (enable_unittests) { "memory/ref_counted_unittest.cc", "memory/task_runner_checker_unittest.cc", "memory/weak_ptr_unittest.cc", - "message_loop_impl_unittests.cc", "message_loop_task_queues_merge_unmerge_unittests.cc", "message_loop_task_queues_unittests.cc", "message_loop_unittests.cc", diff --git a/fml/base32.h b/fml/base32.h index aacbdc696e3ad..7892aaddb3484 100644 --- a/fml/base32.h +++ b/fml/base32.h @@ -25,7 +25,7 @@ class BitConverter { int Extract() { FML_DCHECK(CanExtract()); int result = Peek(); - buffer_ = (buffer_ << to_length) & mask_; + buffer_ = (buffer_ << to_length) & kMask; lower_free_bits_ += to_length; return result; } @@ -40,7 +40,7 @@ class BitConverter { static_assert(buffer_length >= 2 * to_length); static_assert(buffer_length < sizeof(int) * 8); - static constexpr int mask_ = (1 << buffer_length) - 1; + static constexpr int kMask = (1 << buffer_length) - 1; int buffer_ = 0; int lower_free_bits_ = buffer_length; diff --git a/fml/closure.h b/fml/closure.h index 7631f3a7be439..3a0de0dcbbc6d 100644 --- a/fml/closure.h +++ b/fml/closure.h @@ -29,10 +29,19 @@ using closure = std::function; /// automatically at the end of the scope. This covers the cases /// where there are early returns as well. /// -class ScopedCleanupClosure { +class ScopedCleanupClosure final { public: ScopedCleanupClosure() = default; + ScopedCleanupClosure(ScopedCleanupClosure&& other) { + closure_ = other.Release(); + } + + ScopedCleanupClosure& operator=(ScopedCleanupClosure&& other) { + closure_ = other.Release(); + return *this; + } + explicit ScopedCleanupClosure(const fml::closure& closure) : closure_(closure) {} diff --git a/fml/closure_unittests.cc b/fml/closure_unittests.cc new file mode 100644 index 0000000000000..37124d9ae16c6 --- /dev/null +++ b/fml/closure_unittests.cc @@ -0,0 +1,71 @@ +// 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 "fml/closure.h" +#include "gtest/gtest.h" + +TEST(ScopedCleanupClosureTest, DestructorDoesNothingWhenNoClosureSet) { + fml::ScopedCleanupClosure cleanup; + + // Nothing should happen. +} + +TEST(ScopedCleanupClosureTest, ReleaseDoesNothingWhenNoClosureSet) { + fml::ScopedCleanupClosure cleanup; + + // Nothing should happen. + EXPECT_EQ(nullptr, cleanup.Release()); +} + +TEST(ScopedCleanupClosureTest, ClosureInvokedOnDestructorWhenSetInConstructor) { + auto invoked = false; + + { + fml::ScopedCleanupClosure cleanup([&invoked]() { invoked = true; }); + + EXPECT_FALSE(invoked); + } + + EXPECT_TRUE(invoked); +} + +TEST(ScopedCleanupClosureTest, ClosureInvokedOnDestructorWhenSet) { + auto invoked = false; + + { + fml::ScopedCleanupClosure cleanup; + cleanup.SetClosure([&invoked]() { invoked = true; }); + + EXPECT_FALSE(invoked); + } + + EXPECT_TRUE(invoked); +} + +TEST(ScopedCleanupClosureTest, ClosureNotInvokedWhenMoved) { + auto invoked = 0; + + { + fml::ScopedCleanupClosure cleanup([&invoked]() { invoked++; }); + fml::ScopedCleanupClosure cleanup2(std::move(cleanup)); + + EXPECT_EQ(0, invoked); + } + + EXPECT_EQ(1, invoked); +} + +TEST(ScopedCleanupClosureTest, ClosureNotInvokedWhenMovedViaAssignment) { + auto invoked = 0; + + { + fml::ScopedCleanupClosure cleanup([&invoked]() { invoked++; }); + fml::ScopedCleanupClosure cleanup2; + cleanup2 = std::move(cleanup); + + EXPECT_EQ(0, invoked); + } + + EXPECT_EQ(1, invoked); +} diff --git a/fml/cpu_affinity.cc b/fml/cpu_affinity.cc new file mode 100644 index 0000000000000..ccb2e89686581 --- /dev/null +++ b/fml/cpu_affinity.cc @@ -0,0 +1,100 @@ +// 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/fml/cpu_affinity.h" +#include "flutter/fml/build_config.h" + +#include +#include +#include + +#ifdef FML_OS_ANDROID +#include "flutter/fml/platform/android/cpu_affinity.h" +#endif // FML_OS_ANDROID + +namespace fml { + +std::optional EfficiencyCoreCount() { +#ifdef FML_OS_ANDROID + return AndroidEfficiencyCoreCount(); +#else + return std::nullopt; +#endif +} + +bool RequestAffinity(CpuAffinity affinity) { +#ifdef FML_OS_ANDROID + return AndroidRequestAffinity(affinity); +#else + return true; +#endif +} + +CPUSpeedTracker::CPUSpeedTracker(std::vector data) + : cpu_speeds_(std::move(data)) { + std::optional max_speed = std::nullopt; + std::optional min_speed = std::nullopt; + for (const auto& data : cpu_speeds_) { + if (!max_speed.has_value() || data.speed > max_speed.value()) { + max_speed = data.speed; + } + if (!min_speed.has_value() || data.speed < min_speed.value()) { + min_speed = data.speed; + } + } + if (!max_speed.has_value() || !min_speed.has_value() || + min_speed.value() == max_speed.value()) { + return; + } + const int64_t max_speed_value = max_speed.value(); + const int64_t min_speed_value = min_speed.value(); + + for (const auto& data : cpu_speeds_) { + if (data.speed == max_speed_value) { + performance_.push_back(data.index); + } else { + not_performance_.push_back(data.index); + } + if (data.speed == min_speed_value) { + efficiency_.push_back(data.index); + } + } + + valid_ = true; +} + +bool CPUSpeedTracker::IsValid() const { + return valid_; +} + +const std::vector& CPUSpeedTracker::GetIndices( + CpuAffinity affinity) const { + switch (affinity) { + case CpuAffinity::kPerformance: + return performance_; + case CpuAffinity::kEfficiency: + return efficiency_; + case CpuAffinity::kNotPerformance: + return not_performance_; + } +} + +// Get the size of the cpuinfo file by reading it until the end. This is +// required because files under /proc do not always return a valid size +// when using fseek(0, SEEK_END) + ftell(). Nor can they be mmap()-ed. +std::optional ReadIntFromFile(const std::string& path) { + std::ifstream file; + file.open(path.c_str()); + + // Dont use stoi because if this data isnt a parseable number then it + // will abort, as we compile with exceptions disabled. + int64_t speed = 0; + file >> speed; + if (speed > 0) { + return speed; + } + return std::nullopt; +} + +} // namespace fml diff --git a/fml/cpu_affinity.h b/fml/cpu_affinity.h new file mode 100644 index 0000000000000..31b58940a61a1 --- /dev/null +++ b/fml/cpu_affinity.h @@ -0,0 +1,86 @@ +// 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. + +#pragma once + +#include +#include +#include + +namespace fml { + +/// The CPU Affinity provides a hint to the operating system on which cores a +/// particular thread should be scheduled on. The operating system may or may +/// not honor these requests. +enum class CpuAffinity { + /// @brief Request CPU affinity for the performance cores. + /// + /// Generally speaking, only the UI and Raster thread should + /// use this option. + kPerformance, + + /// @brief Request CPU affinity for the efficiency cores. + kEfficiency, + + /// @brief Request affinity for all non-performance cores. + kNotPerformance, +}; + +/// @brief Request count of efficiency cores. +/// +/// Efficiency cores are defined as those with the lowest reported +/// cpu_max_freq. If the CPU speed could not be determined, or if all +/// cores have the same reported speed then this returns std::nullopt. +/// That is, the result will never be 0. +std::optional EfficiencyCoreCount(); + +/// @brief Request the given affinity for the current thread. +/// +/// Returns true if successfull, or if it was a no-op. This function is +/// only supported on Android devices. +/// +/// Affinity requests are based on documented CPU speed. This speed data +/// is parsed from cpuinfo_max_freq files, see also: +/// https://www.kernel.org/doc/Documentation/cpu-freq/user-guide.txt +bool RequestAffinity(CpuAffinity affinity); + +struct CpuIndexAndSpeed { + // The index of the given CPU. + size_t index; + // CPU speed in kHZ + int64_t speed; +}; + +/// @brief A class that computes the correct CPU indices for a requested CPU +/// affinity. +/// +/// @note This is visible for testing. +class CPUSpeedTracker { + public: + explicit CPUSpeedTracker(std::vector data); + + /// @brief The class is valid if it has more than one CPU index and a distinct + /// set of efficiency or performance CPUs. + /// + /// If all CPUs are the same speed this returns false, and all requests + /// to set affinity are ignored. + bool IsValid() const; + + /// @brief Return the set of CPU indices for the requested CPU affinity. + /// + /// If the tracker is valid, this will always return a non-empty set. + const std::vector& GetIndices(CpuAffinity affinity) const; + + private: + bool valid_ = false; + std::vector cpu_speeds_; + std::vector efficiency_; + std::vector performance_; + std::vector not_performance_; +}; + +/// @note Visible for testing. +std::optional ReadIntFromFile(const std::string& path); + +} // namespace fml diff --git a/fml/cpu_affinity_unittests.cc b/fml/cpu_affinity_unittests.cc new file mode 100644 index 0000000000000..8a5c90687ed24 --- /dev/null +++ b/fml/cpu_affinity_unittests.cc @@ -0,0 +1,95 @@ +// 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 "cpu_affinity.h" + +#include "fml/file.h" +#include "fml/mapping.h" +#include "gtest/gtest.h" +#include "logging.h" + +namespace fml { +namespace testing { + +TEST(CpuAffinity, NonAndroidPlatformDefaults) { + ASSERT_FALSE(fml::EfficiencyCoreCount().has_value()); + ASSERT_TRUE(fml::RequestAffinity(fml::CpuAffinity::kEfficiency)); +} + +TEST(CpuAffinity, NormalSlowMedFastCores) { + auto speeds = {CpuIndexAndSpeed{.index = 0, .speed = 1}, + CpuIndexAndSpeed{.index = 1, .speed = 2}, + CpuIndexAndSpeed{.index = 2, .speed = 3}}; + auto tracker = CPUSpeedTracker(speeds); + + ASSERT_TRUE(tracker.IsValid()); + ASSERT_EQ(tracker.GetIndices(CpuAffinity::kEfficiency)[0], 0u); + ASSERT_EQ(tracker.GetIndices(CpuAffinity::kPerformance)[0], 2u); + ASSERT_EQ(tracker.GetIndices(CpuAffinity::kNotPerformance).size(), 2u); + ASSERT_EQ(tracker.GetIndices(CpuAffinity::kNotPerformance)[0], 0u); + ASSERT_EQ(tracker.GetIndices(CpuAffinity::kNotPerformance)[1], 1u); +} + +TEST(CpuAffinity, NoCpuData) { + auto tracker = CPUSpeedTracker({}); + + ASSERT_FALSE(tracker.IsValid()); +} + +TEST(CpuAffinity, AllSameSpeed) { + auto speeds = {CpuIndexAndSpeed{.index = 0, .speed = 1}, + CpuIndexAndSpeed{.index = 1, .speed = 1}, + CpuIndexAndSpeed{.index = 2, .speed = 1}}; + auto tracker = CPUSpeedTracker(speeds); + + ASSERT_FALSE(tracker.IsValid()); +} + +TEST(CpuAffinity, SingleCore) { + auto speeds = {CpuIndexAndSpeed{.index = 0, .speed = 1}}; + auto tracker = CPUSpeedTracker(speeds); + + ASSERT_FALSE(tracker.IsValid()); +} + +TEST(CpuAffinity, FileParsing) { + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + + // Generate a fake CPU speed file + fml::DataMapping test_data(std::string("12345")); + ASSERT_TRUE(fml::WriteAtomically(base_dir.fd(), "test_file", test_data)); + + auto file = fml::OpenFileReadOnly(base_dir.fd(), "test_file"); + ASSERT_TRUE(file.is_valid()); + + // Open file and parse speed. + auto result = ReadIntFromFile(base_dir.path() + "/test_file"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(result.value_or(0), 12345); +} + +TEST(CpuAffinity, FileParsingWithNonNumber) { + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + + // Generate a fake CPU speed file + fml::DataMapping test_data(std::string("whoa this isnt a number")); + ASSERT_TRUE(fml::WriteAtomically(base_dir.fd(), "test_file", test_data)); + + auto file = fml::OpenFileReadOnly(base_dir.fd(), "test_file"); + ASSERT_TRUE(file.is_valid()); + + // Open file and parse speed. + auto result = ReadIntFromFile(base_dir.path() + "/test_file"); + ASSERT_FALSE(result.has_value()); +} + +TEST(CpuAffinity, MissingFileParsing) { + auto result = ReadIntFromFile("/does_not_exist"); + ASSERT_FALSE(result.has_value()); +} + +} // namespace testing +} // namespace fml diff --git a/fml/endianness.h b/fml/endianness.h index eff90f9cafb3c..18758b7b69492 100644 --- a/fml/endianness.h +++ b/fml/endianness.h @@ -32,11 +32,11 @@ struct IsByteSwappable integral_constant || std::is_enum_v> { }; template -constexpr bool IsByteSwappableV = IsByteSwappable::value; +constexpr bool kIsByteSwappableV = IsByteSwappable::value; /// @brief Flips the endianness of the given value. /// The given value must be an integral type of size 1, 2, 4, or 8. -template >> +template >> constexpr T ByteSwap(T n) { if constexpr (sizeof(T) == 1) { return n; @@ -55,7 +55,7 @@ constexpr T ByteSwap(T n) { /// current architecture. This is effectively a cross platform /// ntohl/ntohs (as network byte order is always Big Endian). /// The given value must be an integral type of size 1, 2, 4, or 8. -template >> +template >> constexpr T BigEndianToArch(T n) { #if FML_ARCH_CPU_LITTLE_ENDIAN return ByteSwap(n); @@ -67,7 +67,7 @@ constexpr T BigEndianToArch(T n) { /// @brief Convert a known little endian value to match the endianness of the /// current architecture. /// The given value must be an integral type of size 1, 2, 4, or 8. -template >> +template >> constexpr T LittleEndianToArch(T n) { #if !FML_ARCH_CPU_LITTLE_ENDIAN return ByteSwap(n); diff --git a/fml/log_level.h b/fml/log_level.h index 2c9a85305b53d..8610aa5fe52df 100644 --- a/fml/log_level.h +++ b/fml/log_level.h @@ -7,29 +7,54 @@ namespace fml { +// Default log levels. Negative values can be used for verbose log levels. typedef int LogSeverity; -// Default log levels. Negative values can be used for verbose log levels. -constexpr LogSeverity LOG_INFO = 0; -constexpr LogSeverity LOG_WARNING = 1; -constexpr LogSeverity LOG_ERROR = 2; -constexpr LogSeverity LOG_FATAL = 3; -constexpr LogSeverity LOG_NUM_SEVERITIES = 4; +constexpr LogSeverity kLogInfo = 0; +constexpr LogSeverity kLogWarning = 1; +constexpr LogSeverity kLogError = 2; +constexpr LogSeverity kLogFatal = 3; +constexpr LogSeverity kLogNumSeverities = 4; + +// DEPRECATED: Use |kLogInfo|. +// Ignoring Clang Tidy because this is used in a very common substitution macro. +// NOLINTNEXTLINE(readability-identifier-naming) +constexpr LogSeverity LOG_INFO = kLogInfo; + +// DEPRECATED: Use |kLogWarning|. +// Ignoring Clang Tidy because this is used in a very common substitution macro. +// NOLINTNEXTLINE(readability-identifier-naming) +constexpr LogSeverity LOG_WARNING = kLogWarning; + +// DEPRECATED: Use |kLogError|. +// Ignoring Clang Tidy because this is used in a very common substitution macro. +// NOLINTNEXTLINE(readability-identifier-naming) +constexpr LogSeverity LOG_ERROR = kLogError; + +// DEPRECATED: Use |kLogFatal|. +// Ignoring Clang Tidy because this is used in a very common substitution macro. +// NOLINTNEXTLINE(readability-identifier-naming) +constexpr LogSeverity LOG_FATAL = kLogFatal; // One of the Windows headers defines ERROR to 0. This makes the token // concatenation in FML_LOG(ERROR) to resolve to LOG_0. We define this back to // the appropriate log level. #ifdef _WIN32 -#define LOG_0 LOG_ERROR +#define LOG_0 kLogError #endif -// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode +// kLogDFatal is kLogFatal in debug mode, kLogError in normal mode #ifdef NDEBUG -const LogSeverity LOG_DFATAL = LOG_ERROR; +const LogSeverity kLogDFatal = kLogError; #else -const LogSeverity LOG_DFATAL = LOG_FATAL; +const LogSeverity kLogDFatal = kLogFatal; #endif +// DEPRECATED: Use |kLogDFatal|. +// Ignoring Clang Tidy because this is used in a very common substitution macro. +// NOLINTNEXTLINE(readability-identifier-naming) +const LogSeverity LOG_DFATAL = kLogDFatal; + } // namespace fml #endif // FLUTTER_FML_LOG_LEVEL_H_ diff --git a/fml/log_settings.cc b/fml/log_settings.cc index 93df3d4f23719..b81e78a3d70af 100644 --- a/fml/log_settings.cc +++ b/fml/log_settings.cc @@ -12,6 +12,7 @@ #include #include "flutter/fml/build_config.h" +#include "flutter/fml/log_level.h" #include "flutter/fml/logging.h" #if defined(OS_FUCHSIA) @@ -29,7 +30,7 @@ extern LogSettings g_log_settings; void SetLogSettings(const LogSettings& settings) { // Validate the new settings as we set them. state::g_log_settings.min_log_level = - std::min(LOG_FATAL, settings.min_log_level); + std::min(kLogFatal, settings.min_log_level); #if defined(OS_FUCHSIA) // Syslog should accept all logs, since filtering by severity is done by fml. fx_logger_t* logger = fx_log_get_logger(); @@ -45,7 +46,7 @@ LogSettings GetLogSettings() { } int GetMinLogLevel() { - return std::min(state::g_log_settings.min_log_level, LOG_FATAL); + return std::min(state::g_log_settings.min_log_level, kLogFatal); } ScopedSetLogSettings::ScopedSetLogSettings(const LogSettings& settings) { diff --git a/fml/log_settings.h b/fml/log_settings.h index 6a3664f0d3989..91587aaf82a06 100644 --- a/fml/log_settings.h +++ b/fml/log_settings.h @@ -17,12 +17,12 @@ struct LogSettings { // Anything at or above this level will be logged (if applicable). // Anything below this level will be silently ignored. // - // The log level defaults to 0 (LOG_INFO). + // The log level defaults to 0 (kLogInfo). // // Log messages for FML_VLOG(x) (from flutter/fml/logging.h) are logged // at level -x, so setting the min log level to negative values enables // verbose logging. - LogSeverity min_log_level = LOG_INFO; + LogSeverity min_log_level = kLogInfo; }; // Gets the active log settings for the current process. @@ -32,7 +32,7 @@ void SetLogSettings(const LogSettings& settings); LogSettings GetLogSettings(); // Gets the minimum log level for the current process. Never returs a value -// higher than LOG_FATAL. +// higher than kLogFatal. int GetMinLogLevel(); class ScopedSetLogSettings { diff --git a/fml/logging.cc b/fml/logging.cc index 0bffd08776f29..0d48943980e04 100644 --- a/fml/logging.cc +++ b/fml/logging.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/build_config.h" +#include "flutter/fml/log_level.h" #include "flutter/fml/log_settings.h" #include "flutter/fml/logging.h" @@ -23,11 +24,11 @@ namespace fml { namespace { #if !defined(OS_FUCHSIA) -const char* const kLogSeverityNames[LOG_NUM_SEVERITIES] = {"INFO", "WARNING", - "ERROR", "FATAL"}; +const char* const kLogSeverityNames[kLogNumSeverities] = {"INFO", "WARNING", + "ERROR", "FATAL"}; const char* GetNameForLogSeverity(LogSeverity severity) { - if (severity >= LOG_INFO && severity < LOG_NUM_SEVERITIES) { + if (severity >= kLogInfo && severity < kLogNumSeverities) { return kLogSeverityNames[severity]; } return "UNKNOWN"; @@ -58,12 +59,12 @@ LogMessage::LogMessage(LogSeverity severity, : severity_(severity), file_(file), line_(line) { #if !defined(OS_FUCHSIA) stream_ << "["; - if (severity >= LOG_INFO) { + if (severity >= kLogInfo) { stream_ << GetNameForLogSeverity(severity); } else { stream_ << "VERBOSE" << -severity; } - stream_ << ":" << (severity > LOG_INFO ? StripDots(file_) : StripPath(file_)) + stream_ << ":" << (severity > kLogInfo ? StripDots(file_) : StripPath(file_)) << "(" << line_ << ")] "; #endif @@ -109,16 +110,16 @@ LogMessage::~LogMessage() { android_LogPriority priority = (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN; switch (severity_) { - case LOG_INFO: + case kLogInfo: priority = ANDROID_LOG_INFO; break; - case LOG_WARNING: + case kLogWarning: priority = ANDROID_LOG_WARN; break; - case LOG_ERROR: + case kLogError: priority = ANDROID_LOG_ERROR; break; - case LOG_FATAL: + case kLogFatal: priority = ANDROID_LOG_FATAL; break; } @@ -128,16 +129,16 @@ LogMessage::~LogMessage() { #elif defined(OS_FUCHSIA) fx_log_severity_t fx_severity; switch (severity_) { - case LOG_INFO: + case kLogInfo: fx_severity = FX_LOG_INFO; break; - case LOG_WARNING: + case kLogWarning: fx_severity = FX_LOG_WARNING; break; - case LOG_ERROR: + case kLogError: fx_severity = FX_LOG_ERROR; break; - case LOG_FATAL: + case kLogFatal: fx_severity = FX_LOG_FATAL; break; default: @@ -157,13 +158,13 @@ LogMessage::~LogMessage() { #endif } - if (severity_ >= LOG_FATAL) { + if (severity_ >= kLogFatal) { KillProcess(); } } int GetVlogVerbosity() { - return std::max(-1, LOG_INFO - GetMinLogLevel()); + return std::max(-1, kLogInfo - GetMinLogLevel()); } bool ShouldCreateLogMessage(LogSeverity severity) { diff --git a/fml/logging.h b/fml/logging.h index 39dac4c8e1568..146c35bc88e28 100644 --- a/fml/logging.h +++ b/fml/logging.h @@ -57,7 +57,7 @@ class LogMessage { int GetVlogVerbosity(); // Returns true if |severity| is at or above the current minimum log level. -// LOG_FATAL and above is always true. +// kLogFatal and above is always true. bool ShouldCreateLogMessage(LogSeverity severity); [[noreturn]] void KillProcess(); @@ -74,7 +74,7 @@ bool ShouldCreateLogMessage(LogSeverity severity); true || (ignored) \ ? (void)0 \ : ::fml::LogMessageVoidify() & \ - ::fml::LogMessage(::fml::LOG_FATAL, 0, 0, nullptr).stream() + ::fml::LogMessage(::fml::kLogFatal, 0, 0, nullptr).stream() #define FML_LOG_IS_ON(severity) \ (::fml::ShouldCreateLogMessage(::fml::LOG_##severity)) @@ -84,7 +84,7 @@ bool ShouldCreateLogMessage(LogSeverity severity); #define FML_CHECK(condition) \ FML_LAZY_STREAM( \ - ::fml::LogMessage(::fml::LOG_FATAL, __FILE__, __LINE__, #condition) \ + ::fml::LogMessage(::fml::kLogFatal, __FILE__, __LINE__, #condition) \ .stream(), \ !(condition)) diff --git a/fml/math.h b/fml/math.h index 6182ada09d60c..57dced6630b3b 100644 --- a/fml/math.h +++ b/fml/math.h @@ -12,16 +12,16 @@ namespace math { constexpr float kE = 2.7182818284590452354; // log_2 e -constexpr float kLog2_E = 1.4426950408889634074; +constexpr float kLog2E = 1.4426950408889634074; // log_10 e -constexpr float kLog10_E = 0.43429448190325182765; +constexpr float kLog10E = 0.43429448190325182765; // log_e 2 -constexpr float klogE_2 = 0.69314718055994530942; +constexpr float kLogE2 = 0.69314718055994530942; // log_e 10 -constexpr float klogE_10 = 2.30258509299404568402; +constexpr float kLogE10 = 2.30258509299404568402; // pi constexpr float kPi = 3.14159265358979323846; diff --git a/fml/math_unittests.cc b/fml/math_unittests.cc index 0e627e28bb615..020d886037bbd 100644 --- a/fml/math_unittests.cc +++ b/fml/math_unittests.cc @@ -12,9 +12,9 @@ namespace testing { TEST(MathTest, Constants) { // Don't use the constants in cmath as those aren't portable. - EXPECT_FLOAT_EQ(std::log2(math::kE), math::kLog2_E); - EXPECT_FLOAT_EQ(std::log10(math::kE), math::kLog10_E); - EXPECT_FLOAT_EQ(std::log(2.0f), math::klogE_2); + EXPECT_FLOAT_EQ(std::log2(math::kE), math::kLog2E); + EXPECT_FLOAT_EQ(std::log10(math::kE), math::kLog10E); + EXPECT_FLOAT_EQ(std::log(2.0f), math::kLogE2); EXPECT_FLOAT_EQ(math::kPi / 2.0f, math::kPiOver2); EXPECT_FLOAT_EQ(math::kPi / 4.0f, math::kPiOver4); EXPECT_FLOAT_EQ(1.0f / math::kPi, math::k1OverPi); diff --git a/fml/memory/ref_counted_internal.h b/fml/memory/ref_counted_internal.h index a5c5fe04d6280..47bda01005ef9 100644 --- a/fml/memory/ref_counted_internal.h +++ b/fml/memory/ref_counted_internal.h @@ -76,8 +76,8 @@ class RefCountedThreadSafeBase { mutable std::atomic_uint_fast32_t ref_count_; #ifndef NDEBUG - mutable bool adoption_required_; - mutable bool destruction_started_; + mutable bool adoption_required_ = false; + mutable bool destruction_started_ = false; #endif FML_DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); diff --git a/fml/memory/ref_ptr.h b/fml/memory/ref_ptr.h index c426a4901d7a2..149c058987a8e 100644 --- a/fml/memory/ref_ptr.h +++ b/fml/memory/ref_ptr.h @@ -205,7 +205,7 @@ class RefPtr final { friend RefPtr AdoptRef(T*); - enum AdoptTag { ADOPT }; + enum AdoptTag { kAdopt }; RefPtr(T* ptr, AdoptTag) : ptr_(ptr) { FML_DCHECK(ptr_); } T* ptr_; @@ -222,7 +222,7 @@ inline RefPtr AdoptRef(T* ptr) { #ifndef NDEBUG ptr->Adopt(); #endif - return RefPtr(ptr, RefPtr::ADOPT); + return RefPtr(ptr, RefPtr::kAdopt); } // Constructs a |RefPtr| from a plain pointer (to an object that must diff --git a/fml/memory/thread_checker.h b/fml/memory/thread_checker.h index 6d5f2fe04046e..220ea6fb67782 100644 --- a/fml/memory/thread_checker.h +++ b/fml/memory/thread_checker.h @@ -75,9 +75,10 @@ class ThreadChecker final { if (0 == pthread_getname_np(current_thread, actual_thread, buffer_length) && 0 == pthread_getname_np(self_, expected_thread, buffer_length)) { - FML_DLOG(ERROR) << "IsCreationThreadCurrent expected thread: '" - << expected_thread << "' actual thread:'" - << actual_thread << "'"; + FML_DLOG(ERROR) << "Object referenced on a thread other than the one " + "on which it was created. Expected thread: '" + << expected_thread << "'. Actual thread: '" + << actual_thread << "'."; } } #endif // __APPLE__ diff --git a/fml/message_loop_impl_unittests.cc b/fml/message_loop_impl_unittests.cc deleted file mode 100644 index 1846f8c9d46bc..0000000000000 --- a/fml/message_loop_impl_unittests.cc +++ /dev/null @@ -1,43 +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. - -#define FML_USED_ON_EMBEDDER - -#include "flutter/fml/message_loop_impl.h" - -#include "flutter/fml/time/time_delta.h" -#include "flutter/fml/time/time_point.h" -#include "gtest/gtest.h" - -#define TIMESENSITIVE(x) TimeSensitiveTest_##x - -TEST(MessageLoopImpl, TIMESENSITIVE(WakeUpTimersAreSingletons)) { - auto loop_impl = fml::MessageLoopImpl::Create(); - - const auto t1 = fml::TimeDelta::FromMilliseconds(10); - const auto t2 = fml::TimeDelta::FromMilliseconds(30); - - const auto begin = fml::TimePoint::Now(); - - // Register a task scheduled in the future. This schedules a WakeUp call on - // the MessageLoopImpl with that fml::TimePoint. - loop_impl->PostTask( - [&]() { - auto delta = fml::TimePoint::Now() - begin; - auto ms = delta.ToMillisecondsF(); - ASSERT_GE(ms, 20); - ASSERT_LE(ms, 40); - - loop_impl->Terminate(); - }, - begin + t1); - - // Call WakeUp manually to change the WakeUp time further in the future. If - // the timer is correctly set up to be rearmed instead of a task being - // scheduled for each WakeUp, the above task will be executed at t2 instead of - // t1 now. - loop_impl->WakeUp(begin + t2); - - loop_impl->Run(); -} diff --git a/fml/message_loop_task_queues.cc b/fml/message_loop_task_queues.cc index 30c1e37880421..f92dd86bfbf20 100644 --- a/fml/message_loop_task_queues.cc +++ b/fml/message_loop_task_queues.cc @@ -36,7 +36,7 @@ FML_THREAD_LOCAL ThreadLocalUniquePtr tls_task_source_grade; TaskQueueEntry::TaskQueueEntry(TaskQueueId created_for_arg) - : subsumed_by(_kUnmerged), created_for(created_for_arg) { + : subsumed_by(kUnmerged), created_for(created_for_arg) { wakeable = NULL; task_observers = TaskObservers(); task_source = std::make_unique(created_for); @@ -66,7 +66,7 @@ MessageLoopTaskQueues::~MessageLoopTaskQueues() = default; void MessageLoopTaskQueues::Dispose(TaskQueueId queue_id) { std::lock_guard guard(queue_mutex_); const auto& queue_entry = queue_entries_.at(queue_id); - FML_DCHECK(queue_entry->subsumed_by == _kUnmerged); + FML_DCHECK(queue_entry->subsumed_by == kUnmerged); auto& subsumed_set = queue_entry->owner_of; for (auto& subsumed : subsumed_set) { queue_entries_.erase(subsumed); @@ -78,7 +78,7 @@ void MessageLoopTaskQueues::Dispose(TaskQueueId queue_id) { void MessageLoopTaskQueues::DisposeTasks(TaskQueueId queue_id) { std::lock_guard guard(queue_mutex_); const auto& queue_entry = queue_entries_.at(queue_id); - FML_DCHECK(queue_entry->subsumed_by == _kUnmerged); + FML_DCHECK(queue_entry->subsumed_by == kUnmerged); auto& subsumed_set = queue_entry->owner_of; queue_entry->task_source->ShutDown(); for (auto& subsumed : subsumed_set) { @@ -101,7 +101,7 @@ void MessageLoopTaskQueues::RegisterTask( queue_entry->task_source->RegisterTask( {order, task, target_time, task_source_grade}); TaskQueueId loop_to_wake = queue_id; - if (queue_entry->subsumed_by != _kUnmerged) { + if (queue_entry->subsumed_by != kUnmerged) { loop_to_wake = queue_entry->subsumed_by; } @@ -151,7 +151,7 @@ void MessageLoopTaskQueues::WakeUpUnlocked(TaskQueueId queue_id, size_t MessageLoopTaskQueues::GetNumPendingTasks(TaskQueueId queue_id) const { std::lock_guard guard(queue_mutex_); const auto& queue_entry = queue_entries_.at(queue_id); - if (queue_entry->subsumed_by != _kUnmerged) { + if (queue_entry->subsumed_by != kUnmerged) { return 0; } @@ -185,7 +185,7 @@ std::vector MessageLoopTaskQueues::GetObserversToNotify( std::lock_guard guard(queue_mutex_); std::vector observers; - if (queue_entries_.at(queue_id)->subsumed_by != _kUnmerged) { + if (queue_entries_.at(queue_id)->subsumed_by != kUnmerged) { return observers; } @@ -226,8 +226,8 @@ bool MessageLoopTaskQueues::Merge(TaskQueueId owner, TaskQueueId subsumed) { // Won't check owner_entry->owner_of, because it may contains items when // merged with other different queues. - // Ensure owner_entry->subsumed_by being _kUnmerged - if (owner_entry->subsumed_by != _kUnmerged) { + // Ensure owner_entry->subsumed_by being kUnmerged + if (owner_entry->subsumed_by != kUnmerged) { FML_LOG(WARNING) << "Thread merging failed: owner_entry was already " "subsumed by others, owner=" << owner << ", subsumed=" << subsumed @@ -242,8 +242,8 @@ bool MessageLoopTaskQueues::Merge(TaskQueueId owner, TaskQueueId subsumed) { << ", subsumed->owner_of.size()=" << subsumed_entry->owner_of.size(); return false; } - // Ensure subsumed_entry->subsumed_by being _kUnmerged - if (subsumed_entry->subsumed_by != _kUnmerged) { + // Ensure subsumed_entry->subsumed_by being kUnmerged + if (subsumed_entry->subsumed_by != kUnmerged) { FML_LOG(WARNING) << "Thread merging failed: subsumed_entry was already " "subsumed by others, owner=" << owner << ", subsumed=" << subsumed @@ -271,14 +271,14 @@ bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner, TaskQueueId subsumed) { << owner << ", subsumed=" << subsumed; return false; } - if (owner_entry->subsumed_by != _kUnmerged) { + if (owner_entry->subsumed_by != kUnmerged) { FML_LOG(WARNING) << "Thread unmerging failed: owner_entry was subsumed by others, owner=" << owner << ", subsumed=" << subsumed << ", owner_entry->subsumed_by=" << owner_entry->subsumed_by; return false; } - if (queue_entries_.at(subsumed)->subsumed_by == _kUnmerged) { + if (queue_entries_.at(subsumed)->subsumed_by == kUnmerged) { FML_LOG(WARNING) << "Thread unmerging failed: subsumed_entry wasn't " "subsumed by others, owner=" << owner << ", subsumed=" << subsumed; @@ -291,7 +291,7 @@ bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner, TaskQueueId subsumed) { return false; } - queue_entries_.at(subsumed)->subsumed_by = _kUnmerged; + queue_entries_.at(subsumed)->subsumed_by = kUnmerged; owner_entry->owner_of.erase(subsumed); if (HasPendingTasksUnlocked(owner)) { @@ -308,7 +308,7 @@ bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner, TaskQueueId subsumed) { bool MessageLoopTaskQueues::Owns(TaskQueueId owner, TaskQueueId subsumed) const { std::lock_guard guard(queue_mutex_); - if (owner == _kUnmerged || subsumed == _kUnmerged) { + if (owner == kUnmerged || subsumed == kUnmerged) { return false; } auto& subsumed_set = queue_entries_.at(owner)->owner_of; @@ -340,7 +340,7 @@ void MessageLoopTaskQueues::ResumeSecondarySource(TaskQueueId queue_id) { bool MessageLoopTaskQueues::HasPendingTasksUnlocked( TaskQueueId queue_id) const { const auto& entry = queue_entries_.at(queue_id); - bool is_subsumed = entry->subsumed_by != _kUnmerged; + bool is_subsumed = entry->subsumed_by != kUnmerged; if (is_subsumed) { return false; } diff --git a/fml/message_loop_task_queues.h b/fml/message_loop_task_queues.h index 2d89caac305ab..ba464493952c0 100644 --- a/fml/message_loop_task_queues.h +++ b/fml/message_loop_task_queues.h @@ -22,7 +22,7 @@ namespace fml { -static const TaskQueueId _kUnmerged = TaskQueueId(TaskQueueId::kUnmerged); +static const TaskQueueId kUnmerged = TaskQueueId(TaskQueueId::kUnmerged); /// A collection of tasks and observers associated with one TaskQueue. /// @@ -40,7 +40,7 @@ class TaskQueueEntry { /// empty, this TaskQueue does not own any other TaskQueues. std::set owner_of; - /// Identifies the TaskQueue that subsumes this TaskQueue. If it is _kUnmerged + /// Identifies the TaskQueue that subsumes this TaskQueue. If it is kUnmerged /// it indicates that this TaskQueue is not owned by any other TaskQueue. TaskQueueId subsumed_by; diff --git a/fml/message_loop_task_queues_unittests.cc b/fml/message_loop_task_queues_unittests.cc index f9753ea8ad34c..135af2303413b 100644 --- a/fml/message_loop_task_queues_unittests.cc +++ b/fml/message_loop_task_queues_unittests.cc @@ -46,8 +46,7 @@ TEST(MessageLoopTaskQueue, RegisterOneTask) { [&time](fml::TimePoint wake_time) { ASSERT_TRUE(wake_time == time); }); task_queue->SetWakeable(queue_id, wakeable.get()); - task_queue->RegisterTask( - queue_id, [] {}, time); + task_queue->RegisterTask(queue_id, [] {}, time); ASSERT_TRUE(task_queue->HasPendingTasks(queue_id)); ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 1); } @@ -55,10 +54,8 @@ TEST(MessageLoopTaskQueue, RegisterOneTask) { TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); auto queue_id = task_queue->CreateTaskQueue(); - task_queue->RegisterTask( - queue_id, [] {}, ChronoTicksSinceEpoch()); - task_queue->RegisterTask( - queue_id, [] {}, fml::TimePoint::Max()); + task_queue->RegisterTask(queue_id, [] {}, ChronoTicksSinceEpoch()); + task_queue->RegisterTask(queue_id, [] {}, fml::TimePoint::Max()); ASSERT_TRUE(task_queue->HasPendingTasks(queue_id)); ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 2); } @@ -68,11 +65,9 @@ TEST(MessageLoopTaskQueue, RegisterTasksOnMergedQueuesAndCount) { auto platform_queue = task_queue->CreateTaskQueue(); auto raster_queue = task_queue->CreateTaskQueue(); // A task in platform_queue - task_queue->RegisterTask( - platform_queue, []() {}, fml::TimePoint::Now()); + task_queue->RegisterTask(platform_queue, []() {}, fml::TimePoint::Now()); // A task in raster_queue - task_queue->RegisterTask( - raster_queue, []() {}, fml::TimePoint::Now()); + task_queue->RegisterTask(raster_queue, []() {}, fml::TimePoint::Now()); ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1); ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1); @@ -270,10 +265,8 @@ TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) { [&num_wakes](fml::TimePoint wake_time) { ++num_wakes; }); task_queue->SetWakeable(queue_id, wakeable.get()); - task_queue->RegisterTask( - queue_id, []() {}, ChronoTicksSinceEpoch()); - task_queue->RegisterTask( - queue_id, []() {}, fml::TimePoint::Max()); + task_queue->RegisterTask(queue_id, []() {}, ChronoTicksSinceEpoch()); + task_queue->RegisterTask(queue_id, []() {}, fml::TimePoint::Max()); ASSERT_TRUE(num_wakes == 2); } @@ -293,13 +286,11 @@ TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) { task_queue->SetWakeable(queue_id, wakeable.get()); - task_queue->RegisterTask( - queue_id, []() {}, fml::TimePoint::Max()); + task_queue->RegisterTask(queue_id, []() {}, fml::TimePoint::Max()); const auto now = ChronoTicksSinceEpoch(); expected = now; - task_queue->RegisterTask( - queue_id, []() {}, now); + task_queue->RegisterTask(queue_id, []() {}, now); latch.Wait(); } @@ -338,9 +329,9 @@ TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) { TEST(MessageLoopTaskQueue, QueueDoNotOwnUnmergedTaskQueueId) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); - ASSERT_FALSE(task_queue->Owns(task_queue->CreateTaskQueue(), _kUnmerged)); - ASSERT_FALSE(task_queue->Owns(_kUnmerged, task_queue->CreateTaskQueue())); - ASSERT_FALSE(task_queue->Owns(_kUnmerged, _kUnmerged)); + ASSERT_FALSE(task_queue->Owns(task_queue->CreateTaskQueue(), kUnmerged)); + ASSERT_FALSE(task_queue->Owns(kUnmerged, task_queue->CreateTaskQueue())); + ASSERT_FALSE(task_queue->Owns(kUnmerged, kUnmerged)); } TEST(MessageLoopTaskQueue, QueueOwnsMergedTaskQueueId) { @@ -435,16 +426,14 @@ TEST(MessageLoopTaskQueue, RegisterTaskWakesUpOwnerQueue) { ASSERT_EQ(0UL, wakes.size()); - task_queue->RegisterTask( - platform_queue, []() {}, time1); + task_queue->RegisterTask(platform_queue, []() {}, time1); ASSERT_EQ(1UL, wakes.size()); ASSERT_EQ(time1, wakes[0]); task_queue->Merge(platform_queue, raster_queue); - task_queue->RegisterTask( - raster_queue, []() {}, time2); + task_queue->RegisterTask(raster_queue, []() {}, time2); ASSERT_EQ(3UL, wakes.size()); ASSERT_EQ(time1, wakes[1]); diff --git a/fml/message_loop_unittests.cc b/fml/message_loop_unittests.cc index 222810d406021..df762f4249f3e 100644 --- a/fml/message_loop_unittests.cc +++ b/fml/message_loop_unittests.cc @@ -17,13 +17,6 @@ #include "flutter/fml/time/chrono_timestamp_provider.h" #include "gtest/gtest.h" -#define TIMESENSITIVE(x) TimeSensitiveTest_##x -#if FML_OS_WIN -#define PLATFORM_SPECIFIC_CAPTURE(...) [ __VA_ARGS__, count ] -#else -#define PLATFORM_SPECIFIC_CAPTURE(...) [__VA_ARGS__] -#endif - TEST(MessageLoop, GetCurrent) { std::thread thread([]() { fml::MessageLoop::EnsureInitializedForCurrentThread(); @@ -89,15 +82,14 @@ TEST(MessageLoop, NonDelayedTasksAreRunInOrder) { auto& loop = fml::MessageLoop::GetCurrent(); size_t current = 0; for (size_t i = 0; i < count; i++) { - loop.GetTaskRunner()->PostTask( - PLATFORM_SPECIFIC_CAPTURE(&terminated, i, ¤t)() { - ASSERT_EQ(current, i); - current++; - if (count == i + 1) { - fml::MessageLoop::GetCurrent().Terminate(); - terminated = true; - } - }); + loop.GetTaskRunner()->PostTask([&terminated, i, ¤t]() { + ASSERT_EQ(current, i); + current++; + if (count == i + 1) { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + } + }); } loop.Run(); ASSERT_EQ(current, count); @@ -120,7 +112,7 @@ TEST(MessageLoop, DelayedTasksAtSameTimeAreRunInOrder) { fml::ChronoTicksSinceEpoch() + fml::TimeDelta::FromMilliseconds(2); for (size_t i = 0; i < count; i++) { loop.GetTaskRunner()->PostTaskForTime( - PLATFORM_SPECIFIC_CAPTURE(&terminated, i, ¤t)() { + [&terminated, i, ¤t]() { ASSERT_EQ(current, i); current++; if (count == i + 1) { @@ -155,128 +147,6 @@ TEST(MessageLoop, CheckRunsTaskOnCurrentThread) { thread.join(); } -TEST(MessageLoop, TIMESENSITIVE(SingleDelayedTaskByDelta)) { -#if defined(OS_FUCHSIA) - GTEST_SKIP() - << "This test does not work on Fuchsia. https://fxbug.dev/110020 "; -#else - - bool checked = false; - std::thread thread([&checked]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - auto& loop = fml::MessageLoop::GetCurrent(); - auto begin = fml::ChronoTicksSinceEpoch(); - loop.GetTaskRunner()->PostDelayedTask( - [begin, &checked]() { - auto delta = fml::ChronoTicksSinceEpoch() - begin; - auto ms = delta.ToMillisecondsF(); - ASSERT_GE(ms, 3); - ASSERT_LE(ms, 7); - checked = true; - fml::MessageLoop::GetCurrent().Terminate(); - }, - fml::TimeDelta::FromMilliseconds(5)); - loop.Run(); - }); - thread.join(); - ASSERT_TRUE(checked); -#endif // OS_FUCHSIA -} - -TEST(MessageLoop, TIMESENSITIVE(SingleDelayedTaskForTime)) { -#if defined(OS_FUCHSIA) - GTEST_SKIP() - << "This test does not work on Fuchsia. https://fxbug.dev/110020 "; -#else - - bool checked = false; - std::thread thread([&checked]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - auto& loop = fml::MessageLoop::GetCurrent(); - auto begin = fml::ChronoTicksSinceEpoch(); - loop.GetTaskRunner()->PostTaskForTime( - [begin, &checked]() { - auto delta = fml::ChronoTicksSinceEpoch() - begin; - auto ms = delta.ToMillisecondsF(); - ASSERT_GE(ms, 3); - ASSERT_LE(ms, 7); - checked = true; - fml::MessageLoop::GetCurrent().Terminate(); - }, - fml::ChronoTicksSinceEpoch() + fml::TimeDelta::FromMilliseconds(5)); - loop.Run(); - }); - thread.join(); - ASSERT_TRUE(checked); -#endif // OS_FUCHSIA -} - -TEST(MessageLoop, TIMESENSITIVE(MultipleDelayedTasksWithIncreasingDeltas)) { -#if defined(OS_FUCHSIA) - GTEST_SKIP() - << "This test does not work on Fuchsia. https://fxbug.dev/110020 "; -#else - - const auto count = 10; - int checked = false; - std::thread thread(PLATFORM_SPECIFIC_CAPTURE(&checked)() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - auto& loop = fml::MessageLoop::GetCurrent(); - for (int target_ms = 0 + 2; target_ms < count + 2; target_ms++) { - auto begin = fml::ChronoTicksSinceEpoch(); - loop.GetTaskRunner()->PostDelayedTask( - PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() { - auto delta = fml::ChronoTicksSinceEpoch() - begin; - auto ms = delta.ToMillisecondsF(); - ASSERT_GE(ms, target_ms - 2); - ASSERT_LE(ms, target_ms + 2); - checked++; - if (checked == count) { - fml::MessageLoop::GetCurrent().Terminate(); - } - }, - fml::TimeDelta::FromMilliseconds(target_ms)); - } - loop.Run(); - }); - thread.join(); - ASSERT_EQ(checked, count); -#endif // OS_FUCHSIA -} - -TEST(MessageLoop, TIMESENSITIVE(MultipleDelayedTasksWithDecreasingDeltas)) { -#if defined(OS_FUCHSIA) - GTEST_SKIP() - << "This test does not work on Fuchsia. https://fxbug.dev/110020 "; -#else - - const auto count = 10; - int checked = false; - std::thread thread(PLATFORM_SPECIFIC_CAPTURE(&checked)() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - auto& loop = fml::MessageLoop::GetCurrent(); - for (int target_ms = count + 2; target_ms > 0 + 2; target_ms--) { - auto begin = fml::ChronoTicksSinceEpoch(); - loop.GetTaskRunner()->PostDelayedTask( - PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() { - auto delta = fml::ChronoTicksSinceEpoch() - begin; - auto ms = delta.ToMillisecondsF(); - ASSERT_GE(ms, target_ms - 2); - ASSERT_LE(ms, target_ms + 2); - checked++; - if (checked == count) { - fml::MessageLoop::GetCurrent().Terminate(); - } - }, - fml::TimeDelta::FromMilliseconds(target_ms)); - } - loop.Run(); - }); - thread.join(); - ASSERT_EQ(checked, count); -#endif // OS_FUCHSIA -} - TEST(MessageLoop, TaskObserverFire) { bool started = false; bool terminated = false; @@ -286,19 +156,16 @@ TEST(MessageLoop, TaskObserverFire) { auto& loop = fml::MessageLoop::GetCurrent(); size_t task_count = 0; size_t obs_count = 0; - auto obs = PLATFORM_SPECIFIC_CAPTURE(&obs_count)() { - obs_count++; - }; + auto obs = [&obs_count]() { obs_count++; }; for (size_t i = 0; i < count; i++) { - loop.GetTaskRunner()->PostTask( - PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &task_count)() { - ASSERT_EQ(task_count, i); - task_count++; - if (count == i + 1) { - fml::MessageLoop::GetCurrent().Terminate(); - terminated = true; - } - }); + loop.GetTaskRunner()->PostTask([&terminated, i, &task_count]() { + ASSERT_EQ(task_count, i); + task_count++; + if (count == i + 1) { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + } + }); } loop.AddTaskObserver(0, obs); loop.Run(); diff --git a/fml/platform/android/cpu_affinity.cc b/fml/platform/android/cpu_affinity.cc new file mode 100644 index 0000000000000..f7477e97de3b0 --- /dev/null +++ b/fml/platform/android/cpu_affinity.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 "flutter/fml/platform/android/cpu_affinity.h" + +#include +#include +#include +#include +#include +#include +#include +#include "flutter/fml/logging.h" + +namespace fml { + +/// The CPUSpeedTracker is initialized once the first time a thread affinity is +/// requested. +std::once_flag gCPUTrackerFlag; +static CPUSpeedTracker* gCPUTracker; + +// For each CPU index provided, attempts to open the file +// /sys/devices/system/cpu/cpu$NUM/cpufreq/cpuinfo_max_freq and parse a number +// containing the CPU frequency. +void InitCPUInfo(size_t cpu_count) { + std::vector cpu_speeds; + + for (auto i = 0u; i < cpu_count; i++) { + auto path = "/sys/devices/system/cpu/cpu" + std::to_string(i) + + "/cpufreq/cpuinfo_max_freq"; + auto speed = ReadIntFromFile(path); + if (speed.has_value()) { + cpu_speeds.push_back({.index = i, .speed = speed.value()}); + } + } + gCPUTracker = new CPUSpeedTracker(cpu_speeds); +} + +bool SetUpCPUTracker() { + // Populate CPU Info if uninitialized. + auto count = std::thread::hardware_concurrency(); + std::call_once(gCPUTrackerFlag, [count]() { InitCPUInfo(count); }); + if (gCPUTracker == nullptr || !gCPUTracker->IsValid()) { + return false; + } + return true; +} + +std::optional AndroidEfficiencyCoreCount() { + if (!SetUpCPUTracker()) { + return true; + } + auto result = gCPUTracker->GetIndices(CpuAffinity::kEfficiency).size(); + FML_DCHECK(result > 0); + return result; +} + +bool AndroidRequestAffinity(CpuAffinity affinity) { + if (!SetUpCPUTracker()) { + return true; + } + + cpu_set_t set; + CPU_ZERO(&set); + for (const auto index : gCPUTracker->GetIndices(affinity)) { + CPU_SET(index, &set); + } + return sched_setaffinity(gettid(), sizeof(set), &set) == 0; +} + +} // namespace fml diff --git a/fml/platform/android/cpu_affinity.h b/fml/platform/android/cpu_affinity.h new file mode 100644 index 0000000000000..4ce1e7964aef9 --- /dev/null +++ b/fml/platform/android/cpu_affinity.h @@ -0,0 +1,17 @@ +// 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. + +#pragma once + +#include "flutter/fml/cpu_affinity.h" + +namespace fml { + +/// @brief Android specific implementation of EfficiencyCoreCount. +std::optional AndroidEfficiencyCoreCount(); + +/// @brief Android specific implementation of RequestAffinity. +bool AndroidRequestAffinity(CpuAffinity affinity); + +} // namespace fml diff --git a/fml/platform/darwin/scoped_block.h b/fml/platform/darwin/scoped_block.h index 2608d1ef3149e..319cb25d0d0ee 100644 --- a/fml/platform/darwin/scoped_block.h +++ b/fml/platform/darwin/scoped_block.h @@ -17,20 +17,20 @@ namespace fml { enum class OwnershipPolicy { // The scoped object takes ownership of an object by taking over an existing // ownership claim. - Assume, + kAssume, // The scoped object will retain the object and any initial ownership is // not changed. - Retain, + kRetain, }; template class ScopedBlock { public: explicit ScopedBlock(B block = nullptr, - OwnershipPolicy policy = OwnershipPolicy::Assume) + OwnershipPolicy policy = OwnershipPolicy::kAssume) : block_(block) { - if (block_ && policy == OwnershipPolicy::Retain) { + if (block_ && policy == OwnershipPolicy::kRetain) { block_ = Block_copy(block); } } @@ -48,13 +48,13 @@ class ScopedBlock { } ScopedBlock& operator=(const ScopedBlock& that) { - reset(that.get(), OwnershipPolicy::Retain); + reset(that.get(), OwnershipPolicy::kRetain); return *this; } void reset(B block = nullptr, - OwnershipPolicy policy = OwnershipPolicy::Assume) { - if (block && policy == OwnershipPolicy::Retain) { + OwnershipPolicy policy = OwnershipPolicy::kAssume) { + if (block && policy == OwnershipPolicy::kRetain) { block = Block_copy(block); } if (block_) { diff --git a/fml/status_or.h b/fml/status_or.h new file mode 100644 index 0000000000000..33718da74f126 --- /dev/null +++ b/fml/status_or.h @@ -0,0 +1,81 @@ +// 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_FML_STATUS_OR_H_ +#define FLUTTER_FML_STATUS_OR_H_ + +#include + +#include "flutter/fml/status.h" + +namespace fml { + +// TODO(https://github.com/flutter/flutter/issues/134741): Replace with +// absl::StatusOr. +/// Represents a union type of an object of type `T` and an fml::Status. +/// +/// This is often used as a replacement for C++ exceptions where a function that +/// could fail may return an error or a result. These are typically used for +/// errors that are meant to be recovered from. If there is no recovery +/// available `FML_CHECK` is more appropriate. +/// +/// Example: +/// StatusOr div(int n, int d) { +/// if (d == 0) { +/// return Status(StatusCode::kFailedPrecondition, "div by zero"); +/// } +/// return n / d; +/// } +template +class StatusOr { + public: + StatusOr(const T& value) : status_(), value_(value) {} + StatusOr(const Status& status) : status_(status), value_() {} + + StatusOr(const StatusOr&) = default; + StatusOr(StatusOr&&) = default; + + StatusOr& operator=(const StatusOr&) = default; + StatusOr& operator=(StatusOr&&) = default; + + StatusOr& operator=(const T& value) { + status_ = Status(); + value_ = value; + return *this; + } + + StatusOr& operator=(const T&& value) { + status_ = Status(); + value_ = std::move(value); + return *this; + } + + StatusOr& operator=(const Status& value) { + status_ = value; + value_ = std::nullopt; + return *this; + } + + const Status& status() const { return status_; } + + bool ok() const { return status_.ok(); } + + const T& value() const { + FML_CHECK(status_.ok()); + return value_.value(); + } + + T& value() { + FML_CHECK(status_.ok()); + return value_.value(); + } + + private: + Status status_; + std::optional value_; +}; + +} // namespace fml + +#endif diff --git a/fml/synchronization/sync_switch.cc b/fml/synchronization/sync_switch.cc index ac951062267f8..7340086af95b3 100644 --- a/fml/synchronization/sync_switch.cc +++ b/fml/synchronization/sync_switch.cc @@ -32,8 +32,26 @@ void SyncSwitch::Execute(const SyncSwitch::Handlers& handlers) const { } void SyncSwitch::SetSwitch(bool value) { + { + fml::UniqueLock lock(*mutex_); + value_ = value; + } + for (Observer* observer : observers_) { + observer->OnSyncSwitchUpdate(value); + } +} + +void SyncSwitch::AddObserver(Observer* observer) const { fml::UniqueLock lock(*mutex_); - value_ = value; + if (std::find(observers_.begin(), observers_.end(), observer) == + observers_.end()) { + observers_.push_back(observer); + } } +void SyncSwitch::RemoveObserver(Observer* observer) const { + fml::UniqueLock lock(*mutex_); + observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), + observers_.end()); +} } // namespace fml diff --git a/fml/synchronization/sync_switch.h b/fml/synchronization/sync_switch.h index 470072fe4f25d..d9b0807afd9c3 100644 --- a/fml/synchronization/sync_switch.h +++ b/fml/synchronization/sync_switch.h @@ -5,9 +5,9 @@ #ifndef FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_ #define FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_ -#include #include #include +#include #include "flutter/fml/macros.h" #include "flutter/fml/synchronization/shared_mutex.h" @@ -21,6 +21,16 @@ namespace fml { /// at a time. class SyncSwitch { public: + /// Observes changes to the SyncSwitch. + class Observer { + public: + virtual ~Observer() = default; + /// `new_is_disabled` not guaranteed to be the value of the SyncSwitch + /// during execution, it should be checked with calls to + /// SyncSwitch::Execute. + virtual void OnSyncSwitchUpdate(bool new_is_disabled) = 0; + }; + /// Represents the 2 code paths available when calling |SyncSwitch::Execute|. struct Handlers { /// Sets the handler that will be executed if the |SyncSwitch| is true. @@ -53,8 +63,15 @@ class SyncSwitch { /// @param[in] value New value for the |SyncSwitch|. void SetSwitch(bool value); + /// Threadsafe. + void AddObserver(Observer* observer) const; + + /// Threadsafe. + void RemoveObserver(Observer* observer) const; + private: mutable std::unique_ptr mutex_; + mutable std::vector observers_; bool value_; FML_DISALLOW_COPY_AND_ASSIGN(SyncSwitch); diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 4e9707f467984..c02ccd0558455 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/glfw/glfw_args.gni") +import("//flutter/third_party/glfw/glfw_args.gni") import("tools/impeller.gni") config("impeller_public_config") { @@ -34,12 +34,12 @@ config("impeller_public_config") { defines += [ "IMPELLER_ENABLE_VULKAN=1" ] } - if (impeller_trace_all_gl_calls) { - defines += [ "IMPELLER_TRACE_ALL_GL_CALLS" ] + if (impeller_enable_vulkan_playgrounds) { + defines += [ "IMPELLER_ENABLE_VULKAN_PLAYGROUNDS=1" ] } - if (impeller_error_check_all_gl_calls) { - defines += [ "IMPELLER_ERROR_CHECK_ALL_GL_CALLS" ] + if (impeller_trace_all_gl_calls) { + defines += [ "IMPELLER_TRACE_ALL_GL_CALLS" ] } if (is_win) { @@ -113,6 +113,10 @@ impeller_component("impeller_unittests") { deps += [ "//flutter/impeller/renderer/backend/vulkan:vulkan_unittests" ] } + if (impeller_enable_opengles) { + deps += [ "//flutter/impeller/renderer/backend/gles:gles_unittests" ] + } + if (glfw_vulkan_library != "") { deps += [ "//third_party/swiftshader", diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index ea963093336ba..f341021bdf5d3 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -4,12 +4,18 @@ import("../tools/impeller.gni") +config("impeller_canvas_recorder_config") { + defines = [ "IMPELLER_TRACE_CANVAS" ] +} + impeller_component("aiks") { sources = [ "aiks_context.cc", "aiks_context.h", "canvas.cc", "canvas.h", + "canvas_recorder.h", + "canvas_type.h", "color_filter.cc", "color_filter.h", "color_source.cc", @@ -26,6 +32,7 @@ impeller_component("aiks") { "picture.h", "picture_recorder.cc", "picture_recorder.h", + "trace_serializer.h", ] public_deps = [ @@ -35,6 +42,11 @@ impeller_component("aiks") { ] deps = [ "//flutter/fml" ] + + if (impeller_trace_canvas) { + sources += [ "trace_serializer.cc" ] + public_configs = [ ":impeller_canvas_recorder_config" ] + } } impeller_component("aiks_playground") { @@ -60,10 +72,12 @@ impeller_component("aiks_unittests") { sources = [ "aiks_unittests.cc", + "canvas_recorder_unittests.cc", "canvas_unittests.cc", "testing/context_mock.h", "testing/context_spy.cc", "testing/context_spy.h", + "trace_serializer_unittests.cc", ] deps = [ ":aiks", @@ -75,6 +89,11 @@ impeller_component("aiks_unittests") { "//flutter/impeller/typographer/backends/stb:typographer_stb_backend", "//flutter/testing:testing_lib", ] + + if (!impeller_trace_canvas) { + sources += [ "trace_serializer.cc" ] + public_configs = [ ":impeller_canvas_recorder_config" ] + } } impeller_component("aiks_unittests_golden") { diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index 3f31c658daedb..b9ed2a828e7ae 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -23,6 +23,11 @@ void AiksPlayground::SetTypographerContext( typographer_context_ = std::move(typographer_context); } +void AiksPlayground::TearDown() { + inspector_.HackResetDueToTextureLeaks(); + PlaygroundTest::TearDown(); +} + bool AiksPlayground::OpenPlaygroundHere(Picture picture) { return OpenPlaygroundHere([&picture](AiksContext& renderer) -> Picture { return std::move(picture); diff --git a/impeller/aiks/aiks_playground.h b/impeller/aiks/aiks_playground.h index d33a15b07f6cf..e01f0ef92de09 100644 --- a/impeller/aiks/aiks_playground.h +++ b/impeller/aiks/aiks_playground.h @@ -22,6 +22,8 @@ class AiksPlayground : public PlaygroundTest { ~AiksPlayground(); + void TearDown() override; + void SetTypographerContext( std::shared_ptr typographer_context); diff --git a/impeller/aiks/aiks_playground_inspector.cc b/impeller/aiks/aiks_playground_inspector.cc index 0f8721358d010..89ace8c5d1283 100644 --- a/impeller/aiks/aiks_playground_inspector.cc +++ b/impeller/aiks/aiks_playground_inspector.cc @@ -63,6 +63,10 @@ const std::optional& AiksInspector::RenderInspector( return last_picture_; } +void AiksInspector::HackResetDueToTextureLeaks() { + last_picture_.reset(); +} + static const auto kPropertiesProcTable = CaptureProcTable{ .boolean = [](CaptureBooleanProperty& p) { diff --git a/impeller/aiks/aiks_playground_inspector.h b/impeller/aiks/aiks_playground_inspector.h index 95b1e0777809e..b86b51baec7b2 100644 --- a/impeller/aiks/aiks_playground_inspector.h +++ b/impeller/aiks/aiks_playground_inspector.h @@ -21,6 +21,17 @@ class AiksInspector { AiksContext& aiks_context, const std::function()>& picture_callback); + // Resets (releases) the underlying |Picture| object. + // + // Underlying issue: . + // + // The tear-down code is not running in the right order; we still have a + // reference to the |Picture| object when the |Context| is being destroyed, + // which causes the |Texture| objects to leak. + // + // TODO(matanlurey): https://github.com/flutter/flutter/issues/134748. + void HackResetDueToTextureLeaks(); + private: void RenderCapture(CaptureContext& capture_context); void RenderCaptureElement(CaptureElement& element); diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 6af0cbc9283a9..14d70f2d27f7f 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -13,6 +13,7 @@ #include "flutter/testing/testing.h" #include "impeller/aiks/aiks_playground.h" #include "impeller/aiks/canvas.h" +#include "impeller/aiks/color_filter.h" #include "impeller/aiks/image.h" #include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" @@ -22,7 +23,6 @@ #include "impeller/entity/contents/conical_gradient_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/linear_gradient_contents.h" -#include "impeller/entity/contents/scene_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/tiled_texture_contents.h" #include "impeller/geometry/color.h" @@ -124,16 +124,43 @@ TEST_P(AiksTest, CanRenderImage) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, CanRenderInvertedImage) { +TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) { Canvas canvas; Paint paint; auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); paint.color = Color::Red(); + paint.color_filter = + ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow()); paint.invert_colors = true; + canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) { + Canvas canvas; + Paint paint; + paint.color = Color::Red(); + paint.color_filter = + ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow()); + paint.invert_colors = true; + + canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) { + Canvas canvas; + Paint paint; + paint.color = Color::Red(); + paint.color_filter = + ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow()); + paint.invert_colors = true; + + canvas.DrawPaint(paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + namespace { bool GenerateMipmap(const std::shared_ptr& context, std::shared_ptr texture, @@ -1206,59 +1233,76 @@ TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) { +TEST_P(AiksTest, CanRenderDifferencePaths) { Canvas canvas; Paint paint; paint.color = Color::Red(); - paint.style = Paint::Style::kStroke; - paint.stroke_width = 200; - Rect rect = {100, 100, 200, 200}; PathBuilder builder; - builder.AddArc(rect, Degrees(0), Degrees(90), false); - canvas.DrawPath(builder.TakePath(), paint); + PathBuilder::RoundingRadii radii; + radii.top_left = {50, 25}; + radii.top_right = {25, 50}; + radii.bottom_right = {50, 25}; + radii.bottom_left = {25, 50}; + + builder.AddRoundedRect({100, 100, 200, 200}, radii); + builder.AddCircle({200, 200}, 50); + auto path = builder.TakePath(FillType::kOdd); + + canvas.DrawImage( + std::make_shared(CreateTextureForFixture("boston.jpg")), {10, 10}, + Paint{}); + canvas.DrawPath(path, paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) { +// Regression test for https://github.com/flutter/flutter/issues/134816. +// +// It should be possible to draw 3 lines, and not have an implicit close path. +TEST_P(AiksTest, CanDrawAnOpenPath) { Canvas canvas; + // Starting at (50, 50), draw lines from: + // 1. (50, height) + // 2. (width, height) + // 3. (width, 50) + PathBuilder builder; + builder.MoveTo({50, 50}); + builder.LineTo({50, 100}); + builder.LineTo({100, 100}); + builder.LineTo({100, 50}); + Paint paint; paint.color = Color::Red(); paint.style = Paint::Style::kStroke; - paint.stroke_width = 20; - - PathBuilder builder; - builder.AddCubicCurve({0, 200}, {50, 400}, {350, 0}, {400, 200}); + paint.stroke_width = 10; canvas.DrawPath(builder.TakePath(), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, CanRenderDifferencePaths) { +TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) { Canvas canvas; - Paint paint; - paint.color = Color::Red(); - + // Draw a stroked path that is explicitly closed to verify + // It doesn't become a rectangle. PathBuilder builder; + builder.MoveTo({50, 50}); + builder.LineTo({520, 120}); + builder.LineTo({300, 310}); + builder.LineTo({100, 50}); + builder.Close(); - PathBuilder::RoundingRadii radii; - radii.top_left = {50, 25}; - radii.top_right = {25, 50}; - radii.bottom_right = {50, 25}; - radii.bottom_left = {25, 50}; - - builder.AddRoundedRect({100, 100, 200, 200}, radii); - builder.AddCircle({200, 200}, 50); - auto path = builder.TakePath(FillType::kOdd); + Paint paint; + paint.color = Color::Red(); + paint.style = Paint::Style::kStroke; + paint.stroke_width = 10; - canvas.DrawImage( - std::make_shared(CreateTextureForFixture("boston.jpg")), {10, 10}, - Paint{}); - canvas.DrawPath(path, paint); + canvas.DrawPath(builder.TakePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -1547,6 +1591,43 @@ TEST_P(AiksTest, CanDrawPaintWithAdvancedBlend) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, DrawPaintWithAdvancedBlendOverFilter) { + Paint filtered = { + .color = Color::Black(), + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(60), + }, + }; + + Canvas canvas; + canvas.DrawPaint({.color = Color::White()}); + canvas.DrawCircle({300, 300}, 200, filtered); + canvas.DrawPaint({.color = Color::Green(), .blend_mode = BlendMode::kScreen}); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, DrawAdvancedBlendPartlyOffscreen) { + std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, + Color{0.1294, 0.5882, 0.9529, 1.0}}; + std::vector stops = {0.0, 1.0}; + + Paint paint = { + .color_source = ColorSource::MakeLinearGradient( + {0, 0}, {100, 100}, std::move(colors), std::move(stops), + Entity::TileMode::kRepeat, Matrix::MakeScale(Vector3(0.3, 0.3, 0.3))), + .blend_mode = BlendMode::kLighten, + }; + + Canvas canvas; + canvas.DrawPaint({.color = Color::Blue()}); + canvas.Scale(Vector2(2, 2)); + canvas.ClipRect(Rect::MakeLTRB(0, 0, 200, 200)); + canvas.DrawCircle({100, 100}, 100, paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + #define BLEND_MODE_TUPLE(blend_mode) {#blend_mode, BlendMode::k##blend_mode}, struct BlendModeSelection { @@ -1983,22 +2064,6 @@ TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) { - Canvas canvas; - Paint paint; - paint.color = Color::Red(); - paint.style = Paint::Style::kStroke; - paint.stroke_width = 10; - paint.stroke_join = Join::kBevel; - - canvas.Translate({100, 100}); - canvas.DrawPath( - PathBuilder{}.AddRect(Rect::MakeSize(Size{100, 100})).TakePath(), - {paint}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) { // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636 Canvas canvas; @@ -2069,9 +2134,12 @@ TEST_P(AiksTest, CanRenderClippedLayers) { canvas.DrawRect(Rect::MakeSize(Size{400, 400}), {.color = Color::White()}); // Fill the layer with green, but do so with a color blend that can't be // collapsed into the parent pass. + // TODO(jonahwilliams): this blend mode was changed from color burn to + // hardlight to work around https://github.com/flutter/flutter/issues/136554 + // . canvas.DrawRect( Rect::MakeSize(Size{400, 400}), - {.color = Color::Green(), .blend_mode = BlendMode::kColorBurn}); + {.color = Color::Green(), .blend_mode = BlendMode::kHardLight}); } ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); @@ -2234,6 +2302,30 @@ TEST_P(AiksTest, DrawPaintAbsorbsClears) { ASSERT_EQ(render_pass->GetCommands().size(), 0llu); } +// This is important to enforce with texture reuse, since cached textures need +// to be cleared before reuse. +TEST_P(AiksTest, + ParentSaveLayerCreatesRenderPassWhenChildBackdropFilterIsPresent) { + Canvas canvas; + canvas.SaveLayer({}, std::nullopt, ImageFilter::MakeMatrix(Matrix(), {})); + canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource}); + canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); + canvas.Restore(); + + Picture picture = canvas.EndRecordingAsPicture(); + + std::shared_ptr spy = ContextSpy::Make(); + std::shared_ptr real_context = GetContext(); + std::shared_ptr mock_context = spy->MakeContext(real_context); + AiksContext renderer(mock_context, nullptr); + std::shared_ptr image = picture.ToImage(renderer, {300, 300}); + + ASSERT_EQ(spy->render_passes_.size(), 3llu); + std::shared_ptr render_pass = spy->render_passes_[0]; + ASSERT_EQ(render_pass->GetCommands().size(), 0llu); +} + TEST_P(AiksTest, DrawRectAbsorbsClears) { Canvas canvas; canvas.DrawRect({0, 0, 300, 300}, @@ -2338,6 +2430,35 @@ TEST_P(AiksTest, ClipRectElidesNoOpClips) { ASSERT_EQ(render_pass->GetCommands().size(), 0llu); } +TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) { + Canvas canvas; + canvas.SaveLayer({}, std::nullopt, + ImageFilter::MakeBlur(Sigma(3), Sigma(3), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource}); + canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); + canvas.Restore(); + + Picture picture = canvas.EndRecordingAsPicture(); + + std::optional actual_color; + picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool { + if (auto subpass = std::get_if>(&element)) { + actual_color = subpass->get()->GetClearColor(); + } + // Fail if the first element isn't a subpass. + return true; + }); + + ASSERT_TRUE(actual_color.has_value()); + if (!actual_color) { + return; + } + ASSERT_EQ(actual_color.value(), Color::BlackTransparent()); +} + TEST_P(AiksTest, CollapsedDrawPaintInSubpass) { Canvas canvas; canvas.DrawPaint( @@ -2871,7 +2992,7 @@ TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) { canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400)); canvas.DrawCircle({400, 400}, 200, { - .color = Color::White(), + .color = Color::Grey(), .color_filter = ColorFilter::MakeBlend( BlendMode::kColor, Color::Green()), .mask_blur_descriptor = @@ -3220,7 +3341,7 @@ TEST_P(AiksTest, MatrixSaveLayerFilter) { canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix( Matrix::MakeTranslation(Vector2(1, 1) * (200 + 100 * k1OverSqrt2)) * - Matrix::MakeScale(Vector2(1, 1) * 0.2) * + Matrix::MakeScale(Vector2(1, 1) * 0.5) * Matrix::MakeTranslation(Vector2(-200, -200)), SamplerDescriptor{})}, std::nullopt); @@ -3248,7 +3369,7 @@ TEST_P(AiksTest, MatrixBackdropFilter) { {}, std::nullopt, ImageFilter::MakeMatrix( Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) * - Matrix::MakeScale(Vector2(1, 1) * 0.2) * + Matrix::MakeScale(Vector2(1, 1) * 0.5) * Matrix::MakeTranslation(Vector2(-100, -100)), SamplerDescriptor{})); canvas.Restore(); @@ -3409,5 +3530,198 @@ TEST_P(AiksTest, CaptureInactivatedByDefault) { ASSERT_FALSE(GetContext()->capture.IsActive()); } +// Regression test for https://github.com/flutter/flutter/issues/134678. +TEST_P(AiksTest, ReleasesTextureOnTeardown) { + auto context = GetContext(); + std::weak_ptr weak_texture; + + { + auto texture = CreateTextureForFixture("table_mountain_nx.png"); + + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.Translate({100.0f, 100.0f, 0}); + + Paint paint; + paint.color_source = ColorSource::MakeImage( + texture, Entity::TileMode::kClamp, Entity::TileMode::kClamp, {}, {}); + canvas.DrawRect({0, 0, 600, 600}, paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + } + + // See https://github.com/flutter/flutter/issues/134751. + // + // If the fence waiter was working this may not be released by the end of the + // scope above. Adding a manual shutdown so that future changes to the fence + // waiter will not flake this test. + context->Shutdown(); + + // The texture should be released by now. + ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use " + "by the backend, it should be " + "released."; +} + +// Regression test for https://github.com/flutter/flutter/issues/135441 . +TEST_P(AiksTest, VerticesGeometryUVPositionData) { + Canvas canvas; + Paint paint; + auto texture = CreateTextureForFixture("table_mountain_nx.png"); + + paint.color_source = ColorSource::MakeImage(texture, Entity::TileMode::kClamp, + Entity::TileMode::kClamp, {}, {}); + + auto vertices = {Point(0, 0), Point(texture->GetSize().width, 0), + Point(0, texture->GetSize().height)}; + std::vector indices = {0u, 1u, 2u}; + std::vector texture_coordinates = {}; + std::vector vertex_colors = {}; + auto geometry = std::make_shared( + vertices, indices, texture_coordinates, vertex_colors, + Rect::MakeLTRB(0, 0, 1, 1), VerticesGeometry::VertexMode::kTriangleStrip); + + canvas.DrawVertices(geometry, BlendMode::kSourceOver, paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, ClearBlendWithBlur) { + Canvas canvas; + Paint white; + white.color = Color::Blue(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white); + + Paint clear; + clear.blend_mode = BlendMode::kClear; + clear.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(20), + }; + + canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, ClearBlend) { + Canvas canvas; + Paint white; + white.color = Color::Blue(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white); + + Paint clear; + clear.blend_mode = BlendMode::kClear; + + canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear); +} + +TEST_P(AiksTest, MatrixImageFilterMagnify) { + Canvas canvas; + canvas.Scale(GetContentScale()); + auto image = std::make_shared(CreateTextureForFixture("airplane.jpg")); + canvas.Translate({600, -200}); + canvas.SaveLayer({ + .image_filter = std::make_shared( + Matrix::MakeScale({2, 2, 2}), SamplerDescriptor{}), + }); + canvas.DrawImage(image, {0, 0}, + Paint{.color = Color::White().WithAlpha(0.5)}); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +// Render a white circle at the top left corner of the screen. +TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) { + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.Translate({100, 100}); + // Draw a circle in a SaveLayer at -300, but move it back on-screen with a + // +300 translation applied by a SaveLayer image filter. + canvas.SaveLayer({ + .image_filter = std::make_shared( + Matrix::MakeTranslation({300, 0}), SamplerDescriptor{}), + }); + canvas.DrawCircle({-300, 0}, 100, {.color = Color::Green()}); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +// Render a white circle at the top left corner of the screen. +TEST_P(AiksTest, + MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) { + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.Translate({100, 100}); + // Draw a circle in a SaveLayer at -300, but move it back on-screen with a + // +300 translation applied by a SaveLayer image filter. + canvas.SaveLayer({ + .image_filter = std::make_shared( + Matrix::MakeTranslation({300, 0}) * Matrix::MakeScale({2, 2, 2}), + SamplerDescriptor{}), + }); + canvas.DrawCircle({-150, 0}, 50, {.color = Color::Green()}); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +// This should be solid red, if you see a little red box this is broken. +TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) { + SetWindowSize({400, 400}); + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.DrawRect(Rect::MakeLTRB(200, 200, 300, 300), {.color = Color::Red()}); + canvas.SaveLayer({ + .image_filter = std::make_shared( + Matrix::MakeScale({2, 2, 1}), SamplerDescriptor{}), + }); + // Draw a rectangle that would fully cover the parent pass size, but not + // the subpass that it is rendered in. + canvas.DrawRect(Rect::MakeLTRB(0, 0, 400, 400), {.color = Color::Green()}); + // Draw a bigger rectangle to force the subpass to be bigger. + canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 800), {.color = Color::Red()}); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, BlurHasNoEdge) { + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.DrawPaint({}); + Paint blur = { + .color = Color::Green(), + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(47.6), + }, + }; + canvas.DrawRect(Rect{300, 300, 200, 200}, blur); +} + +TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) { + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.DrawPaint(Paint{.color = Color::Red()}); + canvas.ClipRect({100, 100, 200, 200}); + canvas.SaveLayer(Paint{.color = Color::Blue()}); + canvas.Restore(); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, EmptySaveLayerRendersWithClear) { + Canvas canvas; + canvas.Scale(GetContentScale()); + auto image = std::make_shared(CreateTextureForFixture("airplane.jpg")); + canvas.DrawImage(image, {10, 10}, {}); + canvas.ClipRect({100, 100, 200, 200}); + canvas.SaveLayer(Paint{.blend_mode = BlendMode::kClear}); + canvas.Restore(); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f50dd36af7ad9..2308d5385fb4a 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -4,11 +4,11 @@ #include "impeller/aiks/canvas.h" -#include #include #include #include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" #include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/entity/contents/atlas_contents.h" @@ -62,9 +62,9 @@ void Canvas::Save(bool create_subpass, auto entry = CanvasStackEntry{}; entry.xformation = xformation_stack_.back().xformation; entry.cull_rect = xformation_stack_.back().cull_rect; - entry.stencil_depth = xformation_stack_.back().stencil_depth; + entry.clip_depth = xformation_stack_.back().clip_depth; if (create_subpass) { - entry.is_subpass = true; + entry.rendering_mode = Entity::RenderingMode::kSubpass; auto subpass = std::make_unique(); subpass->SetEnableOffscreenCheckerboard( debug_options.offscreen_texture_checkerboard); @@ -72,10 +72,10 @@ void Canvas::Save(bool create_subpass, EntityPass::BackdropFilterProc backdrop_filter_proc = [backdrop_filter = backdrop_filter->Clone()]( const FilterInput::Ref& input, const Matrix& effect_transform, - bool is_subpass) { + Entity::RenderingMode rendering_mode) { auto filter = backdrop_filter->WrapInput(input); filter->SetEffectTransform(effect_transform); - filter->SetIsForSubpass(is_subpass); + filter->SetRenderingMode(rendering_mode); return filter; }; subpass->SetBackdropFilter(backdrop_filter_proc); @@ -83,7 +83,7 @@ void Canvas::Save(bool create_subpass, subpass->SetBlendMode(blend_mode); current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass)); current_pass_->SetTransformation(xformation_stack_.back().xformation); - current_pass_->SetStencilDepth(xformation_stack_.back().stencil_depth); + current_pass_->SetClipDepth(xformation_stack_.back().clip_depth); } xformation_stack_.emplace_back(entry); } @@ -93,7 +93,8 @@ bool Canvas::Restore() { if (xformation_stack_.size() == 1) { return false; } - if (xformation_stack_.back().is_subpass) { + if (xformation_stack_.back().rendering_mode == + Entity::RenderingMode::kSubpass) { current_pass_ = GetCurrentPass().GetSuperpass(); FML_DCHECK(current_pass_); } @@ -172,7 +173,7 @@ void Canvas::RestoreToCount(size_t count) { void Canvas::DrawPath(const Path& path, const Paint& paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters(paint.CreateContentsForEntity(path))); @@ -182,7 +183,7 @@ void Canvas::DrawPath(const Path& path, const Paint& paint) { void Canvas::DrawPaint(const Paint& paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.CreateContentsForEntity({}, true)); @@ -216,7 +217,7 @@ bool Canvas::AttemptDrawBlurredRRect(const Rect& rect, Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(new_paint.blend_mode); entity.SetContents(new_paint.WithFilters(std::move(contents))); @@ -237,7 +238,7 @@ void Canvas::DrawRect(Rect rect, const Paint& paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters( paint.CreateContentsForGeometry(Geometry::MakeRect(rect)))); @@ -252,11 +253,12 @@ void Canvas::DrawRRect(Rect rect, Scalar corner_radius, const Paint& paint) { auto path = PathBuilder{} .SetConvexity(Convexity::kConvex) .AddRoundedRect(rect, corner_radius) + .SetBounds(rect) .TakePath(); if (paint.style == Paint::Style::kFill) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters( paint.CreateContentsForGeometry(Geometry::MakeFillPath(path)))); @@ -273,10 +275,13 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) { paint)) { return; } - auto circle_path = PathBuilder{} - .AddCircle(center, radius) - .SetConvexity(Convexity::kConvex) - .TakePath(); + auto circle_path = + PathBuilder{} + .AddCircle(center, radius) + .SetConvexity(Convexity::kConvex) + .SetBounds(Rect::MakeLTRB(center.x - radius, center.y - radius, + center.x + radius, center.y + radius)) + .TakePath(); DrawPath(circle_path, paint); } @@ -317,6 +322,7 @@ void Canvas::ClipRRect(const Rect& rect, auto path = PathBuilder{} .SetConvexity(Convexity::kConvex) .AddRoundedRect(rect, corner_radius) + .SetBounds(rect) .TakePath(); std::optional inner_rect = (corner_radius * 2 < rect.size.width && @@ -370,11 +376,11 @@ void Canvas::ClipGeometry(std::unique_ptr geometry, Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetContents(std::move(contents)); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); GetCurrentPass().AddEntity(entity); - ++xformation_stack_.back().stencil_depth; + ++xformation_stack_.back().clip_depth; xformation_stack_.back().contains_clips = true; } @@ -409,7 +415,7 @@ void Canvas::RestoreClip() { // This path is empty because ClipRestoreContents just generates a quad that // takes up the full render target. entity.SetContents(std::make_shared()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); GetCurrentPass().AddEntity(entity); } @@ -424,7 +430,7 @@ void Canvas::DrawPoints(std::vector points, Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters(paint.CreateContentsForGeometry( Geometry::MakePointField(std::move(points), radius, @@ -443,15 +449,15 @@ void Canvas::DrawPicture(const Picture& picture) { pass->IterateAllElements([&](auto& element) -> bool { if (auto entity = std::get_if(&element)) { - entity->IncrementStencilDepth(GetStencilDepth()); + entity->IncrementStencilDepth(GetClipDepth()); entity->SetTransformation(GetCurrentTransformation() * entity->GetTransformation()); return true; } if (auto subpass = std::get_if>(&element)) { - subpass->get()->SetStencilDepth(subpass->get()->GetStencilDepth() + - GetStencilDepth()); + subpass->get()->SetClipDepth(subpass->get()->GetClipDepth() + + GetClipDepth()); return true; } @@ -502,7 +508,7 @@ void Canvas::DrawImageRect(const std::shared_ptr& image, Entity entity; entity.SetBlendMode(paint.blend_mode); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetContents(paint.WithFilters(contents)); entity.SetTransformation(GetCurrentTransformation()); @@ -524,13 +530,14 @@ EntityPass& Canvas::GetCurrentPass() { return *current_pass_; } -size_t Canvas::GetStencilDepth() const { - return xformation_stack_.back().stencil_depth; +size_t Canvas::GetClipDepth() const { + return xformation_stack_.back().clip_depth; } void Canvas::SaveLayer(const Paint& paint, std::optional bounds, const std::shared_ptr& backdrop_filter) { + TRACE_EVENT0("flutter", "Canvas::saveLayer"); Save(true, paint.blend_mode, backdrop_filter); auto& new_layer_pass = GetCurrentPass(); @@ -549,7 +556,7 @@ void Canvas::DrawTextFrame(const std::shared_ptr& text_frame, Point position, const Paint& paint) { Entity entity; - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); auto text_contents = std::make_shared(); @@ -600,7 +607,7 @@ void Canvas::DrawVertices(const std::shared_ptr& vertices, Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); // If there are no vertex color or texture coordinates. Or if there @@ -673,7 +680,7 @@ void Canvas::DrawAtlas(const std::shared_ptr& atlas, Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetStencilDepth(GetStencilDepth()); + entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters(contents)); diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 0ea187c39a5ad..22c154f3cb3e0 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -34,8 +34,8 @@ struct CanvasStackEntry { Matrix xformation; // |cull_rect| is conservative screen-space bounds of the clipped output area std::optional cull_rect; - size_t stencil_depth = 0u; - bool is_subpass = false; + size_t clip_depth = 0u; + Entity::RenderingMode rendering_mode = Entity::RenderingMode::kDirect; bool contains_clips = false; }; @@ -171,7 +171,7 @@ class Canvas { EntityPass& GetCurrentPass(); - size_t GetStencilDepth() const; + size_t GetClipDepth() const; void ClipGeometry(std::unique_ptr geometry, Entity::ClipOperation clip_op); diff --git a/impeller/aiks/canvas_recorder.h b/impeller/aiks/canvas_recorder.h new file mode 100644 index 0000000000000..62711c13fcc92 --- /dev/null +++ b/impeller/aiks/canvas_recorder.h @@ -0,0 +1,288 @@ +// 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. + +#pragma once + +#include + +#include "impeller/aiks/canvas.h" + +#define FLT_CANVAS_RECORDER_OP_ARG(name) CanvasRecorderOp::name, &Canvas::name + +namespace impeller { + +/// TODO(tbd): These are very similar to `flutter::DisplayListOpType`. When +/// golden tests can be written at a higher level, migrate these to +/// flutter::DisplayListOpType. +enum CanvasRecorderOp : uint16_t { + New, + Save, + SaveLayer, + Restore, + RestoreToCount, + ResetTransform, + Transform, + Concat, + PreConcat, + Translate, + Scale2, + Scale3, + Skew, + Rotate, + DrawPath, + DrawPaint, + DrawRect, + DrawRRect, + DrawCircle, + DrawPoints, + DrawImage, + DrawImageRect, + ClipPath, + ClipRect, + ClipRRect, + DrawPicture, + DrawTextFrame, + DrawVertices, + DrawAtlas, +}; + +// Canvas recorder should only be used when IMPELLER_TRACE_CANVAS is defined +// (never in production code). +#ifdef IMPELLER_TRACE_CANVAS +/// Static polymorphic replacement for impeller::Canvas that records methods +/// called on an impeller::Canvas and forwards it to a real instance. +/// TODO(https://github.com/flutter/flutter/issues/135718): Move this recorder +/// to the DisplayList level when golden tests can be written at the ui.Canvas +/// layer. +template +class CanvasRecorder { + public: + CanvasRecorder() : canvas_() { serializer_.Write(CanvasRecorderOp::New); } + + explicit CanvasRecorder(Rect cull_rect) : canvas_(cull_rect) { + serializer_.Write(CanvasRecorderOp::New); + } + + explicit CanvasRecorder(IRect cull_rect) : canvas_(cull_rect) { + serializer_.Write(CanvasRecorderOp::New); + } + + ~CanvasRecorder() {} + + const Serializer& GetSerializer() const { return serializer_; } + + template + ReturnType ExecuteAndSerialize(CanvasRecorderOp op, + ReturnType (Canvas::*canvasMethod)()) { + serializer_.Write(op); + return (canvas_.*canvasMethod)(); + } + + template + auto ExecuteAndSerialize(CanvasRecorderOp op, + FuncType canvasMethod, + Args&&... args) + -> decltype((std::declval().* + canvasMethod)(std::forward(args)...)) { + // Serialize each argument + (serializer_.Write(std::forward(args)), ...); + serializer_.Write(op); + return (canvas_.*canvasMethod)(std::forward(args)...); + } + + ////////////////////////////////////////////////////////////////////////////// + // Canvas Static Polymorphism //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + void Save() { + return ExecuteAndSerialize(CanvasRecorderOp::Save, &Canvas::Save); + } + + void SaveLayer( + const Paint& paint, + std::optional bounds = std::nullopt, + const std::shared_ptr& backdrop_filter = nullptr) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(SaveLayer), paint, + bounds, backdrop_filter); + } + + bool Restore() { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Restore)); + } + + size_t GetSaveCount() const { return canvas_.GetSaveCount(); } + + void RestoreToCount(size_t count) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(RestoreToCount), + count); + } + + const Matrix& GetCurrentTransformation() const { + return canvas_.GetCurrentTransformation(); + } + + const std::optional GetCurrentLocalCullingBounds() const { + return canvas_.GetCurrentLocalCullingBounds(); + } + + void ResetTransform() { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ResetTransform)); + } + + void Transform(const Matrix& xformation) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Transform), + xformation); + } + + void Concat(const Matrix& xformation) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Concat), xformation); + } + + void PreConcat(const Matrix& xformation) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(PreConcat), + xformation); + } + + void Translate(const Vector3& offset) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Translate), offset); + } + + void Scale(const Vector2& scale) { + return ExecuteAndSerialize( + CanvasRecorderOp::Scale2, + static_cast(&Canvas::Scale), scale); + } + + void Scale(const Vector3& scale) { + return ExecuteAndSerialize( + CanvasRecorderOp::Scale3, + static_cast(&Canvas::Scale), scale); + } + + void Skew(Scalar sx, Scalar sy) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Skew), sx, sy); + } + + void Rotate(Radians radians) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Rotate), radians); + } + + void DrawPath(const Path& path, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPath), path, + paint); + } + + void DrawPaint(const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPaint), paint); + } + + void DrawRect(Rect rect, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawRect), rect, + paint); + } + + void DrawRRect(Rect rect, Scalar corner_radius, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawRRect), rect, + corner_radius, paint); + } + + void DrawCircle(Point center, Scalar radius, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawCircle), center, + radius, paint); + } + + void DrawPoints(std::vector points, + Scalar radius, + const Paint& paint, + PointStyle point_style) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPoints), points, + radius, paint, point_style); + } + + void DrawImage(const std::shared_ptr& image, + Point offset, + const Paint& paint, + SamplerDescriptor sampler = {}) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawImage), image, + offset, paint, sampler); + } + + void DrawImageRect(const std::shared_ptr& image, + Rect source, + Rect dest, + const Paint& paint, + SamplerDescriptor sampler = {}) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawImageRect), image, + source, dest, paint, sampler); + } + + void ClipPath( + const Path& path, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ClipPath), path, + clip_op); + } + + void ClipRect( + const Rect& rect, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ClipRect), rect, + clip_op); + } + + void ClipRRect( + const Rect& rect, + Scalar corner_radius, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ClipRRect), rect, + corner_radius, clip_op); + } + + void DrawPicture(const Picture& picture) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPicture), + picture); + } + + void DrawTextFrame(const std::shared_ptr& text_frame, + Point position, + const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawTextFrame), + text_frame, position, paint); + } + + void DrawVertices(const std::shared_ptr& vertices, + BlendMode blend_mode, + const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawVertices), + vertices, blend_mode, paint); + } + + void DrawAtlas(const std::shared_ptr& atlas, + std::vector transforms, + std::vector texture_coordinates, + std::vector colors, + BlendMode blend_mode, + SamplerDescriptor sampler, + std::optional cull_rect, + const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawAtlas), // + atlas, // + transforms, // + texture_coordinates, // + colors, // + blend_mode, // + sampler, // + cull_rect, // + paint); + } + + Picture EndRecordingAsPicture() { return canvas_.EndRecordingAsPicture(); } + + private: + Canvas canvas_; + Serializer serializer_; +}; +#endif + +} // namespace impeller diff --git a/impeller/aiks/canvas_recorder_unittests.cc b/impeller/aiks/canvas_recorder_unittests.cc new file mode 100644 index 0000000000000..041fc185dac23 --- /dev/null +++ b/impeller/aiks/canvas_recorder_unittests.cc @@ -0,0 +1,236 @@ +// 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/testing/testing.h" +#include "impeller/aiks/canvas_recorder.h" +namespace impeller { +namespace testing { + +namespace { +class Serializer { + public: + void Write(CanvasRecorderOp op) { last_op_ = op; } + + void Write(const Paint& paint) {} + + void Write(const std::optional optional_rect) {} + + void Write(const std::shared_ptr& image_filter) {} + + void Write(size_t size) {} + + void Write(const Matrix& matrix) {} + + void Write(const Vector3& vec3) {} + + void Write(const Vector2& vec2) {} + + void Write(const Radians& vec2) {} + + void Write(const Path& path) {} + + void Write(const std::vector& points) {} + + void Write(const PointStyle& point_style) {} + + void Write(const std::shared_ptr& image) {} + + void Write(const SamplerDescriptor& sampler) {} + + void Write(const Entity::ClipOperation& clip_op) {} + + void Write(const Picture& clip_op) {} + + void Write(const std::shared_ptr& text_frame) {} + + void Write(const std::shared_ptr& vertices) {} + + void Write(const BlendMode& blend_mode) {} + + void Write(const std::vector& matrices) {} + + void Write(const std::vector& matrices) {} + + void Write(const std::vector& matrices) {} + + CanvasRecorderOp last_op_; +}; +} // namespace + +TEST(CanvasRecorder, Save) { + CanvasRecorder recorder; + recorder.Save(); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Save); +} + +TEST(CanvasRecorder, SaveLayer) { + CanvasRecorder recorder; + Paint paint; + recorder.SaveLayer(paint); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::SaveLayer); +} + +TEST(CanvasRecorder, Restore) { + CanvasRecorder recorder; + recorder.Restore(); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Restore); +} + +TEST(CanvasRecorder, RestoreToCount) { + CanvasRecorder recorder; + recorder.Save(); + recorder.RestoreToCount(0); + ASSERT_EQ(recorder.GetSerializer().last_op_, + CanvasRecorderOp::RestoreToCount); +} + +TEST(CanvasRecorder, ResetTransform) { + CanvasRecorder recorder; + recorder.ResetTransform(); + ASSERT_EQ(recorder.GetSerializer().last_op_, + CanvasRecorderOp::ResetTransform); +} + +TEST(CanvasRecorder, Transform) { + CanvasRecorder recorder; + recorder.Transform(Matrix()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Transform); +} + +TEST(CanvasRecorder, Concat) { + CanvasRecorder recorder; + recorder.Concat(Matrix()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Concat); +} + +TEST(CanvasRecorder, PreConcat) { + CanvasRecorder recorder; + recorder.PreConcat(Matrix()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::PreConcat); +} + +TEST(CanvasRecorder, Translate) { + CanvasRecorder recorder; + recorder.Translate(Vector3()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Translate); +} + +TEST(CanvasRecorder, Scale2) { + CanvasRecorder recorder; + recorder.Scale(Vector2()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Scale2); +} + +TEST(CanvasRecorder, Scale3) { + CanvasRecorder recorder; + recorder.Scale(Vector3()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Scale3); +} + +TEST(CanvasRecorder, Skew) { + CanvasRecorder recorder; + recorder.Skew(0, 0); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Skew); +} + +TEST(CanvasRecorder, Rotate) { + CanvasRecorder recorder; + recorder.Rotate(Radians(0)); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Rotate); +} + +TEST(CanvasRecorder, DrawPath) { + CanvasRecorder recorder; + recorder.DrawPath(Path(), Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPath); +} + +TEST(CanvasRecorder, DrawPaint) { + CanvasRecorder recorder; + recorder.DrawPaint(Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPaint); +} + +TEST(CanvasRecorder, DrawRect) { + CanvasRecorder recorder; + recorder.DrawRect(Rect(), Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawRect); +} + +TEST(CanvasRecorder, DrawRRect) { + CanvasRecorder recorder; + recorder.DrawRRect(Rect(), 0, Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawRRect); +} + +TEST(CanvasRecorder, DrawCircle) { + CanvasRecorder recorder; + recorder.DrawCircle(Point(), 0, Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawCircle); +} + +TEST(CanvasRecorder, DrawPoints) { + CanvasRecorder recorder; + recorder.DrawPoints(std::vector{}, 0, Paint(), PointStyle::kRound); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPoints); +} + +TEST(CanvasRecorder, DrawImage) { + CanvasRecorder recorder; + recorder.DrawImage({}, {}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawImage); +} + +TEST(CanvasRecorder, DrawImageRect) { + CanvasRecorder recorder; + recorder.DrawImageRect({}, {}, {}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawImageRect); +} + +TEST(CanvasRecorder, ClipPath) { + CanvasRecorder recorder; + recorder.ClipPath({}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::ClipPath); +} + +TEST(CanvasRecorder, ClipRect) { + CanvasRecorder recorder; + recorder.ClipRect({}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::ClipRect); +} + +TEST(CanvasRecorder, ClipRRect) { + CanvasRecorder recorder; + recorder.ClipRRect({}, 0); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::ClipRRect); +} + +TEST(CanvasRecorder, DrawPicture) { + CanvasRecorder recorder; + recorder.DrawPicture({}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPicture); +} + +TEST(CanvasRecorder, DrawTextFrame) { + CanvasRecorder recorder; + recorder.DrawTextFrame({}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawTextFrame); +} + +TEST(CanvasRecorder, DrawVertices) { + CanvasRecorder recorder; + auto geometry = std::shared_ptr( + new VerticesGeometry({}, {}, {}, {}, {}, {})); + recorder.DrawVertices(geometry, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawVertices); +} + +TEST(CanvasRecorder, DrawAtlas) { + CanvasRecorder recorder; + recorder.DrawAtlas({}, {}, {}, {}, {}, {}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawAtlas); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/aiks/canvas_type.h b/impeller/aiks/canvas_type.h new file mode 100644 index 0000000000000..a6992163a77ba --- /dev/null +++ b/impeller/aiks/canvas_type.h @@ -0,0 +1,22 @@ +// 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. + +#pragma once + +#include "impeller/aiks/canvas.h" +#include "impeller/aiks/canvas_recorder.h" +#include "impeller/aiks/trace_serializer.h" + +namespace impeller { + +/// CanvasType defines what is the concrete type of the Canvas to be used. When +/// the recorder is enabled it will be swapped out in place of the Canvas at +/// compile-time. +#ifdef IMPELLER_TRACE_CANVAS +using CanvasType = CanvasRecorder; +#else +using CanvasType = Canvas; +#endif + +} // namespace impeller diff --git a/impeller/aiks/color_filter.cc b/impeller/aiks/color_filter.cc index 72868a0c0d10f..b5498a195408b 100644 --- a/impeller/aiks/color_filter.cc +++ b/impeller/aiks/color_filter.cc @@ -3,7 +3,10 @@ // found in the LICENSE file. #include "impeller/aiks/color_filter.h" + +#include #include "impeller/entity/contents/filters/color_filter_contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/geometry/color.h" @@ -34,6 +37,12 @@ std::shared_ptr ColorFilter::MakeLinearToSrgb() { return std::make_shared(); } +std::shared_ptr ColorFilter::MakeComposed( + const std::shared_ptr& outer, + const std::shared_ptr& inner) { + return std::make_shared(outer, inner); +} + /******************************************************************************* ******* BlendColorFilter ******************************************************************************/ @@ -45,7 +54,7 @@ BlendColorFilter::~BlendColorFilter() = default; std::shared_ptr BlendColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { auto filter = ColorFilterContents::MakeBlend(blend_mode_, {std::move(input)}, color_); filter->SetAbsorbOpacity(absorb_opacity); @@ -73,7 +82,7 @@ MatrixColorFilter::~MatrixColorFilter() = default; std::shared_ptr MatrixColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { auto filter = ColorFilterContents::MakeColorMatrix({std::move(input)}, color_matrix_); filter->SetAbsorbOpacity(absorb_opacity); @@ -101,7 +110,7 @@ SrgbToLinearColorFilter::~SrgbToLinearColorFilter() = default; std::shared_ptr SrgbToLinearColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)}); filter->SetAbsorbOpacity(absorb_opacity); return filter; @@ -127,7 +136,7 @@ LinearToSrgbColorFilter::~LinearToSrgbColorFilter() = default; std::shared_ptr LinearToSrgbColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)}); filter->SetAbsorbOpacity(absorb_opacity); return filter; @@ -142,4 +151,40 @@ std::shared_ptr LinearToSrgbColorFilter::Clone() const { return std::make_shared(*this); } +/******************************************************************************* + ******* ComposedColorFilter + ******************************************************************************/ + +ComposedColorFilter::ComposedColorFilter( + const std::shared_ptr& outer, + const std::shared_ptr& inner) + : outer_(outer), inner_(inner) {} + +ComposedColorFilter::~ComposedColorFilter() = default; + +std::shared_ptr +ComposedColorFilter::WrapWithGPUColorFilter( + std::shared_ptr input, + ColorFilterContents::AbsorbOpacity absorb_opacity) const { + std::shared_ptr inner = inner_->WrapWithGPUColorFilter( + input, ColorFilterContents::AbsorbOpacity::kNo); + return outer_->WrapWithGPUColorFilter(FilterInput::Make(inner), + absorb_opacity); +} + +// |ColorFilter| +ColorFilter::ColorFilterProc ComposedColorFilter::GetCPUColorFilterProc() + const { + return [inner = inner_, outer = outer_](Color color) { + auto inner_proc = inner->GetCPUColorFilterProc(); + auto outer_proc = outer->GetCPUColorFilterProc(); + return outer_proc(inner_proc(color)); + }; +} + +// |ColorFilter| +std::shared_ptr ComposedColorFilter::Clone() const { + return std::make_shared(outer_, inner_); +} + } // namespace impeller diff --git a/impeller/aiks/color_filter.h b/impeller/aiks/color_filter.h index 234cbc63c160f..84dfbbd19b40d 100644 --- a/impeller/aiks/color_filter.h +++ b/impeller/aiks/color_filter.h @@ -34,6 +34,10 @@ class ColorFilter { static std::shared_ptr MakeLinearToSrgb(); + static std::shared_ptr MakeComposed( + const std::shared_ptr& outer, + const std::shared_ptr& inner); + /// @brief Wraps the given filter input with a GPU-based filter that will /// perform the color operation. The given input will first be /// rendered to a texture and then filtered. @@ -43,7 +47,7 @@ class ColorFilter { /// treated as color information. virtual std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const = 0; + ColorFilterContents::AbsorbOpacity absorb_opacity) const = 0; /// @brief Returns a function that can be used to filter unpremultiplied /// Impeller Colors on the CPU. @@ -65,7 +69,7 @@ class BlendColorFilter final : public ColorFilter { // |ColorFilter| std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const override; + ColorFilterContents::AbsorbOpacity absorb_opacity) const override; // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; @@ -91,7 +95,7 @@ class MatrixColorFilter final : public ColorFilter { // |ColorFilter| std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const override; + ColorFilterContents::AbsorbOpacity absorb_opacity) const override; // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; @@ -116,7 +120,7 @@ class SrgbToLinearColorFilter final : public ColorFilter { // |ColorFilter| std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const override; + ColorFilterContents::AbsorbOpacity absorb_opacity) const override; // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; @@ -138,7 +142,7 @@ class LinearToSrgbColorFilter final : public ColorFilter { // |ColorFilter| std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, - bool absorb_opacity) const override; + ColorFilterContents::AbsorbOpacity absorb_opacity) const override; // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; @@ -147,4 +151,28 @@ class LinearToSrgbColorFilter final : public ColorFilter { std::shared_ptr Clone() const override; }; +/// @brief Applies color filters as f(g(x)), where x is the input color. +class ComposedColorFilter final : public ColorFilter { + public: + ComposedColorFilter(const std::shared_ptr& outer, + const std::shared_ptr& inner); + + ~ComposedColorFilter() override; + + // |ColorFilter| + std::shared_ptr WrapWithGPUColorFilter( + std::shared_ptr input, + ColorFilterContents::AbsorbOpacity absorb_opacity) const override; + + // |ColorFilter| + ColorFilterProc GetCPUColorFilterProc() const override; + + // |ColorFilter| + std::shared_ptr Clone() const override; + + private: + std::shared_ptr outer_; + std::shared_ptr inner_; +}; + } // namespace impeller diff --git a/impeller/aiks/color_source.cc b/impeller/aiks/color_source.cc index 3329bcb4b832e..da3191f2d63c4 100644 --- a/impeller/aiks/color_source.cc +++ b/impeller/aiks/color_source.cc @@ -10,6 +10,7 @@ #include "impeller/aiks/paint.h" #include "impeller/core/sampler_descriptor.h" #include "impeller/entity/contents/conical_gradient_contents.h" +#include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/radial_gradient_contents.h" #include "impeller/entity/contents/runtime_effect_contents.h" @@ -182,8 +183,8 @@ ColorSource ColorSource::MakeImage(std::shared_ptr texture, if (paint.color_filter) { TiledTextureContents::ColorFilterProc filter_proc = [color_filter = paint.color_filter](FilterInput::Ref input) { - return color_filter->WrapWithGPUColorFilter(std::move(input), - false); + return color_filter->WrapWithGPUColorFilter( + std::move(input), ColorFilterContents::AbsorbOpacity::kNo); }; contents->SetColorFilter(filter_proc); } diff --git a/impeller/aiks/image_filter.cc b/impeller/aiks/image_filter.cc index 6420e8c293df7..7eba48713c8a3 100644 --- a/impeller/aiks/image_filter.cc +++ b/impeller/aiks/image_filter.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "impeller/aiks/image_filter.h" +#include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" @@ -175,7 +176,8 @@ ColorImageFilter::~ColorImageFilter() = default; std::shared_ptr ColorImageFilter::WrapInput( const FilterInput::Ref& input) const { - return color_filter_->WrapWithGPUColorFilter(input, false); + return color_filter_->WrapWithGPUColorFilter( + input, ColorFilterContents::AbsorbOpacity::kNo); } std::shared_ptr ColorImageFilter::Clone() const { diff --git a/impeller/aiks/image_filter.h b/impeller/aiks/image_filter.h index 0c5bdd3889717..e3e6908df515c 100644 --- a/impeller/aiks/image_filter.h +++ b/impeller/aiks/image_filter.h @@ -15,6 +15,25 @@ namespace impeller { struct Paint; +class LocalMatrixImageFilter; +class BlurImageFilter; +class DilateImageFilter; +class ErodeImageFilter; +class MatrixImageFilter; +class ComposeImageFilter; +class ColorImageFilter; + +class ImageFilterVisitor { + public: + virtual void Visit(const BlurImageFilter& filter) = 0; + virtual void Visit(const LocalMatrixImageFilter& filter) = 0; + virtual void Visit(const DilateImageFilter& filter) = 0; + virtual void Visit(const ErodeImageFilter& filter) = 0; + virtual void Visit(const MatrixImageFilter& filter) = 0; + virtual void Visit(const ComposeImageFilter& filter) = 0; + virtual void Visit(const ColorImageFilter& filter) = 0; +}; + /******************************************************************************* ******* ImageFilter ******************************************************************************/ @@ -65,6 +84,8 @@ class ImageFilter { const FilterInput::Ref& input) const = 0; virtual std::shared_ptr Clone() const = 0; + + virtual void Visit(ImageFilterVisitor& visitor) = 0; }; /******************************************************************************* @@ -87,6 +108,9 @@ class BlurImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Sigma sigma_x_; Sigma sigma_y_; @@ -111,6 +135,9 @@ class DilateImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Radius radius_x_; Radius radius_y_; @@ -133,6 +160,9 @@ class ErodeImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Radius radius_x_; Radius radius_y_; @@ -155,6 +185,11 @@ class MatrixImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + + const Matrix& GetMatrix() const { return matrix_; } + private: Matrix matrix_; SamplerDescriptor sampler_descriptor_; @@ -177,6 +212,9 @@ class ComposeImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: std::shared_ptr inner_; std::shared_ptr outer_; @@ -199,6 +237,9 @@ class ColorImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: std::shared_ptr color_filter_; }; @@ -221,6 +262,9 @@ class LocalMatrixImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Matrix matrix_; std::shared_ptr internal_filter_; diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index a7bb6d00d3152..332973e3c2a99 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -14,6 +14,18 @@ namespace impeller { +/// A color matrix which inverts colors. +// clang-format off +constexpr ColorMatrix kColorInversion = { + .array = { + -1.0, 0, 0, 1.0, 0, // + 0, -1.0, 0, 1.0, 0, // + 0, 0, -1.0, 1.0, 0, // + 1.0, 1.0, 1.0, 1.0, 0 // + } +}; +// clang-format on + std::shared_ptr Paint::CreateContentsForEntity(const Path& path, bool cover) const { std::unique_ptr geometry; @@ -38,6 +50,7 @@ std::shared_ptr Paint::CreateContentsForGeometry( // Attempt to apply the color filter on the CPU first. // Note: This is not just an optimization; some color sources rely on // CPU-applied color filters to behave properly. + auto color_filter = GetColorFilter(); bool needs_color_filter = !!color_filter; if (color_filter && contents->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) { @@ -58,17 +71,24 @@ std::shared_ptr Paint::CreateContentsForGeometry( std::shared_ptr Paint::WithFilters( std::shared_ptr input) const { - input = WithColorFilter(input, /*absorb_opacity=*/true); - input = WithInvertFilter(input); - input = WithImageFilter(input, Matrix(), /*is_subpass=*/false); + input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes); + auto image_filter = + WithImageFilter(input, Matrix(), Entity::RenderingMode::kDirect); + if (image_filter) { + input = image_filter; + } return input; } std::shared_ptr Paint::WithFiltersForSubpassTarget( std::shared_ptr input, const Matrix& effect_transform) const { - input = WithImageFilter(input, effect_transform, /*is_subpass=*/true); - input = WithColorFilter(input, /*absorb_opacity=*/true); + auto image_filter = + WithImageFilter(input, effect_transform, Entity::RenderingMode::kSubpass); + if (image_filter) { + input = image_filter; + } + input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes); return input; } @@ -81,28 +101,29 @@ std::shared_ptr Paint::WithMaskBlur(std::shared_ptr input, return input; } -std::shared_ptr Paint::WithImageFilter( - std::shared_ptr input, +std::shared_ptr Paint::WithImageFilter( + const FilterInput::Variant& input, const Matrix& effect_transform, - bool is_subpass) const { + Entity::RenderingMode rendering_mode) const { if (!image_filter) { - return input; + return nullptr; } auto filter = image_filter->WrapInput(FilterInput::Make(input)); - filter->SetIsForSubpass(is_subpass); + filter->SetRenderingMode(rendering_mode); filter->SetEffectTransform(effect_transform); return filter; } std::shared_ptr Paint::WithColorFilter( std::shared_ptr input, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { // Image input types will directly set their color filter, // if any. See `TiledTextureContents.SetColorFilter`. if (color_source.GetType() == ColorSource::Type::kImage) { return input; } + auto color_filter = GetColorFilter(); if (!color_filter) { return input; } @@ -113,33 +134,10 @@ std::shared_ptr Paint::WithColorFilter( if (input->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) { return input; } - return color_filter->WrapWithGPUColorFilter(FilterInput::Make(input), absorb_opacity); } -/// A color matrix which inverts colors. -// clang-format off -constexpr ColorMatrix kColorInversion = { - .array = { - -1.0, 0, 0, 1.0, 0, // - 0, -1.0, 0, 1.0, 0, // - 0, 0, -1.0, 1.0, 0, // - 1.0, 1.0, 1.0, 1.0, 0 // - } -}; -// clang-format on - -std::shared_ptr Paint::WithInvertFilter( - std::shared_ptr input) const { - if (!invert_colors) { - return input; - } - - return ColorFilterContents::MakeColorMatrix( - {FilterInput::Make(std::move(input))}, kColorInversion); -} - std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( std::shared_ptr color_source_contents, const std::shared_ptr& color_filter) const { @@ -179,7 +177,8 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( if (color_filter) { color_contents = color_filter->WrapWithGPUColorFilter( - FilterInput::Make(color_source_contents), true); + FilterInput::Make(color_source_contents), + ColorFilterContents::AbsorbOpacity::kYes); } /// 5. Composite the color source with the blurred mask. @@ -199,8 +198,22 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style); } +std::shared_ptr Paint::GetColorFilter() const { + if (invert_colors && color_filter) { + auto filter = ColorFilter::MakeMatrix(kColorInversion); + return ColorFilter::MakeComposed(filter, color_filter); + } + if (invert_colors) { + return ColorFilter::MakeMatrix(kColorInversion); + } + if (color_filter) { + return color_filter; + } + return nullptr; +} + bool Paint::HasColorFilter() const { - return !!color_filter; + return !!color_filter || invert_colors; } } // namespace impeller diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index d77a298d5bd21..bf96842b783c9 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -26,7 +26,7 @@ struct Paint { using ImageFilterProc = std::function( FilterInput::Ref, const Matrix& effect_transform, - bool is_subpass)>; + Entity::RenderingMode rendering_mode)>; using MaskFilterProc = std::function( FilterInput::Ref, bool is_solid_color, @@ -67,6 +67,8 @@ struct Paint { std::shared_ptr color_filter; std::optional mask_blur_descriptor; + std::shared_ptr GetColorFilter() const; + /// @brief Wrap this paint's configured filters to the given contents. /// @param[in] input The contents to wrap with paint's filters. /// @return The filter-wrapped contents. If there are no filters that need @@ -98,16 +100,16 @@ struct Paint { std::shared_ptr WithMaskBlur(std::shared_ptr input, bool is_solid_color) const; - private: - std::shared_ptr WithImageFilter(std::shared_ptr input, - const Matrix& effect_transform, - bool is_subpass) const; - - std::shared_ptr WithColorFilter(std::shared_ptr input, - bool absorb_opacity = false) const; + std::shared_ptr WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform, + Entity::RenderingMode rendering_mode) const; - std::shared_ptr WithInvertFilter( - std::shared_ptr input) const; + private: + std::shared_ptr WithColorFilter( + std::shared_ptr input, + ColorFilterContents::AbsorbOpacity absorb_opacity = + ColorFilterContents::AbsorbOpacity::kNo) const; }; } // namespace impeller diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index 382ca3457cd93..f9a081a0b55cf 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -47,6 +47,14 @@ std::shared_ptr PaintPassDelegate::CreateContentsForSubpassTarget( effect_transform); } +// |EntityPassDelgate| +std::shared_ptr PaintPassDelegate::WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const { + return paint_.WithImageFilter(input, effect_transform, + Entity::RenderingMode::kSubpass); +} + /// OpacityPeepholePassDelegate /// ---------------------------------------------- @@ -140,4 +148,12 @@ OpacityPeepholePassDelegate::CreateContentsForSubpassTarget( effect_transform); } +// |EntityPassDelgate| +std::shared_ptr OpacityPeepholePassDelegate::WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const { + return paint_.WithImageFilter(input, effect_transform, + Entity::RenderingMode::kSubpass); +} + } // namespace impeller diff --git a/impeller/aiks/paint_pass_delegate.h b/impeller/aiks/paint_pass_delegate.h index 967b59ea81e63..17e87b20bd366 100644 --- a/impeller/aiks/paint_pass_delegate.h +++ b/impeller/aiks/paint_pass_delegate.h @@ -32,6 +32,11 @@ class PaintPassDelegate final : public EntityPassDelegate { std::shared_ptr target, const Matrix& effect_transform) override; + // |EntityPassDelgate| + std::shared_ptr WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const override; + private: const Paint paint_; @@ -61,6 +66,11 @@ class OpacityPeepholePassDelegate final : public EntityPassDelegate { std::shared_ptr target, const Matrix& effect_transform) override; + // |EntityPassDelgate| + std::shared_ptr WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const override; + private: const Paint paint_; diff --git a/impeller/aiks/picture.cc b/impeller/aiks/picture.cc index d7a6d4085811f..faa5c34e377d1 100644 --- a/impeller/aiks/picture.cc +++ b/impeller/aiks/picture.cc @@ -66,23 +66,17 @@ std::shared_ptr Picture::RenderToTexture( size, // size "Picture Snapshot MSAA", // label RenderTarget:: - kDefaultColorAttachmentConfigMSAA // color_attachment_config -#ifndef FML_OS_ANDROID // Reduce PSO variants for Vulkan. - , - std::nullopt // stencil_attachment_config -#endif // FML_OS_ANDROID + kDefaultColorAttachmentConfigMSAA, // color_attachment_config + std::nullopt // stencil_attachment_config ); } else { target = RenderTarget::CreateOffscreen( - *impeller_context, // context - render_target_allocator, // allocator - size, // size - "Picture Snapshot", // label - RenderTarget::kDefaultColorAttachmentConfig // color_attachment_config -#ifndef FML_OS_ANDROID // Reduce PSO variants for Vulkan. - , + *impeller_context, // context + render_target_allocator, // allocator + size, // size + "Picture Snapshot", // label + RenderTarget::kDefaultColorAttachmentConfig, // color_attachment_config std::nullopt // stencil_attachment_config -#endif // FML_OS_ANDROID ); } if (!target.IsValid()) { diff --git a/impeller/aiks/trace_serializer.cc b/impeller/aiks/trace_serializer.cc new file mode 100644 index 0000000000000..c70fd23f329ba --- /dev/null +++ b/impeller/aiks/trace_serializer.cc @@ -0,0 +1,258 @@ +// 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/aiks/trace_serializer.h" +#include "flutter/fml/logging.h" + +namespace impeller { + +namespace { + +class ImageFilterTraceVisitor : public ImageFilterVisitor { + public: + explicit ImageFilterTraceVisitor(std::ostream& os) : os_(os) {} + void Visit(const BlurImageFilter& filter) override { + os_ << "BlurImageFilter"; + } + void Visit(const LocalMatrixImageFilter& filter) override { + os_ << "LocalMatrixImageFilter"; + } + void Visit(const DilateImageFilter& filter) override { + os_ << "DilateImageFilter"; + } + void Visit(const ErodeImageFilter& filter) override { + os_ << "ErodeImageFilter"; + } + void Visit(const MatrixImageFilter& filter) override { + os_ << "{MatrixImageFilter matrix: " << filter.GetMatrix() << "}"; + } + void Visit(const ComposeImageFilter& filter) override { + os_ << "ComposeImageFilter"; + } + void Visit(const ColorImageFilter& filter) override { + os_ << "ColorImageFilter"; + } + + private: + std::ostream& os_; +}; + +std::ostream& operator<<(std::ostream& os, + const std::shared_ptr& image_filter) { + if (image_filter) { + os << "["; + ImageFilterTraceVisitor visitor(os); + image_filter->Visit(visitor); + os << "]"; + } else { + os << "[None]"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const ColorSource& color_source) { + os << "{ type: "; + switch (color_source.GetType()) { + case ColorSource::Type::kColor: + os << "kColor"; + break; + case ColorSource::Type::kImage: + os << "kImage"; + break; + case ColorSource::Type::kLinearGradient: + os << "kLinearGradient"; + break; + case ColorSource::Type::kRadialGradient: + os << "kRadialGradient"; + break; + case ColorSource::Type::kConicalGradient: + os << "kConicalGradient"; + break; + case ColorSource::Type::kSweepGradient: + os << "kSweepGradient"; + break; + case ColorSource::Type::kRuntimeEffect: + os << "kRuntimeEffect"; + break; + case ColorSource::Type::kScene: + os << "kScene"; + break; + } + os << " }"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const Paint& paint) { + os << "{" << std::endl; + os << " color: [" << paint.color << "]" << std::endl; + os << " color_source:" + << "[" << paint.color_source << "]" << std::endl; + os << " dither: [" << paint.dither << "]" << std::endl; + os << " stroke_width: [" << paint.stroke_width << "]" << std::endl; + os << " stroke_cap: " + << "[Paint::Cap]" << std::endl; + os << " stroke_join: " + << "[Paint::Join]" << std::endl; + os << " stroke_miter: [" << paint.stroke_miter << "]" << std::endl; + os << " style:" + << "[Paint::Style]" << std::endl; + os << " blend_mode: [" << BlendModeToString(paint.blend_mode) << "]" + << std::endl; + os << " invert_colors: [" << paint.invert_colors << "]" << std::endl; + os << " image_filter: " << paint.image_filter << std::endl; + os << " color_filter: " << paint.color_filter << std::endl; + os << " mask_blur_descriptor: " + << "[std::optional]" << std::endl; + os << "}"; + return os; +} +} // namespace + +#define FLT_CANVAS_RECORDER_OP_TO_STRING(name) \ + case CanvasRecorderOp::name: \ + return #name + +namespace { +std::string_view CanvasRecorderOpToString(CanvasRecorderOp op) { + switch (op) { + FLT_CANVAS_RECORDER_OP_TO_STRING(New); + FLT_CANVAS_RECORDER_OP_TO_STRING(Save); + FLT_CANVAS_RECORDER_OP_TO_STRING(SaveLayer); + FLT_CANVAS_RECORDER_OP_TO_STRING(Restore); + FLT_CANVAS_RECORDER_OP_TO_STRING(RestoreToCount); + FLT_CANVAS_RECORDER_OP_TO_STRING(ResetTransform); + FLT_CANVAS_RECORDER_OP_TO_STRING(Transform); + FLT_CANVAS_RECORDER_OP_TO_STRING(Concat); + FLT_CANVAS_RECORDER_OP_TO_STRING(PreConcat); + FLT_CANVAS_RECORDER_OP_TO_STRING(Translate); + FLT_CANVAS_RECORDER_OP_TO_STRING(Scale2); + FLT_CANVAS_RECORDER_OP_TO_STRING(Scale3); + FLT_CANVAS_RECORDER_OP_TO_STRING(Skew); + FLT_CANVAS_RECORDER_OP_TO_STRING(Rotate); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPath); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPaint); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawRRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawCircle); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPoints); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawImage); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawImageRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(ClipPath); + FLT_CANVAS_RECORDER_OP_TO_STRING(ClipRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(ClipRRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPicture); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawTextFrame); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawVertices); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawAtlas); + } +} +} // namespace + +TraceSerializer::TraceSerializer() {} + +void TraceSerializer::Write(CanvasRecorderOp op) { + if (op == CanvasRecorderOp::New) { + FML_LOG(ERROR) << "######################################################"; + } else { + FML_LOG(ERROR) << CanvasRecorderOpToString(op) << ":" << buffer_.str(); + buffer_.str(""); + buffer_.clear(); + } +} + +void TraceSerializer::Write(const Paint& paint) { + buffer_ << "[" << paint << "] "; +} + +void TraceSerializer::Write(const std::optional optional_rect) { + if (optional_rect.has_value()) { + buffer_ << "[" << optional_rect.value() << "] "; + } else { + buffer_ << "[None] "; + } +} + +void TraceSerializer::Write(const std::shared_ptr& image_filter) { + buffer_ << image_filter << " "; +} + +void TraceSerializer::Write(size_t size) { + buffer_ << "[" << size << "] "; +} + +void TraceSerializer::Write(const Matrix& matrix) { + buffer_ << "[" << matrix << "] "; +} + +void TraceSerializer::Write(const Vector3& vec3) { + buffer_ << "[" << vec3 << "] "; +} + +void TraceSerializer::Write(const Vector2& vec2) { + buffer_ << "[" << vec2 << "] "; +} + +void TraceSerializer::Write(const Radians& radians) { + buffer_ << "[" << radians.radians << "] "; +} + +void TraceSerializer::Write(const Path& path) { + buffer_ << "[Path] "; +} + +void TraceSerializer::Write(const std::vector& points) { + buffer_ << "[std::vector] "; +} + +void TraceSerializer::Write(const PointStyle& point_style) { + buffer_ << "[PointStyle] "; +} + +void TraceSerializer::Write(const std::shared_ptr& image) { + buffer_ << "[std::shared_ptr] "; +} + +void TraceSerializer::Write(const SamplerDescriptor& sampler) { + buffer_ << "[SamplerDescriptor] "; +} + +void TraceSerializer::Write(const Entity::ClipOperation& clip_op) { + switch (clip_op) { + case Entity::ClipOperation::kDifference: + buffer_ << "[kDifference] "; + break; + case Entity::ClipOperation::kIntersect: + buffer_ << "[kIntersect] "; + break; + } +} + +void TraceSerializer::Write(const Picture& clip_op) { + buffer_ << "[Picture] "; +} + +void TraceSerializer::Write(const std::shared_ptr& text_frame) { + buffer_ << "[std::shared_ptr] "; +} + +void TraceSerializer::Write(const std::shared_ptr& vertices) { + buffer_ << "[std::shared_ptr] "; +} + +void TraceSerializer::Write(const BlendMode& blend_mode) { + buffer_ << "[" << BlendModeToString(blend_mode) << "] "; +} + +void TraceSerializer::Write(const std::vector& matrices) { + buffer_ << "[std::vector] "; +} + +void TraceSerializer::Write(const std::vector& matrices) { + buffer_ << "[std::vector] "; +} + +void TraceSerializer::Write(const std::vector& matrices) { + buffer_ << "[std::vector] "; +} +} // namespace impeller diff --git a/impeller/aiks/trace_serializer.h b/impeller/aiks/trace_serializer.h new file mode 100644 index 0000000000000..9232de504a375 --- /dev/null +++ b/impeller/aiks/trace_serializer.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. + +#pragma once + +#include +#include "impeller/aiks/canvas_recorder.h" + +namespace impeller { + +class TraceSerializer { + public: + TraceSerializer(); + + void Write(CanvasRecorderOp op); + + void Write(const Paint& paint); + + void Write(const std::optional optional_rect); + + void Write(const std::shared_ptr& image_filter); + + void Write(size_t size); + + void Write(const Matrix& matrix); + + void Write(const Vector3& vec3); + + void Write(const Vector2& vec2); + + void Write(const Radians& vec2); + + void Write(const Path& path); + + void Write(const std::vector& points); + + void Write(const PointStyle& point_style); + + void Write(const std::shared_ptr& image); + + void Write(const SamplerDescriptor& sampler); + + void Write(const Entity::ClipOperation& clip_op); + + void Write(const Picture& clip_op); + + void Write(const std::shared_ptr& text_frame); + + void Write(const std::shared_ptr& vertices); + + void Write(const BlendMode& blend_mode); + + void Write(const std::vector& matrices); + + void Write(const std::vector& matrices); + + void Write(const std::vector& matrices); + + private: + std::stringstream buffer_; +}; + +} // namespace impeller diff --git a/impeller/aiks/trace_serializer_unittests.cc b/impeller/aiks/trace_serializer_unittests.cc new file mode 100644 index 0000000000000..8bfaa47629bec --- /dev/null +++ b/impeller/aiks/trace_serializer_unittests.cc @@ -0,0 +1,20 @@ +// 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 +#include "flutter/testing/testing.h" +#include "impeller/aiks/trace_serializer.h" +namespace impeller { +namespace testing { + +TEST(TraceSerializer, Save) { + CanvasRecorder recorder; + std::ostringstream ss; + fml::LogMessage::CaptureNextLog(&ss); + recorder.Save(); + ASSERT_TRUE(ss.str().size() > 0); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/base/thread.h b/impeller/base/thread.h index 9d44b7681cc93..75ab5aae9720d 100644 --- a/impeller/base/thread.h +++ b/impeller/base/thread.h @@ -53,7 +53,9 @@ class IPLR_CAPABILITY("mutex") RWMutex { class IPLR_SCOPED_CAPABILITY Lock { public: - Lock(Mutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { mutex_.Lock(); } + explicit Lock(Mutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { + mutex_.Lock(); + } ~Lock() IPLR_RELEASE() { mutex_.Unlock(); } @@ -65,7 +67,8 @@ class IPLR_SCOPED_CAPABILITY Lock { class IPLR_SCOPED_CAPABILITY ReaderLock { public: - ReaderLock(RWMutex& mutex) IPLR_ACQUIRE_SHARED(mutex) : mutex_(mutex) { + explicit ReaderLock(RWMutex& mutex) IPLR_ACQUIRE_SHARED(mutex) + : mutex_(mutex) { mutex_.LockReader(); } @@ -79,7 +82,7 @@ class IPLR_SCOPED_CAPABILITY ReaderLock { class IPLR_SCOPED_CAPABILITY WriterLock { public: - WriterLock(RWMutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { + explicit WriterLock(RWMutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { mutex_.LockWriter(); } diff --git a/impeller/base/validation.cc b/impeller/base/validation.cc index bb41c50fec462..bbfd7efda3710 100644 --- a/impeller/base/validation.cc +++ b/impeller/base/validation.cc @@ -48,7 +48,7 @@ void ImpellerValidationBreak(const char* message) { } else { FML_LOG(ERROR) << stream.str(); } -#endif // IMPELLER_DEBUG +#endif // IMPELLER_ENABLE_VALIDATION } } // namespace impeller diff --git a/impeller/base/version.h b/impeller/base/version.h index 8be914f6ba505..423e56272cf27 100644 --- a/impeller/base/version.h +++ b/impeller/base/version.h @@ -18,7 +18,9 @@ struct Version { size_t minor_version; size_t patch_version; - constexpr Version(size_t p_major = 0, size_t p_minor = 0, size_t p_patch = 0) + constexpr explicit Version(size_t p_major = 0, + size_t p_minor = 0, + size_t p_patch = 0) : major_version(p_major), minor_version(p_minor), patch_version(p_patch) {} diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index a5a2afb6d6ed0..fa8cbc865d658 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -155,6 +155,10 @@ static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir, ? source_options.gles_language_version : 100; sl_options.es = true; + if (source_options.require_framebuffer_fetch && + source_options.type == SourceType::kFragmentShader) { + gl_compiler->remap_ext_framebuffer_fetch(0, 0, true); + } gl_compiler->set_variable_type_remap_callback( [&](const spirv_cross::SPIRType& type, const std::string& var_name, std::string& name_of_type) { diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 14a78f6429a26..f09f323fae4f7 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -76,6 +76,7 @@ bool Main(const fml::CommandLine& command_line) { options.gles_language_version = switches.gles_language_version; options.metal_version = switches.metal_version; options.use_half_textures = switches.use_half_textures; + options.require_framebuffer_fetch = switches.require_framebuffer_fetch; Reflector::Options reflector_options; reflector_options.target_platform = switches.target_platform; diff --git a/impeller/compiler/source_options.h b/impeller/compiler/source_options.h index 797cb70a026f1..6c30194b58c24 100644 --- a/impeller/compiler/source_options.h +++ b/impeller/compiler/source_options.h @@ -33,6 +33,11 @@ struct SourceOptions { /// opengl semantics. Only used on metal targets. bool use_half_textures = false; + /// @brief Whether the GLSL framebuffer fetch extension will be required. + /// + /// Only used on OpenGLES targets. + bool require_framebuffer_fetch = false; + SourceOptions(); ~SourceOptions(); diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 8be5b2b6ee974..c539ed423228f 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -75,6 +75,7 @@ void Switches::PrintHelp(std::ostream& stream) { stream << "[optional] --use-half-textures (force openGL semantics when " "targeting metal)" << std::endl; + stream << "[optional] --require-framebuffer-fetch" << std::endl; } Switches::Switches() = default; @@ -136,7 +137,9 @@ Switches::Switches(const fml::CommandLine& command_line) command_line.GetOptionValueWithDefault("metal-version", "1.2")), entry_point( command_line.GetOptionValueWithDefault("entry-point", "main")), - use_half_textures(command_line.HasOption("use-half-textures")) { + use_half_textures(command_line.HasOption("use-half-textures")), + require_framebuffer_fetch( + command_line.HasOption("require-framebuffer-fetch")) { auto language = command_line.GetOptionValueWithDefault("source-language", "glsl"); std::transform(language.begin(), language.end(), language.begin(), diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 66884370b091d..e82bab6a07dac 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -37,12 +37,13 @@ struct Switches { std::string metal_version = ""; std::string entry_point = ""; bool use_half_textures = false; + bool require_framebuffer_fetch = false; Switches(); ~Switches(); - Switches(const fml::CommandLine& command_line); + explicit Switches(const fml::CommandLine& command_line); bool AreValid(std::ostream& explain) const; diff --git a/impeller/core/formats.h b/impeller/core/formats.h index 0e8d0d7d86a43..6cc151e81939c 100644 --- a/impeller/core/formats.h +++ b/impeller/core/formats.h @@ -325,33 +325,11 @@ enum class IndexType { kNone, }; -/// Decides how backend draws pixels based on input vertices. enum class PrimitiveType { - /// Draws a triage for each separate set of three vertices. - /// - /// Vertices [A, B, C, D, E, F] will produce triages - /// [ABC, DEF]. kTriangle, - - /// Draws a triage for every adjacent three vertices. - /// - /// Vertices [A, B, C, D, E, F] will produce triages - /// [ABC, BCD, CDE, DEF]. kTriangleStrip, - - /// Draws a line for each separate set of two vertices. - /// - /// Vertices [A, B, C] will produce discontinued line - /// [AB, BC]. kLine, - - /// Draws a continuous line that connect every input vertices - /// - /// Vertices [A, B, C] will produce one continuous line - /// [ABC]. kLineStrip, - - /// Draws a point at each input vertex. kPoint, // Triangle fans are implementation dependent and need extra extensions // checks. Hence, they are not supported here. diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index edcd363a7e45c..7796efc26b8b6 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -16,20 +15,13 @@ #include "flutter/fml/trace_event.h" #include "impeller/aiks/color_filter.h" #include "impeller/core/formats.h" -#include "impeller/display_list/dl_image_impeller.h" #include "impeller/display_list/dl_vertices_geometry.h" #include "impeller/display_list/nine_patch_converter.h" #include "impeller/display_list/skia_conversions.h" -#include "impeller/entity/contents/conical_gradient_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" -#include "impeller/entity/contents/linear_gradient_contents.h" -#include "impeller/entity/contents/radial_gradient_contents.h" #include "impeller/entity/contents/runtime_effect_contents.h" -#include "impeller/entity/contents/sweep_gradient_contents.h" -#include "impeller/entity/contents/tiled_texture_contents.h" #include "impeller/entity/entity.h" -#include "impeller/entity/geometry/geometry.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" #include "impeller/geometry/scalar.h" @@ -271,30 +263,6 @@ static std::vector ToColors(const flutter::DlColor colors[], int count) { return result; } -// Convert display list colors + stops into impeller colors and stops, taking -// care to ensure that the stops always start with 0.0 and end with 1.0. -template -static void ConvertStops(T* gradient, - std::vector* colors, - std::vector* stops) { - FML_DCHECK(gradient->stop_count() >= 2); - - auto* dl_colors = gradient->colors(); - auto* dl_stops = gradient->stops(); - if (dl_stops[0] != 0.0) { - colors->emplace_back(skia_conversions::ToColor(dl_colors[0])); - stops->emplace_back(0); - } - for (auto i = 0; i < gradient->stop_count(); i++) { - colors->emplace_back(skia_conversions::ToColor(dl_colors[i])); - stops->emplace_back(dl_stops[i]); - } - if (stops->back() != 1.0) { - colors->emplace_back(colors->back()); - stops->emplace_back(1.0); - } -} - static std::optional ToColorSourceType( flutter::DlColorSourceType type) { switch (type) { @@ -351,7 +319,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { auto end_point = skia_conversions::ToPoint(linear->end_point()); std::vector colors; std::vector stops; - ConvertStops(linear, &colors, &stops); + skia_conversions::ConvertStops(linear, colors, stops); auto tile_mode = ToTileMode(linear->tile_mode()); auto matrix = ToMatrix(linear->matrix()); @@ -372,7 +340,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { SkScalar focus_radius = conical_gradient->start_radius(); std::vector colors; std::vector stops; - ConvertStops(conical_gradient, &colors, &stops); + skia_conversions::ConvertStops(conical_gradient, colors, stops); auto tile_mode = ToTileMode(conical_gradient->tile_mode()); auto matrix = ToMatrix(conical_gradient->matrix()); @@ -390,7 +358,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { auto radius = radialGradient->radius(); std::vector colors; std::vector stops; - ConvertStops(radialGradient, &colors, &stops); + skia_conversions::ConvertStops(radialGradient, colors, stops); auto tile_mode = ToTileMode(radialGradient->tile_mode()); auto matrix = ToMatrix(radialGradient->matrix()); @@ -409,7 +377,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { auto end_angle = Degrees(sweepGradient->end()); std::vector colors; std::vector stops; - ConvertStops(sweepGradient, &colors, &stops); + skia_conversions::ConvertStops(sweepGradient, colors, stops); auto tile_mode = ToTileMode(sweepGradient->tile_mode()); auto matrix = ToMatrix(sweepGradient->matrix()); @@ -507,7 +475,6 @@ static std::shared_ptr ToColorFilter( // |flutter::DlOpReceiver| void DlDispatcher::setColorFilter(const flutter::DlColorFilter* filter) { - // Needs https://github.com/flutter/flutter/issues/95434 paint_.color_filter = ToColorFilter(filter); } @@ -848,19 +815,29 @@ void DlDispatcher::drawDRRect(const SkRRect& outer, const SkRRect& inner) { // |flutter::DlOpReceiver| void DlDispatcher::drawPath(const SkPath& path) { SkRect rect; - SkRRect rrect; - SkRect oval; - if (path.isRect(&rect)) { + + // We can't "optimize" a path into a rectangle if it's open. + bool closed; + if (path.isRect(&rect, &closed) && closed) { canvas_.DrawRect(skia_conversions::ToRect(rect), paint_); - } else if (path.isRRect(&rrect) && rrect.isSimple()) { + return; + } + + SkRRect rrect; + if (path.isRRect(&rrect) && rrect.isSimple()) { canvas_.DrawRRect(skia_conversions::ToRect(rrect.rect()), rrect.getSimpleRadii().fX, paint_); - } else if (path.isOval(&oval) && oval.width() == oval.height()) { + return; + } + + SkRect oval; + if (path.isOval(&oval) && oval.width() == oval.height()) { canvas_.DrawCircle(skia_conversions::ToPoint(oval.center()), oval.width() * 0.5, paint_); - } else { - canvas_.DrawPath(skia_conversions::ToPath(path), paint_); + return; } + + canvas_.DrawPath(skia_conversions::ToPath(path), paint_); } // |flutter::DlOpReceiver| diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index 0dc3b7ce72461..8f0136a020917 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -6,7 +6,7 @@ #include "flutter/display_list/dl_op_receiver.h" #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas.h" +#include "impeller/aiks/canvas_type.h" #include "impeller/aiks/paint.h" namespace impeller { @@ -226,7 +226,7 @@ class DlDispatcher final : public flutter::DlOpReceiver { private: Paint paint_; - Canvas canvas_; + CanvasType canvas_; Matrix initial_matrix_; FML_DISALLOW_COPY_AND_ASSIGN(DlDispatcher); diff --git a/impeller/display_list/dl_unittests.cc b/impeller/display_list/dl_unittests.cc index 70396ccdb2200..c59ca6b01056d 100644 --- a/impeller/display_list/dl_unittests.cc +++ b/impeller/display_list/dl_unittests.cc @@ -21,6 +21,7 @@ #include "impeller/display_list/dl_dispatcher.h" #include "impeller/display_list/dl_image_impeller.h" #include "impeller/display_list/dl_playground.h" +#include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_rrect_blur_contents.h" #include "impeller/geometry/constants.h" @@ -411,6 +412,33 @@ TEST_P(DisplayListTest, CanDrawWithOddPathWinding) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +// Regression test for https://github.com/flutter/flutter/issues/134816. +// +// It should be possible to draw 3 lines, and not have an implicit close path. +TEST_P(DisplayListTest, CanDrawAnOpenPath) { + flutter::DisplayListBuilder builder; + flutter::DlPaint paint; + + paint.setColor(flutter::DlColor::kRed()); + paint.setDrawStyle(flutter::DlDrawStyle::kStroke); + paint.setStrokeWidth(10); + + builder.Translate(300, 300); + + // Move to (50, 50) and draw lines from: + // 1. (50, height) + // 2. (width, height) + // 3. (width, 50) + SkPath path; + path.moveTo(50, 50); + path.lineTo(50, 100); + path.lineTo(100, 100); + path.lineTo(100, 50); + builder.DrawPath(path, paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + TEST_P(DisplayListTest, CanDrawWithMaskBlur) { auto texture = CreateTextureForFixture("embarcadero.jpg"); flutter::DisplayListBuilder builder; @@ -1169,11 +1197,13 @@ TEST_P(DisplayListTest, CanDrawRectWithLinearToSrgbColorFilter) { flutter::DlPaint paint; paint.setColor(flutter::DlColor(0xFF2196F3).withAlpha(128)); flutter::DisplayListBuilder builder; - paint.setColorFilter(flutter::DlLinearToSrgbGammaColorFilter::instance.get()); + paint.setColorFilter( + flutter::DlLinearToSrgbGammaColorFilter::kInstance.get()); builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint); builder.Translate(0, 200); - paint.setColorFilter(flutter::DlSrgbToLinearGammaColorFilter::instance.get()); + paint.setColorFilter( + flutter::DlSrgbToLinearGammaColorFilter::kInstance.get()); builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint); ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); @@ -1652,6 +1682,65 @@ TEST_P(DisplayListTest, DrawVerticesBlendModes) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +template +static std::optional GetCoverageOfFirstEntity(const Picture& picture) { + std::optional coverage; + picture.pass->IterateAllEntities([&coverage](Entity& entity) { + if (std::static_pointer_cast(entity.GetContents())) { + auto contents = std::static_pointer_cast(entity.GetContents()); + Entity entity; + coverage = contents->GetCoverage(entity); + return false; + } + return true; + }); + return coverage; +} + +TEST(DisplayListTest, RRectBoundsComputation) { + SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeLTRB(0, 0, 100, 100), 4, 4); + SkPath path = SkPath().addRRect(rrect); + + flutter::DlPaint paint; + flutter::DisplayListBuilder builder; + + builder.DrawPath(path, paint); + auto display_list = builder.Build(); + + DlDispatcher dispatcher; + display_list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + + std::optional coverage = + GetCoverageOfFirstEntity(picture); + + // Validate that the RRect coverage is _exactly_ the same as the input rect. + ASSERT_TRUE(coverage.has_value()); + ASSERT_EQ(coverage.value_or(Rect::MakeMaximum()), + Rect::MakeLTRB(0, 0, 100, 100)); +} + +TEST(DisplayListTest, CircleBoundsComputation) { + SkPath path = SkPath().addCircle(0, 0, 5); + + flutter::DlPaint paint; + flutter::DisplayListBuilder builder; + + builder.DrawPath(path, paint); + auto display_list = builder.Build(); + + DlDispatcher dispatcher; + display_list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + + std::optional coverage = + GetCoverageOfFirstEntity(picture); + + ASSERT_TRUE(coverage.has_value()); + ASSERT_EQ(coverage.value_or(Rect::MakeMaximum()), + Rect::MakeLTRB(-5, -5, 5, 5)); +} + #ifdef IMPELLER_ENABLE_3D TEST_P(DisplayListTest, SceneColorSource) { // Load up the scene. diff --git a/impeller/display_list/nine_patch_converter.cc b/impeller/display_list/nine_patch_converter.cc index 4a4536999ac6e..21f3097937541 100644 --- a/impeller/display_list/nine_patch_converter.cc +++ b/impeller/display_list/nine_patch_converter.cc @@ -61,7 +61,7 @@ void NinePatchConverter::DrawNinePatch(const std::shared_ptr& image, Rect center, Rect dst, const SamplerDescriptor& sampler, - Canvas* canvas, + CanvasType* canvas, Paint* paint) { if (dst.IsEmpty()) { return; diff --git a/impeller/display_list/nine_patch_converter.h b/impeller/display_list/nine_patch_converter.h index 422a8943a0bd6..45caf719df677 100644 --- a/impeller/display_list/nine_patch_converter.h +++ b/impeller/display_list/nine_patch_converter.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas.h" +#include "impeller/aiks/canvas_type.h" #include "impeller/aiks/image.h" #include "impeller/aiks/paint.h" #include "impeller/core/sampler_descriptor.h" @@ -26,7 +26,7 @@ class NinePatchConverter { Rect center, Rect dst, const SamplerDescriptor& sampler, - Canvas* canvas, + CanvasType* canvas, Paint* paint); private: diff --git a/impeller/display_list/skia_conversions.cc b/impeller/display_list/skia_conversions.cc index 8b17ad0acf315..56e2f87825ccd 100644 --- a/impeller/display_list/skia_conversions.cc +++ b/impeller/display_list/skia_conversions.cc @@ -189,5 +189,29 @@ std::optional ToPixelFormat(SkColorType type) { return std::nullopt; } +void ConvertStops(const flutter::DlGradientColorSourceBase* gradient, + std::vector& colors, + std::vector& stops) { + FML_DCHECK(gradient->stop_count() >= 2); + + auto* dl_colors = gradient->colors(); + auto* dl_stops = gradient->stops(); + if (dl_stops[0] != 0.0) { + colors.emplace_back(skia_conversions::ToColor(dl_colors[0])); + stops.emplace_back(0); + } + for (auto i = 0; i < gradient->stop_count(); i++) { + colors.emplace_back(skia_conversions::ToColor(dl_colors[i])); + stops.emplace_back(std::clamp(dl_stops[i], 0.0f, 1.0f)); + } + if (dl_stops[gradient->stop_count() - 1] != 1.0) { + colors.emplace_back(colors.back()); + stops.emplace_back(1.0); + } + for (auto i = 1; i < gradient->stop_count(); i++) { + stops[i] = std::clamp(stops[i], stops[i - 1], stops[i]); + } +} + } // namespace skia_conversions } // namespace impeller diff --git a/impeller/display_list/skia_conversions.h b/impeller/display_list/skia_conversions.h index 4b535db6227c3..4eed60fb124d2 100644 --- a/impeller/display_list/skia_conversions.h +++ b/impeller/display_list/skia_conversions.h @@ -5,6 +5,7 @@ #pragma once #include "display_list/dl_color.h" +#include "display_list/effects/dl_color_source.h" #include "impeller/core/formats.h" #include "impeller/geometry/color.h" #include "impeller/geometry/path.h" @@ -47,5 +48,23 @@ Path PathDataFromTextBlob(const sk_sp& blob, std::optional ToPixelFormat(SkColorType type); +/// @brief Convert display list colors + stops into impeller colors and stops, +/// taking care to ensure that the stops monotonically increase from 0.0 to 1.0. +/// +/// The general process is: +/// * Ensure that the first gradient stop value is 0.0. If not, insert a new +/// stop with a value of 0.0 and use the first gradient color as this new +/// stops color. +/// * Ensure the last gradient stop value is 1.0. If not, insert a new stop +/// with a value of 1.0 and use the last gradient color as this stops color. +/// * Clamp all gradient values between the values of 0.0 and 1.0. +/// * For all stop values, ensure that the values are monotonically increasing +/// by clamping each value to a minimum of the previous stop value and itself. +/// For example, with stop values of 0.0, 0.5, 0.4, 1.0, we would clamp such +/// that the values were 0.0, 0.5, 0.5, 1.0. +void ConvertStops(const flutter::DlGradientColorSourceBase* gradient, + std::vector& colors, + std::vector& stops); + } // namespace skia_conversions } // namespace impeller diff --git a/impeller/display_list/skia_conversions_unittests.cc b/impeller/display_list/skia_conversions_unittests.cc index c714a2fb6c760..eaf1f571ab7f5 100644 --- a/impeller/display_list/skia_conversions_unittests.cc +++ b/impeller/display_list/skia_conversions_unittests.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "display_list/dl_color.h" +#include "display_list/dl_tile_mode.h" #include "flutter/testing/testing.h" #include "impeller/display_list/skia_conversions.h" #include "impeller/geometry/scalar.h" @@ -13,8 +15,8 @@ TEST(SkiaConversionsTest, ToColor) { // Create a color with alpha, red, green, and blue values that are all // trivially divisible by 255 so that we can test the conversion results in // correct scalar values. - // AARRGGBB - const flutter::DlColor color = 0x8040C020; + // AARRGGBB + const flutter::DlColor color = flutter::DlColor(0x8040C020); auto converted_color = skia_conversions::ToColor(color); ASSERT_TRUE(ScalarNearlyEqual(converted_color.alpha, 0x80 * (1.0f / 255))); @@ -23,5 +25,136 @@ TEST(SkiaConversionsTest, ToColor) { ASSERT_TRUE(ScalarNearlyEqual(converted_color.blue, 0x20 * (1.0f / 255))); } +TEST(SkiaConversionsTest, GradientStopConversion) { + // Typical gradient. + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed(), + flutter::DlColor::kGreen()}; + std::vector stops = {0.0, 0.5, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 3, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientMissing0) { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + std::vector stops = {0.5, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 2, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // First color is inserted as blue. + ASSERT_TRUE(ScalarNearlyEqual(converted_colors[0].blue, 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientMissingLastValue) { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + std::vector stops = {0.0, .5}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 2, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Last color is inserted as red. + ASSERT_TRUE(ScalarNearlyEqual(converted_colors[2].red, 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientStopGreaterThan1) { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kGreen(), + flutter::DlColor::kRed()}; + std::vector stops = {0.0, 100, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 3, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Value is clamped to 1.0 + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientConversionNonMonotonic) { + std::vector colors = { + flutter::DlColor::kBlue(), flutter::DlColor::kGreen(), + flutter::DlColor::kGreen(), flutter::DlColor::kRed()}; + std::vector stops = {0.0, 0.5, 0.4, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 4, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Value is clamped to 0.5 + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[3], 1.0f)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index cf26434a97fb2..b82162b305b10 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -103,6 +103,10 @@ impeller_shaders("modern_entity_shaders") { impeller_shaders("framebuffer_blend_entity_shaders") { name = "framebuffer_blend" + require_framebuffer_fetch = true + + # malioc does not support analyzing shaders that use the framebuffer fetch extension on some GPUs. + analyze = false if (is_mac && !is_ios) { # Note: this needs to correspond to the Apple7 Support family @@ -110,15 +114,6 @@ impeller_shaders("framebuffer_blend_entity_shaders") { metal_version = "2.3" } - # This version is to disable malioc checks. - if (impeller_enable_opengles) { - gles_language_version = 460 - } - - if (impeller_enable_vulkan) { - vulkan_language_version = 130 - } - shaders = [ "shaders/blending/ios/framebuffer_blend.vert", "shaders/blending/ios/framebuffer_blend_color.frag", diff --git a/impeller/entity/contents/atlas_contents.cc b/impeller/entity/contents/atlas_contents.cc index 1363e169faea7..34030ac95a9f0 100644 --- a/impeller/entity/contents/atlas_contents.cc +++ b/impeller/entity/contents/atlas_contents.cc @@ -250,7 +250,7 @@ bool AtlasContents::Render(const ContentContext& renderer, DEBUG_COMMAND_INFO( cmd, SPrintF("DrawAtlas Blend (%s)", BlendModeToString(blend_mode_))); cmd.BindVertices(vtx_buffer); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPass(pass); cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options); @@ -262,6 +262,8 @@ bool AtlasContents::Render(const ContentContext& renderer, dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; } + frag_info.supports_decal_sampler_address_mode = + renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode(); auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( dst_sampler_descriptor); FS::BindTextureSamplerDst(cmd, texture_, dst_sampler); @@ -425,7 +427,7 @@ bool AtlasTextureContents::Render(const ContentContext& renderer, auto options = OptionsFromPassAndEntity(pass, entity); cmd.pipeline = renderer.GetTexturePipeline(options); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); FS::BindTextureSampler(cmd, texture, @@ -515,7 +517,7 @@ bool AtlasColorContents::Render(const ContentContext& renderer, auto opts = OptionsFromPassAndEntity(pass, entity); opts.blend_mode = BlendMode::kSourceOver; cmd.pipeline = renderer.GetGeometryColorPipeline(opts); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc index 21c2eb1aa2469..241049a0788cf 100644 --- a/impeller/entity/contents/clip_contents.cc +++ b/impeller/entity/contents/clip_contents.cc @@ -34,31 +34,29 @@ std::optional ClipContents::GetCoverage(const Entity& entity) const { return std::nullopt; }; -Contents::StencilCoverage ClipContents::GetStencilCoverage( +Contents::ClipCoverage ClipContents::GetClipCoverage( const Entity& entity, - const std::optional& current_stencil_coverage) const { - if (!current_stencil_coverage.has_value()) { - return {.type = StencilCoverage::Type::kAppend, .coverage = std::nullopt}; + const std::optional& current_clip_coverage) const { + if (!current_clip_coverage.has_value()) { + return {.type = ClipCoverage::Type::kAppend, .coverage = std::nullopt}; } switch (clip_op_) { case Entity::ClipOperation::kDifference: // This can be optimized further by considering cases when the bounds of // the current stencil will shrink. - return {.type = StencilCoverage::Type::kAppend, - .coverage = current_stencil_coverage}; + return {.type = ClipCoverage::Type::kAppend, + .coverage = current_clip_coverage}; case Entity::ClipOperation::kIntersect: if (!geometry_) { - return {.type = StencilCoverage::Type::kAppend, - .coverage = std::nullopt}; + return {.type = ClipCoverage::Type::kAppend, .coverage = std::nullopt}; } auto coverage = geometry_->GetCoverage(entity.GetTransformation()); - if (!coverage.has_value() || !current_stencil_coverage.has_value()) { - return {.type = StencilCoverage::Type::kAppend, - .coverage = std::nullopt}; + if (!coverage.has_value() || !current_clip_coverage.has_value()) { + return {.type = ClipCoverage::Type::kAppend, .coverage = std::nullopt}; } return { - .type = StencilCoverage::Type::kAppend, - .coverage = current_stencil_coverage->Intersection(coverage.value()), + .type = ClipCoverage::Type::kAppend, + .coverage = current_clip_coverage->Intersection(coverage.value()), }; } FML_UNREACHABLE(); @@ -66,7 +64,7 @@ Contents::StencilCoverage ClipContents::GetStencilCoverage( bool ClipContents::ShouldRender( const Entity& entity, - const std::optional& stencil_coverage) const { + const std::optional& clip_coverage) const { return true; } @@ -87,7 +85,7 @@ bool ClipContents::Render(const ContentContext& renderer, auto options = OptionsFromPass(pass); options.blend_mode = BlendMode::kDestination; - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); options.stencil_compare = CompareFunction::kEqual; options.stencil_operation = StencilOperation::kIncrementClamp; @@ -113,7 +111,7 @@ bool ClipContents::Render(const ContentContext& renderer, { DEBUG_COMMAND_INFO(cmd, "Difference Clip (Punch)"); - cmd.stencil_reference = entity.GetStencilDepth() + 1; + cmd.stencil_reference = entity.GetClipDepth() + 1; options.stencil_compare = CompareFunction::kEqual; options.stencil_operation = StencilOperation::kDecrementClamp; } @@ -155,15 +153,15 @@ std::optional ClipRestoreContents::GetCoverage( return std::nullopt; }; -Contents::StencilCoverage ClipRestoreContents::GetStencilCoverage( +Contents::ClipCoverage ClipRestoreContents::GetClipCoverage( const Entity& entity, - const std::optional& current_stencil_coverage) const { - return {.type = StencilCoverage::Type::kRestore, .coverage = std::nullopt}; + const std::optional& current_clip_coverage) const { + return {.type = ClipCoverage::Type::kRestore, .coverage = std::nullopt}; } bool ClipRestoreContents::ShouldRender( const Entity& entity, - const std::optional& stencil_coverage) const { + const std::optional& clip_coverage) const { return true; } @@ -186,7 +184,7 @@ bool ClipRestoreContents::Render(const ContentContext& renderer, options.stencil_operation = StencilOperation::kSetToReferenceValue; options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetClipPipeline(options); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); // Create a rect that covers either the given restore area, or the whole // render target texture. diff --git a/impeller/entity/contents/clip_contents.h b/impeller/entity/contents/clip_contents.h index 3b0faac98bc60..36f292251042f 100644 --- a/impeller/entity/contents/clip_contents.h +++ b/impeller/entity/contents/clip_contents.h @@ -29,13 +29,13 @@ class ClipContents final : public Contents { std::optional GetCoverage(const Entity& entity) const override; // |Contents| - StencilCoverage GetStencilCoverage( + ClipCoverage GetClipCoverage( const Entity& entity, - const std::optional& current_stencil_coverage) const override; + const std::optional& current_clip_coverage) const override; // |Contents| bool ShouldRender(const Entity& entity, - const std::optional& stencil_coverage) const override; + const std::optional& clip_coverage) const override; // |Contents| bool Render(const ContentContext& renderer, @@ -70,13 +70,13 @@ class ClipRestoreContents final : public Contents { std::optional GetCoverage(const Entity& entity) const override; // |Contents| - StencilCoverage GetStencilCoverage( + ClipCoverage GetClipCoverage( const Entity& entity, - const std::optional& current_stencil_coverage) const override; + const std::optional& current_clip_coverage) const override; // |Contents| bool ShouldRender(const Entity& entity, - const std::optional& stencil_coverage) const override; + const std::optional& clip_coverage) const override; // |Contents| bool Render(const ContentContext& renderer, diff --git a/impeller/entity/contents/conical_gradient_contents.cc b/impeller/entity/contents/conical_gradient_contents.cc index dc61c3794b3fd..8447364a395b4 100644 --- a/impeller/entity/contents/conical_gradient_contents.cc +++ b/impeller/entity/contents/conical_gradient_contents.cc @@ -100,7 +100,7 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "ConicalGradientSSBOFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto geometry_result = GetGeometry()->GetPositionBuffer(renderer, entity, pass); @@ -168,7 +168,7 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "ConicalGradientFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); if (geometry_result.prevent_overdraw) { diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 65ef43e095403..560c9d804800e 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -38,10 +38,19 @@ void ContentContextOptions::ApplyToPipelineDescriptor( switch (pipeline_blend) { case BlendMode::kClear: - color0.dst_alpha_blend_factor = BlendFactor::kZero; - color0.dst_color_blend_factor = BlendFactor::kZero; - color0.src_alpha_blend_factor = BlendFactor::kZero; - color0.src_color_blend_factor = BlendFactor::kZero; + if (is_for_rrect_blur_clear) { + color0.alpha_blend_op = BlendOperation::kReverseSubtract; + color0.color_blend_op = BlendOperation::kReverseSubtract; + color0.dst_alpha_blend_factor = BlendFactor::kOne; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kDestinationColor; + color0.src_color_blend_factor = BlendFactor::kDestinationColor; + } else { + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + } break; case BlendMode::kSource: color0.blending_enabled = false; @@ -155,6 +164,7 @@ static std::unique_ptr CreateDefaultPipeline( const auto default_color_format = context.GetCapabilities()->GetDefaultColorFormat(); ContentContextOptions{.sample_count = SampleCount::kCount4, + .primitive_type = PrimitiveType::kTriangleStrip, .color_attachment_pixel_format = default_color_format} .ApplyToPipelineDescriptor(*desc); return std::make_unique(context, desc); @@ -162,7 +172,8 @@ static std::unique_ptr CreateDefaultPipeline( ContentContext::ContentContext( std::shared_ptr context, - std::shared_ptr typographer_context) + std::shared_ptr typographer_context, + std::shared_ptr render_target_allocator) : context_(std::move(context)), lazy_glyph_atlas_( std::make_shared(std::move(typographer_context))), @@ -170,147 +181,116 @@ ContentContext::ContentContext( #if IMPELLER_ENABLE_3D scene_context_(std::make_shared(context_)), #endif // IMPELLER_ENABLE_3D - render_target_cache_(std::make_shared( - context_->GetResourceAllocator())) { + render_target_cache_(render_target_allocator == nullptr + ? std::make_shared( + context_->GetResourceAllocator()) + : std::move(render_target_allocator)) { if (!context_ || !context_->IsValid()) { return; } - default_options_ = ContentContextOptions{ + auto options = ContentContextOptions{ + .sample_count = SampleCount::kCount4, + .color_attachment_pixel_format = + context_->GetCapabilities()->GetDefaultColorFormat()}; + auto options_trianglestrip = ContentContextOptions{ .sample_count = SampleCount::kCount4, + .primitive_type = PrimitiveType::kTriangleStrip, .color_attachment_pixel_format = context_->GetCapabilities()->GetDefaultColorFormat()}; #ifdef IMPELLER_DEBUG - checkerboard_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + checkerboard_pipelines_.CreateDefault(*context_, options); #endif // IMPELLER_DEBUG - solid_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + solid_fill_pipelines_.CreateDefault(*context_, options); if (context_->GetCapabilities()->SupportsSSBO()) { - linear_gradient_ssbo_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - radial_gradient_ssbo_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - conical_gradient_ssbo_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - sweep_gradient_ssbo_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + linear_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); + radial_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); + conical_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); + sweep_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); } else { - linear_gradient_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - radial_gradient_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - conical_gradient_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - sweep_gradient_fill_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + linear_gradient_fill_pipelines_.CreateDefault(*context_, options); + radial_gradient_fill_pipelines_.CreateDefault(*context_, options); + conical_gradient_fill_pipelines_.CreateDefault(*context_, options); + sweep_gradient_fill_pipelines_.CreateDefault(*context_, options); } if (context_->GetCapabilities()->SupportsFramebufferFetch()) { - framebuffer_blend_color_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_colorburn_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_colordodge_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_darken_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_difference_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_exclusion_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_hardlight_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_hue_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_lighten_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_luminosity_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_multiply_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_overlay_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_saturation_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_screen_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - framebuffer_blend_softlight_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + framebuffer_blend_color_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_colorburn_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_colordodge_pipelines_.CreateDefault( + *context_, options_trianglestrip); + framebuffer_blend_darken_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_difference_pipelines_.CreateDefault( + *context_, options_trianglestrip); + framebuffer_blend_exclusion_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_hardlight_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_hue_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_lighten_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_luminosity_pipelines_.CreateDefault( + *context_, options_trianglestrip); + framebuffer_blend_multiply_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_overlay_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_saturation_pipelines_.CreateDefault( + *context_, options_trianglestrip); + framebuffer_blend_screen_pipelines_.CreateDefault(*context_, + options_trianglestrip); + framebuffer_blend_softlight_pipelines_.CreateDefault(*context_, + options_trianglestrip); } - blend_color_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_colorburn_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_colordodge_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_darken_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_difference_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_exclusion_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_hardlight_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_hue_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_lighten_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_luminosity_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_multiply_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_overlay_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_saturation_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_screen_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - blend_softlight_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - - rrect_blur_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - texture_blend_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - texture_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - position_uv_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - tiled_texture_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - gaussian_blur_noalpha_decal_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - gaussian_blur_noalpha_nodecal_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - border_mask_blur_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - morphology_filter_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - color_matrix_color_filter_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - linear_to_srgb_filter_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - srgb_to_linear_filter_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - glyph_atlas_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - glyph_atlas_color_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - geometry_color_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - yuv_to_rgb_filter_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); - porter_duff_blend_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + blend_color_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_colorburn_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_colordodge_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_darken_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_difference_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_exclusion_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_hardlight_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_hue_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_lighten_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_luminosity_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_multiply_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_overlay_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_saturation_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_screen_pipelines_.CreateDefault(*context_, options_trianglestrip); + blend_softlight_pipelines_.CreateDefault(*context_, options_trianglestrip); + + rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); + texture_blend_pipelines_.CreateDefault(*context_, options); + texture_pipelines_.CreateDefault(*context_, options); + position_uv_pipelines_.CreateDefault(*context_, options); + tiled_texture_pipelines_.CreateDefault(*context_, options); + gaussian_blur_noalpha_decal_pipelines_.CreateDefault(*context_, + options_trianglestrip); + gaussian_blur_noalpha_nodecal_pipelines_.CreateDefault(*context_, + options_trianglestrip); + border_mask_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); + morphology_filter_pipelines_.CreateDefault(*context_, options_trianglestrip); + color_matrix_color_filter_pipelines_.CreateDefault(*context_, + options_trianglestrip); + linear_to_srgb_filter_pipelines_.CreateDefault(*context_, + options_trianglestrip); + srgb_to_linear_filter_pipelines_.CreateDefault(*context_, + options_trianglestrip); + glyph_atlas_pipelines_.CreateDefault(*context_, options); + glyph_atlas_color_pipelines_.CreateDefault(*context_, options); + geometry_color_pipelines_.CreateDefault(*context_, options); + yuv_to_rgb_filter_pipelines_.CreateDefault(*context_, options_trianglestrip); + porter_duff_blend_pipelines_.CreateDefault(*context_, options_trianglestrip); // GLES only shader. #ifdef IMPELLER_ENABLE_OPENGLES if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) { - texture_external_pipelines_[default_options_] = - CreateDefaultPipeline(*context_); + texture_external_pipelines_.CreateDefault(*context_, options); } #endif // IMPELLER_ENABLE_OPENGLES if (context_->GetCapabilities()->SupportsCompute()) { @@ -346,8 +326,8 @@ ContentContext::ContentContext( } clip_pipeline_descriptor->SetColorAttachmentDescriptors( std::move(clip_color_attachments)); - clip_pipelines_[default_options_] = - std::make_unique(*context_, clip_pipeline_descriptor); + clip_pipelines_.SetDefault(options, std::make_unique( + *context_, clip_pipeline_descriptor)); is_valid_ = true; } @@ -370,21 +350,15 @@ std::shared_ptr ContentContext::MakeSubpass( subpass_target = RenderTarget::CreateOffscreenMSAA( *context, *GetRenderTargetCache(), texture_size, SPrintF("%s Offscreen", label.c_str()), - RenderTarget::kDefaultColorAttachmentConfigMSAA // -#ifndef FML_OS_ANDROID // Reduce PSO variants for Vulkan. - , + RenderTarget::kDefaultColorAttachmentConfigMSAA, std::nullopt // stencil_attachment_config -#endif // FML_OS_ANDROID ); } else { subpass_target = RenderTarget::CreateOffscreen( *context, *GetRenderTargetCache(), texture_size, SPrintF("%s Offscreen", label.c_str()), - RenderTarget::kDefaultColorAttachmentConfig // -#ifndef FML_OS_ANDROID // Reduce PSO variants for Vulkan. - , + RenderTarget::kDefaultColorAttachmentConfig, // std::nullopt // stencil_attachment_config -#endif // FML_OS_ANDROID ); } auto subpass_texture = subpass_target.GetRenderTargetTexture(); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 3c72798623758..a5f0aab9bd6a8 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -308,13 +308,14 @@ struct ContentContextOptions { PixelFormat color_attachment_pixel_format = PixelFormat::kUnknown; bool has_stencil_attachment = true; bool wireframe = false; + bool is_for_rrect_blur_clear = false; struct Hash { constexpr std::size_t operator()(const ContentContextOptions& o) const { - return fml::HashCombine(o.sample_count, o.blend_mode, o.stencil_compare, - o.stencil_operation, o.primitive_type, - o.color_attachment_pixel_format, - o.has_stencil_attachment, o.wireframe); + return fml::HashCombine( + o.sample_count, o.blend_mode, o.stencil_compare, o.stencil_operation, + o.primitive_type, o.color_attachment_pixel_format, + o.has_stencil_attachment, o.wireframe, o.is_for_rrect_blur_clear); } }; @@ -329,7 +330,8 @@ struct ContentContextOptions { lhs.color_attachment_pixel_format == rhs.color_attachment_pixel_format && lhs.has_stencil_attachment == rhs.has_stencil_attachment && - lhs.wireframe == rhs.wireframe; + lhs.wireframe == rhs.wireframe && + lhs.is_for_rrect_blur_clear == rhs.is_for_rrect_blur_clear; } }; @@ -343,7 +345,8 @@ class ContentContext { public: explicit ContentContext( std::shared_ptr context, - std::shared_ptr typographer_context); + std::shared_ptr typographer_context, + std::shared_ptr render_target_allocator = nullptr); ~ContentContext(); @@ -718,11 +721,59 @@ class ContentContext { std::shared_ptr context_; std::shared_ptr lazy_glyph_atlas_; - template - using Variants = std::unordered_map, - ContentContextOptions::Hash, - ContentContextOptions::Equal>; + template + class Variants { + public: + Variants() = default; + + void Set(const ContentContextOptions& options, + std::unique_ptr pipeline) { + pipelines_[options] = std::move(pipeline); + } + + void SetDefault(const ContentContextOptions& options, + std::unique_ptr pipeline) { + default_options_ = options; + Set(options, std::move(pipeline)); + } + + void CreateDefault(const Context& context, + const ContentContextOptions& options) { + auto desc = PipelineT::Builder::MakeDefaultPipelineDescriptor(context); + if (!desc.has_value()) { + VALIDATION_LOG << "Failed to create default pipeline."; + return; + } + options.ApplyToPipelineDescriptor(*desc); + SetDefault(options, std::make_unique(context, desc)); + } + + PipelineT* Get(const ContentContextOptions& options) const { + if (auto found = pipelines_.find(options); found != pipelines_.end()) { + return found->second.get(); + } + return nullptr; + } + + PipelineT* GetDefault() const { + if (!default_options_.has_value()) { + return nullptr; + } + return Get(default_options_.value()); + } + + size_t GetPipelineCount() const { return pipelines_.size(); } + + private: + std::optional default_options_; + std::unordered_map, + ContentContextOptions::Hash, + ContentContextOptions::Equal> + pipelines_; + + FML_DISALLOW_COPY_AND_ASSIGN(Variants); + }; // These are mutable because while the prototypes are created eagerly, any // variants requested from that are lazily created and cached in the variants @@ -821,12 +872,6 @@ class ContentContext { point_field_compute_pipelines_; mutable std::shared_ptr> uv_compute_pipelines_; - // The values for the default context options must be cached on - // initial creation. In the presence of wide gamut and platform views, - // it is possible that secondary surfaces will have a different default - // pixel format, which would cause the prototype check in GetPipeline - // below to fail. - ContentContextOptions default_options_; template std::shared_ptr> GetPipeline( @@ -840,29 +885,30 @@ class ContentContext { opts.wireframe = true; } - if (auto found = container.find(opts); found != container.end()) { - return found->second->WaitAndGet(); + if (auto found = container.Get(opts)) { + return found->WaitAndGet(); } - auto prototype = container.find(default_options_); + auto prototype = container.GetDefault(); // The prototype must always be initialized in the constructor. - FML_CHECK(prototype != container.end()); + FML_CHECK(prototype != nullptr); - auto pipeline = prototype->second->WaitAndGet(); + auto pipeline = prototype->WaitAndGet(); if (!pipeline) { return nullptr; } auto variant_future = pipeline->CreateVariant( - [&opts, variants_count = container.size()](PipelineDescriptor& desc) { + [&opts, variants_count = + container.GetPipelineCount()](PipelineDescriptor& desc) { opts.ApplyToPipelineDescriptor(desc); desc.SetLabel( SPrintF("%s V#%zu", desc.GetLabel().c_str(), variants_count)); }); auto variant = std::make_unique(std::move(variant_future)); auto variant_pipeline = variant->WaitAndGet(); - container[opts] = std::move(variant); + container.Set(opts, std::move(variant)); return variant_pipeline; } diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 85e70e534e061..aefdfe16974c1 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -49,11 +49,11 @@ bool Contents::IsOpaque() const { return false; } -Contents::StencilCoverage Contents::GetStencilCoverage( +Contents::ClipCoverage Contents::GetClipCoverage( const Entity& entity, - const std::optional& current_stencil_coverage) const { - return {.type = StencilCoverage::Type::kNoChange, - .coverage = current_stencil_coverage}; + const std::optional& current_clip_coverage) const { + return {.type = ClipCoverage::Type::kNoChange, + .coverage = current_clip_coverage}; } std::optional Contents::RenderToSnapshot( @@ -123,14 +123,18 @@ std::optional Contents::AsBackgroundColor(const Entity& entity, return {}; } +const FilterContents* Contents::AsFilter() const { + return nullptr; +} + bool Contents::ApplyColorFilter( const Contents::ColorFilterProc& color_filter_proc) { return false; } bool Contents::ShouldRender(const Entity& entity, - const std::optional& stencil_coverage) const { - if (!stencil_coverage.has_value()) { + const std::optional& clip_coverage) const { + if (!clip_coverage.has_value()) { return false; } @@ -141,7 +145,7 @@ bool Contents::ShouldRender(const Entity& entity, if (coverage == Rect::MakeMaximum()) { return true; } - return stencil_coverage->IntersectsWithRect(coverage.value()); + return clip_coverage->IntersectsWithRect(coverage.value()); } void Contents::SetCoverageHint(std::optional coverage_hint) { diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 245b8f2d6d514..6a25261ac0c4e 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -23,6 +23,7 @@ struct ContentContextOptions; class Entity; class Surface; class RenderPass; +class FilterContents; ContentContextOptions OptionsFromPass(const RenderPass& pass); @@ -35,7 +36,7 @@ class Contents { /// unpremultiplied color. using ColorFilterProc = std::function; - struct StencilCoverage { + struct ClipCoverage { enum class Type { kNoChange, kAppend, kRestore }; Type type = Type::kNoChange; @@ -65,7 +66,14 @@ class Contents { RenderPass& pass) const = 0; //---------------------------------------------------------------------------- - /// @brief Get the screen space bounding rectangle that this contents affects. + /// @brief Get the area of the render pass that will be affected when this + /// contents is rendered. + /// + /// During rendering, coverage coordinates count pixels from the top + /// left corner of the framebuffer. + /// + /// @return The coverage rectangle. An `std::nullopt` result means that + /// rendering this contents has no effect on the output color. /// virtual std::optional GetCoverage(const Entity& entity) const = 0; @@ -88,18 +96,21 @@ class Contents { virtual bool IsOpaque() const; //---------------------------------------------------------------------------- - /// @brief Given the current screen space bounding rectangle of the stencil, - /// return the expected stencil coverage after this draw call. This - /// should only be implemented for contents that may write to the - /// stencil buffer. + /// @brief Given the current pass space bounding rectangle of the clip + /// buffer, return the expected clip coverage after this draw call. + /// This should only be implemented for contents that may write to the + /// clip buffer. + /// + /// During rendering, coverage coordinates count pixels from the top + /// left corner of the framebuffer. /// - virtual StencilCoverage GetStencilCoverage( + virtual ClipCoverage GetClipCoverage( const Entity& entity, - const std::optional& current_stencil_coverage) const; + const std::optional& current_clip_coverage) const; //---------------------------------------------------------------------------- /// @brief Render this contents to a snapshot, respecting the entity's - /// transform, path, stencil depth, and blend mode. + /// transform, path, clip depth, and blend mode. /// The result texture size is always the size of /// `GetCoverage(entity)`. /// @@ -112,7 +123,7 @@ class Contents { const std::string& label = "Snapshot") const; virtual bool ShouldRender(const Entity& entity, - const std::optional& stencil_coverage) const; + const std::optional& clip_coverage) const; //---------------------------------------------------------------------------- /// @brief Return the color source's intrinsic size, if available. @@ -155,6 +166,12 @@ class Contents { virtual std::optional AsBackgroundColor(const Entity& entity, ISize target_size) const; + //---------------------------------------------------------------------------- + /// @brief Cast to a filter. Returns `nullptr` if this Contents is not a + /// filter. + /// + virtual const FilterContents* AsFilter() const; + //---------------------------------------------------------------------------- /// @brief If possible, applies a color filter to this contents inputs on /// the CPU. diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 3c1b8617612f2..225f915f9aa5e 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -13,6 +13,7 @@ #include "impeller/entity/contents/anonymous_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/entity.h" @@ -76,7 +77,7 @@ static std::optional AdvancedBlend( const Rect& coverage, BlendMode blend_mode, std::optional foreground_color, - bool absorb_opacity, + ColorFilterContents::AbsorbOpacity absorb_opacity, PipelineProc pipeline_proc, std::optional alpha) { using VS = typename TPipeline::VertexShader; @@ -113,7 +114,7 @@ static std::optional AdvancedBlend( return std::nullopt; } return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(), - entity.GetStencilDepth()); + entity.GetClipDepth()); } auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage); if (!maybe_src_uvs.has_value()) { @@ -121,7 +122,7 @@ static std::optional AdvancedBlend( return std::nullopt; } return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(), - entity.GetStencilDepth()); + entity.GetClipDepth()); } src_uvs = maybe_src_uvs.value(); } @@ -154,14 +155,13 @@ static std::optional AdvancedBlend( vtx_builder.AddVertices({ {Point(0, 0), dst_uvs[0], src_uvs[0]}, {Point(size.width, 0), dst_uvs[1], src_uvs[1]}, - {Point(size.width, size.height), dst_uvs[3], src_uvs[3]}, - {Point(0, 0), dst_uvs[0], src_uvs[0]}, - {Point(size.width, size.height), dst_uvs[3], src_uvs[3]}, {Point(0, size.height), dst_uvs[2], src_uvs[2]}, + {Point(size.width, size.height), dst_uvs[3], src_uvs[3]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); auto options = OptionsFromPass(pass); + options.primitive_type = PrimitiveType::kTriangleStrip; options.blend_mode = BlendMode::kSource; std::shared_ptr> pipeline = std::invoke(pipeline_proc, renderer, options); @@ -180,11 +180,16 @@ static std::optional AdvancedBlend( dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; } + blend_info.supports_decal_sampler_address_mode = + renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode(); auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( dst_sampler_descriptor); FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler); frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale(); - blend_info.dst_input_alpha = absorb_opacity ? dst_snapshot->opacity : 1.0; + blend_info.dst_input_alpha = + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? dst_snapshot->opacity + : 1.0; if (foreground_color.has_value()) { blend_info.color_factor = 1; @@ -227,15 +232,18 @@ static std::optional AdvancedBlend( } return Entity::FromSnapshot( - Snapshot{.texture = out_texture, - .transform = Matrix::MakeTranslation(subpass_coverage.origin), - // Since we absorbed the transform of the inputs and used the - // respective snapshot sampling modes when blending, pass on - // the default NN clamp sampler. - .sampler_descriptor = {}, - .opacity = (absorb_opacity ? 1.0f : dst_snapshot->opacity) * - alpha.value_or(1.0)}, - entity.GetBlendMode(), entity.GetStencilDepth()); + Snapshot{ + .texture = out_texture, + .transform = Matrix::MakeTranslation(subpass_coverage.origin), + // Since we absorbed the transform of the inputs and used the + // respective snapshot sampling modes when blending, pass on + // the default NN clamp sampler. + .sampler_descriptor = {}, + .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? 1.0f + : dst_snapshot->opacity) * + alpha.value_or(1.0)}, + entity.GetBlendMode(), entity.GetClipDepth()); } std::optional BlendFilterContents::CreateForegroundAdvancedBlend( @@ -246,7 +254,7 @@ std::optional BlendFilterContents::CreateForegroundAdvancedBlend( Color foreground_color, BlendMode blend_mode, std::optional alpha, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { auto dst_snapshot = input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity); if (!dst_snapshot.has_value()) { @@ -274,12 +282,9 @@ std::optional BlendFilterContents::CreateForegroundAdvancedBlend( vtx_builder.AddVertices({ {origin, dst_uvs[0], dst_uvs[0]}, {Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]}, + {Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]}, {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3], dst_uvs[3]}, - {origin, dst_uvs[0], dst_uvs[0]}, - {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3], - dst_uvs[3]}, - {Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); @@ -287,8 +292,9 @@ std::optional BlendFilterContents::CreateForegroundAdvancedBlend( DEBUG_COMMAND_INFO(cmd, SPrintF("Foreground Advanced Blend Filter (%s)", BlendModeToString(blend_mode))); cmd.BindVertices(vtx_buffer); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPass(pass); + options.primitive_type = PrimitiveType::kTriangleStrip; switch (blend_mode) { case BlendMode::kScreen: @@ -348,12 +354,16 @@ std::optional BlendFilterContents::CreateForegroundAdvancedBlend( dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; } + blend_info.supports_decal_sampler_address_mode = + renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode(); auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( dst_sampler_descriptor); FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler); frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale(); blend_info.dst_input_alpha = - absorb_opacity ? dst_snapshot->opacity * alpha.value_or(1.0) : 1.0; + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? dst_snapshot->opacity * alpha.value_or(1.0) + : 1.0; blend_info.color_factor = 1; blend_info.color = foreground_color; @@ -382,7 +392,7 @@ std::optional BlendFilterContents::CreateForegroundAdvancedBlend( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); return sub_entity; } @@ -395,7 +405,7 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( Color foreground_color, BlendMode blend_mode, std::optional alpha, - bool absorb_opacity) const { + ColorFilterContents::AbsorbOpacity absorb_opacity) const { if (blend_mode == BlendMode::kClear) { return std::nullopt; } @@ -407,7 +417,7 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( Entity foreground_entity; foreground_entity.SetBlendMode(entity.GetBlendMode()); - foreground_entity.SetStencilDepth(entity.GetStencilDepth()); + foreground_entity.SetClipDepth(entity.GetClipDepth()); foreground_entity.SetContents(std::move(contents)); return foreground_entity; } @@ -420,7 +430,7 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( if (blend_mode == BlendMode::kDestination) { return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(), - entity.GetStencilDepth()); + entity.GetClipDepth()); } RenderProc render_proc = [foreground_color, coverage, dst_snapshot, @@ -445,12 +455,9 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( vtx_builder.AddVertices({ {origin, dst_uvs[0], color}, {Point(origin.x + size.width, origin.y), dst_uvs[1], color}, + {Point(origin.x, origin.y + size.height), dst_uvs[2], color}, {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3], color}, - {origin, dst_uvs[0], color}, - {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3], - color}, - {Point(origin.x, origin.y + size.height), dst_uvs[2], color}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); @@ -458,8 +465,9 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( DEBUG_COMMAND_INFO(cmd, SPrintF("Foreground PorterDuff Blend Filter (%s)", BlendModeToString(blend_mode))); cmd.BindVertices(vtx_buffer); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPass(pass); + options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options); FS::FragInfo frag_info; @@ -470,6 +478,8 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; } + frag_info.supports_decal_sampler_address_mode = + renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode(); auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( dst_sampler_descriptor); FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler); @@ -477,7 +487,9 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( dst_snapshot->texture->GetYCoordScale(); frag_info.input_alpha = - absorb_opacity ? dst_snapshot->opacity * alpha.value_or(1.0) : 1.0; + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? dst_snapshot->opacity * alpha.value_or(1.0) + : 1.0; frag_info.output_alpha = 1.0; auto blend_coefficients = @@ -508,7 +520,7 @@ std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); return sub_entity; } @@ -520,7 +532,7 @@ static std::optional PipelineBlend( const Rect& coverage, BlendMode blend_mode, std::optional foreground_color, - bool absorb_opacity, + ColorFilterContents::AbsorbOpacity absorb_opacity, std::optional alpha) { using VS = BlendPipeline::VertexShader; using FS = BlendPipeline::FragmentShader; @@ -554,6 +566,7 @@ static std::optional PipelineBlend( DEBUG_COMMAND_INFO(cmd, SPrintF("Pipeline Blend Filter (%s)", BlendModeToString(blend_mode))); auto options = OptionsFromPass(pass); + options.primitive_type = PrimitiveType::kTriangleStrip; auto add_blend_command = [&](std::optional input) { if (!input.has_value()) { @@ -573,10 +586,8 @@ static std::optional PipelineBlend( vtx_builder.AddVertices({ {Point(0, 0), Point(0, 0)}, {Point(size.width, 0), Point(1, 0)}, - {Point(size.width, size.height), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(size.width, size.height), Point(1, 1)}, {Point(0, size.height), Point(0, 1)}, + {Point(size.width, size.height), Point(1, 1)}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); cmd.BindVertices(vtx_buffer); @@ -589,7 +600,10 @@ static std::optional PipelineBlend( input->texture->GetYCoordScale(); FS::FragInfo frag_info; - frag_info.input_alpha = absorb_opacity ? input->opacity : 1.0; + frag_info.input_alpha = + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? input->opacity + : 1.0; FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); @@ -647,15 +661,18 @@ static std::optional PipelineBlend( } return Entity::FromSnapshot( - Snapshot{.texture = out_texture, - .transform = Matrix::MakeTranslation(subpass_coverage.origin), - // Since we absorbed the transform of the inputs and used the - // respective snapshot sampling modes when blending, pass on - // the default NN clamp sampler. - .sampler_descriptor = {}, - .opacity = (absorb_opacity ? 1.0f : dst_snapshot->opacity) * - alpha.value_or(1.0)}, - entity.GetBlendMode(), entity.GetStencilDepth()); + Snapshot{ + .texture = out_texture, + .transform = Matrix::MakeTranslation(subpass_coverage.origin), + // Since we absorbed the transform of the inputs and used the + // respective snapshot sampling modes when blending, pass on + // the default NN clamp sampler. + .sampler_descriptor = {}, + .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? 1.0f + : dst_snapshot->opacity) * + alpha.value_or(1.0)}, + entity.GetBlendMode(), entity.GetClipDepth()); } #define BLEND_CASE(mode) \ @@ -663,7 +680,8 @@ static std::optional PipelineBlend( advanced_blend_proc_ = \ [](const FilterInput::Vector& inputs, const ContentContext& renderer, \ const Entity& entity, const Rect& coverage, BlendMode blend_mode, \ - std::optional fg_color, bool absorb_opacity, \ + std::optional fg_color, \ + ColorFilterContents::AbsorbOpacity absorb_opacity, \ std::optional alpha) { \ PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \ return AdvancedBlend( \ @@ -726,7 +744,7 @@ std::optional BlendFilterContents::RenderFilter( if (blend_mode_ <= Entity::kLastPipelineBlendMode) { if (inputs.size() == 1 && foreground_color_.has_value() && - GetAbsorbOpacity()) { + GetAbsorbOpacity() == ColorFilterContents::AbsorbOpacity::kYes) { return CreateForegroundPorterDuffBlend( inputs[0], renderer, entity, coverage, foreground_color_.value(), blend_mode_, GetAlpha(), GetAbsorbOpacity()); @@ -737,7 +755,7 @@ std::optional BlendFilterContents::RenderFilter( if (blend_mode_ <= Entity::kLastAdvancedBlendMode) { if (inputs.size() == 1 && foreground_color_.has_value() && - GetAbsorbOpacity()) { + GetAbsorbOpacity() == ColorFilterContents::AbsorbOpacity::kYes) { return CreateForegroundAdvancedBlend( inputs[0], renderer, entity, coverage, foreground_color_.value(), blend_mode_, GetAlpha(), GetAbsorbOpacity()); diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index f279f7f926bd8..a0e49d2adabee 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -33,15 +33,15 @@ std::optional InvertPorterDuffBlend(BlendMode blend_mode); class BlendFilterContents : public ColorFilterContents { public: - using AdvancedBlendProc = - std::function(const FilterInput::Vector& inputs, - const ContentContext& renderer, - const Entity& entity, - const Rect& coverage, - BlendMode blend_mode, - std::optional foreground_color, - bool absorb_opacity, - std::optional alpha)>; + using AdvancedBlendProc = std::function( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + const Rect& coverage, + BlendMode blend_mode, + std::optional foreground_color, + ColorFilterContents::AbsorbOpacity absorb_opacity, + std::optional alpha)>; BlendFilterContents(); @@ -75,7 +75,7 @@ class BlendFilterContents : public ColorFilterContents { Color foreground_color, BlendMode blend_mode, std::optional alpha, - bool absorb_opacity) const; + ColorFilterContents::AbsorbOpacity absorb_opacity) const; /// @brief Optimized porter-duff blend that avoids a second subpass when there /// is only a single input and a foreground color. @@ -89,7 +89,7 @@ class BlendFilterContents : public ColorFilterContents { Color foreground_color, BlendMode blend_mode, std::optional alpha, - bool absorb_opacity) const; + ColorFilterContents::AbsorbOpacity absorb_opacity) const; BlendMode blend_mode_ = BlendMode::kSourceOver; AdvancedBlendProc advanced_blend_proc_; diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc index 2da237445980b..3da610b90bd3f 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc @@ -96,25 +96,22 @@ std::optional BorderMaskBlurFilterContents::RenderFilter( {coverage.origin, input_uvs[0]}, {{coverage.origin.x + coverage.size.width, coverage.origin.y}, input_uvs[1]}, + {{coverage.origin.x, coverage.origin.y + coverage.size.height}, + input_uvs[2]}, {{coverage.origin.x + coverage.size.width, coverage.origin.y + coverage.size.height}, input_uvs[3]}, - {coverage.origin, input_uvs[0]}, - {{coverage.origin.x + coverage.size.width, - coverage.origin.y + coverage.size.height}, - input_uvs[3]}, - {{coverage.origin.x, coverage.origin.y + coverage.size.height}, - input_uvs[2]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); Command cmd; DEBUG_COMMAND_INFO(cmd, "Border Mask Blur Filter"); auto options = OptionsFromPassAndEntity(pass, entity); + options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetBorderMaskBlurPipeline(options); cmd.BindVertices(vtx_buffer); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * @@ -146,7 +143,7 @@ std::optional BorderMaskBlurFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); sub_entity.SetBlendMode(entity.GetBlendMode()); return sub_entity; } diff --git a/impeller/entity/contents/filters/color_filter_contents.cc b/impeller/entity/contents/filters/color_filter_contents.cc index 51de176698bba..f5a36456a451f 100644 --- a/impeller/entity/contents/filters/color_filter_contents.cc +++ b/impeller/entity/contents/filters/color_filter_contents.cc @@ -82,11 +82,12 @@ ColorFilterContents::ColorFilterContents() = default; ColorFilterContents::~ColorFilterContents() = default; -void ColorFilterContents::SetAbsorbOpacity(bool absorb_opacity) { +void ColorFilterContents::SetAbsorbOpacity(AbsorbOpacity absorb_opacity) { absorb_opacity_ = absorb_opacity; } -bool ColorFilterContents::GetAbsorbOpacity() const { +ColorFilterContents::AbsorbOpacity ColorFilterContents::GetAbsorbOpacity() + const { return absorb_opacity_; } diff --git a/impeller/entity/contents/filters/color_filter_contents.h b/impeller/entity/contents/filters/color_filter_contents.h index 0099c16a6c282..f09b2d7287b80 100644 --- a/impeller/entity/contents/filters/color_filter_contents.h +++ b/impeller/entity/contents/filters/color_filter_contents.h @@ -10,6 +10,11 @@ namespace impeller { class ColorFilterContents : public FilterContents { public: + enum class AbsorbOpacity { + kYes, + kNo, + }; + /// @brief the [inputs] are expected to be in the order of dst, src. static std::shared_ptr MakeBlend( BlendMode blend_mode, @@ -30,9 +35,9 @@ class ColorFilterContents : public FilterContents { ~ColorFilterContents() override; - void SetAbsorbOpacity(bool absorb_opacity); + void SetAbsorbOpacity(AbsorbOpacity absorb_opacity); - bool GetAbsorbOpacity() const; + AbsorbOpacity GetAbsorbOpacity() const; /// @brief Sets an alpha that is applied to the final blended result. void SetAlpha(Scalar alpha); @@ -40,7 +45,7 @@ class ColorFilterContents : public FilterContents { std::optional GetAlpha() const; private: - bool absorb_opacity_ = false; + AbsorbOpacity absorb_opacity_ = AbsorbOpacity::kNo; std::optional alpha_; FML_DISALLOW_COPY_AND_ASSIGN(ColorFilterContents); diff --git a/impeller/entity/contents/filters/color_matrix_filter_contents.cc b/impeller/entity/contents/filters/color_matrix_filter_contents.cc index 24d0389bb0947..d9878896cd903 100644 --- a/impeller/entity/contents/filters/color_matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/color_matrix_filter_contents.cc @@ -9,6 +9,7 @@ #include "impeller/entity/contents/anonymous_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/geometry/point.h" #include "impeller/geometry/vector.h" #include "impeller/renderer/render_pass.h" @@ -56,9 +57,10 @@ std::optional ColorMatrixFilterContents::RenderFilter( const Entity& entity, RenderPass& pass) -> bool { Command cmd; DEBUG_COMMAND_INFO(cmd, "Color Matrix Filter"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); + options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetColorMatrixColorFilterPipeline(options); auto size = input_snapshot->texture->GetSize(); @@ -67,10 +69,8 @@ std::optional ColorMatrixFilterContents::RenderFilter( vtx_builder.AddVertices({ {Point(0, 0)}, {Point(1, 0)}, - {Point(1, 1)}, - {Point(0, 0)}, - {Point(1, 1)}, {Point(0, 1)}, + {Point(1, 1)}, }); auto& host_buffer = pass.GetTransientsBuffer(); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); @@ -94,7 +94,10 @@ std::optional ColorMatrixFilterContents::RenderFilter( matrix[3], matrix[8], matrix[13], matrix[18] ); // clang-format on - frag_info.input_alpha = absorb_opacity ? input_snapshot->opacity : 1.0f; + frag_info.input_alpha = + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? input_snapshot->opacity + : 1.0f; auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); FS::BindInputTexture(cmd, input_snapshot->texture, sampler); FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); @@ -113,7 +116,7 @@ std::optional ColorMatrixFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); sub_entity.SetBlendMode(entity.GetBlendMode()); return sub_entity; } diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index ae48cec892ddc..57653b10b739f 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -259,6 +259,10 @@ std::optional FilterContents::RenderToSnapshot( return std::nullopt; } +const FilterContents* FilterContents::AsFilter() const { + return this; +} + Matrix FilterContents::GetLocalTransform(const Matrix& parent_transform) const { return Matrix(); } @@ -266,6 +270,14 @@ Matrix FilterContents::GetLocalTransform(const Matrix& parent_transform) const { Matrix FilterContents::GetTransform(const Matrix& parent_transform) const { return parent_transform * GetLocalTransform(parent_transform); } +bool FilterContents::IsTranslationOnly() const { + for (auto& input : inputs_) { + if (!input->IsTranslationOnly()) { + return false; + } + } + return true; +} bool FilterContents::IsLeaf() const { for (auto& input : inputs_) { @@ -286,9 +298,9 @@ void FilterContents::SetLeafInputs(const FilterInput::Vector& inputs) { } } -void FilterContents::SetIsForSubpass(bool is_subpass) { +void FilterContents::SetRenderingMode(Entity::RenderingMode rendering_mode) { for (auto& input : inputs_) { - input->SetIsForSubpass(is_subpass); + input->SetRenderingMode(rendering_mode); } } diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index e5e7604434821..fb1c6b59f1cb3 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -125,10 +125,23 @@ class FilterContents : public Contents { bool msaa_enabled = true, const std::string& label = "Filter Snapshot") const override; + // |Contents| + const FilterContents* AsFilter() const override; + virtual Matrix GetLocalTransform(const Matrix& parent_transform) const; Matrix GetTransform(const Matrix& parent_transform) const; + /// @brief Returns true if this filter graph doesn't perform any basis + /// transformations to the filtered content. For example: Rotating, + /// scaling, and skewing are all basis transformations, but + /// translating is not. + /// + /// This is useful for determining whether a filtered object's space + /// is compatible enough with the parent pass space to perform certain + /// subpass clipping optimizations. + virtual bool IsTranslationOnly() const; + /// @brief Returns `true` if this filter does not have any `FilterInput` /// children. bool IsLeaf() const; @@ -144,7 +157,7 @@ class FilterContents : public Contents { /// that the current transformation matrix of the entity is not stored /// in the Entity transformation matrix. Instead, the effect transform /// is used in this case. - virtual void SetIsForSubpass(bool is_subpass); + virtual void SetRenderingMode(Entity::RenderingMode rendering_mode); private: virtual std::optional GetFilterCoverage( diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 8e3c9569305bf..bd954d821046a 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -26,8 +26,8 @@ namespace impeller { DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = default; -DirectionalGaussianBlurFilterContents:: - ~DirectionalGaussianBlurFilterContents() = default; +DirectionalGaussianBlurFilterContents::~ +DirectionalGaussianBlurFilterContents() = default; void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) { blur_sigma_ = sigma; @@ -104,17 +104,15 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( } if (blur_sigma_.sigma < kEhCloseEnough) { - return Entity::FromSnapshot( - input_snapshot.value(), entity.GetBlendMode(), - entity.GetStencilDepth()); // No blur to render. + return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(), + entity.GetClipDepth()); // No blur to render. } // If the radius length is < .5, the shader will take at most 1 sample, // resulting in no blur. if (transformed_blur_radius_length < .5) { - return Entity::FromSnapshot( - input_snapshot.value(), entity.GetBlendMode(), - entity.GetStencilDepth()); // No blur to render. + return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(), + entity.GetClipDepth()); // No blur to render. } // A matrix that rotates the snapshot space such that the blur direction is @@ -159,10 +157,8 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( vtx_builder.AddVertices({ {Point(0, 0), input_uvs[0]}, {Point(1, 0), input_uvs[1]}, - {Point(1, 1), input_uvs[3]}, - {Point(0, 0), input_uvs[0]}, - {Point(1, 1), input_uvs[3]}, {Point(0, 1), input_uvs[2]}, + {Point(1, 1), input_uvs[3]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); @@ -187,6 +183,7 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( cmd.BindVertices(vtx_buffer); auto options = OptionsFromPass(pass); + options.primitive_type = PrimitiveType::kTriangleStrip; options.blend_mode = BlendMode::kSource; auto input_descriptor = input_snapshot->sampler_descriptor; switch (tile_mode_) { @@ -273,7 +270,7 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( (scaled_size / floored_size)), .sampler_descriptor = sampler_desc, .opacity = input_snapshot->opacity}, - entity.GetBlendMode(), entity.GetStencilDepth()); + entity.GetBlendMode(), entity.GetClipDepth()); } std::optional DirectionalGaussianBlurFilterContents::GetFilterCoverage( diff --git a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc index e1af31f8bede4..3e43132c1320d 100644 --- a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc +++ b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc @@ -58,6 +58,10 @@ void FilterContentsFilterInput::PopulateGlyphAtlas( filter_->PopulateGlyphAtlas(lazy_glyph_atlas, scale); } +bool FilterContentsFilterInput::IsTranslationOnly() const { + return filter_->IsTranslationOnly(); +} + bool FilterContentsFilterInput::IsLeaf() const { return false; } @@ -71,8 +75,9 @@ void FilterContentsFilterInput::SetEffectTransform(const Matrix& matrix) { filter_->SetEffectTransform(matrix); } -void FilterContentsFilterInput::SetIsForSubpass(bool is_for_subpass) { - filter_->SetIsForSubpass(is_for_subpass); +void FilterContentsFilterInput::SetRenderingMode( + Entity::RenderingMode rendering_mode) { + filter_->SetRenderingMode(rendering_mode); } } // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h index 7aaaa3aebdd7a..e21cb7acc451c 100644 --- a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h +++ b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h @@ -36,6 +36,9 @@ class FilterContentsFilterInput final : public FilterInput { const std::shared_ptr& lazy_glyph_atlas, Scalar scale) override; + // |FilterInput| + bool IsTranslationOnly() const override; + // |FilterInput| bool IsLeaf() const override; @@ -46,7 +49,7 @@ class FilterContentsFilterInput final : public FilterInput { virtual void SetEffectTransform(const Matrix& matrix) override; // |FilterInput| - virtual void SetIsForSubpass(bool is_for_subpass) override; + virtual void SetRenderingMode(Entity::RenderingMode rendering_mode) override; private: explicit FilterContentsFilterInput(std::shared_ptr filter); diff --git a/impeller/entity/contents/filters/inputs/filter_input.cc b/impeller/entity/contents/filters/inputs/filter_input.cc index 1cb4744468c20..b3ecc69895b92 100644 --- a/impeller/entity/contents/filters/inputs/filter_input.cc +++ b/impeller/entity/contents/filters/inputs/filter_input.cc @@ -76,6 +76,10 @@ void FilterInput::PopulateGlyphAtlas( FilterInput::~FilterInput() = default; +bool FilterInput::IsTranslationOnly() const { + return true; +} + bool FilterInput::IsLeaf() const { return true; } @@ -84,6 +88,6 @@ void FilterInput::SetLeafInputs(const FilterInput::Vector& inputs) {} void FilterInput::SetEffectTransform(const Matrix& matrix) {} -void FilterInput::SetIsForSubpass(bool is_for_subpass) {} +void FilterInput::SetRenderingMode(Entity::RenderingMode rendering_mode) {} } // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_input.h b/impeller/entity/contents/filters/inputs/filter_input.h index 598ca1b560cca..955932885ea07 100644 --- a/impeller/entity/contents/filters/inputs/filter_input.h +++ b/impeller/entity/contents/filters/inputs/filter_input.h @@ -69,6 +69,9 @@ class FilterInput { const std::shared_ptr& lazy_glyph_atlas, Scalar scale); + /// @see `FilterContents::HasBasisTransformations` + virtual bool IsTranslationOnly() const; + /// @brief Returns `true` unless this input is a `FilterInput`, which may /// take other inputs. virtual bool IsLeaf() const; @@ -82,7 +85,7 @@ class FilterInput { virtual void SetEffectTransform(const Matrix& matrix); /// @brief Turns on subpass mode for filter inputs. - virtual void SetIsForSubpass(bool is_for_subpass); + virtual void SetRenderingMode(Entity::RenderingMode rendering_mode); }; } // namespace impeller diff --git a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc index e75c269105f88..8b42813e26c69 100644 --- a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc +++ b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc @@ -47,9 +47,10 @@ std::optional LinearToSrgbFilterContents::RenderFilter( const Entity& entity, RenderPass& pass) -> bool { Command cmd; DEBUG_COMMAND_INFO(cmd, "Linear to sRGB Filter"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); + options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetLinearToSrgbFilterPipeline(options); auto size = input_snapshot->texture->GetSize(); @@ -58,10 +59,8 @@ std::optional LinearToSrgbFilterContents::RenderFilter( vtx_builder.AddVertices({ {Point(0, 0)}, {Point(1, 0)}, - {Point(1, 1)}, - {Point(0, 0)}, - {Point(1, 1)}, {Point(0, 1)}, + {Point(1, 1)}, }); auto& host_buffer = pass.GetTransientsBuffer(); @@ -76,7 +75,10 @@ std::optional LinearToSrgbFilterContents::RenderFilter( input_snapshot->texture->GetYCoordScale(); FS::FragInfo frag_info; - frag_info.input_alpha = absorb_opacity ? input_snapshot->opacity : 1.0f; + frag_info.input_alpha = + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? input_snapshot->opacity + : 1.0f; auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); FS::BindInputTexture(cmd, input_snapshot->texture, sampler); @@ -95,7 +97,7 @@ std::optional LinearToSrgbFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); sub_entity.SetBlendMode(entity.GetBlendMode()); return sub_entity; } diff --git a/impeller/entity/contents/filters/local_matrix_filter_contents.cc b/impeller/entity/contents/filters/local_matrix_filter_contents.cc index 709af709fc739..7f96fd1c7ba1e 100644 --- a/impeller/entity/contents/filters/local_matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/local_matrix_filter_contents.cc @@ -28,7 +28,7 @@ std::optional LocalMatrixFilterContents::RenderFilter( const std::optional& coverage_hint) const { return Entity::FromSnapshot( inputs[0]->GetSnapshot("LocalMatrix", renderer, entity), - entity.GetBlendMode(), entity.GetStencilDepth()); + entity.GetBlendMode(), entity.GetClipDepth()); } } // namespace impeller diff --git a/impeller/entity/contents/filters/matrix_filter_contents.cc b/impeller/entity/contents/filters/matrix_filter_contents.cc index 5483b4d7d306b..c16bcdb88d698 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/matrix_filter_contents.cc @@ -14,9 +14,14 @@ void MatrixFilterContents::SetMatrix(Matrix matrix) { matrix_ = matrix; } -void MatrixFilterContents::SetIsForSubpass(bool is_subpass) { - is_for_subpass_ = is_subpass; - FilterContents::SetIsForSubpass(is_subpass); +void MatrixFilterContents::SetRenderingMode( + Entity::RenderingMode rendering_mode) { + rendering_mode_ = rendering_mode; + FilterContents::SetRenderingMode(rendering_mode); +} + +bool MatrixFilterContents::IsTranslationOnly() const { + return matrix_.Basis().IsIdentity() && FilterContents::IsTranslationOnly(); } void MatrixFilterContents::SetSamplerDescriptor(SamplerDescriptor desc) { @@ -40,7 +45,7 @@ std::optional MatrixFilterContents::RenderFilter( // scaled up, then translations applied by the matrix should be magnified // accordingly. // - // To accomplish this, we sandwitch the filter's matrix within the CTM in both + // To accomplish this, we sandwich the filter's matrix within the CTM in both // cases. But notice that for the subpass backdrop filter case, we use the // "effect transform" instead of the Entity's transform! // @@ -50,8 +55,9 @@ std::optional MatrixFilterContents::RenderFilter( // mentioned above). And so we sneak the subpass's captured CTM in through the // effect transform. - auto transform = - is_for_subpass_ ? effect_transform : entity.GetTransformation(); + auto transform = rendering_mode_ == Entity::RenderingMode::kSubpass + ? effect_transform + : entity.GetTransformation(); snapshot->transform = transform * // matrix_ * // transform.Invert() * // @@ -59,7 +65,7 @@ std::optional MatrixFilterContents::RenderFilter( snapshot->sampler_descriptor = sampler_descriptor_; return Entity::FromSnapshot(snapshot, entity.GetBlendMode(), - entity.GetStencilDepth()); + entity.GetClipDepth()); } std::optional MatrixFilterContents::GetFilterCoverage( @@ -74,8 +80,9 @@ std::optional MatrixFilterContents::GetFilterCoverage( if (!coverage.has_value()) { return std::nullopt; } - auto& m = - is_for_subpass_ ? effect_transform : inputs[0]->GetTransform(entity); + auto& m = rendering_mode_ == Entity::RenderingMode::kSubpass + ? effect_transform + : inputs[0]->GetTransform(entity); auto transform = m * // matrix_ * // m.Invert(); // diff --git a/impeller/entity/contents/filters/matrix_filter_contents.h b/impeller/entity/contents/filters/matrix_filter_contents.h index a2e4c5f239953..9f2cb9d0d1774 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.h +++ b/impeller/entity/contents/filters/matrix_filter_contents.h @@ -18,7 +18,10 @@ class MatrixFilterContents final : public FilterContents { void SetMatrix(Matrix matrix); // |FilterContents| - void SetIsForSubpass(bool is_for_subpass) override; + void SetRenderingMode(Entity::RenderingMode rendering_mode) override; + + // |FilterContents| + bool IsTranslationOnly() const override; void SetSamplerDescriptor(SamplerDescriptor desc); @@ -40,7 +43,7 @@ class MatrixFilterContents final : public FilterContents { Matrix matrix_; SamplerDescriptor sampler_descriptor_ = {}; - bool is_for_subpass_ = false; + Entity::RenderingMode rendering_mode_ = Entity::RenderingMode::kDirect; FML_DISALLOW_COPY_AND_ASSIGN(MatrixFilterContents); }; diff --git a/impeller/entity/contents/filters/morphology_filter_contents.cc b/impeller/entity/contents/filters/morphology_filter_contents.cc index 4fd83270c3df8..bdbe3266c4088 100644 --- a/impeller/entity/contents/filters/morphology_filter_contents.cc +++ b/impeller/entity/contents/filters/morphology_filter_contents.cc @@ -6,6 +6,7 @@ #include +#include "impeller/core/formats.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/contents.h" #include "impeller/renderer/render_pass.h" @@ -59,7 +60,7 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( if (radius_.radius < kEhCloseEnough) { return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(), - entity.GetStencilDepth()); + entity.GetClipDepth()); } auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage); @@ -80,10 +81,8 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( vtx_builder.AddVertices({ {Point(0, 0), input_uvs[0]}, {Point(1, 0), input_uvs[1]}, - {Point(1, 1), input_uvs[3]}, - {Point(0, 0), input_uvs[0]}, - {Point(1, 1), input_uvs[3]}, {Point(0, 1), input_uvs[2]}, + {Point(1, 1), input_uvs[3]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); @@ -118,6 +117,7 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( Command cmd; DEBUG_COMMAND_INFO(cmd, "Morphology Filter"); auto options = OptionsFromPass(pass); + options.primitive_type = PrimitiveType::kTriangleStrip; options.blend_mode = BlendMode::kSource; cmd.pipeline = renderer.GetMorphologyFilterPipeline(options); cmd.BindVertices(vtx_buffer); @@ -127,6 +127,8 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; } + frag_info.supports_decal_sampler_address_mode = + renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode(); FS::BindTextureSampler( cmd, input_snapshot->texture, @@ -153,7 +155,7 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( .transform = Matrix::MakeTranslation(coverage.origin), .sampler_descriptor = sampler_desc, .opacity = input_snapshot->opacity}, - entity.GetBlendMode(), entity.GetStencilDepth()); + entity.GetBlendMode(), entity.GetClipDepth()); } std::optional DirectionalMorphologyFilterContents::GetFilterCoverage( diff --git a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc index 84568a0a2f25f..2d47c79cad31b 100644 --- a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc +++ b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc @@ -47,9 +47,10 @@ std::optional SrgbToLinearFilterContents::RenderFilter( const Entity& entity, RenderPass& pass) -> bool { Command cmd; DEBUG_COMMAND_INFO(cmd, "sRGB to Linear Filter"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); + options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetSrgbToLinearFilterPipeline(options); auto size = input_snapshot->texture->GetSize(); @@ -58,10 +59,8 @@ std::optional SrgbToLinearFilterContents::RenderFilter( vtx_builder.AddVertices({ {Point(0, 0)}, {Point(1, 0)}, - {Point(1, 1)}, - {Point(0, 0)}, - {Point(1, 1)}, {Point(0, 1)}, + {Point(1, 1)}, }); auto& host_buffer = pass.GetTransientsBuffer(); @@ -76,7 +75,10 @@ std::optional SrgbToLinearFilterContents::RenderFilter( input_snapshot->texture->GetYCoordScale(); FS::FragInfo frag_info; - frag_info.input_alpha = absorb_opacity ? input_snapshot->opacity : 1.0f; + frag_info.input_alpha = + absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes + ? input_snapshot->opacity + : 1.0f; auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); FS::BindInputTexture(cmd, input_snapshot->texture, sampler); @@ -95,7 +97,7 @@ std::optional SrgbToLinearFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); sub_entity.SetBlendMode(entity.GetBlendMode()); return sub_entity; } diff --git a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc index 5b944ca68e023..29098fb37bb5c 100644 --- a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc +++ b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc @@ -75,9 +75,10 @@ std::optional YUVToRGBFilterContents::RenderFilter( const Entity& entity, RenderPass& pass) -> bool { Command cmd; DEBUG_COMMAND_INFO(cmd, "YUV to RGB Filter"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); + options.primitive_type = PrimitiveType::kTriangleStrip; cmd.pipeline = renderer.GetYUVToRGBFilterPipeline(options); auto size = y_input_snapshot->texture->GetSize(); @@ -86,10 +87,8 @@ std::optional YUVToRGBFilterContents::RenderFilter( vtx_builder.AddVertices({ {Point(0, 0)}, {Point(1, 0)}, - {Point(1, 1)}, - {Point(0, 0)}, - {Point(1, 1)}, {Point(0, 1)}, + {Point(1, 1)}, }); auto& host_buffer = pass.GetTransientsBuffer(); @@ -133,7 +132,7 @@ std::optional YUVToRGBFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); - sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetClipDepth(entity.GetClipDepth()); sub_entity.SetBlendMode(entity.GetBlendMode()); return sub_entity; } diff --git a/impeller/entity/contents/framebuffer_blend_contents.cc b/impeller/entity/contents/framebuffer_blend_contents.cc index 96b915ee4ac23..7edc478076499 100644 --- a/impeller/entity/contents/framebuffer_blend_contents.cc +++ b/impeller/entity/contents/framebuffer_blend_contents.cc @@ -48,6 +48,7 @@ bool FramebufferBlendContents::Render(const ContentContext& renderer, std::nullopt, // sampler_descriptor true, // msaa_enabled "FramebufferBlendContents Snapshot"); // label + if (!src_snapshot.has_value()) { return true; } @@ -56,31 +57,25 @@ bool FramebufferBlendContents::Render(const ContentContext& renderer, return true; } Rect src_coverage = coverage.value(); - auto maybe_src_uvs = src_snapshot->GetCoverageUVs(src_coverage); - if (!maybe_src_uvs.has_value()) { - return true; - } - std::array src_uvs = maybe_src_uvs.value(); auto size = src_coverage.size; VertexBufferBuilder vtx_builder; vtx_builder.AddVertices({ - {Point(0, 0), src_uvs[0]}, - {Point(size.width, 0), src_uvs[1]}, - {Point(size.width, size.height), src_uvs[3]}, - {Point(0, 0), src_uvs[0]}, - {Point(size.width, size.height), src_uvs[3]}, - {Point(0, size.height), src_uvs[2]}, + {Point(0, 0), Point(0, 0)}, + {Point(size.width, 0), Point(1, 0)}, + {Point(0, size.height), Point(0, 1)}, + {Point(size.width, size.height), Point(1, 1)}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); auto options = OptionsFromPass(pass); options.blend_mode = BlendMode::kSource; + options.primitive_type = PrimitiveType::kTriangleStrip; Command cmd; DEBUG_COMMAND_INFO(cmd, "Framebuffer Advanced Blend Filter"); cmd.BindVertices(vtx_buffer); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); switch (blend_mode_) { case BlendMode::kScreen: @@ -136,12 +131,10 @@ bool FramebufferBlendContents::Render(const ContentContext& renderer, FS::FragInfo frag_info; auto src_sampler_descriptor = src_snapshot->sampler_descriptor; - if (!renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) { - // No known devices that support framebuffer fetch but not decal tile mode. - return false; + if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) { + src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; + src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; } - src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; - src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; auto src_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( src_sampler_descriptor); FS::BindTextureSamplerSrc(cmd, src_snapshot->texture, src_sampler); diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index b35ee0d9084e8..4432494cd97e9 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -101,7 +101,7 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "LinearGradientFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); if (geometry_result.prevent_overdraw) { @@ -162,7 +162,7 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "LinearGradientSSBOFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto geometry_result = GetGeometry()->GetPositionBuffer(renderer, entity, pass); diff --git a/impeller/entity/contents/radial_gradient_contents.cc b/impeller/entity/contents/radial_gradient_contents.cc index 86cc1d616730d..1513aa5652d66 100644 --- a/impeller/entity/contents/radial_gradient_contents.cc +++ b/impeller/entity/contents/radial_gradient_contents.cc @@ -99,7 +99,7 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "RadialGradientSSBOFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto geometry_result = GetGeometry()->GetPositionBuffer(renderer, entity, pass); @@ -160,7 +160,7 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "RadialGradientFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); if (geometry_result.prevent_overdraw) { diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 15bdd7333c6e5..b4629f141e4f9 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -153,7 +153,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "RuntimeEffectContents"); cmd.pipeline = pipeline; - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(geometry_result.vertex_buffer); //-------------------------------------------------------------------------- diff --git a/impeller/entity/contents/scene_contents.h b/impeller/entity/contents/scene_contents.h index 9191728cb972b..b1f443cd7502d 100644 --- a/impeller/entity/contents/scene_contents.h +++ b/impeller/entity/contents/scene_contents.h @@ -4,6 +4,10 @@ #pragma once +#if !IMPELLER_ENABLE_3D +static_assert(false); +#endif + #include #include "impeller/entity/contents/color_source_contents.h" diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 7ee05315596bf..071e3f3e86c7b 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -54,7 +54,7 @@ bool SolidColorContents::Render(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "Solid Fill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto geometry_result = GetGeometry()->GetPositionBuffer(renderer, entity, pass); diff --git a/impeller/entity/contents/solid_rrect_blur_contents.cc b/impeller/entity/contents/solid_rrect_blur_contents.cc index ec8bc3e61d9ce..e549856dc1857 100644 --- a/impeller/entity/contents/solid_rrect_blur_contents.cc +++ b/impeller/entity/contents/solid_rrect_blur_contents.cc @@ -16,6 +16,14 @@ namespace impeller { +namespace { +// Generous padding to make sure blurs with large sigmas are fully visible. +// Used to expand the geometry around the rrect. +Scalar PadForSigma(Scalar sigma) { + return sigma * 4.0; +} +} // namespace + SolidRRectBlurContents::SolidRRectBlurContents() = default; SolidRRectBlurContents::~SolidRRectBlurContents() = default; @@ -44,7 +52,7 @@ std::optional SolidRRectBlurContents::GetCoverage( return std::nullopt; } - Scalar radius = sigma_.sigma * 2; + Scalar radius = PadForSigma(sigma_.sigma); auto ltrb = rect_->GetLTRB(); Rect bounds = Rect::MakeLTRB(ltrb[0] - radius, ltrb[1] - radius, @@ -66,9 +74,9 @@ bool SolidRRectBlurContents::Render(const ContentContext& renderer, // Clamp the max kernel width/height to 1000. auto blur_sigma = std::min(sigma_.sigma, 250.0f); - // Increase quality by make the radius a bit bigger than the typical + // Increase quality by making the radius a bit bigger than the typical // sigma->radius conversion we use for slower blurs. - auto blur_radius = blur_sigma * 2; + auto blur_radius = PadForSigma(blur_sigma); auto positive_rect = rect_->GetPositive(); { auto left = -blur_radius; @@ -80,18 +88,21 @@ bool SolidRRectBlurContents::Render(const ContentContext& renderer, {Point(left, top)}, {Point(right, top)}, {Point(left, bottom)}, - {Point(left, bottom)}, - {Point(right, top)}, {Point(right, bottom)}, }); } Command cmd; DEBUG_COMMAND_INFO(cmd, "RRect Shadow"); - auto opts = OptionsFromPassAndEntity(pass, entity); - opts.primitive_type = PrimitiveType::kTriangle; + ContentContextOptions opts = OptionsFromPassAndEntity(pass, entity); + opts.primitive_type = PrimitiveType::kTriangleStrip; + Color color = color_; + if (entity.GetBlendMode() == BlendMode::kClear) { + opts.is_for_rrect_blur_clear = true; + color = Color::White(); + } cmd.pipeline = renderer.GetRRectBlurPipeline(opts); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); @@ -102,7 +113,7 @@ bool SolidRRectBlurContents::Render(const ContentContext& renderer, VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); FS::FragInfo frag_info; - frag_info.color = color_; + frag_info.color = color; frag_info.blur_sigma = blur_sigma; frag_info.rect_size = Point(positive_rect.size); frag_info.corner_radius = @@ -117,4 +128,10 @@ bool SolidRRectBlurContents::Render(const ContentContext& renderer, return true; } +bool SolidRRectBlurContents::ApplyColorFilter( + const ColorFilterProc& color_filter_proc) { + color_ = color_filter_proc(color_); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents/solid_rrect_blur_contents.h b/impeller/entity/contents/solid_rrect_blur_contents.h index ccc681f59fc32..60c41afd1db56 100644 --- a/impeller/entity/contents/solid_rrect_blur_contents.h +++ b/impeller/entity/contents/solid_rrect_blur_contents.h @@ -43,6 +43,10 @@ class SolidRRectBlurContents final : public Contents { const Entity& entity, RenderPass& pass) const override; + // |Contents| + [[nodiscard]] bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc) override; + private: std::optional rect_; Scalar corner_radius_; diff --git a/impeller/entity/contents/sweep_gradient_contents.cc b/impeller/entity/contents/sweep_gradient_contents.cc index 32cb87a4eab0b..88343ee64179c 100644 --- a/impeller/entity/contents/sweep_gradient_contents.cc +++ b/impeller/entity/contents/sweep_gradient_contents.cc @@ -105,7 +105,7 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "SweepGradientSSBOFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto geometry_result = GetGeometry()->GetPositionBuffer(renderer, entity, pass); @@ -167,7 +167,7 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "SweepGradientFill"); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); if (geometry_result.prevent_overdraw) { diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index c17c47ae20b31..dbd3d89960539 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -98,7 +98,7 @@ bool TextContents::Render(const ContentContext& renderer, } else { cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts); } - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); using VS = GlyphAtlasPipeline::VertexShader; using FS = GlyphAtlasPipeline::FragmentShader; diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index 5d4d886d243fb..01f3d4df8d0d3 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -170,7 +170,7 @@ bool TextureContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetTexturePipeline(pipeline_options); #endif // IMPELLER_ENABLE_OPENGLES - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); if (is_external_texture) { diff --git a/impeller/entity/contents/tiled_texture_contents.cc b/impeller/entity/contents/tiled_texture_contents.cc index 67155fe2b9450..0b54d4ba0144e 100644 --- a/impeller/entity/contents/tiled_texture_contents.cc +++ b/impeller/entity/contents/tiled_texture_contents.cc @@ -145,7 +145,7 @@ bool TiledTextureContents::Render(const ContentContext& renderer, DEBUG_COMMAND_INFO(cmd, "TextureFill"); } - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); auto options = OptionsFromPassAndEntity(pass, entity); if (geometry_result.prevent_overdraw) { diff --git a/impeller/entity/contents/vertices_contents.cc b/impeller/entity/contents/vertices_contents.cc index 8567516b02fb0..41d8432a71dda 100644 --- a/impeller/entity/contents/vertices_contents.cc +++ b/impeller/entity/contents/vertices_contents.cc @@ -137,7 +137,7 @@ bool VerticesUVContents::Render(const ContentContext& renderer, auto opts = OptionsFromPassAndEntity(pass, entity); opts.primitive_type = geometry_result.type; cmd.pipeline = renderer.GetTexturePipeline(opts); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(geometry_result.vertex_buffer); VS::FrameInfo frame_info; @@ -187,7 +187,7 @@ bool VerticesColorContents::Render(const ContentContext& renderer, auto opts = OptionsFromPassAndEntity(pass, entity); opts.primitive_type = geometry_result.type; cmd.pipeline = renderer.GetGeometryColorPipeline(opts); - cmd.stencil_reference = entity.GetStencilDepth(); + cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(geometry_result.vertex_buffer); VS::FrameInfo frame_info; diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index b7ebc2af966c9..5bd509263b55f 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -21,7 +21,7 @@ namespace impeller { std::optional Entity::FromSnapshot( const std::optional& snapshot, BlendMode blend_mode, - uint32_t stencil_depth) { + uint32_t clip_depth) { if (!snapshot.has_value()) { return std::nullopt; } @@ -36,7 +36,7 @@ std::optional Entity::FromSnapshot( Entity entity; entity.SetBlendMode(blend_mode); - entity.SetStencilDepth(stencil_depth); + entity.SetClipDepth(clip_depth); entity.SetTransformation(snapshot->transform); entity.SetContents(contents); return entity; @@ -62,16 +62,16 @@ std::optional Entity::GetCoverage() const { return contents_->GetCoverage(*this); } -Contents::StencilCoverage Entity::GetStencilCoverage( - const std::optional& current_stencil_coverage) const { +Contents::ClipCoverage Entity::GetClipCoverage( + const std::optional& current_clip_coverage) const { if (!contents_) { return {}; } - return contents_->GetStencilCoverage(*this, current_stencil_coverage); + return contents_->GetClipCoverage(*this, current_clip_coverage); } -bool Entity::ShouldRender(const std::optional& stencil_coverage) const { - return contents_->ShouldRender(*this, stencil_coverage); +bool Entity::ShouldRender(const std::optional& clip_coverage) const { + return contents_->ShouldRender(*this, clip_coverage); } void Entity::SetContents(std::shared_ptr contents) { @@ -82,16 +82,16 @@ const std::shared_ptr& Entity::GetContents() const { return contents_; } -void Entity::SetStencilDepth(uint32_t depth) { - stencil_depth_ = depth; +void Entity::SetClipDepth(uint32_t depth) { + clip_depth_ = depth; } -uint32_t Entity::GetStencilDepth() const { - return stencil_depth_; +uint32_t Entity::GetClipDepth() const { + return clip_depth_; } void Entity::IncrementStencilDepth(uint32_t increment) { - stencil_depth_ += increment; + clip_depth_ += increment; } void Entity::SetBlendMode(BlendMode blend_mode) { diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 929824fbf5354..26fa0b3974e14 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -23,6 +23,17 @@ class Entity { static constexpr BlendMode kLastPipelineBlendMode = BlendMode::kModulate; static constexpr BlendMode kLastAdvancedBlendMode = BlendMode::kLuminosity; + enum class RenderingMode { + /// In direct mode, the Entity's transform is used as the current + /// local-to-screen transformation matrix. + kDirect, + /// In subpass mode, the Entity passed through the filter is in screen space + /// rather than local space, and so some filters (namely, + /// MatrixFilterContents) need to interpret the given EffectTransform as the + /// current transformation matrix. + kSubpass, + }; + /// An enum to define how to repeat, fold, or omit colors outside of the /// typically defined range of the source of the colors (such as the /// bounds of an image or the defining geometry of a gradient). @@ -54,32 +65,34 @@ class Entity { static std::optional FromSnapshot( const std::optional& snapshot, BlendMode blend_mode = BlendMode::kSourceOver, - uint32_t stencil_depth = 0); + uint32_t clip_depth = 0); Entity(); ~Entity(); + /// @brief Get the global transformation matrix for this Entity. const Matrix& GetTransformation() const; + /// @brief Set the global transformation matrix for this Entity. void SetTransformation(const Matrix& transformation); std::optional GetCoverage() const; - Contents::StencilCoverage GetStencilCoverage( - const std::optional& current_stencil_coverage) const; + Contents::ClipCoverage GetClipCoverage( + const std::optional& current_clip_coverage) const; - bool ShouldRender(const std::optional& stencil_coverage) const; + bool ShouldRender(const std::optional& clip_coverage) const; void SetContents(std::shared_ptr contents); const std::shared_ptr& GetContents() const; - void SetStencilDepth(uint32_t stencil_depth); + void SetClipDepth(uint32_t clip_depth); void IncrementStencilDepth(uint32_t increment); - uint32_t GetStencilDepth() const; + uint32_t GetClipDepth() const; void SetBlendMode(BlendMode blend_mode); @@ -105,7 +118,7 @@ class Entity { Matrix transformation_; std::shared_ptr contents_; BlendMode blend_mode_ = BlendMode::kSourceOver; - uint32_t stencil_depth_ = 0u; + uint32_t clip_depth_ = 0u; mutable Capture capture_; }; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 29a737e8f57e5..4d6897b5b3445 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -110,12 +110,72 @@ std::optional EntityPass::GetElementsCoverage( if (auto entity = std::get_if(&element)) { coverage = entity->GetCoverage(); + // When the coverage limit is std::nullopt, that means there is no limit, + // as opposed to empty coverage. if (coverage.has_value() && coverage_limit.has_value()) { - coverage = coverage->Intersection(coverage_limit.value()); + const auto* filter = entity->GetContents()->AsFilter(); + if (!filter || filter->IsTranslationOnly()) { + coverage = coverage->Intersection(coverage_limit.value()); + } } - } else if (auto subpass = + } else if (auto subpass_ptr = std::get_if>(&element)) { - coverage = GetSubpassCoverage(*subpass->get(), coverage_limit); + auto& subpass = *subpass_ptr->get(); + + std::optional unfiltered_coverage = + GetSubpassCoverage(subpass, std::nullopt); + + // If the current pass elements have any coverage so far and there's a + // backdrop filter, then incorporate the backdrop filter in the + // pre-filtered coverage of the subpass. + if (result.has_value() && subpass.backdrop_filter_proc_) { + std::shared_ptr backdrop_filter = + subpass.backdrop_filter_proc_(FilterInput::Make(result.value()), + subpass.xformation_, + Entity::RenderingMode::kSubpass); + if (backdrop_filter) { + auto backdrop_coverage = backdrop_filter->GetCoverage({}); + backdrop_coverage->origin += result->origin; + if (backdrop_coverage.has_value()) { + if (unfiltered_coverage.has_value()) { + unfiltered_coverage = coverage->Union(*backdrop_coverage); + } else { + unfiltered_coverage = backdrop_coverage; + } + } + } else { + VALIDATION_LOG << "The EntityPass backdrop filter proc didn't return " + "a valid filter."; + } + } + + if (!unfiltered_coverage.has_value()) { + continue; + } + + // Additionally, subpass textures may be passed through filters, which may + // modify the coverage. + // + // Note that we currently only assume that ImageFilters (such as blurs and + // matrix transforms) may modify coverage, although it's technically + // possible ColorFilters to affect coverage as well. For example: A + // ColorMatrixFilter could output a completely transparent result, and + // we could potentially detect this case as zero coverage in the future. + std::shared_ptr image_filter = + subpass.delegate_->WithImageFilter(*unfiltered_coverage, + subpass.xformation_); + if (image_filter) { + Entity subpass_entity; + subpass_entity.SetTransformation(subpass.xformation_); + coverage = image_filter->GetCoverage(subpass_entity); + } else { + coverage = unfiltered_coverage; + } + + if (coverage.has_value() && coverage_limit.has_value() && + (!image_filter || image_filter->IsTranslationOnly())) { + coverage = coverage->Intersection(coverage_limit.value()); + } } else { FML_UNREACHABLE(); } @@ -135,6 +195,14 @@ std::optional EntityPass::GetElementsCoverage( std::optional EntityPass::GetSubpassCoverage( const EntityPass& subpass, std::optional coverage_limit) const { + std::shared_ptr image_filter = + subpass.delegate_->WithImageFilter(Rect(), subpass.xformation_); + + // If the subpass has an image filter, then its coverage space may deviate + // from the parent pass and make intersecting with the pass coverage limit + // unsafe. + coverage_limit = image_filter ? std::nullopt : coverage_limit; + auto entities_coverage = subpass.GetElementsCoverage(coverage_limit); // The entities don't cover anything. There is nothing to do. if (!entities_coverage.has_value()) { @@ -282,9 +350,9 @@ bool EntityPass::Render(ContentContext& renderer, return true; }); - StencilCoverageStack stencil_coverage_stack = {StencilCoverageLayer{ + ClipCoverageStack clip_coverage_stack = {ClipCoverageLayer{ .coverage = Rect::MakeSize(root_render_target.GetRenderTargetSize()), - .stencil_depth = 0}}; + .clip_depth = 0}}; bool supports_onscreen_backdrop_reads = renderer.GetDeviceCapabilities().SupportsReadFromOnscreenTexture() && @@ -308,7 +376,7 @@ bool EntityPass::Render(ContentContext& renderer, Point(), // global_pass_position Point(), // local_pass_position 0, // pass_depth - stencil_coverage_stack // stencil_coverage_stack + clip_coverage_stack // clip_coverage_stack )) { // Validation error messages are triggered for all `OnRender()` failure // cases. @@ -420,7 +488,7 @@ bool EntityPass::Render(ContentContext& renderer, Point(), // global_pass_position Point(), // local_pass_position 0, // pass_depth - stencil_coverage_stack); // stencil_coverage_stack + clip_coverage_stack); // clip_coverage_stack } EntityPass::EntityResult EntityPass::GetEntityForElement( @@ -431,8 +499,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( ISize root_pass_size, Point global_pass_position, uint32_t pass_depth, - StencilCoverageStack& stencil_coverage_stack, - size_t stencil_depth_floor) const { + ClipCoverageStack& clip_coverage_stack, + size_t clip_depth_floor) const { Entity element_entity; //-------------------------------------------------------------------------- @@ -476,8 +544,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( global_pass_position, // global_pass_position Point(), // local_pass_position pass_depth, // pass_depth - stencil_coverage_stack, // stencil_coverage_stack - stencil_depth_, // stencil_depth_floor + clip_coverage_stack, // clip_coverage_stack + clip_depth_, // clip_depth_floor nullptr, // backdrop_filter_contents pass_context.GetRenderPass(pass_depth) // collapsed_parent_pass )) { @@ -493,9 +561,17 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( auto texture = pass_context.GetTexture(); // Render the backdrop texture before any of the pass elements. const auto& proc = subpass->backdrop_filter_proc_; - subpass_backdrop_filter_contents = proc( - FilterInput::Make(std::move(texture)), subpass->xformation_.Basis(), - /*is_subpass*/ true); + subpass_backdrop_filter_contents = + proc(FilterInput::Make(std::move(texture)), + subpass->xformation_.Basis(), Entity::RenderingMode::kSubpass); + + // If the very first thing we render in this EntityPass is a subpass that + // happens to have a backdrop filter, than that backdrop filter will end + // may wind up sampling from the raw, uncleared texture that came straight + // out of the texture cache. By calling `pass_context.GetRenderPass` here, + // we force the texture to pass through at least one RenderPass with the + // correct clear configuration before any sampling occurs. + pass_context.GetRenderPass(pass_depth); // The subpass will need to read from the current pass texture when // rendering the backdrop, so if there's an active pass, end it prior to @@ -503,14 +579,14 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( pass_context.EndPass(); } - if (stencil_coverage_stack.empty()) { + if (clip_coverage_stack.empty()) { // The current clip is empty. This means the pass texture won't be // visible, so skip it. capture.CreateChild("Subpass Entity (Skipped: Empty clip A)"); return EntityPass::EntityResult::Skip(); } - auto stencil_coverage_back = stencil_coverage_stack.back().coverage; - if (!stencil_coverage_back.has_value()) { + auto clip_coverage_back = clip_coverage_stack.back().coverage; + if (!clip_coverage_back.has_value()) { capture.CreateChild("Subpass Entity (Skipped: Empty clip B)"); return EntityPass::EntityResult::Skip(); } @@ -521,7 +597,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( Rect(global_pass_position, Size(pass_context.GetPassTarget() .GetRenderTarget() .GetRenderTargetSize())) - .Intersection(stencil_coverage_back.value()); + .Intersection(clip_coverage_back.value()); if (!coverage_limit.has_value()) { capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit A)"); return EntityPass::EntityResult::Skip(); @@ -563,6 +639,14 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( auto subpass_capture = capture.CreateChild("EntityPass"); subpass_capture.AddRect("Coverage", *subpass_coverage, {.readonly = true}); + // Start non-collapsed subpasses with a fresh clip coverage stack limited by + // the subpass coverage. This is important because image filters applied to + // save layers may transform the subpass texture after it's rendered, + // causing parent clip coverage to get misaligned with the actual area that + // the subpass will affect in the parent pass. + ClipCoverageStack subpass_clip_coverage_stack = {ClipCoverageLayer{ + .coverage = subpass_coverage, .clip_depth = subpass->clip_depth_}}; + // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). if (!subpass->OnRender( @@ -574,8 +658,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( subpass_coverage->origin - global_pass_position, // local_pass_position ++pass_depth, // pass_depth - stencil_coverage_stack, // stencil_coverage_stack - subpass->stencil_depth_, // stencil_depth_floor + subpass_clip_coverage_stack, // clip_coverage_stack + subpass->clip_depth_, // clip_depth_floor subpass_backdrop_filter_contents // backdrop_filter_contents )) { // Validation error messages are triggered for all `OnRender()` failure @@ -608,7 +692,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( capture.CreateChild("Entity (Subpass texture)"); element_entity.SetCapture(subpass_texture_capture); element_entity.SetContents(std::move(offscreen_texture_contents)); - element_entity.SetStencilDepth(subpass->stencil_depth_); + element_entity.SetClipDepth(subpass->clip_depth_); element_entity.SetBlendMode(subpass->blend_mode_); element_entity.SetTransformation(subpass_texture_capture.AddMatrix( "Transform", Matrix::MakeTranslation(Vector3(subpass_coverage->origin - @@ -628,8 +712,8 @@ bool EntityPass::OnRender( Point global_pass_position, Point local_pass_position, uint32_t pass_depth, - StencilCoverageStack& stencil_coverage_stack, - size_t stencil_depth_floor, + ClipCoverageStack& clip_coverage_stack, + size_t clip_depth_floor, std::shared_ptr backdrop_filter_contents, const std::optional& collapsed_parent_pass) const { @@ -642,16 +726,17 @@ bool EntityPass::OnRender( VALIDATION_LOG << SPrintF("Pass context invalid (Depth=%d)", pass_depth); return false; } + auto clear_color_size = pass_target.GetRenderTarget().GetRenderTargetSize(); if (!collapsed_parent_pass && - !GetClearColor(root_pass_size).IsTransparent()) { + !GetClearColor(clear_color_size).IsTransparent()) { // Force the pass context to create at least one new pass if the clear color // is present. pass_context.GetRenderPass(pass_depth); } - auto render_element = [&stencil_depth_floor, &pass_context, &pass_depth, - &renderer, &stencil_coverage_stack, + auto render_element = [&clip_depth_floor, &pass_context, &pass_depth, + &renderer, &clip_coverage_stack, &global_pass_position](Entity& element_entity) { auto result = pass_context.GetRenderPass(pass_depth); @@ -684,69 +769,79 @@ bool EntityPass::OnRender( } } - auto current_stencil_coverage = stencil_coverage_stack.back().coverage; - if (current_stencil_coverage.has_value()) { + auto current_clip_coverage = clip_coverage_stack.back().coverage; + if (current_clip_coverage.has_value()) { // Entity transforms are relative to the current pass position, so we need - // to check stencil coverage in the same space. - current_stencil_coverage->origin -= global_pass_position; + // to check clip coverage in the same space. + current_clip_coverage->origin -= global_pass_position; } - if (!element_entity.ShouldRender(current_stencil_coverage)) { + if (!element_entity.ShouldRender(current_clip_coverage)) { return true; // Nothing to render. } - auto stencil_coverage = - element_entity.GetStencilCoverage(current_stencil_coverage); - if (stencil_coverage.coverage.has_value()) { - stencil_coverage.coverage->origin += global_pass_position; + auto clip_coverage = element_entity.GetClipCoverage(current_clip_coverage); + if (clip_coverage.coverage.has_value()) { + clip_coverage.coverage->origin += global_pass_position; } // The coverage hint tells the rendered Contents which portion of the // rendered output will actually be used, and so we set this to the current - // stencil coverage (which is the max clip bounds). The contents may + // clip coverage (which is the max clip bounds). The contents may // optionally use this hint to avoid unnecessary rendering work. - element_entity.GetContents()->SetCoverageHint(current_stencil_coverage); + if (element_entity.GetContents()->GetCoverageHint().has_value()) { + // If the element already has a coverage hint (because its an advanced + // blend), then we need to intersect the clip coverage hint with the + // existing coverage hint. + element_entity.GetContents()->SetCoverageHint( + current_clip_coverage->Intersection( + element_entity.GetContents()->GetCoverageHint().value())); + } else { + element_entity.GetContents()->SetCoverageHint(current_clip_coverage); + } - switch (stencil_coverage.type) { - case Contents::StencilCoverage::Type::kNoChange: + switch (clip_coverage.type) { + case Contents::ClipCoverage::Type::kNoChange: break; - case Contents::StencilCoverage::Type::kAppend: { - auto op = stencil_coverage_stack.back().coverage; - stencil_coverage_stack.push_back(StencilCoverageLayer{ - .coverage = stencil_coverage.coverage, - .stencil_depth = element_entity.GetStencilDepth() + 1}); - FML_DCHECK(stencil_coverage_stack.back().stencil_depth == - stencil_coverage_stack.size() - 1); + case Contents::ClipCoverage::Type::kAppend: { + auto op = clip_coverage_stack.back().coverage; + clip_coverage_stack.push_back( + ClipCoverageLayer{.coverage = clip_coverage.coverage, + .clip_depth = element_entity.GetClipDepth() + 1}); + FML_DCHECK(clip_coverage_stack.back().clip_depth == + clip_coverage_stack.front().clip_depth + + clip_coverage_stack.size() - 1); if (!op.has_value()) { - // Running this append op won't impact the stencil because the whole - // screen is already being clipped, so skip it. + // Running this append op won't impact the clip buffer because the + // whole screen is already being clipped, so skip it. return true; } } break; - case Contents::StencilCoverage::Type::kRestore: { - if (stencil_coverage_stack.back().stencil_depth <= - element_entity.GetStencilDepth()) { - // Drop stencil restores that will do nothing. + case Contents::ClipCoverage::Type::kRestore: { + if (clip_coverage_stack.back().clip_depth <= + element_entity.GetClipDepth()) { + // Drop clip restores that will do nothing. return true; } - auto restoration_depth = element_entity.GetStencilDepth(); - FML_DCHECK(restoration_depth < stencil_coverage_stack.size()); + auto restoration_index = element_entity.GetClipDepth() - + clip_coverage_stack.front().clip_depth; + FML_DCHECK(restoration_index < clip_coverage_stack.size()); // We only need to restore the area that covers the coverage of the - // stencil rect at target depth + 1. + // clip rect at target depth + 1. std::optional restore_coverage = - (restoration_depth + 1 < stencil_coverage_stack.size()) - ? stencil_coverage_stack[restoration_depth + 1].coverage + (restoration_index + 1 < clip_coverage_stack.size()) + ? clip_coverage_stack[restoration_index + 1].coverage : std::nullopt; if (restore_coverage.has_value()) { // Make the coverage rectangle relative to the current pass. restore_coverage->origin -= global_pass_position; } - stencil_coverage_stack.resize(restoration_depth + 1); + clip_coverage_stack.resize(restoration_index + 1); - if (!stencil_coverage_stack.back().coverage.has_value()) { + if (!clip_coverage_stack.back().coverage.has_value()) { // Running this restore op won't make anything renderable, so skip it. return true; } @@ -769,8 +864,8 @@ bool EntityPass::OnRender( } #endif - element_entity.SetStencilDepth(element_entity.GetStencilDepth() - - stencil_depth_floor); + element_entity.SetClipDepth(element_entity.GetClipDepth() - + clip_depth_floor); if (!element_entity.Render(renderer, *result.pass)) { VALIDATION_LOG << "Failed to render entity."; return false; @@ -792,7 +887,7 @@ bool EntityPass::OnRender( backdrop_entity.SetContents(std::move(backdrop_filter_contents)); backdrop_entity.SetTransformation( Matrix::MakeTranslation(Vector3(-local_pass_position))); - backdrop_entity.SetStencilDepth(stencil_depth_floor); + backdrop_entity.SetClipDepth(clip_depth_floor); render_element(backdrop_entity); } @@ -805,7 +900,7 @@ bool EntityPass::OnRender( // Skip elements that are incorporated into the clear color. if (is_collapsing_clear_colors) { auto [entity_color, _] = - ElementAsBackgroundColor(element, root_pass_size); + ElementAsBackgroundColor(element, clear_color_size); if (entity_color.has_value()) { continue; } @@ -813,15 +908,15 @@ bool EntityPass::OnRender( } EntityResult result = - GetEntityForElement(element, // element - renderer, // renderer - capture, // capture - pass_context, // pass_context - root_pass_size, // root_pass_size - global_pass_position, // global_pass_position - pass_depth, // pass_depth - stencil_coverage_stack, // stencil_coverage_stack - stencil_depth_floor); // stencil_depth_floor + GetEntityForElement(element, // element + renderer, // renderer + capture, // capture + pass_context, // pass_context + root_pass_size, // root_pass_size + global_pass_position, // global_pass_position + pass_depth, // pass_depth + clip_coverage_stack, // clip_coverage_stack + clip_depth_floor); // clip_depth_floor switch (result.status) { case EntityResult::kSuccess: @@ -1039,12 +1134,12 @@ void EntityPass::SetTransformation(Matrix xformation) { xformation_ = xformation; } -void EntityPass::SetStencilDepth(size_t stencil_depth) { - stencil_depth_ = stencil_depth; +void EntityPass::SetClipDepth(size_t clip_depth) { + clip_depth_ = clip_depth; } -size_t EntityPass::GetStencilDepth() { - return stencil_depth_; +size_t EntityPass::GetClipDepth() { + return clip_depth_; } void EntityPass::SetBlendMode(BlendMode blend_mode) { @@ -1054,6 +1149,10 @@ void EntityPass::SetBlendMode(BlendMode blend_mode) { Color EntityPass::GetClearColor(ISize target_size) const { Color result = Color::BlackTransparent(); + if (backdrop_filter_proc_) { + return result; + } + for (const Element& element : elements_) { auto [entity_color, blend_mode] = ElementAsBackgroundColor(element, target_size); diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index f9b11d155f50f..261d3272b14aa 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -41,14 +41,14 @@ class EntityPass { using BackdropFilterProc = std::function( FilterInput::Ref, const Matrix& effect_transform, - bool is_subpass)>; + Entity::RenderingMode rendering_mode)>; - struct StencilCoverageLayer { + struct ClipCoverageLayer { std::optional coverage; - size_t stencil_depth; + size_t clip_depth; }; - using StencilCoverageStack = std::vector; + using ClipCoverageStack = std::vector; EntityPass(); @@ -129,9 +129,9 @@ class EntityPass { void SetTransformation(Matrix xformation); - void SetStencilDepth(size_t stencil_depth); + void SetClipDepth(size_t clip_depth); - size_t GetStencilDepth(); + size_t GetClipDepth(); void SetBlendMode(BlendMode blend_mode); @@ -141,6 +141,23 @@ class EntityPass { void SetEnableOffscreenCheckerboard(bool enabled); + //---------------------------------------------------------------------------- + /// @brief Computes the coverage of a given subpass. This is used to + /// determine the texture size of a given subpass before it's rendered + /// to and passed through the subpass ImageFilter, if any. + /// + /// @param[in] subpass The EntityPass for which to compute + /// pre-filteredcoverage. + /// @param[in] coverage_limit Confines coverage to a specified area. This + /// hint is used to trim coverage to the root + /// framebuffer area. `std::nullopt` means there + /// is no limit. + /// + /// @return The screen space pixel area that the subpass contents will render + /// into, prior to being transformed by the subpass ImageFilter, if + /// any. `std::nullopt` means rendering the subpass will have no + /// effect on the color attachment. + /// std::optional GetSubpassCoverage( const EntityPass& subpass, std::optional coverage_limit) const; @@ -179,8 +196,8 @@ class EntityPass { ISize root_pass_size, Point global_pass_position, uint32_t pass_depth, - StencilCoverageStack& stencil_coverage_stack, - size_t stencil_depth_floor) const; + ClipCoverageStack& clip_coverage_stack, + size_t clip_depth_floor) const; //---------------------------------------------------------------------------- /// @brief OnRender is the internal command recording routine for @@ -214,20 +231,20 @@ class EntityPass { /// and debugging purposes. This can vary /// depending on whether passes are /// collapsed or not. - /// @param[in] stencil_coverage_stack A global stack of coverage rectangles - /// for the stencil buffer at each depth. + /// @param[in] clip_coverage_stack A global stack of coverage rectangles + /// for the clip buffer at each depth. /// Higher depths are more restrictive. /// Used to cull Elements that we /// know won't result in a visible /// change. - /// @param[in] stencil_depth_floor The stencil depth that a value of + /// @param[in] clip_depth_floor The clip depth that a value of /// zero corresponds to in the given - /// `pass_target` stencil buffer. + /// `pass_target` clip buffer. /// When new `pass_target`s are created - /// for subpasses, their stencils are + /// for subpasses, their clip buffers are /// initialized at zero, and so this /// value is used to offset Entity clip - /// depths to match the stencil. + /// depths to match the clip buffer. /// @param[in] backdrop_filter_contents Optional. Is supplied, this contents /// is rendered prior to anything else in /// the `EntityPass`, offset by the @@ -246,8 +263,8 @@ class EntityPass { Point global_pass_position, Point local_pass_position, uint32_t pass_depth, - StencilCoverageStack& stencil_coverage_stack, - size_t stencil_depth_floor = 0, + ClipCoverageStack& clip_coverage_stack, + size_t clip_depth_floor = 0, std::shared_ptr backdrop_filter_contents = nullptr, const std::optional& collapsed_parent_pass = std::nullopt) const; @@ -258,7 +275,7 @@ class EntityPass { EntityPass* superpass_ = nullptr; Matrix xformation_; - size_t stencil_depth_ = 0u; + size_t clip_depth_ = 0u; BlendMode blend_mode_ = BlendMode::kSourceOver; bool flood_clip_ = false; bool enable_offscreen_debug_checkerboard_ = false; diff --git a/impeller/entity/entity_pass_delegate.cc b/impeller/entity/entity_pass_delegate.cc index b996995b57de6..e6a1021956176 100644 --- a/impeller/entity/entity_pass_delegate.cc +++ b/impeller/entity/entity_pass_delegate.cc @@ -34,6 +34,13 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate { FML_UNREACHABLE(); } + // |EntityPassDelgate| + std::shared_ptr WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const override { + return nullptr; + } + private: FML_DISALLOW_COPY_AND_ASSIGN(DefaultEntityPassDelegate); }; diff --git a/impeller/entity/entity_pass_delegate.h b/impeller/entity/entity_pass_delegate.h index fbc7f97be2bbb..4675236b35a79 100644 --- a/impeller/entity/entity_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -9,6 +9,8 @@ #include "flutter/fml/macros.h" #include "impeller/core/texture.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" namespace impeller { @@ -32,6 +34,10 @@ class EntityPassDelegate { std::shared_ptr target, const Matrix& effect_transform) = 0; + virtual std::shared_ptr WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const = 0; + private: FML_DISALLOW_COPY_AND_ASSIGN(EntityPassDelegate); }; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b5d36ca2abad8..0eb86e8b9fc27 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -7,12 +7,14 @@ #include #include #include +#include #include #include "flutter/testing/testing.h" #include "fml/logging.h" #include "fml/time/time_point.h" #include "gtest/gtest.h" +#include "impeller/core/texture_descriptor.h" #include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/conical_gradient_contents.h" @@ -42,6 +44,7 @@ #include "impeller/geometry/geometry_asserts.h" #include "impeller/geometry/path_builder.h" #include "impeller/geometry/sigma.h" +#include "impeller/geometry/vector.h" #include "impeller/playground/playground.h" #include "impeller/playground/widgets.h" #include "impeller/renderer/command.h" @@ -91,6 +94,13 @@ class TestPassDelegate final : public EntityPassDelegate { return nullptr; } + // |EntityPassDelegate| + std::shared_ptr WithImageFilter( + const FilterInput::Variant& input, + const Matrix& effect_transform) const override { + return nullptr; + } + private: const std::optional coverage_; const bool collapse_; @@ -1621,12 +1631,12 @@ TEST_P(EntityTest, ClipContentsShouldRenderIsCorrect) { } } -TEST_P(EntityTest, ClipContentsGetStencilCoverageIsCorrect) { +TEST_P(EntityTest, ClipContentsGetClipCoverageIsCorrect) { // Intersection: No stencil coverage, no geometry. { auto clip = std::make_shared(); clip->SetClipOperation(Entity::ClipOperation::kIntersect); - auto result = clip->GetStencilCoverage(Entity{}, Rect{}); + auto result = clip->GetClipCoverage(Entity{}, Rect{}); ASSERT_FALSE(result.coverage.has_value()); } @@ -1637,7 +1647,7 @@ TEST_P(EntityTest, ClipContentsGetStencilCoverageIsCorrect) { clip->SetClipOperation(Entity::ClipOperation::kIntersect); clip->SetGeometry(Geometry::MakeFillPath( PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath())); - auto result = clip->GetStencilCoverage(Entity{}, Rect{}); + auto result = clip->GetClipCoverage(Entity{}, Rect{}); ASSERT_FALSE(result.coverage.has_value()); } @@ -1647,7 +1657,7 @@ TEST_P(EntityTest, ClipContentsGetStencilCoverageIsCorrect) { auto clip = std::make_shared(); clip->SetClipOperation(Entity::ClipOperation::kIntersect); auto result = - clip->GetStencilCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100)); + clip->GetClipCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100)); ASSERT_FALSE(result.coverage.has_value()); } @@ -1659,11 +1669,11 @@ TEST_P(EntityTest, ClipContentsGetStencilCoverageIsCorrect) { clip->SetGeometry(Geometry::MakeFillPath( PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 50, 50)).TakePath())); auto result = - clip->GetStencilCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100)); + clip->GetClipCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100)); ASSERT_TRUE(result.coverage.has_value()); ASSERT_RECT_NEAR(result.coverage.value(), Rect::MakeLTRB(0, 0, 50, 50)); - ASSERT_EQ(result.type, Contents::StencilCoverage::Type::kAppend); + ASSERT_EQ(result.type, Contents::ClipCoverage::Type::kAppend); } // Difference: With stencil coverage, with geometry. @@ -1673,11 +1683,11 @@ TEST_P(EntityTest, ClipContentsGetStencilCoverageIsCorrect) { clip->SetGeometry(Geometry::MakeFillPath( PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 50, 50)).TakePath())); auto result = - clip->GetStencilCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100)); + clip->GetClipCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100)); ASSERT_TRUE(result.coverage.has_value()); ASSERT_RECT_NEAR(result.coverage.value(), Rect::MakeLTRB(0, 0, 100, 100)); - ASSERT_EQ(result.type, Contents::StencilCoverage::Type::kAppend); + ASSERT_EQ(result.type, Contents::ClipCoverage::Type::kAppend); } } @@ -2407,6 +2417,14 @@ TEST_P(EntityTest, PointFieldGeometryDivisions) { ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(20000.0, true), 140u); } +TEST_P(EntityTest, PointFieldGeometryCoverage) { + std::vector points = {{10, 20}, {100, 200}}; + auto geometry = Geometry::MakePointField(points, 5.0, false); + ASSERT_EQ(*geometry->GetCoverage(Matrix()), Rect::MakeLTRB(5, 15, 105, 205)); + ASSERT_EQ(*geometry->GetCoverage(Matrix::MakeTranslation({30, 0, 0})), + Rect::MakeLTRB(35, 15, 135, 205)); +} + TEST_P(EntityTest, ColorFilterContentsWithLargeGeometry) { Entity entity; entity.SetTransformation(Matrix::MakeScale(GetContentScale())); @@ -2434,6 +2452,89 @@ TEST_P(EntityTest, TextContentsCeilsGlyphScaleToDecimal) { ASSERT_EQ(TextFrame::RoundScaledFontSize(0.0f, 12), 0.0f); } +class TestRenderTargetAllocator : public RenderTargetAllocator { + public: + explicit TestRenderTargetAllocator(std::shared_ptr allocator) + : RenderTargetAllocator(std::move(allocator)) {} + + ~TestRenderTargetAllocator() = default; + + std::shared_ptr CreateTexture( + const TextureDescriptor& desc) override { + allocated_.push_back(desc); + return RenderTargetAllocator::CreateTexture(desc); + } + + void Start() override { RenderTargetAllocator::Start(); } + + void End() override { RenderTargetAllocator::End(); } + + std::vector GetDescriptors() const { return allocated_; } + + private: + std::vector allocated_; +}; + +TEST_P(EntityTest, AdvancedBlendCoverageHintIsNotResetByEntityPass) { + if (GetContext()->GetCapabilities()->SupportsFramebufferFetch()) { + GTEST_SKIP() << "Backends that support framebuffer fetch dont use coverage " + "for advanced blends."; + } + + auto contents = std::make_shared(); + contents->SetGeometry(Geometry::MakeRect({100, 100, 100, 100})); + contents->SetColor(Color::Red()); + + Entity entity; + entity.SetTransformation(Matrix::MakeScale(Vector3(2, 2, 1))); + entity.SetBlendMode(BlendMode::kColorBurn); + entity.SetContents(contents); + + auto coverage = entity.GetCoverage(); + EXPECT_TRUE(coverage.has_value()); + + auto pass = std::make_unique(); + auto test_allocator = std::make_shared( + GetContext()->GetResourceAllocator()); + auto stencil_config = RenderTarget::AttachmentConfig{ + .storage_mode = StorageMode::kDevicePrivate, + .load_action = LoadAction::kClear, + .store_action = StoreAction::kDontCare, + .clear_color = Color::BlackTransparent()}; + auto rt = RenderTarget::CreateOffscreen( + *GetContext(), *test_allocator, ISize::MakeWH(1000, 1000), "Offscreen", + RenderTarget::kDefaultColorAttachmentConfig, stencil_config); + auto content_context = ContentContext( + GetContext(), TypographerContextSkia::Make(), test_allocator); + pass->AddEntity(entity); + + EXPECT_TRUE(pass->Render(content_context, rt)); + + if (test_allocator->GetDescriptors().size() == 6u) { + EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000)); + + EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200)); + } else if (test_allocator->GetDescriptors().size() == 9u) { + // Onscreen render target. + EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(1000, 1000)); + + EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[6].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[7].size, ISize(200, 200)); + } else { + EXPECT_TRUE(false); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/geometry/fill_path_geometry.cc b/impeller/entity/geometry/fill_path_geometry.cc index b608ed0428ae1..1153572f25c08 100644 --- a/impeller/entity/geometry/fill_path_geometry.cc +++ b/impeller/entity/geometry/fill_path_geometry.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "impeller/entity/geometry/fill_path_geometry.h" +#include "impeller/core/formats.h" namespace impeller { @@ -47,11 +48,17 @@ GeometryResult FillPathGeometry::GetPositionBuffer( const float* vertices, size_t vertices_count, const uint16_t* indices, size_t indices_count) { vertex_buffer.vertex_buffer = host_buffer.Emplace( - vertices, vertices_count * sizeof(float), alignof(float)); - vertex_buffer.index_buffer = host_buffer.Emplace( - indices, indices_count * sizeof(uint16_t), alignof(uint16_t)); - vertex_buffer.vertex_count = indices_count; - vertex_buffer.index_type = IndexType::k16bit; + vertices, vertices_count * sizeof(float) * 2, alignof(float)); + if (indices != nullptr) { + vertex_buffer.index_buffer = host_buffer.Emplace( + indices, indices_count * sizeof(uint16_t), alignof(uint16_t)); + vertex_buffer.vertex_count = indices_count; + vertex_buffer.index_type = IndexType::k16bit; + } else { + vertex_buffer.index_buffer = {}; + vertex_buffer.vertex_count = vertices_count; + vertex_buffer.index_type = IndexType::kNone; + } return true; }); if (tesselation_result != Tessellator::Result::kSuccess) { @@ -112,7 +119,7 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer( [&vertex_builder, &texture_coverage, &effect_transform]( const float* vertices, size_t vertices_count, const uint16_t* indices, size_t indices_count) { - for (auto i = 0u; i < vertices_count; i += 2) { + for (auto i = 0u; i < vertices_count * 2; i += 2) { VS::PerVertexData data; Point vtx = {vertices[i], vertices[i + 1]}; data.position = vtx; @@ -121,9 +128,11 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer( texture_coverage.size; vertex_builder.AppendVertex(data); } - FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count / 2); - for (auto i = 0u; i < indices_count; i++) { - vertex_builder.AppendIndex(indices[i]); + FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count); + if (indices != nullptr) { + for (auto i = 0u; i < indices_count; i++) { + vertex_builder.AppendIndex(indices[i]); + } } return true; }); diff --git a/impeller/entity/geometry/point_field_geometry.cc b/impeller/entity/geometry/point_field_geometry.cc index 81fe3885d916b..9821bad91ad7b 100644 --- a/impeller/entity/geometry/point_field_geometry.cc +++ b/impeller/entity/geometry/point_field_geometry.cc @@ -287,8 +287,9 @@ std::optional PointFieldGeometry::GetCoverage( right = std::max(right, it->x); bottom = std::max(bottom, it->y); } - return Rect::MakeLTRB(left - radius_, top - radius_, right + radius_, - bottom + radius_); + auto coverage = Rect::MakeLTRB(left - radius_, top - radius_, + right + radius_, bottom + radius_); + return coverage.TransformBounds(transform); } return std::nullopt; } diff --git a/impeller/entity/geometry/stroke_path_geometry.cc b/impeller/entity/geometry/stroke_path_geometry.cc index 9b5ab6877cd99..43648a548e29e 100644 --- a/impeller/entity/geometry/stroke_path_geometry.cc +++ b/impeller/entity/geometry/stroke_path_geometry.cc @@ -253,7 +253,6 @@ StrokePathGeometry::CreateSolidStrokeVertices( for (size_t contour_i = 0; contour_i < polyline.contours.size(); contour_i++) { auto contour = polyline.contours[contour_i]; - size_t contour_component_i = 0; size_t contour_start_point_i, contour_end_point_i; std::tie(contour_start_point_i, contour_end_point_i) = polyline.GetContourPointBounds(contour_i); @@ -309,55 +308,27 @@ StrokePathGeometry::CreateSolidStrokeVertices( // Generate contour geometry. for (size_t point_i = contour_start_point_i + 1; point_i < contour_end_point_i; point_i++) { - if ((contour_component_i + 1 >= contour.components.size()) && - contour.components[contour_component_i + 1].component_start_index <= - point_i) { - // The point_i has entered the next component in this contour. - contour_component_i += 1; - } // Generate line rect. vtx.position = polyline.points[point_i - 1] + offset; vtx_builder.AppendVertex(vtx); vtx.position = polyline.points[point_i - 1] - offset; vtx_builder.AppendVertex(vtx); + vtx.position = polyline.points[point_i] + offset; + vtx_builder.AppendVertex(vtx); + vtx.position = polyline.points[point_i] - offset; + vtx_builder.AppendVertex(vtx); - auto is_end_of_contour = point_i == contour_end_point_i - 1; - - if (!contour.components[contour_component_i].is_curve) { - // For line components, two additional points need to be appended prior - // to appending a join connecting the next component. - vtx.position = polyline.points[point_i] + offset; - vtx_builder.AppendVertex(vtx); - vtx.position = polyline.points[point_i] - offset; - vtx_builder.AppendVertex(vtx); + if (point_i < contour_end_point_i - 1) { + compute_offset(point_i + 1); - if (!is_end_of_contour) { - compute_offset(point_i + 1); - // Generate join from the current line to the next line. - join_proc(vtx_builder, polyline.points[point_i], previous_offset, - offset, scaled_miter_limit, scale); - } - } else { - // For curve components, the polyline is detailed enough such that - // it can avoid worrying about joins altogether. - if (!is_end_of_contour) { - compute_offset(point_i + 1); - } else { - // If this is a curve and is the end of the contour, two end points - // need to be drawn with the contour end_direction. - auto end_offset = - Vector2(-contour.end_direction.y, contour.end_direction.x) * - stroke_width * 0.5; - vtx.position = polyline.points[contour_end_point_i - 1] + end_offset; - vtx_builder.AppendVertex(vtx); - vtx.position = polyline.points[contour_end_point_i - 1] - end_offset; - vtx_builder.AppendVertex(vtx); - } + // Generate join from the current line to the next line. + join_proc(vtx_builder, polyline.points[point_i], previous_offset, + offset, scaled_miter_limit, scale); } } // Generate end cap or join. - if (!contour.is_closed) { + if (!polyline.contours[contour_i].is_closed) { auto cap_offset = Vector2(-contour.end_direction.y, contour.end_direction.x) * stroke_width * 0.5; // Clockwise normal diff --git a/impeller/entity/geometry/vertices_geometry.cc b/impeller/entity/geometry/vertices_geometry.cc index 8c1a0c0d542b2..23a1c32a5f0b1 100644 --- a/impeller/entity/geometry/vertices_geometry.cc +++ b/impeller/entity/geometry/vertices_geometry.cc @@ -229,11 +229,13 @@ GeometryResult VerticesGeometry::GetPositionUVBuffer( auto vertex_count = vertices_.size(); auto size = texture_coverage.size; auto origin = texture_coverage.origin; + auto has_texture_coordinates = HasTextureCoordinates(); std::vector vertex_data(vertex_count); { for (auto i = 0u; i < vertex_count; i++) { auto vertex = vertices_[i]; - auto texture_coord = texture_coordinates_[i]; + auto texture_coord = + has_texture_coordinates ? texture_coordinates_[i] : vertices_[i]; auto uv = effect_transform * Point((texture_coord.x - origin.x) / size.width, (texture_coord.y - origin.y) / size.height); diff --git a/impeller/entity/render_target_cache.cc b/impeller/entity/render_target_cache.cc index a26551768975b..89e16289092a8 100644 --- a/impeller/entity/render_target_cache.cc +++ b/impeller/entity/render_target_cache.cc @@ -39,12 +39,16 @@ std::shared_ptr RenderTargetCache::CreateTexture( for (auto& td : texture_data_) { const auto other_desc = td.texture->GetTextureDescriptor(); + FML_DCHECK(td.texture != nullptr); if (!td.used_this_frame && desc == other_desc) { td.used_this_frame = true; return td.texture; } } auto result = RenderTargetAllocator::CreateTexture(desc); + if (result == nullptr) { + return result; + } texture_data_.push_back( TextureData{.used_this_frame = true, .texture = result}); return result; diff --git a/impeller/entity/render_target_cache_unittests.cc b/impeller/entity/render_target_cache_unittests.cc index d8086b32aec44..fc402f87fa9f5 100644 --- a/impeller/entity/render_target_cache_unittests.cc +++ b/impeller/entity/render_target_cache_unittests.cc @@ -25,13 +25,21 @@ class TestAllocator : public Allocator { std::shared_ptr OnCreateBuffer( const DeviceBufferDescriptor& desc) override { + if (should_fail) { + return nullptr; + } return std::make_shared(desc); }; virtual std::shared_ptr OnCreateTexture( const TextureDescriptor& desc) override { + if (should_fail) { + return nullptr; + } return std::make_shared(desc); }; + + bool should_fail = false; }; TEST(RenderTargetCacheTest, CachesUsedTexturesAcrossFrames) { @@ -62,5 +70,20 @@ TEST(RenderTargetCacheTest, CachesUsedTexturesAcrossFrames) { ASSERT_EQ(render_target_cache.CachedTextureCount(), 1u); } +TEST(RenderTargetCacheTest, DoesNotPersistFailedAllocations) { + auto allocator = std::make_shared(); + auto render_target_cache = RenderTargetCache(allocator); + auto desc = TextureDescriptor{ + .format = PixelFormat::kR8G8B8A8UNormInt, + .size = ISize(100, 100), + .usage = static_cast(TextureUsage::kRenderTarget)}; + + render_target_cache.Start(); + allocator->should_fail = true; + + ASSERT_EQ(render_target_cache.CreateTexture(desc), nullptr); + ASSERT_EQ(render_target_cache.CachedTextureCount(), 0u); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/shaders/blending/advanced_blend.glsl b/impeller/entity/shaders/blending/advanced_blend.glsl index c2e26d59bf7b3..9a11dce29f789 100644 --- a/impeller/entity/shaders/blending/advanced_blend.glsl +++ b/impeller/entity/shaders/blending/advanced_blend.glsl @@ -12,6 +12,7 @@ uniform BlendInfo { float16_t src_input_alpha; float16_t color_factor; f16vec4 color; // This color input is expected to be unpremultiplied. + float supports_decal_sampler_address_mode; } blend_info; @@ -24,9 +25,12 @@ in vec2 v_src_texture_coords; out f16vec4 frag_color; f16vec4 Sample(f16sampler2D texture_sampler, vec2 texture_coords) { -// gles 2.0 is the only backend without native decal support. #ifdef IMPELLER_TARGET_OPENGLES - return IPSampleDecal(texture_sampler, texture_coords); + if (blend_info.supports_decal_sampler_address_mode > 0.0) { + return texture(texture_sampler, texture_coords); + } else { + return IPHalfSampleDecal(texture_sampler, texture_coords); + } #else return texture(texture_sampler, texture_coords); #endif diff --git a/impeller/entity/shaders/blending/ios/framebuffer_blend.glsl b/impeller/entity/shaders/blending/ios/framebuffer_blend.glsl index f67506b5603e2..67bd9cad8370e 100644 --- a/impeller/entity/shaders/blending/ios/framebuffer_blend.glsl +++ b/impeller/entity/shaders/blending/ios/framebuffer_blend.glsl @@ -9,7 +9,6 @@ #include #include -#ifdef IMPELLER_TARGET_METAL layout(set = 0, binding = 0, input_attachment_index = 0) uniform subpassInput uSub; @@ -17,12 +16,6 @@ layout(set = 0, vec4 ReadDestination() { return subpassLoad(uSub); } -#else - -vec4 ReadDestination() { - return vec4(0); -} -#endif uniform sampler2D texture_sampler_src; @@ -35,12 +28,20 @@ in vec2 v_src_texture_coords; out vec4 frag_color; +vec4 Sample(sampler2D texture_sampler, vec2 texture_coords) { +// gles 2.0 is the only backend without native decal support. +#ifdef IMPELLER_TARGET_OPENGLES + return IPSampleDecal(texture_sampler, texture_coords); +#else + return texture(texture_sampler, texture_coords); +#endif +} + void main() { - f16vec4 dst_sample = f16vec4(ReadDestination()); - f16vec4 dst = dst_sample; - f16vec4 src = f16vec4(texture(texture_sampler_src, // sampler - v_src_texture_coords // texture coordinates - )) * + f16vec4 dst = f16vec4(ReadDestination()); + f16vec4 src = f16vec4(Sample(texture_sampler_src, // sampler + v_src_texture_coords // texture coordinates + )) * frag_info.src_input_alpha; f16vec4 blended = mix(src, f16vec4(Blend(dst.rgb, src.rgb), dst.a), dst.a); diff --git a/impeller/entity/shaders/blending/porter_duff_blend.frag b/impeller/entity/shaders/blending/porter_duff_blend.frag index a24b523c07005..7e3498ff0d33c 100644 --- a/impeller/entity/shaders/blending/porter_duff_blend.frag +++ b/impeller/entity/shaders/blending/porter_duff_blend.frag @@ -19,6 +19,7 @@ uniform FragInfo { float16_t dst_coeff_src_color; float16_t input_alpha; float16_t output_alpha; + float supports_decal_sampler_address_mode; } frag_info; @@ -28,9 +29,12 @@ in f16vec4 v_color; out f16vec4 frag_color; f16vec4 Sample(f16sampler2D texture_sampler, vec2 texture_coords) { -// gles 2.0 is the only backend without native decal support. #ifdef IMPELLER_TARGET_OPENGLES - return IPSampleDecal(texture_sampler, texture_coords); + if (frag_info.supports_decal_sampler_address_mode > 0.0) { + return texture(texture_sampler, texture_coords); + } else { + return IPHalfSampleDecal(texture_sampler, texture_coords); + } #else return texture(texture_sampler, texture_coords); #endif diff --git a/impeller/entity/shaders/morphology_filter.frag b/impeller/entity/shaders/morphology_filter.frag index 8f2d01f98ee0d..29428d0774b7f 100644 --- a/impeller/entity/shaders/morphology_filter.frag +++ b/impeller/entity/shaders/morphology_filter.frag @@ -19,6 +19,7 @@ uniform FragInfo { f16vec2 uv_offset; float16_t radius; float16_t morph_type; + float supports_decal_sampler_address_mode; } frag_info; @@ -32,11 +33,15 @@ void main() { for (float16_t i = -frag_info.radius; i <= frag_info.radius; i++) { vec2 texture_coords = v_texture_coords + frag_info.uv_offset * i; -// gles 2.0 is the only backend without native decal support. + f16vec4 color; #ifdef IMPELLER_TARGET_OPENGLES - f16vec4 color = IPHalfSampleDecal(texture_sampler, texture_coords); + if (frag_info.supports_decal_sampler_address_mode > 0.0) { + color = texture(texture_sampler, texture_coords); + } else { + color = IPHalfSampleDecal(texture_sampler, texture_coords); + } #else - f16vec4 color = texture(texture_sampler, texture_coords); + color = texture(texture_sampler, texture_coords); #endif if (frag_info.morph_type == kMorphTypeDilate) { diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 02c89627d94d4..14247f1c6e5fd 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -144,7 +144,7 @@ struct Color { explicit Color(const ColorHSB& hsbColor); - Color(const Vector4& value); + explicit Color(const Vector4& value); constexpr Color(Scalar r, Scalar g, Scalar b, Scalar a) : red(r), green(g), blue(b), alpha(a) {} diff --git a/impeller/geometry/geometry_benchmarks.cc b/impeller/geometry/geometry_benchmarks.cc index d3c1f9ea525c5..8bff21f3282bf 100644 --- a/impeller/geometry/geometry_benchmarks.cc +++ b/impeller/geometry/geometry_benchmarks.cc @@ -35,8 +35,8 @@ static void BM_Polyline(benchmark::State& state, Args&&... args) { if (tessellate) { tess.Tessellate( FillType::kNonZero, polyline, - [](const float* vertices, size_t vertices_size, - const uint16_t* indices, size_t indices_size) { return true; }); + [](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { return true; }); } } state.counters["SinglePointCount"] = single_point_count; diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 119702f6130a8..8a1f791e05cc4 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -62,7 +62,7 @@ struct Matrix { Vector4(m12, m13, m14, m15)} {} // clang-format on - Matrix(const MatrixDecomposition& decomposition); + explicit Matrix(const MatrixDecomposition& decomposition); // clang-format off static constexpr Matrix MakeColumn( diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 863b220455382..ff2c39d6ecaf0 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -324,10 +324,9 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { return Vector2(0, -1); }; - std::vector components; std::optional previous_path_component_index; auto end_contour = [&polyline, &previous_path_component_index, - &get_path_component, &components]() { + &get_path_component]() { // Whenever a contour has ended, extract the exact end direction from the // last component. if (polyline.contours.empty()) { @@ -340,8 +339,6 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { auto& contour = polyline.contours.back(); contour.end_direction = Vector2(0, 1); - contour.components = components; - components.clear(); size_t previous_index = previous_path_component_index.value(); while (!std::holds_alternative( @@ -366,26 +363,14 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { const auto& component = components_[component_i]; switch (component.type) { case ComponentType::kLinear: - components.push_back({ - .component_start_index = polyline.points.size(), - .is_curve = false, - }); collect_points(linears_[component.index].CreatePolyline()); previous_path_component_index = component_i; break; case ComponentType::kQuadratic: - components.push_back({ - .component_start_index = polyline.points.size(), - .is_curve = true, - }); collect_points(quads_[component.index].CreatePolyline(scale)); previous_path_component_index = component_i; break; case ComponentType::kCubic: - components.push_back({ - .component_start_index = polyline.points.size(), - .is_curve = true, - }); collect_points(cubics_[component.index].CreatePolyline(scale)); previous_path_component_index = component_i; break; @@ -401,14 +386,13 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { const auto& contour = contours_[component.index]; polyline.contours.push_back({.start_index = polyline.points.size(), .is_closed = contour.is_closed, - .start_direction = start_direction, - .components = components}); + .start_direction = start_direction}); previous_contour_point = std::nullopt; collect_points({contour.destination}); break; } + end_contour(); } - end_contour(); return polyline; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index bbbb207793d5f..757c5c03d9548 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -61,17 +61,8 @@ class Path { }; struct PolylineContour { - struct Component { - size_t component_start_index; - /// Denotes whether this component is a curve. - /// - /// This is set to true when this component is generated from - /// QuadraticComponent or CubicPathComponent. - bool is_curve; - }; /// Index that denotes the first point of this contour. size_t start_index; - /// Denotes whether the last point of this contour is connected to the first /// point of this contour or not. bool is_closed; @@ -80,12 +71,6 @@ class Path { Vector2 start_direction; /// The direction of the contour's end cap. Vector2 end_direction; - - /// Distinct components in this contour. - /// - /// If this contour is generated from multiple path components, each - /// path component forms a component in this vector. - std::vector components; }; /// One or more contours represented as a series of points and indices in diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index a8b0d4fa9bd68..c2a808cf18d2a 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -47,13 +47,9 @@ struct LinearPathComponent { std::optional GetEndDirection() const; }; -// A component that represets a Quadratic Bézier curve. struct QuadraticPathComponent { - // Start point. Point p1; - // Control point. Point cp; - // End point. Point p2; QuadraticPathComponent() {} @@ -91,15 +87,10 @@ struct QuadraticPathComponent { std::optional GetEndDirection() const; }; -// A component that represets a Cubic Bézier curve. struct CubicPathComponent { - // Start point. Point p1; - // The first control point. Point cp1; - // The second control point. Point cp2; - // End point. Point p2; CubicPathComponent() {} diff --git a/impeller/golden_tests/BUILD.gn b/impeller/golden_tests/BUILD.gn index 6682a0732725a..0e1c7bd216e17 100644 --- a/impeller/golden_tests/BUILD.gn +++ b/impeller/golden_tests/BUILD.gn @@ -47,12 +47,13 @@ if (is_mac) { sources = [ "metal_screenshot.h", "metal_screenshot.mm", - "metal_screenshoter.h", - "metal_screenshoter.mm", + "metal_screenshotter.h", + "metal_screenshotter.mm", ] deps = [ "//flutter/impeller/aiks", + "//flutter/impeller/display_list", "//flutter/impeller/playground", "//flutter/impeller/renderer/backend/metal:metal", ] diff --git a/impeller/golden_tests/golden_playground_test.h b/impeller/golden_tests/golden_playground_test.h index 9754d943be655..0124d86108ea9 100644 --- a/impeller/golden_tests/golden_playground_test.h +++ b/impeller/golden_tests/golden_playground_test.h @@ -57,6 +57,9 @@ class GoldenPlaygroundTest ISize GetWindowSize() const; + protected: + void SetWindowSize(ISize size); + private: #if FML_OS_MACOSX // This must be placed first so that the autorelease pool is not destroyed diff --git a/impeller/golden_tests/golden_playground_test_mac.cc b/impeller/golden_tests/golden_playground_test_mac.cc index e7f0c0fb7b6b7..686b1956987cd 100644 --- a/impeller/golden_tests/golden_playground_test_mac.cc +++ b/impeller/golden_tests/golden_playground_test_mac.cc @@ -10,7 +10,7 @@ #include "flutter/impeller/aiks/picture.h" #include "flutter/impeller/golden_tests/golden_digest.h" -#include "flutter/impeller/golden_tests/metal_screenshoter.h" +#include "flutter/impeller/golden_tests/metal_screenshotter.h" #include "impeller/typographer/backends/skia/typographer_context_skia.h" #include "impeller/typographer/typographer_context.h" @@ -85,8 +85,9 @@ bool SaveScreenshot(std::unique_ptr screenshot) { } // namespace struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl { - GoldenPlaygroundTestImpl() : screenshoter(new testing::MetalScreenshoter()) {} - std::unique_ptr screenshoter; + GoldenPlaygroundTestImpl() + : screenshotter(new testing::MetalScreenshotter()) {} + std::unique_ptr screenshotter; ISize window_size = ISize{1024, 768}; }; @@ -140,8 +141,8 @@ PlaygroundBackend GoldenPlaygroundTest::GetBackend() const { bool GoldenPlaygroundTest::OpenPlaygroundHere(Picture picture) { AiksContext renderer(GetContext(), typographer_context_); - auto screenshot = pimpl_->screenshoter->MakeScreenshot(renderer, picture, - pimpl_->window_size); + auto screenshot = pimpl_->screenshotter->MakeScreenshot(renderer, picture, + pimpl_->window_size); return SaveScreenshot(std::move(screenshot)); } @@ -178,11 +179,11 @@ std::shared_ptr GoldenPlaygroundTest::OpenAssetAsRuntimeStage( } std::shared_ptr GoldenPlaygroundTest::GetContext() const { - return pimpl_->screenshoter->GetPlayground().GetContext(); + return pimpl_->screenshotter->GetPlayground().GetContext(); } Point GoldenPlaygroundTest::GetContentScale() const { - return pimpl_->screenshoter->GetPlayground().GetContentScale(); + return pimpl_->screenshotter->GetPlayground().GetContentScale(); } Scalar GoldenPlaygroundTest::GetSecondsElapsed() const { @@ -193,4 +194,8 @@ ISize GoldenPlaygroundTest::GetWindowSize() const { return pimpl_->window_size; } +void GoldenPlaygroundTest::GoldenPlaygroundTest::SetWindowSize(ISize size) { + pimpl_->window_size = size; +} + } // namespace impeller diff --git a/impeller/golden_tests/golden_playground_test_stub.cc b/impeller/golden_tests/golden_playground_test_stub.cc index fe7a931c112f9..7827d9c5e1007 100644 --- a/impeller/golden_tests/golden_playground_test_stub.cc +++ b/impeller/golden_tests/golden_playground_test_stub.cc @@ -64,4 +64,6 @@ ISize GoldenPlaygroundTest::GetWindowSize() const { return ISize(); } +void GoldenPlaygroundTest::SetWindowSize(ISize size) {} + } // namespace impeller diff --git a/impeller/golden_tests/golden_tests.cc b/impeller/golden_tests/golden_tests.cc index d57bbf751d5d5..9919aa5160ff8 100644 --- a/impeller/golden_tests/golden_tests.cc +++ b/impeller/golden_tests/golden_tests.cc @@ -13,7 +13,7 @@ #include "impeller/geometry/path_builder.h" #include "impeller/golden_tests/golden_digest.h" #include "impeller/golden_tests/metal_screenshot.h" -#include "impeller/golden_tests/metal_screenshoter.h" +#include "impeller/golden_tests/metal_screenshotter.h" #include "impeller/golden_tests/working_directory.h" namespace impeller { @@ -50,14 +50,14 @@ bool SaveScreenshot(std::unique_ptr screenshot) { class GoldenTests : public ::testing::Test { public: - GoldenTests() : screenshoter_(new MetalScreenshoter()) {} + GoldenTests() : screenshotter_(new MetalScreenshotter()) {} - MetalScreenshoter& Screenshoter() { return *screenshoter_; } + MetalScreenshotter& Screenshotter() { return *screenshotter_; } void SetUp() override { testing::GoldenDigest::Instance()->AddDimension( "gpu_string", - Screenshoter().GetPlayground().GetContext()->DescribeGpuModel()); + Screenshotter().GetPlayground().GetContext()->DescribeGpuModel()); } private: @@ -65,7 +65,7 @@ class GoldenTests : public ::testing::Test { // autorelease pool. fml::ScopedNSAutoreleasePool autorelease_pool_; - std::unique_ptr screenshoter_; + std::unique_ptr screenshotter_; }; TEST_F(GoldenTests, ConicalGradient) { @@ -82,8 +82,8 @@ TEST_F(GoldenTests, ConicalGradient) { Picture picture = canvas.EndRecordingAsPicture(); auto aiks_context = - AiksContext(Screenshoter().GetPlayground().GetContext(), nullptr); - auto screenshot = Screenshoter().MakeScreenshot(aiks_context, picture); + AiksContext(Screenshotter().GetPlayground().GetContext(), nullptr); + auto screenshot = Screenshotter().MakeScreenshot(aiks_context, picture); ASSERT_TRUE(SaveScreenshot(std::move(screenshot))); } } // namespace testing diff --git a/impeller/golden_tests/metal_screenshot.h b/impeller/golden_tests/metal_screenshot.h index 0d2df7fc44309..93b25b0b2ce07 100644 --- a/impeller/golden_tests/metal_screenshot.h +++ b/impeller/golden_tests/metal_screenshot.h @@ -13,9 +13,7 @@ namespace impeller { namespace testing { -class MetalScreenshoter; - -/// A screenshot that was produced from `MetalScreenshoter`. +/// A screenshot that was produced from `MetalScreenshotter`. class MetalScreenshot { public: ~MetalScreenshot(); @@ -26,13 +24,15 @@ class MetalScreenshot { size_t GetWidth() const; + size_t GetBytesPerRow() const; + bool WriteToPNG(const std::string& path) const; private: - friend class MetalScreenshoter; - MetalScreenshot(CGImageRef cgImage); + friend class MetalScreenshotter; + explicit MetalScreenshot(CGImageRef cgImage); FML_DISALLOW_COPY_AND_ASSIGN(MetalScreenshot); - CGImageRef cgImage_; + CGImageRef cg_image_; CFDataRef pixel_data_; }; } // namespace testing diff --git a/impeller/golden_tests/metal_screenshot.mm b/impeller/golden_tests/metal_screenshot.mm index db274a1de81e2..767ee93cb4132 100644 --- a/impeller/golden_tests/metal_screenshot.mm +++ b/impeller/golden_tests/metal_screenshot.mm @@ -7,14 +7,14 @@ namespace impeller { namespace testing { -MetalScreenshot::MetalScreenshot(CGImageRef cgImage) : cgImage_(cgImage) { +MetalScreenshot::MetalScreenshot(CGImageRef cgImage) : cg_image_(cgImage) { CGDataProviderRef data_provider = CGImageGetDataProvider(cgImage); pixel_data_ = CGDataProviderCopyData(data_provider); } MetalScreenshot::~MetalScreenshot() { CFRelease(pixel_data_); - CGImageRelease(cgImage_); + CGImageRelease(cg_image_); } const UInt8* MetalScreenshot::GetBytes() const { @@ -22,11 +22,15 @@ } size_t MetalScreenshot::GetHeight() const { - return CGImageGetHeight(cgImage_); + return CGImageGetHeight(cg_image_); } size_t MetalScreenshot::GetWidth() const { - return CGImageGetWidth(cgImage_); + return CGImageGetWidth(cg_image_); +} + +size_t MetalScreenshot::GetBytesPerRow() const { + return CGImageGetBytesPerRow(cg_image_); } bool MetalScreenshot::WriteToPNG(const std::string& path) const { @@ -36,7 +40,7 @@ CGImageDestinationRef destination = CGImageDestinationCreateWithURL( (__bridge CFURLRef)output_url, kUTTypePNG, 1, nullptr); if (destination != nullptr) { - CGImageDestinationAddImage(destination, cgImage_, + CGImageDestinationAddImage(destination, cg_image_, (__bridge CFDictionaryRef) @{}); if (CGImageDestinationFinalize(destination)) { diff --git a/impeller/golden_tests/metal_screenshoter.h b/impeller/golden_tests/metal_screenshotter.h similarity index 78% rename from impeller/golden_tests/metal_screenshoter.h rename to impeller/golden_tests/metal_screenshotter.h index 961d3186f5c81..c013cf27d3f48 100644 --- a/impeller/golden_tests/metal_screenshoter.h +++ b/impeller/golden_tests/metal_screenshotter.h @@ -12,15 +12,17 @@ namespace impeller { namespace testing { -/// Converts `Picture`s to `MetalScreenshot`s with the playground backend. -class MetalScreenshoter { +/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the +/// playground backend. +class MetalScreenshotter { public: - MetalScreenshoter(); + MetalScreenshotter(); std::unique_ptr MakeScreenshot(AiksContext& aiks_context, const Picture& picture, const ISize& size = {300, - 300}); + 300}, + bool scale_content = true); const PlaygroundImpl& GetPlayground() const { return *playground_; } diff --git a/impeller/golden_tests/metal_screenshoter.mm b/impeller/golden_tests/metal_screenshotter.mm similarity index 84% rename from impeller/golden_tests/metal_screenshoter.mm rename to impeller/golden_tests/metal_screenshotter.mm index 1d01b65386a8a..5cd6e0014c61d 100644 --- a/impeller/golden_tests/metal_screenshoter.mm +++ b/impeller/golden_tests/metal_screenshotter.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/golden_tests/metal_screenshoter.h" +#include "flutter/impeller/golden_tests/metal_screenshotter.h" #include #include "impeller/renderer/backend/metal/context_mtl.h" @@ -13,17 +13,19 @@ namespace impeller { namespace testing { -MetalScreenshoter::MetalScreenshoter() { +MetalScreenshotter::MetalScreenshotter() { FML_CHECK(::glfwInit() == GLFW_TRUE); playground_ = PlaygroundImpl::Create(PlaygroundBackend::kMetal, PlaygroundSwitches{}); } -std::unique_ptr MetalScreenshoter::MakeScreenshot( +std::unique_ptr MetalScreenshotter::MakeScreenshot( AiksContext& aiks_context, const Picture& picture, - const ISize& size) { - Vector2 content_scale = playground_->GetContentScale(); + const ISize& size, + bool scale_content) { + Vector2 content_scale = + scale_content ? playground_->GetContentScale() : Vector2{1, 1}; std::shared_ptr image = picture.ToImage( aiks_context, ISize(size.width * content_scale.x, size.height * content_scale.y)); diff --git a/impeller/golden_tests_harvester/pubspec.yaml b/impeller/golden_tests_harvester/pubspec.yaml index 04e93ab85e450..bbb49e9cabbd5 100644 --- a/impeller/golden_tests_harvester/pubspec.yaml +++ b/impeller/golden_tests_harvester/pubspec.yaml @@ -5,7 +5,7 @@ name: golden_tests_harvester publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/dart/pkg, //third_party/dart/third_party/pkg, or diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 08e47647dc7e3..f3d7072ce02b8 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -47,8 +47,8 @@ impeller_component("playground") { "../scene/shaders", "imgui:imgui_impeller_backend", "//flutter/fml", - "//third_party/glfw", - "//third_party/imgui:imgui_glfw", + "//flutter/third_party/glfw", + "//flutter/third_party/imgui:imgui_glfw", ] if (impeller_supports_rendering) { diff --git a/impeller/playground/backend/gles/playground_impl_gles.cc b/impeller/playground/backend/gles/playground_impl_gles.cc index 6e5c3f11ab198..fbf64c44ba35c 100644 --- a/impeller/playground/backend/gles/playground_impl_gles.cc +++ b/impeller/playground/backend/gles/playground_impl_gles.cc @@ -112,8 +112,8 @@ std::shared_ptr PlaygroundImplGLES::GetContext() const { return nullptr; } - auto context = - ContextGLES::Create(std::move(gl), ShaderLibraryMappingsForPlayground()); + auto context = ContextGLES::Create( + std::move(gl), ShaderLibraryMappingsForPlayground(), true); if (!context) { FML_LOG(ERROR) << "Could not create context."; return nullptr; diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index 26ba03bbd2a32..82d8d0e25ffbc 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -93,6 +93,15 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) return; } + // Without this, the playground will timeout waiting for the presentation. + // It's better to have some Vulkan validation tests running on CI to catch + // regressions, but for now this is a workaround. + // + // TODO(matanlurey): https://github.com/flutter/flutter/issues/134852. + // + // (Note, if you're using MoltenVK, or Linux, you can comment out this line). + context_vk->SetSyncPresentation(true); + VkSurfaceKHR vk_surface; auto res = vk::Result{::glfwCreateWindowSurface( context_vk->GetInstance(), // instance diff --git a/impeller/playground/imgui/BUILD.gn b/impeller/playground/imgui/BUILD.gn index 497bb5c199c1d..19bb195876f4d 100644 --- a/impeller/playground/imgui/BUILD.gn +++ b/impeller/playground/imgui/BUILD.gn @@ -19,7 +19,7 @@ impeller_shaders("imgui_shaders") { impeller_component("imgui_impeller_backend") { public_deps = [ ":imgui_shaders", - "//third_party/imgui", + "//flutter/third_party/imgui", ] deps = [ "//flutter/impeller/renderer" ] diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index 93eb4eb3ce204..8d2f591a561dd 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -102,7 +102,7 @@ bool Playground::SupportsBackend(PlaygroundBackend backend) { return false; #endif // IMPELLER_ENABLE_OPENGLES case PlaygroundBackend::kVulkan: -#if IMPELLER_ENABLE_VULKAN +#if IMPELLER_ENABLE_VULKAN && IMPELLER_ENABLE_VULKAN_PLAYGROUNDS return true; #else // IMPELLER_ENABLE_VULKAN return false; diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 039777a590a55..27b483f6d2da3 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -92,6 +92,8 @@ class Playground { virtual bool ShouldKeepRendering() const; + void SetWindowSize(ISize size); + private: struct GLFWInitializer; @@ -105,8 +107,6 @@ class Playground { void SetCursorPosition(Point pos); - void SetWindowSize(ISize size); - FML_DISALLOW_COPY_AND_ASSIGN(Playground); }; diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn index 7141abc3304ed..4188d25478250 100644 --- a/impeller/renderer/backend/gles/BUILD.gn +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//flutter/vulkan/config.gni") import("../../../tools/impeller.gni") config("gles_config") { @@ -10,6 +11,22 @@ config("gles_config") { include_dirs = [ "//third_party/angle/include" ] } +impeller_component("gles_unittests") { + testonly = true + sources = [ + "test/capabilities_unittests.cc", + "test/formats_gles_unittests.cc", + "test/gpu_tracer_gles_unittests.cc", + "test/mock_gles.cc", + "test/mock_gles.h", + "test/mock_gles_unittests.cc", + ] + deps = [ + ":gles", + "//flutter/testing:testing_lib", + ] +} + impeller_component("gles") { public_configs = [] @@ -35,6 +52,8 @@ impeller_component("gles") { "formats_gles.cc", "formats_gles.h", "gles.h", + "gpu_tracer_gles.cc", + "gpu_tracer_gles.h", "handle_gles.cc", "handle_gles.h", "pipeline_gles.cc", diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc index d6e56b6996081..5fbfc802e5f9c 100644 --- a/impeller/renderer/backend/gles/blit_command_gles.cc +++ b/impeller/renderer/backend/gles/blit_command_gles.cc @@ -41,7 +41,7 @@ static std::optional ConfigureFBO( gl.BindFramebuffer(fbo_type, fbo); if (!TextureGLES::Cast(*texture).SetAsFramebufferAttachment( - fbo_type, fbo, TextureGLES::AttachmentPoint::kColor0)) { + fbo_type, TextureGLES::AttachmentPoint::kColor0)) { VALIDATION_LOG << "Could not attach texture to framebuffer."; DeleteFBO(gl, fbo, fbo_type); return std::nullopt; diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc index 797f28364b790..92364c4c7cf20 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.cc +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -4,19 +4,13 @@ #include "impeller/renderer/backend/gles/blit_pass_gles.h" -#include #include #include "flutter/fml/trace_event.h" -#include "impeller/base/config.h" -#include "impeller/base/validation.h" +#include "fml/closure.h" #include "impeller/core/formats.h" #include "impeller/renderer/backend/gles/blit_command_gles.h" -#include "impeller/renderer/backend/gles/device_buffer_gles.h" -#include "impeller/renderer/backend/gles/formats_gles.h" -#include "impeller/renderer/backend/gles/pipeline_gles.h" #include "impeller/renderer/backend/gles/proc_table_gles.h" -#include "impeller/renderer/backend/gles/texture_gles.h" namespace impeller { diff --git a/impeller/renderer/backend/gles/capabilities_gles.cc b/impeller/renderer/backend/gles/capabilities_gles.cc index fff4e5889808e..37bd3e665d90d 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.cc +++ b/impeller/renderer/backend/gles/capabilities_gles.cc @@ -8,6 +8,17 @@ namespace impeller { +// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_shader_framebuffer_fetch.txt +static const constexpr char* kFramebufferFetchExt = + "GL_EXT_shader_framebuffer_fetch"; + +static const constexpr char* kTextureBorderClampExt = + "GL_EXT_texture_border_clamp"; +static const constexpr char* kNvidiaTextureBorderClampExt = + "GL_NV_texture_border_clamp"; +static const constexpr char* kOESTextureBorderClampExt = + "GL_OES_texture_border_clamp"; + CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) { { GLint value = 0; @@ -86,6 +97,15 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) { gl.GetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &value); num_shader_binary_formats = value; } + + supports_framebuffer_fetch_ = + gl.GetDescription()->HasExtension(kFramebufferFetchExt); + + if (gl.GetDescription()->HasExtension(kTextureBorderClampExt) || + gl.GetDescription()->HasExtension(kNvidiaTextureBorderClampExt) || + gl.GetDescription()->HasExtension(kOESTextureBorderClampExt)) { + supports_decal_sampler_address_mode_ = true; + } } size_t CapabilitiesGLES::GetMaxTextureUnits(ShaderStage stage) const { @@ -103,4 +123,60 @@ size_t CapabilitiesGLES::GetMaxTextureUnits(ShaderStage stage) const { FML_UNREACHABLE(); } +bool CapabilitiesGLES::SupportsOffscreenMSAA() const { + return false; +} + +bool CapabilitiesGLES::SupportsSSBO() const { + return false; +} + +bool CapabilitiesGLES::SupportsBufferToTextureBlits() const { + return false; +} + +bool CapabilitiesGLES::SupportsTextureToTextureBlits() const { + return false; +} + +bool CapabilitiesGLES::SupportsFramebufferFetch() const { + return supports_framebuffer_fetch_; +} + +bool CapabilitiesGLES::SupportsCompute() const { + return false; +} + +bool CapabilitiesGLES::SupportsComputeSubgroups() const { + return false; +} + +bool CapabilitiesGLES::SupportsReadFromOnscreenTexture() const { + return false; +} + +bool CapabilitiesGLES::SupportsReadFromResolve() const { + return false; +} + +bool CapabilitiesGLES::SupportsDecalSamplerAddressMode() const { + return supports_decal_sampler_address_mode_; +} + +bool CapabilitiesGLES::SupportsDeviceTransientTextures() const { + return false; +} + +PixelFormat CapabilitiesGLES::GetDefaultColorFormat() const { + return PixelFormat::kR8G8B8A8UNormInt; +} + +PixelFormat CapabilitiesGLES::GetDefaultStencilFormat() const { + return PixelFormat::kS8UInt; +} + +PixelFormat CapabilitiesGLES::GetDefaultDepthStencilFormat() const { + return PixelFormat::kD24UnormS8Uint; +} + } // namespace impeller diff --git a/impeller/renderer/backend/gles/capabilities_gles.h b/impeller/renderer/backend/gles/capabilities_gles.h index edcfb99892c9c..3bcd0cfd85d27 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.h +++ b/impeller/renderer/backend/gles/capabilities_gles.h @@ -6,16 +6,31 @@ #include -#include "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" #include "impeller/core/shader_types.h" #include "impeller/geometry/size.h" +#include "impeller/renderer/capabilities.h" namespace impeller { class ProcTableGLES; -struct CapabilitiesGLES { - CapabilitiesGLES(const ProcTableGLES& gl); +//------------------------------------------------------------------------------ +/// @brief The Vulkan layers and extensions wrangler. +/// +class CapabilitiesGLES final + : public Capabilities, + public BackendCast { + public: + explicit CapabilitiesGLES(const ProcTableGLES& gl); + + CapabilitiesGLES(const CapabilitiesGLES&) = delete; + + CapabilitiesGLES(CapabilitiesGLES&&) = delete; + + CapabilitiesGLES& operator=(const CapabilitiesGLES&) = delete; + + CapabilitiesGLES& operator=(CapabilitiesGLES&&) = delete; // Must be at least 8. size_t max_combined_texture_image_units = 8; @@ -57,6 +72,52 @@ struct CapabilitiesGLES { size_t num_shader_binary_formats = 0; size_t GetMaxTextureUnits(ShaderStage stage) const; + + // |Capabilities| + bool SupportsOffscreenMSAA() const override; + + // |Capabilities| + bool SupportsSSBO() const override; + + // |Capabilities| + bool SupportsBufferToTextureBlits() const override; + + // |Capabilities| + bool SupportsTextureToTextureBlits() const override; + + // |Capabilities| + bool SupportsFramebufferFetch() const override; + + // |Capabilities| + bool SupportsCompute() const override; + + // |Capabilities| + bool SupportsComputeSubgroups() const override; + + // |Capabilities| + bool SupportsReadFromOnscreenTexture() const override; + + // |Capabilities| + bool SupportsReadFromResolve() const override; + + // |Capabilities| + bool SupportsDecalSamplerAddressMode() const override; + + // |Capabilities| + bool SupportsDeviceTransientTextures() const override; + + // |Capabilities| + PixelFormat GetDefaultColorFormat() const override; + + // |Capabilities| + PixelFormat GetDefaultStencilFormat() const override; + + // |Capabilities| + PixelFormat GetDefaultDepthStencilFormat() const override; + + private: + bool supports_framebuffer_fetch_ = false; + bool supports_decal_sampler_address_mode_ = false; }; } // namespace impeller diff --git a/impeller/renderer/backend/gles/context_gles.cc b/impeller/renderer/backend/gles/context_gles.cc index 3ab34acc40bd9..779255dffa6c4 100644 --- a/impeller/renderer/backend/gles/context_gles.cc +++ b/impeller/renderer/backend/gles/context_gles.cc @@ -3,22 +3,27 @@ // found in the LICENSE file. #include "impeller/renderer/backend/gles/context_gles.h" +#include #include "impeller/base/config.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/command_buffer_gles.h" +#include "impeller/renderer/backend/gles/gpu_tracer_gles.h" namespace impeller { std::shared_ptr ContextGLES::Create( std::unique_ptr gl, - const std::vector>& shader_libraries) { + const std::vector>& shader_libraries, + bool enable_gpu_tracing) { return std::shared_ptr( - new ContextGLES(std::move(gl), shader_libraries)); + new ContextGLES(std::move(gl), shader_libraries, enable_gpu_tracing)); } -ContextGLES::ContextGLES(std::unique_ptr gl, - const std::vector>& - shader_libraries_mappings) { +ContextGLES::ContextGLES( + std::unique_ptr gl, + const std::vector>& shader_libraries_mappings, + bool enable_gpu_tracing) { reactor_ = std::make_shared(std::move(gl)); if (!reactor_->IsValid()) { VALIDATION_LOG << "Could not create valid reactor."; @@ -52,34 +57,16 @@ ContextGLES::ContextGLES(std::unique_ptr gl, } } + device_capabilities_ = reactor_->GetProcTable().GetCapabilities(); + // Create the sampler library. { sampler_library_ = - std::shared_ptr(new SamplerLibraryGLES()); - } - - // Create the device capabilities. - { - device_capabilities_ = - CapabilitiesBuilder() - .SetSupportsOffscreenMSAA(false) - .SetSupportsSSBO(false) - .SetSupportsBufferToTextureBlits(false) - .SetSupportsTextureToTextureBlits( - reactor_->GetProcTable().BlitFramebuffer.IsAvailable()) - .SetSupportsFramebufferFetch(false) - .SetDefaultColorFormat(PixelFormat::kR8G8B8A8UNormInt) - .SetDefaultStencilFormat(PixelFormat::kS8UInt) - .SetDefaultDepthStencilFormat(PixelFormat::kD24UnormS8Uint) - .SetSupportsCompute(false) - .SetSupportsComputeSubgroups(false) - .SetSupportsReadFromResolve(false) - .SetSupportsReadFromOnscreenTexture(false) - .SetSupportsDecalSamplerAddressMode(false) - .SetSupportsDeviceTransientTextures(false) - .Build(); + std::shared_ptr(new SamplerLibraryGLES( + device_capabilities_->SupportsDecalSamplerAddressMode())); } - + gpu_tracer_ = std::make_shared(GetReactor()->GetProcTable(), + enable_gpu_tracing); is_valid_ = true; } diff --git a/impeller/renderer/backend/gles/context_gles.h b/impeller/renderer/backend/gles/context_gles.h index 92054cb413bee..0d097c3f18a87 100644 --- a/impeller/renderer/backend/gles/context_gles.h +++ b/impeller/renderer/backend/gles/context_gles.h @@ -4,10 +4,13 @@ #pragma once +#include +#include #include "flutter/fml/macros.h" #include "impeller/base/backend_cast.h" #include "impeller/renderer/backend/gles/allocator_gles.h" -#include "impeller/renderer/backend/gles/command_buffer_gles.h" +#include "impeller/renderer/backend/gles/capabilities_gles.h" +#include "impeller/renderer/backend/gles/gpu_tracer_gles.h" #include "impeller/renderer/backend/gles/pipeline_library_gles.h" #include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/backend/gles/sampler_library_gles.h" @@ -23,7 +26,8 @@ class ContextGLES final : public Context, public: static std::shared_ptr Create( std::unique_ptr gl, - const std::vector>& shader_libraries); + const std::vector>& shader_libraries, + bool enable_gpu_tracing); // |Context| ~ContextGLES() override; @@ -38,18 +42,26 @@ class ContextGLES final : public Context, bool RemoveReactorWorker(ReactorGLES::WorkerID id); + std::shared_ptr GetGPUTracer() const { return gpu_tracer_; } + private: ReactorGLES::Ref reactor_; std::shared_ptr shader_library_; std::shared_ptr pipeline_library_; std::shared_ptr sampler_library_; std::shared_ptr resource_allocator_; + std::shared_ptr gpu_tracer_; + + // Note: This is stored separately from the ProcTableGLES CapabilitiesGLES + // in order to satisfy the Context::GetCapabilities signature which returns + // a reference. std::shared_ptr device_capabilities_; bool is_valid_ = false; ContextGLES( std::unique_ptr gl, - const std::vector>& shader_libraries); + const std::vector>& shader_libraries, + bool enable_gpu_tracing); // |Context| std::string DescribeGpuModel() const override; diff --git a/impeller/renderer/backend/gles/description_gles.h b/impeller/renderer/backend/gles/description_gles.h index 7630d431c9282..bac5935b7298e 100644 --- a/impeller/renderer/backend/gles/description_gles.h +++ b/impeller/renderer/backend/gles/description_gles.h @@ -16,7 +16,7 @@ class ProcTableGLES; class DescriptionGLES { public: - DescriptionGLES(const ProcTableGLES& gl); + explicit DescriptionGLES(const ProcTableGLES& gl); ~DescriptionGLES(); @@ -28,6 +28,7 @@ class DescriptionGLES { bool HasExtension(const std::string& ext) const; + /// @brief Returns whether GLES includes the debug extension. bool HasDebugExtension() const; private: diff --git a/impeller/renderer/backend/gles/formats_gles.cc b/impeller/renderer/backend/gles/formats_gles.cc index 7d5f3b832d946..0f8b719b40869 100644 --- a/impeller/renderer/backend/gles/formats_gles.cc +++ b/impeller/renderer/backend/gles/formats_gles.cc @@ -6,6 +6,21 @@ namespace impeller { -// +std::string DebugToFramebufferError(int status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "Unknown error code: " + std::to_string(status); + } +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/formats_gles.h b/impeller/renderer/backend/gles/formats_gles.h index 4e789707af294..ea56ebb6e516f 100644 --- a/impeller/renderer/backend/gles/formats_gles.h +++ b/impeller/renderer/backend/gles/formats_gles.h @@ -194,4 +194,6 @@ constexpr std::optional ToTextureTarget(TextureType type) { FML_UNREACHABLE(); } +std::string DebugToFramebufferError(int status); + } // namespace impeller diff --git a/impeller/renderer/backend/gles/gles.h b/impeller/renderer/backend/gles/gles.h index 599e3c9433c97..9022cd02cfe88 100644 --- a/impeller/renderer/backend/gles/gles.h +++ b/impeller/renderer/backend/gles/gles.h @@ -4,6 +4,13 @@ #pragma once +// IWYU pragma: begin_exports #include "GLES3/gl3.h" + +// Defines for extension enums. +#define IMPELLER_GL_CLAMP_TO_BORDER 0x812D +#define IMPELLER_GL_TEXTURE_BORDER_COLOR 0x1004 + #define GL_GLEXT_PROTOTYPES #include "GLES2/gl2ext.h" +// IWYU pragma: end_exports diff --git a/impeller/renderer/backend/gles/gpu_tracer_gles.cc b/impeller/renderer/backend/gles/gpu_tracer_gles.cc new file mode 100644 index 0000000000000..6e1e167877b3d --- /dev/null +++ b/impeller/renderer/backend/gles/gpu_tracer_gles.cc @@ -0,0 +1,88 @@ +// 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/gpu_tracer_gles.h" +#include +#include "fml/trace_event.h" + +namespace impeller { + +GPUTracerGLES::GPUTracerGLES(const ProcTableGLES& gl, bool enable_tracing) { +#ifdef IMPELLER_DEBUG + auto desc = gl.GetDescription(); + enabled_ = + enable_tracing && desc->HasExtension("GL_EXT_disjoint_timer_query"); +#endif // IMPELLER_DEBUG +} + +void GPUTracerGLES::MarkFrameStart(const ProcTableGLES& gl) { + if (!enabled_ || active_frame_.has_value() || + std::this_thread::get_id() != raster_thread_) { + return; + } + + // At the beginning of a frame, check the status of all pending + // previous queries. + ProcessQueries(gl); + + uint32_t query = 0; + gl.GenQueriesEXT(1, &query); + if (query == 0) { + return; + } + + active_frame_ = query; + gl.BeginQueryEXT(GL_TIME_ELAPSED_EXT, query); +} + +void GPUTracerGLES::RecordRasterThread() { + raster_thread_ = std::this_thread::get_id(); +} + +void GPUTracerGLES::ProcessQueries(const ProcTableGLES& gl) { + // For reasons unknown to me, querying the state of more than + // one query object per frame causes crashes on a Pixel 6 pro. + // It does not crash on an S10. + while (!pending_traces_.empty()) { + auto query = pending_traces_.front(); + + // First check if the query is complete without blocking + // on the result. Incomplete results are left in the pending + // trace vector and will not be checked again for another + // frame. + GLuint available = GL_FALSE; + gl.GetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &available); + + if (available != GL_TRUE) { + // If a query is not available, then all subsequent queries will be + // unavailable. + return; + } + // Return the timer resolution in nanoseconds. + uint64_t duration = 0; + gl.GetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &duration); + auto gpu_ms = duration / 1000000.0; + + FML_TRACE_COUNTER("flutter", "GPUTracer", + reinterpret_cast(this), // Trace Counter ID + "FrameTimeMS", gpu_ms); + gl.DeleteQueriesEXT(1, &query); + pending_traces_.pop_front(); + } +} + +void GPUTracerGLES::MarkFrameEnd(const ProcTableGLES& gl) { + if (!enabled_ || std::this_thread::get_id() != raster_thread_ || + !active_frame_.has_value()) { + return; + } + + auto query = active_frame_.value(); + gl.EndQueryEXT(GL_TIME_ELAPSED_EXT); + + pending_traces_.push_back(query); + active_frame_ = std::nullopt; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gpu_tracer_gles.h b/impeller/renderer/backend/gles/gpu_tracer_gles.h new file mode 100644 index 0000000000000..8de6963fc6759 --- /dev/null +++ b/impeller/renderer/backend/gles/gpu_tracer_gles.h @@ -0,0 +1,51 @@ +// 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. + +#pragma once + +#include +#include + +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { + +/// @brief Trace GPU execution times using GL_EXT_disjoint_timer_query on GLES. +/// +/// Note: there are a substantial number of GPUs where usage of the this API is +/// known to cause crashes. As a result, this functionality is disabled by +/// default and can only be enabled in debug/profile mode via a specific opt-in +/// flag that is exposed in the Android manifest. +/// +/// To enable, add the following metadata to the application's Android manifest: +/// +class GPUTracerGLES { + public: + GPUTracerGLES(const ProcTableGLES& gl, bool enable_tracing); + + ~GPUTracerGLES() = default; + + /// @brief Record the thread id of the raster thread. + void RecordRasterThread(); + + /// @brief Record the start of a frame workload, if one hasn't already been + /// started. + void MarkFrameStart(const ProcTableGLES& gl); + + /// @brief Record the end of a frame workload. + void MarkFrameEnd(const ProcTableGLES& gl); + + private: + void ProcessQueries(const ProcTableGLES& gl); + + std::deque pending_traces_; + std::optional active_frame_ = std::nullopt; + std::thread::id raster_thread_; + + bool enabled_ = false; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.cc b/impeller/renderer/backend/gles/pipeline_library_gles.cc index 8ad3bcc9aaa7c..b0190826e5771 100644 --- a/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -9,6 +9,7 @@ #include "flutter/fml/container.h" #include "flutter/fml/trace_event.h" +#include "fml/closure.h" #include "impeller/base/promise.h" #include "impeller/renderer/backend/gles/pipeline_gles.h" #include "impeller/renderer/backend/gles/shader_function_gles.h" diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.h b/impeller/renderer/backend/gles/pipeline_library_gles.h index aca495418eabb..75f5d0e4fe8f5 100644 --- a/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -23,7 +23,7 @@ class PipelineLibraryGLES final : public PipelineLibrary { ReactorGLES::Ref reactor_; PipelineMap pipelines_; - PipelineLibraryGLES(ReactorGLES::Ref reactor); + explicit PipelineLibraryGLES(ReactorGLES::Ref reactor); // |PipelineLibrary| bool IsValid() const override; diff --git a/impeller/renderer/backend/gles/proc_table_gles.cc b/impeller/renderer/backend/gles/proc_table_gles.cc index b3c8a8ff434f4..2541424ad9439 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.cc +++ b/impeller/renderer/backend/gles/proc_table_gles.cc @@ -9,6 +9,8 @@ #include "impeller/base/allocation.h" #include "impeller/base/comparable.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/capabilities_gles.h" +#include "impeller/renderer/capabilities.h" namespace impeller { @@ -32,6 +34,20 @@ const char* GLErrorToString(GLenum value) { return "Unknown."; } +bool GLErrorIsFatal(GLenum value) { + switch (value) { + case GL_NO_ERROR: + return false; + case GL_INVALID_ENUM: + case GL_INVALID_VALUE: + case GL_INVALID_OPERATION: + case GL_INVALID_FRAMEBUFFER_OPERATION: + case GL_OUT_OF_MEMORY: + return true; + } + return false; +} + ProcTableGLES::Resolver WrappedResolver( const ProcTableGLES::Resolver& resolver) { return [resolver](const char* function_name) -> void* { @@ -111,7 +127,7 @@ ProcTableGLES::ProcTableGLES(Resolver resolver) { DiscardFramebufferEXT.Reset(); } - capabilities_ = std::make_unique(*this); + capabilities_ = std::make_shared(*this); is_valid_ = true; } @@ -134,8 +150,9 @@ const DescriptionGLES* ProcTableGLES::GetDescription() const { return description_.get(); } -const CapabilitiesGLES* ProcTableGLES::GetCapabilities() const { - return capabilities_.get(); +const std::shared_ptr& ProcTableGLES::GetCapabilities() + const { + return capabilities_; } static const char* FramebufferStatusToString(GLenum status) { @@ -305,9 +322,11 @@ bool ProcTableGLES::SetDebugLabel(DebugResourceType type, } void ProcTableGLES::PushDebugGroup(const std::string& label) const { +#ifdef IMPELLER_DEBUG if (debug_label_max_length_ <= 0) { return; } + UniqueID id; const auto label_length = std::min(debug_label_max_length_ - 1, label.size()); @@ -316,13 +335,17 @@ void ProcTableGLES::PushDebugGroup(const std::string& label) const { label_length, // length label.data() // message ); +#endif // IMPELLER_DEBUG } void ProcTableGLES::PopDebugGroup() const { +#ifdef IMPELLER_DEBUG if (debug_label_max_length_ <= 0) { return; } + PopDebugGroupKHR(); +#endif // IMPELLER_DEBUG } std::string ProcTableGLES::GetProgramInfoLogString(GLuint program) const { diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h index 0af40009cdf13..643ddd413f3f9 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -6,12 +6,10 @@ #include #include -#include #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "flutter/fml/trace_event.h" #include "impeller/renderer/backend/gles/capabilities_gles.h" #include "impeller/renderer/backend/gles/description_gles.h" #include "impeller/renderer/backend/gles/gles.h" @@ -19,9 +17,13 @@ namespace impeller { const char* GLErrorToString(GLenum value); +bool GLErrorIsFatal(GLenum value); struct AutoErrorCheck { const PFNGLGETERRORPROC error_fn; + + // TODO(matanlurey) Change to string_view. + // https://github.com/flutter/flutter/issues/135922 const char* name; AutoErrorCheck(PFNGLGETERRORPROC error, const char* name) @@ -30,9 +32,18 @@ struct AutoErrorCheck { ~AutoErrorCheck() { if (error_fn) { auto error = error_fn(); - FML_CHECK(error == GL_NO_ERROR) - << "GL Error " << GLErrorToString(error) << "(" << error << ")" - << " encountered on call to " << name; + if (error == GL_NO_ERROR) { + return; + } + if (GLErrorIsFatal(error)) { + FML_LOG(FATAL) << "Fatal GL Error " << GLErrorToString(error) << "(" + << error << ")" + << " encountered on call to " << name; + } else { + FML_LOG(ERROR) << "GL Error " << GLErrorToString(error) << "(" << error + << ")" + << " encountered on call to " << name; + } } } }; @@ -41,6 +52,9 @@ template struct GLProc { using GLFunctionType = T; + // TODO(matanlurey) Change to string_view. + // https://github.com/flutter/flutter/issues/135922 + //---------------------------------------------------------------------------- /// The name of the GL function. /// @@ -65,9 +79,14 @@ struct GLProc { /// template auto operator()(Args&&... args) const { -#ifdef IMPELLER_ERROR_CHECK_ALL_GL_CALLS +#ifdef IMPELLER_DEBUG AutoErrorCheck error(error_fn, name); -#endif // IMPELLER_ERROR_CHECK_ALL_GL_CALLS + // We check for the existence of extensions, and reset the function pointer + // but it's still called unconditionally below, and will segfault. This + // validation log will at least give us a hint as to what's going on. + FML_CHECK(IsAvailable()) << "GL function " << name << " is not available. " + << "This is likely due to a missing extension."; +#endif // IMPELLER_DEBUG #ifdef IMPELLER_TRACE_ALL_GL_CALLS TRACE_EVENT0("impeller", name); #endif // IMPELLER_TRACE_ALL_GL_CALLS @@ -77,7 +96,6 @@ struct GLProc { constexpr bool IsAvailable() const { return function != nullptr; } void Reset() { - name = nullptr; function = nullptr; error_fn = nullptr; } @@ -157,6 +175,7 @@ struct GLProc { PROC(StencilOpSeparate); \ PROC(TexImage2D); \ PROC(TexParameteri); \ + PROC(TexParameterfv); \ PROC(Uniform1fv); \ PROC(Uniform1i); \ PROC(Uniform2fv); \ @@ -170,11 +189,20 @@ struct GLProc { #define FOR_EACH_IMPELLER_GLES3_PROC(PROC) PROC(BlitFramebuffer); -#define FOR_EACH_IMPELLER_EXT_PROC(PROC) \ - PROC(DiscardFramebufferEXT); \ - PROC(PushDebugGroupKHR); \ - PROC(PopDebugGroupKHR); \ - PROC(ObjectLabelKHR); +#define FOR_EACH_IMPELLER_EXT_PROC(PROC) \ + PROC(DebugMessageControlKHR); \ + PROC(DiscardFramebufferEXT); \ + PROC(FramebufferTexture2DMultisampleEXT) \ + PROC(PushDebugGroupKHR); \ + PROC(PopDebugGroupKHR); \ + PROC(ObjectLabelKHR); \ + PROC(RenderbufferStorageMultisampleEXT); \ + PROC(GenQueriesEXT); \ + PROC(DeleteQueriesEXT); \ + PROC(GetQueryObjectui64vEXT); \ + PROC(BeginQueryEXT); \ + PROC(EndQueryEXT); \ + PROC(GetQueryObjectuivEXT); enum class DebugResourceType { kTexture, @@ -188,7 +216,8 @@ enum class DebugResourceType { class ProcTableGLES { public: using Resolver = std::function; - ProcTableGLES(Resolver resolver); + explicit ProcTableGLES(Resolver resolver); + ProcTableGLES(ProcTableGLES&& other) = default; ~ProcTableGLES(); @@ -207,7 +236,7 @@ class ProcTableGLES { const DescriptionGLES* GetDescription() const; - const CapabilitiesGLES* GetCapabilities() const; + const std::shared_ptr& GetCapabilities() const; std::string DescribeCurrentFramebuffer() const; @@ -226,7 +255,7 @@ class ProcTableGLES { private: bool is_valid_ = false; std::unique_ptr description_; - std::unique_ptr capabilities_; + std::shared_ptr capabilities_; GLint debug_label_max_length_ = 0; FML_DISALLOW_COPY_AND_ASSIGN(ProcTableGLES); diff --git a/impeller/renderer/backend/gles/reactor_gles.cc b/impeller/renderer/backend/gles/reactor_gles.cc index f4d9ca9396233..df22c68573a80 100644 --- a/impeller/renderer/backend/gles/reactor_gles.cc +++ b/impeller/renderer/backend/gles/reactor_gles.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/trace_event.h" +#include "fml/logging.h" #include "impeller/base/validation.h" namespace impeller { @@ -158,6 +159,10 @@ bool ReactorGLES::React() { } TRACE_EVENT0("impeller", "ReactorGLES::React"); while (HasPendingOperations()) { + // Both the raster thread and the IO thread can flush queued operations. + // Ensure that execution of the ops is serialized. + Lock execution_lock(ops_execution_mutex_); + if (!ReactOnce()) { return false; } @@ -233,6 +238,13 @@ bool ReactorGLES::ConsolidateHandles() { bool ReactorGLES::FlushOps() { TRACE_EVENT0("impeller", __FUNCTION__); + +#ifdef IMPELLER_DEBUG + // glDebugMessageControl sometimes must be called before glPushDebugGroup: + // https://github.com/flutter/flutter/issues/135715#issuecomment-1740153506 + SetupDebugGroups(); +#endif + // Do NOT hold the ops or handles locks while performing operations in case // the ops enqueue more ops. decltype(ops_) ops; @@ -247,6 +259,18 @@ bool ReactorGLES::FlushOps() { return true; } +void ReactorGLES::SetupDebugGroups() { + // Setup of a default active debug group: Filter everything in. + if (proc_table_->DebugMessageControlKHR.IsAvailable()) { + proc_table_->DebugMessageControlKHR(GL_DONT_CARE, // source + GL_DONT_CARE, // type + GL_DONT_CARE, // severity + 0, // count + nullptr, // ids + GL_TRUE); // enabled + } +} + void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) { if (!can_set_debug_labels_) { return; diff --git a/impeller/renderer/backend/gles/reactor_gles.h b/impeller/renderer/backend/gles/reactor_gles.h index 70d3bda65b77f..e20b7d42105be 100644 --- a/impeller/renderer/backend/gles/reactor_gles.h +++ b/impeller/renderer/backend/gles/reactor_gles.h @@ -8,7 +8,6 @@ #include #include -#include "flutter/fml/closure.h" #include "flutter/fml/macros.h" #include "impeller/base/thread.h" #include "impeller/renderer/backend/gles/handle_gles.h" @@ -16,43 +15,207 @@ namespace impeller { +//------------------------------------------------------------------------------ +/// @brief The reactor attempts to make thread-safe usage of OpenGL ES +/// easier to reason about. +/// +/// In the other Impeller backends (like Metal and Vulkan), +/// resources can be created, used, and deleted on any thread with +/// relatively few restrictions. However, OpenGL resources can only +/// be created, used, and deleted on a thread on which an OpenGL +/// context (or one in the same sharegroup) is current. +/// +/// There aren't too many OpenGL contexts to go around and making +/// the caller reason about the timing and threading requirement +/// only when the OpenGL backend is in use is tedious. To work +/// around this tedium, there is an abstraction between the +/// resources and their handles in OpenGL. The reactor is this +/// abstraction. +/// +/// The reactor is thread-safe and can created, used, and collected +/// on any thread. +/// +/// Reactor handles `HandleGLES` can be created, used, and collected +/// on any thread. These handles can be to textures, buffers, etc.. +/// +/// Operations added to the reactor are guaranteed to run on a +/// worker within a finite amount of time unless the reactor itself +/// is torn down or there are no workers. These operations may run +/// on the calling thread immediately if a worker is active on the +/// current thread and can perform reactions. The operations are +/// guaranteed to run with an OpenGL context current and all reactor +/// handles having live OpenGL handle counterparts. +/// +/// Creating a handle in the reactor doesn't mean an OpenGL handle +/// is created immediately. OpenGL handles become live before the +/// next reaction. Similarly, dropping the last reference to a +/// reactor handle means that the OpenGL handle will be deleted at +/// some point in the near future. +/// class ReactorGLES { public: using WorkerID = UniqueID; + //---------------------------------------------------------------------------- + /// @brief A delegate implemented by a thread on which an OpenGL context + /// is current. There may be multiple workers for the reactor to + /// perform reactions on. In that case, it is the workers + /// responsibility to ensure that all of them use either the same + /// OpenGL context or multiple OpenGL contexts in the same + /// sharegroup. + /// class Worker { public: virtual ~Worker() = default; + //-------------------------------------------------------------------------- + /// @brief Determines the ability of the worker to service a reaction + /// on the current thread. The OpenGL context must be current on + /// the thread if the worker says it is able to service a + /// reaction. + /// + /// @param[in] reactor The reactor + /// + /// @return If the worker is able to service a reaction. The reactor + /// assumes the context is already current if true. + /// virtual bool CanReactorReactOnCurrentThreadNow( const ReactorGLES& reactor) const = 0; }; using Ref = std::shared_ptr; - ReactorGLES(std::unique_ptr gl); - + //---------------------------------------------------------------------------- + /// @brief Create a new reactor. There are expensive and only one per + /// application instance is necessary. + /// + /// @param[in] gl The proc table for GL access. This is necessary for the + /// reactor to be able to create and collect OpenGL handles. + /// + explicit ReactorGLES(std::unique_ptr gl); + + //---------------------------------------------------------------------------- + /// @brief Destroy a reactor. + /// ~ReactorGLES(); + //---------------------------------------------------------------------------- + /// @brief If this is a valid reactor. Invalid reactors must be discarded + /// immediately. + /// + /// @return If this reactor is valid. + /// bool IsValid() const; + //---------------------------------------------------------------------------- + /// @brief Adds a worker to the reactor. Each new worker must ensure that + /// the context it manages is the same as the other workers in the + /// reactor or in the same sharegroup. + /// + /// @param[in] worker The worker + /// + /// @return The worker identifier. This identifier can be used to remove + /// the worker from the reactor later. + /// WorkerID AddWorker(std::weak_ptr worker); - bool RemoveWorker(WorkerID); - + //---------------------------------------------------------------------------- + /// @brief Remove a previously added worker from the reactor. If the + /// reactor has no workers, pending added operations will never + /// run. + /// + /// @param[in] id The worker identifier previously returned by `AddWorker`. + /// + /// @return If a worker with the given identifer was successfully removed + /// from the reactor. + /// + bool RemoveWorker(WorkerID id); + + //---------------------------------------------------------------------------- + /// @brief Get the OpenGL proc. table the reactor uses to manage handles. + /// + /// @return The proc table. + /// const ProcTableGLES& GetProcTable() const; + //---------------------------------------------------------------------------- + /// @brief Returns the OpenGL handle for a reactor handle if one is + /// available. This is typically only safe to call within a + /// reaction. That is, within a `ReactorGLES::Operation`. + /// + /// Asking for the OpenGL handle before the reactor has a chance + /// to reactor will return `std::nullopt`. + /// + /// This can be called on any thread but is typically useless + /// outside of a reaction since the handle is useless outside of a + /// reactor operation. + /// + /// @param[in] handle The reactor handle. + /// + /// @return The OpenGL handle if the reactor has had a chance to react. + /// `std::nullopt` otherwise. + /// std::optional GetGLHandle(const HandleGLES& handle) const; + //---------------------------------------------------------------------------- + /// @brief Create a reactor handle. + /// + /// This can be called on any thread. Even one that doesn't have + /// an OpenGL context. + /// + /// @param[in] type The type of handle to create. + /// + /// @return The reactor handle. + /// HandleGLES CreateHandle(HandleType type); + //---------------------------------------------------------------------------- + /// @brief Collect a reactor handle. + /// + /// This can be called on any thread. Even one that doesn't have + /// an OpenGL context. + /// + /// @param[in] handle The reactor handle handle + /// void CollectHandle(HandleGLES handle); + //---------------------------------------------------------------------------- + /// @brief Set the debug label on a reactor handle. + /// + /// This call ensures that the OpenGL debug label is propagated to + /// even the OpenGL handle hasn't been created at the time the + /// caller sets the label. + /// + /// @param[in] handle The handle + /// @param[in] label The label + /// void SetDebugLabel(const HandleGLES& handle, std::string label); using Operation = std::function; + + //---------------------------------------------------------------------------- + /// @brief Adds an operation that the reactor runs on a worker that + /// ensures that an OpenGL context is current. + /// + /// This operation is not guaranteed to run immediately. It will + /// complete in a finite amount of time on any thread as long as + /// there is a reactor worker and the reactor itself is not being + /// torn down. + /// + /// @param[in] operation The operation + /// + /// @return If the operation was successfully queued for completion. + /// [[nodiscard]] bool AddOperation(Operation operation); + //---------------------------------------------------------------------------- + /// @brief Perform a reaction on the current thread if able. + /// + /// It is safe to call this simultaneously from multiple threads + /// at the same time. + /// + /// @return If a reaction was performed on the calling thread. + /// [[nodiscard]] bool React(); private: @@ -63,14 +226,14 @@ class ReactorGLES { LiveHandle() = default; - explicit LiveHandle(std::optional p_name) - : name(std::move(p_name)) {} + explicit LiveHandle(std::optional p_name) : name(p_name) {} constexpr bool IsLive() const { return name.has_value(); } }; std::unique_ptr proc_table_; + Mutex ops_execution_mutex_; mutable Mutex ops_mutex_; std::vector ops_ IPLR_GUARDED_BY(ops_mutex_); @@ -90,7 +253,7 @@ class ReactorGLES { bool can_set_debug_labels_ = false; bool is_valid_ = false; - bool ReactOnce(); + bool ReactOnce() IPLR_REQUIRES(ops_execution_mutex_); bool HasPendingOperations() const; @@ -100,6 +263,8 @@ class ReactorGLES { bool FlushOps(); + void SetupDebugGroups(); + FML_DISALLOW_COPY_AND_ASSIGN(ReactorGLES); }; diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc index 65ce6c2108189..a856feeeff1a1 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -4,13 +4,13 @@ #include "impeller/renderer/backend/gles/render_pass_gles.h" -#include - #include "flutter/fml/trace_event.h" -#include "impeller/base/config.h" +#include "fml/closure.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/context_gles.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/formats_gles.h" +#include "impeller/renderer/backend/gles/gpu_tracer_gles.h" #include "impeller/renderer/backend/gles/pipeline_gles.h" #include "impeller/renderer/backend/gles/texture_gles.h" @@ -143,7 +143,8 @@ struct RenderPassData { const RenderPassData& pass_data, const std::shared_ptr& transients_allocator, const ReactorGLES& reactor, - const std::vector& commands) { + const std::vector& commands, + const std::shared_ptr& tracer) { TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor"); if (commands.empty()) { @@ -151,6 +152,9 @@ struct RenderPassData { } const auto& gl = reactor.GetProcTable(); +#ifdef IMPELLER_DEBUG + tracer->MarkFrameStart(gl); +#endif // IMPELLER_DEBUG fml::ScopedCleanupClosure pop_pass_debug_marker( [&gl]() { gl.PopDebugGroup(); }); @@ -178,25 +182,27 @@ struct RenderPassData { if (auto color = TextureGLES::Cast(pass_data.color_attachment.get())) { if (!color->SetAsFramebufferAttachment( - GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kColor0)) { + GL_FRAMEBUFFER, TextureGLES::AttachmentPoint::kColor0)) { return false; } } if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) { if (!depth->SetAsFramebufferAttachment( - GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kDepth)) { + GL_FRAMEBUFFER, TextureGLES::AttachmentPoint::kDepth)) { return false; } } if (auto stencil = TextureGLES::Cast(pass_data.stencil_attachment.get())) { if (!stencil->SetAsFramebufferAttachment( - GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kStencil)) { + GL_FRAMEBUFFER, TextureGLES::AttachmentPoint::kStencil)) { return false; } } - if (gl.CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - VALIDATION_LOG << "Could not create a complete frambuffer."; + auto status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + VALIDATION_LOG << "Could not create a complete frambuffer: " + << DebugToFramebufferError(status); return false; } } @@ -207,7 +213,12 @@ struct RenderPassData { pass_data.clear_color.alpha // alpha ); if (pass_data.depth_attachment) { + // TODO(bdero): Desktop GL for Apple requires glClearDepth. glClearDepthf + // throws GL_INVALID_OPERATION. + // https://github.com/flutter/flutter/issues/136322 +#if !FML_OS_MACOSX gl.ClearDepthf(pass_data.clear_depth); +#endif } if (pass_data.stencil_attachment) { gl.ClearStencil(pass_data.clear_stencil); @@ -303,7 +314,12 @@ struct RenderPassData { viewport.rect.size.height // height ); if (pass_data.depth_attachment) { + // TODO(bdero): Desktop GL for Apple requires glDepthRange. glDepthRangef + // throws GL_INVALID_OPERATION. + // https://github.com/flutter/flutter/issues/136322 +#if !FML_OS_MACOSX gl.DepthRangef(viewport.depth_range.z_near, viewport.depth_range.z_far); +#endif } //-------------------------------------------------------------------------- @@ -482,6 +498,11 @@ struct RenderPassData { attachments.data() // size ); } +#ifdef IMPELLER_DEBUG + if (is_default_fbo) { + tracer->MarkFrameEnd(gl); + } +#endif // IMPELLER_DEBUG return true; } @@ -539,12 +560,13 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const { } std::shared_ptr shared_this = shared_from_this(); + auto tracer = ContextGLES::Cast(context).GetGPUTracer(); return reactor_->AddOperation([pass_data, allocator = context.GetResourceAllocator(), - render_pass = std::move(shared_this)]( - const auto& reactor) { + render_pass = std::move(shared_this), + tracer](const auto& reactor) { auto result = EncodeCommandsInReactor(*pass_data, allocator, reactor, - render_pass->commands_); + render_pass->commands_, tracer); FML_CHECK(result) << "Must be able to encode GL commands without error."; }); } diff --git a/impeller/renderer/backend/gles/sampler_gles.cc b/impeller/renderer/backend/gles/sampler_gles.cc index f1a54debcbc19..729add5fee14e 100644 --- a/impeller/renderer/backend/gles/sampler_gles.cc +++ b/impeller/renderer/backend/gles/sampler_gles.cc @@ -53,7 +53,8 @@ static GLint ToParam(MinMagFilter minmag_filter, FML_UNREACHABLE(); } -static GLint ToAddressMode(SamplerAddressMode mode) { +static GLint ToAddressMode(SamplerAddressMode mode, + bool supports_decal_sampler_address_mode) { switch (mode) { case SamplerAddressMode::kClampToEdge: return GL_CLAMP_TO_EDGE; @@ -62,7 +63,11 @@ static GLint ToAddressMode(SamplerAddressMode mode) { case SamplerAddressMode::kMirror: return GL_MIRRORED_REPEAT; case SamplerAddressMode::kDecal: - break; // Unsupported. + if (supports_decal_sampler_address_mode) { + return IMPELLER_GL_CLAMP_TO_BORDER; + } else { + return GL_CLAMP_TO_EDGE; + } } FML_UNREACHABLE(); } @@ -92,14 +97,28 @@ bool SamplerGLES::ConfigureBoundTexture(const TextureGLES& texture, mip_filter = desc.mip_filter; } - gl.TexParameteri(target.value(), GL_TEXTURE_MIN_FILTER, + gl.TexParameteri(*target, GL_TEXTURE_MIN_FILTER, ToParam(desc.min_filter, mip_filter)); - gl.TexParameteri(target.value(), GL_TEXTURE_MAG_FILTER, - ToParam(desc.mag_filter)); - gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_S, - ToAddressMode(desc.width_address_mode)); - gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_T, - ToAddressMode(desc.height_address_mode)); + gl.TexParameteri(*target, GL_TEXTURE_MAG_FILTER, ToParam(desc.mag_filter)); + + const auto supports_decal_mode = + gl.GetCapabilities()->SupportsDecalSamplerAddressMode(); + + const auto wrap_s = + ToAddressMode(desc.width_address_mode, supports_decal_mode); + const auto wrap_t = + ToAddressMode(desc.height_address_mode, supports_decal_mode); + + gl.TexParameteri(*target, GL_TEXTURE_WRAP_S, wrap_s); + gl.TexParameteri(*target, GL_TEXTURE_WRAP_T, wrap_t); + + if (wrap_s == IMPELLER_GL_CLAMP_TO_BORDER || + wrap_t == IMPELLER_GL_CLAMP_TO_BORDER) { + // Transparent black. + const GLfloat border_color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + gl.TexParameterfv(*target, IMPELLER_GL_TEXTURE_BORDER_COLOR, border_color); + } + return true; } diff --git a/impeller/renderer/backend/gles/sampler_library_gles.cc b/impeller/renderer/backend/gles/sampler_library_gles.cc index 7f4cd3d243180..6012203897a2b 100644 --- a/impeller/renderer/backend/gles/sampler_library_gles.cc +++ b/impeller/renderer/backend/gles/sampler_library_gles.cc @@ -11,7 +11,9 @@ namespace impeller { -SamplerLibraryGLES::SamplerLibraryGLES() = default; +SamplerLibraryGLES::SamplerLibraryGLES(bool supports_decal_sampler_address_mode) + : supports_decal_sampler_address_mode_( + supports_decal_sampler_address_mode) {} // |SamplerLibrary| SamplerLibraryGLES::~SamplerLibraryGLES() = default; @@ -19,14 +21,12 @@ SamplerLibraryGLES::~SamplerLibraryGLES() = default; // |SamplerLibrary| std::shared_ptr SamplerLibraryGLES::GetSampler( SamplerDescriptor descriptor) { - // TODO(bdero): Change this validation once optional support for kDecal is - // added to the OpenGLES backend: - // https://github.com/flutter/flutter/issues/129358 - if (descriptor.width_address_mode == SamplerAddressMode::kDecal || - descriptor.height_address_mode == SamplerAddressMode::kDecal || - descriptor.depth_address_mode == SamplerAddressMode::kDecal) { + if (!supports_decal_sampler_address_mode_ && + (descriptor.width_address_mode == SamplerAddressMode::kDecal || + descriptor.height_address_mode == SamplerAddressMode::kDecal || + descriptor.depth_address_mode == SamplerAddressMode::kDecal)) { VALIDATION_LOG << "SamplerAddressMode::kDecal is not supported by the " - "OpenGLES backend."; + "current OpenGLES backend."; return nullptr; } diff --git a/impeller/renderer/backend/gles/sampler_library_gles.h b/impeller/renderer/backend/gles/sampler_library_gles.h index 43794426d9ed5..e23af540731f3 100644 --- a/impeller/renderer/backend/gles/sampler_library_gles.h +++ b/impeller/renderer/backend/gles/sampler_library_gles.h @@ -12,6 +12,7 @@ namespace impeller { class SamplerLibraryGLES final : public SamplerLibrary { public: + explicit SamplerLibraryGLES(bool supports_decal_sampler_address_mode); // |SamplerLibrary| ~SamplerLibraryGLES() override; @@ -26,6 +27,8 @@ class SamplerLibraryGLES final : public SamplerLibrary { std::shared_ptr GetSampler( SamplerDescriptor descriptor) override; + bool supports_decal_sampler_address_mode_ = false; + FML_DISALLOW_COPY_AND_ASSIGN(SamplerLibraryGLES); }; diff --git a/impeller/renderer/backend/gles/surface_gles.cc b/impeller/renderer/backend/gles/surface_gles.cc index a179b3798f3d9..9c817e6e382a7 100644 --- a/impeller/renderer/backend/gles/surface_gles.cc +++ b/impeller/renderer/backend/gles/surface_gles.cc @@ -60,6 +60,10 @@ std::unique_ptr SurfaceGLES::WrapFBO( render_target_desc.SetColorAttachment(color0, 0u); render_target_desc.SetStencilAttachment(stencil0); +#ifdef IMPELLER_DEBUG + gl_context.GetGPUTracer()->RecordRasterThread(); +#endif // IMPELLER_DEBUG + return std::unique_ptr( new SurfaceGLES(std::move(swap_callback), render_target_desc)); } diff --git a/impeller/renderer/backend/gles/test/README.md b/impeller/renderer/backend/gles/test/README.md new file mode 100644 index 0000000000000..dd9d028e1b8bd --- /dev/null +++ b/impeller/renderer/backend/gles/test/README.md @@ -0,0 +1,48 @@ +# `MockGLES` + +This directory contains a mock implementation of the GLES backend. + +Most functions are implemented as no-ops, have a default implementation that is not configurable, or just record the call. The latter is useful for testing: + +```cc +TEST(MockGLES, Example) { + // Creates a mock GLES implementation and sets it as the current one. + auto mock_gles = MockGLES::Init(); + auto& gl = mock_gles->GetProcTable(); + + // Call the proc table methods as usual, or pass the proc table to a class + // that needs it. + gl.PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 0, -1, "test"); + gl.PopDebugGroupKHR(); + + // Method names are recorded and can be inspected. + // + // Note that many built-ins, like glGetString, are not recorded (otherwise the // logs would be much bigger and less useful). + auto calls = mock_gles->GetCapturedCalls(); + EXPECT_EQ(calls, std::vector( + {"PushDebugGroupKHR", "PopDebugGroupKHR"})); +} +``` + +To add a new function, do the following: + +1. Add a new top-level method to [`mock_gles.cc`](mock_gles.cc): + + ```cc + void glFooBar() { + recordCall("glFooBar"); + } + ``` + +2. Edit the `kMockResolver`, and add a new `else if` clause: + + ```diff + + else if (strcmp(name, "glFooBar") == 0) { + + return reinterpret_cast(&glFooBar); + } else { + return reinterpret_cast(&glDoNothing); + } + ``` + +It's possible we'll want to add a more sophisticated mechanism for mocking +besides capturing calls, but this is a good start. PRs welcome! diff --git a/impeller/renderer/backend/gles/test/capabilities_unittests.cc b/impeller/renderer/backend/gles/test/capabilities_unittests.cc new file mode 100644 index 0000000000000..5a82cf5b4c9b6 --- /dev/null +++ b/impeller/renderer/backend/gles/test/capabilities_unittests.cc @@ -0,0 +1,59 @@ +// 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/testing/testing.h" // IWYU pragma: keep +#include "gtest/gtest.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" +#include "impeller/renderer/backend/gles/test/mock_gles.h" + +namespace impeller { +namespace testing { + +TEST(CapabilitiesGLES, CanInitializeWithDefaults) { + auto mock_gles = MockGLES::Init(); + + auto capabilities = mock_gles->GetProcTable().GetCapabilities(); + + EXPECT_FALSE(capabilities->SupportsOffscreenMSAA()); + EXPECT_FALSE(capabilities->SupportsSSBO()); + EXPECT_FALSE(capabilities->SupportsBufferToTextureBlits()); + EXPECT_FALSE(capabilities->SupportsTextureToTextureBlits()); + EXPECT_FALSE(capabilities->SupportsFramebufferFetch()); + EXPECT_FALSE(capabilities->SupportsCompute()); + EXPECT_FALSE(capabilities->SupportsComputeSubgroups()); + EXPECT_FALSE(capabilities->SupportsReadFromOnscreenTexture()); + EXPECT_FALSE(capabilities->SupportsReadFromResolve()); + EXPECT_FALSE(capabilities->SupportsDecalSamplerAddressMode()); + EXPECT_FALSE(capabilities->SupportsDeviceTransientTextures()); + + EXPECT_EQ(capabilities->GetDefaultColorFormat(), + PixelFormat::kR8G8B8A8UNormInt); + EXPECT_EQ(capabilities->GetDefaultStencilFormat(), PixelFormat::kS8UInt); + EXPECT_EQ(capabilities->GetDefaultDepthStencilFormat(), + PixelFormat::kD24UnormS8Uint); +} + +TEST(CapabilitiesGLES, SupportsDecalSamplerAddressMode) { + auto const extensions = std::vector{ + reinterpret_cast("GL_KHR_debug"), // + reinterpret_cast("GL_EXT_texture_border_clamp"), // + }; + auto mock_gles = MockGLES::Init(extensions); + auto capabilities = mock_gles->GetProcTable().GetCapabilities(); + EXPECT_TRUE(capabilities->SupportsDecalSamplerAddressMode()); +} + +TEST(CapabilitiesGLES, SupportsFramebufferFetch) { + auto const extensions = std::vector{ + reinterpret_cast("GL_KHR_debug"), // + reinterpret_cast( + "GL_EXT_shader_framebuffer_fetch"), // + }; + auto mock_gles = MockGLES::Init(extensions); + auto capabilities = mock_gles->GetProcTable().GetCapabilities(); + EXPECT_TRUE(capabilities->SupportsFramebufferFetch()); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/test/formats_gles_unittests.cc b/impeller/renderer/backend/gles/test/formats_gles_unittests.cc new file mode 100644 index 0000000000000..4e0fd114e80e3 --- /dev/null +++ b/impeller/renderer/backend/gles/test/formats_gles_unittests.cc @@ -0,0 +1,28 @@ +// 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/testing/testing.h" // IWYU pragma: keep +#include "gtest/gtest.h" +#include "impeller/renderer/backend/gles/formats_gles.h" + +namespace impeller { +namespace testing { + +TEST(FormatsGLES, CanFormatFramebufferErrorMessage) { + ASSERT_EQ(DebugToFramebufferError(GL_FRAMEBUFFER_UNDEFINED), + "GL_FRAMEBUFFER_UNDEFINED"); + ASSERT_EQ(DebugToFramebufferError(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), + "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + ASSERT_EQ( + DebugToFramebufferError(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT), + "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); + ASSERT_EQ(DebugToFramebufferError(GL_FRAMEBUFFER_UNSUPPORTED), + "GL_FRAMEBUFFER_UNSUPPORTED"); + ASSERT_EQ(DebugToFramebufferError(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE), + "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"); + ASSERT_EQ(DebugToFramebufferError(0), "Unknown error code: 0"); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/test/gpu_tracer_gles_unittests.cc b/impeller/renderer/backend/gles/test/gpu_tracer_gles_unittests.cc new file mode 100644 index 0000000000000..d0579a2091b23 --- /dev/null +++ b/impeller/renderer/backend/gles/test/gpu_tracer_gles_unittests.cc @@ -0,0 +1,50 @@ +// 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/testing/testing.h" // IWYU pragma: keep +#include "gtest/gtest.h" +#include "impeller/renderer/backend/gles/gpu_tracer_gles.h" +#include "impeller/renderer/backend/gles/test/mock_gles.h" + +namespace impeller { +namespace testing { + +#ifdef IMPELLER_DEBUG +TEST(GPUTracerGLES, CanFormatFramebufferErrorMessage) { + auto const extensions = std::vector{ + reinterpret_cast("GL_KHR_debug"), // + reinterpret_cast("GL_EXT_disjoint_timer_query"), // + }; + auto mock_gles = MockGLES::Init(extensions); + auto tracer = + std::make_shared(mock_gles->GetProcTable(), true); + tracer->RecordRasterThread(); + tracer->MarkFrameStart(mock_gles->GetProcTable()); + tracer->MarkFrameEnd(mock_gles->GetProcTable()); + + auto calls = mock_gles->GetCapturedCalls(); + + std::vector expected = {"glGenQueriesEXT", "glBeginQueryEXT", + "glEndQueryEXT"}; + for (auto i = 0; i < 3; i++) { + EXPECT_EQ(calls[i], expected[i]); + } + + // Begin second frame, which prompts the tracer to query the result + // from the previous frame. + tracer->MarkFrameStart(mock_gles->GetProcTable()); + + calls = mock_gles->GetCapturedCalls(); + std::vector expected_b = {"glGetQueryObjectuivEXT", + "glGetQueryObjectui64vEXT", + "glDeleteQueriesEXT"}; + for (auto i = 0; i < 3; i++) { + EXPECT_EQ(calls[i], expected_b[i]); + } +} + +#endif // IMPELLER_DEBUG + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/test/mock_gles.cc b/impeller/renderer/backend/gles/test/mock_gles.cc new file mode 100644 index 0000000000000..30ea1ae3eefc7 --- /dev/null +++ b/impeller/renderer/backend/gles/test/mock_gles.cc @@ -0,0 +1,210 @@ +// 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 + +#include "GLES3/gl3.h" +#include "fml/logging.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" +#include "impeller/renderer/backend/gles/test/mock_gles.h" + +namespace impeller { +namespace testing { + +// OpenGLES is not thread safe. +// +// This mutex is used to ensure that only one test is using the mock at a time. +static std::mutex g_test_lock; + +static std::weak_ptr g_mock_gles; + +static std::vector g_extensions; + +// Has friend visibility into MockGLES to record calls. +void RecordGLCall(const char* name) { + if (auto mock_gles = g_mock_gles.lock()) { + mock_gles->RecordCall(name); + } +} + +template +struct CheckSameSignature : std::false_type {}; + +template +struct CheckSameSignature : std::true_type {}; + +// This is a stub function that does nothing/records nothing. +void doNothing() {} + +auto const kMockVendor = (unsigned char*)"MockGLES"; +auto const kMockVersion = (unsigned char*)"3.0"; +auto const kExtensions = std::vector{ + (unsigned char*)"GL_KHR_debug" // +}; + +const unsigned char* mockGetString(GLenum name) { + switch (name) { + case GL_VENDOR: + return kMockVendor; + case GL_VERSION: + return kMockVersion; + case GL_SHADING_LANGUAGE_VERSION: + return kMockVersion; + default: + return (unsigned char*)""; + } +} + +static_assert(CheckSameSignature::value); + +const unsigned char* mockGetStringi(GLenum name, GLuint index) { + switch (name) { + case GL_EXTENSIONS: + return g_extensions[index]; + default: + return (unsigned char*)""; + } +} + +static_assert(CheckSameSignature::value); + +void mockGetIntegerv(GLenum name, int* value) { + switch (name) { + case GL_NUM_EXTENSIONS: { + *value = g_extensions.size(); + } break; + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + *value = 8; + break; + default: + *value = 0; + break; + } +} + +static_assert(CheckSameSignature::value); + +GLenum mockGetError() { + return GL_NO_ERROR; +} + +static_assert(CheckSameSignature::value); + +void mockPopDebugGroupKHR() { + RecordGLCall("PopDebugGroupKHR"); +} + +static_assert(CheckSameSignature::value); + +void mockPushDebugGroupKHR(GLenum source, + GLuint id, + GLsizei length, + const GLchar* message) { + RecordGLCall("PushDebugGroupKHR"); +} + +static_assert(CheckSameSignature::value); + +void mockGenQueriesEXT(GLsizei n, GLuint* ids) { + RecordGLCall("glGenQueriesEXT"); + for (auto i = 0; i < n; i++) { + ids[i] = i + 1; + } +} + +static_assert(CheckSameSignature::value); + +void mockBeginQueryEXT(GLenum target, GLuint id) { + RecordGLCall("glBeginQueryEXT"); +} + +static_assert(CheckSameSignature::value); + +void mockEndQueryEXT(GLuint id) { + RecordGLCall("glEndQueryEXT"); +} + +static_assert(CheckSameSignature::value); + +void mockGetQueryObjectuivEXT(GLuint id, GLenum target, GLuint* result) { + RecordGLCall("glGetQueryObjectuivEXT"); + *result = GL_TRUE; +} + +static_assert(CheckSameSignature::value); + +void mockGetQueryObjectui64vEXT(GLuint id, GLenum target, GLuint64* result) { + RecordGLCall("glGetQueryObjectui64vEXT"); + *result = 1000u; +} + +static_assert(CheckSameSignature::value); + +void mockDeleteQueriesEXT(GLsizei size, const GLuint* queries) { + RecordGLCall("glDeleteQueriesEXT"); +} + +static_assert(CheckSameSignature::value); + +std::shared_ptr MockGLES::Init( + const std::optional>& extensions) { + // If we cannot obtain a lock, MockGLES is already being used elsewhere. + FML_CHECK(g_test_lock.try_lock()) + << "MockGLES is already being used by another test."; + g_extensions = extensions.value_or(kExtensions); + auto mock_gles = std::shared_ptr(new MockGLES()); + g_mock_gles = mock_gles; + return mock_gles; +} + +const ProcTableGLES::Resolver kMockResolver = [](const char* name) { + if (strcmp(name, "glPopDebugGroupKHR") == 0) { + return reinterpret_cast(&mockPopDebugGroupKHR); + } else if (strcmp(name, "glPushDebugGroupKHR") == 0) { + return reinterpret_cast(&mockPushDebugGroupKHR); + } else if (strcmp(name, "glGetString") == 0) { + return reinterpret_cast(&mockGetString); + } else if (strcmp(name, "glGetStringi") == 0) { + return reinterpret_cast(&mockGetStringi); + } else if (strcmp(name, "glGetIntegerv") == 0) { + return reinterpret_cast(&mockGetIntegerv); + } else if (strcmp(name, "glGetError") == 0) { + return reinterpret_cast(&mockGetError); + } else if (strcmp(name, "glGenQueriesEXT") == 0) { + return reinterpret_cast(&mockGenQueriesEXT); + } else if (strcmp(name, "glBeginQueryEXT") == 0) { + return reinterpret_cast(&mockBeginQueryEXT); + } else if (strcmp(name, "glEndQueryEXT") == 0) { + return reinterpret_cast(&mockEndQueryEXT); + } else if (strcmp(name, "glDeleteQueriesEXT") == 0) { + return reinterpret_cast(&mockDeleteQueriesEXT); + } else if (strcmp(name, "glGetQueryObjectui64vEXT") == 0) { + return reinterpret_cast(mockGetQueryObjectui64vEXT); + } else if (strcmp(name, "glGetQueryObjectuivEXT") == 0) { + return reinterpret_cast(mockGetQueryObjectuivEXT); + } else { + return reinterpret_cast(&doNothing); + } +}; + +MockGLES::MockGLES() : proc_table_(kMockResolver) {} + +MockGLES::~MockGLES() { + g_test_lock.unlock(); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/test/mock_gles.h b/impeller/renderer/backend/gles/test/mock_gles.h new file mode 100644 index 0000000000000..fb2971500ff85 --- /dev/null +++ b/impeller/renderer/backend/gles/test/mock_gles.h @@ -0,0 +1,60 @@ +// 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 +#include +#include "fml/macros.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { +namespace testing { + +/// @brief Provides a mocked version of the |ProcTableGLES| class. +/// +/// Typically, Open GLES at runtime will be provided the host's GLES bindings +/// (as function pointers). This class maintains a set of function pointers that +/// appear to be GLES functions, but are actually just stubs that record +/// invocations. +/// +/// See `README.md` for more information. +class MockGLES final { + public: + /// @brief Returns an initialized |MockGLES| instance. + /// + /// This method overwrites mocked global GLES function pointers to record + /// invocations on this instance of |MockGLES|. As such, it should only be + /// called once per test. + static std::shared_ptr Init( + const std::optional>& extensions = + std::nullopt); + + /// @brief Returns a configured |ProcTableGLES| instance. + const ProcTableGLES& GetProcTable() const { return proc_table_; } + + /// @brief Returns a vector of the names of all recorded calls. + /// + /// Calls are cleared after this method is called. + std::vector GetCapturedCalls() { + std::vector calls = captured_calls_; + captured_calls_.clear(); + return calls; + } + + ~MockGLES(); + + private: + friend void RecordGLCall(const char* name); + + MockGLES(); + + void RecordCall(const char* name) { captured_calls_.emplace_back(name); } + + const ProcTableGLES proc_table_; + std::vector captured_calls_; + + FML_DISALLOW_COPY_AND_ASSIGN(MockGLES); +}; + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/test/mock_gles_unittests.cc b/impeller/renderer/backend/gles/test/mock_gles_unittests.cc new file mode 100644 index 0000000000000..a6c248b04ffcf --- /dev/null +++ b/impeller/renderer/backend/gles/test/mock_gles_unittests.cc @@ -0,0 +1,48 @@ +// 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/testing/testing.h" // IWYU pragma: keep +#include "gtest/gtest.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" +#include "impeller/renderer/backend/gles/test/mock_gles.h" + +namespace impeller { +namespace testing { + +// This test just checks that the proc table is initialized correctly. +// +// If this test doesn't pass, no test that uses the proc table will pass. +TEST(MockGLES, CanInitialize) { + auto mock_gles = MockGLES::Init(); + + EXPECT_EQ(mock_gles->GetProcTable().GetString(GL_VENDOR), + (unsigned char*)"MockGLES"); +} + +// Tests we can call two functions and capture the calls. +TEST(MockGLES, CapturesPushAndPopDebugGroup) { + auto mock_gles = MockGLES::Init(); + + auto& gl = mock_gles->GetProcTable(); + gl.PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 0, -1, "test"); + gl.PopDebugGroupKHR(); + + auto calls = mock_gles->GetCapturedCalls(); + EXPECT_EQ(calls, std::vector( + {"PushDebugGroupKHR", "PopDebugGroupKHR"})); +} + +// Tests that if we call a function we have not mocked, it's OK. +TEST(MockGLES, CanCallUnmockedFunction) { + auto mock_gles = MockGLES::Init(); + + auto& gl = mock_gles->GetProcTable(); + gl.DeleteFramebuffers(1, nullptr); + + // Test should still complete. + // If we end up mocking DeleteFramebuffers, delete this test. +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index 6c01e6b143e71..ad8d319150c53 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -10,7 +10,6 @@ #include "flutter/fml/mapping.h" #include "flutter/fml/trace_event.h" #include "impeller/base/allocation.h" -#include "impeller/base/config.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" #include "impeller/renderer/backend/gles/formats_gles.h" @@ -33,6 +32,8 @@ HandleType ToHandleType(TextureGLES::Type type) { case TextureGLES::Type::kTexture: return HandleType::kTexture; case TextureGLES::Type::kRenderBuffer: + // MSAA textures are treated as render buffers. + case TextureGLES::Type::kRenderBufferMultisampled: return HandleType::kRenderBuffer; } FML_UNREACHABLE(); @@ -383,7 +384,7 @@ void TextureGLES::InitializeContentsIfNecessary() const { } } break; - case Type::kRenderBuffer: + case Type::kRenderBuffer: { auto render_buffer_format = ToRenderBufferFormat(GetTextureDescriptor().format); if (!render_buffer_format.has_value()) { @@ -399,7 +400,27 @@ void TextureGLES::InitializeContentsIfNecessary() const { size.height // height ); } + } break; + case Type::kRenderBufferMultisampled: { + auto render_buffer_msaa = + ToRenderBufferFormat(GetTextureDescriptor().format); + if (!render_buffer_msaa.has_value()) { + VALIDATION_LOG << "Invalid format for render-buffer MSAA image."; + return; + } + gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value()); + { + TRACE_EVENT0("impeller", "RenderBufferStorageInitialization"); + gl.RenderbufferStorageMultisampleEXT( + GL_RENDERBUFFER, // target + 4, // samples + render_buffer_msaa.value(), // internal format + size.width, // width + size.height // height + ); + } break; + } } } @@ -426,6 +447,8 @@ bool TextureGLES::Bind() const { gl.BindTexture(target.value(), handle.value()); } break; case Type::kRenderBuffer: + // MSAA textures are treated as render buffers. + case Type::kRenderBufferMultisampled: gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value()); break; } @@ -483,7 +506,6 @@ static GLenum ToAttachmentPoint(TextureGLES::AttachmentPoint point) { } bool TextureGLES::SetAsFramebufferAttachment(GLenum target, - GLuint fbo, AttachmentPoint point) const { if (!IsValid()) { return false; @@ -510,6 +532,18 @@ bool TextureGLES::SetAsFramebufferAttachment(GLenum target, handle.value() // render-buffer ); break; + case Type::kRenderBufferMultisampled: + // Assume that when MSAA is enabled, we're using 4x MSAA. + FML_DCHECK(GetTextureDescriptor().sample_count == SampleCount::kCount4); + gl.FramebufferTexture2DMultisampleEXT( + target, // target + ToAttachmentPoint(point), // attachment + GL_TEXTURE_2D, // textarget + handle.value(), // texture + 0, // level + 4 // samples + ); + break; } return true; } diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index e2d2ae32c496e..7f5cb90e4d00b 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -18,6 +18,7 @@ class TextureGLES final : public Texture, enum class Type { kTexture, kRenderBuffer, + kRenderBufferMultisampled, }; enum class IsWrapped { @@ -45,7 +46,6 @@ class TextureGLES final : public Texture, kStencil, }; [[nodiscard]] bool SetAsFramebufferAttachment(GLenum target, - GLuint fbo, AttachmentPoint point) const; Type GetType() const; diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn index 9a0f6a631e3d7..254cae97f063a 100644 --- a/impeller/renderer/backend/metal/BUILD.gn +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -24,6 +24,8 @@ impeller_component("metal") { "device_buffer_mtl.mm", "formats_mtl.h", "formats_mtl.mm", + "gpu_tracer_mtl.h", + "gpu_tracer_mtl.mm", "pipeline_library_mtl.h", "pipeline_library_mtl.mm", "pipeline_mtl.h", diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 49384a9e1cdfd..b3161b74c4e36 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -159,6 +159,13 @@ static bool LogMTLCommandBufferErrorIfPresent(id buffer) { } bool CommandBufferMTL::OnSubmitCommands(CompletionCallback callback) { + auto context = context_.lock(); + if (!context) { + return false; + } +#ifdef IMPELLER_DEBUG + ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer_); +#endif // IMPELLER_DEBUG if (callback) { [buffer_ addCompletedHandler:^(id buffer) { @@ -190,6 +197,10 @@ static bool LogMTLCommandBufferErrorIfPresent(id buffer) { auto buffer = buffer_; buffer_ = nil; +#ifdef IMPELLER_DEBUG + ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer); +#endif // IMPELLER_DEBUG + auto worker_task_runner = ContextMTL::Cast(*context).GetWorkerTaskRunner(); auto mtl_render_pass = static_cast(render_pass.get()); diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index c272c68769fe9..9fbff94d4669b 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -6,8 +6,8 @@ #include +#include #include -#include #include "flutter/fml/concurrent_message_loop.h" #include "flutter/fml/macros.h" @@ -16,6 +16,7 @@ #include "impeller/core/sampler.h" #include "impeller/renderer/backend/metal/allocator_mtl.h" #include "impeller/renderer/backend/metal/command_buffer_mtl.h" +#include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" #include "impeller/renderer/backend/metal/pipeline_library_mtl.h" #include "impeller/renderer/backend/metal/shader_library_mtl.h" #include "impeller/renderer/capabilities.h" @@ -93,7 +94,24 @@ class ContextMTL final : public Context, std::shared_ptr GetIsGpuDisabledSyncSwitch() const; +#ifdef IMPELLER_DEBUG + std::shared_ptr GetGPUTracer() const; +#endif // IMPELLER_DEBUG + + // |Context| + void StoreTaskForGPU(std::function task) override; + private: + class SyncSwitchObserver : public fml::SyncSwitch::Observer { + public: + SyncSwitchObserver(ContextMTL& parent); + virtual ~SyncSwitchObserver() = default; + void OnSyncSwitchUpdate(bool new_value) override; + + private: + ContextMTL& parent_; + }; + id device_ = nullptr; id command_queue_ = nullptr; std::shared_ptr shader_library_; @@ -103,6 +121,11 @@ class ContextMTL final : public Context, std::shared_ptr device_capabilities_; std::shared_ptr raster_message_loop_; std::shared_ptr is_gpu_disabled_sync_switch_; +#ifdef IMPELLER_DEBUG + std::shared_ptr gpu_tracer_; +#endif // IMPELLER_DEBUG + std::deque> tasks_awaiting_gpu_; + std::unique_ptr sync_switch_observer_; bool is_valid_ = false; ContextMTL( @@ -114,6 +137,8 @@ class ContextMTL final : public Context, std::shared_ptr CreateCommandBufferInQueue( id queue) const; + void FlushTasksAwaitingGPU(); + FML_DISALLOW_COPY_AND_ASSIGN(ContextMTL); }; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 528c1854802a3..06fd34724685a 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/metal/context_mtl.h" #include +#include #include "flutter/fml/concurrent_message_loop.h" #include "flutter/fml/file.h" @@ -12,6 +13,7 @@ #include "flutter/fml/paths.h" #include "flutter/fml/synchronization/sync_switch.h" #include "impeller/core/sampler_descriptor.h" +#include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" #include "impeller/renderer/backend/metal/sampler_library_mtl.h" #include "impeller/renderer/capabilities.h" @@ -83,6 +85,9 @@ static bool DeviceSupportsComputeSubgroups(id device) { return; } + sync_switch_observer_.reset(new SyncSwitchObserver(*this)); + is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get()); + // Worker task runner. { raster_message_loop_ = fml::ConcurrentMessageLoop::Create( @@ -142,7 +147,9 @@ static bool DeviceSupportsComputeSubgroups(id device) { device_capabilities_ = InferMetalCapabilities(device_, PixelFormat::kB8G8R8A8UNormInt); - +#ifdef IMPELLER_DEBUG + gpu_tracer_ = std::make_shared(); +#endif // IMPELLER_DEBUG is_valid_ = true; } @@ -284,7 +291,9 @@ new ContextMTL(device, command_queue, return context; } -ContextMTL::~ContextMTL() = default; +ContextMTL::~ContextMTL() { + is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get()); +} Context::BackendType ContextMTL::GetBackendType() const { return Context::BackendType::kMetal; @@ -325,6 +334,12 @@ new ContextMTL(device, command_queue, raster_message_loop_.reset(); } +#ifdef IMPELLER_DEBUG +std::shared_ptr ContextMTL::GetGPUTracer() const { + return gpu_tracer_; +} +#endif // IMPELLER_DEBUG + const std::shared_ptr ContextMTL::GetWorkerTaskRunner() const { return raster_message_loop_->GetTaskRunner(); @@ -376,4 +391,28 @@ new ContextMTL(device, command_queue, return buffer; } +void ContextMTL::StoreTaskForGPU(std::function task) { + tasks_awaiting_gpu_.emplace_back(std::move(task)); + while (tasks_awaiting_gpu_.size() > kMaxTasksAwaitingGPU) { + tasks_awaiting_gpu_.front()(); + tasks_awaiting_gpu_.pop_front(); + } +} + +void ContextMTL::FlushTasksAwaitingGPU() { + for (const auto& task : tasks_awaiting_gpu_) { + task(); + } + tasks_awaiting_gpu_.clear(); +} + +ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent) + : parent_(parent) {} + +void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) { + if (!new_is_disabled) { + parent_.FlushTasksAwaitingGPU(); + } +} + } // namespace impeller diff --git a/impeller/renderer/backend/metal/gpu_tracer_mtl.h b/impeller/renderer/backend/metal/gpu_tracer_mtl.h new file mode 100644 index 0000000000000..ac2c3916d0207 --- /dev/null +++ b/impeller/renderer/backend/metal/gpu_tracer_mtl.h @@ -0,0 +1,48 @@ +// 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. + +#pragma once + +#include + +#include +#include +#include "impeller/base/thread.h" +#include "impeller/base/thread_safety.h" +#include "impeller/geometry/scalar.h" + +namespace impeller { + +class ContextMTL; + +/// @brief Approximate the GPU frame time by computing a difference between the +/// smallest GPUStartTime and largest GPUEndTime for all command buffers +/// submitted in a frame workload. +class GPUTracerMTL : public std::enable_shared_from_this { + public: + GPUTracerMTL() = default; + + ~GPUTracerMTL() = default; + + /// @brief Record that the current frame has ended. Any additional cmd buffers + /// will be attributed to the "next" frame. + void MarkFrameEnd(); + + /// @brief Record the current cmd buffer GPU execution timestamps into an + /// aggregate frame workload metric. + void RecordCmdBuffer(id buffer); + + private: + struct GPUTraceState { + Scalar smallest_timestamp = std::numeric_limits::max(); + Scalar largest_timestamp = 0; + size_t pending_buffers = 0; + }; + + mutable Mutex trace_state_mutex_; + GPUTraceState trace_states_[16] IPLR_GUARDED_BY(trace_state_mutex_); + size_t current_state_ IPLR_GUARDED_BY(trace_state_mutex_) = 0u; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/gpu_tracer_mtl.mm b/impeller/renderer/backend/metal/gpu_tracer_mtl.mm new file mode 100644 index 0000000000000..41f1aa04a3328 --- /dev/null +++ b/impeller/renderer/backend/metal/gpu_tracer_mtl.mm @@ -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. + +#include +#include "fml/trace_event.h" +#include "impeller/renderer/backend/metal/context_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" + +#include + +#include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" + +namespace impeller { + +void GPUTracerMTL::MarkFrameEnd() { + if (@available(ios 10.3, tvos 10.2, macos 10.15, macCatalyst 13.0, *)) { + Lock lock(trace_state_mutex_); + current_state_ = (current_state_ + 1) % 16; + } +} + +void GPUTracerMTL::RecordCmdBuffer(id buffer) { + if (@available(ios 10.3, tvos 10.2, macos 10.15, macCatalyst 13.0, *)) { + Lock lock(trace_state_mutex_); + auto current_state = current_state_; + trace_states_[current_state].pending_buffers += 1; + + auto weak_self = weak_from_this(); + [buffer addCompletedHandler:^(id buffer) { + auto self = weak_self.lock(); + if (!self) { + return; + } + Lock lock(self->trace_state_mutex_); + auto& state = self->trace_states_[current_state]; + state.pending_buffers--; + state.smallest_timestamp = std::min( + state.smallest_timestamp, static_cast(buffer.GPUStartTime)); + state.largest_timestamp = std::max( + state.largest_timestamp, static_cast(buffer.GPUEndTime)); + + if (state.pending_buffers == 0) { + auto gpu_ms = + (state.largest_timestamp - state.smallest_timestamp) * 1000; + state.smallest_timestamp = std::numeric_limits::max(); + state.largest_timestamp = 0; + FML_TRACE_COUNTER("flutter", "GPUTracer", + reinterpret_cast(this), // Trace Counter ID + "FrameTimeMS", gpu_ms); + } + }]; + } +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.mm b/impeller/renderer/backend/metal/sampler_library_mtl.mm index c9099c509e646..fa6559139f1a1 100644 --- a/impeller/renderer/backend/metal/sampler_library_mtl.mm +++ b/impeller/renderer/backend/metal/sampler_library_mtl.mm @@ -29,7 +29,9 @@ desc.sAddressMode = ToMTLSamplerAddressMode(descriptor.width_address_mode); desc.tAddressMode = ToMTLSamplerAddressMode(descriptor.height_address_mode); desc.rAddressMode = ToMTLSamplerAddressMode(descriptor.depth_address_mode); - + if (@available(iOS 14.0, macos 10.12, *)) { + desc.borderColor = MTLSamplerBorderColorTransparentBlack; + } if (!descriptor.label.empty()) { desc.label = @(descriptor.label.c_str()); } diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 526e66f4c5ff5..beb900a89be13 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -246,15 +246,27 @@ return false; } } +#ifdef IMPELLER_DEBUG + ContextMTL::Cast(context.get())->GetGPUTracer()->MarkFrameEnd(); +#endif // IMPELLER_DEBUG if (drawable_) { - TRACE_EVENT0("flutter", "waitUntilScheduled"); id command_buffer = ContextMTL::Cast(context.get()) ->CreateMTLCommandBuffer("Present Waiter Command Buffer"); - [command_buffer commit]; - [command_buffer waitUntilScheduled]; - [drawable_ present]; + // If the threads have been merged, or there is a pending frame capture, + // then block on cmd buffer scheduling to ensure that the + // transaction/capture work correctly. + if ([[NSThread currentThread] isMainThread] || + [[MTLCaptureManager sharedCaptureManager] isCapturing]) { + TRACE_EVENT0("flutter", "waitUntilScheduled"); + [command_buffer commit]; + [command_buffer waitUntilScheduled]; + [drawable_ present]; + } else { + [command_buffer presentDrawable:drawable_]; + [command_buffer commit]; + } } return true; diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 0c71fab1035a1..6de8fff0b13c5 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -10,9 +10,12 @@ impeller_component("vulkan_unittests") { sources = [ "blit_command_vk_unittests.cc", "command_encoder_vk_unittests.cc", + "command_pool_vk_unittests.cc", "context_vk_unittests.cc", + "fence_waiter_vk_unittests.cc", "pass_bindings_cache_unittests.cc", "resource_manager_vk_unittests.cc", + "test/gpu_tracer_unittests.cc", "test/mock_vulkan.cc", "test/mock_vulkan.h", "test/mock_vulkan_unittests.cc", @@ -59,6 +62,8 @@ impeller_component("vulkan") { "fence_waiter_vk.h", "formats_vk.cc", "formats_vk.h", + "gpu_tracer_vk.cc", + "gpu_tracer_vk.h", "limits_vk.h", "pass_bindings_cache.cc", "pass_bindings_cache.h", diff --git a/impeller/renderer/backend/vulkan/allocator_vk.cc b/impeller/renderer/backend/vulkan/allocator_vk.cc index 3fc4f2f99ca5a..f90ee4bc447d6 100644 --- a/impeller/renderer/backend/vulkan/allocator_vk.cc +++ b/impeller/renderer/backend/vulkan/allocator_vk.cc @@ -265,6 +265,7 @@ class AllocatedTextureSourceVK final : public TextureSourceVK { vk::Device device, bool supports_memoryless_textures) : TextureSourceVK(desc), resource_(std::move(resource_manager)) { + FML_DCHECK(desc.format != PixelFormat::kUnknown); TRACE_EVENT0("impeller", "CreateDeviceTexture"); vk::ImageCreateInfo image_info; image_info.flags = ToVKImageCreateFlags(desc.type); diff --git a/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc b/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc index 44206c2e7ddb5..260c9e4a45861 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/testing/testing.h" +#include "flutter/testing/testing.h" // IWYU pragma: keep #include "impeller/renderer/backend/vulkan/blit_command_vk.h" #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" @@ -11,14 +11,16 @@ namespace impeller { namespace testing { TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) { - auto context = CreateMockVulkanContext(); - auto pool = CommandPoolVK::GetThreadLocal(context.get()); + auto context = MockVulkanContextBuilder().Build(); + auto pool = context->GetCommandPoolRecycler()->Get(); auto encoder = std::make_unique(context)->Create(); BlitCopyTextureToTextureCommandVK cmd; cmd.source = context->GetResourceAllocator()->CreateTexture({ + .format = PixelFormat::kR8G8B8A8UNormInt, .size = ISize(100, 100), }); cmd.destination = context->GetResourceAllocator()->CreateTexture({ + .format = PixelFormat::kR8G8B8A8UNormInt, .size = ISize(100, 100), }); bool result = cmd.Encode(*encoder.get()); @@ -28,10 +30,11 @@ TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) { } TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); auto encoder = std::make_unique(context)->Create(); BlitCopyTextureToBufferCommandVK cmd; cmd.source = context->GetResourceAllocator()->CreateTexture({ + .format = PixelFormat::kR8G8B8A8UNormInt, .size = ISize(100, 100), }); cmd.destination = context->GetResourceAllocator()->CreateBuffer({ @@ -44,10 +47,11 @@ TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) { } TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); auto encoder = std::make_unique(context)->Create(); BlitCopyBufferToTextureCommandVK cmd; cmd.destination = context->GetResourceAllocator()->CreateTexture({ + .format = PixelFormat::kR8G8B8A8UNormInt, .size = ISize(100, 100), }); cmd.source = context->GetResourceAllocator() @@ -62,10 +66,11 @@ TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) { } TEST(BlitCommandVkTest, BlitGenerateMipmapCommandVK) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); auto encoder = std::make_unique(context)->Create(); BlitGenerateMipmapCommandVK cmd; cmd.texture = context->GetResourceAllocator()->CreateTexture({ + .format = PixelFormat::kR8G8B8A8UNormInt, .size = ISize(100, 100), .mip_count = 2, }); diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.cc b/impeller/renderer/backend/vulkan/command_buffer_vk.cc index 0059fe41b26f2..78eb547847082 100644 --- a/impeller/renderer/backend/vulkan/command_buffer_vk.cc +++ b/impeller/renderer/backend/vulkan/command_buffer_vk.cc @@ -52,6 +52,9 @@ const std::shared_ptr& CommandBufferVK::GetEncoder() { } bool CommandBufferVK::OnSubmitCommands(CompletionCallback callback) { + if (!encoder_) { + encoder_ = encoder_factory_->Create(); + } if (!callback) { return encoder_->Submit(); } diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.cc b/impeller/renderer/backend/vulkan/command_encoder_vk.cc index 9b187896a4f6d..4bd8f69391eb3 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.cc @@ -5,9 +5,9 @@ #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" #include "flutter/fml/closure.h" -#include "flutter/fml/trace_event.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" +#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" namespace impeller { @@ -16,12 +16,13 @@ class TrackedObjectsVK { public: explicit TrackedObjectsVK( const std::weak_ptr& device_holder, - const std::shared_ptr& pool) - : desc_pool_(device_holder) { + const std::shared_ptr& pool, + std::unique_ptr probe) + : desc_pool_(device_holder), probe_(std::move(probe)) { if (!pool) { return; } - auto buffer = pool->CreateGraphicsCommandBuffer(); + auto buffer = pool->CreateCommandBuffer(); if (!buffer) { return; } @@ -34,7 +35,7 @@ class TrackedObjectsVK { if (!buffer_) { return; } - pool_->CollectGraphicsCommandBuffer(std::move(buffer_)); + pool_->CollectCommandBuffer(std::move(buffer_)); } bool IsValid() const { return is_valid_; } @@ -78,6 +79,8 @@ class TrackedObjectsVK { DescriptorPoolVK& GetDescriptorPool() { return desc_pool_; } + GPUProbe& GetGPUProbe() const { return *probe_.get(); } + private: DescriptorPoolVK desc_pool_; // `shared_ptr` since command buffers have a link to the command pool. @@ -86,6 +89,7 @@ class TrackedObjectsVK { std::set> tracked_objects_; std::set> tracked_buffers_; std::set> tracked_textures_; + std::unique_ptr probe_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(TrackedObjectsVK); @@ -105,13 +109,18 @@ std::shared_ptr CommandEncoderFactoryVK::Create() { return nullptr; } auto& context_vk = ContextVK::Cast(*context); - auto tls_pool = CommandPoolVK::GetThreadLocal(&context_vk); + auto recycler = context_vk.GetCommandPoolRecycler(); + if (!recycler) { + return nullptr; + } + auto tls_pool = recycler->Get(); if (!tls_pool) { return nullptr; } auto tracked_objects = std::make_shared( - context_vk.GetDeviceHolder(), tls_pool); + context_vk.GetDeviceHolder(), tls_pool, + context->GetGPUTracer()->CreateGPUProbe()); auto queue = context_vk.GetGraphicsQueue(); if (!tracked_objects || !tracked_objects->IsValid() || !queue) { @@ -130,6 +139,8 @@ std::shared_ptr CommandEncoderFactoryVK::Create() { context_vk.SetDebugName(tracked_objects->GetCommandBuffer(), label_.value()); } + tracked_objects->GetGPUProbe().RecordCmdBufferStart( + tracked_objects->GetCommandBuffer()); return std::make_shared(context_vk.GetDeviceHolder(), tracked_objects, queue, @@ -175,6 +186,8 @@ bool CommandEncoderVK::Submit(SubmitCallback callback) { auto command_buffer = GetCommandBuffer(); + tracked_objects_->GetGPUProbe().RecordCmdBufferEnd(command_buffer); + auto status = command_buffer.end(); if (status != vk::Result::eSuccess) { VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(status); @@ -205,7 +218,10 @@ bool CommandEncoderVK::Submit(SubmitCallback callback) { fail_callback = false; return fence_waiter_->AddFence( std::move(fence), - [callback, tracked_objects = std::move(tracked_objects_)] { + [callback, tracked_objects = std::move(tracked_objects_)]() mutable { + // Ensure tracked objects are destructed before calling any final + // callbacks. + tracked_objects.reset(); if (callback) { callback(true); } diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.h b/impeller/renderer/backend/vulkan/command_encoder_vk.h index 5402a6ad3c228..641a776853abc 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.h +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.h @@ -6,7 +6,6 @@ #include #include -#include #include "flutter/fml/macros.h" #include "impeller/renderer/backend/vulkan/command_pool_vk.h" @@ -26,10 +25,12 @@ class Texture; class TextureSourceVK; class TrackedObjectsVK; class FenceWaiterVK; +class GPUProbe; class CommandEncoderFactoryVK { public: - CommandEncoderFactoryVK(const std::weak_ptr& context); + explicit CommandEncoderFactoryVK( + const std::weak_ptr& context); std::shared_ptr Create(); diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc b/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc index 0fa38fdb82985..3f17a6e99c1ac 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc @@ -4,10 +4,9 @@ #include -#include "flutter/fml/synchronization/count_down_latch.h" -#include "flutter/testing/testing.h" +#include "flutter/testing/testing.h" // IWYU pragma: keep +#include "fml/synchronization/waitable_event.h" #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" -#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" namespace impeller { @@ -18,7 +17,7 @@ TEST(CommandEncoderVKTest, DeleteEncoderAfterThreadDies) { // command buffers before it cleans up its command pool. std::shared_ptr> called_functions; { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); called_functions = GetMockVulkanFunctions(context->GetDevice()); std::shared_ptr encoder; std::thread thread([&] { @@ -26,6 +25,7 @@ TEST(CommandEncoderVKTest, DeleteEncoderAfterThreadDies) { encoder = factory.Create(); }); thread.join(); + context->Shutdown(); } auto destroy_pool = std::find(called_functions->begin(), called_functions->end(), @@ -46,7 +46,7 @@ TEST(CommandEncoderVKTest, CleanupAfterSubmit) { { fml::AutoResetWaitableEvent wait_for_submit; fml::AutoResetWaitableEvent wait_for_thread_join; - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); std::thread thread([&] { CommandEncoderFactoryVK factory(context); std::shared_ptr encoder = factory.Create(); @@ -60,7 +60,9 @@ TEST(CommandEncoderVKTest, CleanupAfterSubmit) { wait_for_thread_join.Signal(); wait_for_submit.Wait(); called_functions = GetMockVulkanFunctions(context->GetDevice()); + context->Shutdown(); } + auto destroy_pool = std::find(called_functions->begin(), called_functions->end(), "vkDestroyCommandPool"); diff --git a/impeller/renderer/backend/vulkan/command_pool_vk.cc b/impeller/renderer/backend/vulkan/command_pool_vk.cc index ad657e3dac60d..05354271b7f5d 100644 --- a/impeller/renderer/backend/vulkan/command_pool_vk.cc +++ b/impeller/renderer/backend/vulkan/command_pool_vk.cc @@ -4,165 +4,262 @@ #include "impeller/renderer/backend/vulkan/command_pool_vk.h" -#include -#include -#include +#include +#include +#include -#include "flutter/fml/thread_local.h" -#include "impeller/base/thread.h" -#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "fml/macros.h" +#include "fml/thread_local.h" +#include "fml/trace_event.h" +#include "impeller/renderer/backend/vulkan/resource_manager_vk.h" +#include "impeller/renderer/backend/vulkan/vk.h" // IWYU pragma: keep. +#include "vulkan/vulkan_structs.hpp" namespace impeller { -using CommandPoolMap = std::map>; -FML_THREAD_LOCAL fml::ThreadLocalUniquePtr tls_command_pool; +// Holds the command pool in a background thread, recyling it when not in use. +class BackgroundCommandPoolVK final { + public: + BackgroundCommandPoolVK(BackgroundCommandPoolVK&&) = default; -static Mutex g_all_pools_mutex; -static std::unordered_map>> - g_all_pools IPLR_GUARDED_BY(g_all_pools_mutex); + explicit BackgroundCommandPoolVK( + vk::UniqueCommandPool&& pool, + std::vector&& buffers, + std::weak_ptr recycler) + : pool_(std::move(pool)), + buffers_(std::move(buffers)), + recycler_(std::move(recycler)) {} -std::shared_ptr CommandPoolVK::GetThreadLocal( - const ContextVK* context) { - if (!context) { - return nullptr; - } - if (tls_command_pool.get() == nullptr) { - tls_command_pool.reset(new CommandPoolMap()); + ~BackgroundCommandPoolVK() { + auto const recycler = recycler_.lock(); + + // Not only does this prevent recycling when the context is being destroyed, + // but it also prevents the destructor from effectively being called twice; + // once for the original BackgroundCommandPoolVK() and once for the moved + // BackgroundCommandPoolVK(). + if (!recycler) { + return; + } + buffers_.clear(); + + recycler->Reclaim(std::move(pool_)); } - CommandPoolMap& pool_map = *tls_command_pool.get(); - auto found = pool_map.find(context->GetHash()); - if (found != pool_map.end() && found->second->IsValid()) { - return found->second; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(BackgroundCommandPoolVK); + + vk::UniqueCommandPool pool_; + + // These are retained because the destructor of the C++ UniqueCommandBuffer + // wrapper type will attempt to reset the cmd buffer, and doing so may be a + // thread safety violation as this may happen on the fence waiter thread. + std::vector buffers_; + std::weak_ptr recycler_; +}; + +CommandPoolVK::~CommandPoolVK() { + if (!pool_) { + return; } - auto pool = std::shared_ptr(new CommandPoolVK(context)); - if (!pool->IsValid()) { - return nullptr; + + auto const context = context_.lock(); + if (!context) { + return; } - pool_map[context->GetHash()] = pool; - { - Lock pool_lock(g_all_pools_mutex); - g_all_pools[context].push_back(pool); + auto const recycler = context->GetCommandPoolRecycler(); + if (!recycler) { + return; } - return pool; + + auto reset_pool_when_dropped = BackgroundCommandPoolVK( + std::move(pool_), std::move(collected_buffers_), recycler); + + UniqueResourceVKT pool( + context->GetResourceManager(), std::move(reset_pool_when_dropped)); } -void CommandPoolVK::ClearAllPools(const ContextVK* context) { - if (tls_command_pool.get()) { - tls_command_pool.get()->erase(context->GetHash()); +// TODO(matanlurey): Return a status_or<> instead of {} when we have one. +vk::UniqueCommandBuffer CommandPoolVK::CreateCommandBuffer() { + auto const context = context_.lock(); + if (!context) { + return {}; } - Lock pool_lock(g_all_pools_mutex); - if (auto found = g_all_pools.find(context); found != g_all_pools.end()) { - for (auto& weak_pool : found->second) { - auto pool = weak_pool.lock(); - if (!pool) { - // The pool has already died because the thread died. - continue; - } - // The pool is reset but its reference in the TLS map remains till the - // thread dies. - pool->Reset(); - } - g_all_pools.erase(found); + + Lock lock(pool_mutex_); + if (!pool_) { + return {}; } -} -CommandPoolVK::CommandPoolVK(const ContextVK* context) - : owner_id_(std::this_thread::get_id()) { - vk::CommandPoolCreateInfo pool_info; + auto const device = context->GetDevice(); + vk::CommandBufferAllocateInfo info; + info.setCommandPool(pool_.get()); + info.setCommandBufferCount(1u); + info.setLevel(vk::CommandBufferLevel::ePrimary); + auto [result, buffers] = device.allocateCommandBuffersUnique(info); + if (result != vk::Result::eSuccess) { + return {}; + } + return std::move(buffers[0]); +} - pool_info.queueFamilyIndex = context->GetGraphicsQueue()->GetIndex().family; - pool_info.flags = vk::CommandPoolCreateFlagBits::eTransient | - vk::CommandPoolCreateFlagBits::eResetCommandBuffer; - auto pool = context->GetDevice().createCommandPoolUnique(pool_info); - if (pool.result != vk::Result::eSuccess) { +void CommandPoolVK::CollectCommandBuffer(vk::UniqueCommandBuffer&& buffer) { + Lock lock(pool_mutex_); + if (!pool_) { + // If the command pool has already been destroyed, then its buffers have + // already been freed. + buffer.release(); return; } - - device_holder_ = context->GetDeviceHolder(); - graphics_pool_ = std::move(pool.value); - is_valid_ = true; + collected_buffers_.push_back(std::move(buffer)); } -CommandPoolVK::~CommandPoolVK() = default; +void CommandPoolVK::Destroy() { + Lock lock(pool_mutex_); + pool_.reset(); -bool CommandPoolVK::IsValid() const { - return is_valid_; + // When the command pool is destroyed, all of its command buffers are freed. + // Handles allocated from that pool are now invalid and must be discarded. + for (auto& buffer : collected_buffers_) { + buffer.release(); + } + collected_buffers_.clear(); } -void CommandPoolVK::Reset() { - { - Lock lock(buffers_to_collect_mutex_); - graphics_pool_.reset(); +// Associates a resource with a thread and context. +using CommandPoolMap = + std::unordered_map>; +FML_THREAD_LOCAL fml::ThreadLocalUniquePtr tls_command_pool_map; - // When the command pool is destroyed, all of its command buffers are freed. - // Handles allocated from that pool are now invalid and must be discarded. - for (vk::UniqueCommandBuffer& buffer : buffers_to_collect_) { - buffer.release(); - } - buffers_to_collect_.clear(); - } +// Map each context to a list of all thread-local command pools associated +// with that context. +static Mutex g_all_pools_map_mutex; +static std::unordered_map>> + g_all_pools_map IPLR_GUARDED_BY(g_all_pools_map_mutex); - for (vk::UniqueCommandBuffer& buffer : recycled_buffers_) { - buffer.release(); +// TODO(matanlurey): Return a status_or<> instead of nullptr when we have one. +std::shared_ptr CommandPoolRecyclerVK::Get() { + auto const strong_context = context_.lock(); + if (!strong_context) { + return nullptr; } - recycled_buffers_.clear(); - is_valid_ = false; -} + // If there is a resource in used for this thread and context, return it. + if (!tls_command_pool_map.get()) { + tls_command_pool_map.reset(new CommandPoolMap()); + } + CommandPoolMap& pool_map = *tls_command_pool_map.get(); + auto const hash = strong_context->GetHash(); + auto const it = pool_map.find(hash); + if (it != pool_map.end()) { + return it->second; + } -vk::UniqueCommandBuffer CommandPoolVK::CreateGraphicsCommandBuffer() { - std::shared_ptr strong_device = device_holder_.lock(); - if (!strong_device) { - return {}; + // Otherwise, create a new resource and return it. + auto pool = Create(); + if (!pool) { + return nullptr; } - FML_DCHECK(std::this_thread::get_id() == owner_id_); + + auto const resource = + std::make_shared(std::move(*pool), context_); + pool_map.emplace(hash, resource); + { - Lock lock(buffers_to_collect_mutex_); - GarbageCollectBuffersIfAble(); + Lock all_pools_lock(g_all_pools_map_mutex); + g_all_pools_map[strong_context.get()].push_back(resource); } - if (!recycled_buffers_.empty()) { - vk::UniqueCommandBuffer result = std::move(recycled_buffers_.back()); - recycled_buffers_.pop_back(); - return result; + return resource; +} + +// TODO(matanlurey): Return a status_or<> instead of nullopt when we have one. +std::optional CommandPoolRecyclerVK::Create() { + // If we can reuse a command pool, do so. + if (auto pool = Reuse()) { + return pool; + } + + // Otherwise, create a new one. + auto context = context_.lock(); + if (!context) { + return std::nullopt; } + vk::CommandPoolCreateInfo info; + info.setQueueFamilyIndex(context->GetGraphicsQueue()->GetIndex().family); + info.setFlags(vk::CommandPoolCreateFlagBits::eTransient); - vk::CommandBufferAllocateInfo alloc_info; - alloc_info.commandPool = graphics_pool_.get(); - alloc_info.commandBufferCount = 1u; - alloc_info.level = vk::CommandBufferLevel::ePrimary; - auto [result, buffers] = - strong_device->GetDevice().allocateCommandBuffersUnique(alloc_info); + auto device = context->GetDevice(); + auto [result, pool] = device.createCommandPoolUnique(info); if (result != vk::Result::eSuccess) { - return {}; + return std::nullopt; } - return std::move(buffers[0]); + return std::move(pool); } -void CommandPoolVK::CollectGraphicsCommandBuffer( - vk::UniqueCommandBuffer buffer) { - Lock lock(buffers_to_collect_mutex_); - if (!graphics_pool_) { - // If the command pool has already been destroyed, then its command buffers - // have been freed and are now invalid. - buffer.release(); +std::optional CommandPoolRecyclerVK::Reuse() { + // If there are no recycled pools, return nullopt. + Lock recycled_lock(recycled_mutex_); + if (recycled_.empty()) { + return std::nullopt; } - buffers_to_collect_.emplace_back(std::move(buffer)); - GarbageCollectBuffersIfAble(); + + // Otherwise, remove and return a recycled pool. + auto pool = std::move(recycled_.back()); + recycled_.pop_back(); + return std::move(pool); } -void CommandPoolVK::GarbageCollectBuffersIfAble() { - if (std::this_thread::get_id() != owner_id_) { +void CommandPoolRecyclerVK::Reclaim(vk::UniqueCommandPool&& pool) { + TRACE_EVENT0("impeller", "ReclaimCommandPool"); + + // Reset the pool on a background thread. + auto strong_context = context_.lock(); + if (!strong_context) { return; } + auto device = strong_context->GetDevice(); + device.resetCommandPool(pool.get()); - for (auto& buffer : buffers_to_collect_) { - buffer->reset(); - recycled_buffers_.emplace_back(std::move(buffer)); + // Move the pool to the recycled list. + Lock recycled_lock(recycled_mutex_); + recycled_.push_back(std::move(pool)); +} + +CommandPoolRecyclerVK::~CommandPoolRecyclerVK() { + // Ensure all recycled pools are reclaimed before this is destroyed. + Dispose(); +} + +void CommandPoolRecyclerVK::Dispose() { + CommandPoolMap* pool_map = tls_command_pool_map.get(); + if (pool_map) { + pool_map->clear(); } +} - buffers_to_collect_.clear(); +void CommandPoolRecyclerVK::DestroyThreadLocalPools(const ContextVK* context) { + // Delete the context's entry in this thread's command pool map. + if (tls_command_pool_map.get()) { + tls_command_pool_map.get()->erase(context->GetHash()); + } + + // Destroy all other thread-local CommandPoolVK instances associated with + // this context. + Lock all_pools_lock(g_all_pools_map_mutex); + auto found = g_all_pools_map.find(context); + if (found != g_all_pools_map.end()) { + for (auto& weak_pool : found->second) { + auto pool = weak_pool.lock(); + if (!pool) { + continue; + } + // Delete all objects held by this pool. The destroyed pool will still + // remain in its thread's TLS map until that thread exits. + pool->Destroy(); + } + g_all_pools_map.erase(found); + } } } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/command_pool_vk.h b/impeller/renderer/backend/vulkan/command_pool_vk.h index 44722dba4b895..d9ea54bf3643b 100644 --- a/impeller/renderer/backend/vulkan/command_pool_vk.h +++ b/impeller/renderer/backend/vulkan/command_pool_vk.h @@ -5,109 +5,140 @@ #pragma once #include -#include - -#include "flutter/fml/macros.h" +#include +#include +#include "fml/macros.h" #include "impeller/base/thread.h" #include "impeller/renderer/backend/vulkan/context_vk.h" -#include "impeller/renderer/backend/vulkan/device_holder.h" +#include "impeller/renderer/backend/vulkan/vk.h" // IWYU pragma: keep. namespace impeller { +class ContextVK; +class CommandPoolRecyclerVK; + //------------------------------------------------------------------------------ -/// @brief An opaque object that provides |vk::CommandBuffer| objects. +/// @brief Manages the lifecycle of a single |vk::CommandPool|. /// -/// A best practice is to create a |CommandPoolVK| for each thread that will -/// submit commands to the GPU, and to recycle (reuse) |vk::CommandBuffer|s by -/// resetting them in a background thread. +/// A |vk::CommandPool| is expensive to create and reset. This class manages +/// the lifecycle of a single |vk::CommandPool| by returning to the origin +/// (|CommandPoolRecyclerVK|) when it is destroyed to be reused. /// -/// @see -/// https://arm-software.github.io/vulkan_best_practice_for_mobile_developers/samples/performance/command_buffer_usage/command_buffer_usage_tutorial.html#resetting-the-command-pool +/// @warning This class is not thread-safe. +/// +/// @see |CommandPoolRecyclerVK| class CommandPoolVK final { public: - /// @brief Gets the |CommandPoolVK| for the current thread. - /// - /// @param[in] context The |ContextVK| to use. - /// - /// If the current thread does not have a |CommandPoolVK|, one will be created - /// and returned. If the current thread already has a |CommandPoolVK|, it will - /// be returned. - /// - /// @return The |CommandPoolVK| for the current thread, or |nullptr| if - /// either the |ContextVK| is invalid, or a pool could not be - /// created for any reason. - /// - /// In other words an invalid command pool will never be returned. - static std::shared_ptr GetThreadLocal( - const ContextVK* context); - - /// @brief Clears all |CommandPoolVK|s for the given |ContextVK|. - /// - /// @param[in] context The |ContextVK| to clear. - /// - /// Every |CommandPoolVK| that was created for every thread that has ever - /// called |GetThreadLocal| with the given |ContextVK| will be cleared. - /// - /// @note Should only be called when the |ContextVK| is being destroyed. - static void ClearAllPools(const ContextVK* context); - ~CommandPoolVK(); - /// @brief Whether or not this |CommandPoolVK| is valid. + /// @brief Creates a resource that manages the life of a command pool. /// - /// A command pool is no longer when valid once it's been |Reset|. - bool IsValid() const; + /// @param[in] pool The command pool to manage. + /// @param[in] recycler The context that will be notified on destruction. + explicit CommandPoolVK(vk::UniqueCommandPool pool, + std::weak_ptr& context) + : pool_(std::move(pool)), context_(context) {} /// @brief Creates and returns a new |vk::CommandBuffer|. /// - /// An attempt is made to reuse existing buffers (instead of creating new - /// ones) by recycling buffers that have been collected by - /// |CollectGraphicsCommandBuffer|. - /// /// @return Always returns a new |vk::CommandBuffer|, but if for any /// reason a valid command buffer could not be created, it will be /// a `{}` default instance (i.e. while being torn down). - vk::UniqueCommandBuffer CreateGraphicsCommandBuffer(); + vk::UniqueCommandBuffer CreateCommandBuffer(); - /// @brief Collects the given |vk::CommandBuffer| for recycling. - /// - /// The given |vk::CommandBuffer| will be recycled (reused) in the future when - /// |CreateGraphicsCommandBuffer| is called. + /// @brief Collects the given |vk::CommandBuffer| to be retained. /// /// @param[in] buffer The |vk::CommandBuffer| to collect. /// - /// @note This method is a noop if a different thread created the pool. - /// /// @see |GarbageCollectBuffersIfAble| - void CollectGraphicsCommandBuffer(vk::UniqueCommandBuffer buffer); + void CollectCommandBuffer(vk::UniqueCommandBuffer&& buffer); + + /// @brief Delete all Vulkan objects in this command pool. + void Destroy(); private: - const std::thread::id owner_id_; - std::weak_ptr device_holder_; - vk::UniqueCommandPool graphics_pool_; - Mutex buffers_to_collect_mutex_; - std::vector buffers_to_collect_ - IPLR_GUARDED_BY(buffers_to_collect_mutex_); - std::vector recycled_buffers_; - bool is_valid_ = false; - - /// @brief Resets, releasing all |vk::CommandBuffer|s. + FML_DISALLOW_COPY_AND_ASSIGN(CommandPoolVK); + + Mutex pool_mutex_; + vk::UniqueCommandPool pool_ IPLR_GUARDED_BY(pool_mutex_); + std::weak_ptr& context_; + + // Used to retain a reference on these until the pool is reset. + std::vector collected_buffers_ + IPLR_GUARDED_BY(pool_mutex_); +}; + +//------------------------------------------------------------------------------ +/// @brief Creates and manages the lifecycle of |vk::CommandPool| objects. +/// +/// A |vk::CommandPool| is expensive to create and reset. This class manages +/// the lifecycle of |vk::CommandPool| objects by creating and recycling them; +/// or in other words, a pool for command pools. +/// +/// A single instance should be created per |ContextVK|. +/// +/// Every "frame", a single |CommandPoolResourceVk| is made available for each +/// thread that calls |Get|. After calling |Dispose|, the current thread's pool +/// is moved to a background thread, reset, and made available for the next time +/// |Get| is called and needs to create a command pool. +/// +/// Commands in the command pool are not necessarily done executing when the +/// pool is recycled, when all references are dropped to the pool, they are +/// reset and returned to the pool of available pools. +/// +/// @note This class is thread-safe. +/// +/// @see |vk::CommandPoolResourceVk| +/// @see |ContextVK| +/// @see +/// https://arm-software.github.io/vulkan_best_practice_for_mobile_developers/samples/performance/command_buffer_usage/command_buffer_usage_tutorial.html +class CommandPoolRecyclerVK final + : public std::enable_shared_from_this { + public: + ~CommandPoolRecyclerVK(); + + /// @brief Clean up resources held by all per-thread command pools + /// associated with the given context. /// - /// @note "All" includes active and recycled buffers. - void Reset(); + /// @param[in] context The context. + static void DestroyThreadLocalPools(const ContextVK* context); - explicit CommandPoolVK(const ContextVK* context); + /// @brief Creates a recycler for the given |ContextVK|. + /// + /// @param[in] context The context to create the recycler for. + explicit CommandPoolRecyclerVK(std::weak_ptr context) + : context_(std::move(context)) {} - /// @brief Collects buffers for recycling if able. + /// @brief Gets a command pool for the current thread. /// - /// If any buffers have been returned through |CollectGraphicsCommandBuffer|, - /// then they are reset and made available to future calls to - /// |CreateGraphicsCommandBuffer|. + /// @warning Returns a |nullptr| if a pool could not be created. + std::shared_ptr Get(); + + /// @brief Returns a command pool to be reset on a background thread. /// - /// @note This method is a noop if a different thread created the pool. - void GarbageCollectBuffersIfAble() IPLR_REQUIRES(buffers_to_collect_mutex_); + /// @param[in] pool The pool to recycler. + void Reclaim(vk::UniqueCommandPool&& pool); - FML_DISALLOW_COPY_AND_ASSIGN(CommandPoolVK); + /// @brief Clears all recycled command pools to let them be reclaimed. + void Dispose(); + + private: + std::weak_ptr context_; + + Mutex recycled_mutex_; + std::vector recycled_ IPLR_GUARDED_BY(recycled_mutex_); + + /// @brief Creates a new |vk::CommandPool|. + /// + /// @returns Returns a |std::nullopt| if a pool could not be created. + std::optional Create(); + + /// @brief Reuses a recycled |vk::CommandPool|, if available. + /// + /// @returns Returns a |std::nullopt| if a pool was not available. + std::optional Reuse(); + + FML_DISALLOW_COPY_AND_ASSIGN(CommandPoolRecyclerVK); }; } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc b/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc new file mode 100644 index 0000000000000..0b140542c40ff --- /dev/null +++ b/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc @@ -0,0 +1,116 @@ +// 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/testing/testing.h" // IWYU pragma: keep. +#include "fml/synchronization/waitable_event.h" +#include "impeller/renderer/backend/vulkan/command_pool_vk.h" +#include "impeller/renderer/backend/vulkan/resource_manager_vk.h" +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" + +namespace impeller { +namespace testing { + +TEST(CommandPoolRecyclerVKTest, GetsACommandPoolPerThread) { + auto const context = MockVulkanContextBuilder().Build(); + + { + // Record the memory location of each pointer to a command pool. + // + // These pools have to be held at this context, otherwise they will be + // dropped and recycled and potentially reused by another thread, causing + // flaky tests. + std::shared_ptr pool1; + std::shared_ptr pool2; + + // Create a command pool in two threads and record the memory location. + std::thread thread1( + [&]() { pool1 = context->GetCommandPoolRecycler()->Get(); }); + + std::thread thread2( + [&]() { pool2 = context->GetCommandPoolRecycler()->Get(); }); + + thread1.join(); + thread2.join(); + + // The two command pools should be different. + EXPECT_NE(pool1, pool2); + } + + context->Shutdown(); +} + +TEST(CommandPoolRecyclerVKTest, GetsTheSameCommandPoolOnSameThread) { + auto const context = MockVulkanContextBuilder().Build(); + + auto const pool1 = context->GetCommandPoolRecycler()->Get(); + auto const pool2 = context->GetCommandPoolRecycler()->Get(); + + // The two command pools should be the same. + EXPECT_EQ(pool1.get(), pool2.get()); + + context->Shutdown(); +} + +namespace { + +// Invokes the provided callback when the destructor is called. +// +// Can be moved, but not copied. +class DeathRattle final { + public: + explicit DeathRattle(std::function callback) + : callback_(std::move(callback)) {} + + DeathRattle(DeathRattle&&) = default; + DeathRattle& operator=(DeathRattle&&) = default; + + ~DeathRattle() { callback_(); } + + private: + std::function callback_; +}; + +} // namespace + +TEST(CommandPoolRecyclerVKTest, ReclaimMakesCommandPoolAvailable) { + auto const context = MockVulkanContextBuilder().Build(); + + { + // Fetch a pool (which will be created). + auto const recycler = context->GetCommandPoolRecycler(); + auto const pool = recycler->Get(); + + // This normally is called at the end of a frame. + recycler->Dispose(); + } + + // Add something to the resource manager and have it notify us when it's + // destroyed. That should give us a non-flaky signal that the pool has been + // reclaimed as well. + auto waiter = fml::AutoResetWaitableEvent(); + auto rattle = DeathRattle([&waiter]() { waiter.Signal(); }); + { + UniqueResourceVKT resource(context->GetResourceManager(), + std::move(rattle)); + } + waiter.Wait(); + + // On another thread explicitly, request a new pool. + std::thread thread([&]() { + auto const pool = context->GetCommandPoolRecycler()->Get(); + EXPECT_NE(pool.get(), nullptr); + }); + + thread.join(); + + // Now check that we only ever created one pool. + auto const called = GetMockVulkanFunctions(context->GetDevice()); + EXPECT_EQ(std::count(called->begin(), called->end(), "vkCreateCommandPool"), + 1u); + + context->Shutdown(); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index a4748427e104a..8204f5965e39f 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -13,11 +13,10 @@ #include #include #include -#include #include #include -#include "flutter/fml/build_config.h" +#include "flutter/fml/cpu_affinity.h" #include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" #include "impeller/renderer/backend/vulkan/allocator_vk.h" @@ -27,6 +26,7 @@ #include "impeller/renderer/backend/vulkan/command_pool_vk.h" #include "impeller/renderer/backend/vulkan/debug_report_vk.h" #include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" +#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h" #include "impeller/renderer/backend/vulkan/resource_manager_vk.h" #include "impeller/renderer/backend/vulkan/surface_context_vk.h" #include "impeller/renderer/capabilities.h" @@ -114,7 +114,7 @@ ContextVK::~ContextVK() { if (device_holder_ && device_holder_->device) { [[maybe_unused]] auto result = device_holder_->device->waitIdle(); } - CommandPoolVK::ClearAllPools(this); + CommandPoolRecyclerVK::DestroyThreadLocalPools(this); } Context::BackendType ContextVK::GetBackendType() const { @@ -130,13 +130,16 @@ void ContextVK::Setup(Settings settings) { raster_message_loop_ = fml::ConcurrentMessageLoop::Create( std::min(4u, std::thread::hardware_concurrency())); -#ifdef FML_OS_ANDROID raster_message_loop_->PostTaskToAllWorkers([]() { + // Currently we only use the worker task pool for small parts of a frame + // workload, if this changes this setting may need to be adjusted. + fml::RequestAffinity(fml::CpuAffinity::kNotPerformance); +#ifdef FML_OS_ANDROID if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) { FML_LOG(ERROR) << "Failed to set Workers task runner priority"; } - }); #endif // FML_OS_ANDROID + }); auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER; dispatcher.init(settings.proc_address_callback); @@ -377,13 +380,9 @@ void ContextVK::Setup(Settings settings) { /// auto fence_waiter = std::shared_ptr(new FenceWaiterVK(device_holder)); - if (!fence_waiter->IsValid()) { - VALIDATION_LOG << "Could not create fence waiter."; - return; - } //---------------------------------------------------------------------------- - /// Create the resource manager. + /// Create the resource manager and command pool recycler. /// auto resource_manager = ResourceManagerVK::Create(); if (!resource_manager) { @@ -391,6 +390,13 @@ void ContextVK::Setup(Settings settings) { return; } + auto command_pool_recycler = + std::make_shared(weak_from_this()); + if (!command_pool_recycler) { + VALIDATION_LOG << "Could not create command pool recycler."; + return; + } + //---------------------------------------------------------------------------- /// Fetch the queues. /// @@ -421,9 +427,14 @@ void ContextVK::Setup(Settings settings) { device_capabilities_ = std::move(caps); fence_waiter_ = std::move(fence_waiter); resource_manager_ = std::move(resource_manager); + command_pool_recycler_ = std::move(command_pool_recycler); device_name_ = std::string(physical_device_properties.deviceName); is_valid_ = true; + // Create the GPU Tracer later because it depends on state from + // the ContextVK. + gpu_tracer_ = std::make_shared(GetDeviceHolder()); + //---------------------------------------------------------------------------- /// Label all the relevant objects. This happens after setup so that the /// debug messengers have had a chance to be set up. @@ -481,6 +492,14 @@ ContextVK::GetConcurrentWorkerTaskRunner() const { } void ContextVK::Shutdown() { + // There are multiple objects, for example |CommandPoolVK|, that in their + // destructors make a strong reference to |ContextVK|. Resetting these shared + // pointers ensures that cleanup happens in a correct order. + // + // tl;dr: Without it, we get thread::join failures on shutdown. + fence_waiter_.reset(); + resource_manager_.reset(); + raster_message_loop_->Terminate(); } @@ -508,9 +527,18 @@ std::shared_ptr ContextVK::GetResourceManager() const { return resource_manager_; } +std::shared_ptr ContextVK::GetCommandPoolRecycler() + const { + return command_pool_recycler_; +} + std::unique_ptr ContextVK::CreateGraphicsCommandEncoderFactory() const { return std::make_unique(weak_from_this()); } +std::shared_ptr ContextVK::GetGPUTracer() const { + return gpu_tracer_; +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/context_vk.h b/impeller/renderer/backend/vulkan/context_vk.h index 316cf1cbb2161..0febc8b03da15 100644 --- a/impeller/renderer/backend/vulkan/context_vk.h +++ b/impeller/renderer/backend/vulkan/context_vk.h @@ -12,6 +12,7 @@ #include "flutter/fml/unique_fd.h" #include "impeller/base/backend_cast.h" #include "impeller/core/formats.h" +#include "impeller/renderer/backend/vulkan/command_pool_vk.h" #include "impeller/renderer/backend/vulkan/device_holder.h" #include "impeller/renderer/backend/vulkan/pipeline_library_vk.h" #include "impeller/renderer/backend/vulkan/queue_vk.h" @@ -26,10 +27,12 @@ bool HasValidationLayers(); class CommandEncoderFactoryVK; class CommandEncoderVK; +class CommandPoolRecyclerVK; class DebugReportVK; class FenceWaiterVK; class ResourceManagerVK; class SurfaceContextVK; +class GPUTracerVK; class ContextVK final : public Context, public BackendCast, @@ -140,6 +143,12 @@ class ContextVK final : public Context, std::shared_ptr GetResourceManager() const; + std::shared_ptr GetCommandPoolRecycler() const; + + std::shared_ptr GetGPUTracer() const; + + void RecordFrameEndTime() const; + private: struct DeviceHolderImpl : public DeviceHolder { // |DeviceHolder| @@ -164,8 +173,11 @@ class ContextVK final : public Context, std::shared_ptr device_capabilities_; std::shared_ptr fence_waiter_; std::shared_ptr resource_manager_; + std::shared_ptr command_pool_recycler_; std::string device_name_; std::shared_ptr raster_message_loop_; + std::shared_ptr gpu_tracer_; + bool sync_presentation_ = false; const uint64_t hash_; diff --git a/impeller/renderer/backend/vulkan/context_vk_unittests.cc b/impeller/renderer/backend/vulkan/context_vk_unittests.cc index 8a577be6d79bc..25128aa347966 100644 --- a/impeller/renderer/backend/vulkan/context_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/context_vk_unittests.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/fml/synchronization/waitable_event.h" #include "flutter/testing/testing.h" // IWYU pragma: keep #include "impeller/renderer/backend/vulkan/command_pool_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h" @@ -14,9 +15,8 @@ TEST(ContextVKTest, DeletesCommandPools) { std::weak_ptr weak_context; std::weak_ptr weak_pool; { - std::shared_ptr context = CreateMockVulkanContext(); - std::shared_ptr pool = - CommandPoolVK::GetThreadLocal(context.get()); + std::shared_ptr context = MockVulkanContextBuilder().Build(); + auto const pool = context->GetCommandPoolRecycler()->Get(); weak_pool = pool; weak_context = context; ASSERT_TRUE(weak_pool.lock()); @@ -26,11 +26,42 @@ TEST(ContextVKTest, DeletesCommandPools) { ASSERT_FALSE(weak_context.lock()); } +TEST(ContextVKTest, DeletesCommandPoolsOnAllThreads) { + std::weak_ptr weak_context; + std::weak_ptr weak_pool_main; + + std::shared_ptr context = MockVulkanContextBuilder().Build(); + weak_pool_main = context->GetCommandPoolRecycler()->Get(); + weak_context = context; + ASSERT_TRUE(weak_pool_main.lock()); + ASSERT_TRUE(weak_context.lock()); + + // Start a second thread that obtains a command pool. + fml::AutoResetWaitableEvent latch1, latch2; + std::weak_ptr weak_pool_thread; + std::thread thread([&]() { + weak_pool_thread = context->GetCommandPoolRecycler()->Get(); + latch1.Signal(); + latch2.Wait(); + }); + + // Delete the ContextVK on the main thread. + latch1.Wait(); + context.reset(); + ASSERT_FALSE(weak_pool_main.lock()); + ASSERT_FALSE(weak_context.lock()); + + // Stop the second thread and check that its command pool has been deleted. + latch2.Signal(); + thread.join(); + ASSERT_FALSE(weak_pool_thread.lock()); +} + TEST(ContextVKTest, DeletePipelineAfterContext) { std::shared_ptr> pipeline; std::shared_ptr> functions; { - std::shared_ptr context = CreateMockVulkanContext(); + std::shared_ptr context = MockVulkanContextBuilder().Build(); PipelineDescriptor pipeline_desc; pipeline_desc.SetVertexDescriptor(std::make_shared()); PipelineFuture pipeline_future = @@ -49,7 +80,7 @@ TEST(ContextVKTest, DeleteShaderFunctionAfterContext) { std::shared_ptr shader_function; std::shared_ptr> functions; { - std::shared_ptr context = CreateMockVulkanContext(); + std::shared_ptr context = MockVulkanContextBuilder().Build(); PipelineDescriptor pipeline_desc; pipeline_desc.SetVertexDescriptor(std::make_shared()); std::vector data = {0x03, 0x02, 0x23, 0x07}; @@ -71,7 +102,7 @@ TEST(ContextVKTest, DeletePipelineLibraryAfterContext) { std::shared_ptr pipeline_library; std::shared_ptr> functions; { - std::shared_ptr context = CreateMockVulkanContext(); + std::shared_ptr context = MockVulkanContextBuilder().Build(); PipelineDescriptor pipeline_desc; pipeline_desc.SetVertexDescriptor(std::make_shared()); pipeline_library = context->GetPipelineLibrary(); @@ -86,9 +117,30 @@ TEST(ContextVKTest, DeletePipelineLibraryAfterContext) { TEST(ContextVKTest, CanCreateContextInAbsenceOfValidationLayers) { // The mocked methods don't report the presence of a validation layer but we // explicitly ask for validation. Context creation should continue anyway. - auto context = CreateMockVulkanContext( - [](auto& settings) { settings.enable_validation = true; }); + auto context = MockVulkanContextBuilder() + .SetSettingsCallback([](auto& settings) { + settings.enable_validation = true; + }) + .Build(); + ASSERT_NE(context, nullptr); + const CapabilitiesVK* capabilites_vk = + reinterpret_cast(context->GetCapabilities().get()); + ASSERT_FALSE(capabilites_vk->AreValidationsEnabled()); +} + +TEST(ContextVKTest, CanCreateContextWithValidationLayers) { + auto context = + MockVulkanContextBuilder() + .SetSettingsCallback( + [](auto& settings) { settings.enable_validation = true; }) + .SetInstanceExtensions( + {"VK_KHR_surface", "VK_MVK_macos_surface", "VK_EXT_debug_utils"}) + .SetInstanceLayers({"VK_LAYER_KHRONOS_validation"}) + .Build(); ASSERT_NE(context, nullptr); + const CapabilitiesVK* capabilites_vk = + reinterpret_cast(context->GetCapabilities().get()); + ASSERT_TRUE(capabilites_vk->AreValidationsEnabled()); } } // namespace testing diff --git a/impeller/renderer/backend/vulkan/fence_waiter_vk.cc b/impeller/renderer/backend/vulkan/fence_waiter_vk.cc index 73ba0879dd3c2..ef906f869af2c 100644 --- a/impeller/renderer/backend/vulkan/fence_waiter_vk.cc +++ b/impeller/renderer/backend/vulkan/fence_waiter_vk.cc @@ -6,7 +6,9 @@ #include #include +#include +#include "flutter/fml/cpu_affinity.h" #include "flutter/fml/thread.h" #include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" @@ -47,28 +49,25 @@ class WaitSetEntry { FenceWaiterVK::FenceWaiterVK(std::weak_ptr device_holder) : device_holder_(std::move(device_holder)) { waiter_thread_ = std::make_unique([&]() { Main(); }); - is_valid_ = true; } FenceWaiterVK::~FenceWaiterVK() { Terminate(); - if (waiter_thread_) { - waiter_thread_->join(); - } -} - -bool FenceWaiterVK::IsValid() const { - return is_valid_; + waiter_thread_->join(); } bool FenceWaiterVK::AddFence(vk::UniqueFence fence, const fml::closure& callback) { TRACE_EVENT0("flutter", "FenceWaiterVK::AddFence"); - if (!IsValid() || !fence || !callback) { + if (!fence || !callback) { return false; } { + // Maintain the invariant that terminate_ is accessed only under the lock. std::scoped_lock lock(wait_set_mutex_); + if (terminate_) { + return false; + } wait_set_.emplace_back(WaitSetEntry::Create(std::move(fence), callback)); } wait_set_cv_.notify_one(); @@ -88,89 +87,116 @@ static std::vector GetFencesForWaitSet(const WaitSet& set) { void FenceWaiterVK::Main() { fml::Thread::SetCurrentThreadName( fml::Thread::ThreadConfig{"io.flutter.impeller.fence_waiter"}); - - using namespace std::literals::chrono_literals; + // Since this thread mostly waits on fences, it doesn't need to be fast. + fml::RequestAffinity(fml::CpuAffinity::kEfficiency); while (true) { - std::unique_lock lock(wait_set_mutex_); + // We'll read the terminate_ flag within the lock below. + bool terminate = false; - // If there are no fences to wait on, wait on the condition variable. - wait_set_cv_.wait(lock, [&]() { return !wait_set_.empty() || terminate_; }); - - // We don't want to check on fence status or collect wait set entries in the - // critical section. Copy the array of entries and immediately unlock the - // mutex. - WaitSet wait_set = wait_set_; + { + std::unique_lock lock(wait_set_mutex_); - const auto terminate = terminate_; + // If there are no fences to wait on, wait on the condition variable. + wait_set_cv_.wait(lock, + [&]() { return !wait_set_.empty() || terminate_; }); - lock.unlock(); + // Still under the lock, check if the waiter has been terminated. + terminate = terminate_; + } if (terminate) { + WaitUntilEmpty(); break; } - // Check if the context had died in the meantime. - auto device_holder = device_holder_.lock(); - if (!device_holder) { + if (!Wait()) { break; } + } +} - const auto& device = device_holder->GetDevice(); +void FenceWaiterVK::WaitUntilEmpty() { + // Note, there is no lock because once terminate_ is set to true, no other + // fence can be added to the wait set. Just in case, here's a FML_DCHECK: + FML_DCHECK(terminate_) << "Fence waiter must be terminated."; + while (!wait_set_.empty() && Wait()) { + // Intentionally empty. + } +} - // Wait for one or more fences to be signaled. Any additional fences added - // to the waiter will be serviced in the next pass. If a fence that is going - // to be signaled at an abnormally long deadline is the only one in the set, - // a timeout will bail out the wait. - auto fences = GetFencesForWaitSet(wait_set); - if (fences.empty()) { - continue; - } +bool FenceWaiterVK::Wait() { + // Snapshot the wait set and wait on the fences. + WaitSet wait_set; + { + std::scoped_lock lock(wait_set_mutex_); + wait_set = wait_set_; + } - auto result = device.waitForFences( - fences.size(), // fences count - fences.data(), // fences - false, // wait for all - std::chrono::nanoseconds{100ms}.count() // timeout (ns) - ); - if (!(result == vk::Result::eSuccess || result == vk::Result::eTimeout)) { - VALIDATION_LOG << "Fence waiter encountered an unexpected error. Tearing " - "down the waiter thread."; - break; - } + using namespace std::literals::chrono_literals; - // One or more fences have been signaled. Find out which ones and update - // their signaled statuses. - { - TRACE_EVENT0("impeller", "CheckFenceStatus"); - for (auto& entry : wait_set) { - entry->UpdateSignalledStatus(device); - } - wait_set.clear(); - } + // Check if the context had died in the meantime. + auto device_holder = device_holder_.lock(); + if (!device_holder) { + return false; + } - // Quickly acquire the wait set lock and erase signaled entries. Make sure - // the mutex is unlocked before calling the destructors of the erased - // entries. These might touch allocators. - WaitSet erased_entries; - { - static auto is_signalled = [](const auto& entry) { - return entry->IsSignalled(); - }; - std::scoped_lock lock(wait_set_mutex_); - std::copy_if(wait_set_.begin(), wait_set_.end(), - std::back_inserter(erased_entries), is_signalled); - wait_set_.erase( - std::remove_if(wait_set_.begin(), wait_set_.end(), is_signalled), - wait_set_.end()); - } + const auto& device = device_holder->GetDevice(); + // Wait for one or more fences to be signaled. Any additional fences added + // to the waiter will be serviced in the next pass. If a fence that is going + // to be signaled at an abnormally long deadline is the only one in the set, + // a timeout will bail out the wait. + auto fences = GetFencesForWaitSet(wait_set); + if (fences.empty()) { + return true; + } - { - TRACE_EVENT0("impeller", "ClearSignaledFences"); - // Erase the erased entries which will invoke callbacks. - erased_entries.clear(); // Bit redundant because of scope but hey. + auto result = device.waitForFences( + /*fenceCount=*/fences.size(), + /*pFences=*/fences.data(), + /*waitAll=*/false, + /*timeout=*/std::chrono::nanoseconds{100ms}.count()); + if (!(result == vk::Result::eSuccess || result == vk::Result::eTimeout)) { + VALIDATION_LOG << "Fence waiter encountered an unexpected error. Tearing " + "down the waiter thread."; + return false; + } + + // One or more fences have been signaled. Find out which ones and update + // their signaled statuses. + { + TRACE_EVENT0("impeller", "CheckFenceStatus"); + for (auto& entry : wait_set) { + entry->UpdateSignalledStatus(device); } + wait_set.clear(); } + + // Quickly acquire the wait set lock and erase signaled entries. Make sure + // the mutex is unlocked before calling the destructors of the erased + // entries. These might touch allocators. + WaitSet erased_entries; + { + static constexpr auto is_signalled = [](const auto& entry) { + return entry->IsSignalled(); + }; + std::scoped_lock lock(wait_set_mutex_); + + // TODO(matanlurey): Iterate the list 1x by copying is_signaled into erased. + std::copy_if(wait_set_.begin(), wait_set_.end(), + std::back_inserter(erased_entries), is_signalled); + wait_set_.erase( + std::remove_if(wait_set_.begin(), wait_set_.end(), is_signalled), + wait_set_.end()); + } + + { + TRACE_EVENT0("impeller", "ClearSignaledFences"); + // Erase the erased entries which will invoke callbacks. + erased_entries.clear(); // Bit redundant because of scope but hey. + } + + return true; } void FenceWaiterVK::Terminate() { diff --git a/impeller/renderer/backend/vulkan/fence_waiter_vk.h b/impeller/renderer/backend/vulkan/fence_waiter_vk.h index 7e562433ddb9d..83050c554d037 100644 --- a/impeller/renderer/backend/vulkan/fence_waiter_vk.h +++ b/impeller/renderer/backend/vulkan/fence_waiter_vk.h @@ -42,12 +42,14 @@ class FenceWaiterVK { std::condition_variable wait_set_cv_; WaitSet wait_set_; bool terminate_ = false; - bool is_valid_ = false; explicit FenceWaiterVK(std::weak_ptr device_holder); void Main(); + bool Wait(); + void WaitUntilEmpty(); + FML_DISALLOW_COPY_AND_ASSIGN(FenceWaiterVK); }; diff --git a/impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc b/impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc new file mode 100644 index 0000000000000..ef2ff5d40cbb9 --- /dev/null +++ b/impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc @@ -0,0 +1,130 @@ +// 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 "fml/synchronization/waitable_event.h" +#include "gtest/gtest.h" // IWYU pragma: keep +#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" // IWYU pragma: keep +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" + +namespace impeller { +namespace testing { + +TEST(FenceWaiterVKTest, IgnoresNullFence) { + auto const context = MockVulkanContextBuilder().Build(); + auto const waiter = context->GetFenceWaiter(); + EXPECT_FALSE(waiter->AddFence(vk::UniqueFence(), []() {})); +} + +TEST(FenceWaiterVKTest, IgnoresNullCallback) { + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + auto const waiter = context->GetFenceWaiter(); + + auto fence = device.createFenceUnique({}).value; + EXPECT_FALSE(waiter->AddFence(std::move(fence), nullptr)); +} + +TEST(FenceWaiterVKTest, ExecutesFenceCallback) { + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + auto const waiter = context->GetFenceWaiter(); + + auto signal = fml::ManualResetWaitableEvent(); + auto fence = device.createFenceUnique({}).value; + waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); + + signal.Wait(); +} + +TEST(FenceWaiterVKTest, ExecutesFenceCallbackX2) { + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + auto const waiter = context->GetFenceWaiter(); + + auto signal = fml::ManualResetWaitableEvent(); + auto fence = device.createFenceUnique({}).value; + waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); + + auto signal2 = fml::ManualResetWaitableEvent(); + auto fence2 = device.createFenceUnique({}).value; + waiter->AddFence(std::move(fence2), [&signal2]() { signal2.Signal(); }); + + signal.Wait(); + signal2.Wait(); +} + +TEST(FenceWaiterVKTest, ExecutesNewFenceThenOldFence) { + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + auto const waiter = context->GetFenceWaiter(); + + auto signal = fml::ManualResetWaitableEvent(); + auto fence = device.createFenceUnique({}).value; + MockFence::SetStatus(fence, vk::Result::eNotReady); + auto raw_fence = MockFence::GetRawPointer(fence); + waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); + + // The easiest way to verify that the callback was _not_ called is to wait + // for a timeout, but that could introduce flakiness. Instead, we'll add a + // second fence that will signal immediately, and wait for that one instead. + { + auto signal2 = fml::ManualResetWaitableEvent(); + auto fence2 = device.createFenceUnique({}).value; + MockFence::SetStatus(fence2, vk::Result::eSuccess); + waiter->AddFence(std::move(fence2), [&signal2]() { signal2.Signal(); }); + signal2.Wait(); + } + + // Now, we'll signal the first fence, and wait for the callback to be called. + raw_fence->SetStatus(vk::Result::eSuccess); + + // Now, we'll signal the first fence, and wait for the callback to be called. + signal.Wait(); +} + +TEST(FenceWaiterVKTest, AddFenceDoesNothingIfTerminating) { + auto signal = fml::ManualResetWaitableEvent(); + + { + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + auto const waiter = context->GetFenceWaiter(); + waiter->Terminate(); + + auto fence = device.createFenceUnique({}).value; + waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); + } + + // Ensure the fence did _not_ signal. + EXPECT_TRUE(signal.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(100))); +} + +TEST(FenceWaiterVKTest, InProgressFencesStillWaitIfTerminated) { + MockFence* raw_fence = nullptr; + auto signal = fml::ManualResetWaitableEvent(); + + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + auto const waiter = context->GetFenceWaiter(); + + // Add a fence that isn't signalled yet. + auto fence = device.createFenceUnique({}).value; + + // Even if the fence is eSuccess, it's not guaranteed to be called in time. + MockFence::SetStatus(fence, vk::Result::eNotReady); + raw_fence = MockFence::GetRawPointer(fence); + waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); + + // Terminate the waiter. + waiter->Terminate(); + + // Signal the fence. + raw_fence->SetStatus(vk::Result::eSuccess); + + // This will hang if the fence was not signalled. + signal.Wait(); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc b/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc new file mode 100644 index 0000000000000..ea738ad269ce4 --- /dev/null +++ b/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc @@ -0,0 +1,212 @@ +// 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/gpu_tracer_vk.h" + +#include +#include +#include +#include +#include "fml/logging.h" +#include "fml/trace_event.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "vulkan/vulkan.hpp" + +namespace impeller { + +static constexpr uint32_t kPoolSize = 64u; + +GPUTracerVK::GPUTracerVK(const std::shared_ptr& device_holder) + : device_holder_(device_holder) { + timestamp_period_ = device_holder_->GetPhysicalDevice() + .getProperties() + .limits.timestampPeriod; + if (timestamp_period_ <= 0) { + // The device does not support timestamp queries. + return; + } + // Disable tracing in release mode. +#ifdef IMPELLER_DEBUG + enabled_ = true; +#endif +} + +bool GPUTracerVK::IsEnabled() const { + return enabled_; +} + +void GPUTracerVK::MarkFrameStart() { + FML_DCHECK(!in_frame_); + in_frame_ = true; + raster_thread_id_ = std::this_thread::get_id(); +} + +void GPUTracerVK::MarkFrameEnd() { + if (!enabled_) { + return; + } + + Lock lock(trace_state_mutex_); + current_state_ = (current_state_ + 1) % kTraceStatesSize; + + auto& state = trace_states_[current_state_]; + // If there are still pending buffers on the trace state we're switching to, + // that means that a cmd buffer we were relying on to signal this likely + // never finished. This shouldn't happen unless there is a bug in the + // encoder logic. We set it to zero anyway to prevent a validation error + // from becoming a memory leak. + FML_DCHECK(state.pending_buffers == 0u); + + state.pending_buffers = 0; + state.current_index = 0; + in_frame_ = false; +} + +std::unique_ptr GPUTracerVK::CreateGPUProbe() { + return std::make_unique(weak_from_this()); +} + +void GPUTracerVK::RecordCmdBufferStart(const vk::CommandBuffer& buffer, + GPUProbe& probe) { + if (!enabled_ || std::this_thread::get_id() != raster_thread_id_ || + !in_frame_) { + return; + } + Lock lock(trace_state_mutex_); + auto& state = trace_states_[current_state_]; + + // Initialize the query pool for the first query on each frame. + if (state.pending_buffers == 0) { + vk::QueryPoolCreateInfo info; + info.queryCount = kPoolSize; + info.queryType = vk::QueryType::eTimestamp; + + auto [status, pool] = + device_holder_->GetDevice().createQueryPoolUnique(info); + if (status != vk::Result::eSuccess) { + VALIDATION_LOG << "Failed to create query pool."; + return; + } + trace_states_[current_state_].query_pool = std::move(pool); + buffer.resetQueryPool(trace_states_[current_state_].query_pool.get(), 0, + kPoolSize); + } + + // We size the query pool to kPoolSize, but Flutter applications can create an + // unbounded amount of work per frame. If we encounter this, stop recording + // cmds. + if (state.current_index >= kPoolSize) { + return; + } + + buffer.writeTimestamp(vk::PipelineStageFlagBits::eTopOfPipe, + trace_states_[current_state_].query_pool.get(), + state.current_index); + state.current_index += 1; + if (!probe.index_.has_value()) { + state.pending_buffers += 1; + probe.index_ = current_state_; + } +} + +void GPUTracerVK::RecordCmdBufferEnd(const vk::CommandBuffer& buffer, + GPUProbe& probe) { + if (!enabled_ || std::this_thread::get_id() != raster_thread_id_ || + !in_frame_) { + return; + } + Lock lock(trace_state_mutex_); + GPUTraceState& state = trace_states_[current_state_]; + + if (state.current_index >= kPoolSize) { + return; + } + + buffer.writeTimestamp(vk::PipelineStageFlagBits::eBottomOfPipe, + state.query_pool.get(), state.current_index); + + state.current_index += 1; + if (!probe.index_.has_value()) { + state.pending_buffers += 1; + probe.index_ = current_state_; + } +} + +void GPUTracerVK::OnFenceComplete(size_t frame_index) { + if (!enabled_) { + return; + } + Lock lock(trace_state_mutex_); + GPUTraceState& state = trace_states_[frame_index]; + + FML_DCHECK(state.pending_buffers > 0); + state.pending_buffers -= 1; + + if (state.pending_buffers == 0) { + auto buffer_count = state.current_index; + std::vector bits(buffer_count); + + auto result = device_holder_->GetDevice().getQueryPoolResults( + state.query_pool.get(), 0, state.current_index, + buffer_count * sizeof(uint64_t), bits.data(), sizeof(uint64_t), + vk::QueryResultFlagBits::e64); + // This may return VK_NOT_READY if the query couldn't be completed, or if + // there are queries still pending. From local testing, this happens + // occassionally on very expensive frames. Its unclear if we can do anything + // about this, because by design this should only signal after all cmd + // buffers have signaled. Adding VK_QUERY_RESULT_WAIT_BIT to the flags + // passed to getQueryPoolResults seems like it would fix this, but actually + // seems to result in more stuck query errors. Better to just drop them and + // move on. + if (result != vk::Result::eSuccess) { + return; + } + + uint64_t smallest_timestamp = std::numeric_limits::max(); + uint64_t largest_timestamp = 0; + for (auto i = 0u; i < bits.size(); i++) { + smallest_timestamp = std::min(smallest_timestamp, bits[i]); + largest_timestamp = std::max(largest_timestamp, bits[i]); + } + auto gpu_ms = + (((largest_timestamp - smallest_timestamp) * timestamp_period_) / + 1000000); + FML_TRACE_COUNTER("flutter", "GPUTracer", + reinterpret_cast(this), // Trace Counter ID + "FrameTimeMS", gpu_ms); + } +} + +GPUProbe::GPUProbe(const std::weak_ptr& tracer) + : tracer_(tracer) {} + +GPUProbe::~GPUProbe() { + if (!index_.has_value()) { + return; + } + auto tracer = tracer_.lock(); + if (!tracer) { + return; + } + tracer->OnFenceComplete(index_.value()); +} + +void GPUProbe::RecordCmdBufferStart(const vk::CommandBuffer& buffer) { + auto tracer = tracer_.lock(); + if (!tracer) { + return; + } + tracer->RecordCmdBufferStart(buffer, *this); +} + +void GPUProbe::RecordCmdBufferEnd(const vk::CommandBuffer& buffer) { + auto tracer = tracer_.lock(); + if (!tracer) { + return; + } + tracer->RecordCmdBufferEnd(buffer, *this); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/gpu_tracer_vk.h b/impeller/renderer/backend/vulkan/gpu_tracer_vk.h new file mode 100644 index 0000000000000..e1fd7150f8355 --- /dev/null +++ b/impeller/renderer/backend/vulkan/gpu_tracer_vk.h @@ -0,0 +1,113 @@ +// 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 +#include + +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/device_holder.h" +#include "vulkan/vulkan_handles.hpp" + +namespace impeller { + +class GPUProbe; + +/// @brief A class that uses timestamp queries to record the approximate GPU +/// execution time. +class GPUTracerVK : public std::enable_shared_from_this { + public: + explicit GPUTracerVK(const std::shared_ptr& device_holder); + + ~GPUTracerVK() = default; + + /// @brief Create a GPUProbe to trace the execution of a command buffer on the + /// GPU. + std::unique_ptr CreateGPUProbe(); + + /// @brief Signal the start of a frame workload. + /// + /// Any cmd buffers that are created after this call and before + /// [MarkFrameEnd] will be attributed to the current frame. + void MarkFrameStart(); + + /// @brief Signal the end of a frame workload. + void MarkFrameEnd(); + + // visible for testing. + bool IsEnabled() const; + + private: + friend class GPUProbe; + + static const constexpr size_t kTraceStatesSize = 32u; + + /// @brief Signal that the cmd buffer is completed. + /// + /// If [frame_index] is std::nullopt, this frame recording is ignored. + void OnFenceComplete(size_t frame); + + /// @brief Record a timestamp query into the provided cmd buffer to record + /// start time. + void RecordCmdBufferStart(const vk::CommandBuffer& buffer, GPUProbe& probe); + + /// @brief Record a timestamp query into the provided cmd buffer to record end + /// time. + void RecordCmdBufferEnd(const vk::CommandBuffer& buffer, GPUProbe& probe); + + const std::shared_ptr device_holder_; + + struct GPUTraceState { + size_t current_index = 0; + size_t pending_buffers = 0; + vk::UniqueQueryPool query_pool; + }; + + mutable Mutex trace_state_mutex_; + GPUTraceState trace_states_[kTraceStatesSize] IPLR_GUARDED_BY( + trace_state_mutex_); + size_t current_state_ IPLR_GUARDED_BY(trace_state_mutex_) = 0u; + + // The number of nanoseconds for each timestamp unit. + float timestamp_period_ = 1; + + // If in_frame_ is not true, then this cmd buffer was started as a part of + // some non-frame workload like image decoding. We should not record this as + // part of the frame workload, as the gap between this non-frame and a + // frameworkload may be substantial. For example, support the engine creates a + // cmd buffer to perform an image upload at timestamp 0 and then 30 ms later + // actually renders a frame. Without the in_frame_ guard, the GPU frame time + // would include this 30ms gap during which the engine was idle. + bool in_frame_ = false; + + // Track the raster thread id to avoid recording mipmap/image cmd buffers + // that are not guaranteed to start/end according to frame boundaries. + std::thread::id raster_thread_id_; + bool enabled_ = false; +}; + +class GPUProbe { + public: + explicit GPUProbe(const std::weak_ptr& tracer); + + GPUProbe(GPUProbe&&) = delete; + GPUProbe& operator=(GPUProbe&&) = delete; + + ~GPUProbe(); + + /// @brief Record a timestamp query into the provided cmd buffer to record + /// start time. + void RecordCmdBufferStart(const vk::CommandBuffer& buffer); + + /// @brief Record a timestamp query into the provided cmd buffer to record end + /// time. + void RecordCmdBufferEnd(const vk::CommandBuffer& buffer); + + private: + friend class GPUTracerVK; + + std::weak_ptr tracer_; + std::optional index_ = std::nullopt; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc b/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc index bab2a25db7f35..e76213c200493 100644 --- a/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc +++ b/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc @@ -24,7 +24,7 @@ int32_t CountStringViewInstances(const std::vector& strings, } // namespace TEST(PassBindingsCacheTest, bindPipeline) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); PassBindingsCache cache; auto encoder = std::make_unique(context)->Create(); auto buffer = encoder->GetCommandBuffer(); @@ -38,7 +38,7 @@ TEST(PassBindingsCacheTest, bindPipeline) { } TEST(PassBindingsCacheTest, setStencilReference) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); PassBindingsCache cache; auto encoder = std::make_unique(context)->Create(); auto buffer = encoder->GetCommandBuffer(); @@ -53,7 +53,7 @@ TEST(PassBindingsCacheTest, setStencilReference) { } TEST(PassBindingsCacheTest, setScissor) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); PassBindingsCache cache; auto encoder = std::make_unique(context)->Create(); auto buffer = encoder->GetCommandBuffer(); @@ -66,7 +66,7 @@ TEST(PassBindingsCacheTest, setScissor) { } TEST(PassBindingsCacheTest, setViewport) { - auto context = CreateMockVulkanContext(); + auto context = MockVulkanContextBuilder().Build(); PassBindingsCache cache; auto encoder = std::make_unique(context)->Create(); auto buffer = encoder->GetCommandBuffer(); diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index b47c9d57237ac..830d2d08120e1 100644 --- a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -75,8 +75,9 @@ static vk::AttachmentDescription CreatePlaceholderAttachmentDescription( /// spec: /// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap8.html#renderpass-compatibility /// -static vk::UniqueRenderPass CreateRenderPass(const vk::Device& device, - const PipelineDescriptor& desc) { +static vk::UniqueRenderPass CreateCompatRenderPassForPipeline( + const vk::Device& device, + const PipelineDescriptor& desc) { std::vector attachments; std::vector color_refs; @@ -128,6 +129,13 @@ static vk::UniqueRenderPass CreateRenderPass(const vk::Device& device, return {}; } + // This pass is not used with the render pass. It is only necessary to tell + // Vulkan the expected render pass layout. The actual pass will be created + // later during render pass setup and will need to be compatible with this + // one. + ContextVK::SetDebugName(device, pass.get(), + "Compat Render Pass: " + desc.GetLabel()); + return std::move(pass); } @@ -332,7 +340,8 @@ std::unique_ptr PipelineLibraryVK::CreatePipeline( return nullptr; } - auto render_pass = CreateRenderPass(strong_device->GetDevice(), desc); + auto render_pass = + CreateCompatRenderPassForPipeline(strong_device->GetDevice(), desc); if (render_pass) { pipeline_info.setBasePipelineHandle(VK_NULL_HANDLE); pipeline_info.setSubpass(0); diff --git a/impeller/renderer/backend/vulkan/resource_manager_vk.cc b/impeller/renderer/backend/vulkan/resource_manager_vk.cc index 382f7aa715e7c..b719737337f8e 100644 --- a/impeller/renderer/backend/vulkan/resource_manager_vk.cc +++ b/impeller/renderer/backend/vulkan/resource_manager_vk.cc @@ -4,6 +4,7 @@ #include "impeller/renderer/backend/vulkan/resource_manager_vk.h" +#include "flutter/fml/cpu_affinity.h" #include "flutter/fml/thread.h" #include "flutter/fml/trace_event.h" #include "fml/logging.h" @@ -22,6 +23,11 @@ std::shared_ptr ResourceManagerVK::Create() { ResourceManagerVK::ResourceManagerVK() : waiter_([&]() { Start(); }) {} ResourceManagerVK::~ResourceManagerVK() { + FML_DCHECK(waiter_.get_id() != std::this_thread::get_id()) + << "The ResourceManager being destructed on its own spawned thread is a " + << "sign that ContextVK was not properly destroyed. A usual fix for this " + << "is to ensure that ContextVK is shutdown (i.e. context->Shutdown()) " + "before the ResourceManager is destroyed (i.e. at the end of a test)."; Terminate(); waiter_.join(); } @@ -34,6 +40,9 @@ void ResourceManagerVK::Start() { fml::Thread::SetCurrentThreadName( fml::Thread::ThreadConfig{"io.flutter.impeller.resource_manager"}); + // While this code calls destructors it doesn't need to be particularly fast + // with them, as long as it doesn't interrupt raster thread. + fml::RequestAffinity(fml::CpuAffinity::kEfficiency); bool should_exit = false; while (!should_exit) { diff --git a/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc b/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc index 30df654d3dac0..fd5f10d8e0f70 100644 --- a/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc @@ -6,7 +6,6 @@ #include #include #include -#include "fml/logging.h" #include "fml/synchronization/waitable_event.h" #include "gtest/gtest.h" #include "impeller/renderer/backend/vulkan/resource_manager_vk.h" @@ -75,5 +74,34 @@ TEST(ResourceManagerVKTest, TerminatesWhenOutOfScope) { EXPECT_EQ(manager.lock(), nullptr); } +TEST(ResourceManagerVKTest, IsThreadSafe) { + // In a typical app, there is a single ResourceManagerVK per app, shared b/w + // threads. + // + // This test ensures that the ResourceManagerVK is thread-safe. + std::weak_ptr manager; + + { + auto const manager = ResourceManagerVK::Create(); + + // Spawn two threads, and have them both put resources into the manager. + struct MockResource {}; + + std::thread thread1([&manager]() { + UniqueResourceVKT(manager, MockResource{}); + }); + + std::thread thread2([&manager]() { + UniqueResourceVKT(manager, MockResource{}); + }); + + thread1.join(); + thread2.join(); + } + + // The thread should have terminated. + EXPECT_EQ(manager.lock(), nullptr); +} + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.cc b/impeller/renderer/backend/vulkan/surface_context_vk.cc index a1cddaef6e924..afc1407f6f3f5 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/vulkan/surface_context_vk.h" #include "flutter/fml/trace_event.h" +#include "impeller/renderer/backend/vulkan/command_pool_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/swapchain_vk.h" @@ -62,6 +63,10 @@ bool SurfaceContextVK::SetWindowSurface(vk::UniqueSurfaceKHR surface) { VALIDATION_LOG << "Could not create swapchain."; return false; } + if (!swapchain->IsValid()) { + VALIDATION_LOG << "Could not create valid swapchain."; + return false; + } swapchain_ = std::move(swapchain); return true; } @@ -79,6 +84,7 @@ std::unique_ptr SurfaceContextVK::AcquireNextSurface() { if (auto allocator = parent_->GetResourceAllocator()) { allocator->DidAcquireSurfaceFrame(); } + parent_->GetCommandPoolRecycler()->Dispose(); return surface; } diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc index d9146d9056291..eb089e44ec45d 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc @@ -4,12 +4,15 @@ #include "impeller/renderer/backend/vulkan/swapchain_impl_vk.h" +#include "impeller/base/validation.h" #include "impeller/renderer/backend/vulkan/command_buffer_vk.h" #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h" #include "impeller/renderer/backend/vulkan/surface_vk.h" #include "impeller/renderer/backend/vulkan/swapchain_image_vk.h" +#include "impeller/renderer/context.h" #include "vulkan/vulkan_structs.hpp" namespace impeller { @@ -142,6 +145,7 @@ SwapchainImplVK::SwapchainImplVK( vk::SwapchainKHR old_swapchain, vk::SurfaceTransformFlagBitsKHR last_transform) { if (!context) { + VALIDATION_LOG << "Cannot create a swapchain without a context."; return; } @@ -396,6 +400,9 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() { return {}; } + /// Record all subsequent cmd buffers as part of the current frame. + context.GetGPUTracer()->MarkFrameStart(); + auto image = images_[index % images_.size()]; uint32_t image_index = index; return AcquireResult{SurfaceVK::WrapSwapchainImage( @@ -470,7 +477,9 @@ bool SwapchainImplVK::Present(const std::shared_ptr& image, } } - auto task = [&, index, image, current_frame = current_frame_] { + context.GetGPUTracer()->MarkFrameEnd(); + + auto task = [&, index, current_frame = current_frame_] { auto context_strong = context_.lock(); if (!context_strong) { return; diff --git a/impeller/renderer/backend/vulkan/swapchain_vk.cc b/impeller/renderer/backend/vulkan/swapchain_vk.cc index 0a09a9eb371e0..c5399ce4835e7 100644 --- a/impeller/renderer/backend/vulkan/swapchain_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_vk.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/vulkan/swapchain_vk.h" #include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" #include "impeller/renderer/backend/vulkan/swapchain_impl_vk.h" namespace impeller { @@ -14,6 +15,7 @@ std::shared_ptr SwapchainVK::Create( vk::UniqueSurfaceKHR surface) { auto impl = SwapchainImplVK::Create(context, std::move(surface)); if (!impl || !impl->IsValid()) { + VALIDATION_LOG << "Failed to create SwapchainVK implementation."; return nullptr; } return std::shared_ptr(new SwapchainVK(std::move(impl))); diff --git a/impeller/renderer/backend/vulkan/test/gpu_tracer_unittests.cc b/impeller/renderer/backend/vulkan/test/gpu_tracer_unittests.cc new file mode 100644 index 0000000000000..8afa8832f72c9 --- /dev/null +++ b/impeller/renderer/backend/vulkan/test/gpu_tracer_unittests.cc @@ -0,0 +1,108 @@ +// 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 +#include "flutter/testing/testing.h" // IWYU pragma: keep +#include "fml/synchronization/count_down_latch.h" +#include "gtest/gtest.h" +#include "impeller/renderer//backend/vulkan/command_encoder_vk.h" +#include "impeller/renderer/backend/vulkan/command_buffer_vk.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h" +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" + +namespace impeller { +namespace testing { + +#ifdef IMPELLER_DEBUG +TEST(GPUTracerVK, CanTraceCmdBuffer) { + auto const context = MockVulkanContextBuilder().Build(); + auto tracer = context->GetGPUTracer(); + + ASSERT_TRUE(tracer->IsEnabled()); + tracer->MarkFrameStart(); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + blit_pass->EncodeCommands(context->GetResourceAllocator()); + + auto latch = std::make_shared(1u); + + if (!cmd_buffer->SubmitCommands( + [latch](CommandBuffer::Status status) { latch->CountDown(); })) { + GTEST_FAIL() << "Failed to submit cmd buffer"; + } + + tracer->MarkFrameEnd(); + latch->Wait(); + + auto called = GetMockVulkanFunctions(context->GetDevice()); + ASSERT_NE(called, nullptr); + ASSERT_TRUE(std::find(called->begin(), called->end(), "vkCreateQueryPool") != + called->end()); + ASSERT_TRUE(std::find(called->begin(), called->end(), + "vkGetQueryPoolResults") != called->end()); +} + +TEST(GPUTracerVK, DoesNotTraceOutsideOfFrameWorkload) { + auto const context = MockVulkanContextBuilder().Build(); + auto tracer = context->GetGPUTracer(); + + ASSERT_TRUE(tracer->IsEnabled()); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + blit_pass->EncodeCommands(context->GetResourceAllocator()); + + auto latch = std::make_shared(1u); + if (!cmd_buffer->SubmitCommands( + [latch](CommandBuffer::Status status) { latch->CountDown(); })) { + GTEST_FAIL() << "Failed to submit cmd buffer"; + } + + latch->Wait(); + + auto called = GetMockVulkanFunctions(context->GetDevice()); + + ASSERT_NE(called, nullptr); + ASSERT_TRUE(std::find(called->begin(), called->end(), "vkCreateQueryPool") == + called->end()); + ASSERT_TRUE(std::find(called->begin(), called->end(), + "vkGetQueryPoolResults") == called->end()); +} + +// This cmd buffer starts when there is a frame but finishes when there is none. +// This should result in the same recorded work. +TEST(GPUTracerVK, TracesWithPartialFrameOverlap) { + auto const context = MockVulkanContextBuilder().Build(); + auto tracer = context->GetGPUTracer(); + + ASSERT_TRUE(tracer->IsEnabled()); + tracer->MarkFrameStart(); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + blit_pass->EncodeCommands(context->GetResourceAllocator()); + tracer->MarkFrameEnd(); + + auto latch = std::make_shared(1u); + if (!cmd_buffer->SubmitCommands( + [latch](CommandBuffer::Status status) { latch->CountDown(); })) { + GTEST_FAIL() << "Failed to submit cmd buffer"; + } + + latch->Wait(); + + auto called = GetMockVulkanFunctions(context->GetDevice()); + ASSERT_NE(called, nullptr); + ASSERT_TRUE(std::find(called->begin(), called->end(), "vkCreateQueryPool") != + called->end()); + ASSERT_TRUE(std::find(called->begin(), called->end(), + "vkGetQueryPoolResults") != called->end()); +} + +#endif // IMPELLER_DEBUG + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc index aafb3cf410153..bee4d53e67360 100644 --- a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -3,9 +3,15 @@ // found in the LICENSE file. #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" +#include +#include #include #include "fml/macros.h" +#include "fml/thread_local.h" #include "impeller/base/thread_safety.h" +#include "impeller/renderer/backend/vulkan/vk.h" // IWYU pragma: keep. +#include "third_party/swiftshader/include/vulkan/vulkan_core.h" +#include "vulkan/vulkan_core.h" namespace impeller { namespace testing { @@ -19,6 +25,10 @@ struct MockCommandBuffer { std::shared_ptr> called_functions_; }; +struct MockQueryPool {}; + +struct MockCommandPool {}; + class MockDevice final { public: explicit MockDevice() : called_functions_(new std::vector()) {} @@ -31,6 +41,25 @@ class MockDevice final { return result; } + MockCommandPool* NewCommandPool() { + auto pool = std::make_unique(); + MockCommandPool* result = pool.get(); + Lock lock(commmand_pools_mutex_); + command_pools_.emplace_back(std::move(pool)); + return result; + } + + void DeleteCommandPool(MockCommandPool* pool) { + Lock lock(commmand_pools_mutex_); + auto it = std::find_if(command_pools_.begin(), command_pools_.end(), + [pool](const std::unique_ptr& p) { + return p.get() == pool; + }); + if (it != command_pools_.end()) { + command_pools_.erase(it); + } + } + const std::shared_ptr>& GetCalledFunctions() { return called_functions_; } @@ -50,29 +79,49 @@ class MockDevice final { Mutex command_buffers_mutex_; std::vector> command_buffers_ IPLR_GUARDED_BY(command_buffers_mutex_); + + Mutex commmand_pools_mutex_; + std::vector> command_pools_ + IPLR_GUARDED_BY(commmand_pools_mutex_); }; void noop() {} +FML_THREAD_LOCAL std::vector g_instance_extensions; + VkResult vkEnumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) { if (!pProperties) { - *pPropertyCount = 2; - + *pPropertyCount = g_instance_extensions.size(); } else { - strcpy(pProperties[0].extensionName, "VK_KHR_surface"); - pProperties[0].specVersion = 0; - strcpy(pProperties[1].extensionName, "VK_MVK_macos_surface"); - pProperties[1].specVersion = 0; + uint32_t count = 0; + for (const std::string& ext : g_instance_extensions) { + strncpy(pProperties[count].extensionName, ext.c_str(), + sizeof(VkExtensionProperties::extensionName)); + pProperties[count].specVersion = 0; + count++; + } } return VK_SUCCESS; } +FML_THREAD_LOCAL std::vector g_instance_layers; + VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) { - *pPropertyCount = 0; + if (!pProperties) { + *pPropertyCount = g_instance_layers.size(); + } else { + uint32_t count = 0; + for (const std::string& layer : g_instance_layers) { + strncpy(pProperties[count].layerName, layer.c_str(), + sizeof(VkLayerProperties::layerName)); + pProperties[count].specVersion = 0; + count++; + } + } return VK_SUCCESS; } @@ -108,6 +157,7 @@ void vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, static_cast(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); pProperties->limits.maxImageDimension2D = 4096; + pProperties->limits.timestampPeriod = 1; } void vkGetPhysicalDeviceQueueFamilyProperties( @@ -179,7 +229,16 @@ VkResult vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) { - *pCommandPool = reinterpret_cast(0xc0de0001); + MockDevice* mock_device = reinterpret_cast(device); + mock_device->AddCalledFunction("vkCreateCommandPool"); + *pCommandPool = + reinterpret_cast(mock_device->NewCommandPool()); + return VK_SUCCESS; +} + +VkResult vkResetCommandPool(VkDevice device, + VkCommandPool commandPool, + VkCommandPoolResetFlags flags) { return VK_SUCCESS; } @@ -381,6 +440,8 @@ void vkDestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator) { MockDevice* mock_device = reinterpret_cast(device); + mock_device->DeleteCommandPool( + reinterpret_cast(commandPool)); mock_device->AddCalledFunction("vkDestroyCommandPool"); } @@ -392,7 +453,15 @@ VkResult vkCreateFence(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence) { - *pFence = reinterpret_cast(0xfe0ce); + MockDevice* mock_device = reinterpret_cast(device); + *pFence = reinterpret_cast(new MockFence()); + return VK_SUCCESS; +} + +VkResult vkDestroyFence(VkDevice device, + VkFence fence, + const VkAllocationCallbacks* pAllocator) { + delete reinterpret_cast(fence); return VK_SUCCESS; } @@ -412,6 +481,56 @@ VkResult vkWaitForFences(VkDevice device, } VkResult vkGetFenceStatus(VkDevice device, VkFence fence) { + MockDevice* mock_device = reinterpret_cast(device); + MockFence* mock_fence = reinterpret_cast(fence); + return mock_fence->GetStatus(); +} + +VkResult vkCreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pMessenger) { + return VK_SUCCESS; +} + +VkResult vkSetDebugUtilsObjectNameEXT( + VkDevice device, + const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { + return VK_SUCCESS; +} + +VkResult vkCreateQueryPool(VkDevice device, + const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkQueryPool* pQueryPool) { + *pQueryPool = reinterpret_cast(new MockQueryPool()); + MockDevice* mock_device = reinterpret_cast(device); + mock_device->AddCalledFunction("vkCreateQueryPool"); + return VK_SUCCESS; +} + +VkResult vkGetQueryPoolResults(VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + size_t dataSize, + void* pData, + VkDeviceSize stride, + VkQueryResultFlags flags) { + MockDevice* mock_device = reinterpret_cast(device); + if (dataSize == sizeof(uint32_t)) { + uint32_t* data = static_cast(pData); + for (auto i = firstQuery; i < queryCount; i++) { + data[0] = i; + } + } else if (dataSize == sizeof(int64_t)) { + uint64_t* data = static_cast(pData); + for (auto i = firstQuery; i < queryCount; i++) { + data[0] = i; + } + } + mock_device->AddCalledFunction("vkGetQueryPoolResults"); return VK_SUCCESS; } @@ -441,6 +560,8 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, return (PFN_vkVoidFunction)vkCreatePipelineCache; } else if (strcmp("vkCreateCommandPool", pName) == 0) { return (PFN_vkVoidFunction)vkCreateCommandPool; + } else if (strcmp("vkResetCommandPool", pName) == 0) { + return (PFN_vkVoidFunction)vkResetCommandPool; } else if (strcmp("vkAllocateCommandBuffers", pName) == 0) { return (PFN_vkVoidFunction)vkAllocateCommandBuffers; } else if (strcmp("vkBeginCommandBuffer", pName) == 0) { @@ -501,27 +622,42 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, return (PFN_vkVoidFunction)vkEndCommandBuffer; } else if (strcmp("vkCreateFence", pName) == 0) { return (PFN_vkVoidFunction)vkCreateFence; + } else if (strcmp("vkDestroyFence", pName) == 0) { + return (PFN_vkVoidFunction)vkDestroyFence; } else if (strcmp("vkQueueSubmit", pName) == 0) { return (PFN_vkVoidFunction)vkQueueSubmit; } else if (strcmp("vkWaitForFences", pName) == 0) { return (PFN_vkVoidFunction)vkWaitForFences; } else if (strcmp("vkGetFenceStatus", pName) == 0) { return (PFN_vkVoidFunction)vkGetFenceStatus; + } else if (strcmp("vkCreateDebugUtilsMessengerEXT", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateDebugUtilsMessengerEXT; + } else if (strcmp("vkSetDebugUtilsObjectNameEXT", pName) == 0) { + return (PFN_vkVoidFunction)vkSetDebugUtilsObjectNameEXT; + } else if (strcmp("vkCreateQueryPool", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateQueryPool; + } else if (strcmp("vkGetQueryPoolResults", pName) == 0) { + return (PFN_vkVoidFunction)vkGetQueryPoolResults; } return noop; } } // namespace -std::shared_ptr CreateMockVulkanContext( - const std::function& settings_callback) { +MockVulkanContextBuilder::MockVulkanContextBuilder() + : instance_extensions_({"VK_KHR_surface", "VK_MVK_macos_surface"}) {} + +std::shared_ptr MockVulkanContextBuilder::Build() { auto message_loop = fml::ConcurrentMessageLoop::Create(); ContextVK::Settings settings; settings.proc_address_callback = GetMockVulkanProcAddress; - if (settings_callback) { - settings_callback(settings); + if (settings_callback_) { + settings_callback_(settings); } - return ContextVK::Create(std::move(settings)); + g_instance_extensions = instance_extensions_; + g_instance_layers = instance_layers_; + std::shared_ptr result = ContextVK::Create(std::move(settings)); + return result; } std::shared_ptr> GetMockVulkanFunctions( diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.h b/impeller/renderer/backend/vulkan/test/mock_vulkan.h index 2984db473ff09..8f41b9f86ee62 100644 --- a/impeller/renderer/backend/vulkan/test/mock_vulkan.h +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.h @@ -5,10 +5,13 @@ #pragma once #include +#include #include #include +#include "impeller/base/thread.h" #include "impeller/renderer/backend/vulkan/context_vk.h" +#include "vulkan/vulkan_enums.hpp" namespace impeller { namespace testing { @@ -16,18 +19,77 @@ namespace testing { std::shared_ptr> GetMockVulkanFunctions( VkDevice device); -//------------------------------------------------------------------------------ -/// @brief Create a Vulkan context with Vulkan functions mocked. The caller -/// is given a chance to tinker on the settings right before a -/// context is created. -/// -/// @param[in] settings_callback The settings callback -/// -/// @return A context if one can be created. -/// -std::shared_ptr CreateMockVulkanContext( - const std::function& settings_callback = - nullptr); +// A test-controlled version of |vk::Fence|. +class MockFence final { + public: + MockFence() = default; + + // Returns the result that was set in the constructor or |SetStatus|. + VkResult GetStatus() { return static_cast(result_.load()); } + + // Sets the result that will be returned by `GetFenceStatus`. + void SetStatus(vk::Result result) { result_ = result; } + + // Sets the result that will be returned by `GetFenceStatus`. + static void SetStatus(vk::UniqueFence& fence, vk::Result result) { + // Cast the fence to a MockFence and set the result. + VkFence raw_fence = fence.get(); + MockFence* mock_fence = reinterpret_cast(raw_fence); + mock_fence->SetStatus(result); + } + + // Gets a raw pointer to manipulate the fence after it's been moved. + static MockFence* GetRawPointer(vk::UniqueFence& fence) { + // Cast the fence to a MockFence and get the result. + VkFence raw_fence = fence.get(); + MockFence* mock_fence = reinterpret_cast(raw_fence); + return mock_fence; + } + + private: + std::atomic result_ = vk::Result::eSuccess; + + FML_DISALLOW_COPY_AND_ASSIGN(MockFence); +}; + +class MockVulkanContextBuilder { + public: + MockVulkanContextBuilder(); + + //------------------------------------------------------------------------------ + /// @brief Create a Vulkan context with Vulkan functions mocked. The + /// caller is given a chance to tinker on the settings right + /// before a context is created. + /// + /// @return A context if one can be created. + /// + std::shared_ptr Build(); + + /// A callback that allows the modification of the ContextVK::Settings before + /// the context is made. + MockVulkanContextBuilder& SetSettingsCallback( + const std::function& settings_callback) { + settings_callback_ = settings_callback; + return *this; + } + + MockVulkanContextBuilder& SetInstanceExtensions( + const std::vector& instance_extensions) { + instance_extensions_ = instance_extensions; + return *this; + } + + MockVulkanContextBuilder& SetInstanceLayers( + const std::vector& instance_layers) { + instance_layers_ = instance_layers; + return *this; + } + + private: + std::function settings_callback_; + std::vector instance_extensions_; + std::vector instance_layers_; +}; } // namespace testing } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan_unittests.cc b/impeller/renderer/backend/vulkan/test/mock_vulkan_unittests.cc index 05cc7682e004d..2555994a8fd03 100644 --- a/impeller/renderer/backend/vulkan/test/mock_vulkan_unittests.cc +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan_unittests.cc @@ -4,8 +4,8 @@ #include "flutter/testing/testing.h" // IWYU pragma: keep #include "gtest/gtest.h" -#include "impeller/renderer/backend/vulkan/command_pool_vk.h" #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" +#include "vulkan/vulkan_enums.hpp" namespace impeller { namespace testing { @@ -14,16 +14,16 @@ TEST(MockVulkanContextTest, IsThreadSafe) { // In a typical app, there is a single ContextVK per app, shared b/w threads. // // This test ensures that the (mock) ContextVK is thread-safe. - auto const context = CreateMockVulkanContext(); + auto const context = MockVulkanContextBuilder().Build(); // Spawn two threads, and have them create a CommandPoolVK each. std::thread thread1([&context]() { - auto const pool = CommandPoolVK::GetThreadLocal(context.get()); + auto const pool = context->GetCommandPoolRecycler()->Get(); EXPECT_TRUE(pool); }); std::thread thread2([&context]() { - auto const pool = CommandPoolVK::GetThreadLocal(context.get()); + auto const pool = context->GetCommandPoolRecycler()->Get(); EXPECT_TRUE(pool); }); @@ -33,5 +33,26 @@ TEST(MockVulkanContextTest, IsThreadSafe) { context->Shutdown(); } +TEST(MockVulkanContextTest, DefaultFenceAlwaysReportsSuccess) { + auto const context = MockVulkanContextBuilder().Build(); + auto const device = context->GetDevice(); + + auto fence = device.createFenceUnique({}).value; + EXPECT_EQ(vk::Result::eSuccess, device.getFenceStatus(*fence)); +} + +TEST(MockVulkanContextTest, MockedFenceReportsStatus) { + auto const context = MockVulkanContextBuilder().Build(); + + auto const device = context->GetDevice(); + auto fence = device.createFenceUnique({}).value; + MockFence::SetStatus(fence, vk::Result::eNotReady); + + EXPECT_EQ(vk::Result::eNotReady, device.getFenceStatus(fence.get())); + + MockFence::SetStatus(fence, vk::Result::eSuccess); + EXPECT_EQ(vk::Result::eSuccess, device.getFenceStatus(*fence)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/texture_vk.cc b/impeller/renderer/backend/vulkan/texture_vk.cc index 94843de103bdf..cbe525400c303 100644 --- a/impeller/renderer/backend/vulkan/texture_vk.cc +++ b/impeller/renderer/backend/vulkan/texture_vk.cc @@ -93,7 +93,8 @@ bool TextureVK::OnSetContents(const uint8_t* contents, copy.imageExtent.width = desc.size.width; copy.imageExtent.height = desc.size.height; copy.imageExtent.depth = 1u; - copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; + copy.imageSubresource.aspectMask = + ToImageAspectFlags(GetTextureDescriptor().format); copy.imageSubresource.mipLevel = 0u; copy.imageSubresource.baseArrayLayer = slice; copy.imageSubresource.layerCount = 1u; diff --git a/impeller/renderer/context.h b/impeller/renderer/context.h index 3cc847235cd7d..3d0cb9d97ad6e 100644 --- a/impeller/renderer/context.h +++ b/impeller/renderer/context.h @@ -52,6 +52,14 @@ class Context { kVulkan, }; + /// The maximum number of tasks that should ever be stored for + /// `StoreTaskForGPU`. + /// + /// This number was arbitrarily chosen. The idea is that this is a somewhat + /// rare situation where tasks happen to get executed in that tiny amount of + /// time while an app is being backgrounded but still executing. + static constexpr int32_t kMaxTasksAwaitingGPU = 10; + //---------------------------------------------------------------------------- /// @brief Destroys an Impeller context. /// @@ -176,6 +184,19 @@ class Context { CaptureContext capture; + /// Stores a task on the `ContextMTL` that is awaiting access for the GPU. + /// + /// The task will be executed in the event that the GPU access has changed to + /// being available or that the task has been canceled. The task should + /// operate with the `SyncSwitch` to make sure the GPU is accessible. + /// + /// Threadsafe. + /// + /// `task` will be executed on the platform thread. + virtual void StoreTaskForGPU(std::function task) { + FML_CHECK(false && "not supported in this context"); + } + protected: Context(); diff --git a/impeller/renderer/pool.h b/impeller/renderer/pool.h index f215ff2a6d975..a90357594196a 100644 --- a/impeller/renderer/pool.h +++ b/impeller/renderer/pool.h @@ -14,7 +14,7 @@ namespace impeller { template class Pool { public: - Pool(uint32_t limit_bytes) : limit_bytes_(limit_bytes), size_(0) {} + explicit Pool(uint32_t limit_bytes) : limit_bytes_(limit_bytes) {} std::shared_ptr Grab() { std::scoped_lock lock(mutex_); @@ -46,7 +46,7 @@ class Pool { private: std::vector> pool_; const uint32_t limit_bytes_; - uint32_t size_; + uint32_t size_ = 0; // Note: This would perform better as a lockless ring buffer. mutable std::mutex mutex_; }; diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 636601ddd3874..87d158e0447d3 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -231,11 +231,6 @@ RenderTarget RenderTarget::CreateOffscreen( return {}; } -// Dont force additional PSO variants on Vulkan. -#ifdef FML_OS_ANDROID - FML_DCHECK(stencil_attachment_config.has_value()); -#endif // FML_OS_ANDROID - RenderTarget target; PixelFormat pixel_format = context.GetCapabilities()->GetDefaultColorFormat(); TextureDescriptor color_tex0; @@ -278,11 +273,6 @@ RenderTarget RenderTarget::CreateOffscreenMSAA( return {}; } -// Dont force additional PSO variants on Vulkan. -#ifdef FML_OS_ANDROID - FML_DCHECK(stencil_attachment_config.has_value()); -#endif // FML_OS_ANDROID - RenderTarget target; PixelFormat pixel_format = context.GetCapabilities()->GetDefaultColorFormat(); diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 575178f229bf2..2ecc6fab2a004 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -58,6 +58,7 @@ TEST_P(RendererTest, CanCreateBoxPrimitive) { auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); // Vertex buffer. VertexBufferBuilder vertex_builder; @@ -130,6 +131,7 @@ TEST_P(RendererTest, CanRenderPerspectiveCube) { desc->SetCullMode(CullMode::kBackFace); desc->SetWindingOrder(WindingOrder::kCounterClockwise); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); auto pipeline = context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get(); ASSERT_TRUE(pipeline); @@ -219,6 +221,7 @@ TEST_P(RendererTest, CanRenderMultiplePrimitives) { auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); auto box_pipeline = context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get(); ASSERT_TRUE(box_pipeline); @@ -401,14 +404,14 @@ TEST_P(RendererTest, CanRenderInstanced) { .AddRect(Rect::MakeXYWH(10, 10, 100, 100)) .TakePath() .CreatePolyline(1.0f), - [&builder](const float* vertices, size_t vertices_size, - const uint16_t* indices, size_t indices_size) { - for (auto i = 0u; i < vertices_size; i += 2) { + [&builder](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { + for (auto i = 0u; i < vertices_count * 2; i += 2) { VS::PerVertexData data; data.vtx = {vertices[i], vertices[i + 1]}; builder.AppendVertex(data); } - for (auto i = 0u; i < indices_size; i++) { + for (auto i = 0u; i < indices_count; i++) { builder.AppendIndex(indices[i]); } return true; @@ -420,7 +423,9 @@ TEST_P(RendererTest, CanRenderInstanced) { ->GetPipelineLibrary() ->GetPipeline(PipelineBuilder::MakeDefaultPipelineDescriptor( *GetContext()) - ->SetSampleCount(SampleCount::kCount4)) + ->SetSampleCount(SampleCount::kCount4) + .SetStencilAttachmentDescriptors(std::nullopt)) + .Get(); ASSERT_TRUE(pipeline && pipeline->IsValid()); @@ -459,6 +464,7 @@ TEST_P(RendererTest, CanBlitTextureToTexture) { auto desc = PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); auto mipmaps_pipeline = context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get(); ASSERT_TRUE(mipmaps_pipeline); @@ -571,6 +577,7 @@ TEST_P(RendererTest, CanBlitTextureToBuffer) { auto desc = PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); auto mipmaps_pipeline = context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get(); ASSERT_TRUE(mipmaps_pipeline); @@ -702,6 +709,7 @@ TEST_P(RendererTest, CanGenerateMipmaps) { auto desc = PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); auto mipmaps_pipeline = context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get(); ASSERT_TRUE(mipmaps_pipeline); @@ -818,6 +826,7 @@ TEST_P(RendererTest, TheImpeller) { PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(pipeline_descriptor.has_value()); pipeline_descriptor->SetSampleCount(SampleCount::kCount4); + pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt); auto pipeline = context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get(); ASSERT_TRUE(pipeline && pipeline->IsValid()); @@ -878,6 +887,7 @@ TEST_P(RendererTest, ArrayUniforms) { PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(pipeline_descriptor.has_value()); pipeline_descriptor->SetSampleCount(SampleCount::kCount4); + pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt); auto pipeline = context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get(); ASSERT_TRUE(pipeline && pipeline->IsValid()); @@ -934,6 +944,7 @@ TEST_P(RendererTest, InactiveUniforms) { PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(pipeline_descriptor.has_value()); pipeline_descriptor->SetSampleCount(SampleCount::kCount4); + pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt); auto pipeline = context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get(); ASSERT_TRUE(pipeline && pipeline->IsValid()); @@ -1120,6 +1131,7 @@ TEST_P(RendererTest, StencilMask) { ASSERT_TRUE(vertex_buffer); desc->SetSampleCount(SampleCount::kCount4); + desc->SetStencilAttachmentDescriptors(std::nullopt); auto bridge = CreateTextureForFixture("bay_bridge.jpg"); auto boston = CreateTextureForFixture("boston.jpg"); diff --git a/impeller/scene/importer/importer_unittests.cc b/impeller/scene/importer/importer_unittests.cc index d172b7418deb4..c4aa6b27ec872 100644 --- a/impeller/scene/importer/importer_unittests.cc +++ b/impeller/scene/importer/importer_unittests.cc @@ -102,10 +102,10 @@ TEST(ImporterTest, CanParseSkinnedGLTF) { ASSERT_COLOR_NEAR(color, Color(1, 1, 1, 1)); Vector4 joints = ToVector4(vertex.joints()); - ASSERT_COLOR_NEAR(joints, Vector4(0, 0, 0, 0)); + ASSERT_VECTOR4_NEAR(joints, Vector4(0, 0, 0, 0)); Vector4 weights = ToVector4(vertex.weights()); - ASSERT_COLOR_NEAR(weights, Vector4(1, 0, 0, 0)); + ASSERT_VECTOR4_NEAR(weights, Vector4(1, 0, 0, 0)); ASSERT_EQ(scene.animations.size(), 2u); ASSERT_EQ(scene.animations[0]->name, "Idle"); diff --git a/impeller/tessellator/c/tessellator.cc b/impeller/tessellator/c/tessellator.cc index 1bdbfde66db84..b576d4393e809 100644 --- a/impeller/tessellator/c/tessellator.cc +++ b/impeller/tessellator/c/tessellator.cc @@ -45,14 +45,14 @@ struct Vertices* Tessellate(PathBuilder* builder, std::vector points; if (Tessellator{}.Tessellate( path.GetFillType(), polyline, - [&points](const float* vertices, size_t vertices_size, - const uint16_t* indices, size_t indices_size) { + [&points](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { // Results are expected to be re-duplicated. std::vector raw_points; - for (auto i = 0u; i < vertices_size; i += 2) { + for (auto i = 0u; i < vertices_count * 2; i += 2) { raw_points.emplace_back(Point{vertices[i], vertices[i + 1]}); } - for (auto i = 0u; i < indices_size; i++) { + for (auto i = 0u; i < indices_count; i++) { auto point = raw_points[indices[i]]; points.push_back(point.x); points.push_back(point.y); diff --git a/impeller/tessellator/dart/pubspec.yaml b/impeller/tessellator/dart/pubspec.yaml index fc17320855790..12ec6ec21b003 100644 --- a/impeller/tessellator/dart/pubspec.yaml +++ b/impeller/tessellator/dart/pubspec.yaml @@ -8,4 +8,4 @@ publish_to: none homepage: https://github.com/flutter/impeller/tree/main/tessellator/dart environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 4e2c3c535f41e..f7c80fe747ea0 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -21,7 +21,7 @@ static void HeapFree(void* userData, void* ptr) { } // Note: these units are "number of entities" for bucket size and not in KB. -static TESSalloc alloc = { +static const TESSalloc kAlloc = { HeapAlloc, HeapRealloc, HeapFree, 0, /* =userData */ 16, /* =meshEdgeBucketSize */ 16, /* =meshVertexBucketSize */ @@ -31,8 +31,14 @@ static TESSalloc alloc = { 0 /* =extraVertices */ }; -Tessellator::Tessellator() - : c_tessellator_(::tessNewTess(&alloc), &DestroyTessellator) {} +Tessellator::Tessellator() : c_tessellator_(nullptr, &DestroyTessellator) { + TESSalloc alloc = kAlloc; + { + // libTess2 copies the TESSalloc despite the non-const argument. + CTessellator tessellator(::tessNewTess(&alloc), &DestroyTessellator); + c_tessellator_ = std::move(tessellator); + } +} Tessellator::~Tessellator() = default; @@ -72,51 +78,141 @@ Tessellator::Result Tessellator::Tessellate( constexpr int kVertexSize = 2; constexpr int kPolygonSize = 3; - //---------------------------------------------------------------------------- - /// Feed contour information to the tessellator. - /// - static_assert(sizeof(Point) == 2 * sizeof(float)); - for (size_t contour_i = 0; contour_i < polyline.contours.size(); - contour_i++) { - size_t start_point_index, end_point_index; - std::tie(start_point_index, end_point_index) = - polyline.GetContourPointBounds(contour_i); - - ::tessAddContour(tessellator, // the C tessellator - kVertexSize, // - polyline.points.data() + start_point_index, // - sizeof(Point), // - end_point_index - start_point_index // + // If we have a larger polyline and the fill type is non-zero, we can split + // the tessellation up per contour. Since in general the complexity is at + // least nlog(n), this speeds up the processes substantially. + if (polyline.contours.size() > kMultiContourThreshold && + fill_type == FillType::kNonZero) { + std::vector points; + std::vector data; + + //---------------------------------------------------------------------------- + /// Feed contour information to the tessellator. + /// + size_t total = 0u; + static_assert(sizeof(Point) == 2 * sizeof(float)); + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t start_point_index, end_point_index; + std::tie(start_point_index, end_point_index) = + polyline.GetContourPointBounds(contour_i); + + ::tessAddContour(tessellator, // the C tessellator + kVertexSize, // + polyline.points.data() + start_point_index, // + sizeof(Point), // + end_point_index - start_point_index // + ); + + //---------------------------------------------------------------------------- + /// Let's tessellate. + /// + auto result = ::tessTesselate(tessellator, // tessellator + ToTessWindingRule(fill_type), // winding + TESS_POLYGONS, // element type + kPolygonSize, // polygon size + kVertexSize, // vertex size + nullptr // normal (null is automatic) + ); + + if (result != 1) { + return Result::kTessellationError; + } + + int vertex_item_count = tessGetVertexCount(tessellator) * kVertexSize; + auto vertices = tessGetVertices(tessellator); + for (int i = 0; i < vertex_item_count; i += 2) { + points.emplace_back(vertices[i], vertices[i + 1]); + } + + int element_item_count = tessGetElementCount(tessellator) * kPolygonSize; + auto elements = tessGetElements(tessellator); + total += element_item_count; + for (int i = 0; i < element_item_count; i++) { + data.emplace_back(points[elements[i]].x); + data.emplace_back(points[elements[i]].y); + } + points.clear(); + } + if (!callback(data.data(), total, nullptr, 0u)) { + return Result::kInputError; + } + } else { + //---------------------------------------------------------------------------- + /// Feed contour information to the tessellator. + /// + static_assert(sizeof(Point) == 2 * sizeof(float)); + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t start_point_index, end_point_index; + std::tie(start_point_index, end_point_index) = + polyline.GetContourPointBounds(contour_i); + + ::tessAddContour(tessellator, // the C tessellator + kVertexSize, // + polyline.points.data() + start_point_index, // + sizeof(Point), // + end_point_index - start_point_index // + ); + } + + //---------------------------------------------------------------------------- + /// Let's tessellate. + /// + auto result = ::tessTesselate(tessellator, // tessellator + ToTessWindingRule(fill_type), // winding + TESS_POLYGONS, // element type + kPolygonSize, // polygon size + kVertexSize, // vertex size + nullptr // normal (null is automatic) ); - } - - //---------------------------------------------------------------------------- - /// Let's tessellate. - /// - auto result = ::tessTesselate(tessellator, // tessellator - ToTessWindingRule(fill_type), // winding - TESS_POLYGONS, // element type - kPolygonSize, // polygon size - kVertexSize, // vertex size - nullptr // normal (null is automatic) - ); - - if (result != 1) { - return Result::kTessellationError; - } - int vertexItemCount = tessGetVertexCount(tessellator) * kVertexSize; - auto vertices = tessGetVertices(tessellator); - int elementItemCount = tessGetElementCount(tessellator) * kPolygonSize; - auto elements = tessGetElements(tessellator); - // libtess uses an int index internally due to usage of -1 as a sentinel - // value. - std::vector indices(elementItemCount); - for (int i = 0; i < elementItemCount; i++) { - indices[i] = static_cast(elements[i]); - } - if (!callback(vertices, vertexItemCount, indices.data(), elementItemCount)) { - return Result::kInputError; + if (result != 1) { + return Result::kTessellationError; + } + + int element_item_count = tessGetElementCount(tessellator) * kPolygonSize; + + // We default to using a 16bit index buffer, but in cases where we generate + // more tessellated data than this can contain we need to fall back to + // dropping the index buffer entirely. Instead code could instead switch to + // a uint32 index buffer, but this is done for simplicity with the other + // fast path above. + if (element_item_count < USHRT_MAX) { + int vertex_item_count = tessGetVertexCount(tessellator); + auto vertices = tessGetVertices(tessellator); + auto elements = tessGetElements(tessellator); + + // libtess uses an int index internally due to usage of -1 as a sentinel + // value. + std::vector indices(element_item_count); + for (int i = 0; i < element_item_count; i++) { + indices[i] = static_cast(elements[i]); + } + if (!callback(vertices, vertex_item_count, indices.data(), + element_item_count)) { + return Result::kInputError; + } + } else { + std::vector points; + std::vector data; + + int vertex_item_count = tessGetVertexCount(tessellator) * kVertexSize; + auto vertices = tessGetVertices(tessellator); + for (int i = 0; i < vertex_item_count; i += 2) { + points.emplace_back(vertices[i], vertices[i + 1]); + } + + int element_item_count = tessGetElementCount(tessellator) * kPolygonSize; + auto elements = tessGetElements(tessellator); + for (int i = 0; i < element_item_count; i++) { + data.emplace_back(points[elements[i]].x); + data.emplace_back(points[elements[i]].y); + } + if (!callback(data.data(), element_item_count, nullptr, 0u)) { + return Result::kInputError; + } + } } return Result::kSuccess; diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index ff93b7091a3f6..b6d444c5a81ba 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -44,10 +44,18 @@ class Tessellator { ~Tessellator(); + /// @brief An arbitrary value to determine when a multi-contour non-zero fill + /// path should be split into multiple tessellations. + static constexpr size_t kMultiContourThreshold = 30u; + + /// @brief A callback that returns the results of the tessellation. + /// + /// The index buffer may not be populated, in which case [indices] will + /// be nullptr and indices_count will be 0. using BuilderCallback = std::function; + size_t indices_count)>; //---------------------------------------------------------------------------- /// @brief Generates filled triangles from the polyline. A callback is diff --git a/impeller/tessellator/tessellator_unittests.cc b/impeller/tessellator/tessellator_unittests.cc index 0b943d1391254..8fca675a9a9a6 100644 --- a/impeller/tessellator/tessellator_unittests.cc +++ b/impeller/tessellator/tessellator_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/testing/testing.h" #include "gtest/gtest.h" +#include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" #include "impeller/tessellator/tessellator.h" @@ -17,8 +18,8 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { auto polyline = PathBuilder{}.TakePath().CreatePolyline(1.0f); Tessellator::Result result = t.Tessellate( FillType::kPositive, polyline, - [](const float* vertices, size_t vertices_size, const uint16_t* indices, - size_t indices_size) { return true; }); + [](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { return true; }); ASSERT_EQ(polyline.points.size(), 0u); ASSERT_EQ(result, Tessellator::Result::kInputError); @@ -29,10 +30,11 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { Tessellator t; auto polyline = PathBuilder{}.LineTo({0, 0}).TakePath().CreatePolyline(1.0f); - Tessellator::Result result = t.Tessellate( - FillType::kPositive, polyline, - [](const float* vertices, size_t vertices_size, const uint16_t* indices, - size_t indices_size) { return true; }); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_count, + const uint16_t* indices_count, + size_t indices_size) { return true; }); ASSERT_EQ(polyline.points.size(), 1u); ASSERT_EQ(result, Tessellator::Result::kSuccess); } @@ -42,10 +44,11 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { Tessellator t; auto polyline = PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath().CreatePolyline(1.0f); - Tessellator::Result result = t.Tessellate( - FillType::kPositive, polyline, - [](const float* vertices, size_t vertices_size, const uint16_t* indices, - size_t indices_size) { return true; }); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_count, + const uint16_t* indices_count, + size_t indices_size) { return true; }); ASSERT_EQ(polyline.points.size(), 2u); ASSERT_EQ(result, Tessellator::Result::kSuccess); @@ -60,10 +63,11 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { builder.AddLine({coord, coord}, {coord + 1, coord + 1}); } auto polyline = builder.TakePath().CreatePolyline(1.0f); - Tessellator::Result result = t.Tessellate( - FillType::kPositive, polyline, - [](const float* vertices, size_t vertices_size, const uint16_t* indices, - size_t indices_size) { return true; }); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_count, + const uint16_t* indices_count, + size_t indices_size) { return true; }); ASSERT_EQ(polyline.points.size(), 2000u); ASSERT_EQ(result, Tessellator::Result::kSuccess); @@ -74,14 +78,60 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { Tessellator t; auto polyline = PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath().CreatePolyline(1.0f); - Tessellator::Result result = t.Tessellate( - FillType::kPositive, polyline, - [](const float* vertices, size_t vertices_size, const uint16_t* indices, - size_t indices_size) { return false; }); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_count, + const uint16_t* indices_count, + size_t indices_size) { return false; }); ASSERT_EQ(polyline.points.size(), 2u); ASSERT_EQ(result, Tessellator::Result::kInputError); } + + // More than 30 contours, non-zero fill mode. + { + Tessellator t; + PathBuilder builder = {}; + for (auto i = 0u; i < Tessellator::kMultiContourThreshold + 1; i++) { + builder.AddCircle(Point(i, i), 4); + } + auto polyline = builder.TakePath().CreatePolyline(1.0f); + bool no_indices = false; + Tessellator::Result result = t.Tessellate( + FillType::kNonZero, polyline, + [&no_indices](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { + no_indices = indices == nullptr; + return true; + }); + + ASSERT_TRUE(no_indices); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } + + // More than uint16 points, odd fill mode. + { + Tessellator t; + PathBuilder builder = {}; + for (auto i = 0; i < 1000; i++) { + builder.AddCircle(Point(i, i), 4); + } + auto polyline = builder.TakePath(FillType::kOdd).CreatePolyline(1.0f); + bool no_indices = false; + size_t count = 0u; + Tessellator::Result result = t.Tessellate( + FillType::kOdd, polyline, + [&no_indices, &count](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { + no_indices = indices == nullptr; + count = vertices_count; + return true; + }); + + ASSERT_TRUE(no_indices); + ASSERT_TRUE(count >= USHRT_MAX); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } } } // namespace testing diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index f2d33b0c28e4a..15caa5631c472 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -22,7 +22,14 @@ declare_args() { (is_linux || is_win || is_android) && target_os != "fuchsia" # Whether the Vulkan backend is enabled. - impeller_enable_vulkan = + impeller_enable_vulkan = (is_linux || is_win || is_android || + enable_unittests) && target_os != "fuchsia" + + # Whether playgrounds should run with Vulkan. + # + # impeller_enable_vulkan may be true in build environments that run tests but + # do not have a Vulkan ICD present. + impeller_enable_vulkan_playgrounds = (is_linux || is_win || is_android) && target_os != "fuchsia" # Whether to use a prebuilt impellerc. @@ -39,11 +46,11 @@ declare_args() { # overhead may be substantial, this is not enabled by default. impeller_trace_all_gl_calls = false - # Call glGetError after each OpenGL call and log failures. - impeller_error_check_all_gl_calls = is_debug - # Enable experimental 3D scene rendering. impeller_enable_3d = false + + # Enable to get trace statements for canvas usage. + impeller_trace_canvas = false } declare_args() { @@ -321,6 +328,10 @@ template("impellerc") { if (defined(invoker.use_half_textures) && invoker.use_half_textures) { args += [ "--use-half-textures" ] } + if (defined(invoker.require_framebuffer_fetch) && + invoker.require_framebuffer_fetch) { + args += [ "--require-framebuffer-fetch" ] + } if (json) { args += [ "--json" ] @@ -506,6 +517,12 @@ template("_impeller_shaders_gles") { assert(defined(invoker.name), "Name of the shader library must be specified.") assert(defined(invoker.analyze), "Whether to analyze must be specified.") + require_framebuffer_fetch = false + if (defined(invoker.require_framebuffer_fetch) && + invoker.require_framebuffer_fetch) { + require_framebuffer_fetch = invoker.require_framebuffer_fetch + } + shaders_base_name = string_join("", [ invoker.name, @@ -515,6 +532,7 @@ template("_impeller_shaders_gles") { impellerc(impellerc_gles) { shaders = invoker.shaders sl_file_extension = "gles" + require_framebuffer_fetch = require_framebuffer_fetch if (defined(invoker.gles_language_version)) { gles_language_version = invoker.gles_language_version } @@ -690,11 +708,14 @@ template("_impeller_shaders_vk") { # # The SPIR-V version required by the shaders. # -# @param[options] use_half_textures +# @param[optional] use_half_textures # # Whether the metal shader is using half-precision textures and requires # openGL semantics when compilig SPIR-V. # +# @param[optional] require_framebuffer_fetch +# +# Whether to require the framebuffer fetch extension for GLES fragment shaders. template("impeller_shaders") { if (defined(invoker.metal_version)) { metal_version = invoker.metal_version @@ -705,9 +726,16 @@ template("impeller_shaders") { use_half_textures = true } + require_framebuffer_fetch = false + if (defined(invoker.require_framebuffer_fetch) && + invoker.require_framebuffer_fetch) { + require_framebuffer_fetch = true + } + not_needed([ "metal_version", "use_half_textures", + "require_framebuffer_fetch", ]) enable_opengles = impeller_enable_opengles @@ -737,6 +765,7 @@ template("impeller_shaders") { gles_shaders = "gles_$target_name" _impeller_shaders_gles(gles_shaders) { name = invoker.name + require_framebuffer_fetch = require_framebuffer_fetch if (defined(invoker.gles_language_version)) { gles_language_version = invoker.gles_language_version } diff --git a/impeller/tools/malioc.json b/impeller/tools/malioc.json index 392449302a8c0..17828b671dec7 100644 --- a/impeller/tools/malioc.json +++ b/impeller/tools/malioc.json @@ -2176,1213 +2176,6 @@ } } }, - "flutter/impeller/entity/framebuffer_blend.vert.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend.vert.vkspv", - "has_uniform_computation": true, - "type": "Vertex", - "variants": { - "Position": { - "fp16_arithmetic": 0, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "load_store" - ], - "longest_path_cycles": [ - 0.125, - 0.125, - 0.0, - 0.0, - 2.0, - 0.0 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "texture" - ], - "shortest_path_bound_pipelines": [ - "load_store" - ], - "shortest_path_cycles": [ - 0.125, - 0.125, - 0.0, - 0.0, - 2.0, - 0.0 - ], - "total_bound_pipelines": [ - "load_store" - ], - "total_cycles": [ - 0.125, - 0.125, - 0.0, - 0.0, - 2.0, - 0.0 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 30, - "work_registers_used": 32 - }, - "Varying": { - "fp16_arithmetic": 0, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "load_store" - ], - "longest_path_cycles": [ - 0.015625, - 0.015625, - 0.015625, - 0.0, - 3.0, - 0.0 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "texture" - ], - "shortest_path_bound_pipelines": [ - "load_store" - ], - "shortest_path_cycles": [ - 0.015625, - 0.015625, - 0.015625, - 0.0, - 3.0, - 0.0 - ], - "total_bound_pipelines": [ - "load_store" - ], - "total_cycles": [ - 0.015625, - 0.015625, - 0.015625, - 0.0, - 3.0, - 0.0 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 22, - "work_registers_used": 8 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_color.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_color.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_colorburn.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_colorburn.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_colordodge.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_colordodge.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_darken.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_darken.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_difference.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_difference.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_exclusion.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_exclusion.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_hardlight.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_hardlight.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_hue.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_hue.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_lighten.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_lighten.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_luminosity.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_luminosity.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_multiply.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_multiply.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_overlay.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_overlay.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_saturation.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_saturation.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_screen.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_screen.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, - "flutter/impeller/entity/framebuffer_blend_softlight.frag.vkspv": { - "Mali-G78": { - "core": "Mali-G78", - "filename": "flutter/impeller/entity/framebuffer_blend_softlight.frag.vkspv", - "has_side_effects": false, - "has_uniform_computation": true, - "modifies_coverage": false, - "reads_color_buffer": false, - "type": "Fragment", - "uses_late_zs_test": false, - "uses_late_zs_update": false, - "variants": { - "Main": { - "fp16_arithmetic": 100, - "has_stack_spilling": false, - "performance": { - "longest_path_bound_pipelines": [ - "varying", - "texture" - ], - "longest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "pipelines": [ - "arith_total", - "arith_fma", - "arith_cvt", - "arith_sfu", - "load_store", - "varying", - "texture" - ], - "shortest_path_bound_pipelines": [ - "varying", - "texture" - ], - "shortest_path_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ], - "total_bound_pipelines": [ - "varying", - "texture" - ], - "total_cycles": [ - 0.0625, - 0.0625, - 0.0, - 0.0, - 0.0, - 0.25, - 0.25 - ] - }, - "stack_spill_bytes": 0, - "thread_occupancy": 100, - "uniform_registers_used": 4, - "work_registers_used": 6 - } - } - } - }, "flutter/impeller/entity/gaussian_blur.vert.vkspv": { "Mali-G78": { "core": "Mali-G78", @@ -3820,7 +2613,7 @@ "longest_path_cycles": [ 0.609375, 0.609375, - 0.4375, + 0.46875, 0.5, 0.0, 0.5, @@ -3842,24 +2635,23 @@ "shortest_path_cycles": [ 0.34375, 0.34375, - 0.3125, - 0.1875, + 0.265625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_fma" + "texture" ], "total_cycles": [ 0.609375, 0.609375, - 0.484375, + 0.578125, 0.5, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -3882,7 +2674,7 @@ "arithmetic" ], "longest_path_cycles": [ - 8.90999984741211, + 9.569999694824219, 2.0, 2.0 ], @@ -3895,7 +2687,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 5.28000020980835, + 4.949999809265137, 1.0, 0.0 ], @@ -3903,13 +2695,13 @@ "arithmetic" ], "total_cycles": [ - 9.666666984558105, + 11.0, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 3 } } @@ -3936,9 +2728,9 @@ "arith_cvt" ], "longest_path_cycles": [ - 0.578125, + 0.637499988079071, 0.28125, - 0.578125, + 0.637499988079071, 0.5625, 0.0, 0.5, @@ -3958,26 +2750,25 @@ "arith_cvt" ], "shortest_path_cycles": [ - 0.453125, + 0.40625, 0.25, - 0.453125, - 0.375, + 0.40625, + 0.1875, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_cvt" + "texture" ], "total_cycles": [ - 0.625, + 0.75, 0.28125, - 0.625, + 0.75, 0.5625, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4000,7 +2791,7 @@ "arithmetic" ], "longest_path_cycles": [ - 9.569999694824219, + 10.229999542236328, 2.0, 2.0 ], @@ -4013,7 +2804,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 7.920000076293945, + 7.590000152587891, 1.0, 0.0 ], @@ -4021,13 +2812,13 @@ "arithmetic" ], "total_cycles": [ - 10.333333015441895, + 11.666666984558105, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -4051,13 +2842,12 @@ "performance": { "longest_path_bound_pipelines": [ "arith_total", - "arith_cvt", - "arith_sfu" + "arith_cvt" ], "longest_path_cycles": [ - 0.5625, + 0.625, 0.25, - 0.5625, + 0.625, 0.5625, 0.0, 0.5, @@ -4077,26 +2867,25 @@ "arith_cvt" ], "shortest_path_cycles": [ - 0.4375, + 0.390625, 0.21875, - 0.4375, - 0.375, + 0.390625, + 0.1875, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_cvt" + "texture" ], "total_cycles": [ - 0.609375, + 0.737500011920929, 0.25, - 0.609375, + 0.737500011920929, 0.5625, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4119,7 +2908,7 @@ "arithmetic" ], "longest_path_cycles": [ - 9.569999694824219, + 10.229999542236328, 2.0, 2.0 ], @@ -4132,7 +2921,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 7.920000076293945, + 7.590000152587891, 1.0, 0.0 ], @@ -4140,13 +2929,13 @@ "arithmetic" ], "total_cycles": [ - 10.333333015441895, + 11.666666984558105, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -4173,9 +2962,9 @@ "texture" ], "longest_path_cycles": [ - 0.375, + 0.40625, 0.1875, - 0.34375, + 0.40625, 0.375, 0.0, 0.5, @@ -4191,29 +2980,29 @@ "texture" ], "shortest_path_bound_pipelines": [ - "varying" + "varying", + "texture" ], "shortest_path_cycles": [ - 0.21875, + 0.171875, 0.15625, - 0.21875, - 0.1875, + 0.171875, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ - 0.390625, + 0.515625, 0.1875, - 0.390625, + 0.515625, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4236,7 +3025,7 @@ "arithmetic" ], "longest_path_cycles": [ - 4.619999885559082, + 5.28000020980835, 2.0, 2.0 ], @@ -4249,7 +3038,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.640000104904175, + 2.309999942779541, 1.0, 0.0 ], @@ -4257,13 +3046,13 @@ "arithmetic" ], "total_cycles": [ - 5.0, + 6.333333492279053, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -4292,7 +3081,7 @@ "longest_path_cycles": [ 0.375, 0.234375, - 0.3125, + 0.375, 0.375, 0.0, 0.5, @@ -4308,29 +3097,29 @@ "texture" ], "shortest_path_bound_pipelines": [ - "varying" + "varying", + "texture" ], "shortest_path_cycles": [ 0.203125, 0.203125, - 0.1875, - 0.1875, + 0.140625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ - 0.375, + 0.484375, 0.234375, - 0.359375, + 0.484375, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4353,7 +3142,7 @@ "arithmetic" ], "longest_path_cycles": [ - 4.949999809265137, + 5.610000133514404, 2.0, 2.0 ], @@ -4366,7 +3155,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.9700000286102295, + 2.640000104904175, 1.0, 0.0 ], @@ -4374,13 +3163,13 @@ "arithmetic" ], "total_cycles": [ - 5.333333492279053, + 6.666666507720947, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -4409,7 +3198,7 @@ "longest_path_cycles": [ 0.375, 0.28125, - 0.3125, + 0.375, 0.375, 0.0, 0.5, @@ -4427,29 +3216,29 @@ "shortest_path_bound_pipelines": [ "arith_total", "arith_fma", - "varying" + "varying", + "texture" ], "shortest_path_cycles": [ 0.25, 0.25, - 0.1875, - 0.1875, + 0.140625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ - 0.375, + 0.484375, 0.28125, - 0.359375, + 0.484375, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4472,7 +3261,7 @@ "arithmetic" ], "longest_path_cycles": [ - 4.949999809265137, + 5.610000133514404, 2.0, 2.0 ], @@ -4485,7 +3274,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.9700000286102295, + 2.640000104904175, 1.0, 0.0 ], @@ -4493,13 +3282,13 @@ "arithmetic" ], "total_cycles": [ - 5.333333492279053, + 6.666666507720947, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -4528,7 +3317,7 @@ "longest_path_cycles": [ 0.453125, 0.453125, - 0.359375, + 0.421875, 0.375, 0.0, 0.5, @@ -4550,24 +3339,23 @@ "shortest_path_cycles": [ 0.421875, 0.421875, - 0.234375, 0.1875, 0.0, + 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ + 0.53125, 0.453125, - 0.453125, - 0.40625, + 0.53125, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4590,7 +3378,7 @@ "arithmetic" ], "longest_path_cycles": [ - 5.940000057220459, + 6.599999904632568, 2.0, 2.0 ], @@ -4603,7 +3391,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 4.289999961853027, + 3.9600000381469727, 1.0, 0.0 ], @@ -4611,13 +3399,13 @@ "arithmetic" ], "total_cycles": [ - 6.666666507720947, + 8.0, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 4 } } @@ -4646,7 +3434,7 @@ "longest_path_cycles": [ 0.71875, 0.71875, - 0.59375, + 0.625, 0.5625, 0.0, 0.5, @@ -4666,26 +3454,25 @@ "arith_cvt" ], "shortest_path_cycles": [ - 0.453125, + 0.40625, 0.34375, - 0.453125, - 0.1875, + 0.40625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_fma" + "texture" ], "total_cycles": [ + 0.78125, 0.71875, - 0.71875, - 0.6875, + 0.78125, 0.5625, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4708,7 +3495,7 @@ "arithmetic" ], "longest_path_cycles": [ - 10.5600004196167, + 11.220000267028809, 2.0, 2.0 ], @@ -4721,7 +3508,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 5.940000057220459, + 5.610000133514404, 1.0, 0.0 ], @@ -4729,13 +3516,13 @@ "arithmetic" ], "total_cycles": [ - 11.666666984558105, + 13.0, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 3 } } @@ -4762,9 +3549,9 @@ "texture" ], "longest_path_cycles": [ - 0.375, + 0.40625, 0.1875, - 0.34375, + 0.40625, 0.375, 0.0, 0.5, @@ -4780,29 +3567,29 @@ "texture" ], "shortest_path_bound_pipelines": [ - "varying" + "varying", + "texture" ], "shortest_path_cycles": [ - 0.21875, + 0.171875, 0.15625, - 0.21875, - 0.1875, + 0.171875, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ - 0.390625, + 0.515625, 0.1875, - 0.390625, + 0.515625, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4825,7 +3612,7 @@ "arithmetic" ], "longest_path_cycles": [ - 4.619999885559082, + 5.28000020980835, 2.0, 2.0 ], @@ -4838,7 +3625,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.640000104904175, + 2.309999942779541, 1.0, 0.0 ], @@ -4846,13 +3633,13 @@ "arithmetic" ], "total_cycles": [ - 5.0, + 6.333333492279053, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -4881,7 +3668,7 @@ "longest_path_cycles": [ 0.609375, 0.609375, - 0.4375, + 0.46875, 0.5, 0.0, 0.5, @@ -4903,24 +3690,23 @@ "shortest_path_cycles": [ 0.34375, 0.34375, - 0.3125, - 0.1875, + 0.265625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_fma" + "texture" ], "total_cycles": [ 0.609375, 0.609375, - 0.484375, + 0.578125, 0.5, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -4943,7 +3729,7 @@ "arithmetic" ], "longest_path_cycles": [ - 8.90999984741211, + 9.569999694824219, 2.0, 2.0 ], @@ -4956,7 +3742,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 5.28000020980835, + 4.949999809265137, 1.0, 0.0 ], @@ -4964,13 +3750,13 @@ "arithmetic" ], "total_cycles": [ - 9.666666984558105, + 11.0, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 3 } } @@ -4999,7 +3785,7 @@ "longest_path_cycles": [ 0.375, 0.203125, - 0.3125, + 0.375, 0.375, 0.0, 0.5, @@ -5015,29 +3801,29 @@ "texture" ], "shortest_path_bound_pipelines": [ - "varying" + "varying", + "texture" ], "shortest_path_cycles": [ - 0.1875, 0.171875, - 0.1875, - 0.1875, + 0.171875, + 0.140625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ - 0.375, + 0.484375, 0.203125, - 0.359375, + 0.484375, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -5060,7 +3846,7 @@ "arithmetic" ], "longest_path_cycles": [ - 4.949999809265137, + 5.610000133514404, 2.0, 2.0 ], @@ -5073,7 +3859,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.9700000286102295, + 2.640000104904175, 1.0, 0.0 ], @@ -5081,13 +3867,13 @@ "arithmetic" ], "total_cycles": [ - 5.333333492279053, + 6.666666507720947, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -5116,7 +3902,7 @@ "longest_path_cycles": [ 0.453125, 0.453125, - 0.375, + 0.4375, 0.375, 0.0, 0.5, @@ -5138,24 +3924,23 @@ "shortest_path_cycles": [ 0.421875, 0.421875, - 0.25, - 0.1875, + 0.203125, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ + 0.546875, 0.453125, - 0.453125, - 0.421875, + 0.546875, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -5178,7 +3963,7 @@ "arithmetic" ], "longest_path_cycles": [ - 6.269999980926514, + 6.929999828338623, 2.0, 2.0 ], @@ -5191,7 +3976,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 4.289999961853027, + 3.9600000381469727, 1.0, 0.0 ], @@ -5199,13 +3984,13 @@ "arithmetic" ], "total_cycles": [ - 6.666666507720947, + 8.0, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 4 } } @@ -5234,7 +4019,7 @@ "longest_path_cycles": [ 0.71875, 0.71875, - 0.59375, + 0.625, 0.5625, 0.0, 0.5, @@ -5254,26 +4039,25 @@ "arith_cvt" ], "shortest_path_cycles": [ - 0.453125, + 0.40625, 0.34375, - 0.453125, - 0.1875, + 0.40625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_fma" + "texture" ], "total_cycles": [ + 0.78125, 0.71875, - 0.71875, - 0.6875, + 0.78125, 0.5625, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -5296,7 +4080,7 @@ "arithmetic" ], "longest_path_cycles": [ - 10.890000343322754, + 11.550000190734863, 2.0, 2.0 ], @@ -5309,7 +4093,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 5.940000057220459, + 5.610000133514404, 1.0, 0.0 ], @@ -5317,13 +4101,13 @@ "arithmetic" ], "total_cycles": [ - 11.666666984558105, + 13.0, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 3 } } @@ -5352,7 +4136,7 @@ "longest_path_cycles": [ 0.375, 0.25, - 0.3125, + 0.375, 0.375, 0.0, 0.5, @@ -5368,29 +4152,29 @@ "texture" ], "shortest_path_bound_pipelines": [ - "varying" + "varying", + "texture" ], "shortest_path_cycles": [ 0.21875, 0.21875, - 0.1875, - 0.1875, + 0.140625, + 0.0, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "varying", "texture" ], "total_cycles": [ - 0.375, + 0.484375, 0.25, - 0.359375, + 0.484375, 0.375, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -5413,7 +4197,7 @@ "arithmetic" ], "longest_path_cycles": [ - 4.949999809265137, + 5.610000133514404, 2.0, 2.0 ], @@ -5426,7 +4210,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.9700000286102295, + 2.640000104904175, 1.0, 0.0 ], @@ -5434,13 +4218,13 @@ "arithmetic" ], "total_cycles": [ - 5.333333492279053, + 6.666666507720947, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 2 } } @@ -5469,7 +4253,7 @@ "longest_path_cycles": [ 0.75, 0.75, - 0.515625, + 0.578125, 0.5625, 0.0, 0.5, @@ -5491,24 +4275,23 @@ "shortest_path_cycles": [ 0.71875, 0.71875, - 0.390625, - 0.375, + 0.34375, + 0.1875, 0.0, 0.25, - 0.0 + 0.25 ], "total_bound_pipelines": [ - "arith_total", - "arith_fma" + "texture" ], "total_cycles": [ 0.75, 0.75, - 0.5625, + 0.6875, 0.5625, 0.0, 0.5, - 0.5 + 1.0 ] }, "stack_spill_bytes": 0, @@ -5531,7 +4314,7 @@ "arithmetic" ], "longest_path_cycles": [ - 8.90999984741211, + 9.569999694824219, 2.0, 2.0 ], @@ -5544,7 +4327,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 6.929999828338623, + 6.599999904632568, 1.0, 0.0 ], @@ -5552,13 +4335,13 @@ "arithmetic" ], "total_cycles": [ - 9.333333015441895, + 10.666666984558105, 2.0, - 2.0 + 4.0 ] }, "thread_occupancy": 100, - "uniform_registers_used": 1, + "uniform_registers_used": 2, "work_registers_used": 4 } } @@ -8354,17 +7137,16 @@ 0.0 ], "total_bound_pipelines": [ - "arith_total", - "arith_cvt" + "texture" ], "total_cycles": [ - 0.359375, + 0.390625, 0.078125, - 0.359375, + 0.390625, 0.0625, 0.0, 0.25, - 0.25 + 0.5 ] }, "stack_spill_bytes": 0, @@ -8409,14 +7191,14 @@ "arithmetic" ], "total_cycles": [ - 4.0, + 4.666666507720947, 1.0, - 1.0 + 2.0 ] }, "thread_occupancy": 100, "uniform_registers_used": 1, - "work_registers_used": 4 + "work_registers_used": 3 } } } diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc index b5d7ee8c468e5..d39e36ad2bd44 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.cc +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -18,7 +18,7 @@ namespace impeller { static Font ToFont(const SkTextBlobRunIterator& run) { auto& font = run.font(); - auto typeface = std::make_shared(font.refTypefaceOrDefault()); + auto typeface = std::make_shared(font.refTypeface()); SkFontMetrics sk_metrics; font.getMetrics(&sk_metrics); diff --git a/impeller/typographer/backends/skia/typographer_context_skia.cc b/impeller/typographer/backends/skia/typographer_context_skia.cc index 9f8725a67c81d..e3082678d541a 100644 --- a/impeller/typographer/backends/skia/typographer_context_skia.cc +++ b/impeller/typographer/backends/skia/typographer_context_skia.cc @@ -117,10 +117,10 @@ static ISize OptimumAtlasSizeForFontGlyphPairs( const std::vector& pairs, std::vector& glyph_positions, const std::shared_ptr& atlas_context, - GlyphAtlas::Type type) { + GlyphAtlas::Type type, + const ISize& max_texture_size) { static constexpr auto kMinAtlasSize = 8u; static constexpr auto kMinAlphaBitmapSize = 1024u; - static constexpr auto kMaxAtlasSize = 4096u; TRACE_EVENT0("impeller", __FUNCTION__); @@ -147,8 +147,8 @@ static ISize OptimumAtlasSizeForFontGlyphPairs( Allocation::NextPowerOfTwoSize(current_size.width + 1), Allocation::NextPowerOfTwoSize(current_size.height + 1)); } - } while (current_size.width <= kMaxAtlasSize && - current_size.height <= kMaxAtlasSize); + } while (current_size.width <= max_texture_size.width && + current_size.height <= max_texture_size.height); return ISize{0, 0}; } @@ -366,7 +366,7 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( // --------------------------------------------------------------------------- // Step 3a: Record the positions in the glyph atlas of the newly added - // glyphs. + // glyphs. // --------------------------------------------------------------------------- for (size_t i = 0, count = glyph_positions.size(); i < count; i++) { last_atlas->AddTypefaceGlyphPosition(new_glyphs[i], glyph_positions[i]); @@ -405,7 +405,12 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( } auto glyph_atlas = std::make_shared(type); auto atlas_size = OptimumAtlasSizeForFontGlyphPairs( - font_glyph_pairs, glyph_positions, atlas_context, type); + font_glyph_pairs, // + glyph_positions, // + atlas_context, // + type, // + context.GetResourceAllocator()->GetMaxTextureSizeSupported() // + ); atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size); if (atlas_size.IsEmpty()) { diff --git a/impeller/typographer/backends/stb/typographer_context_stb.cc b/impeller/typographer/backends/stb/typographer_context_stb.cc index 5669d7eba7837..a3cf14b91837a 100644 --- a/impeller/typographer/backends/stb/typographer_context_stb.cc +++ b/impeller/typographer/backends/stb/typographer_context_stb.cc @@ -160,10 +160,10 @@ static ISize OptimumAtlasSizeForFontGlyphPairs( const std::vector& pairs, std::vector& glyph_positions, const std::shared_ptr& atlas_context, - GlyphAtlas::Type type) { + GlyphAtlas::Type type, + const ISize& max_texture_size) { static constexpr auto kMinAtlasSize = 8u; static constexpr auto kMinAlphaBitmapSize = 1024u; - static constexpr auto kMaxAtlasSize = 2048u; // QNX required 2048 or less. TRACE_EVENT0("impeller", __FUNCTION__); @@ -190,8 +190,8 @@ static ISize OptimumAtlasSizeForFontGlyphPairs( Allocation::NextPowerOfTwoSize(current_size.width + 1), Allocation::NextPowerOfTwoSize(current_size.height + 1)); } - } while (current_size.width <= kMaxAtlasSize && - current_size.height <= kMaxAtlasSize); + } while (current_size.width <= max_texture_size.width && + current_size.height <= max_texture_size.height); return ISize{0, 0}; } @@ -469,7 +469,12 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( } auto glyph_atlas = std::make_shared(type); auto atlas_size = OptimumAtlasSizeForFontGlyphPairs( - font_glyph_pairs, glyph_positions, atlas_context, type); + font_glyph_pairs, // + glyph_positions, // + atlas_context, // + type, // + context.GetResourceAllocator()->GetMaxTextureSizeSupported() // + ); atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size); if (atlas_size.IsEmpty()) { diff --git a/lib/gpu/context.cc b/lib/gpu/context.cc index 34418320b5ddc..7539f0cb7300a 100644 --- a/lib/gpu/context.cc +++ b/lib/gpu/context.cc @@ -55,7 +55,8 @@ Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) { // Grab the Impeller context from the IO manager. std::promise> context_promise; auto impeller_context_future = context_promise.get_future(); - dart_state->GetTaskRunners().GetIOTaskRunner()->PostTask( + fml::TaskRunner::RunNowOrPostTask( + dart_state->GetTaskRunners().GetIOTaskRunner(), fml::MakeCopyable([promise = std::move(context_promise), io_manager = dart_state->GetIOManager()]() mutable { promise.set_value(io_manager ? io_manager->GetImpellerContext() diff --git a/lib/gpu/lib/src/context.dart b/lib/gpu/lib/src/context.dart index 1c1be14267fbb..a0efc586d16a1 100644 --- a/lib/gpu/lib/src/context.dart +++ b/lib/gpu/lib/src/context.dart @@ -8,7 +8,7 @@ import 'dart:nativewrappers'; /// A handle to a graphics context. Used to create and manage GPU resources. /// /// To obtain the default graphics context, use [getContext]. -class GpuContext extends NativeFieldWrapperClass1 { +base class GpuContext extends NativeFieldWrapperClass1 { /// Creates a new graphics context that corresponds to the default Impeller /// context. GpuContext._createDefault() { diff --git a/lib/gpu/pubspec.yaml b/lib/gpu/pubspec.yaml index a45d82dab12d3..204f445df09fb 100644 --- a/lib/gpu/pubspec.yaml +++ b/lib/gpu/pubspec.yaml @@ -7,7 +7,7 @@ description: A framework for writing Flutter applications homepage: https://flutter.dev environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: sky_engine: diff --git a/lib/snapshot/BUILD.gn b/lib/snapshot/BUILD.gn index eacbee21539f9..d7c6eab5c979a 100644 --- a/lib/snapshot/BUILD.gn +++ b/lib/snapshot/BUILD.gn @@ -4,6 +4,7 @@ import("//build/compiled_action.gni") import("//build/fuchsia/sdk.gni") +import("//flutter/build/bin_to_obj.gni") import("//flutter/common/config.gni") import("//flutter/impeller/tools/impeller.gni") import("//flutter/lib/ui/dart_ui.gni") @@ -110,103 +111,6 @@ compiled_action("generate_snapshot_bin") { } } -# Generates an assembly file defining a given symbol with the bytes from a -# binary file. Places the symbol in a text section if 'executable' is true, -# otherwise places the symbol in a read-only data section. -template("bin_to_assembly") { - assert(defined(invoker.deps), "Must define deps") - assert(defined(invoker.input), "Must define input binary file") - assert(defined(invoker.symbol), "Must define symbol name") - assert(defined(invoker.executable), "Must define boolean executable") - - action(target_name) { - deps = invoker.deps - script = "//third_party/dart/runtime/tools/bin_to_assembly.py" - output = invoker.input + ".S" - args = [ - "--input", - rebase_path(invoker.input), - "--output", - rebase_path(output), - "--symbol_name", - invoker.symbol, - "--target_os", - current_os, - ] - if (defined(invoker.size_symbol)) { - args += [ - "--size_symbol_name", - invoker.size_symbol, - "--target_arch", - current_cpu, - ] - } - if (invoker.executable) { - args += [ "--executable" ] - } - inputs = [ - script, - invoker.input, - ] - outputs = [ output ] - } -} - -# Generates an object file defining a given symbol with the bytes from a -# binary file. Places the symbol in the read-only data section. -template("bin_to_coff") { - assert(defined(invoker.deps), "Must define deps") - assert(defined(invoker.input), "Must define input binary file") - assert(defined(invoker.symbol), "Must define symbol name") - assert(defined(invoker.executable), "Must define executable") - - action(target_name) { - deps = invoker.deps - script = "//third_party/dart/runtime/tools/bin_to_coff.py" - output = invoker.input + ".o" - args = [ - "--input", - rebase_path(invoker.input), - "--output", - rebase_path(output), - "--symbol_name", - invoker.symbol, - ] - - if (defined(invoker.size_symbol)) { - args += [ - "--size_symbol_name", - invoker.size_symbol, - ] - } - - if (invoker.executable) { - args += [ "--executable" ] - } - - args += [ "--arch=$current_cpu" ] - inputs = [ invoker.input ] - outputs = [ output ] - } -} - -# Generates a linkable output file defining the specified symbol with the bytes -# from the binary file. Emits a COFF object file when targeting Windows, -# otherwise assembly. -template("bin_to_linkable") { - assert(defined(invoker.deps), "Must define deps") - assert(defined(invoker.input), "Must define input binary file") - assert(defined(invoker.symbol), "Must define symbol name") - target_type = "bin_to_assembly" - if (is_win) { - target_type = "bin_to_coff" - } - - target(target_type, target_name) { - forward_variables_from(invoker, "*") - } -} - bin_to_linkable("vm_snapshot_data_linkable") { deps = [ ":generate_snapshot_bin" ] input = "$target_gen_dir/vm_isolate_snapshot.bin" diff --git a/lib/snapshot/pubspec.yaml b/lib/snapshot/pubspec.yaml index bb7cd30be46ca..0fa6a26f9ecaa 100644 --- a/lib/snapshot/pubspec.yaml +++ b/lib/snapshot/pubspec.yaml @@ -5,4 +5,4 @@ # This file is needed by Fuchsia's dart_library template. environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index fac0ac8a5f121..7550a3ac19f1a 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -574,7 +574,6 @@ abstract class SceneBuilder { base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements SceneBuilder { /// Creates an empty [SceneBuilder] object. - @pragma('vm:entry-point') _NativeSceneBuilder() { _constructor(); } diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 976b373d29d08..310de114e377a 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -98,7 +98,7 @@ typedef CanvasPath Path; V(NativeStringAttribute::initSpellOutStringAttribute, 3) \ V(PlatformConfigurationNativeApi::DefaultRouteName, 0) \ V(PlatformConfigurationNativeApi::ScheduleFrame, 0) \ - V(PlatformConfigurationNativeApi::Render, 1) \ + V(PlatformConfigurationNativeApi::Render, 2) \ V(PlatformConfigurationNativeApi::UpdateSemantics, 1) \ V(PlatformConfigurationNativeApi::SetNeedsReportTimings, 1) \ V(PlatformConfigurationNativeApi::SetIsolateDebugName, 1) \ diff --git a/lib/ui/experiments/gpu.dart b/lib/ui/experiments/gpu.dart index 282f955e6658e..6b07dc65efc7b 100644 --- a/lib/ui/experiments/gpu.dart +++ b/lib/ui/experiments/gpu.dart @@ -147,7 +147,7 @@ class RasterPipeline {} /// A handle to a graphics context. Used to create and manage GPU resources. /// /// To obtain the default graphics context, use [getGpuContext]. -class GpuContext extends NativeFieldWrapperClass1 { +base class GpuContext extends NativeFieldWrapperClass1 { /// Creates a new graphics context that corresponds to the default Impeller /// context. GpuContext._createDefault() { diff --git a/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.67.png b/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.67.png new file mode 100644 index 0000000000000..bdec9bc8f1499 Binary files /dev/null and b/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.67.png differ diff --git a/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.68.png b/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.68.png new file mode 100644 index 0000000000000..6ade80e00e468 Binary files /dev/null and b/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.68.png differ diff --git a/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.69.png b/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.69.png new file mode 100644 index 0000000000000..c3006d0c2f89b Binary files /dev/null and b/lib/ui/fixtures/impeller_2_dispose_op_restore_previous.apng.69.png differ diff --git a/lib/ui/fixtures/impeller_four_frame_with_reuse_end.png b/lib/ui/fixtures/impeller_four_frame_with_reuse_end.png new file mode 100644 index 0000000000000..7c67236810621 Binary files /dev/null and b/lib/ui/fixtures/impeller_four_frame_with_reuse_end.png differ diff --git a/lib/ui/fixtures/impeller_heart_end.png b/lib/ui/fixtures/impeller_heart_end.png new file mode 100644 index 0000000000000..cd33ff064c605 Binary files /dev/null and b/lib/ui/fixtures/impeller_heart_end.png differ diff --git a/lib/ui/fixtures/ui_test.dart b/lib/ui/fixtures/ui_test.dart index 0aff1775688b7..dc4f75bb67f33 100644 --- a/lib/ui/fixtures/ui_test.dart +++ b/lib/ui/fixtures/ui_test.dart @@ -310,7 +310,7 @@ Future encodeImageProducesExternalUint8List() async { canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); final Image image = await picture.toImage(100, 100); - _encodeImage(image, ImageByteFormat.png.index, (Uint8List result) { + _encodeImage(image, ImageByteFormat.png.index, (Uint8List result, String? error) { // The buffer should be non-null and writable. result[0] = 0; // The buffer should be external typed data. @@ -319,9 +319,65 @@ Future encodeImageProducesExternalUint8List() async { } @pragma('vm:external-name', 'EncodeImage') -external void _encodeImage(Image i, int format, void Function(Uint8List result)); +external void _encodeImage(Image i, int format, void Function(Uint8List result, String? error)); @pragma('vm:external-name', 'ValidateExternal') external void _validateExternal(Uint8List result); +@pragma('vm:external-name', 'ValidateError') +external void _validateError(String? error); +@pragma('vm:external-name', 'TurnOffGPU') +external void _turnOffGPU(bool value); +@pragma('vm:external-name', 'FlushGpuAwaitingTasks') +external void _flushGpuAwaitingTasks(); +@pragma('vm:external-name', 'ValidateNotNull') +external void _validateNotNull(Object? object); + +@pragma('vm:entry-point') +Future toByteDataWithoutGPU() async { + final PictureRecorder pictureRecorder = PictureRecorder(); + final Canvas canvas = Canvas(pictureRecorder); + final Paint paint = Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; + final Offset c = Offset(50.0, 50.0); + canvas.drawCircle(c, 25.0, paint); + final Picture picture = pictureRecorder.endRecording(); + final Image image = await picture.toImage(100, 100); + _turnOffGPU(true); + Timer flusher = Timer.periodic(Duration(milliseconds: 1), (timer) { + _flushGpuAwaitingTasks(); + }); + try { + ByteData? byteData = await image.toByteData(); + _validateError(null); + } catch (error) { + _validateError(error.toString()); + } finally { + flusher.cancel(); + } +} + +@pragma('vm:entry-point') +Future toByteDataRetries() async { + final PictureRecorder pictureRecorder = PictureRecorder(); + final Canvas canvas = Canvas(pictureRecorder); + final Paint paint = Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; + final Offset c = Offset(50.0, 50.0); + canvas.drawCircle(c, 25.0, paint); + final Picture picture = pictureRecorder.endRecording(); + final Image image = await picture.toImage(100, 100); + _turnOffGPU(true); + Future.delayed(Duration(milliseconds: 10), () { + _turnOffGPU(false); + }); + try { + ByteData? byteData = await image.toByteData(); + _validateNotNull(byteData); + } catch (error) { + _validateNotNull(null); + } +} @pragma('vm:entry-point') Future pumpImage() async { diff --git a/lib/ui/geometry.dart b/lib/ui/geometry.dart index 26ac53a5f4df6..6f00295aaa40a 100644 --- a/lib/ui/geometry.dart +++ b/lib/ui/geometry.dart @@ -632,7 +632,6 @@ class Rect { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltrb.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltrb_dark.png#gh-dark-mode-only) - @pragma('vm:entry-point') const Rect.fromLTRB(this.left, this.top, this.right, this.bottom); /// Construct a rectangle from its left and top edges, its width, and its diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 5c2244d43a9ad..2d228e1bf66ba 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -288,8 +288,7 @@ bool _onError(Object error, StackTrace? stackTrace) { return PlatformDispatcher.instance._dispatchError(error, stackTrace ?? StackTrace.empty); } -// ignore: always_declare_return_types, prefer_generic_function_type_aliases -typedef _ListStringArgFunction(List args); +typedef _ListStringArgFunction = Object? Function(List args); @pragma('vm:entry-point') void _runMain(Function startMainIsolateFunction, diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 03d32b97cbc90..1fbf1cae9fdd0 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -99,7 +99,6 @@ class Color { /// For example, to get a fully opaque orange, you would use `const /// Color(0xFFFF9000)` (`FF` for the alpha, `FF` for the red, `90` for the /// green, and `00` for the blue). - @pragma('vm:entry-point') const Color(int value) : value = value & 0xFFFFFFFF; /// Construct a color from the lower 8 bits of four integers. @@ -1092,9 +1091,8 @@ class Paint { /// Constructs an empty [Paint] object with all fields initialized to /// their defaults. Paint() { - if (enableDithering) { - _dither = true; - } + // TODO(matanlurey): Remove as part of https://github.com/flutter/flutter/issues/112498. + _enableDithering(); } // Paint objects are encoded in two buffers: @@ -1480,27 +1478,11 @@ class Paint { _data.setInt32(_kInvertColorOffset, value ? 1 : 0, _kFakeHostEndian); } - bool get _dither { - return _data.getInt32(_kDitherOffset, _kFakeHostEndian) == 1; - } - set _dither(bool value) { - _data.setInt32(_kDitherOffset, value ? 1 : 0, _kFakeHostEndian); + // TODO(matanlurey): Remove as part of https://github.com/flutter/flutter/issues/112498. + void _enableDithering() { + _data.setInt32(_kDitherOffset, 1, _kFakeHostEndian); } - /// Whether to dither the output when drawing some elements such as gradients. - /// - /// It is not expected that this flag will be used in the future; please leave - /// feedback in if there is - /// a use case for this flag to remain long term. - @Deprecated( - 'Dithering is now enabled by default on some elements (such as gradients) ' - 'and further support for dithering is expected to be handled by custom ' - 'shaders, so this flag is being removed: ' - 'https://github.com/flutter/flutter/issues/112498.' - 'This feature was deprecated after 3.14.0-0.1.pre.' - ) - static bool enableDithering = true; - @override String toString() { if (const bool.fromEnvironment('dart.vm.product')) { @@ -1563,9 +1545,6 @@ class Paint { if (invertColors) { result.write('${semicolon}invert: $invertColors'); } - if (_dither) { - result.write('${semicolon}dither: $_dither'); - } result.write(')'); return result.toString(); } @@ -1957,16 +1936,20 @@ base class _Image extends NativeFieldWrapperClass1 { external int get height; Future toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) { - return _futurize((_Callback callback) { - return _toByteData(format.index, (Uint8List? encoded) { - callback(encoded!.buffer.asByteData()); + return _futurizeWithError((_CallbackWithError callback) { + return _toByteData(format.index, (Uint8List? encoded, String? error) { + if (error == null && encoded != null) { + callback(encoded.buffer.asByteData(), null); + } else { + callback(null, error); + } }); }); } /// Returns an error message on failure, null on success. @Native, Int32, Handle)>(symbol: 'Image::toByteData') - external String? _toByteData(int format, _Callback callback); + external String? _toByteData(int format, void Function(Uint8List?, String?) callback); bool _disposed = false; void dispose() { @@ -2098,7 +2081,6 @@ abstract class Codec { void dispose(); } -@pragma('vm:entry-point') base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec { // // This class is created by the engine, and should not be instantiated @@ -2106,7 +2088,6 @@ base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec { // // To obtain an instance of the [Codec] interface, see // [instantiateImageCodec]. - @pragma('vm:entry-point') _NativeCodec._(); int? _cachedFrameCount; @@ -2574,11 +2555,9 @@ abstract class EngineLayer { void dispose(); } -@pragma('vm:entry-point') base class _NativeEngineLayer extends NativeFieldWrapperClass1 implements EngineLayer { /// This class is created by the engine, and should not be instantiated /// or extended directly. - @pragma('vm:entry-point') _NativeEngineLayer._(); @override @@ -2889,10 +2868,8 @@ abstract class Path { PathMetrics computeMetrics({bool forceClosed = false}); } -@pragma('vm:entry-point') base class _NativePath extends NativeFieldWrapperClass1 implements Path { /// Create a new empty [Path] object. - @pragma('vm:entry-point') _NativePath() { _constructor(); } /// Avoids creating a new native backing for the path for methods that will @@ -3489,8 +3466,9 @@ class ColorFilter implements ImageFilter { _matrix = null, _type = _kTypeMode; - /// Construct a color filter that transforms a color by a 5x5 matrix, where - /// the fifth row is implicitly added in an identity configuration. + /// Construct a color filter from a 4x5 row-major matrix. The matrix is + /// interpreted as a 5x5 matrix, where the fifth row is the identity + /// configuration. /// /// Every pixel's color value, represented as an `[R, G, B, A]`, is matrix /// multiplied to create a new color: @@ -4197,7 +4175,11 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). + /// `color` must therefore only have two entries). Stop values less than 0.0 + /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded + /// down to 1.0. Each stop value must be greater than or equal to the previous + /// stop value. Stop values that do not meet this criteria will be rounded up + /// to the previous stop value. /// /// The behavior before `from` and after `to` is described by the `tileMode` /// argument. For details, see the [TileMode] enum. @@ -4239,7 +4221,11 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). + /// `color` must therefore only have two entries). Stop values less than 0.0 + /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded + /// down to 1.0. Each stop value must be greater than or equal to the previous + /// stop value. Stop values that do not meet this criteria will be rounded up + /// to the previous stop value. /// /// The behavior before and after the radius is described by the `tileMode` /// argument. For details, see the [TileMode] enum. @@ -4301,7 +4287,11 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). + /// `color` must therefore only have two entries). Stop values less than 0.0 + /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded + /// down to 1.0. Each stop value must be greater than or equal to the previous + /// stop value. Stop values that do not meet this criteria will be rounded up + /// to the previous stop value. /// /// The behavior before `startAngle` and after `endAngle` is described by the /// `tileMode` argument. For details, see the [TileMode] enum. @@ -5766,7 +5756,6 @@ abstract class Canvas { } base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { - @pragma('vm:entry-point') _NativeCanvas(PictureRecorder recorder, [ Rect? cullRect ]) { if (recorder.isRecording) { throw ArgumentError('"recorder" must not already be associated with another Canvas.'); @@ -6305,13 +6294,11 @@ abstract class Picture { int get approximateBytesUsed; } -@pragma('vm:entry-point') base class _NativePicture extends NativeFieldWrapperClass1 implements Picture { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a [Picture], use a [PictureRecorder]. - @pragma('vm:entry-point') _NativePicture._(); @override @@ -6410,7 +6397,6 @@ abstract class PictureRecorder { } base class _NativePictureRecorder extends NativeFieldWrapperClass1 implements PictureRecorder { - @pragma('vm:entry-point') _NativePictureRecorder() { _constructor(); } @Native(symbol: 'PictureRecorder::Create') @@ -6902,12 +6888,19 @@ base class _NativeImageDescriptor extends NativeFieldWrapperClass1 implements Im /// Generic callback signature, used by [_futurize]. typedef _Callback = void Function(T result); +/// Generic callback signature, used by [_futurizeWithError]. +typedef _CallbackWithError = void Function(T result, String? error); + /// Signature for a method that receives a [_Callback]. /// /// Return value should be null on success, and a string error message on /// failure. typedef _Callbacker = String? Function(_Callback callback); +/// Signature for a method that receives a [_CallbackWithError]. +/// See also: [_Callbacker] +typedef _CallbackerWithError = String? Function(_CallbackWithError callback); + // Converts a method that receives a value-returning callback to a method that // returns a Future. // @@ -6959,6 +6952,31 @@ Future _futurize(_Callbacker callbacker) { return completer.future; } +/// A variant of `_futurize` that can communicate specific errors. +Future _futurizeWithError(_CallbackerWithError callbacker) { + final Completer completer = Completer.sync(); + // If the callback synchronously throws an error, then synchronously + // rethrow that error instead of adding it to the completer. This + // prevents the Zone from receiving an uncaught exception. + bool isSync = true; + final String? error = callbacker((T? t, String? error) { + if (t != null) { + completer.complete(t); + } else { + if (isSync) { + throw Exception(error ?? 'operation failed'); + } else { + completer.completeError(Exception(error ?? 'operation failed')); + } + } + }); + isSync = false; + if (error != null) { + throw Exception(error); + } + return completer.future; +} + /// An exception thrown by [Canvas.drawImage] and related methods when drawing /// an [Image] created via [Picture.toImageSync] that is in an invalid state. /// diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 0b090b68c9a7b..485d36b6bdf34 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -214,7 +214,7 @@ void Canvas::getLocalClipBounds(Dart_Handle rect_handle) { void Canvas::drawColor(SkColor color, DlBlendMode blend_mode) { if (display_list_builder_) { - builder()->DrawColor(color, blend_mode); + builder()->DrawColor(DlColor(color), blend_mode); } } @@ -638,7 +638,7 @@ void Canvas::drawShadow(const CanvasPath* path, // that situation we bypass the canvas interface and inject the // shadow parameters directly into the underlying DisplayList. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - builder()->DrawShadow(path->path(), color, SafeNarrow(elevation), + builder()->DrawShadow(path->path(), DlColor(color), SafeNarrow(elevation), transparentOccluder, dpr); } } diff --git a/lib/ui/painting/color_filter.cc b/lib/ui/painting/color_filter.cc index 06d421b377369..f0d780d27208d 100644 --- a/lib/ui/painting/color_filter.cc +++ b/lib/ui/painting/color_filter.cc @@ -43,11 +43,11 @@ void ColorFilter::initMatrix(const tonic::Float32List& color_matrix) { } void ColorFilter::initLinearToSrgbGamma() { - filter_ = DlLinearToSrgbGammaColorFilter::instance; + filter_ = DlLinearToSrgbGammaColorFilter::kInstance; } void ColorFilter::initSrgbToLinearGamma() { - filter_ = DlSrgbToLinearGammaColorFilter::instance; + filter_ = DlSrgbToLinearGammaColorFilter::kInstance; } ColorFilter::~ColorFilter() = default; diff --git a/lib/ui/painting/image.h b/lib/ui/painting/image.h index 18a7200739813..ad3a7d7d18dbe 100644 --- a/lib/ui/painting/image.h +++ b/lib/ui/painting/image.h @@ -39,7 +39,7 @@ class CanvasImage final : public RefCountedDartWrappable { sk_sp image() const { return image_; } - void set_image(sk_sp image) { + void set_image(const sk_sp& image) { FML_DCHECK(image->isUIThreadSafe()); image_ = image; } diff --git a/lib/ui/painting/image_decoder_impeller.cc b/lib/ui/painting/image_decoder_impeller.cc index 02380442984af..9be5f0c631ee6 100644 --- a/lib/ui/painting/image_decoder_impeller.cc +++ b/lib/ui/painting/image_decoder_impeller.cc @@ -10,14 +10,13 @@ #include "flutter/fml/make_copyable.h" #include "flutter/fml/trace_event.h" #include "flutter/impeller/core/allocator.h" -#include "flutter/impeller/core/texture.h" #include "flutter/impeller/display_list/dl_image_impeller.h" #include "flutter/impeller/renderer/command_buffer.h" #include "flutter/impeller/renderer/context.h" -#include "flutter/lib/ui/painting/image_decoder_skia.h" #include "impeller/base/strings.h" #include "impeller/display_list/skia_conversions.h" #include "impeller/geometry/size.h" + #include "third_party/skia/include/core/SkAlphaType.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColorSpace.h" @@ -32,42 +31,6 @@ namespace flutter { -class MallocDeviceBuffer : public impeller::DeviceBuffer { - public: - explicit MallocDeviceBuffer(impeller::DeviceBufferDescriptor desc) - : impeller::DeviceBuffer(desc) { - data_ = static_cast(malloc(desc.size)); - } - - ~MallocDeviceBuffer() override { free(data_); } - - bool SetLabel(const std::string& label) override { return true; } - - bool SetLabel(const std::string& label, impeller::Range range) override { - return true; - } - - uint8_t* OnGetContents() const override { return data_; } - - bool OnCopyHostBuffer(const uint8_t* source, - impeller::Range source_range, - size_t offset) override { - memcpy(data_ + offset, source + source_range.offset, source_range.length); - return true; - } - - private: - uint8_t* data_; - - FML_DISALLOW_COPY_AND_ASSIGN(MallocDeviceBuffer); -}; - -#ifdef FML_OS_ANDROID -static constexpr bool kShouldUseMallocDeviceBuffer = true; -#else -static constexpr bool kShouldUseMallocDeviceBuffer = false; -#endif // FML_OS_ANDROID - namespace { /** * Loads the gamut as a set of three points (triangle). @@ -522,8 +485,7 @@ void ImageDecoderImpeller::Decode(fml::RefPtr descriptor, gpu_disabled_switch]() { sk_sp image; std::string decode_error; - if (!kShouldUseMallocDeviceBuffer && - context->GetCapabilities()->SupportsBufferToTextureBlits()) { + if (context->GetCapabilities()->SupportsBufferToTextureBlits()) { std::tie(image, decode_error) = UploadTextureToPrivate( context, bitmap_result.device_buffer, bitmap_result.image_info, bitmap_result.sk_bitmap, gpu_disabled_switch); @@ -567,9 +529,7 @@ bool ImpellerAllocator::allocPixelRef(SkBitmap* bitmap) { (bitmap->width() * bitmap->bytesPerPixel()); std::shared_ptr device_buffer = - kShouldUseMallocDeviceBuffer - ? std::make_shared(descriptor) - : allocator_->CreateBuffer(descriptor); + allocator_->CreateBuffer(descriptor); struct ImpellerPixelRef final : public SkPixelRef { ImpellerPixelRef(int w, int h, void* s, size_t r) diff --git a/lib/ui/painting/image_dispose_unittests.cc b/lib/ui/painting/image_dispose_unittests.cc index 0f8bb6d027062..93600ca83c93d 100644 --- a/lib/ui/painting/image_dispose_unittests.cc +++ b/lib/ui/painting/image_dispose_unittests.cc @@ -5,6 +5,7 @@ #define FML_USED_ON_EMBEDDER #include "flutter/common/task_runners.h" +#include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/painting/image.h" @@ -57,6 +58,10 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { }; Settings settings = CreateSettingsForFixture(); + fml::CountDownLatch frame_latch{2}; + settings.frame_rasterized_callback = [&frame_latch](const FrameTiming& t) { + frame_latch.CountDown(); + }; auto task_runner = CreateNewThread(); TaskRunners task_runners("test", // label GetCurrentTaskRunner(), // platform @@ -83,12 +88,15 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { shell->RunEngine(std::move(configuration), [&](auto result) { ASSERT_EQ(result, Engine::RunStatus::Success); }); - message_latch_.Wait(); ASSERT_TRUE(current_display_list_); ASSERT_TRUE(current_image_); + // Wait for 2 frames to be rasterized. The 2nd frame releases resources of the + // 1st frame. + frame_latch.Wait(); + // Force a drain the SkiaUnrefQueue. The engine does this normally as frames // pump, but we force it here to make the test more deterministic. message_latch_.Reset(); diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index 93ccb4fa2c8a1..69d3bd38f87ba 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -11,6 +11,7 @@ #include "flutter/common/task_runners.h" #include "flutter/fml/build_config.h" #include "flutter/fml/make_copyable.h" +#include "flutter/fml/status_or.h" #include "flutter/fml/trace_event.h" #include "flutter/lib/ui/painting/image.h" #if IMPELLER_SUPPORTS_RENDERING @@ -34,40 +35,33 @@ class Context; namespace flutter { namespace { -// This must be kept in sync with the enum in painting.dart -enum ImageByteFormat { - kRawRGBA, - kRawStraightRGBA, - kRawUnmodified, - kRawExtendedRgba128, - kPNG, -}; - void FinalizeSkData(void* isolate_callback_data, void* peer) { SkData* buffer = reinterpret_cast(peer); buffer->unref(); } void InvokeDataCallback(std::unique_ptr callback, - sk_sp buffer) { + fml::StatusOr>&& buffer) { std::shared_ptr dart_state = callback->dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); - if (!buffer) { - DartInvoke(callback->value(), {Dart_Null()}); + if (!buffer.ok()) { + std::string error_copy(buffer.status().message()); + Dart_Handle dart_string = ToDart(error_copy); + DartInvoke(callback->value(), {Dart_Null(), dart_string}); return; } // Skia will not modify the buffer, and it is backed by memory that is // read/write, so Dart can be given direct access to the buffer through an // external Uint8List. - void* bytes = const_cast(buffer->data()); - const intptr_t length = buffer->size(); - void* peer = reinterpret_cast(buffer.release()); + void* bytes = const_cast(buffer.value()->data()); + const intptr_t length = buffer.value()->size(); + void* peer = reinterpret_cast(buffer.value().release()); Dart_Handle dart_data = Dart_NewExternalTypedDataWithFinalizer( Dart_TypedData_kUint8, bytes, length, peer, length, FinalizeSkData); - DartInvoke(callback->value(), {dart_data}); + DartInvoke(callback->value(), {dart_data, Dart_Null()}); } sk_sp CopyImageByteData(const sk_sp& raster_image, @@ -107,44 +101,6 @@ sk_sp CopyImageByteData(const sk_sp& raster_image, return SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize()); } -sk_sp EncodeImage(const sk_sp& raster_image, - ImageByteFormat format) { - TRACE_EVENT0("flutter", __FUNCTION__); - - if (!raster_image) { - return nullptr; - } - - switch (format) { - case kPNG: { - auto png_image = SkPngEncoder::Encode(nullptr, raster_image.get(), {}); - - if (png_image == nullptr) { - FML_LOG(ERROR) << "Could not convert raster image to PNG."; - return nullptr; - }; - return png_image; - } - case kRawRGBA: - return CopyImageByteData(raster_image, kRGBA_8888_SkColorType, - kPremul_SkAlphaType); - - case kRawStraightRGBA: - return CopyImageByteData(raster_image, kRGBA_8888_SkColorType, - kUnpremul_SkAlphaType); - - case kRawUnmodified: - return CopyImageByteData(raster_image, raster_image->colorType(), - raster_image->alphaType()); - case kRawExtendedRgba128: - return CopyImageByteData(raster_image, kRGBA_F32_SkColorType, - kUnpremul_SkAlphaType); - } - - FML_LOG(ERROR) << "Unknown error encoding image."; - return nullptr; -} - void EncodeImageAndInvokeDataCallback( const sk_sp& image, std::unique_ptr callback, @@ -157,21 +113,30 @@ void EncodeImageAndInvokeDataCallback( const std::shared_ptr& is_gpu_disabled_sync_switch, const std::shared_ptr& impeller_context, bool is_impeller_enabled) { - auto callback_task = fml::MakeCopyable( - [callback = std::move(callback)](sk_sp encoded) mutable { + auto callback_task = + fml::MakeCopyable([callback = std::move(callback)]( + fml::StatusOr>&& encoded) mutable { InvokeDataCallback(std::move(callback), std::move(encoded)); }); // The static leak checker gets confused by the use of fml::MakeCopyable in // EncodeImage. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - auto encode_task = [callback_task = std::move(callback_task), format, - ui_task_runner](const sk_sp& raster_image) { - sk_sp encoded = EncodeImage(raster_image, format); - ui_task_runner->PostTask([callback_task = callback_task, - encoded = std::move(encoded)]() mutable { - callback_task(std::move(encoded)); - }); - }; + auto encode_task = + [callback_task = std::move(callback_task), format, + ui_task_runner](const fml::StatusOr>& raster_image) { + if (raster_image.ok()) { + sk_sp encoded = EncodeImage(raster_image.value(), format); + ui_task_runner->PostTask([callback_task = callback_task, + encoded = std::move(encoded)]() mutable { + callback_task(std::move(encoded)); + }); + } else { + ui_task_runner->PostTask([callback_task = callback_task, + raster_image = raster_image]() mutable { + callback_task(raster_image.status()); + }); + } + }; FML_DCHECK(image); #if IMPELLER_SUPPORTS_RENDERING @@ -229,4 +194,42 @@ Dart_Handle EncodeImage(CanvasImage* canvas_image, return Dart_Null(); } +sk_sp EncodeImage(const sk_sp& raster_image, + ImageByteFormat format) { + TRACE_EVENT0("flutter", __FUNCTION__); + + if (!raster_image) { + return nullptr; + } + + switch (format) { + case kPNG: { + auto png_image = SkPngEncoder::Encode(nullptr, raster_image.get(), {}); + + if (png_image == nullptr) { + FML_LOG(ERROR) << "Could not convert raster image to PNG."; + return nullptr; + }; + return png_image; + } + case kRawRGBA: + return CopyImageByteData(raster_image, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + case kRawStraightRGBA: + return CopyImageByteData(raster_image, kRGBA_8888_SkColorType, + kUnpremul_SkAlphaType); + + case kRawUnmodified: + return CopyImageByteData(raster_image, raster_image->colorType(), + raster_image->alphaType()); + case kRawExtendedRgba128: + return CopyImageByteData(raster_image, kRGBA_F32_SkColorType, + kUnpremul_SkAlphaType); + } + + FML_LOG(ERROR) << "Unknown error encoding image."; + return nullptr; +} + } // namespace flutter diff --git a/lib/ui/painting/image_encoding.h b/lib/ui/painting/image_encoding.h index df7801302d9be..be852043022ae 100644 --- a/lib/ui/painting/image_encoding.h +++ b/lib/ui/painting/image_encoding.h @@ -5,16 +5,29 @@ #ifndef FLUTTER_LIB_UI_PAINTING_IMAGE_ENCODING_H_ #define FLUTTER_LIB_UI_PAINTING_IMAGE_ENCODING_H_ +#include "third_party/skia/include/core/SkImage.h" #include "third_party/tonic/dart_library_natives.h" namespace flutter { class CanvasImage; +// This must be kept in sync with the enum in painting.dart +enum ImageByteFormat { + kRawRGBA, + kRawStraightRGBA, + kRawUnmodified, + kRawExtendedRgba128, + kPNG, +}; + Dart_Handle EncodeImage(CanvasImage* canvas_image, int format, Dart_Handle callback_handle); +sk_sp EncodeImage(const sk_sp& raster_image, + ImageByteFormat format); + } // namespace flutter #endif // FLUTTER_LIB_UI_PAINTING_IMAGE_ENCODING_H_ diff --git a/lib/ui/painting/image_encoding_impeller.cc b/lib/ui/painting/image_encoding_impeller.cc index 445bc2bfa5eb2..0801f7930d12d 100644 --- a/lib/ui/painting/image_encoding_impeller.cc +++ b/lib/ui/painting/image_encoding_impeller.cc @@ -56,37 +56,87 @@ sk_sp ConvertBufferToSkImage( return raster_image; } -void DoConvertImageToRasterImpeller( +[[nodiscard]] fml::Status DoConvertImageToRasterImpeller( const sk_sp& dl_image, - std::function)> encode_task, + const std::function>)>& encode_task, const std::shared_ptr& is_gpu_disabled_sync_switch, const std::shared_ptr& impeller_context) { + fml::Status result; is_gpu_disabled_sync_switch->Execute( fml::SyncSwitch::Handlers() - .SetIfTrue([&encode_task] { encode_task(nullptr); }) + .SetIfTrue([&result] { + result = + fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable."); + }) .SetIfFalse([&dl_image, &encode_task, &impeller_context] { ImageEncodingImpeller::ConvertDlImageToSkImage( - dl_image, std::move(encode_task), impeller_context); + dl_image, encode_task, impeller_context); })); + return result; +} + +/// Same as `DoConvertImageToRasterImpeller` but it will attempt to retry the +/// operation if `DoConvertImageToRasterImpeller` returns kUnavailable when the +/// GPU becomes available again. +void DoConvertImageToRasterImpellerWithRetry( + const sk_sp& dl_image, + std::function>)>&& encode_task, + const std::shared_ptr& is_gpu_disabled_sync_switch, + const std::shared_ptr& impeller_context, + const fml::RefPtr& retry_runner) { + fml::Status status = DoConvertImageToRasterImpeller( + dl_image, encode_task, is_gpu_disabled_sync_switch, impeller_context); + if (!status.ok()) { + // If the conversion failed because of the GPU is unavailable, store the + // task on the Context so it can be executed when the GPU becomes available. + if (status.code() == fml::StatusCode::kUnavailable) { + impeller_context->StoreTaskForGPU( + [dl_image, encode_task = std::move(encode_task), + is_gpu_disabled_sync_switch, impeller_context, + retry_runner]() mutable { + auto retry_task = [dl_image, encode_task = std::move(encode_task), + is_gpu_disabled_sync_switch, impeller_context] { + fml::Status retry_status = DoConvertImageToRasterImpeller( + dl_image, encode_task, is_gpu_disabled_sync_switch, + impeller_context); + if (!retry_status.ok()) { + // The retry failed for some reason, maybe the GPU became + // unavailable again. Don't retry again, just fail in this case. + encode_task(retry_status); + } + }; + // If a `retry_runner` is specified, post the retry to it, otherwise + // execute it directly. + if (retry_runner) { + retry_runner->PostTask(retry_task); + } else { + retry_task(); + } + }); + } else { + // Pass on errors that are not `kUnavailable`. + encode_task(status); + } + } } } // namespace void ImageEncodingImpeller::ConvertDlImageToSkImage( const sk_sp& dl_image, - std::function)> encode_task, + std::function>)> encode_task, const std::shared_ptr& impeller_context) { auto texture = dl_image->impeller_texture(); if (impeller_context == nullptr) { - FML_LOG(ERROR) << "Impeller context was null."; - encode_task(nullptr); + encode_task(fml::Status(fml::StatusCode::kFailedPrecondition, + "Impeller context was null.")); return; } if (texture == nullptr) { - FML_LOG(ERROR) << "Image was null."; - encode_task(nullptr); + encode_task( + fml::Status(fml::StatusCode::kFailedPrecondition, "Image was null.")); return; } @@ -94,14 +144,14 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage( auto color_type = ToSkColorType(texture->GetTextureDescriptor().format); if (dimensions.isEmpty()) { - FML_LOG(ERROR) << "Image dimensions were empty."; - encode_task(nullptr); + encode_task(fml::Status(fml::StatusCode::kFailedPrecondition, + "Image dimensions were empty.")); return; } if (!color_type.has_value()) { - FML_LOG(ERROR) << "Failed to get color type from pixel format."; - encode_task(nullptr); + encode_task(fml::Status(fml::StatusCode::kUnimplemented, + "Failed to get color type from pixel format.")); return; } @@ -121,7 +171,7 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage( encode_task = std::move(encode_task)]( impeller::CommandBuffer::Status status) { if (status != impeller::CommandBuffer::Status::kCompleted) { - encode_task(nullptr); + encode_task(fml::Status(fml::StatusCode::kUnknown, "")); return; } auto sk_image = ConvertBufferToSkImage(buffer, color_type, dimensions); @@ -135,14 +185,14 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage( void ImageEncodingImpeller::ConvertImageToRaster( const sk_sp& dl_image, - std::function)> encode_task, + std::function>)> encode_task, const fml::RefPtr& raster_task_runner, const fml::RefPtr& io_task_runner, const std::shared_ptr& is_gpu_disabled_sync_switch, const std::shared_ptr& impeller_context) { auto original_encode_task = std::move(encode_task); encode_task = [original_encode_task = std::move(original_encode_task), - io_task_runner](sk_sp image) mutable { + io_task_runner](fml::StatusOr> image) mutable { fml::TaskRunner::RunNowOrPostTask( io_task_runner, [original_encode_task = std::move(original_encode_task), @@ -150,18 +200,20 @@ void ImageEncodingImpeller::ConvertImageToRaster( }; if (dl_image->owning_context() != DlImage::OwningContext::kRaster) { - DoConvertImageToRasterImpeller(dl_image, std::move(encode_task), - is_gpu_disabled_sync_switch, - impeller_context); + DoConvertImageToRasterImpellerWithRetry(dl_image, std::move(encode_task), + is_gpu_disabled_sync_switch, + impeller_context, + /*retry_runner=*/nullptr); return; } raster_task_runner->PostTask([dl_image, encode_task = std::move(encode_task), io_task_runner, is_gpu_disabled_sync_switch, - impeller_context]() mutable { - DoConvertImageToRasterImpeller(dl_image, std::move(encode_task), - is_gpu_disabled_sync_switch, - impeller_context); + impeller_context, + raster_task_runner]() mutable { + DoConvertImageToRasterImpellerWithRetry( + dl_image, std::move(encode_task), is_gpu_disabled_sync_switch, + impeller_context, raster_task_runner); }); } diff --git a/lib/ui/painting/image_encoding_impeller.h b/lib/ui/painting/image_encoding_impeller.h index 18fc3d90650bf..84894407854a3 100644 --- a/lib/ui/painting/image_encoding_impeller.h +++ b/lib/ui/painting/image_encoding_impeller.h @@ -7,6 +7,7 @@ #include "flutter/common/task_runners.h" #include "flutter/display_list/image/dl_image.h" +#include "flutter/fml/status_or.h" #include "flutter/fml/synchronization/sync_switch.h" namespace impeller { @@ -26,14 +27,14 @@ class ImageEncodingImpeller { /// Visible for testing. static void ConvertDlImageToSkImage( const sk_sp& dl_image, - std::function)> encode_task, + std::function>)> encode_task, const std::shared_ptr& impeller_context); /// Converts a DlImage to a SkImage. /// `encode_task` is executed with the resulting `SkImage`. static void ConvertImageToRaster( const sk_sp& dl_image, - std::function)> encode_task, + std::function>)> encode_task, const fml::RefPtr& raster_task_runner, const fml::RefPtr& io_task_runner, const std::shared_ptr& is_gpu_disabled_sync_switch, diff --git a/lib/ui/painting/image_encoding_unittests.cc b/lib/ui/painting/image_encoding_unittests.cc index 65fb4df75e410..8de593ce4bb73 100644 --- a/lib/ui/painting/image_encoding_unittests.cc +++ b/lib/ui/painting/image_encoding_unittests.cc @@ -23,6 +23,8 @@ // CREATE_NATIVE_ENTRY is leaky by design // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) +#pragma GCC diagnostic ignored "-Wunreachable-code" + namespace flutter { namespace testing { @@ -224,6 +226,122 @@ std::shared_ptr MakeConvertDlImageToSkImageContext( } } // namespace +TEST_F(ShellTest, EncodeImageRetries) { +#ifndef FML_OS_MACOSX + // Only works on macos currently. + GTEST_SKIP(); +#endif + Settings settings = CreateSettingsForFixture(); + settings.enable_impeller = true; + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + std::unique_ptr shell = CreateShell({ + .settings = settings, + .task_runners = task_runners, + }); + + auto turn_off_gpu = [&](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + bool value = true; + ASSERT_TRUE(Dart_IsBoolean(handle)); + Dart_BooleanValue(handle, &value); + TurnOffGPU(shell.get(), value); + }; + + AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu)); + + auto validate_not_null = [&](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + EXPECT_FALSE(Dart_IsNull(handle)); + message_latch.Signal(); + }; + + AddNativeCallback("ValidateNotNull", CREATE_NATIVE_ENTRY(validate_not_null)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("toByteDataRetries"); + + shell->RunEngine(std::move(configuration), [&](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch.Wait(); + DestroyShell(std::move(shell), task_runners); +} + +TEST_F(ShellTest, EncodeImageFailsWithoutGPUImpeller) { +#ifndef FML_OS_MACOSX + // Only works on macos currently. + GTEST_SKIP(); +#endif + Settings settings = CreateSettingsForFixture(); + settings.enable_impeller = true; + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + auto native_validate_error = [&](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + + EXPECT_FALSE(Dart_IsNull(handle)); + + message_latch.Signal(); + }; + + AddNativeCallback("ValidateError", + CREATE_NATIVE_ENTRY(native_validate_error)); + + std::unique_ptr shell = CreateShell({ + .settings = settings, + .task_runners = task_runners, + }); + + auto turn_off_gpu = [&](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + bool value = true; + ASSERT_TRUE(Dart_IsBoolean(handle)); + Dart_BooleanValue(handle, &value); + TurnOffGPU(shell.get(), true); + }; + + AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu)); + + auto flush_awaiting_tasks = [&](Dart_NativeArguments args) { + task_runners.GetIOTaskRunner()->PostTask([&] { + std::shared_ptr impeller_context = + shell->GetIOManager()->GetImpellerContext(); + // This will cause the stored tasks to overflow and start throwing them + // away. + for (int i = 0; i < impeller::Context::kMaxTasksAwaitingGPU; ++i) { + impeller_context->StoreTaskForGPU([] {}); + } + }); + }; + + AddNativeCallback("FlushGpuAwaitingTasks", + CREATE_NATIVE_ENTRY(flush_awaiting_tasks)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("toByteDataWithoutGPU"); + + shell->RunEngine(std::move(configuration), [&](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch.Wait(); + DestroyShell(std::move(shell), task_runners); +} + TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage16Float) { sk_sp image(new MockDlImage()); EXPECT_CALL(*image, dimensions) @@ -238,13 +356,14 @@ TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage16Float) { bool did_call = false; ImageEncodingImpeller::ConvertDlImageToSkImage( image, - [&did_call](const sk_sp& image) { + [&did_call](const fml::StatusOr>& image) { did_call = true; - ASSERT_TRUE(image); - EXPECT_EQ(100, image->width()); - EXPECT_EQ(100, image->height()); - EXPECT_EQ(kRGBA_F16_SkColorType, image->colorType()); - EXPECT_EQ(nullptr, image->colorSpace()); + ASSERT_TRUE(image.ok()); + ASSERT_TRUE(image.value()); + EXPECT_EQ(100, image.value()->width()); + EXPECT_EQ(100, image.value()->height()); + EXPECT_EQ(kRGBA_F16_SkColorType, image.value()->colorType()); + EXPECT_EQ(nullptr, image.value()->colorSpace()); }, context); EXPECT_TRUE(did_call); @@ -264,17 +383,41 @@ TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage10XR) { bool did_call = false; ImageEncodingImpeller::ConvertDlImageToSkImage( image, - [&did_call](const sk_sp& image) { + [&did_call](const fml::StatusOr>& image) { did_call = true; - ASSERT_TRUE(image); - EXPECT_EQ(100, image->width()); - EXPECT_EQ(100, image->height()); - EXPECT_EQ(kBGR_101010x_XR_SkColorType, image->colorType()); - EXPECT_EQ(nullptr, image->colorSpace()); + ASSERT_TRUE(image.ok()); + ASSERT_TRUE(image.value()); + EXPECT_EQ(100, image.value()->width()); + EXPECT_EQ(100, image.value()->height()); + EXPECT_EQ(kBGR_101010x_XR_SkColorType, image.value()->colorType()); + EXPECT_EQ(nullptr, image.value()->colorSpace()); }, context); EXPECT_TRUE(did_call); } + +TEST(ImageEncodingImpellerTest, PngEncoding10XR) { + int width = 100; + int height = 100; + SkImageInfo info = SkImageInfo::Make( + width, height, kBGR_101010x_XR_SkColorType, kUnpremul_SkAlphaType); + + auto surface = SkSurfaces::Raster(info); + SkCanvas* canvas = surface->getCanvas(); + + SkPaint paint; + paint.setColor(SK_ColorBLUE); + paint.setAntiAlias(true); + + canvas->clear(SK_ColorWHITE); + canvas->drawCircle(width / 2, height / 2, 100, paint); + + sk_sp image = surface->makeImageSnapshot(); + + sk_sp png = EncodeImage(image, ImageByteFormat::kPNG); + EXPECT_TRUE(png); +} + #endif // IMPELLER_SUPPORTS_RENDERING } // namespace testing diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 592643bee7d82..0614f3f966cea 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -137,7 +137,7 @@ const DlPaint* Paint::paint(DlPaint& paint, if (flags.applies_alpha_or_color()) { uint32_t encoded_color = uint_data[kColorIndex]; - paint.setColor(encoded_color ^ kColorDefault); + paint.setColor(DlColor(encoded_color ^ kColorDefault)); } if (flags.applies_blend()) { @@ -248,7 +248,7 @@ void Paint::toDlPaint(DlPaint& paint) const { paint.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0); uint32_t encoded_color = uint_data[kColorIndex]; - paint.setColor(encoded_color ^ kColorDefault); + paint.setColor(DlColor(encoded_color ^ kColorDefault)); uint32_t encoded_blend_mode = uint_data[kBlendModeIndex]; uint32_t blend_mode = encoded_blend_mode ^ kBlendModeDefault; diff --git a/lib/ui/painting/paint_unittests.cc b/lib/ui/painting/paint_unittests.cc index 1324568cb2347..87447fa548124 100644 --- a/lib/ui/painting/paint_unittests.cc +++ b/lib/ui/painting/paint_unittests.cc @@ -50,9 +50,9 @@ TEST_F(ShellTest, ConvertPaintToDlPaint) { DestroyShell(std::move(shell), task_runners); ASSERT_EQ(dl_paint.getBlendMode(), DlBlendMode::kModulate); - ASSERT_EQ(static_cast(dl_paint.getColor()), 0x11223344u); + ASSERT_EQ(static_cast(dl_paint.getColor().argb()), 0x11223344u); ASSERT_EQ(*dl_paint.getColorFilter(), - DlBlendColorFilter(0x55667788, DlBlendMode::kXor)); + DlBlendColorFilter(DlColor(0x55667788), DlBlendMode::kXor)); ASSERT_EQ(*dl_paint.getMaskFilter(), DlBlurMaskFilter(DlBlurStyle::kInner, 0.75)); ASSERT_EQ(dl_paint.getDrawStyle(), DlDrawStyle::kStroke); diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 0bd387270ba13..66e68b88be56c 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -308,6 +308,28 @@ class PlatformDispatcher { _invoke(onMetricsChanged, _onMetricsChangedZone); } + // A debug-only variable that stores the [FlutterView]s for which + // [FlutterView.render] has already been called during the current + // [onBeginFrame]/[onDrawFrame] callback sequence. + // + // It is null outside the scope of those callbacks indicating that calls to + // [FlutterView.render] must be ignored. Furthermore, if a given [FlutterView] + // is already present in this set when its [FlutterView.render] is called + // again, that call must be ignored as a duplicate. + // + // Between [onBeginFrame] and [onDrawFrame] the properties value is + // temporarily stored in `_renderedViewsBetweenCallbacks` so that it survives + // the gap between the two callbacks. + // + // In release build, this variable is null, and therefore the calling rule is + // not enforced. This is because the check might hurt cold startup delay; + // see https://github.com/flutter/engine/pull/46919. + Set? _debugRenderedViews; + // A debug-only variable that temporarily stores the `_renderedViews` value + // between `_beginFrame` and `_drawFrame`. + // + // In release build, this variable is null. + Set? _debugRenderedViewsBetweenCallbacks; /// A callback invoked when any view begins a frame. /// @@ -329,11 +351,26 @@ class PlatformDispatcher { // Called from the engine, via hooks.dart void _beginFrame(int microseconds) { + assert(_debugRenderedViews == null); + assert(_debugRenderedViewsBetweenCallbacks == null); + assert(() { + _debugRenderedViews = {}; + return true; + }()); + _invoke1( onBeginFrame, _onBeginFrameZone, Duration(microseconds: microseconds), ); + + assert(_debugRenderedViews != null); + assert(_debugRenderedViewsBetweenCallbacks == null); + assert(() { + _debugRenderedViewsBetweenCallbacks = _debugRenderedViews; + _debugRenderedViews = null; + return true; + }()); } /// A callback that is invoked for each frame after [onBeginFrame] has @@ -351,7 +388,22 @@ class PlatformDispatcher { // Called from the engine, via hooks.dart void _drawFrame() { + assert(_debugRenderedViews == null); + assert(_debugRenderedViewsBetweenCallbacks != null); + assert(() { + _debugRenderedViews = _debugRenderedViewsBetweenCallbacks; + _debugRenderedViewsBetweenCallbacks = null; + return true; + }()); + _invoke(onDrawFrame, _onDrawFrameZone); + + assert(_debugRenderedViews != null); + assert(_debugRenderedViewsBetweenCallbacks == null); + assert(() { + _debugRenderedViews = null; + return true; + }()); } /// A callback that is invoked when pointer data is available. diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index c619ab4e596c9..bb8d58f10087e 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -848,9 +848,7 @@ abstract class SemanticsUpdateBuilder { SemanticsUpdate build(); } -@pragma('vm:entry-point') base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implements SemanticsUpdateBuilder { - @pragma('vm:entry-point') _NativeSemanticsUpdateBuilder() { _constructor(); } @Native(symbol: 'SemanticsUpdateBuilder::Create') @@ -1039,13 +1037,11 @@ abstract class SemanticsUpdate { void dispose(); } -@pragma('vm:entry-point') base class _NativeSemanticsUpdate extends NativeFieldWrapperClass1 implements SemanticsUpdate { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. - @pragma('vm:entry-point') _NativeSemanticsUpdate._(); @override diff --git a/lib/ui/text.dart b/lib/ui/text.dart index b35ab8ad3ba9e..8bf5a8ccec707 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -2314,7 +2314,6 @@ enum TextDirection { /// A rectangle enclosing a run of text. /// /// This is similar to [Rect] but includes an inherent [TextDirection]. -@pragma('vm:entry-point') class TextBox { /// Creates an object that describes a box containing text. const TextBox.fromLTRBD( @@ -3004,13 +3003,11 @@ abstract class Paragraph { bool get debugDisposed; } -@pragma('vm:entry-point') base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragraph { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a [Paragraph] object, use a [ParagraphBuilder]. - @pragma('vm:entry-point') _NativeParagraph._(); bool _needsLayout = true; @@ -3322,7 +3319,6 @@ abstract class ParagraphBuilder { } base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements ParagraphBuilder { - @pragma('vm:entry-point') _NativeParagraphBuilder(ParagraphStyle style) : _defaultLeadingDistribution = style._leadingDistribution { List? strutFontFamilies; diff --git a/lib/ui/text/font_collection.cc b/lib/ui/text/font_collection.cc index 5a4afd334fc12..86225d4ad8df9 100644 --- a/lib/ui/text/font_collection.cc +++ b/lib/ui/text/font_collection.cc @@ -22,6 +22,9 @@ #include "third_party/tonic/typed_data/typed_list.h" #include "txt/asset_font_manager.h" #include "txt/test_font_manager.h" +#if FML_OS_MACOSX || FML_OS_IOS +#include "txt/platform_mac.h" +#endif namespace flutter { @@ -63,6 +66,9 @@ void FontCollection::SetupDefaultFontManager( // Structure described in https://docs.flutter.dev/cookbook/design/fonts void FontCollection::RegisterFonts( const std::shared_ptr& asset_manager) { +#if FML_OS_MACOSX || FML_OS_IOS + RegisterSystemFonts(*dynamic_font_manager_); +#endif std::unique_ptr manifest_mapping = asset_manager->GetAsMapping("FontManifest.json"); if (manifest_mapping == nullptr) { diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 26a258cfa96c1..022227be017c4 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -353,10 +353,23 @@ class FlutterView { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene scene) => _render(scene as _NativeScene); + void render(Scene scene) { + // Duplicated calls or calls outside of onBeginFrame/onDrawFrame (indicated + // by _debugRenderedViews being null) are ignored. See _debugRenderedViews. + // TODO(dkwingsmt): We should change this skip into an assertion. + // https://github.com/flutter/flutter/issues/137073 + bool validRender = true; + assert(() { + validRender = platformDispatcher._debugRenderedViews?.add(this) ?? false; + return true; + }()); + if (validRender) { + _render(viewId, scene as _NativeScene); + } + } - @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') - external static void _render(_NativeScene scene); + @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') + external static void _render(int viewId, _NativeScene scene); /// Change the retained semantics data about this [FlutterView]. /// diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index d19c80a7a8028..082756d667976 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -449,9 +449,10 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } -void PlatformConfigurationNativeApi::Render(Scene* scene) { +void PlatformConfigurationNativeApi::Render(int64_t view_id, Scene* scene) { UIDartState::ThrowIfUIOperationsProhibited(); - UIDartState::Current()->platform_configuration()->client()->Render(scene); + UIDartState::Current()->platform_configuration()->client()->Render(view_id, + scene); } void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) { diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 7965ba10a7b91..9c762b460c05c 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -69,7 +69,7 @@ class PlatformConfigurationClient { /// @brief Updates the client's rendering on the GPU with the newly /// provided Scene. /// - virtual void Render(Scene* scene) = 0; + virtual void Render(int64_t view_id, Scene* scene) = 0; //-------------------------------------------------------------------------- /// @brief Receives an updated semantics tree from the Framework. @@ -557,7 +557,7 @@ class PlatformConfigurationNativeApi { static void ScheduleFrame(); - static void Render(Scene* scene); + static void Render(int64_t view_id, Scene* scene); static void UpdateSemantics(SemanticsUpdate* update); diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index 7410caeb66d6c..916f47c7026e8 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -15,8 +15,166 @@ #include "flutter/shell/common/shell_test.h" #include "flutter/shell/common/thread_host.h" #include "flutter/testing/testing.h" +#include "gmock/gmock.h" namespace flutter { + +namespace { + +static constexpr int64_t kImplicitViewId = 0; + +static void PostSync(const fml::RefPtr& task_runner, + const fml::closure& task) { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] { + task(); + latch.Signal(); + }); + latch.Wait(); +} + +class MockRuntimeDelegate : public RuntimeDelegate { + public: + MOCK_METHOD(std::string, DefaultRouteName, (), (override)); + MOCK_METHOD(void, ScheduleFrame, (bool), (override)); + MOCK_METHOD(void, + Render, + (int64_t, std::unique_ptr, float), + (override)); + MOCK_METHOD(void, + UpdateSemantics, + (SemanticsNodeUpdates, CustomAccessibilityActionUpdates), + (override)); + MOCK_METHOD(void, + HandlePlatformMessage, + (std::unique_ptr), + (override)); + MOCK_METHOD(FontCollection&, GetFontCollection, (), (override)); + MOCK_METHOD(std::shared_ptr, GetAssetManager, (), (override)); + MOCK_METHOD(void, OnRootIsolateCreated, (), (override)); + MOCK_METHOD(void, + UpdateIsolateDescription, + (const std::string, int64_t), + (override)); + MOCK_METHOD(void, SetNeedsReportTimings, (bool), (override)); + MOCK_METHOD(std::unique_ptr>, + ComputePlatformResolvedLocale, + (const std::vector&), + (override)); + MOCK_METHOD(void, RequestDartDeferredLibrary, (intptr_t), (override)); + MOCK_METHOD(std::weak_ptr, + GetPlatformMessageHandler, + (), + (const, override)); + MOCK_METHOD(void, SendChannelUpdate, (std::string, bool), (override)); + MOCK_METHOD(double, + GetScaledFontSize, + (double font_size, int configuration_id), + (const, override)); +}; + +class MockPlatformMessageHandler : public PlatformMessageHandler { + public: + MOCK_METHOD(void, + HandlePlatformMessage, + (std::unique_ptr message), + (override)); + MOCK_METHOD(bool, + DoesHandlePlatformMessageOnPlatformThread, + (), + (const, override)); + MOCK_METHOD(void, + InvokePlatformMessageResponseCallback, + (int response_id, std::unique_ptr mapping), + (override)); + MOCK_METHOD(void, + InvokePlatformMessageEmptyResponseCallback, + (int response_id), + (override)); +}; + +// A class that can launch a RuntimeController with the specified +// RuntimeDelegate. +// +// To use this class, contruct this class with Create, call LaunchRootIsolate, +// and use the controller with ControllerTaskSync(). +class RuntimeControllerContext { + public: + using ControllerCallback = std::function; + + [[nodiscard]] static std::unique_ptr Create( + Settings settings, // + const TaskRunners& task_runners, // + RuntimeDelegate& client) { + auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings); + FML_CHECK(vm) << "Must be able to initialize the VM."; + // Construct the class with `new` because `make_unique` has no access to the + // private constructor. + RuntimeControllerContext* raw_pointer = new RuntimeControllerContext( + settings, task_runners, client, std::move(vm), isolate_snapshot); + return std::unique_ptr(raw_pointer); + } + + ~RuntimeControllerContext() { + PostSync(task_runners_.GetUITaskRunner(), + [&]() { runtime_controller_.reset(); }); + } + + // Launch the root isolate. The post_launch callback will be executed in the + // same UI task, which can be used to create initial views. + void LaunchRootIsolate(RunConfiguration& configuration, + ControllerCallback post_launch) { + PostSync(task_runners_.GetUITaskRunner(), [&]() { + bool launch_success = runtime_controller_->LaunchRootIsolate( + settings_, // + []() {}, // + configuration.GetEntrypoint(), // + configuration.GetEntrypointLibrary(), // + configuration.GetEntrypointArgs(), // + configuration.TakeIsolateConfiguration()); // + ASSERT_TRUE(launch_success); + post_launch(*runtime_controller_); + }); + } + + // Run a task that operates the RuntimeController on the UI thread, and wait + // for the task to end. + void ControllerTaskSync(ControllerCallback task) { + ASSERT_TRUE(runtime_controller_); + ASSERT_TRUE(task); + PostSync(task_runners_.GetUITaskRunner(), + [&]() { task(*runtime_controller_); }); + } + + private: + RuntimeControllerContext(const Settings& settings, + const TaskRunners& task_runners, + RuntimeDelegate& client, + DartVMRef vm, + fml::RefPtr isolate_snapshot) + : settings_(settings), + task_runners_(task_runners), + isolate_snapshot_(std::move(isolate_snapshot)), + vm_(std::move(vm)), + runtime_controller_(std::make_unique( + client, + &vm_, + std::move(isolate_snapshot_), + settings.idle_notification_callback, // idle notification callback + flutter::PlatformData(), // platform data + settings.isolate_create_callback, // isolate create callback + settings.isolate_shutdown_callback, // isolate shutdown callback + settings.persistent_isolate_data, // persistent isolate data + UIDartState::Context{task_runners})) {} + + Settings settings_; + TaskRunners task_runners_; + fml::RefPtr isolate_snapshot_; + DartVMRef vm_; + std::unique_ptr runtime_controller_; +}; +} // namespace + namespace testing { class PlatformConfigurationTest : public ShellTest {}; diff --git a/lib/web_ui/README.md b/lib/web_ui/README.md index e5a68a9d3fd3a..078c31aea3af6 100644 --- a/lib/web_ui/README.md +++ b/lib/web_ui/README.md @@ -135,8 +135,8 @@ tests locally. To make changes effective on LUCI follow instructions in ### Rolling browsers -When running tests on LUCI using Chromium, LUCI uses the version of Chromium -fetched from CIPD. +When running tests on LUCI using Chrome, LUCI uses the version of Chrome for +Testing fetched from CIPD. Since the engine code and infra recipes do not live in the same repository there are few steps to follow in order to upgrade a browser's version. @@ -166,12 +166,11 @@ the `--dry-run` flag to the felt command. NOTE: Because this script uses `fc-config`, this roll step only actually works on Linux, not on macOS or Windows. -#### Chromium +#### Chrome for Testing -Chromium is an independent project that gets rolled into Flutter manually, and as needed. -Flutter consumes a pre-built Chromium version from chromium.org. When a new version of -Chromium (check [here](https://www.chromium.org/getting-involved/download-chromium/#downloading-old-builds-of-chrome-chromium)) -is needed, follow these steps to roll the new version: +Chrome for Testing is an independent project that gets rolled into Flutter +manually, and as needed. Flutter consumes a pre-built Chrome for Testing build. +The available versions of Chrome for Testing available can be found [here](https://googlechromelabs.github.io/chrome-for-testing/). To roll to a newer version: - Make sure you have `depot_tools` installed (if you are regularly hacking on the engine code, you probably do). @@ -179,13 +178,8 @@ is needed, follow these steps to roll the new version: instructions (this step requires sufficient privileges; contact #hackers-infra-🌡 on [Flutter's Discord server](https://github.com/flutter/flutter/wiki/Chat)). - Edit `dev/browser_lock.yaml` and update the following values under `chrome`: - - Set `Windows`, `Mac` and `Linux` to the `branch_base_position`s given [in this table](https://omahaproxy.appspot.com). - (Pick from `linux`, `mac` and `win` as `os`, and the `stable` channel.) - - Set `version` to a string composed of the Major Version of the browser, and - the number of times that major version has been uploaded to CIPD. For example, - start with `'99'` for version 99.0.4844.51 of Chromium, and update to `'99.1'`, - `'99.2'` and so on if you need to upload newer bundles of the same major version. - (This is required because tags can't be repeated in CIPD). + - Set `version` to the full four part version number of the build of Chrome + for Testing you want to roll (for example, `118.0.5993.70`) - Run `dart dev/browser_roller.dart` and make sure it completes successfully. The script uploads the specified versions of Chromium (and Chromedriver) to the right locations in CIPD: [Chrome](https://chrome-infra-packages.appspot.com/p/flutter_internal/browsers/chrome), diff --git a/lib/web_ui/dev/browser_lock.dart b/lib/web_ui/dev/browser_lock.dart index d460975fd1994..40226e5bb800e 100644 --- a/lib/web_ui/dev/browser_lock.dart +++ b/lib/web_ui/dev/browser_lock.dart @@ -7,7 +7,6 @@ import 'dart:io' as io; import 'package:path/path.dart' as path; import 'package:yaml/yaml.dart'; -import 'common.dart'; import 'environment.dart'; /// Returns the browser configuration based on the `browser_lock.yaml` file in @@ -36,23 +35,10 @@ class BrowserLock { class ChromeLock { ChromeLock._fromYaml(YamlMap yaml) : - linux = (yaml['Linux'] as int).toString(), - mac = (yaml['Mac'] as int).toString(), - macArm = (yaml['Mac_Arm'] as int).toString(), - windows = (yaml['Win'] as int).toString(), version = yaml['version'] as String; - final String linux; - final String mac; - final String macArm; - final String windows; - /// The major version of Chromium represented by this lock. E.g: '96' (for Chromium 96.0.554.51) + /// The full version of Chromium represented by this lock. E.g: '119.0.6045.9' final String version; - - /// Return the Chromium Build ID to use for the current operating system. - String get versionForCurrentPlatform { - return PlatformBinding.instance.getChromeBuild(this); - } } class FirefoxLock { diff --git a/lib/web_ui/dev/browser_lock.yaml b/lib/web_ui/dev/browser_lock.yaml index 05fe635e109d7..29b8709255e76 100644 --- a/lib/web_ui/dev/browser_lock.yaml +++ b/lib/web_ui/dev/browser_lock.yaml @@ -1,23 +1,7 @@ # Please refer to the "Upgrade Browser Version" section in the README.md for # more details on how to update browser version numbers. chrome: - # It seems Chrome can't always release from the same build for all operating - # systems, so we specify per-OS build number. - # - # Follow these instructions to find the correct build number for a specific - # Chromium version + OS combo: - # - # https://www.chromium.org/getting-involved/download-chromium/#downloading-old-builds-of-chrome-chromium - # - # The OS names here must match what recipe Python expression - # `self.m.platform.name.capitalize()` evaluates to. See: - # - # recipe_modules/web_util/api.py - Linux: 1148103 - Mac: 1148119 - Mac_Arm: 1148112 - Win: 1148119 - version: '115.0' # CIPD tag for the above Build IDs. Normally "ChromeMajorVersion.UploadAttempt". ;) + version: '119.0.6045.9' firefox: version: '106.0' diff --git a/lib/web_ui/dev/browser_roller.dart b/lib/web_ui/dev/browser_roller.dart index 7c87b3c7da21d..b39de18503f6d 100644 --- a/lib/web_ui/dev/browser_roller.dart +++ b/lib/web_ui/dev/browser_roller.dart @@ -209,19 +209,18 @@ class _BrowserRoller { // Downloads Chromium from the internet, packs it in the directory structure // that the LUCI script wants. The result of this will be then uploaded to CIPD. Future _rollChromium(_Platform platform) async { - final String chromeBuild = platform.binding.getChromeBuild(_lock.chromeLock); - final String majorVersion = _lock.chromeLock.version; - final String url = platform.binding.getChromeDownloadUrl(chromeBuild); + final String version = _lock.chromeLock.version; + final String url = platform.binding.getChromeDownloadUrl(version); final String cipdPackageName = 'flutter_internal/browsers/chrome/${platform.name}'; final io.Directory platformDir = io.Directory(path.join(_rollDir.path, platform.name)); - print('\nRolling Chromium for ${platform.name} (version:$majorVersion, build $chromeBuild)'); + print('\nRolling Chromium for ${platform.name} (version:$version)'); // Bail out if CIPD already has version:$majorVersion for this package! if (!dryRun && await cipdKnowsPackageVersion( package: cipdPackageName, - versionTag: majorVersion, + versionTag: version, isVerbose: verbose )) { - print(' Skipping $cipdPackageName version:$majorVersion. Already uploaded to CIPD!'); + print(' Skipping $cipdPackageName version:$version. Already uploaded to CIPD!'); vprint(' Update browser_lock.yaml and use a different version value.'); return; } @@ -233,24 +232,17 @@ class _BrowserRoller { await _unzipAndDeleteFile(chromeDownload, platformDir); - late String relativePlatformDirPath; - // Preserve the `chrome-mac` directory when bundling, but remove it for win and linux. - if (platform.os == 'mac') { - relativePlatformDirPath = path.relative(platformDir.path, from: _rollDir.path); - } else { - final io.Directory? actualContentRoot = await _locateContentRoot(platformDir); - assert(actualContentRoot != null); - relativePlatformDirPath = path.relative(actualContentRoot!.path, from: _rollDir.path); - } + final io.Directory? actualContentRoot = await _locateContentRoot(platformDir); + assert(actualContentRoot != null); + final String relativePlatformDirPath = path.relative(actualContentRoot!.path, from: _rollDir.path); vprint(' Uploading Chromium (${platform.name}) to CIPD...'); await uploadDirectoryToCipd( directory: _rollDir, packageName: cipdPackageName, configFileName: 'cipd.chromium.${platform.name}.yaml', - description: 'Chromium $majorVersion (build $chromeBuild) used for testing', - version: majorVersion, - buildId: chromeBuild, + description: 'Chromium $version used for testing', + version: version, root: relativePlatformDirPath, isDryRun: dryRun, isVerbose: verbose, @@ -260,19 +252,18 @@ class _BrowserRoller { // Downloads Chromedriver from the internet, packs it in the directory structure // that the LUCI script wants. The result of this will be then uploaded to CIPD. Future _rollChromeDriver(_Platform platform) async { - final String chromeBuild = platform.binding.getChromeBuild(_lock.chromeLock); - final String majorVersion = _lock.chromeLock.version; - final String url = platform.binding.getChromeDriverDownloadUrl(chromeBuild); + final String version = _lock.chromeLock.version; + final String url = platform.binding.getChromeDriverDownloadUrl(version); final String cipdPackageName = 'flutter_internal/browser-drivers/chrome/${platform.name}'; final io.Directory platformDir = io.Directory(path.join(_rollDir.path, '${platform.name}_driver')); - print('\nRolling Chromedriver for ${platform.os}-${platform.arch} (version:$majorVersion, build $chromeBuild)'); + print('\nRolling Chromedriver for ${platform.os}-${platform.arch} (version:$version)'); // Bail out if CIPD already has version:$majorVersion for this package! if (!dryRun && await cipdKnowsPackageVersion( package: cipdPackageName, - versionTag: majorVersion, + versionTag: version, isVerbose: verbose )) { - print(' Skipping $cipdPackageName version:$majorVersion. Already uploaded to CIPD!'); + print(' Skipping $cipdPackageName version:$version. Already uploaded to CIPD!'); vprint(' Update browser_lock.yaml and use a different version value.'); return; } @@ -294,9 +285,8 @@ class _BrowserRoller { directory: _rollDir, packageName: cipdPackageName, configFileName: 'cipd.chromedriver.${platform.name}.yaml', - description: 'Chromedriver for Chromium $majorVersion (build $chromeBuild) used for testing', - version: majorVersion, - buildId: chromeBuild, + description: 'Chromedriver for Chromium $version used for testing', + version: version, root: relativePlatformDirPath, isDryRun: dryRun, isVerbose: verbose, @@ -341,7 +331,6 @@ class _BrowserRoller { configFileName: 'cipd.firefox.${platform.name}.yaml', description: 'Firefox $version used for testing', version: version, - buildId: version, root: relativePlatformDirPath, isDryRun: dryRun, isVerbose: verbose, diff --git a/lib/web_ui/dev/chrome.dart b/lib/web_ui/dev/chrome.dart index 9a1c4883324fe..b1043ec3f06aa 100644 --- a/lib/web_ui/dev/chrome.dart +++ b/lib/web_ui/dev/chrome.dart @@ -52,7 +52,7 @@ class ChromeEnvironment implements BrowserEnvironment { @override Future prepare() async { - final String version = browserLock.chromeLock.versionForCurrentPlatform; + final String version = browserLock.chromeLock.version; _installation = await getOrInstallChrome( version, infoLog: isCi ? stdout : DevNull(), diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index fda05dbfe01d8..0f812319806c8 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -180,7 +180,7 @@ class ChromeInstaller { /// Windows LUCI bots does not have a `unzip`. Instead we are /// using `archive` pub package. /// - /// We didn't use `archieve` on Mac/Linux since the new files have + /// We didn't use `archive` on Mac/Linux since the new files have /// permission issues. For now we are not able change file permissions /// from dart. /// See: https://github.com/dart-lang/sdk/issues/15078. @@ -219,7 +219,7 @@ class ChromeInstaller { // named e.g. 'chrome-linux'. We need to copy the files out of that // directory and into the version directory. final io.Directory tmpDir = await io.Directory.systemTemp.createTemp(); - final io.Directory unzipDir = io.Platform.isLinux ? tmpDir : versionDir; + final io.Directory unzipDir = tmpDir; final io.ProcessResult unzipResult = await io.Process.run('unzip', [ downloadedFile.path, @@ -233,17 +233,13 @@ class ChromeInstaller { 'The unzip process exited with code ${unzipResult.exitCode}.'); } - // Remove the "chrome-linux/" path prefix, which is the Linux - // convention for Chromium directory structure. - if (io.Platform.isLinux) { - final io.Directory chromeLinuxDir = - await tmpDir.list().single as io.Directory; - await for (final io.FileSystemEntity entity in chromeLinuxDir.list()) { - await entity - .rename(path.join(versionDir.path, path.basename(entity.path))); - } - await tmpDir.delete(recursive: true); + final io.Directory topLevelDir = + await tmpDir.list().single as io.Directory; + await for (final io.FileSystemEntity entity in topLevelDir.list()) { + await entity + .rename(path.join(versionDir.path, path.basename(entity.path))); } + await tmpDir.delete(recursive: true); } downloadedFile.deleteSync(); diff --git a/lib/web_ui/dev/cipd.dart b/lib/web_ui/dev/cipd.dart index dac64787e59f8..70a17b170acef 100644 --- a/lib/web_ui/dev/cipd.dart +++ b/lib/web_ui/dev/cipd.dart @@ -40,7 +40,6 @@ Future uploadDirectoryToCipd({ required String description, required String root, required String version, - required String buildId, bool isDryRun = false, bool isVerbose = false, }) async { @@ -72,7 +71,7 @@ data: '--tag', 'version:$version', '--ref', - buildId, + version, ], if (isDryRun) ...[ '--out', diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 857f25a660a2c..1c75526fbb180 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -7,7 +7,6 @@ import 'dart:io' as io; import 'package:path/path.dart' as path; import 'browser.dart'; -import 'browser_lock.dart'; import 'chrome.dart'; import 'edge.dart'; import 'environment.dart'; @@ -43,9 +42,14 @@ abstract class PlatformBinding { throw UnsupportedError('${io.Platform.operatingSystem} is not supported'); } - String getChromeBuild(ChromeLock chromeLock); - String getChromeDownloadUrl(String version); - String getChromeDriverDownloadUrl(String version); + String get chromePlatformString; + + String getChromeDownloadUrl(String version) => + 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/$version/$chromePlatformString/chrome-$chromePlatformString.zip'; + + String getChromeDriverDownloadUrl(String version) => + 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/$version/$chromePlatformString/chromedriver-$chromePlatformString.zip'; + String getFirefoxDownloadUrl(String version); String getFirefoxDownloadFilename(String version); String getChromeExecutablePath(io.Directory versionDir); @@ -55,22 +59,9 @@ abstract class PlatformBinding { String getCommandToRunEdge(); } -const String _kBaseDownloadUrl = - 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o'; - -class WindowsPlatformBinding implements PlatformBinding { - @override - String getChromeBuild(ChromeLock chromeLock) { - return chromeLock.windows; - } - +class WindowsPlatformBinding extends PlatformBinding { @override - String getChromeDownloadUrl(String version) => - 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win_x64%2F$version%2Fchrome-win.zip?alt=media'; - - @override - String getChromeDriverDownloadUrl(String version) => - 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win_x64%2F$version%2Fchromedriver_win32.zip?alt=media'; + String get chromePlatformString => 'win64'; @override String getChromeExecutablePath(io.Directory versionDir) => @@ -100,19 +91,9 @@ class WindowsPlatformBinding implements PlatformBinding { String getCommandToRunEdge() => 'MicrosoftEdgeLauncher'; } -class LinuxPlatformBinding implements PlatformBinding { +class LinuxPlatformBinding extends PlatformBinding { @override - String getChromeBuild(ChromeLock chromeLock) { - return chromeLock.linux; - } - - @override - String getChromeDownloadUrl(String version) => - '$_kBaseDownloadUrl/Linux_x64%2F$version%2Fchrome-linux.zip?alt=media'; - - @override - String getChromeDriverDownloadUrl(String version) => - '$_kBaseDownloadUrl/Linux_x64%2F$version%2Fchromedriver_linux64.zip?alt=media'; + String get chromePlatformString => 'linux64'; @override String getChromeExecutablePath(io.Directory versionDir) => @@ -144,25 +125,14 @@ class LinuxPlatformBinding implements PlatformBinding { throw UnsupportedError('Edge is not supported on Linux'); } -abstract class MacPlatformBinding implements PlatformBinding { - String get chromePlatformString; - - @override - String getChromeDownloadUrl(String version) => - '$_kBaseDownloadUrl/$chromePlatformString%2F$version%2Fchrome-mac.zip?alt=media'; - - @override - String getChromeDriverDownloadUrl(String version) => - '$_kBaseDownloadUrl/$chromePlatformString%2F$version%2Fchromedriver_mac64.zip?alt=media'; - +abstract class MacPlatformBinding extends PlatformBinding { @override String getChromeExecutablePath(io.Directory versionDir) => path.join( versionDir.path, - 'chrome-mac', - 'Chromium.app', + 'Google Chrome for Testing.app', 'Contents', 'MacOS', - 'Chromium', + 'Google Chrome for Testing', ); @override @@ -191,22 +161,12 @@ abstract class MacPlatformBinding implements PlatformBinding { class MacArmPlatformBinding extends MacPlatformBinding { @override - String get chromePlatformString => 'Mac_Arm'; - - @override - String getChromeBuild(ChromeLock chromeLock) { - return chromeLock.macArm; - } + String get chromePlatformString => 'mac-arm64'; } class Macx64PlatformBinding extends MacPlatformBinding { @override - String get chromePlatformString => 'Mac'; - - @override - String getChromeBuild(ChromeLock chromeLock) { - return chromeLock.mac; - } + String get chromePlatformString => 'mac-x64'; } class BrowserInstallation { diff --git a/lib/web_ui/dev/edge.dart b/lib/web_ui/dev/edge.dart index 032071ae8b3dc..db264f459135b 100644 --- a/lib/web_ui/dev/edge.dart +++ b/lib/web_ui/dev/edge.dart @@ -24,7 +24,7 @@ class EdgeEnvironment implements BrowserEnvironment { } @override - Runtime get packageTestRuntime => Runtime.internetExplorer; + Runtime get packageTestRuntime => Runtime.edge; @override Future prepare() async { diff --git a/lib/web_ui/dev/generate_builder_json.dart b/lib/web_ui/dev/generate_builder_json.dart index e85e7cb279c06..5e6733237fc15 100644 --- a/lib/web_ui/dev/generate_builder_json.dart +++ b/lib/web_ui/dev/generate_builder_json.dart @@ -9,7 +9,7 @@ import 'felt_config.dart'; String generateBuilderJson(FeltConfig config) { final Map outputJson = { '_comment': 'THIS IS A GENERATED FILE. Do not edit this file directly.', - '_comment2': 'See `generated_builder_json.dart` for the generator code', + '_comment2': 'See `generate_builder_json.dart` for the generator code', 'builds': [ _getArtifactBuildStep(), for (final TestBundle bundle in config.testBundles) @@ -113,7 +113,10 @@ Iterable _getAllTestSteps(List suites) { suite.runConfig.browser == BrowserName.chrome || suite.runConfig.browser == BrowserName.firefox ), - ..._getTestStepsForPlatform(suites, 'Mac', (TestSuite suite) => + // TODO(jacksongardner): Stop filtering to Mac-12 after macOS 13 issues are fixed: + // https://github.com/flutter/flutter/issues/136274, + // https://github.com/flutter/flutter/issues/136279 + ..._getTestStepsForPlatform(suites, 'Mac', specificOS: 'Mac-12', (TestSuite suite) => suite.runConfig.browser == BrowserName.safari ), ..._getTestStepsForPlatform(suites, 'Windows', (TestSuite suite) => @@ -129,7 +132,9 @@ Iterable _getAllTestSteps(List suites) { Iterable _getTestStepsForPlatform( List suites, String platform, - bool Function(TestSuite suite) filter) { + bool Function(TestSuite suite) filter, { + String? specificOS, +}) { return suites .where(filter) .map((TestSuite suite) => { @@ -137,7 +142,7 @@ Iterable _getTestStepsForPlatform( 'recipe': 'engine_v2/tester_engine', 'drone_dimensions': [ 'device_type=none', - 'os=$platform', + 'os=${specificOS ?? platform}', ], 'gclient_variables': { 'download_android_deps': false, @@ -149,12 +154,12 @@ Iterable _getTestStepsForPlatform( 'test_dependencies': [ { 'dependency': 'goldctl', - 'version': 'git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603', + 'version': 'git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09', }, if (suite.runConfig.browser == BrowserName.chrome) { 'dependency': 'chrome_and_driver', - 'version': 'version:115.0', + 'version': '119.0.6045.9', }, if (suite.runConfig.browser == BrowserName.firefox) { diff --git a/lib/web_ui/dev/roll_fallback_fonts.dart b/lib/web_ui/dev/roll_fallback_fonts.dart index efcb424f3bedd..a281138009fda 100644 --- a/lib/web_ui/dev/roll_fallback_fonts.dart +++ b/lib/web_ui/dev/roll_fallback_fonts.dart @@ -311,7 +311,6 @@ OTHER DEALINGS IN THE FONT SOFTWARE. description: 'A set of Noto fonts to fall back to for use in testing.', root: fontDir.path, version: versionString, - buildId: versionString, isDryRun: isDryRun, ); } @@ -334,6 +333,7 @@ const List fallbackFonts = [ 'Noto Sans', 'Noto Color Emoji', 'Noto Emoji', + 'Noto Music', 'Noto Sans Symbols', 'Noto Sans Symbols 2', 'Noto Sans Adlam', diff --git a/lib/web_ui/dev/test_dart2wasm.js b/lib/web_ui/dev/test_dart2wasm.js index b48e2403a3535..e1d1c32c164f9 100644 --- a/lib/web_ui/dev/test_dart2wasm.js +++ b/lib/web_ui/dev/test_dart2wasm.js @@ -64,7 +64,8 @@ window.onload = async function () { const skwasmInstance = await skwasm(); window._flutter_skwasmInstance = skwasmInstance; resolve({ - "skwasm": skwasmInstance.asm ?? skwasmInstance.wasmExports, + "skwasm": skwasmInstance.wasmExports, + "skwasmWrapper": skwasmInstance, "ffi": { "memory": skwasmInstance.wasmMemory, } diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index d9d717a1c4b77..da32db94ca187 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -1004,7 +1004,7 @@ class BrowserManager { final String pathToTest = p.dirname(path); final String mapPath = p.join( - _sourceMapDirectory!.path, + _sourceMapDirectory.path, pathToTest, sourceMapFileName ); diff --git a/lib/web_ui/dev/webdriver_browser.dart b/lib/web_ui/dev/webdriver_browser.dart index 8c1c82a9b01c1..ef27394127fa7 100644 --- a/lib/web_ui/dev/webdriver_browser.dart +++ b/lib/web_ui/dev/webdriver_browser.dart @@ -8,7 +8,7 @@ import 'dart:io'; import 'dart:math'; import 'package:image/image.dart'; -import 'package:webdriver/io.dart' show WebDriver, createDriver; +import 'package:webdriver/async_io.dart' show WebDriver, createDriver; import 'browser.dart'; diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index 026f20bfe6bad..c050e43dec611 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -217,7 +217,6 @@ enum Clip { abstract class Paint { factory Paint() => engine.renderer.createPaint(); - static bool enableDithering = false; BlendMode get blendMode; set blendMode(BlendMode value); PaintingStyle get style; diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 016117334272e..974411798aab5 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -43,10 +43,11 @@ export 'engine/canvaskit/picture.dart'; export 'engine/canvaskit/picture_recorder.dart'; export 'engine/canvaskit/raster_cache.dart'; export 'engine/canvaskit/rasterizer.dart'; +export 'engine/canvaskit/render_canvas.dart'; +export 'engine/canvaskit/render_canvas_factory.dart'; export 'engine/canvaskit/renderer.dart'; export 'engine/canvaskit/shader.dart'; export 'engine/canvaskit/surface.dart'; -export 'engine/canvaskit/surface_factory.dart'; export 'engine/canvaskit/text.dart'; export 'engine/canvaskit/text_fragmenter.dart'; export 'engine/canvaskit/util.dart'; @@ -111,7 +112,9 @@ export 'engine/js_interop/js_typed_data.dart'; export 'engine/key_map.g.dart'; export 'engine/keyboard_binding.dart'; export 'engine/layers.dart'; -export 'engine/mouse_cursor.dart'; +export 'engine/mouse/context_menu.dart'; +export 'engine/mouse/cursor.dart'; +export 'engine/mouse/prevent_default.dart'; export 'engine/navigation/history.dart'; export 'engine/noto_font.dart'; export 'engine/noto_font_encoding.dart'; @@ -141,6 +144,7 @@ export 'engine/semantics/focusable.dart'; export 'engine/semantics/image.dart'; export 'engine/semantics/incrementable.dart'; export 'engine/semantics/label_and_value.dart'; +export 'engine/semantics/link.dart'; export 'engine/semantics/live_region.dart'; export 'engine/semantics/platform_view.dart'; export 'engine/semantics/scrollable.dart'; diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index 22c94573fdf6f..dff531d06c244 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -161,6 +161,13 @@ extension CanvasKitExtension on CanvasKit { DomCanvasElement canvas, SkWebGLContextOptions options) => _GetWebGLContext(canvas, options).toDartDouble; + @JS('GetWebGLContext') + external JSNumber _GetOffscreenWebGLContext( + DomOffscreenCanvas canvas, SkWebGLContextOptions options); + double GetOffscreenWebGLContext( + DomOffscreenCanvas canvas, SkWebGLContextOptions options) => + _GetOffscreenWebGLContext(canvas, options).toDartDouble; + @JS('MakeGrContext') external SkGrContext _MakeGrContext(JSNumber glContext); SkGrContext MakeGrContext(double glContext) => @@ -199,6 +206,9 @@ extension CanvasKitExtension on CanvasKit { external SkSurface MakeSWCanvasSurface(DomCanvasElement canvas); + @JS('MakeSWCanvasSurface') + external SkSurface MakeOffscreenSWCanvasSurface(DomOffscreenCanvas canvas); + /// Creates an image from decoded pixels represented as a list of bytes. /// /// The pixel data must be encoded according to the image info in [info]. @@ -240,7 +250,7 @@ extension CanvasKitExtension on CanvasKit { DomImageBitmap imageBitmap, bool hasPremultipliedAlpha, ) => _MakeLazyImageFromTextureSource3( - imageBitmap as JSAny, + imageBitmap, 0.toJS, hasPremultipliedAlpha.toJS, ); diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index 779ca9babb1bc..57ff2ed6836ae 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -4,7 +4,7 @@ import 'package:ui/ui.dart' as ui; -import '../../engine.dart' show platformViewManager; +import '../../engine.dart' show PlatformViewManager; import '../configuration.dart'; import '../dom.dart'; import '../html/path_to_svg_clip.dart'; @@ -18,9 +18,9 @@ import 'embedded_views_diff.dart'; import 'path.dart'; import 'picture.dart'; import 'picture_recorder.dart'; +import 'render_canvas.dart'; +import 'render_canvas_factory.dart'; import 'renderer.dart'; -import 'surface.dart'; -import 'surface_factory.dart'; /// This composites HTML views into the [ui.Scene]. class HtmlViewEmbedder { @@ -31,42 +31,6 @@ class HtmlViewEmbedder { DomElement get skiaSceneHost => CanvasKitRenderer.instance.sceneHost!; - /// Force the view embedder to disable overlays. - /// - /// This should never be used outside of tests. - static set debugDisableOverlays(bool disable) { - // Short circuit if the value is the same as what we already have. - if (disable == _debugOverlaysDisabled) { - return; - } - _debugOverlaysDisabled = disable; - final SurfaceFactory? instance = SurfaceFactory.debugUninitializedInstance; - if (instance != null) { - instance.releaseSurfaces(); - instance.removeSurfacesFromDom(); - instance.debugClear(); - } - if (disable) { - // If we are disabling overlays then get the current [SurfaceFactory] - // instance, clear it, and overwrite it with a new instance with only - // one surface for the base surface. - SurfaceFactory.debugSetInstance(SurfaceFactory(1)); - } else { - // If we are re-enabling overlays then replace the current - // [SurfaceFactory]instance with one with - // [configuration.canvasKitMaximumSurfaces] overlays. - SurfaceFactory.debugSetInstance( - SurfaceFactory(configuration.canvasKitMaximumSurfaces)); - } - } - - static bool _debugOverlaysDisabled = false; - - /// Whether or not we have issues a warning to the user about having too many - /// surfaces on screen at once. This is so we only warn once, instead of every - /// frame. - bool _warnedAboutTooManySurfaces = false; - /// The context for the current frame. EmbedderFrameContext _context = EmbedderFrameContext(); @@ -86,10 +50,12 @@ class HtmlViewEmbedder { /// * The number of clipping elements used last time the view was composited. final Map _viewClipChains = {}; - /// Surfaces used to draw on top of platform views, keyed by platform view ID. - /// - /// These surfaces are cached in the [OverlayCache] and reused. - final Map _overlays = {}; + /// The maximum number of overlays to create. Too many overlays can cause a + /// performance burden. + static const int maximumOverlays = 7; + + /// Canvases used to draw on top of platform views, keyed by platform view ID. + final Map _overlays = {}; /// The views that need to be recomposited into the scene on the next frame. final Set _viewsToRecomposite = {}; @@ -100,6 +66,9 @@ class HtmlViewEmbedder { /// The most recent composition order. final List _activeCompositionOrder = []; + /// The most recent overlay groups. + List _activeOverlayGroups = []; + /// The size of the frame, in physical pixels. ui.Size _frameSize = ui.window.physicalSize; @@ -124,20 +93,10 @@ class HtmlViewEmbedder { } void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) { - final bool hasAvailableOverlay = - _context.pictureRecordersCreatedDuringPreroll.length < - SurfaceFactory.instance.maximumOverlays; - if (!hasAvailableOverlay && !_warnedAboutTooManySurfaces) { - _warnedAboutTooManySurfaces = true; - printWarning('Flutter was unable to create enough overlay surfaces. ' - 'This is usually caused by too many platform views being ' - 'displayed at once. ' - 'You may experience incorrect rendering.'); - } // We need an overlay for each visible platform view. Invisible platform // views will be grouped with (at most) one visible platform view later. - final bool needNewOverlay = platformViewManager.isVisible(viewId); - if (needNewOverlay && hasAvailableOverlay) { + final bool needNewOverlay = PlatformViewManager.instance.isVisible(viewId); + if (needNewOverlay) { final CkPictureRecorder pictureRecorder = CkPictureRecorder(); pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); _context.pictureRecordersCreatedDuringPreroll.add(pictureRecorder); @@ -164,11 +123,11 @@ class HtmlViewEmbedder { final int overlayIndex = _context.visibleViewCount; _compositionOrder.add(viewId); // Keep track of the number of visible platform views. - if (platformViewManager.isVisible(viewId)) { + if (PlatformViewManager.instance.isVisible(viewId)) { _context.visibleViewCount++; } // We need a new overlay if this is a visible view. - final bool needNewOverlay = platformViewManager.isVisible(viewId); + final bool needNewOverlay = PlatformViewManager.instance.isVisible(viewId); CkPictureRecorder? recorderToUseForRendering; if (needNewOverlay) { if (overlayIndex < _context.pictureRecordersCreatedDuringPreroll.length) { @@ -409,26 +368,27 @@ class HtmlViewEmbedder { (_activeCompositionOrder.isEmpty || _compositionOrder.isEmpty) ? null : diffViewList(_activeCompositionOrder, _compositionOrder); - _updateOverlays(diffResult); + final List? overlayGroups = _updateOverlays(diffResult); + if (overlayGroups != null) { + _activeOverlayGroups = overlayGroups; + } assert( - _context.pictureRecorders.length == _overlays.length, - 'There should be the same number of picture recorders ' + _context.pictureRecorders.length >= _overlays.length, + 'There should be at least as many picture recorders ' '(${_context.pictureRecorders.length}) as overlays (${_overlays.length}).', ); - int pictureRecorderIndex = 0; - for (int i = 0; i < _compositionOrder.length; i++) { - final int viewId = _compositionOrder[i]; - if (_overlays[viewId] != null) { - final SurfaceFrame frame = _overlays[viewId]!.acquireFrame(_frameSize); - final CkCanvas canvas = frame.skiaCanvas; - final CkPicture ckPicture = - _context.pictureRecorders[pictureRecorderIndex].endRecording(); - canvas.clear(const ui.Color(0x00000000)); - canvas.drawPicture(ckPicture); + int pictureRecorderIndex = 0; + for (final OverlayGroup overlayGroup in _activeOverlayGroups) { + final RenderCanvas overlay = _overlays[overlayGroup.last]!; + final List pictures = []; + for (int i = 0; i < overlayGroup.visibleCount; i++) { + pictures.add( + _context.pictureRecorders[pictureRecorderIndex].endRecording()); pictureRecorderIndex++; - frame.submit(); } + CanvasKitRenderer.instance.rasterizer + .rasterizeToCanvas(overlay, pictures); } for (final CkPictureRecorder recorder in _context.pictureRecordersCreatedDuringPreroll) { @@ -467,7 +427,7 @@ class HtmlViewEmbedder { for (final int viewId in diffResult.viewsToAdd) { bool isViewInvalid = false; assert(() { - isViewInvalid = !platformViewManager.knowsViewId(viewId); + isViewInvalid = !PlatformViewManager.instance.knowsViewId(viewId); if (isViewInvalid) { debugInvalidViewIds ??= []; debugInvalidViewIds!.add(viewId); @@ -481,7 +441,7 @@ class HtmlViewEmbedder { if (diffResult.addToBeginning) { final DomElement platformViewRoot = _viewClipChains[viewId]!.root; skiaSceneHost.insertBefore(platformViewRoot, elementToInsertBefore); - final Surface? overlay = _overlays[viewId]; + final RenderCanvas? overlay = _overlays[viewId]; if (overlay != null) { skiaSceneHost.insertBefore( overlay.htmlElement, elementToInsertBefore); @@ -489,7 +449,7 @@ class HtmlViewEmbedder { } else { final DomElement platformViewRoot = _viewClipChains[viewId]!.root; skiaSceneHost.append(platformViewRoot); - final Surface? overlay = _overlays[viewId]; + final RenderCanvas? overlay = _overlays[viewId]; if (overlay != null) { skiaSceneHost.append(overlay.htmlElement); } @@ -514,13 +474,13 @@ class HtmlViewEmbedder { } } } else { - SurfaceFactory.instance.removeSurfacesFromDom(); + RenderCanvasFactory.instance.removeSurfacesFromDom(); for (int i = 0; i < _compositionOrder.length; i++) { final int viewId = _compositionOrder[i]; bool isViewInvalid = false; assert(() { - isViewInvalid = !platformViewManager.knowsViewId(viewId); + isViewInvalid = !PlatformViewManager.instance.knowsViewId(viewId); if (isViewInvalid) { debugInvalidViewIds ??= []; debugInvalidViewIds!.add(viewId); @@ -532,7 +492,7 @@ class HtmlViewEmbedder { } final DomElement platformViewRoot = _viewClipChains[viewId]!.root; - final Surface? overlay = _overlays[viewId]; + final RenderCanvas? overlay = _overlays[viewId]; skiaSceneHost.append(platformViewRoot); if (overlay != null) { skiaSceneHost.append(overlay.htmlElement); @@ -568,8 +528,8 @@ class HtmlViewEmbedder { void _releaseOverlay(int viewId) { if (_overlays[viewId] != null) { - final Surface overlay = _overlays[viewId]!; - SurfaceFactory.instance.releaseSurface(overlay); + final RenderCanvas overlay = _overlays[viewId]!; + RenderCanvasFactory.instance.releaseCanvas(overlay); _overlays.remove(viewId); } } @@ -591,13 +551,13 @@ class HtmlViewEmbedder { // composition order of the current and previous frame, respectively. // // TODO(hterkelsen): Test this more thoroughly. - void _updateOverlays(ViewListDiffResult? diffResult) { + List? _updateOverlays(ViewListDiffResult? diffResult) { if (diffResult != null && diffResult.viewsToAdd.isEmpty && diffResult.viewsToRemove.isEmpty) { // The composition order has not changed, continue using the assigned // overlays. - return; + return null; } // Group platform views from their composition order. // Each group contains one visible view, and any number of invisible views @@ -606,17 +566,10 @@ class HtmlViewEmbedder { getOverlayGroups(_compositionOrder); final List viewsNeedingOverlays = overlayGroups.map((OverlayGroup group) => group.last).toList(); - // If there were more visible views than overlays, then the last group - // doesn't have an overlay. - if (viewsNeedingOverlays.length > SurfaceFactory.instance.maximumOverlays) { - assert(viewsNeedingOverlays.length == - SurfaceFactory.instance.maximumOverlays + 1); - viewsNeedingOverlays.removeLast(); - } if (diffResult == null) { // Everything is going to be explicitly recomposited anyway. Release all // the surfaces and assign an overlay to all the surfaces needing one. - SurfaceFactory.instance.releaseSurfaces(); + RenderCanvasFactory.instance.releaseCanvases(); _overlays.clear(); viewsNeedingOverlays.forEach(_initializeOverlay); } else { @@ -635,6 +588,7 @@ class HtmlViewEmbedder { .forEach(_initializeOverlay); } assert(_overlays.length == viewsNeedingOverlays.length); + return overlayGroups; } // Group the platform views into "overlay groups". These are sublists @@ -646,22 +600,20 @@ class HtmlViewEmbedder { // be assigned an overlay are grouped together and will be rendered on top of // the rest of the scene. List getOverlayGroups(List views) { - final int maxOverlays = SurfaceFactory.instance.maximumOverlays; - if (maxOverlays == 0) { - return const []; - } final List result = []; - OverlayGroup currentGroup = OverlayGroup([]); + OverlayGroup currentGroup = OverlayGroup(); for (int i = 0; i < views.length; i++) { final int view = views[i]; - if (platformViewManager.isInvisible(view)) { + if (PlatformViewManager.instance.isInvisible(view)) { // We add as many invisible views as we find to the current group. currentGroup.add(view); } else { // `view` is visible. - if (!currentGroup.hasVisibleView) { - // If `view` is the first visible one of the group, add it. + if (!currentGroup.hasVisibleView || + result.length + 1 >= HtmlViewEmbedder.maximumOverlays) { + // If `view` is the first visible one of the group or we've reached + // the maximum number of overlays, add it. currentGroup.add(view, visible: true); } else { // There's already a visible `view` in `currentGroup`, so a new @@ -671,17 +623,8 @@ class HtmlViewEmbedder { // We only care about groups that have one visible view. result.add(currentGroup); } - // If there are overlays still available. - if (result.length < maxOverlays) { - // Create a new group, starting with `view`. - currentGroup = OverlayGroup([view], visible: true); - } else { - // Add the rest of the views to a final group that will be rendered - // on top of the scene. - currentGroup = OverlayGroup(views.sublist(i), visible: true); - // And break out of the loop! - break; - } + currentGroup = OverlayGroup(); + currentGroup.add(view, visible: true); } } } @@ -696,8 +639,7 @@ class HtmlViewEmbedder { assert(!_overlays.containsKey(viewId)); // Try reusing a cached overlay created for another platform view. - final Surface overlay = SurfaceFactory.instance.getSurface()!; - overlay.createOrUpdateSurface(_frameSize); + final RenderCanvas overlay = RenderCanvasFactory.instance.getCanvas(); _overlays[viewId] = overlay; } @@ -720,7 +662,7 @@ class HtmlViewEmbedder { /// Clears the state of this view embedder. Used in tests. void debugClear() { - final Set allViews = platformViewManager.debugClear(); + final Set allViews = PlatformViewManager.instance.debugClear(); disposeViews(allViews); _context = EmbedderFrameContext(); _currentCompositionParams.clear(); @@ -742,29 +684,30 @@ class HtmlViewEmbedder { /// Every overlay group is a list containing a visible view preceded or followed /// by zero or more invisible views. class OverlayGroup { - /// Constructor - OverlayGroup( - List viewGroup, { - bool visible = false, - }) : _group = viewGroup, - _containsVisibleView = visible; + OverlayGroup() : _group = []; // The internal list of ints. final List _group; - // A boolean flag to mark if any visible view has been added to the list. - bool _containsVisibleView; + + /// The number of visible views in this group. + int _visibleCount = 0; /// Add a [view] (maybe [visible]) to this group. void add(int view, {bool visible = false}) { _group.add(view); - _containsVisibleView |= visible; + if (visible) { + _visibleCount++; + } } /// Get the "last" view added to this group. int get last => _group.last; /// Returns true if this group contains any visible view. - bool get hasVisibleView => _group.isNotEmpty && _containsVisibleView; + bool get hasVisibleView => _visibleCount > 0; + + /// Returns the number of visible views in this overlay group. + int get visibleCount => _visibleCount; } /// Represents a Clip Chain (for a view). diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index 9a8c37b75edae..f54d7acd026b8 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -400,10 +400,10 @@ class ImageFilterEngineLayer extends ContainerLayer @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { - final Matrix4 transform = - (_filter as CkManagedSkImageFilterConvertible).transform; - final Matrix4 childMatrix = matrix.multiplied(transform); - prerollContext.mutatorsStack.pushTransform(transform); + final Matrix4 childMatrix = Matrix4.copy(matrix); + childMatrix.translate(_offset.dx, _offset.dy); + prerollContext.mutatorsStack + .pushTransform(Matrix4.translationValues(_offset.dx, _offset.dy, 0.0)); final ui.Rect childPaintBounds = prerollChildren(prerollContext, childMatrix); (_filter as CkManagedSkImageFilterConvertible) diff --git a/lib/web_ui/lib/src/engine/canvaskit/picture.dart b/lib/web_ui/lib/src/engine/canvaskit/picture.dart index 52b8229ec2b9f..668afeeb47434 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/picture.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/picture.dart @@ -11,8 +11,8 @@ import 'canvas.dart'; import 'canvaskit_api.dart'; import 'image.dart'; import 'native_memory.dart'; +import 'render_canvas_factory.dart'; import 'surface.dart'; -import 'surface_factory.dart'; /// Implements [ui.Picture] on top of [SkPicture]. class CkPicture implements ScenePicture { @@ -99,7 +99,7 @@ class CkPicture implements ScenePicture { CkImage toImageSync(int width, int height) { assert(debugCheckNotDisposed('Cannot convert picture to image.')); - final Surface surface = SurfaceFactory.instance.pictureToImageSurface; + final Surface surface = RenderCanvasFactory.instance.pictureToImageSurface; final CkSurface ckSurface = surface .createOrUpdateSurface(ui.Size(width.toDouble(), height.toDouble())); final CkCanvas ckCanvas = ckSurface.getCanvas(); diff --git a/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart b/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart index 67f01fff852f9..8d150ecae4144 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart @@ -3,22 +3,29 @@ // found in the LICENSE file. import 'package:meta/meta.dart'; +import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../frame_reference.dart'; -import 'canvas.dart'; -import 'embedded_views.dart'; -import 'layer_tree.dart'; -import 'surface.dart'; -import 'surface_factory.dart'; - /// A class that can rasterize [LayerTree]s into a given [Surface]. class Rasterizer { final CompositorContext context = CompositorContext(); final List _postFrameCallbacks = []; + /// This is an SkSurface backed by an OffScreenCanvas. This single Surface is + /// used to render to many RenderCanvases to produce the rendered scene. + final Surface _offscreenSurface = Surface(); + ui.Size _currentFrameSize = ui.Size.zero; + + /// Render the given [pictures] so it is displayed by the given [canvas]. + Future rasterizeToCanvas( + RenderCanvas canvas, List pictures) async { + await _offscreenSurface.rasterizeToCanvas( + _currentFrameSize, canvas, pictures); + } + + /// Sets the maximum size of the Skia resource cache, in bytes. void setSkiaResourceCacheMaxBytes(int bytes) => - SurfaceFactory.instance.baseSurface.setSkiaResourceCacheMaxBytes(bytes); + _offscreenSurface.setSkiaResourceCacheMaxBytes(bytes); /// Creates a new frame from this rasterizer's surface, draws the given /// [LayerTree] into it, and then submits the frame. @@ -29,17 +36,22 @@ class Rasterizer { return; } - final SurfaceFrame frame = - SurfaceFactory.instance.baseSurface.acquireFrame(layerTree.frameSize); - HtmlViewEmbedder.instance.frameSize = layerTree.frameSize; - final CkCanvas canvas = frame.skiaCanvas; - canvas.clear(const ui.Color(0x00000000)); - final Frame compositorFrame = - context.acquireFrame(canvas, HtmlViewEmbedder.instance); + _currentFrameSize = layerTree.frameSize; + _offscreenSurface.acquireFrame(_currentFrameSize); + HtmlViewEmbedder.instance.frameSize = _currentFrameSize; + final CkPictureRecorder pictureRecorder = CkPictureRecorder(); + pictureRecorder.beginRecording(ui.Offset.zero & _currentFrameSize); + pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000)); + final Frame compositorFrame = context.acquireFrame( + pictureRecorder.recordingCanvas!, HtmlViewEmbedder.instance); compositorFrame.raster(layerTree, ignoreRasterCache: true); - SurfaceFactory.instance.baseSurface.addToScene(); - frame.submit(); + + CanvasKitRenderer.instance.sceneHost! + .prepend(RenderCanvasFactory.instance.baseCanvas.htmlElement); + rasterizeToCanvas(RenderCanvasFactory.instance.baseCanvas, + [pictureRecorder.endRecording()]); + HtmlViewEmbedder.instance.submitFrame(); } finally { _runPostFrameCallbacks(); diff --git a/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart new file mode 100644 index 0000000000000..7793cfbe6cbce --- /dev/null +++ b/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart @@ -0,0 +1,113 @@ +// 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. + +import 'dart:js_interop'; + +import 'package:ui/ui.dart' as ui; + +import '../dom.dart'; +import '../window.dart'; + +/// A visible (on-screen) canvas that can display bitmaps produced by CanvasKit +/// in the (off-screen) SkSurface which is backed by an OffscreenCanvas. +/// +/// In a typical frame, the content will be rendered via CanvasKit in an +/// OffscreenCanvas, and then the contents will be transferred to the +/// RenderCanvas via `transferFromImageBitmap()`. +/// +/// If we need more RenderCanvases, for example in the case where there are +/// platform views and we need overlays to render the frame correctly, then +/// we will create multiple RenderCanvases, but crucially still only have +/// one OffscreenCanvas which transfers bitmaps to all of the RenderCanvases. +/// +/// To render into the OffscreenCanvas with CanvasKit we need to create a +/// WebGL context, which is not only expensive, but the browser has a limit +/// on the maximum amount of WebGL contexts which can be live at once. Using +/// a single OffscreenCanvas and multiple RenderCanvases allows us to only +/// create a single WebGL context. +class RenderCanvas { + RenderCanvas() { + canvasElement.setAttribute('aria-hidden', 'true'); + canvasElement.style.position = 'absolute'; + _updateLogicalHtmlCanvasSize(); + htmlElement.append(canvasElement); + } + + /// The root HTML element for this canvas. + /// + /// This element contains the canvas used to draw the UI. Unlike the canvas, + /// this element is permanent. It is never replaced or deleted, until this + /// canvas is disposed of via [dispose]. + /// + /// Conversely, the canvas that lives inside this element can be swapped, for + /// example, when the screen size changes, or when the WebGL context is lost + /// due to the browser tab becoming dormant. + final DomElement htmlElement = createDomElement('flt-canvas-container'); + + /// The underlying `` element used to display the pixels. + final DomCanvasElement canvasElement = createDomCanvasElement(); + int _pixelWidth = 0; + int _pixelHeight = 0; + + late final DomCanvasRenderingContextBitmapRenderer renderContext = + canvasElement.contextBitmapRenderer; + + double _currentDevicePixelRatio = -1; + + /// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device + /// pixels. + /// + /// The logical size of the canvas is not based on the size of the window + /// but on the size of the canvas, which, due to `ceil()` above, may not be + /// the same as the window. We do not round/floor/ceil the logical size as + /// CSS pixels can contain more than one physical pixel and therefore to + /// match the size of the window precisely we use the most precise floating + /// point value we can get. + void _updateLogicalHtmlCanvasSize() { + final double logicalWidth = _pixelWidth / window.devicePixelRatio; + final double logicalHeight = _pixelHeight / window.devicePixelRatio; + final DomCSSStyleDeclaration style = canvasElement.style; + style.width = '${logicalWidth}px'; + style.height = '${logicalHeight}px'; + _currentDevicePixelRatio = window.devicePixelRatio; + } + + /// Render the given [bitmap] with this [RenderCanvas]. + /// + /// The canvas will be resized to accomodate the bitmap immediately before + /// rendering it. + void render(DomImageBitmap bitmap) { + _ensureSize(ui.Size(bitmap.width.toDartDouble, bitmap.height.toDartDouble)); + renderContext.transferFromImageBitmap(bitmap); + } + + /// Ensures that this canvas can draw a frame of the given [size]. + void _ensureSize(ui.Size size) { + // Check if the frame is the same size as before, and if so, we don't need + // to resize the canvas. + if (size.width.ceil() == _pixelWidth && + size.height.ceil() == _pixelHeight) { + // The existing canvas doesn't need to be resized (unless the device pixel + // ratio changed). + if (window.devicePixelRatio != _currentDevicePixelRatio) { + _updateLogicalHtmlCanvasSize(); + } + return; + } + + // If the canvas is too large or too small, resize it to the exact size of + // the frame. We cannot allow the canvas to be larger than the screen + // because then when we call `transferFromImageBitmap()` the bitmap will + // be scaled to cover the entire canvas. + _pixelWidth = size.width.ceil(); + _pixelHeight = size.height.ceil(); + canvasElement.width = _pixelWidth.toDouble(); + canvasElement.height = _pixelHeight.toDouble(); + _updateLogicalHtmlCanvasSize(); + } + + void dispose() { + htmlElement.remove(); + } +} diff --git a/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart b/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart new file mode 100644 index 0000000000000..593390972377c --- /dev/null +++ b/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart @@ -0,0 +1,142 @@ +// 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. +import 'package:meta/meta.dart'; + +import '../../engine.dart'; + +/// Caches canvases used to overlay platform views. +class RenderCanvasFactory { + RenderCanvasFactory() { + assert(() { + registerHotRestartListener(debugClear); + return true; + }()); + } + + /// The lazy-initialized singleton surface factory. + /// + /// [debugClear] causes this singleton to be reinitialized. + static RenderCanvasFactory get instance => + _instance ??= RenderCanvasFactory(); + + /// Returns the raw (potentially uninitialized) value of the singleton. + /// + /// Useful in tests for checking the lifecycle of this class. + static RenderCanvasFactory? get debugUninitializedInstance => _instance; + + // Override the current instance with a new one. + // + // This should only be used in tests. + static void debugSetInstance(RenderCanvasFactory newInstance) { + _instance = newInstance; + } + + static RenderCanvasFactory? _instance; + + /// The base canvas to paint on. This is the default canvas which will be + /// painted to. If there are no platform views, then this canvas will render + /// the entire scene. + final RenderCanvas baseCanvas = RenderCanvas(); + + /// A surface used specifically for `Picture.toImage` when software rendering + /// is supported. + late final Surface pictureToImageSurface = Surface(); + + /// Canvases created by this factory which are currently in use. + final List _liveCanvases = []; + + /// Canvases created by this factory which are no longer in use. These can be + /// reused. + final List _cache = []; + + /// The number of canvases which have been created by this factory. + int get _canvasCount => _liveCanvases.length + _cache.length + 1; + + /// The number of surfaces created by this factory. Used for testing. + @visibleForTesting + int get debugSurfaceCount => _canvasCount; + + /// Returns the number of cached surfaces. + /// + /// Useful in tests. + int get debugCacheSize => _cache.length; + + /// Gets an overlay canvas from the cache or creates a new one if there are + /// none in the cache. + RenderCanvas getCanvas() { + if (_cache.isNotEmpty) { + final RenderCanvas canvas = _cache.removeLast(); + _liveCanvases.add(canvas); + return canvas; + } else { + final RenderCanvas canvas = RenderCanvas(); + _liveCanvases.add(canvas); + return canvas; + } + } + + /// Releases all surfaces so they can be reused in the next frame. + /// + /// If a released surface is in the DOM, it is not removed. This allows the + /// engine to release the surfaces at the end of the frame so they are ready + /// to be used in the next frame, but still used for painting in the current + /// frame. + void releaseCanvases() { + _cache.addAll(_liveCanvases); + _liveCanvases.clear(); + } + + /// Removes all surfaces except the base surface from the DOM. + /// + /// This is called at the beginning of the frame to prepare for painting into + /// the new surfaces. + void removeSurfacesFromDom() { + _cache.forEach(_removeFromDom); + } + + // Removes [canvas] from the DOM. + void _removeFromDom(RenderCanvas canvas) { + canvas.htmlElement.remove(); + } + + /// Signals that a canvas is no longer being used. It can be reused. + void releaseCanvas(RenderCanvas canvas) { + assert(canvas != baseCanvas, 'Attempting to release the base canvas'); + assert( + _liveCanvases.contains(canvas), + 'Attempting to release a Canvas which ' + 'was not created by this factory'); + canvas.htmlElement.remove(); + _liveCanvases.remove(canvas); + _cache.add(canvas); + } + + /// Returns [true] if [canvas] is currently being used to paint content. + /// + /// The base canvas always counts as live. + /// + /// If a canvas is not live, then it must be in the cache and ready to be + /// reused. + bool isLive(RenderCanvas canvas) { + if (canvas == baseCanvas || _liveCanvases.contains(canvas)) { + return true; + } + assert(_cache.contains(canvas)); + return false; + } + + /// Dispose all canvases created by this factory. Used in tests. + void debugClear() { + for (final RenderCanvas canvas in _cache) { + canvas.dispose(); + } + for (final RenderCanvas canvas in _liveCanvases) { + canvas.dispose(); + } + baseCanvas.dispose(); + _liveCanvases.clear(); + _cache.clear(); + _instance = null; + } +} diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface.dart b/lib/web_ui/lib/src/engine/canvaskit/surface.dart index 76719d68dcea8..db2277c65ac6b 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/surface.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/surface.dart @@ -11,11 +11,10 @@ import '../configuration.dart'; import '../dom.dart'; import '../platform_dispatcher.dart'; import '../util.dart'; -import '../window.dart'; import 'canvas.dart'; import 'canvaskit_api.dart'; -import 'renderer.dart'; -import 'surface_factory.dart'; +import 'picture.dart'; +import 'render_canvas.dart'; import 'util.dart'; // Only supported in profile/release mode. Allows Flutter to use MSAA but @@ -26,8 +25,7 @@ typedef SubmitCallback = bool Function(SurfaceFrame, CkCanvas); /// A frame which contains a canvas to be drawn into. class SurfaceFrame { - SurfaceFrame(this.skiaSurface, this.submitCallback) - : _submitted = false; + SurfaceFrame(this.skiaSurface, this.submitCallback) : _submitted = false; final CkSurface skiaSurface; final SubmitCallback submitCallback; @@ -82,19 +80,16 @@ class Surface { int? _glContext; int? _skiaCacheBytes; - /// The root HTML element for this surface. - /// - /// This element contains the canvas used to draw the UI. Unlike the canvas, - /// this element is permanent. It is never replaced or deleted, until this - /// surface is disposed of via [dispose]. - /// - /// Conversely, the canvas that lives inside this element can be swapped, for - /// example, when the screen size changes, or when the WebGL context is lost - /// due to the browser tab becoming dormant. - final DomElement htmlElement = createDomElement('flt-canvas-container'); + /// The underlying OffscreenCanvas element used for this surface. + DomOffscreenCanvas? _offscreenCanvas; + + /// Returns the underlying OffscreenCanvas. Should only be used in tests. + DomOffscreenCanvas? get debugOffscreenCanvas => _offscreenCanvas; + + /// The backing this Surface in the case that OffscreenCanvas isn't + /// supported. + DomCanvasElement? _canvasElement; - /// The underlying `` element used for this surface. - DomCanvasElement? htmlCanvas; int _pixelWidth = -1; int _pixelHeight = -1; int _sampleCount = -1; @@ -112,7 +107,33 @@ class Surface { } } - bool _addedToScene = false; + Future rasterizeToCanvas( + ui.Size frameSize, RenderCanvas canvas, List pictures) async { + final CkCanvas skCanvas = _surface!.getCanvas(); + skCanvas.clear(const ui.Color(0x00000000)); + pictures.forEach(skCanvas.drawPicture); + _surface!.flush(); + + DomImageBitmap bitmap; + if (Surface.offscreenCanvasSupported) { + bitmap = (await createSizedOffscreenImageBitmap( + _offscreenCanvas!, + 0, + _pixelHeight - frameSize.height.toInt(), + frameSize.width.toInt(), + frameSize.height.toInt(), + ))!; + } else { + bitmap = (await createSizedImageBitmap( + _canvasElement!, + 0, + _pixelHeight - frameSize.height.toInt(), + frameSize.width.toInt(), + frameSize.height.toInt(), + ))!; + } + canvas.render(bitmap); + } /// Acquire a frame of the given [size] containing a drawable canvas. /// @@ -129,21 +150,16 @@ class Surface { return SurfaceFrame(surface, submitCallback); } - void addToScene() { - if (!_addedToScene) { - CanvasKitRenderer.instance.sceneHost!.prepend(htmlElement); - } - _addedToScene = true; - } - ui.Size? _currentCanvasPhysicalSize; ui.Size? _currentSurfaceSize; - double _currentDevicePixelRatio = -1; /// This is only valid after the first frame or if [ensureSurface] has been /// called - bool get usingSoftwareBackend => _glContext == null || - _grContext == null || webGLVersion == -1 || configuration.canvasKitForceCpuOnly; + bool get usingSoftwareBackend => + _glContext == null || + _grContext == null || + webGLVersion == -1 || + configuration.canvasKitForceCpuOnly; /// Ensure that the initial surface exists and has a size of at least [size]. /// @@ -159,22 +175,10 @@ class Surface { } // TODO(jonahwilliams): this is somewhat wasteful. We should probably // eagerly setup this surface instead of delaying until the first frame? - // Or at least cache the estimated window size. + // Or at least cache the estimated window sizeThis is the first frame we have rendered with this canvas. createOrUpdateSurface(size); } - /// This method is not supported if software rendering is used. - CkSurface createRenderTargetSurface(ui.Size size) { - assert(!usingSoftwareBackend); - - final SkSurface skSurface = canvasKit.MakeRenderTarget( - _grContext!, - size.width.ceil(), - size.height.ceil(), - )!; - return CkSurface(skSurface, _glContext); - } - /// Creates a and SkSurface for the given [size]. CkSurface createOrUpdateSurface(ui.Size size) { if (size.isEmpty) { @@ -188,11 +192,6 @@ class Surface { if (previousSurfaceSize != null && size.width == previousSurfaceSize.width && size.height == previousSurfaceSize.height) { - // The existing surface is still reusable. - if (window.devicePixelRatio != _currentDevicePixelRatio) { - _updateLogicalHtmlCanvasSize(); - _translateCanvas(); - } return _surface!; } @@ -205,12 +204,16 @@ class Surface { final ui.Size newSize = size * 1.4; _surface?.dispose(); _surface = null; - htmlCanvas!.width = newSize.width; - htmlCanvas!.height = newSize.height; + if (Surface.offscreenCanvasSupported) { + _offscreenCanvas!.width = newSize.width; + _offscreenCanvas!.height = newSize.height; + } else { + _canvasElement!.width = newSize.width; + _canvasElement!.height = newSize.height; + } _currentCanvasPhysicalSize = newSize; _pixelWidth = newSize.width.ceil(); _pixelHeight = newSize.height.ceil(); - _updateLogicalHtmlCanvasSize(); } } @@ -218,57 +221,20 @@ class Surface { if (_forceNewContext || _currentCanvasPhysicalSize == null) { _surface?.dispose(); _surface = null; - _addedToScene = false; _grContext?.releaseResourcesAndAbandonContext(); _grContext?.delete(); _grContext = null; _createNewCanvas(size); _currentCanvasPhysicalSize = size; - } else if (window.devicePixelRatio != _currentDevicePixelRatio) { - _updateLogicalHtmlCanvasSize(); } - _currentDevicePixelRatio = window.devicePixelRatio; _currentSurfaceSize = size; - _translateCanvas(); _surface?.dispose(); _surface = _createNewSurface(size); return _surface!; } - /// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device - /// pixels. - /// - /// The logical size of the canvas is not based on the size of the window - /// but on the size of the canvas, which, due to `ceil()` above, may not be - /// the same as the window. We do not round/floor/ceil the logical size as - /// CSS pixels can contain more than one physical pixel and therefore to - /// match the size of the window precisely we use the most precise floating - /// point value we can get. - void _updateLogicalHtmlCanvasSize() { - final double logicalWidth = _pixelWidth / window.devicePixelRatio; - final double logicalHeight = _pixelHeight / window.devicePixelRatio; - final DomCSSStyleDeclaration style = htmlCanvas!.style; - style.width = '${logicalWidth}px'; - style.height = '${logicalHeight}px'; - } - - /// Translate the canvas so the surface covers the visible portion of the - /// screen. - /// - /// The may be larger than the visible screen, but the SkSurface is - /// exactly the size of the visible screen. Unfortunately, the SkSurface is - /// drawn in the lower left corner of the , and without translation, - /// only the top left of the is visible. So we shift the canvas up so - /// the bottom left corner is visible. - void _translateCanvas() { - final int surfaceHeight = _currentSurfaceSize!.height.ceil(); - final double offset = - (_pixelHeight - surfaceHeight) / window.devicePixelRatio; - htmlCanvas!.style.transform = 'translate(0, -${offset}px)'; - } - JSVoid _contextRestoredListener(DomEvent event) { assert( _contextLost, @@ -282,16 +248,11 @@ class Surface { } JSVoid _contextLostListener(DomEvent event) { - assert(event.target == htmlCanvas, + assert(event.target == _offscreenCanvas || event.target == _canvasElement, 'Received a context lost event for a disposed canvas'); - final SurfaceFactory factory = SurfaceFactory.instance; _contextLost = true; - if (factory.isLive(this)) { - _forceNewContext = true; - event.preventDefault(); - } else { - dispose(); - } + _forceNewContext = true; + event.preventDefault(); } /// This function is expensive. @@ -299,18 +260,32 @@ class Surface { /// It's better to reuse canvas if possible. void _createNewCanvas(ui.Size physicalSize) { // Clear the container, if it's not empty. We're going to create a new . - if (this.htmlCanvas != null) { - this.htmlCanvas!.removeEventListener( - 'webglcontextrestored', - _cachedContextRestoredListener, - false, - ); - this.htmlCanvas!.removeEventListener( - 'webglcontextlost', - _cachedContextLostListener, - false, - ); - this.htmlCanvas!.remove(); + if (_offscreenCanvas != null) { + _offscreenCanvas!.removeEventListener( + 'webglcontextrestored', + _cachedContextRestoredListener, + false, + ); + _offscreenCanvas!.removeEventListener( + 'webglcontextlost', + _cachedContextLostListener, + false, + ); + _offscreenCanvas = null; + _cachedContextRestoredListener = null; + _cachedContextLostListener = null; + } else if (_canvasElement != null) { + _canvasElement!.removeEventListener( + 'webglcontextrestored', + _cachedContextRestoredListener, + false, + ); + _canvasElement!.removeEventListener( + 'webglcontextlost', + _cachedContextLostListener, + false, + ); + _canvasElement = null; _cachedContextRestoredListener = null; _cachedContextLostListener = null; } @@ -319,25 +294,22 @@ class Surface { // we ensure that the rendred picture covers the entire browser window. _pixelWidth = physicalSize.width.ceil(); _pixelHeight = physicalSize.height.ceil(); - final DomCanvasElement htmlCanvas = createDomCanvasElement( - width: _pixelWidth, - height: _pixelHeight, - ); - this.htmlCanvas = htmlCanvas; - - // The DOM elements used to render pictures are used purely to put pixels on - // the screen. They have no semantic information. If an assistive technology - // attempts to scan picture content it will look like garbage and confuse - // users. UI semantics are exported as a separate DOM tree rendered parallel - // to pictures. - // - // Why are layer and scene elements not hidden from ARIA? Because those - // elements may contain platform views, and platform views must be - // accessible. - htmlCanvas.setAttribute('aria-hidden', 'true'); - - htmlCanvas.style.position = 'absolute'; - _updateLogicalHtmlCanvasSize(); + DomEventTarget htmlCanvas; + if (Surface.offscreenCanvasSupported) { + final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas( + _pixelWidth, + _pixelHeight, + ); + htmlCanvas = offscreenCanvas; + _offscreenCanvas = offscreenCanvas; + _canvasElement = null; + } else { + final DomCanvasElement canvas = + createDomCanvasElement(width: _pixelWidth, height: _pixelHeight); + htmlCanvas = canvas; + _canvasElement = canvas; + _offscreenCanvas = null; + } // When the browser tab using WebGL goes dormant the browser and/or OS may // decide to clear GPU resources to let other tabs/programs use the GPU. @@ -345,7 +317,8 @@ class Surface { // notification. When we receive this notification we force a new context. // // See also: https://www.khronos.org/webgl/wiki/HandlingContextLost - _cachedContextRestoredListener = createDomEventListener(_contextRestoredListener); + _cachedContextRestoredListener = + createDomEventListener(_contextRestoredListener); _cachedContextLostListener = createDomEventListener(_contextLostListener); htmlCanvas.addEventListener( 'webglcontextlost', @@ -361,15 +334,24 @@ class Surface { _contextLost = false; if (webGLVersion != -1 && !configuration.canvasKitForceCpuOnly) { - final int glContext = canvasKit.GetWebGLContext( - htmlCanvas, - SkWebGLContextOptions( - // Default to no anti-aliasing. Paint commands can be explicitly - // anti-aliased by setting their `Paint` object's `antialias` property. - antialias: _kUsingMSAA ? 1 : 0, - majorVersion: webGLVersion.toDouble(), - ), - ).toInt(); + int glContext = 0; + final SkWebGLContextOptions options = SkWebGLContextOptions( + // Default to no anti-aliasing. Paint commands can be explicitly + // anti-aliased by setting their `Paint` object's `antialias` property. + antialias: _kUsingMSAA ? 1 : 0, + majorVersion: webGLVersion.toDouble(), + ); + if (Surface.offscreenCanvasSupported) { + glContext = canvasKit.GetOffscreenWebGLContext( + _offscreenCanvas!, + options, + ).toInt(); + } else { + glContext = canvasKit.GetWebGLContext( + _canvasElement!, + options, + ).toInt(); + } _glContext = glContext; @@ -387,40 +369,38 @@ class Surface { _syncCacheBytes(); } } - - htmlElement.append(htmlCanvas); } void _initWebglParams() { - final WebGLContext gl = htmlCanvas!.getGlContext(webGLVersion); + WebGLContext gl; + if (Surface.offscreenCanvasSupported) { + gl = _offscreenCanvas!.getGlContext(webGLVersion); + } else { + gl = _canvasElement!.getGlContext(webGLVersion); + } _sampleCount = gl.getParameter(gl.samples); _stencilBits = gl.getParameter(gl.stencilBits); } CkSurface _createNewSurface(ui.Size size) { - assert(htmlCanvas != null); + assert(_offscreenCanvas != null || _canvasElement != null); if (webGLVersion == -1) { - return _makeSoftwareCanvasSurface( - htmlCanvas!, 'WebGL support not detected'); + return _makeSoftwareCanvasSurface('WebGL support not detected'); } else if (configuration.canvasKitForceCpuOnly) { - return _makeSoftwareCanvasSurface( - htmlCanvas!, 'CPU rendering forced by application'); + return _makeSoftwareCanvasSurface('CPU rendering forced by application'); } else if (_glContext == 0) { - return _makeSoftwareCanvasSurface( - htmlCanvas!, 'Failed to initialize WebGL context'); + return _makeSoftwareCanvasSurface('Failed to initialize WebGL context'); } else { final SkSurface? skSurface = canvasKit.MakeOnScreenGLSurface( - _grContext!, - size.width.roundToDouble(), - size.height.roundToDouble(), - SkColorSpaceSRGB, - _sampleCount, - _stencilBits - ); + _grContext!, + size.width.roundToDouble(), + size.height.roundToDouble(), + SkColorSpaceSRGB, + _sampleCount, + _stencilBits); if (skSurface == null) { - return _makeSoftwareCanvasSurface( - htmlCanvas!, 'Failed to initialize WebGL surface'); + return _makeSoftwareCanvasSurface('Failed to initialize WebGL surface'); } return CkSurface(skSurface, _glContext); @@ -429,14 +409,20 @@ class Surface { static bool _didWarnAboutWebGlInitializationFailure = false; - CkSurface _makeSoftwareCanvasSurface( - DomCanvasElement htmlCanvas, String reason) { + CkSurface _makeSoftwareCanvasSurface(String reason) { if (!_didWarnAboutWebGlInitializationFailure) { printWarning('WARNING: Falling back to CPU-only rendering. $reason.'); _didWarnAboutWebGlInitializationFailure = true; } + + SkSurface surface; + if (Surface.offscreenCanvasSupported) { + surface = canvasKit.MakeOffscreenSWCanvasSurface(_offscreenCanvas!); + } else { + surface = canvasKit.MakeSWCanvasSurface(_canvasElement!); + } return CkSurface( - canvasKit.MakeSWCanvasSurface(htmlCanvas), + surface, null, ); } @@ -447,15 +433,19 @@ class Surface { } void dispose() { - htmlCanvas?.removeEventListener( + _offscreenCanvas?.removeEventListener( 'webglcontextlost', _cachedContextLostListener, false); - htmlCanvas?.removeEventListener( + _offscreenCanvas?.removeEventListener( 'webglcontextrestored', _cachedContextRestoredListener, false); _cachedContextLostListener = null; _cachedContextRestoredListener = null; - htmlElement.remove(); _surface?.dispose(); } + + /// Safari 15 doesn't support OffscreenCanvas at all. Safari 16 supports + /// OffscreenCanvas, but only with the context2d API, not WebGL. + static bool get offscreenCanvasSupported => + browserSupportsOffscreenCanvas && !isSafari; } /// A Dart wrapper around Skia's CkSurface. diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart b/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart deleted file mode 100644 index ee5b001dd8e04..0000000000000 --- a/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart +++ /dev/null @@ -1,167 +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. -import 'dart:math' as math show max; - -import 'package:meta/meta.dart'; - -import '../../engine.dart'; - -/// Caches surfaces used to overlay platform views. -class SurfaceFactory { - SurfaceFactory(int maximumSurfaces) - : maximumSurfaces = math.max(maximumSurfaces, 1) { - assert(() { - if (maximumSurfaces < 1) { - printWarning('Attempted to create a $SurfaceFactory with ' - '$maximumSurfaces maximum surfaces. At least 1 surface is required ' - 'for rendering.'); - } - registerHotRestartListener(debugClear); - return true; - }()); - } - - /// The lazy-initialized singleton surface factory. - /// - /// [debugClear] causes this singleton to be reinitialized. - static SurfaceFactory get instance => - _instance ??= SurfaceFactory(configuration.canvasKitMaximumSurfaces); - - /// Returns the raw (potentially uninitialized) value of the singleton. - /// - /// Useful in tests for checking the lifecycle of this class. - static SurfaceFactory? get debugUninitializedInstance => _instance; - - // Override the current instance with a new one. - // - // This should only be used in tests. - static void debugSetInstance(SurfaceFactory newInstance) { - _instance = newInstance; - } - - static SurfaceFactory? _instance; - - /// The base surface to paint on. This is the default surface which will be - /// painted to. If there are no platform views, then this surface will receive - /// all painting commands. - final Surface baseSurface = Surface(); - - /// The maximum number of surfaces which can be live at once. - final int maximumSurfaces; - - /// A surface used specifically for `Picture.toImage` when software rendering - /// is supported. - late final Surface pictureToImageSurface = Surface(); - - /// The maximum number of assignable overlays. - /// - /// This is just `maximumSurfaces - 1` (the maximum number of surfaces minus - /// the required base surface). - int get maximumOverlays => maximumSurfaces - 1; - - /// Surfaces created by this factory which are currently in use. - final List _liveSurfaces = []; - - /// Surfaces created by this factory which are no longer in use. These can be - /// reused. - final List _cache = []; - - /// The number of surfaces which have been created by this factory. - int get _surfaceCount => _liveSurfaces.length + _cache.length + 1; - - /// The number of available overlay surfaces. - /// - /// This does not include the base surface. - int get numAvailableOverlays => maximumOverlays - _liveSurfaces.length; - - /// The number of surfaces created by this factory. Used for testing. - @visibleForTesting - int get debugSurfaceCount => _surfaceCount; - - /// Returns the number of cached surfaces. - /// - /// Useful in tests. - int get debugCacheSize => _cache.length; - - /// Gets an overlay surface from the cache or creates a new one if it wouldn't - /// exceed the maximum. If there are no available surfaces, returns `null`. - Surface? getSurface() { - if (_cache.isNotEmpty) { - final Surface surface = _cache.removeLast(); - _liveSurfaces.add(surface); - return surface; - } else if (debugSurfaceCount < maximumSurfaces) { - final Surface surface = Surface(); - _liveSurfaces.add(surface); - return surface; - } else { - return null; - } - } - - /// Releases all surfaces so they can be reused in the next frame. - /// - /// If a released surface is in the DOM, it is not removed. This allows the - /// engine to release the surfaces at the end of the frame so they are ready - /// to be used in the next frame, but still used for painting in the current - /// frame. - void releaseSurfaces() { - _cache.addAll(_liveSurfaces); - _liveSurfaces.clear(); - } - - /// Removes all surfaces except the base surface from the DOM. - /// - /// This is called at the beginning of the frame to prepare for painting into - /// the new surfaces. - void removeSurfacesFromDom() { - _cache.forEach(_removeFromDom); - } - - // Removes [surface] from the DOM. - void _removeFromDom(Surface surface) { - surface.htmlElement.remove(); - } - - /// Signals that a surface is no longer being used. It can be reused. - void releaseSurface(Surface surface) { - assert(surface != baseSurface, 'Attempting to release the base surface'); - assert( - _liveSurfaces.contains(surface), - 'Attempting to release a Surface which ' - 'was not created by this factory'); - surface.htmlElement.remove(); - _liveSurfaces.remove(surface); - _cache.add(surface); - } - - /// Returns [true] if [surface] is currently being used to paint content. - /// - /// The base surface always counts as live. - /// - /// If a surface is not live, then it must be in the cache and ready to be - /// reused. - bool isLive(Surface surface) { - if (surface == baseSurface || - _liveSurfaces.contains(surface)) { - return true; - } - assert(_cache.contains(surface)); - return false; - } - - /// Dispose all surfaces created by this factory. Used in tests. - void debugClear() { - for (final Surface surface in _cache) { - surface.dispose(); - } - for (final Surface surface in _liveSurfaces) { - surface.dispose(); - } - baseSurface.dispose(); - _liveSurfaces.clear(); - _cache.clear(); - _instance = null; - } -} diff --git a/lib/web_ui/lib/src/engine/canvaskit/util.dart b/lib/web_ui/lib/src/engine/canvaskit/util.dart index 29855455abd3a..2b2e646dd117b 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/util.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/util.dart @@ -73,10 +73,10 @@ class SkiaShadowFlags { // material spec. const double ckShadowAmbientAlpha = 0.039; const double ckShadowSpotAlpha = 0.25; -const double ckShadowLightRadius = 1.1; -const double ckShadowLightHeight = 600.0; const double ckShadowLightXOffset = 0; const double ckShadowLightYOffset = -450; +const double ckShadowLightHeight = 600; +const double ckShadowLightRadius = 800; const double ckShadowLightXTangent = ckShadowLightXOffset / ckShadowLightHeight; const double ckShadowLightYTangent = ckShadowLightYOffset / ckShadowLightHeight; @@ -161,13 +161,15 @@ void drawSkShadow( bool transparentOccluder, double devicePixelRatio, ) { - final int flags = transparentOccluder + int flags = transparentOccluder ? SkiaShadowFlags.kTransparentOccluderShadowFlags : SkiaShadowFlags.kDefaultShadowFlags; + flags |= SkiaShadowFlags.kDirectionalLight_ShadowFlag; final ui.Color inAmbient = color.withAlpha((color.alpha * ckShadowAmbientAlpha).round()); - final ui.Color inSpot = color.withAlpha((color.alpha * ckShadowSpotAlpha).round()); + final ui.Color inSpot = + color.withAlpha((color.alpha * ckShadowSpotAlpha).round()); final SkTonalColors inTonalColors = SkTonalColors( ambient: makeFreshSkColor(inAmbient), @@ -180,10 +182,10 @@ void drawSkShadow( path.skiaObject, Float32List(3)..[2] = devicePixelRatio * elevation, Float32List(3) - ..[0] = ckShadowLightXOffset - ..[1] = ckShadowLightYOffset - ..[2] = devicePixelRatio * ckShadowLightHeight, - devicePixelRatio * ckShadowLightRadius, + ..[0] = 0 + ..[1] = -1 + ..[2] = 1, + ckShadowLightRadius / ckShadowLightHeight, tonalColors.ambient, tonalColors.spot, flags.toDouble(), diff --git a/lib/web_ui/lib/src/engine/configuration.dart b/lib/web_ui/lib/src/engine/configuration.dart index 151e2c2bc83d7..1a5dff3bdd696 100644 --- a/lib/web_ui/lib/src/engine/configuration.dart +++ b/lib/web_ui/lib/src/engine/configuration.dart @@ -257,15 +257,10 @@ class FlutterConfiguration { 'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', ); - /// The maximum number of overlay surfaces that the CanvasKit renderer will use. - /// - /// Overlay surfaces are extra WebGL `` elements used to paint on top - /// of platform views. Too many platform views can cause the browser to run - /// out of resources (memory, CPU, GPU) to handle the content efficiently. - /// The number of overlay surfaces is therefore limited. - /// - /// This value can be specified using either the `FLUTTER_WEB_MAXIMUM_SURFACES` - /// environment variable, or using the runtime configuration. + /// This is deprecated. The CanvasKit renderer will only ever create one + /// WebGL context, obviating the problem this configuration was meant to + /// solve originally. + @Deprecated('Setting canvasKitMaximumSurfaces has no effect') int get canvasKitMaximumSurfaces => _configuration?.canvasKitMaximumSurfaces?.toInt() ?? _defaultCanvasKitMaximumSurfaces; static const int _defaultCanvasKitMaximumSurfaces = int.fromEnvironment( diff --git a/lib/web_ui/lib/src/engine/display.dart b/lib/web_ui/lib/src/engine/display.dart index dd05733025aef..1264b49865b09 100644 --- a/lib/web_ui/lib/src/engine/display.dart +++ b/lib/web_ui/lib/src/engine/display.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:ui/ui.dart' as ui; import '../engine.dart'; @@ -59,3 +61,76 @@ class EngineFlutterDisplay extends ui.Display { double? _debugDevicePixelRatioOverride; } + +/// Controls the screen orientation using the browser's screen orientation API. +class ScreenOrientation { + const ScreenOrientation(); + + static ScreenOrientation get instance => _instance; + static const ScreenOrientation _instance = ScreenOrientation(); + + static const String lockTypeAny = 'any'; + static const String lockTypeNatural = 'natural'; + static const String lockTypeLandscape = 'landscape'; + static const String lockTypePortrait = 'portrait'; + static const String lockTypePortraitPrimary = 'portrait-primary'; + static const String lockTypePortraitSecondary = 'portrait-secondary'; + static const String lockTypeLandscapePrimary = 'landscape-primary'; + static const String lockTypeLandscapeSecondary = 'landscape-secondary'; + + /// Sets preferred screen orientation. + /// + /// Specifies the set of orientations the application interface can be + /// displayed in. + /// + /// The [orientations] argument is a list of DeviceOrientation values. + /// The empty list uses Screen unlock api and causes the application to + /// defer to the operating system default. + /// + /// See w3c screen api: https://www.w3.org/TR/screen-orientation/ + Future setPreferredOrientation(List orientations) async { + final DomScreen? screen = domWindow.screen; + if (screen != null) { + final DomScreenOrientation? screenOrientation = screen.orientation; + if (screenOrientation != null) { + if (orientations.isEmpty) { + screenOrientation.unlock(); + return true; + } else { + final String? lockType = + _deviceOrientationToLockType(orientations.first as String?); + if (lockType != null) { + try { + await screenOrientation.lock(lockType); + return true; + } catch (_) { + // On Chrome desktop an error with 'not supported on this device + // error' is fired. + return Future.value(false); + } + } + } + } + } + // API is not supported on this browser return false. + return false; + } + + // Converts device orientation to w3c OrientationLockType enum. + // + // See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock + static String? _deviceOrientationToLockType(String? deviceOrientation) { + switch (deviceOrientation) { + case 'DeviceOrientation.portraitUp': + return lockTypePortraitPrimary; + case 'DeviceOrientation.portraitDown': + return lockTypePortraitSecondary; + case 'DeviceOrientation.landscapeLeft': + return lockTypeLandscapePrimary; + case 'DeviceOrientation.landscapeRight': + return lockTypeLandscapeSecondary; + default: + return null; + } + } +} diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index c5b4411a97b70..9eab879b37c63 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -322,7 +322,7 @@ external DomHTMLDocument get domDocument; @JS() @staticInterop -class DomEventTarget {} +class DomEventTarget implements JSObject {} extension DomEventTargetExtension on DomEventTarget { @JS('addEventListener') @@ -529,6 +529,7 @@ extension DomElementExtension on DomElement { createDomListWrapper(_children); external DomElement? get firstElementChild; + external DomElement? get lastElementChild; external DomElement? get nextElementSibling; @@ -743,6 +744,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { set alignContent(String value) => setProperty('align-content', value); set textAlign(String value) => setProperty('text-align', value); set font(String value) => setProperty('font', value); + set cursor(String value) => setProperty('cursor', value); String get width => getPropertyValue('width'); String get height => getPropertyValue('height'); String get position => getPropertyValue('position'); @@ -807,6 +809,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { String get alignContent => getPropertyValue('align-content'); String get textAlign => getPropertyValue('text-align'); String get font => getPropertyValue('font'); + String get cursor => getPropertyValue('cursor'); @JS('getPropertyValue') external JSString _getPropertyValue(JSString property); @@ -1132,7 +1135,7 @@ extension WebGLContextExtension on WebGLContext { @JS() @staticInterop -abstract class DomCanvasImageSource {} +abstract class DomCanvasImageSource implements JSObject {} @JS() @staticInterop @@ -1425,7 +1428,7 @@ extension DomImageDataExtension on DomImageData { @JS('ImageBitmap') @staticInterop -class DomImageBitmap {} +class DomImageBitmap implements JSObject {} extension DomImageBitmapExtension on DomImageBitmap { external JSNumber get width; @@ -1828,7 +1831,7 @@ class HttpFetchError implements Exception { @JS() @staticInterop -class DomResponse {} +class DomResponse implements JSObject {} extension DomResponseExtension on DomResponse { @JS('status') @@ -1865,7 +1868,7 @@ extension DomHeadersExtension on DomHeaders { @JS() @staticInterop -class _DomReadableStream {} +class _DomReadableStream implements JSObject {} extension _DomReadableStreamExtension on _DomReadableStream { external _DomStreamReader getReader(); @@ -3024,6 +3027,31 @@ extension DomMessageEventExtension on DomMessageEvent { @JS('origin') external JSString get _origin; String get origin => _origin.toDart; + + /// The source may be a `WindowProxy`, a `MessagePort`, or a `ServiceWorker`. + /// + /// When a message is sent from an iframe through `window.parent.postMessage` + /// the source will be a `WindowProxy` which has the same methods as [Window]. + DomMessageEventSource get source => js_util.getProperty(this, 'source'); + + List get ports => + js_util.getProperty>(this, 'ports').cast(); +} + +@JS() +@staticInterop +class DomMessageEventSource {} + +extension DomMEssageEventSourceExtension on DomMessageEventSource { + external DomMessageEventLocation? get location; +} + +@JS() +@staticInterop +class DomMessageEventLocation {} + +extension DomMessageEventSourceExtension on DomMessageEventLocation { + external String? get href; } @JS() @@ -3588,3 +3616,10 @@ extension DomFinalizationRegistryExtension on DomFinalizationRegistry { /// Whether the current browser supports `FinalizationRegistry`. bool browserSupportsFinalizationRegistry = _finalizationRegistryConstructor != null; + +@JS() +@staticInterop +extension JSArrayExtension on JSArray { + external void push(JSAny value); + external JSNumber get length; +} diff --git a/lib/web_ui/lib/src/engine/embedder.dart b/lib/web_ui/lib/src/engine/embedder.dart index 623d6aa9bdd1c..7f362eee1f455 100644 --- a/lib/web_ui/lib/src/engine/embedder.dart +++ b/lib/web_ui/lib/src/engine/embedder.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:ui/src/engine/safe_browser_api.dart'; import 'package:ui/ui.dart' as ui; @@ -288,78 +286,6 @@ class FlutterViewEmbedder { } } - static const String orientationLockTypeAny = 'any'; - static const String orientationLockTypeNatural = 'natural'; - static const String orientationLockTypeLandscape = 'landscape'; - static const String orientationLockTypePortrait = 'portrait'; - static const String orientationLockTypePortraitPrimary = 'portrait-primary'; - static const String orientationLockTypePortraitSecondary = - 'portrait-secondary'; - static const String orientationLockTypeLandscapePrimary = 'landscape-primary'; - static const String orientationLockTypeLandscapeSecondary = - 'landscape-secondary'; - - /// Sets preferred screen orientation. - /// - /// Specifies the set of orientations the application interface can be - /// displayed in. - /// - /// The [orientations] argument is a list of DeviceOrientation values. - /// The empty list uses Screen unlock api and causes the application to - /// defer to the operating system default. - /// - /// See w3c screen api: https://www.w3.org/TR/screen-orientation/ - Future setPreferredOrientation(List orientations) { - final DomScreen? screen = domWindow.screen; - if (screen != null) { - final DomScreenOrientation? screenOrientation = screen.orientation; - if (screenOrientation != null) { - if (orientations.isEmpty) { - screenOrientation.unlock(); - return Future.value(true); - } else { - final String? lockType = - _deviceOrientationToLockType(orientations.first as String?); - if (lockType != null) { - final Completer completer = Completer(); - try { - screenOrientation.lock(lockType).then((dynamic _) { - completer.complete(true); - }).catchError((dynamic error) { - // On Chrome desktop an error with 'not supported on this device - // error' is fired. - completer.complete(false); - }); - } catch (_) { - return Future.value(false); - } - return completer.future; - } - } - } - } - // API is not supported on this browser return false. - return Future.value(false); - } - - // Converts device orientation to w3c OrientationLockType enum. - // - // See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock - static String? _deviceOrientationToLockType(String? deviceOrientation) { - switch (deviceOrientation) { - case 'DeviceOrientation.portraitUp': - return orientationLockTypePortraitPrimary; - case 'DeviceOrientation.portraitDown': - return orientationLockTypePortraitSecondary; - case 'DeviceOrientation.landscapeLeft': - return orientationLockTypeLandscapePrimary; - case 'DeviceOrientation.landscapeRight': - return orientationLockTypeLandscapeSecondary; - default: - return null; - } - } - /// Add an element as a global resource to be referenced by CSS. /// /// This call create a global resource host element on demand and either @@ -392,20 +318,6 @@ class FlutterViewEmbedder { assert(element.parentNode == _resourcesHost); element.remove(); } - - /// Disables the browser's context menu for this part of the DOM. - /// - /// By default, when a Flutter web app starts, the context menu is enabled. - /// - /// Can be re-enabled by calling [enableContextMenu]. - void disableContextMenu() => _embeddingStrategy.disableContextMenu(); - - /// Enables the browser's context menu for this part of the DOM. - /// - /// By default, when a Flutter web app starts, the context menu is already - /// enabled. Typically, this method would be used after calling - /// [disableContextMenu] to first disable it. - void enableContextMenu() => _embeddingStrategy.enableContextMenu(); } /// The embedder singleton. diff --git a/lib/web_ui/lib/src/engine/font_fallback_data.dart b/lib/web_ui/lib/src/engine/font_fallback_data.dart index 46c76a5881dbc..ab4306885e5a3 100644 --- a/lib/web_ui/lib/src/engine/font_fallback_data.dart +++ b/lib/web_ui/lib/src/engine/font_fallback_data.dart @@ -7,32 +7,33 @@ import 'noto_font.dart'; List getFallbackFontList(bool useColorEmoji) => [ - NotoFont('Noto Sans', 'notosans/v30/o-0IIpQlx3QUlC5A4PNb4j5Ba_2c7A.ttf'), + NotoFont('Noto Sans', 'notosans/v32/o-0IIpQlx3QUlC5A4PNb4j5Ba_2c7A.ttf'), NotoFont('Noto Color Emoji', enabled: useColorEmoji, 'notocoloremoji/v25/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFab5s79iz64w.ttf'), - NotoFont('Noto Emoji', enabled: !useColorEmoji, 'notoemoji/v39/bMrnmSyK7YY-MEu6aWjPDs-ar6uWaGWuob-r0jwvS-FGJCMY.ttf'), - NotoFont('Noto Sans Symbols', 'notosanssymbols/v40/rP2up3q65FkAtHfwd-eIS2brbDN6gxP34F9jRRCe4W3gfQ8gavVFRkzrbQ.ttf'), - NotoFont('Noto Sans Symbols 2', 'notosanssymbols2/v21/I_uyMoGduATTei9eI8daxVHDyfisHr71ypPqfX71-AI.ttf'), - NotoFont('Noto Sans Adlam', 'notosansadlam/v21/neIczCCpqp0s5pPusPamd81eMfjPonvqdbYxxpgufnv0TGnBZLwhuvk.ttf'), + NotoFont('Noto Emoji', enabled: !useColorEmoji, 'notoemoji/v47/bMrnmSyK7YY-MEu6aWjPDs-ar6uWaGWuob-r0jwvS-FGJCMY.ttf'), + NotoFont('Noto Music', 'notomusic/v20/pe0rMIiSN5pO63htf1sxIteQB9Zra1U.ttf'), + NotoFont('Noto Sans Symbols', 'notosanssymbols/v41/rP2up3q65FkAtHfwd-eIS2brbDN6gxP34F9jRRCe4W3gfQ8gavVFRkzrbQ.ttf'), + NotoFont('Noto Sans Symbols 2', 'notosanssymbols2/v22/I_uyMoGduATTei9eI8daxVHDyfisHr71ypPqfX71-AI.ttf'), + NotoFont('Noto Sans Adlam', 'notosansadlam/v22/neIczCCpqp0s5pPusPamd81eMfjPonvqdbYxxpgufnv0TGnBZLwhuvk.ttf'), NotoFont('Noto Sans Anatolian Hieroglyphs', 'notosansanatolianhieroglyphs/v16/ijw9s4roRME5LLRxjsRb8A0gKPSWq4BbDmHHu6j2pEtUJzZWXybIymc5QYo.ttf'), NotoFont('Noto Sans Arabic', 'notosansarabic/v18/nwpxtLGrOAZMl5nJ_wfgRg3DrWFZWsnVBJ_sS6tlqHHFlhQ5l3sQWIHPqzCfyGyvu3CBFQLaig.ttf'), NotoFont('Noto Sans Armenian', 'notosansarmenian/v42/ZgN0jOZKPa7CHqq0h37c7ReDUubm2SEdFXp7ig73qtTY5idb74R9UdM3y2nZLorxb60iYy6zF3Eg.ttf'), - NotoFont('Noto Sans Avestan', 'notosansavestan/v20/bWti7ejKfBziStx7lIzKOLQZKhIJkyu9SASLji8U.ttf'), + NotoFont('Noto Sans Avestan', 'notosansavestan/v21/bWti7ejKfBziStx7lIzKOLQZKhIJkyu9SASLji8U.ttf'), NotoFont('Noto Sans Balinese', 'notosansbalinese/v24/NaPwcYvSBuhTirw6IaFn6UrRDaqje-lpbbRtYf-Fwu2Ov7fdhE5Vd222PPY.ttf'), - NotoFont('Noto Sans Bamum', 'notosansbamum/v26/uk-0EGK3o6EruUbnwovcbBTkkklK_Ya_PBHfNGTPEddO-_gLykxEkxA.ttf'), + NotoFont('Noto Sans Bamum', 'notosansbamum/v27/uk-0EGK3o6EruUbnwovcbBTkkklK_Ya_PBHfNGTPEddO-_gLykxEkxA.ttf'), NotoFont('Noto Sans Bassa Vah', 'notosansbassavah/v17/PN_bRee-r3f7LnqsD5sax12gjZn7mBpL5YwUpA2MBdcFn4MaAc6p34gH-GD7.ttf'), - NotoFont('Noto Sans Batak', 'notosansbatak/v16/gok2H6TwAEdtF9N8-mdTCQvT-Zdgo4_PHuk74A.ttf'), + NotoFont('Noto Sans Batak', 'notosansbatak/v19/gok2H6TwAEdtF9N8-mdTCQvT-Zdgo4_PHuk74A.ttf'), NotoFont('Noto Sans Bengali', 'notosansbengali/v20/Cn-SJsCGWQxOjaGwMQ6fIiMywrNJIky6nvd8BjzVMvJx2mcSPVFpVEqE-6KmsolLudCk8izI0lc.ttf'), - NotoFont('Noto Sans Bhaiksuki', 'notosansbhaiksuki/v15/UcC63EosKniBH4iELXATsSBWdvUHXxhj8rLUdU4wh9U.ttf'), + NotoFont('Noto Sans Bhaiksuki', 'notosansbhaiksuki/v17/UcC63EosKniBH4iELXATsSBWdvUHXxhj8rLUdU4wh9U.ttf'), NotoFont('Noto Sans Brahmi', 'notosansbrahmi/v18/vEFK2-VODB8RrNDvZSUmQQIIByV18tK1W77HtMo.ttf'), NotoFont('Noto Sans Buginese', 'notosansbuginese/v18/esDM30ldNv-KYGGJpKGk18phe_7Da6_gtfuEXLmNtw.ttf'), - NotoFont('Noto Sans Buhid', 'notosansbuhid/v18/Dxxy8jiXMW75w3OmoDXVWJD7YwzAe6tgnaFoGA.ttf'), - NotoFont('Noto Sans Canadian Aboriginal', 'notosanscanadianaboriginal/v21/4C_TLjTuEqPj-8J01CwaGkiZ9os0iGVkezM1mUT-j_Lmlzda6uH_nnX1bzigWLn_yAsg0q0uhQ.ttf'), + NotoFont('Noto Sans Buhid', 'notosansbuhid/v22/Dxxy8jiXMW75w3OmoDXVWJD7YwzAe6tgnaFoGA.ttf'), + NotoFont('Noto Sans Canadian Aboriginal', 'notosanscanadianaboriginal/v22/4C_TLjTuEqPj-8J01CwaGkiZ9os0iGVkezM1mUT-j_Lmlzda6uH_nnX1bzigWLn_yAsg0q0uhQ.ttf'), NotoFont('Noto Sans Carian', 'notosanscarian/v16/LDIpaoiONgYwA9Yc6f0gUILeMIOgs7ob9yGLmfI.ttf'), NotoFont('Noto Sans Caucasian Albanian', 'notosanscaucasianalbanian/v16/nKKA-HM_FYFRJvXzVXaANsU0VzsAc46QGOkWytlTs-TXrYDmoVmRSZo.ttf'), NotoFont('Noto Sans Chakma', 'notosanschakma/v17/Y4GQYbJ8VTEp4t3MKJSMjg5OIzhi4JjTQhYBeYo.ttf'), - NotoFont('Noto Sans Cham', 'notosanscham/v27/pe06MIySN5pO62Z5YkFyQb_bbuRhe6D4yip43qfcERwcv7GykboaLg.ttf'), - NotoFont('Noto Sans Cherokee', 'notosanscherokee/v19/KFOPCm6Yu8uF-29fiz9vQF9YWK6Z8O10cHNA0cSkZCHYWi5PDkm5rAffjl0.ttf'), - NotoFont('Noto Sans Coptic', 'notosanscoptic/v17/iJWfBWmUZi_OHPqn4wq6kgqumOEd78u_VG0xR4Y.ttf'), + NotoFont('Noto Sans Cham', 'notosanscham/v29/pe06MIySN5pO62Z5YkFyQb_bbuRhe6D4yip43qfcERwcv7GykboaLg.ttf'), + NotoFont('Noto Sans Cherokee', 'notosanscherokee/v20/KFOPCm6Yu8uF-29fiz9vQF9YWK6Z8O10cHNA0cSkZCHYWi5PDkm5rAffjl0.ttf'), + NotoFont('Noto Sans Coptic', 'notosanscoptic/v20/iJWfBWmUZi_OHPqn4wq6kgqumOEd78u_VG0xR4Y.ttf'), NotoFont('Noto Sans Cuneiform', 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWgb9JlRQueeQ.ttf'), NotoFont('Noto Sans Cypriot', 'notosanscypriot/v15/8AtzGta9PYqQDjyp79a6f8Cj-3a3cxIsK5MPpahF.ttf'), NotoFont('Noto Sans Deseret', 'notosansdeseret/v17/MwQsbgPp1eKH6QsAVuFb9AZM6MMr2Vq9ZnJSZtQG.ttf'), @@ -46,10 +47,10 @@ List getFallbackFontList(bool useColorEmoji) => [ NotoFont('Noto Sans Gothic', 'notosansgothic/v16/TuGKUUVzXI5FBtUq5a8bj6wRbzxTFMX40kFQRx0.ttf'), NotoFont('Noto Sans Grantha', 'notosansgrantha/v17/3y976akwcCjmsU8NDyrKo3IQfQ4o-r8cFeulHc6N.ttf'), NotoFont('Noto Sans Gujarati', 'notosansgujarati/v23/wlpWgx_HC1ti5ViekvcxnhMlCVo3f5pv17ivlzsUB14gg1TMR2Gw4VceEl7MA_ypFwPM_OdiEH0s.ttf'), - NotoFont('Noto Sans Gunjala Gondi', 'notosansgunjalagondi/v17/bWto7e7KfBziStx7lIzKPrcSMwcEnCv6DW7n5hcVXYMTK4q1.ttf'), + NotoFont('Noto Sans Gunjala Gondi', 'notosansgunjalagondi/v19/bWtX7e7KfBziStx7lIzKPrcSMwcEnCv6DW7n5g0ef3PLtymzNxYL4YDE4J4vCTxEJQ.ttf'), NotoFont('Noto Sans Gurmukhi', 'notosansgurmukhi/v26/w8g9H3EvQP81sInb43inmyN9zZ7hb7ATbSWo4q8dJ74a3cVrYFQ_bogT0-gPeG1OenbxZ_trdp7h.ttf'), NotoFont('Noto Sans HK', 'notosanshk/v31/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oWTiYjNvVA.ttf'), - NotoFont('Noto Sans Hanunoo', 'notosanshanunoo/v17/f0Xs0fCv8dxkDWlZSoXOj6CphMloFsEsEpgL_ix2.ttf'), + NotoFont('Noto Sans Hanunoo', 'notosanshanunoo/v20/f0Xs0fCv8dxkDWlZSoXOj6CphMloFsEsEpgL_ix2.ttf'), NotoFont('Noto Sans Hatran', 'notosanshatran/v16/A2BBn4Ne0RgnVF3Lnko-0sOBIfL_mM83r1nwzDs.ttf'), NotoFont('Noto Sans Hebrew', 'notosanshebrew/v43/or3HQ7v33eiDljA1IufXTtVf7V6RvEEdhQlk0LlGxCyaeNKYZC0sqk3xXGiXd4qtoiJltutR2g.ttf'), NotoFont('Noto Sans Imperial Aramaic', 'notosansimperialaramaic/v16/a8IMNpjwKmHXpgXbMIsbTc_kvks91LlLetBr5itQrtdml3YfPNno.ttf'), @@ -57,24 +58,24 @@ List getFallbackFontList(bool useColorEmoji) => [ NotoFont('Noto Sans Inscriptional Pahlavi', 'notosansinscriptionalpahlavi/v16/ll8UK3GaVDuxR-TEqFPIbsR79Xxz9WEKbwsjpz7VklYlC7FCVtqVOAYK0QA.ttf'), NotoFont('Noto Sans Inscriptional Parthian', 'notosansinscriptionalparthian/v16/k3k7o-IMPvpLmixcA63oYi-yStDkgXuXncL7dzfW3P4TAJ2yklBJ2jNkLlLr.ttf'), NotoFont('Noto Sans JP', 'notosansjp/v52/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj75vY0rw-oME.ttf'), - NotoFont('Noto Sans Javanese', 'notosansjavanese/v21/2V01KJkDAIA6Hp4zoSScDjV0Y-eoHAHT-Z3MngEefiidxJnkFFliZYWj4O8.ttf'), + NotoFont('Noto Sans Javanese', 'notosansjavanese/v23/2V01KJkDAIA6Hp4zoSScDjV0Y-eoHAHT-Z3MngEefiidxJnkFFliZYWj4O8.ttf'), NotoFont('Noto Sans KR', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLTq8H4hfeE.ttf'), - NotoFont('Noto Sans Kaithi', 'notosanskaithi/v18/buEtppS9f8_vkXadMBJJu0tWjLwjQi0KdoZIKlo.ttf'), + NotoFont('Noto Sans Kaithi', 'notosanskaithi/v20/buEtppS9f8_vkXadMBJJu0tWjLwjQi0KdoZIKlo.ttf'), NotoFont('Noto Sans Kannada', 'notosanskannada/v26/8vIs7xs32H97qzQKnzfeXycxXZyUmySvZWItmf1fe6TVmgop9ndpS-BqHEyGrDvNzSIMLsPKrkY.ttf'), - NotoFont('Noto Sans Kayah Li', 'notosanskayahli/v20/B50nF61OpWTRcGrhOVJJwOMXdca6Yecki3E06x2jVTX3WCc3CZH4EXLuKVM.ttf'), + NotoFont('Noto Sans Kayah Li', 'notosanskayahli/v21/B50nF61OpWTRcGrhOVJJwOMXdca6Yecki3E06x2jVTX3WCc3CZH4EXLuKVM.ttf'), NotoFont('Noto Sans Kharoshthi', 'notosanskharoshthi/v16/Fh4qPiLjKS30-P4-pGMMXCCfvkc5Vd7KE5z4rFyx5mR1.ttf'), NotoFont('Noto Sans Khmer', 'notosanskhmer/v23/ijw3s5roRME5LLRxjsRb-gssOenAyendxrgV2c-Zw-9vbVUti_Z_dWgtWYuNAJz4kAbrddiA.ttf'), - NotoFont('Noto Sans Khojki', 'notosanskhojki/v16/-nFnOHM29Oofr2wohFbTuPPKVWpmK_d709jy92k.ttf'), - NotoFont('Noto Sans Khudawadi', 'notosanskhudawadi/v18/fdNi9t6ZsWBZ2k5ltHN73zZ5hc8HANlHIjRnVVXz9MY.ttf'), - NotoFont('Noto Sans Lao', 'notosanslao/v24/bx6lNx2Ol_ixgdYWLm9BwxM3NW6BOkuf763Clj73CiQ_J1Djx9pidOt4ccbdf5MK3riB2w.ttf'), - NotoFont('Noto Sans Lepcha', 'notosanslepcha/v16/0QI7MWlB_JWgA166SKhu05TekNS32AJstqBXgd4.ttf'), + NotoFont('Noto Sans Khojki', 'notosanskhojki/v18/-nFnOHM29Oofr2wohFbTuPPKVWpmK_d709jy92k.ttf'), + NotoFont('Noto Sans Khudawadi', 'notosanskhudawadi/v21/fdNi9t6ZsWBZ2k5ltHN73zZ5hc8HANlHIjRnVVXz9MY.ttf'), + NotoFont('Noto Sans Lao', 'notosanslao/v30/bx6lNx2Ol_ixgdYWLm9BwxM3NW6BOkuf763Clj73CiQ_J1Djx9pidOt4ccbdf5MK3riB2w.ttf'), + NotoFont('Noto Sans Lepcha', 'notosanslepcha/v19/0QI7MWlB_JWgA166SKhu05TekNS32AJstqBXgd4.ttf'), NotoFont('Noto Sans Limbu', 'notosanslimbu/v22/3JnlSDv90Gmq2mrzckOBBRRoNJVj0MF3OHRDnA.ttf'), NotoFont('Noto Sans Linear A', 'notosanslineara/v18/oPWS_l16kP4jCuhpgEGmwJOiA18FZj22zmHQAGQicw.ttf'), NotoFont('Noto Sans Linear B', 'notosanslinearb/v17/HhyJU4wt9vSgfHoORYOiXOckKNB737IV3BkFTq4EPw.ttf'), NotoFont('Noto Sans Lisu', 'notosanslisu/v25/uk-3EGO3o6EruUbnwovcYhz6kh57_nqbcTdjJnHP2Vwt29IlxkVdig.ttf'), NotoFont('Noto Sans Lycian', 'notosanslycian/v15/QldVNSNMqAsHtsJ7UmqxBQA9r8wA5_naCJwn00E.ttf'), NotoFont('Noto Sans Lydian', 'notosanslydian/v17/c4m71mVzGN7s8FmIukZJ1v4ZlcPReUPXMoIjEQI.ttf'), - NotoFont('Noto Sans Mahajani', 'notosansmahajani/v17/-F6sfiVqLzI2JPCgQBnw60Agp0JrvD5Fh8ARHNh4zg.ttf'), + NotoFont('Noto Sans Mahajani', 'notosansmahajani/v19/-F6sfiVqLzI2JPCgQBnw60Agp0JrvD5Fh8ARHNh4zg.ttf'), NotoFont('Noto Sans Malayalam', 'notosansmalayalam/v26/sJoi3K5XjsSdcnzn071rL37lpAOsUThnDZIfPdbeSNzVakglNM-Qw8EaeB8Nss-_RuD9BFzEr6HxEA.ttf'), NotoFont('Noto Sans Mandaic', 'notosansmandaic/v16/cIfnMbdWt1w_HgCcilqhKQBo_OsMI5_A_gMk0izH.ttf'), NotoFont('Noto Sans Manichaean', 'notosansmanichaean/v17/taiVGntiC4--qtsfi4Jp9-_GkPZZCcrfekqCNTtFCtdX.ttf'), @@ -82,30 +83,30 @@ List getFallbackFontList(bool useColorEmoji) => [ NotoFont('Noto Sans Masaram Gondi', 'notosansmasaramgondi/v17/6xK_dThFKcWIu4bpRBjRYRV7KZCbUq6n_1kPnuGe7RI9WSWX.ttf'), NotoFont('Noto Sans Math', 'notosansmath/v15/7Aump_cpkSecTWaHRlH2hyV5UHkG-V048PW0.ttf'), NotoFont('Noto Sans Mayan Numerals', 'notosansmayannumerals/v16/PlIuFk25O6RzLfvNNVSivR09_KqYMwvvDKYjfIiE68oo6eepYQ.ttf'), - NotoFont('Noto Sans Medefaidrin', 'notosansmedefaidrin/v22/WwkzxOq6Dk-wranENynkfeVsNbRZtbOIdLb1exeM4ZeuabBfmErWlT318e5A3rw.ttf'), - NotoFont('Noto Sans Meetei Mayek', 'notosansmeeteimayek/v14/HTxAL3QyKieByqY9eZPFweO0be7M21uSphSdhqILnmrRfJ8t_1TJ_vTW5PgeFYVa.ttf'), + NotoFont('Noto Sans Medefaidrin', 'notosansmedefaidrin/v23/WwkzxOq6Dk-wranENynkfeVsNbRZtbOIdLb1exeM4ZeuabBfmErWlT318e5A3rw.ttf'), + NotoFont('Noto Sans Meetei Mayek', 'notosansmeeteimayek/v15/HTxAL3QyKieByqY9eZPFweO0be7M21uSphSdhqILnmrRfJ8t_1TJ_vTW5PgeFYVa.ttf'), NotoFont('Noto Sans Meroitic', 'notosansmeroitic/v17/IFS5HfRJndhE3P4b5jnZ3ITPvC6i00UDgDhTiKY9KQ.ttf'), NotoFont('Noto Sans Miao', 'notosansmiao/v17/Dxxz8jmXMW75w3OmoDXVV4zyZUjgUYVslLhx.ttf'), - NotoFont('Noto Sans Modi', 'notosansmodi/v20/pe03MIySN5pO62Z5YkFyT7jeav5qWVAgVol-.ttf'), + NotoFont('Noto Sans Modi', 'notosansmodi/v23/pe03MIySN5pO62Z5YkFyT7jeav5qWVAgVol-.ttf'), NotoFont('Noto Sans Mongolian', 'notosansmongolian/v17/VdGCAYADGIwE0EopZx8xQfHlgEAMsrToxLsg6-av1x0.ttf'), NotoFont('Noto Sans Mro', 'notosansmro/v18/qWcsB6--pZv9TqnUQMhe9b39WDzRtjkho4M.ttf'), NotoFont('Noto Sans Multani', 'notosansmultani/v20/9Bty3ClF38_RfOpe1gCaZ8p30BOFO1A0pfCs5Kos.ttf'), NotoFont('Noto Sans Myanmar', 'notosansmyanmar/v20/AlZq_y1ZtY3ymOryg38hOCSdOnFq0En23OU4o1AC.ttf'), - NotoFont('Noto Sans NKo', 'notosansnko/v2/esDX31ZdNv-KYGGJpKGk2_RpMpCMHMLBrdA.ttf'), + NotoFont('Noto Sans NKo', 'notosansnko/v6/esDX31ZdNv-KYGGJpKGk2_RpMpCMHMLBrdA.ttf'), NotoFont('Noto Sans Nabataean', 'notosansnabataean/v16/IFS4HfVJndhE3P4b5jnZ34DfsjO330dNoBJ9hK8kMK4.ttf'), - NotoFont('Noto Sans New Tai Lue', 'notosansnewtailue/v20/H4cKBW-Pl9DZ0Xe_nHUapt7PovLXAhAnY7wqaLy-OJgU3p_pdeXAYUbghFPKzeY.ttf'), + NotoFont('Noto Sans New Tai Lue', 'notosansnewtailue/v22/H4cKBW-Pl9DZ0Xe_nHUapt7PovLXAhAnY7wqaLy-OJgU3p_pdeXAYUbghFPKzeY.ttf'), NotoFont('Noto Sans Newa', 'notosansnewa/v16/7r3fqXp6utEsO9pI4f8ok8sWg8n_qN4R5lNU.ttf'), NotoFont('Noto Sans Nushu', 'notosansnushu/v19/rnCw-xRQ3B7652emAbAe_Ai1IYaFWFAMArZKqQ.ttf'), NotoFont('Noto Sans Ogham', 'notosansogham/v17/kmKlZqk1GBDGN0mY6k5lmEmww4hrt5laQxcoCA.ttf'), - NotoFont('Noto Sans Ol Chiki', 'notosansolchiki/v21/N0b92TJNOPt-eHmFZCdQbrL32r-4CvhzDzRwlxOQYuVALWk267I6gVrz5gQ.ttf'), - NotoFont('Noto Sans Old Hungarian', 'notosansoldhungarian/v16/E213_cD6hP3GwCJPEUssHEM0KqLaHJXg2PiIgRfjbg5nCYXt.ttf'), + NotoFont('Noto Sans Ol Chiki', 'notosansolchiki/v29/N0b92TJNOPt-eHmFZCdQbrL32r-4CvhzDzRwlxOQYuVALWk267I6gVrz5gQ.ttf'), + NotoFont('Noto Sans Old Hungarian', 'notosansoldhungarian/v18/E213_cD6hP3GwCJPEUssHEM0KqLaHJXg2PiIgRfjbg5nCYXt.ttf'), NotoFont('Noto Sans Old Italic', 'notosansolditalic/v16/TuGOUUFzXI5FBtUq5a8bh68BJxxEVam7tWlRdRhtCC4d.ttf'), NotoFont('Noto Sans Old North Arabian', 'notosansoldnortharabian/v16/esDF30BdNv-KYGGJpKGk2tNiMt7Jar6olZDyNdr81zBQmUo_xw4ABw.ttf'), NotoFont('Noto Sans Old Permic', 'notosansoldpermic/v17/snf1s1q1-dF8pli1TesqcbUY4Mr-ElrwKLdXgv_dKYB5.ttf'), NotoFont('Noto Sans Old Persian', 'notosansoldpersian/v16/wEOjEAbNnc5caQTFG18FHrZr9Bp6-8CmIJ_tqOlQfx9CjA.ttf'), NotoFont('Noto Sans Old Sogdian', 'notosansoldsogdian/v16/3JnjSCH90Gmq2mrzckOBBhFhdrMst48aURt7neIqM-9uyg.ttf'), NotoFont('Noto Sans Old South Arabian', 'notosansoldsoutharabian/v16/3qT5oiOhnSyU8TNFIdhZTice3hB_HWKsEnF--0XCHiKx1OtDT9HwTA.ttf'), - NotoFont('Noto Sans Old Turkic', 'notosansoldturkic/v16/yMJNMJVya43H0SUF_WmcGEQVqoEMKDKbsE2RjEw-Vyws.ttf'), + NotoFont('Noto Sans Old Turkic', 'notosansoldturkic/v17/yMJNMJVya43H0SUF_WmcGEQVqoEMKDKbsE2RjEw-Vyws.ttf'), NotoFont('Noto Sans Oriya', 'notosansoriya/v27/AYCppXfzfccDCstK_hrjDyADv5e9748vhj3CJBLHIARtgD6TJQS0dJT5Ivj0f6_c6LhHBRe-.ttf'), NotoFont('Noto Sans Osage', 'notosansosage/v18/oPWX_kB6kP4jCuhpgEGmw4mtAVtXRlaSxkrMCQ.ttf'), NotoFont('Noto Sans Osmanya', 'notosansosmanya/v18/8vIS7xs32H97qzQKnzfeWzUyUpOJmz6kR47NCV5Z.ttf'), @@ -115,13 +116,13 @@ List getFallbackFontList(bool useColorEmoji) => [ NotoFont('Noto Sans Phags Pa', 'notosansphagspa/v15/pxiZyoo6v8ZYyWh5WuPeJzMkd4SrGChkqkSsrvNXiA.ttf'), NotoFont('Noto Sans Phoenician', 'notosansphoenician/v17/jizFRF9Ksm4Bt9PvcTaEkIHiTVtxmFtS5X7Jot-p5561.ttf'), NotoFont('Noto Sans Psalter Pahlavi', 'notosanspsalterpahlavi/v16/rP2Vp3K65FkAtHfwd-eISGznYihzggmsicPfud3w1G3KsUQBct4.ttf'), - NotoFont('Noto Sans Rejang', 'notosansrejang/v18/Ktk2AKuMeZjqPnXgyqrib7DIogqwN4O3WYZB_sU.ttf'), + NotoFont('Noto Sans Rejang', 'notosansrejang/v21/Ktk2AKuMeZjqPnXgyqrib7DIogqwN4O3WYZB_sU.ttf'), NotoFont('Noto Sans Runic', 'notosansrunic/v17/H4c_BXWPl9DZ0Xe_nHUaus7W68WWaxpvHtgIYg.ttf'), NotoFont('Noto Sans SC', 'notosanssc/v36/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYxNbPzS5HE.ttf'), - NotoFont('Noto Sans Saurashtra', 'notosanssaurashtra/v19/ea8GacQ0Wfz_XKWXe6OtoA8w8zvmYwTef9ndjhPTSIx9.ttf'), + NotoFont('Noto Sans Saurashtra', 'notosanssaurashtra/v23/ea8GacQ0Wfz_XKWXe6OtoA8w8zvmYwTef9ndjhPTSIx9.ttf'), NotoFont('Noto Sans Sharada', 'notosanssharada/v16/gok0H7rwAEdtF9N8-mdTGALG6p0kwoXLPOwr4H8a.ttf'), NotoFont('Noto Sans Shavian', 'notosansshavian/v17/CHy5V_HZE0jxJBQlqAeCKjJvQBNF4EFQSplv2Cwg.ttf'), - NotoFont('Noto Sans Siddham', 'notosanssiddham/v17/OZpZg-FwqiNLe9PELUikxTWDoCCeGqndk3Ic92ZH.ttf'), + NotoFont('Noto Sans Siddham', 'notosanssiddham/v20/OZpZg-FwqiNLe9PELUikxTWDoCCeGqndk3Ic92ZH.ttf'), NotoFont('Noto Sans Sinhala', 'notosanssinhala/v26/yMJ2MJBya43H0SUF_WmcBEEf4rQVO2P524V5N_MxQzQtb-tf5dJbC30Fu9zUwg2a5lgLpJwbQRM.ttf'), NotoFont('Noto Sans Sogdian', 'notosanssogdian/v16/taiQGn5iC4--qtsfi4Jp6eHPnfxQBo--Pm6KHidM.ttf'), NotoFont('Noto Sans Sora Sompeng', 'notosanssorasompeng/v24/PlIRFkO5O6RzLfvNNVSioxM2_OTrEhPyDLolKvCsHzCxWuGkYHR818DpZXJQd4Mu.ttf'), @@ -130,705 +131,705 @@ List getFallbackFontList(bool useColorEmoji) => [ NotoFont('Noto Sans Syloti Nagri', 'notosanssylotinagri/v20/uU9eCAQZ75uhfF9UoWDRiY3q7Sf_VFV3m4dGFVfxN87gsj0.ttf'), NotoFont('Noto Sans Syriac', 'notosanssyriac/v16/Ktk7AKuMeZjqPnXgyqribqzQqgW0LYiVqV7dXcP0C-VD9MaJyZfUL_FC.ttf'), NotoFont('Noto Sans TC', 'notosanstc/v35/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_CpOtma3uNQ.ttf'), - NotoFont('Noto Sans Tagalog', 'notosanstagalog/v18/J7aFnoNzCnFcV9ZI-sUYuvote1R0wwEAA8jHexnL.ttf'), + NotoFont('Noto Sans Tagalog', 'notosanstagalog/v22/J7aFnoNzCnFcV9ZI-sUYuvote1R0wwEAA8jHexnL.ttf'), NotoFont('Noto Sans Tagbanwa', 'notosanstagbanwa/v18/Y4GWYbB8VTEp4t3MKJSMmQdIKjRtt_nZRjQEaYpGoQ.ttf'), NotoFont('Noto Sans Tai Le', 'notosanstaile/v17/vEFK2-VODB8RrNDvZSUmVxEATwR58tK1W77HtMo.ttf'), - NotoFont('Noto Sans Tai Tham', 'notosanstaitham/v19/kJEbBv0U4hgtwxDUw2x9q7tbjLIfbPGHBoaVSAZ3MdLJBCUbPgquyaRGKMw.ttf'), - NotoFont('Noto Sans Tai Viet', 'notosanstaiviet/v16/8QIUdj3HhN_lv4jf9vsE-9GMOLsaSPZr644fWsRO9w.ttf'), + NotoFont('Noto Sans Tai Tham', 'notosanstaitham/v20/kJEbBv0U4hgtwxDUw2x9q7tbjLIfbPGHBoaVSAZ3MdLJBCUbPgquyaRGKMw.ttf'), + NotoFont('Noto Sans Tai Viet', 'notosanstaiviet/v19/8QIUdj3HhN_lv4jf9vsE-9GMOLsaSPZr644fWsRO9w.ttf'), NotoFont('Noto Sans Takri', 'notosanstakri/v23/TuGJUVpzXI5FBtUq5a8bnKIOdTwQNO_W3khJXg.ttf'), NotoFont('Noto Sans Tamil', 'notosanstamil/v27/ieVc2YdFI3GCY6SyQy1KfStzYKZgzN1z4LKDbeZce-0429tBManUktuex7vGo70RqKDt_EvT.ttf'), NotoFont('Noto Sans Tamil Supplement', 'notosanstamilsupplement/v21/DdTz78kEtnooLS5rXF1DaruiCd_bFp_Ph4sGcn7ax_vsAeMkeq1x.ttf'), NotoFont('Noto Sans Telugu', 'notosanstelugu/v25/0FlxVOGZlE2Rrtr-HmgkMWJNjJ5_RyT8o8c7fHkeg-esVC5dzHkHIJQqrEntezbqQUbf-3v37w.ttf'), NotoFont('Noto Sans Thaana', 'notosansthaana/v23/C8c14dM-vnz-s-3jaEsxlxHkBH-WZOETXfoQrfQ9Y4XrbhLhnu4-tbNu.ttf'), NotoFont('Noto Sans Thai', 'notosansthai/v20/iJWnBXeUZi_OHPqn4wq6hQ2_hbJ1xyN9wd43SofNWcd1MKVQt_So_9CdU5RtpzF-QRvzzXg.ttf'), - NotoFont('Noto Sans Tifinagh', 'notosanstifinagh/v17/I_uzMoCduATTei9eI8dawkHIwvmhCvbn6rnEcXfs4Q.ttf'), + NotoFont('Noto Sans Tifinagh', 'notosanstifinagh/v20/I_uzMoCduATTei9eI8dawkHIwvmhCvbn6rnEcXfs4Q.ttf'), NotoFont('Noto Sans Tirhuta', 'notosanstirhuta/v16/t5t6IQYRNJ6TWjahPR6X-M-apUyby7uGUBsTrn5P.ttf'), NotoFont('Noto Sans Ugaritic', 'notosansugaritic/v16/3qTwoiqhnSyU8TNFIdhZVCwbjCpkAXXkMhoIkiazfg.ttf'), NotoFont('Noto Sans Vai', 'notosansvai/v17/NaPecZTSBuhTirw6IaFn_UrURMTsDIRSfr0.ttf'), NotoFont('Noto Sans Wancho', 'notosanswancho/v17/zrf-0GXXyfn6Fs0lH9P4cUubP0GBqAPopiRfKp8.ttf'), NotoFont('Noto Sans Warang Citi', 'notosanswarangciti/v17/EYqtmb9SzL1YtsZSScyKDXIeOv3w-zgsNvKRpeVCCXzdgA.ttf'), NotoFont('Noto Sans Yi', 'notosansyi/v19/sJoD3LFXjsSdcnzn071rO3apxVDJNVgSNg.ttf'), - NotoFont('Noto Sans Zanabazar Square', 'notosanszanabazarsquare/v16/Cn-jJsuGWQxOjaGwMQ6fOicyxLBEMRfDtkzl4uagQtJxOCEgN0Gc.ttf'), + NotoFont('Noto Sans Zanabazar Square', 'notosanszanabazarsquare/v19/Cn-jJsuGWQxOjaGwMQ6fOicyxLBEMRfDtkzl4uagQtJxOCEgN0Gc.ttf'), ]; -// 313 unique sets of fonts containing 3816 font references encoded in 4439 characters +// 315 unique sets of fonts containing 3642 font references encoded in 4273 characters const String encodedFontSets = - // #0: 5 fonts: HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - '1phb2gl,' - // #1: 3 fonts: HK₄₁, SC₁₁₀, TC₁₂₂. - '1p2ql,' - // #2: 4 fonts: HK₄₁, JP₄₉, SC₁₁₀, TC₁₂₂. - '1ph2il,' - // #3: 1 font: SC₁₁₀. - '4g,' + // #0: 5 fonts: HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + '1qhb2gl,' + // #1: 3 fonts: HK₄₂, SC₁₁₁, TC₁₂₃. + '1q2ql,' + // #2: 4 fonts: HK₄₂, JP₅₀, SC₁₁₁, TC₁₂₃. + '1qh2il,' + // #3: 1 font: SC₁₁₁. + '4h,' // #4: 0 fonts. ',' - // #5: 2 fonts: JP₄₉, SC₁₁₀. - '1x2i,' - // #6: 2 fonts: HK₄₁, TC₁₂₂. - '1p3c,' - // #7: 1 font: JP₄₉. - '1x,' - // #8: 4 fonts: HK₄₁, KR₅₁, SC₁₁₀, TC₁₂₂. - '1pj2gl,' - // #9: 3 fonts: JP₄₉, KR₅₁, SC₁₁₀. - '1xb2g,' - // #10: 2 fonts: KR₅₁, SC₁₁₀. - '1z2g,' + // #5: 2 fonts: JP₅₀, SC₁₁₁. + '1y2i,' + // #6: 2 fonts: HK₄₂, TC₁₂₃. + '1q3c,' + // #7: 1 font: JP₅₀. + '1y,' + // #8: 4 fonts: HK₄₂, KR₅₂, SC₁₁₁, TC₁₂₃. + '1qj2gl,' + // #9: 3 fonts: JP₅₀, KR₅₂, SC₁₁₁. + '1yb2g,' + // #10: 2 fonts: KR₅₂, SC₁₁₁. + '2a2g,' // #11: 1 font: Noto Sans₀. 'a,' - // #12: 1 font: Symbols 2₄. - 'e,' - // #13: 3 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₄. - 'bab,' - // #14: 1 font: Math₇₃. - '2v,' + // #12: 1 font: Symbols 2₅. + 'f,' + // #13: 3 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₅. + 'bac,' + // #14: 1 font: Math₇₄. + '2w,' // #15: 2 fonts: Noto Color Emoji₁, Noto Emoji₂. 'ba,' - // #16: 2 fonts: JP₄₉, KR₅₁. - '1xb,' - // #17: 1 font: KR₅₁. - '1z,' - // #18: 6 fonts: Noto Sans₀, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'a1ohb2gl,' - // #19: 1 font: Symbols₃. - 'd,' - // #20: 6 fonts: HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - '1phbv1kl,' - // #21: 128 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #22: 6 fonts: Symbols 2₄, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'e1khb2gl,' - // #23: 3 fonts: HK₄₁, JP₄₉, TC₁₂₂. - '1ph2u,' - // #24: 123 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, Javanese₅₀, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaabbaaaaaabbaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaabaaaaabaaaaabaaabaaaaaaaaaabaaaaaaaaaaaaaaaaaa,' - // #25: 1 font: Arabic₇. - 'h,' - // #26: 6 fonts: Symbols₃, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'd1lhb2gl,' - // #27: 2 fonts: Noto Sans₀, Math₇₃. - 'a2u,' - // #28: 3 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₃. - 'baa,' - // #29: 1 font: Lao₅₉. - '2h,' - // #30: 1 font: Tamil₁₂₉. - '4z,' - // #31: 1 font: Bengali₁₄. - 'o,' - // #32: 1 font: Grantha₃₇. - '1l,' - // #33: 1 font: Gurmukhi₄₀. - '1o,' - // #34: 8 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₄, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'bab1khb2gl,' - // #35: 2 fonts: Noto Sans₀, Devanagari₂₉. - 'a1c,' - // #36: 1 font: Gujarati₃₈. - '1m,' - // #37: 1 font: Oriya₉₉. - '3v,' - // #38: 1 font: Kannada₅₃. - '2b,' - // #39: 1 font: Sinhala₁₁₅. - '4l,' - // #40: 2 fonts: Noto Sans₀, Coptic₂₅. - 'ay,' - // #41: 1 font: Telugu₁₃₁. - '5b,' - // #42: 129 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #43: 7 fonts: Noto Color Emoji₁, Noto Emoji₂, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'ba1mhb2gl,' - // #44: 1 font: Georgian₃₄. - '1i,' - // #45: 4 fonts: HK₄₁, JP₄₉, KR₅₁, TC₁₂₂. - '1phb2s,' - // #46: 1 font: Hebrew₄₄. - '1s,' - // #47: 130 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #48: 7 fonts: Noto Sans₀, HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - 'a1ohbv1kl,' - // #49: 8 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₃, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'baa1lhb2gl,' - // #50: 4 fonts: HK₄₁, JP₄₉, KR₅₁, SC₁₁₀. - '1phb2g,' - // #51: 1 font: Kharoshthi₅₅. - '2d,' - // #52: 1 font: Linear B₆₃. - '2l,' - // #53: 3 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉. - 'ano,' - // #54: 7 fonts: Symbols 2₄, HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - 'e1khbv1kl,' - // #55: 1 font: Glagolitic₃₅. - '1j,' - // #56: 3 fonts: HK₄₁, KR₅₁, TC₁₂₂. - '1pj2s,' - // #57: 1 font: Malayalam₆₈. - '2q,' - // #58: 1 font: Masaram Gondi₇₂. - '2u,' - // #59: 1 font: Mongolian₈₀. - '3c,' - // #60: 2 fonts: Symbols₃, Math₇₃. - 'd2r,' - // #61: 1 font: Cypriot₂₇. - '1b,' - // #62: 2 fonts: Grantha₃₇, Tamil₁₂₉. - '1l3n,' - // #63: 1 font: Gunjala Gondi₃₉. - '1n,' - // #64: 7 fonts: HK₄₁, JP₄₉, KR₅₁, New Tai Lue₈₆, SC₁₁₀, TC₁₂₂, Yi₁₄₀. - '1phb1ixlr,' - // #65: 2 fonts: Noto Sans₀, Duployan₃₀. - 'a1d,' - // #66: 2 fonts: Symbols 2₄, Math₇₃. - 'e2q,' - // #67: 1 font: Armenian₈. + // #16: 2 fonts: JP₅₀, KR₅₂. + '1yb,' + // #17: 1 font: KR₅₂. + '2a,' + // #18: 6 fonts: Noto Sans₀, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'a1phb2gl,' + // #19: 1 font: Symbols₄. + 'e,' + // #20: 6 fonts: HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + '1qhbv1kl,' + // #21: 133 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #22: 6 fonts: Symbols 2₅, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'f1khb2gl,' + // #23: 3 fonts: HK₄₂, JP₅₀, TC₁₂₃. + '1qh2u,' + // #24: 128 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, Javanese₅₁, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaabaaaaaabbaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaabaaaaaaaaaabaaaaaaaaaaaaaaaaaa,' + // #25: 1 font: Arabic₈. 'i,' - // #68: 1 font: Duployan₃₀. - '1e,' - // #69: 1 font: Limbu₆₁. - '2j,' - // #70: 1 font: Multani₈₂. - '3e,' - // #71: 1 font: New Tai Lue₈₆. - '3i,' - // #72: 1 font: Pahawh Hmong₁₀₂. - '3y,' - // #73: 1 font: Tai Tham₁₂₆. - '4w,' - // #74: 131 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Arabic₇, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaaaaaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #75: 128 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #76: 3 fonts: Noto Sans₀, Devanagari₂₉, Grantha₃₇. - 'a1ch,' - // #77: 3 fonts: Noto Sans₀, Devanagari₂₉, Sharada₁₁₂. - 'a1c3e,' - // #78: 2 fonts: Noto Sans₀, Elbasan₃₂. - 'a1f,' - // #79: 1 font: Bhaiksuki₁₅. + // #26: 2 fonts: Noto Sans₀, Math₇₄. + 'a2v,' + // #27: 6 fonts: Symbols₄, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'e1lhb2gl,' + // #28: 3 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₄. + 'bab,' + // #29: 1 font: Tamil₁₃₀. + '5a,' + // #30: 1 font: Bengali₁₅. 'p,' - // #80: 1 font: Cham₂₃. - 'x,' - // #81: 1 font: Cuneiform₂₆. - '1a,' - // #82: 3 fonts: HK₄₁, JP₄₉, KR₅₁. - '1phb,' - // #83: 1 font: Khmer₅₆. + // #31: 1 font: Grantha₃₈. + '1m,' + // #32: 1 font: Gurmukhi₄₁. + '1p,' + // #33: 134 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #34: 8 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₅, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'bac1khb2gl,' + // #35: 2 fonts: Noto Sans₀, Devanagari₃₀. + 'a1d,' + // #36: 1 font: Gujarati₃₉. + '1n,' + // #37: 1 font: Oriya₁₀₀. + '3w,' + // #38: 1 font: Kannada₅₄. + '2c,' + // #39: 1 font: Sinhala₁₁₆. + '4m,' + // #40: 2 fonts: Noto Sans₀, Coptic₂₆. + 'az,' + // #41: 1 font: Telugu₁₃₂. + '5c,' + // #42: 1 font: Lao₆₀. + '2i,' + // #43: 7 fonts: Noto Color Emoji₁, Noto Emoji₂, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'ba1nhb2gl,' + // #44: 1 font: Georgian₃₅. + '1j,' + // #45: 4 fonts: HK₄₂, JP₅₀, KR₅₂, TC₁₂₃. + '1qhb2s,' + // #46: 1 font: Hebrew₄₅. + '1t,' + // #47: 7 fonts: Noto Sans₀, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'a1phbv1kl,' + // #48: 8 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₄, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'bab1lhb2gl,' + // #49: 4 fonts: HK₄₂, JP₅₀, KR₅₂, SC₁₁₁. + '1qhb2g,' + // #50: 1 font: Kharoshthi₅₆. '2e,' - // #84: 1 font: Myanmar₈₃. + // #51: 1 font: Linear B₆₄. + '2m,' + // #52: 3 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀. + 'aoo,' + // #53: 7 fonts: Symbols 2₅, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'f1khbv1kl,' + // #54: 1 font: Glagolitic₃₆. + '1k,' + // #55: 3 fonts: HK₄₂, KR₅₂, TC₁₂₃. + '1qj2s,' + // #56: 1 font: Malayalam₆₉. + '2r,' + // #57: 1 font: Masaram Gondi₇₃. + '2v,' + // #58: 1 font: Mongolian₈₁. + '3d,' + // #59: 2 fonts: Symbols₄, Math₇₄. + 'e2r,' + // #60: 1 font: Cypriot₂₈. + '1c,' + // #61: 2 fonts: Grantha₃₈, Tamil₁₃₀. + '1m3n,' + // #62: 1 font: Gunjala Gondi₄₀. + '1o,' + // #63: 7 fonts: HK₄₂, JP₅₀, KR₅₂, New Tai Lue₈₇, SC₁₁₁, TC₁₂₃, Yi₁₄₁. + '1qhb1ixlr,' + // #64: 2 fonts: Noto Sans₀, Duployan₃₁. + 'a1e,' + // #65: 2 fonts: Symbols 2₅, Math₇₄. + 'f2q,' + // #66: 1 font: Armenian₉. + 'j,' + // #67: 1 font: Duployan₃₁. + '1f,' + // #68: 1 font: Limbu₆₂. + '2k,' + // #69: 1 font: Multani₈₃. '3f,' - // #85: 130 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'aaaaaaabaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #86: 7 fonts: Noto Sans₀, Adlam₅, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'ae1jhb2gl,' - // #87: 2 fonts: Noto Sans₀, Glagolitic₃₅. - 'a1i,' - // #88: 2 fonts: Noto Sans₀, Syriac₁₂₁. - 'a4q,' - // #89: 7 fonts: Symbols₃, HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - 'd1lhbv1kl,' - // #90: 1 font: Adlam₅. - 'f,' - // #91: 4 fonts: Arabic₇, NKo₈₄, Syriac₁₂₁, Thaana₁₃₂. - 'h2y1kk,' - // #92: 2 fonts: Arabic₇, Syriac₁₂₁. - 'h4j,' - // #93: 1 font: Brahmi₁₆. + // #70: 1 font: Pahawh Hmong₁₀₃. + '3z,' + // #71: 1 font: Tai Tham₁₂₇. + '4x,' + // #72: 135 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Arabic₈, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaaaaaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #73: 133 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #74: 3 fonts: Noto Sans₀, Devanagari₃₀, Grantha₃₈. + 'a1dh,' + // #75: 3 fonts: Noto Sans₀, Devanagari₃₀, Sharada₁₁₃. + 'a1d3e,' + // #76: 2 fonts: Noto Sans₀, Elbasan₃₃. + 'a1g,' + // #77: 1 font: Noto Music₃. + 'd,' + // #78: 1 font: Bhaiksuki₁₆. 'q,' - // #94: 1 font: Canadian Aboriginal₁₉. - 't,' - // #95: 1 font: Cherokee₂₄. + // #79: 1 font: Cham₂₄. 'y,' - // #96: 1 font: Coptic₂₅. + // #80: 1 font: Cuneiform₂₇. + '1b,' + // #81: 3 fonts: HK₄₂, JP₅₀, KR₅₂. + '1qhb,' + // #82: 1 font: Khmer₅₇. + '2f,' + // #83: 1 font: Myanmar₈₄. + '3g,' + // #84: 1 font: New Tai Lue₈₇. + '3j,' + // #85: 135 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'aaaaaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #86: 7 fonts: Noto Sans₀, Adlam₆, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'af1jhb2gl,' + // #87: 2 fonts: Noto Sans₀, Glagolitic₃₆. + 'a1j,' + // #88: 2 fonts: Noto Sans₀, Syriac₁₂₂. + 'a4r,' + // #89: 1 font: Adlam₆. + 'g,' + // #90: 4 fonts: Arabic₈, NKo₈₅, Syriac₁₂₂, Thaana₁₃₃. + 'i2y1kk,' + // #91: 2 fonts: Arabic₈, Syriac₁₂₂. + 'i4j,' + // #92: 1 font: Brahmi₁₇. + 'r,' + // #93: 1 font: Canadian Aboriginal₂₀. + 'u,' + // #94: 1 font: Cherokee₂₅. 'z,' - // #97: 6 fonts: HK₄₁, JP₄₉, KR₅₁, New Tai Lue₈₆, SC₁₁₀, TC₁₂₂. - '1phb1ixl,' - // #98: 6 fonts: HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂, Yi₁₄₀. - '1phb2glr,' - // #99: 1 font: Hatran₄₃. - '1r,' - // #100: 1 font: Javanese₅₀. - '1y,' - // #101: 1 font: Lepcha₆₀. - '2i,' - // #102: 1 font: Linear A₆₂. - '2k,' - // #103: 1 font: Marchen₇₁. - '2t,' - // #104: 1 font: Meetei Mayek₇₆. - '2y,' - // #105: 1 font: Meroitic₇₇. + // #95: 1 font: Coptic₂₆. + '1a,' + // #96: 6 fonts: HK₄₂, JP₅₀, KR₅₂, New Tai Lue₈₇, SC₁₁₁, TC₁₂₃. + '1qhb1ixl,' + // #97: 6 fonts: HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃, Yi₁₄₁. + '1qhb2glr,' + // #98: 1 font: Hatran₄₄. + '1s,' + // #99: 1 font: Javanese₅₁. + '1z,' + // #100: 1 font: Lepcha₆₁. + '2j,' + // #101: 1 font: Linear A₆₃. + '2l,' + // #102: 1 font: Marchen₇₂. + '2u,' + // #103: 1 font: Meetei Mayek₇₇. '2z,' - // #106: 1 font: Miao₇₈. + // #104: 1 font: Meroitic₇₈. '3a,' - // #107: 1 font: Mro₈₁. - '3d,' - // #108: 1 font: Old Hungarian₉₁. - '3n,' - // #109: 1 font: Psalter Pahlavi₁₀₇. - '4d,' - // #110: 1 font: Syriac₁₂₁. - '4r,' - // #111: 1 font: Tagbanwa₁₂₄. - '4u,' - // #112: 1 font: Tifinagh₁₃₄. - '5e,' - // #113: 129 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Arabic₇, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaaaaaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #114: 130 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, Myanmar₈₃, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaaaaaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #115: 124 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, Javanese₅₀, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaabbaaaaaabbaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaabaaaaaaaaaabaaaaaaaaaaaaaaaaaa,' - // #116: 2 fonts: Noto Sans₀, Adlam₅. - 'ae,' - // #117: 3 fonts: Noto Sans₀, Adlam₅, Arabic₇. - 'aeb,' - // #118: 5 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Grantha₃₇, Kannada₅₃. - 'anohp,' - // #119: 2 fonts: Noto Sans₀, Caucasian Albanian₂₁. - 'au,' - // #120: 8 fonts: Noto Sans₀, Elbasan₃₂, HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - 'a1fihbv1kl,' - // #121: 7 fonts: Noto Sans₀, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂, Tamil₁₂₉. - 'a1ohb2glg,' - // #122: 2 fonts: Noto Sans₀, Tifinagh₁₃₄. - 'a5d,' - // #123: 2 fonts: Symbols₃, Symbols 2₄. - 'da,' - // #124: 2 fonts: Arabic₇, Indic Siyaq Numbers₄₆. - 'h1m,' - // #125: 2 fonts: Arabic₇, Thaana₁₃₂. - 'h4u,' - // #126: 1 font: Avestan₉. - 'j,' - // #127: 1 font: Balinese₁₀. + // #105: 1 font: Miao₇₉. + '3b,' + // #106: 1 font: Mro₈₂. + '3e,' + // #107: 1 font: Old Hungarian₉₂. + '3o,' + // #108: 1 font: Psalter Pahlavi₁₀₈. + '4e,' + // #109: 1 font: Syriac₁₂₂. + '4s,' + // #110: 1 font: Tagbanwa₁₂₅. + '4v,' + // #111: 1 font: Tifinagh₁₃₅. + '5f,' + // #112: 136 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'aaaaaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #113: 134 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Arabic₈, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaaaaaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #114: 134 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, Myanmar₈₄, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #115: 129 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, Javanese₅₁, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaabaaaaaabbaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaabaaaaaaaaaabaaaaaaaaaaaaaaaaaa,' + // #116: 2 fonts: Noto Sans₀, Adlam₆. + 'af,' + // #117: 3 fonts: Noto Sans₀, Adlam₆, Arabic₈. + 'afb,' + // #118: 5 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Grantha₃₈, Kannada₅₄. + 'aoohp,' + // #119: 2 fonts: Noto Sans₀, Caucasian Albanian₂₂. + 'av,' + // #120: 8 fonts: Noto Sans₀, Elbasan₃₃, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'a1gihbv1kl,' + // #121: 7 fonts: Noto Sans₀, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃, Tamil₁₃₀. + 'a1phb2glg,' + // #122: 2 fonts: Noto Sans₀, Tifinagh₁₃₅. + 'a5e,' + // #123: 2 fonts: Symbols₄, Symbols 2₅. + 'ea,' + // #124: 7 fonts: Symbols₄, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'e1lhbv1kl,' + // #125: 2 fonts: Arabic₈, Indic Siyaq Numbers₄₇. + 'i1m,' + // #126: 2 fonts: Arabic₈, Thaana₁₃₃. + 'i4u,' + // #127: 1 font: Avestan₁₀. 'k,' - // #128: 1 font: Bamum₁₁. + // #128: 1 font: Balinese₁₁. 'l,' - // #129: 1 font: Bassa Vah₁₂. + // #129: 1 font: Bamum₁₂. 'm,' - // #130: 1 font: Batak₁₃. + // #130: 1 font: Bassa Vah₁₃. 'n,' - // #131: 1 font: Buginese₁₇. - 'r,' - // #132: 1 font: Caucasian Albanian₂₁. - 'v,' - // #133: 1 font: Chakma₂₂. + // #131: 1 font: Batak₁₄. + 'o,' + // #132: 1 font: Buginese₁₈. + 's,' + // #133: 1 font: Caucasian Albanian₂₂. 'w,' - // #134: 6 fonts: HK₄₁, JP₄₉, KR₅₁, Mongolian₈₀, SC₁₁₀, TC₁₂₂. - '1phb1c1dl,' - // #135: 7 fonts: HK₄₁, JP₄₉, KR₅₁, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂, Yi₁₄₀. - '1phb2belr,' - // #136: 1 font: Imperial Aramaic₄₅. - '1t,' - // #137: 1 font: Inscriptional Pahlavi₄₇. - '1v,' - // #138: 1 font: Inscriptional Parthian₄₈. + // #134: 1 font: Chakma₂₃. + 'x,' + // #135: 6 fonts: HK₄₂, JP₅₀, KR₅₂, Mongolian₈₁, SC₁₁₁, TC₁₂₃. + '1qhb1c1dl,' + // #136: 7 fonts: HK₄₂, JP₅₀, KR₅₂, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃, Yi₁₄₁. + '1qhb2belr,' + // #137: 1 font: Imperial Aramaic₄₆. + '1u,' + // #138: 1 font: Inscriptional Pahlavi₄₈. '1w,' - // #139: 4 fonts: JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - '1xb2gl,' - // #140: 1 font: Kaithi₅₂. - '2a,' - // #141: 1 font: Kayah Li₅₄. - '2c,' - // #142: 1 font: Khojki₅₇. - '2f,' - // #143: 1 font: Khudawadi₅₈. + // #139: 1 font: Inscriptional Parthian₄₉. + '1x,' + // #140: 4 fonts: JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + '1yb2gl,' + // #141: 1 font: Kaithi₅₃. + '2b,' + // #142: 1 font: Kayah Li₅₅. + '2d,' + // #143: 1 font: Khojki₅₈. '2g,' - // #144: 2 fonts: Linear A₆₂, Linear B₆₃. - '2ka,' - // #145: 1 font: Lisu₆₄. - '2m,' - // #146: 1 font: Lydian₆₆. - '2o,' - // #147: 1 font: Mandaic₆₉. - '2r,' - // #148: 1 font: Manichaean₇₀. + // #144: 1 font: Khudawadi₅₉. + '2h,' + // #145: 2 fonts: Linear A₆₃, Linear B₆₄. + '2la,' + // #146: 1 font: Lisu₆₅. + '2n,' + // #147: 1 font: Lydian₆₇. + '2p,' + // #148: 1 font: Mandaic₇₀. '2s,' - // #149: 1 font: Modi₇₉. - '3b,' - // #150: 2 fonts: Mongolian₈₀, Phags Pa₁₀₅. - '3cy,' - // #151: 1 font: NKo₈₄. - '3g,' - // #152: 1 font: Nabataean₈₅. + // #149: 1 font: Manichaean₇₁. + '2t,' + // #150: 1 font: Modi₈₀. + '3c,' + // #151: 2 fonts: Mongolian₈₁, Phags Pa₁₀₆. + '3dy,' + // #152: 1 font: NKo₈₅. '3h,' - // #153: 1 font: Newa₈₇. - '3j,' - // #154: 1 font: Nushu₈₈. + // #153: 1 font: Nabataean₈₆. + '3i,' + // #154: 1 font: Newa₈₈. '3k,' - // #155: 1 font: Old Italic₉₂. - '3o,' - // #156: 1 font: Old Persian₉₅. - '3r,' - // #157: 1 font: Osage₁₀₀. - '3w,' - // #158: 1 font: Osmanya₁₀₁. + // #155: 1 font: Nushu₈₉. + '3l,' + // #156: 1 font: Old Italic₉₃. + '3p,' + // #157: 1 font: Old Persian₉₆. + '3s,' + // #158: 1 font: Osage₁₀₁. '3x,' - // #159: 1 font: Phoenician₁₀₆. - '4c,' - // #160: 1 font: Rejang₁₀₈. - '4e,' - // #161: 2 fonts: SC₁₁₀, TC₁₂₂. - '4gl,' - // #162: 1 font: Saurashtra₁₁₁. - '4h,' - // #163: 1 font: Siddham₁₁₄. - '4k,' - // #164: 1 font: Sora Sompeng₁₁₇. - '4n,' - // #165: 1 font: Sundanese₁₁₉. - '4p,' - // #166: 1 font: Tagalog₁₂₃. - '4t,' - // #167: 1 font: Tai Le₁₂₅. - '4v,' - // #168: 1 font: Tai Viet₁₂₇. - '4x,' - // #169: 1 font: Takri₁₂₈. + // #159: 1 font: Osmanya₁₀₂. + '3y,' + // #160: 1 font: Phoenician₁₀₇. + '4d,' + // #161: 1 font: Rejang₁₀₉. + '4f,' + // #162: 2 fonts: SC₁₁₁, TC₁₂₃. + '4hl,' + // #163: 1 font: Saurashtra₁₁₂. + '4i,' + // #164: 1 font: Siddham₁₁₅. + '4l,' + // #165: 1 font: Sora Sompeng₁₁₈. + '4o,' + // #166: 1 font: Sundanese₁₂₀. + '4q,' + // #167: 1 font: Tagalog₁₂₄. + '4u,' + // #168: 1 font: Tai Le₁₂₆. + '4w,' + // #169: 1 font: Tai Viet₁₂₈. '4y,' - // #170: 1 font: Tamil Supplement₁₃₀. - '5a,' - // #171: 1 font: Thai₁₃₃. - '5d,' - // #172: 1 font: Tirhuta₁₃₅. - '5f,' - // #173: 1 font: Ugaritic₁₃₆. + // #170: 1 font: Takri₁₂₉. + '4z,' + // #171: 1 font: Tamil Supplement₁₃₁. + '5b,' + // #172: 1 font: Thai₁₃₄. + '5e,' + // #173: 1 font: Tirhuta₁₃₆. '5g,' - // #174: 1 font: Wancho₁₃₈. - '5i,' - // #175: 1 font: Warang Citi₁₃₉. + // #174: 1 font: Ugaritic₁₃₇. + '5h,' + // #175: 1 font: Wancho₁₃₉. '5j,' - // #176: 1 font: Yi₁₄₀. + // #176: 1 font: Warang Citi₁₄₀. '5k,' - // #177: 3 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂. + // #177: 1 font: Yi₁₄₁. + '5l,' + // #178: 3 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂. 'aaa,' - // #178: 142 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Arabic₇, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Bhaiksuki₁₅, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Caucasian Albanian₂₁, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Cypriot₂₇, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Elymaic₃₃, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lycian₆₅, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, Myanmar₈₃, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Hungarian₉₁, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Old Turkic₉₈, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phags Pa₁₀₅, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #179: 132 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Arabic₇, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'aaaaaaaaaaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #180: 132 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'aaaaaaabaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #181: 131 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'aaaaaaabaaaaaabaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #182: 69 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Arabic₇, Avestan₉, Balinese₁₀, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Chakma₂₂, Cham₂₃, Devanagari₂₉, Egyptian Hieroglyphs₃₁, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, Hanunoo₄₂, Hebrew₄₄, Javanese₅₀, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Meetei Mayek₇₆, Modi₇₉, Mongolian₈₀, Myanmar₈₃, NKo₈₄, New Tai Lue₈₆, Newa₈₇, Old Hungarian₉₁, Oriya₉₉, Pahawh Hmong₁₀₂, Phags Pa₁₀₅, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Saurashtra₁₁₁, Sharada₁₁₂, Siddham₁₁₄, Sinhala₁₁₅, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Warang Citi₁₃₉. - 'aaaebacabaadafbfaaabbfbaaaaaaaaafaaafcacabadhccbacabadaabaaaaaabaaaad,' - // #183: 9 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, HK₄₁, JP₄₉, KR₅₁, Mongolian₈₀, SC₁₁₀, TC₁₂₂. - 'aaa1mhb1c1dl,' - // #184: 8 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'aaa1mhb2gl,' - // #185: 140 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Arabic₇, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Bhaiksuki₁₅, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Caucasian Albanian₂₁, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Cypriot₂₇, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Elymaic₃₃, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lycian₆₅, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, Myanmar₈₃, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Hungarian₉₁, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Old Turkic₉₈, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phags Pa₁₀₅, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #186: 130 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Arabic₇, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaaaaaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaabaaaaacaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #187: 129 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Caucasian Albanian₂₁, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #188: 131 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, Myanmar₈₃, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #189: 131 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, Myanmar₈₃, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phags Pa₁₀₅, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaaaaaaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #190: 127 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Avestan₉, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Carian₂₀, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Devanagari₂₉, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, Inscriptional Parthian₄₈, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Linear A₆₂, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Newa₈₇, Nushu₈₈, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Tamil Supplement₁₃₀, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀, Zanabazar Square₁₄₁. - 'acaaabaaaaaabaaaabaaaabababaaaabaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaabaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' - // #191: 94 fonts: Noto Sans₀, Symbols₃, Symbols 2₄, Adlam₅, Arabic₇, Armenian₈, Balinese₁₀, Bamum₁₁, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Bhaiksuki₁₅, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Caucasian Albanian₂₁, Chakma₂₂, Cham₂₃, Coptic₂₅, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Elbasan₃₂, Gothic₃₆, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, HK₄₁, Hanunoo₄₂, Hebrew₄₄, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Lisu₆₄, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Math₇₃, Meetei Mayek₇₆, Miao₇₈, Modi₇₉, Mongolian₈₀, Myanmar₈₃, NKo₈₄, New Tai Lue₈₆, Newa₈₇, Old Permic₉₄, Old Sogdian₉₆, Oriya₉₉, Osage₁₀₀, Pahawh Hmong₁₀₂, Phags Pa₁₀₅, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Saurashtra₁₁₁, Sharada₁₁₂, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Soyombo₁₁₈, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Wancho₁₃₈, Zanabazar Square₁₄₁. - 'acaababaaaaaaaaabaabdaaadaaaaaabeaaaaaaaaaaaaccaaaaaacbaacabagbcabcbaaaaabaabaaaaaaabaabaaaacc,' - // #192: 100 fonts: Noto Sans₀, Symbols₃, Adlam₅, Anatolian Hieroglyphs₆, Armenian₈, Balinese₁₀, Bassa Vah₁₂, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Canadian Aboriginal₁₉, Chakma₂₂, Cham₂₃, Cherokee₂₄, Coptic₂₅, Cuneiform₂₆, Deseret₂₈, Egyptian Hieroglyphs₃₁, Georgian₃₄, Glagolitic₃₅, Gothic₃₆, Grantha₃₇, HK₄₁, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Imperial Aramaic₄₅, Indic Siyaq Numbers₄₆, Inscriptional Pahlavi₄₇, JP₄₉, Javanese₅₀, KR₅₁, Kaithi₅₂, Kayah Li₅₄, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Limbu₆₁, Linear B₆₃, Lisu₆₄, Lydian₆₆, Mahajani₆₇, Mandaic₆₉, Manichaean₇₀, Marchen₇₁, Masaram Gondi₇₂, Mayan Numerals₇₄, Medefaidrin₇₅, Meetei Mayek₇₆, Meroitic₇₇, Miao₇₈, Modi₇₉, Mongolian₈₀, Mro₈₁, Multani₈₂, NKo₈₄, Nabataean₈₅, New Tai Lue₈₆, Ogham₈₉, Ol Chiki₉₀, Old Italic₉₂, Old North Arabian₉₃, Old Permic₉₄, Old Persian₉₅, Old Sogdian₉₆, Old South Arabian₉₇, Oriya₉₉, Osage₁₀₀, Osmanya₁₀₁, Pahawh Hmong₁₀₂, Palmyrene₁₀₃, Pau Cin Hau₁₀₄, Phoenician₁₀₆, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Runic₁₀₉, SC₁₁₀, Shavian₁₁₃, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sora Sompeng₁₁₇, Soyombo₁₁₈, Syloti Nagri₁₂₀, TC₁₂₂, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Thaana₁₃₂, Thai₁₃₃, Tirhuta₁₃₅, Ugaritic₁₃₆, Vai₁₃₇, Wancho₁₃₈, Warang Citi₁₃₉, Yi₁₄₀. - 'acbabbbaabaaacaaaabccaaadaaaaaabaaabbaaabbababaaabaaaaaaaabaacabaaaaabaaaaabaaaacaaaaabbaaaafabaaaaa,' - // #193: 4 fonts: Noto Sans₀, Adlam₅, Duployan₃₀, Syriac₁₂₁. - 'aey3m,' - // #194: 41 fonts: Noto Sans₀, Anatolian Hieroglyphs₆, Arabic₇, Balinese₁₀, Batak₁₃, Bengali₁₄, Bhaiksuki₁₅, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Devanagari₂₉, Gujarati₃₈, Gurmukhi₄₀, Hanunoo₄₂, Javanese₅₀, Kaithi₅₂, Kannada₅₃, Kharoshthi₅₅, Khmer₅₆, Lao₅₉, Lepcha₆₀, Limbu₆₁, Malayalam₆₈, Meetei Mayek₇₆, Myanmar₈₃, Oriya₉₉, Phags Pa₁₀₅, Rejang₁₀₈, Saurashtra₁₁₁, Sinhala₁₁₅, Sundanese₁₁₉, Syloti Nagri₁₂₀, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Tamil₁₂₉, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃. - 'afaccaaaaakibbhbabacaaghgpfccddacaaaabbaa,' - // #195: 29 fonts: Noto Sans₀, Arabic₇, Armenian₈, Bengali₁₄, Coptic₂₅, Devanagari₂₉, Georgian₃₄, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, Hebrew₄₄, JP₄₉, KR₅₁, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Lisu₆₄, Malayalam₆₈, Oriya₉₉, SC₁₁₀, Sora Sompeng₁₁₇, Sundanese₁₁₉, Syloti Nagri₁₂₀, TC₁₂₂, Tamil₁₂₉, Telugu₁₃₁, Thai₁₃₃. - 'agafkdedbacebaaaaahd1ekgbabgbb,' - // #196: 69 fonts: Noto Sans₀, Arabic₇, Avestan₉, Balinese₁₀, Batak₁₃, Bengali₁₄, Brahmi₁₆, Buginese₁₇, Buhid₁₈, Chakma₂₂, Cham₂₃, Devanagari₂₉, Duployan₃₀, Egyptian Hieroglyphs₃₁, Grantha₃₇, Gujarati₃₈, Gunjala Gondi₃₉, Gurmukhi₄₀, Hanunoo₄₂, Hatran₄₃, Hebrew₄₄, Javanese₅₀, Kaithi₅₂, Kannada₅₃, Kayah Li₅₄, Kharoshthi₅₅, Khmer₅₆, Khojki₅₇, Khudawadi₅₈, Lao₅₉, Lepcha₆₀, Limbu₆₁, Mahajani₆₇, Malayalam₆₈, Mandaic₆₉, Manichaean₇₀, Meetei Mayek₇₆, Modi₇₉, Mongolian₈₀, Myanmar₈₃, NKo₈₄, New Tai Lue₈₆, Newa₈₇, Oriya₉₉, Pahawh Hmong₁₀₂, Phags Pa₁₀₅, Psalter Pahlavi₁₀₇, Rejang₁₀₈, Saurashtra₁₁₁, Sharada₁₁₂, Siddham₁₁₄, Sinhala₁₁₅, Sogdian₁₁₆, Sundanese₁₁₉, Syloti Nagri₁₂₀, Syriac₁₂₁, Tagalog₁₂₃, Tagbanwa₁₂₄, Tai Le₁₂₅, Tai Tham₁₂₆, Tai Viet₁₂₇, Takri₁₂₈, Tamil₁₂₉, Telugu₁₃₁, Thaana₁₃₂, Thai₁₃₃, Tifinagh₁₃₄, Tirhuta₁₃₅, Warang Citi₁₃₉. - 'agbacabaadafaafaaabaafbaaaaaaaaafaaafcacabalccbacabaacaabaaaaaabaaaad,' - // #197: 8 fonts: Noto Sans₀, Arabic₇, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, Syloti Nagri₁₂₀, TC₁₂₂. - 'ag1hhb2gjb,' - // #198: 3 fonts: Noto Sans₀, Arabic₇, Hebrew₄₄. - 'ag1k,' - // #199: 7 fonts: Noto Sans₀, Arabic₇, Hebrew₄₄, NKo₈₄, Phags Pa₁₀₅, Syriac₁₂₁, Thaana₁₃₂. - 'ag1k1nupk,' - // #200: 2 fonts: Noto Sans₀, Armenian₈. - 'ah,' - // #201: 2 fonts: Noto Sans₀, Avestan₉. + // #179: 143 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Arabic₈, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Caucasian Albanian₂₂, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Cypriot₂₈, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Elymaic₃₄, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lycian₆₆, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, Myanmar₈₄, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phags Pa₁₀₆, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #180: 137 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Arabic₈, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'aaaaaaaaaaaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #181: 70 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, Arabic₈, Avestan₁₀, Balinese₁₁, Batak₁₄, Bengali₁₅, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Chakma₂₃, Cham₂₄, Devanagari₃₀, Egyptian Hieroglyphs₃₂, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, Hanunoo₄₃, Hebrew₄₅, Javanese₅₁, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Meetei Mayek₇₇, Modi₈₀, Mongolian₈₁, Myanmar₈₄, NKo₈₅, New Tai Lue₈₇, Newa₈₈, Old Hungarian₉₂, Old Turkic₉₉, Oriya₁₀₀, Pahawh Hmong₁₀₃, Phags Pa₁₀₆, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Saurashtra₁₁₂, Sharada₁₁₃, Siddham₁₁₅, Sinhala₁₁₆, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Warang Citi₁₄₀. + 'aaafbacabaadafbfaaabbfbaaaaaaaaafaaafcacabadgaccbacabadaabaaaaaabaaaad,' + // #182: 9 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, HK₄₂, JP₅₀, KR₅₂, Mongolian₈₁, SC₁₁₁, TC₁₂₃. + 'aaa1nhb1c1dl,' + // #183: 8 fonts: Noto Sans₀, Noto Color Emoji₁, Noto Emoji₂, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'aaa1nhb2gl,' + // #184: 141 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Arabic₈, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Caucasian Albanian₂₂, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Cypriot₂₈, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Elymaic₃₄, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lycian₆₆, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, Myanmar₈₄, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phags Pa₁₀₆, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #185: 134 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Arabic₈, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaaaaaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaabaaaaaaaaaaaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #186: 134 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Caucasian Albanian₂₂, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaaaaaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #187: 135 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, Myanmar₈₄, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #188: 135 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, Myanmar₈₄, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phags Pa₁₀₆, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #189: 132 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Avestan₁₀, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Carian₂₁, Chakma₂₃, Cham₂₄, Cherokee₂₅, Coptic₂₆, Cuneiform₂₇, Deseret₂₉, Devanagari₃₀, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, Inscriptional Parthian₄₉, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Linear A₆₃, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Modi₈₀, Mongolian₈₁, Mro₈₂, Multani₈₃, NKo₈₅, Nabataean₈₆, New Tai Lue₈₇, Newa₈₈, Nushu₈₉, Ogham₉₀, Ol Chiki₉₁, Old Hungarian₉₂, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Old Turkic₉₉, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Shavian₁₁₄, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Tamil Supplement₁₃₁, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁, Zanabazar Square₁₄₂. + 'acaaaabaaaaaaaaaaaabaaaabababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaabaaaaaaaabaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,' + // #190: 95 fonts: Noto Sans₀, Noto Music₃, Symbols₄, Symbols 2₅, Adlam₆, Arabic₈, Armenian₉, Balinese₁₁, Bamum₁₂, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Caucasian Albanian₂₂, Chakma₂₃, Cham₂₄, Coptic₂₆, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Elbasan₃₃, Gothic₃₇, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, HK₄₂, Hanunoo₄₃, Hebrew₄₅, JP₅₀, Javanese₅₁, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Lisu₆₅, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Math₇₄, Meetei Mayek₇₇, Miao₇₉, Modi₈₀, Mongolian₈₁, Myanmar₈₄, NKo₈₅, New Tai Lue₈₇, Newa₈₈, Old Permic₉₅, Old Sogdian₉₇, Oriya₁₀₀, Osage₁₀₁, Pahawh Hmong₁₀₃, Phags Pa₁₀₆, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Runic₁₁₀, SC₁₁₁, Saurashtra₁₁₂, Sharada₁₁₃, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Soyombo₁₁₉, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, TC₁₂₃, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Wancho₁₃₉, Zanabazar Square₁₄₂. + 'acaaababaaaaaaaaabaabdaaadaaaaaabeaaaaaaaaaaaaccaaaaaacbaacabagbcabcbaaaaabaabaaaaaaabaabaaaacc,' + // #191: 89 fonts: Noto Sans₀, Adlam₆, Anatolian Hieroglyphs₇, Armenian₉, Balinese₁₁, Bassa Vah₁₃, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Canadian Aboriginal₂₀, Chakma₂₃, Cherokee₂₅, Cuneiform₂₇, Deseret₂₉, Egyptian Hieroglyphs₃₂, Georgian₃₅, Glagolitic₃₆, Gothic₃₇, Grantha₃₈, HK₄₂, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Imperial Aramaic₄₆, Indic Siyaq Numbers₄₇, Inscriptional Pahlavi₄₈, JP₅₀, KR₅₂, Kaithi₅₃, Kayah Li₅₅, Khmer₅₇, Khudawadi₅₉, Limbu₆₂, Linear B₆₄, Lisu₆₅, Lydian₆₇, Mahajani₆₈, Mandaic₇₀, Manichaean₇₁, Marchen₇₂, Masaram Gondi₇₃, Mayan Numerals₇₅, Medefaidrin₇₆, Meetei Mayek₇₇, Meroitic₇₈, Miao₇₉, Mongolian₈₁, Mro₈₂, Multani₈₃, Nabataean₈₆, Ogham₉₀, Ol Chiki₉₁, Old Italic₉₃, Old North Arabian₉₄, Old Permic₉₅, Old Persian₉₆, Old Sogdian₉₇, Old South Arabian₉₈, Oriya₁₀₀, Osage₁₀₁, Osmanya₁₀₂, Pahawh Hmong₁₀₃, Palmyrene₁₀₄, Pau Cin Hau₁₀₅, Phoenician₁₀₇, Psalter Pahlavi₁₀₈, Runic₁₁₀, SC₁₁₁, Shavian₁₁₄, Sinhala₁₁₆, Sogdian₁₁₇, Sora Sompeng₁₁₈, Soyombo₁₁₉, Syloti Nagri₁₂₁, TC₁₂₃, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Thaana₁₃₃, Thai₁₃₄, Tirhuta₁₃₆, Ugaritic₁₃₇, Vai₁₃₈, Wancho₁₃₉, Warang Citi₁₄₀, Yi₁₄₁. + 'afabbbaaaaaaacbbbccaaadaaaaaabbabbbcbababaaabaaaabaacdabaaaaabaaaaababacbaaabbbaafabaaaaa,' + // #192: 4 fonts: Noto Sans₀, Adlam₆, Duployan₃₁, Syriac₁₂₂. + 'afy3m,' + // #193: 41 fonts: Noto Sans₀, Anatolian Hieroglyphs₇, Arabic₈, Balinese₁₁, Batak₁₄, Bengali₁₅, Bhaiksuki₁₆, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Devanagari₃₀, Gujarati₃₉, Gurmukhi₄₁, Hanunoo₄₃, Javanese₅₁, Kaithi₅₃, Kannada₅₄, Kharoshthi₅₆, Khmer₅₇, Lao₆₀, Lepcha₆₁, Limbu₆₂, Malayalam₆₉, Meetei Mayek₇₇, Myanmar₈₄, Oriya₁₀₀, Phags Pa₁₀₆, Rejang₁₀₉, Saurashtra₁₁₂, Sinhala₁₁₆, Sundanese₁₂₀, Syloti Nagri₁₂₁, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Tamil₁₃₀, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄. + 'agaccaaaaakibbhbabacaaghgpfccddacaaaabbaa,' + // #194: 29 fonts: Noto Sans₀, Arabic₈, Armenian₉, Bengali₁₅, Coptic₂₆, Devanagari₃₀, Georgian₃₅, Gujarati₃₉, Gurmukhi₄₁, HK₄₂, Hebrew₄₅, JP₅₀, KR₅₂, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Lisu₆₅, Malayalam₆₉, Oriya₁₀₀, SC₁₁₁, Sora Sompeng₁₁₈, Sundanese₁₂₀, Syloti Nagri₁₂₁, TC₁₂₃, Tamil₁₃₀, Telugu₁₃₂, Thai₁₃₄. + 'ahafkdedbacebaaaaahd1ekgbabgbb,' + // #195: 69 fonts: Noto Sans₀, Arabic₈, Avestan₁₀, Balinese₁₁, Batak₁₄, Bengali₁₅, Brahmi₁₇, Buginese₁₈, Buhid₁₉, Chakma₂₃, Cham₂₄, Devanagari₃₀, Duployan₃₁, Egyptian Hieroglyphs₃₂, Grantha₃₈, Gujarati₃₉, Gunjala Gondi₄₀, Gurmukhi₄₁, Hanunoo₄₃, Hatran₄₄, Hebrew₄₅, Javanese₅₁, Kaithi₅₃, Kannada₅₄, Kayah Li₅₅, Kharoshthi₅₆, Khmer₅₇, Khojki₅₈, Khudawadi₅₉, Lao₆₀, Lepcha₆₁, Limbu₆₂, Mahajani₆₈, Malayalam₆₉, Mandaic₇₀, Manichaean₇₁, Meetei Mayek₇₇, Modi₈₀, Mongolian₈₁, Myanmar₈₄, NKo₈₅, New Tai Lue₈₇, Newa₈₈, Oriya₁₀₀, Pahawh Hmong₁₀₃, Phags Pa₁₀₆, Psalter Pahlavi₁₀₈, Rejang₁₀₉, Saurashtra₁₁₂, Sharada₁₁₃, Siddham₁₁₅, Sinhala₁₁₆, Sogdian₁₁₇, Sundanese₁₂₀, Syloti Nagri₁₂₁, Syriac₁₂₂, Tagalog₁₂₄, Tagbanwa₁₂₅, Tai Le₁₂₆, Tai Tham₁₂₇, Tai Viet₁₂₈, Takri₁₂₉, Tamil₁₃₀, Telugu₁₃₂, Thaana₁₃₃, Thai₁₃₄, Tifinagh₁₃₅, Tirhuta₁₃₆, Warang Citi₁₄₀. + 'ahbacabaadafaafaaabaafbaaaaaaaaafaaafcacabalccbacabaacaabaaaaaabaaaad,' + // #196: 8 fonts: Noto Sans₀, Arabic₈, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, Syloti Nagri₁₂₁, TC₁₂₃. + 'ah1hhb2gjb,' + // #197: 3 fonts: Noto Sans₀, Arabic₈, Hebrew₄₅. + 'ah1k,' + // #198: 7 fonts: Noto Sans₀, Arabic₈, Hebrew₄₅, NKo₈₅, Phags Pa₁₀₆, Syriac₁₂₂, Thaana₁₃₃. + 'ah1k1nupk,' + // #199: 2 fonts: Noto Sans₀, Armenian₉. 'ai,' - // #202: 20 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, Kannada₅₃, Khudawadi₅₈, Limbu₆₁, Mahajani₆₇, Malayalam₆₈, Masaram Gondi₇₂, Multani₈₂, Oriya₉₉, Sinhala₁₁₅, Syloti Nagri₁₂₀, Takri₁₂₈, Tamil₁₂₉, Telugu₁₃₁, Tirhuta₁₃₅. - 'anohabmecfadjqpehabd,' - // #203: 12 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Grantha₃₇, Gujarati₃₈, Gurmukhi₄₀, Kannada₅₃, Malayalam₆₈, Sharada₁₁₂, Tamil₁₂₉, Telugu₁₃₁, Tirhuta₁₃₅. - 'anohabmo1rqbd,' - // #204: 7 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Grantha₃₇, Kannada₅₃, Telugu₁₃₁, Tirhuta₁₃₅. - 'anohp2zd,' - // #205: 12 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Gujarati₃₈, Gurmukhi₄₀, Kannada₅₃, Malayalam₆₈, Meetei Mayek₇₆, Ol Chiki₉₀, Oriya₉₉, Tamil₁₂₉, Telugu₁₃₁. - 'anoibmohni1db,' - // #206: 7 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Gurmukhi₄₀, Lisu₆₄, Oriya₉₉, Thai₁₃₃. - 'anokx1i1h,' - // #207: 4 fonts: Noto Sans₀, Bengali₁₄, Devanagari₂₉, Kannada₅₃. - 'anox,' - // #208: 16 fonts: Noto Sans₀, Bengali₁₄, Gujarati₃₈, Gurmukhi₄₀, HK₄₁, JP₄₉, KR₅₁, Kannada₅₃, Khmer₅₆, Malayalam₆₈, Oriya₉₉, SC₁₁₀, Sinhala₁₁₅, TC₁₂₂, Tamil₁₂₉, Telugu₁₃₁. - 'anxbahbbcl1ekeggb,' - // #209: 8 fonts: Noto Sans₀, Caucasian Albanian₂₁, Cherokee₂₄, Duployan₃₀, Gothic₃₆, Syriac₁₂₁, Thai₁₃₃, Tifinagh₁₃₄. - 'aucff3gla,' - // #210: 4 fonts: Noto Sans₀, Caucasian Albanian₂₁, Coptic₂₅, Glagolitic₃₅. - 'audj,' - // #211: 3 fonts: Noto Sans₀, Caucasian Albanian₂₁, Glagolitic₃₅. - 'aun,' - // #212: 9 fonts: Noto Sans₀, Cherokee₂₄, Coptic₂₅, Duployan₃₀, Lydian₆₆, Malayalam₆₈, Runic₁₀₉, Syriac₁₂₁, Tifinagh₁₃₄. - 'axae1jb1olm,' - // #213: 4 fonts: Noto Sans₀, Cherokee₂₄, Duployan₃₀, Syriac₁₂₁. - 'axf3m,' - // #214: 4 fonts: Noto Sans₀, Cherokee₂₄, Math₇₃, Syriac₁₂₁. - 'ax1w1v,' - // #215: 6 fonts: Noto Sans₀, Coptic₂₅, Elbasan₃₂, Glagolitic₃₅, Gothic₃₆, Math₇₃. - 'aygca1k,' - // #216: 4 fonts: Noto Sans₀, Devanagari₂₉, Grantha₃₇, Kannada₅₃. - 'a1chp,' - // #217: 13 fonts: Noto Sans₀, Devanagari₂₉, Gujarati₃₈, Gurmukhi₄₀, Kaithi₅₂, Kannada₅₃, Khojki₅₇, Khudawadi₅₈, Mahajani₆₇, Malayalam₆₈, Modi₇₉, Takri₁₂₈, Tirhuta₁₃₅. - 'a1cibladaiak1wg,' - // #218: 12 fonts: Noto Sans₀, Devanagari₂₉, Gujarati₃₈, Gurmukhi₄₀, Kaithi₅₂, Kannada₅₃, Khojki₅₇, Khudawadi₅₈, Mahajani₆₇, Modi₇₉, Takri₁₂₈, Tirhuta₁₃₅. - 'a1cibladail1wg,' - // #219: 11 fonts: Noto Sans₀, Devanagari₂₉, Gujarati₃₈, Gurmukhi₄₀, Kaithi₅₂, Khojki₅₇, Khudawadi₅₈, Mahajani₆₇, Modi₇₉, Takri₁₂₈, Tirhuta₁₃₅. - 'a1cibleail1wg,' - // #220: 4 fonts: Noto Sans₀, Devanagari₂₉, Kaithi₅₂, Mahajani₆₇. - 'a1cwo,' - // #221: 6 fonts: Noto Sans₀, Devanagari₂₉, Kannada₅₃, Malayalam₆₈, Tamil₁₂₉, Telugu₁₃₁. - 'a1cxo2ib,' - // #222: 3 fonts: Noto Sans₀, Devanagari₂₉, Modi₇₉. - 'a1c1x,' - // #223: 3 fonts: Noto Sans₀, Devanagari₂₉, Tamil₁₂₉. - 'a1c3v,' - // #224: 7 fonts: Noto Sans₀, Duployan₃₀, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'a1dkhb2gl,' - // #225: 2 fonts: Noto Sans₀, Georgian₃₄. - 'a1h,' - // #226: 3 fonts: Noto Sans₀, Glagolitic₃₅, Old Permic₉₄. - 'a1i2g,' - // #227: 7 fonts: Noto Sans₀, HK₄₁, JP₄₉, KR₅₁, Mongolian₈₀, SC₁₁₀, TC₁₂₂. - 'a1ohb1c1dl,' - // #228: 7 fonts: Noto Sans₀, HK₄₁, JP₄₉, KR₅₁, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂. - 'a1ohb2bel,' - // #229: 2 fonts: Noto Sans₀, Hebrew₄₄. - 'a1r,' - // #230: 3 fonts: Noto Sans₀, Kayah Li₅₄, Myanmar₈₃. - 'a2b1c,' - // #231: 2 fonts: Noto Sans₀, Lao₅₉. - 'a2g,' - // #232: 2 fonts: Noto Sans₀, Lisu₆₄. - 'a2l,' - // #233: 4 fonts: Noto Sans₀, Manichaean₇₀, Myanmar₈₃, Phags Pa₁₀₅. - 'a2rmv,' - // #234: 2 fonts: Noto Sans₀, Meroitic₇₇. - 'a2y,' - // #235: 2 fonts: Noto Sans₀, Mongolian₈₀. - 'a3b,' - // #236: 2 fonts: Noto Sans₀, NKo₈₄. - 'a3f,' - // #237: 2 fonts: Noto Sans₀, Newa₈₇. - 'a3i,' - // #238: 2 fonts: Noto Sans₀, Old Permic₉₄. - 'a3p,' - // #239: 2 fonts: Noto Sans₀, Oriya₉₉. - 'a3u,' - // #240: 2 fonts: Noto Sans₀, Osage₁₀₀. + // #200: 2 fonts: Noto Sans₀, Avestan₁₀. + 'aj,' + // #201: 20 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Grantha₃₈, Gujarati₃₉, Gurmukhi₄₁, Kannada₅₄, Khudawadi₅₉, Limbu₆₂, Mahajani₆₈, Malayalam₆₉, Masaram Gondi₇₃, Multani₈₃, Oriya₁₀₀, Sinhala₁₁₆, Syloti Nagri₁₂₁, Takri₁₂₉, Tamil₁₃₀, Telugu₁₃₂, Tirhuta₁₃₆. + 'aoohabmecfadjqpehabd,' + // #202: 12 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Grantha₃₈, Gujarati₃₉, Gurmukhi₄₁, Kannada₅₄, Malayalam₆₉, Sharada₁₁₃, Tamil₁₃₀, Telugu₁₃₂, Tirhuta₁₃₆. + 'aoohabmo1rqbd,' + // #203: 7 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Grantha₃₈, Kannada₅₄, Telugu₁₃₂, Tirhuta₁₃₆. + 'aoohp2zd,' + // #204: 12 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Gujarati₃₉, Gurmukhi₄₁, Kannada₅₄, Malayalam₆₉, Meetei Mayek₇₇, Ol Chiki₉₁, Oriya₁₀₀, Tamil₁₃₀, Telugu₁₃₂. + 'aooibmohni1db,' + // #205: 7 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Gurmukhi₄₁, Lisu₆₅, Oriya₁₀₀, Thai₁₃₄. + 'aookx1i1h,' + // #206: 4 fonts: Noto Sans₀, Bengali₁₅, Devanagari₃₀, Kannada₅₄. + 'aoox,' + // #207: 16 fonts: Noto Sans₀, Bengali₁₅, Gujarati₃₉, Gurmukhi₄₁, HK₄₂, JP₅₀, KR₅₂, Kannada₅₄, Khmer₅₇, Malayalam₆₉, Oriya₁₀₀, SC₁₁₁, Sinhala₁₁₆, TC₁₂₃, Tamil₁₃₀, Telugu₁₃₂. + 'aoxbahbbcl1ekeggb,' + // #208: 8 fonts: Noto Sans₀, Caucasian Albanian₂₂, Cherokee₂₅, Duployan₃₁, Gothic₃₇, Syriac₁₂₂, Thai₁₃₄, Tifinagh₁₃₅. + 'avcff3gla,' + // #209: 4 fonts: Noto Sans₀, Caucasian Albanian₂₂, Coptic₂₆, Glagolitic₃₆. + 'avdj,' + // #210: 3 fonts: Noto Sans₀, Caucasian Albanian₂₂, Glagolitic₃₆. + 'avn,' + // #211: 9 fonts: Noto Sans₀, Cherokee₂₅, Coptic₂₆, Duployan₃₁, Lydian₆₇, Malayalam₆₉, Runic₁₁₀, Syriac₁₂₂, Tifinagh₁₃₅. + 'ayae1jb1olm,' + // #212: 4 fonts: Noto Sans₀, Cherokee₂₅, Duployan₃₁, Syriac₁₂₂. + 'ayf3m,' + // #213: 4 fonts: Noto Sans₀, Cherokee₂₅, Math₇₄, Syriac₁₂₂. + 'ay1w1v,' + // #214: 6 fonts: Noto Sans₀, Coptic₂₆, Elbasan₃₃, Glagolitic₃₆, Gothic₃₇, Math₇₄. + 'azgca1k,' + // #215: 4 fonts: Noto Sans₀, Devanagari₃₀, Grantha₃₈, Kannada₅₄. + 'a1dhp,' + // #216: 13 fonts: Noto Sans₀, Devanagari₃₀, Gujarati₃₉, Gurmukhi₄₁, Kaithi₅₃, Kannada₅₄, Khojki₅₈, Khudawadi₅₉, Mahajani₆₈, Malayalam₆₉, Modi₈₀, Takri₁₂₉, Tirhuta₁₃₆. + 'a1dibladaiak1wg,' + // #217: 12 fonts: Noto Sans₀, Devanagari₃₀, Gujarati₃₉, Gurmukhi₄₁, Kaithi₅₃, Kannada₅₄, Khojki₅₈, Khudawadi₅₉, Mahajani₆₈, Modi₈₀, Takri₁₂₉, Tirhuta₁₃₆. + 'a1dibladail1wg,' + // #218: 11 fonts: Noto Sans₀, Devanagari₃₀, Gujarati₃₉, Gurmukhi₄₁, Kaithi₅₃, Khojki₅₈, Khudawadi₅₉, Mahajani₆₈, Modi₈₀, Takri₁₂₉, Tirhuta₁₃₆. + 'a1dibleail1wg,' + // #219: 4 fonts: Noto Sans₀, Devanagari₃₀, Kaithi₅₃, Mahajani₆₈. + 'a1dwo,' + // #220: 6 fonts: Noto Sans₀, Devanagari₃₀, Kannada₅₄, Malayalam₆₉, Tamil₁₃₀, Telugu₁₃₂. + 'a1dxo2ib,' + // #221: 3 fonts: Noto Sans₀, Devanagari₃₀, Modi₈₀. + 'a1d1x,' + // #222: 3 fonts: Noto Sans₀, Devanagari₃₀, Tamil₁₃₀. + 'a1d3v,' + // #223: 7 fonts: Noto Sans₀, Duployan₃₁, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'a1ekhb2gl,' + // #224: 2 fonts: Noto Sans₀, Georgian₃₅. + 'a1i,' + // #225: 3 fonts: Noto Sans₀, Glagolitic₃₆, Old Permic₉₅. + 'a1j2g,' + // #226: 7 fonts: Noto Sans₀, HK₄₂, JP₅₀, KR₅₂, Mongolian₈₁, SC₁₁₁, TC₁₂₃. + 'a1phb1c1dl,' + // #227: 7 fonts: Noto Sans₀, HK₄₂, JP₅₀, KR₅₂, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃. + 'a1phb2bel,' + // #228: 2 fonts: Noto Sans₀, Hebrew₄₅. + 'a1s,' + // #229: 3 fonts: Noto Sans₀, Kayah Li₅₅, Myanmar₈₄. + 'a2c1c,' + // #230: 2 fonts: Noto Sans₀, Lao₆₀. + 'a2h,' + // #231: 2 fonts: Noto Sans₀, Lisu₆₅. + 'a2m,' + // #232: 4 fonts: Noto Sans₀, Manichaean₇₁, Myanmar₈₄, Phags Pa₁₀₆. + 'a2smv,' + // #233: 3 fonts: Noto Sans₀, Meroitic₇₈, Old Hungarian₉₂. + 'a2zn,' + // #234: 2 fonts: Noto Sans₀, Mongolian₈₁. + 'a3c,' + // #235: 2 fonts: Noto Sans₀, NKo₈₅. + 'a3g,' + // #236: 2 fonts: Noto Sans₀, Newa₈₈. + 'a3j,' + // #237: 2 fonts: Noto Sans₀, Old Hungarian₉₂. + 'a3n,' + // #238: 3 fonts: Noto Sans₀, Old Hungarian₉₂, Old Turkic₉₉. + 'a3ng,' + // #239: 2 fonts: Noto Sans₀, Old Permic₉₅. + 'a3q,' + // #240: 2 fonts: Noto Sans₀, Oriya₁₀₀. 'a3v,' - // #241: 2 fonts: Noto Sans₀, Syloti Nagri₁₂₀. - 'a4p,' - // #242: 2 fonts: Noto Sans₀, Tai Viet₁₂₇. - 'a4w,' - // #243: 2 fonts: Noto Sans₀, Tamil₁₂₉. - 'a4y,' - // #244: 2 fonts: Noto Sans₀, Thai₁₃₃. - 'a5c,' - // #245: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Symbols 2₄. - 'baaa,' - // #246: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₃, Duployan₃₀. - 'baa1a,' - // #247: 9 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₃, HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - 'baa1lhbv1kl,' - // #248: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₄, Duployan₃₀. - 'babz,' - // #249: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₄, Math₇₃. - 'bab2q,' - // #250: 8 fonts: Noto Color Emoji₁, Noto Emoji₂, HK₄₁, JP₄₉, KR₅₁, Math₇₃, SC₁₁₀, TC₁₂₂. - 'ba1mhbv1kl,' - // #251: 3 fonts: Noto Color Emoji₁, Noto Emoji₂, Math₇₃. - 'ba2s,' + // #241: 2 fonts: Noto Sans₀, Osage₁₀₁. + 'a3w,' + // #242: 2 fonts: Noto Sans₀, Syloti Nagri₁₂₁. + 'a4q,' + // #243: 2 fonts: Noto Sans₀, Tamil₁₃₀. + 'a4z,' + // #244: 2 fonts: Noto Sans₀, Thai₁₃₄. + 'a5d,' + // #245: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₄, Symbols 2₅. + 'baba,' + // #246: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₄, Duployan₃₁. + 'bab1a,' + // #247: 9 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols₄, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'bab1lhbv1kl,' + // #248: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₅, Duployan₃₁. + 'bacz,' + // #249: 4 fonts: Noto Color Emoji₁, Noto Emoji₂, Symbols 2₅, Math₇₄. + 'bac2q,' + // #250: 8 fonts: Noto Color Emoji₁, Noto Emoji₂, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'ba1nhbv1kl,' + // #251: 3 fonts: Noto Color Emoji₁, Noto Emoji₂, Math₇₄. + 'ba2t,' // #252: 1 font: Noto Emoji₂. 'c,' - // #253: 7 fonts: Symbols₃, Duployan₃₀, HK₄₁, JP₄₉, KR₅₁, SC₁₁₀, TC₁₂₂. - 'd1akhb2gl,' - // #254: 2 fonts: Symbols₃, Gurmukhi₄₀. - 'd1k,' - // #255: 7 fonts: Symbols₃, HK₄₁, JP₄₉, KR₅₁, Mongolian₈₀, SC₁₁₀, TC₁₂₂. - 'd1lhb1c1dl,' - // #256: 2 fonts: Symbols₃, Syriac₁₂₁. - 'd4n,' - // #257: 2 fonts: Symbols 2₄, Coptic₂₅. - 'eu,' - // #258: 3 fonts: Symbols 2₄, Math₇₃, Tai Tham₁₂₆. - 'e2q2a,' - // #259: 2 fonts: Symbols 2₄, Mayan Numerals₇₄. - 'e2r,' - // #260: 7 fonts: Adlam₅, Arabic₇, Mandaic₆₉, Manichaean₇₀, Psalter Pahlavi₁₀₇, Sogdian₁₁₆, Syriac₁₂₁. - 'fb2ja1kie,' - // #261: 5 fonts: Adlam₅, Arabic₇, NKo₈₄, Syriac₁₂₁, Thaana₁₃₂. - 'fb2y1kk,' - // #262: 1 font: Anatolian Hieroglyphs₆. - 'g,' - // #263: 2 fonts: Arabic₇, Coptic₂₅. - 'hr,' - // #264: 4 fonts: Arabic₇, Indic Siyaq Numbers₄₆, Syriac₁₂₁, Thaana₁₃₂. - 'h1m2wk,' - // #265: 2 fonts: Arabic₇, NKo₈₄. - 'h2y,' - // #266: 3 fonts: Arabic₇, Syriac₁₂₁, Thaana₁₃₂. - 'h4jk,' - // #267: 2 fonts: Armenian₈, Georgian₃₄. - 'iz,' - // #268: 3 fonts: Bengali₁₄, Chakma₂₂, Syloti Nagri₁₂₀. - 'oh3t,' - // #269: 2 fonts: Bengali₁₄, Tirhuta₁₃₅. - 'o4q,' - // #270: 2 fonts: Buginese₁₇, Javanese₅₀. - 'r1g,' - // #271: 1 font: Buhid₁₈. - 's,' - // #272: 4 fonts: Buhid₁₈, Hanunoo₄₂, Tagalog₁₂₃, Tagbanwa₁₂₄. - 'sx3ca,' - // #273: 1 font: Carian₂₀. - 'u,' - // #274: 3 fonts: Chakma₂₂, Myanmar₈₃, Tai Le₁₂₅. - 'w2i1p,' - // #275: 1 font: Deseret₂₈. - '1c,' - // #276: 1 font: Egyptian Hieroglyphs₃₁. - '1f,' - // #277: 1 font: Elbasan₃₂. + // #253: 8 fonts: Noto Music₃, Symbols₄, HK₄₂, JP₅₀, KR₅₂, Math₇₄, SC₁₁₁, TC₁₂₃. + 'da1lhbv1kl,' + // #254: 7 fonts: Noto Music₃, Symbols₄, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'da1lhb2gl,' + // #255: 7 fonts: Symbols₄, Duployan₃₁, HK₄₂, JP₅₀, KR₅₂, SC₁₁₁, TC₁₂₃. + 'e1akhb2gl,' + // #256: 2 fonts: Symbols₄, Gurmukhi₄₁. + 'e1k,' + // #257: 7 fonts: Symbols₄, HK₄₂, JP₅₀, KR₅₂, Mongolian₈₁, SC₁₁₁, TC₁₂₃. + 'e1lhb1c1dl,' + // #258: 2 fonts: Symbols₄, Syriac₁₂₂. + 'e4n,' + // #259: 2 fonts: Symbols 2₅, Coptic₂₆. + 'fu,' + // #260: 3 fonts: Symbols 2₅, Math₇₄, Tai Tham₁₂₇. + 'f2q2a,' + // #261: 2 fonts: Symbols 2₅, Mayan Numerals₇₅. + 'f2r,' + // #262: 7 fonts: Adlam₆, Arabic₈, Mandaic₇₀, Manichaean₇₁, Psalter Pahlavi₁₀₈, Sogdian₁₁₇, Syriac₁₂₂. + 'gb2ja1kie,' + // #263: 5 fonts: Adlam₆, Arabic₈, NKo₈₅, Syriac₁₂₂, Thaana₁₃₃. + 'gb2y1kk,' + // #264: 1 font: Anatolian Hieroglyphs₇. + 'h,' + // #265: 2 fonts: Arabic₈, Coptic₂₆. + 'ir,' + // #266: 4 fonts: Arabic₈, Indic Siyaq Numbers₄₇, Syriac₁₂₂, Thaana₁₃₃. + 'i1m2wk,' + // #267: 2 fonts: Arabic₈, NKo₈₅. + 'i2y,' + // #268: 3 fonts: Arabic₈, Syriac₁₂₂, Thaana₁₃₃. + 'i4jk,' + // #269: 2 fonts: Armenian₉, Georgian₃₅. + 'jz,' + // #270: 3 fonts: Bengali₁₅, Chakma₂₃, Syloti Nagri₁₂₁. + 'ph3t,' + // #271: 2 fonts: Bengali₁₅, Tirhuta₁₃₆. + 'p4q,' + // #272: 2 fonts: Buginese₁₈, Javanese₅₁. + 's1g,' + // #273: 1 font: Buhid₁₉. + 't,' + // #274: 4 fonts: Buhid₁₉, Hanunoo₄₃, Tagalog₁₂₄, Tagbanwa₁₂₅. + 'tx3ca,' + // #275: 1 font: Carian₂₁. + 'v,' + // #276: 3 fonts: Chakma₂₃, Myanmar₈₄, Tai Le₁₂₆. + 'x2i1p,' + // #277: 1 font: Deseret₂₉. + '1d,' + // #278: 1 font: Egyptian Hieroglyphs₃₂. '1g,' - // #278: 1 font: Elymaic₃₃. + // #279: 1 font: Elbasan₃₃. '1h,' - // #279: 1 font: Gothic₃₆. - '1k,' - // #280: 2 fonts: Gujarati₃₈, Khojki₅₇. - '1ms,' - // #281: 2 fonts: Gurmukhi₄₀, Multani₈₂. - '1o1p,' - // #282: 11 fonts: HK₄₁, JP₄₉, KR₅₁, Lisu₆₄, Mongolian₈₀, New Tai Lue₈₆, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂, Tai Le₁₂₅, Yi₁₄₀. - '1phbmpfselco,' - // #283: 10 fonts: HK₄₁, JP₄₉, KR₅₁, Mongolian₈₀, New Tai Lue₈₆, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂, Tai Le₁₂₅, Yi₁₄₀. - '1phb1cfselco,' - // #284: 8 fonts: HK₄₁, JP₄₉, KR₅₁, Mongolian₈₀, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂, Yi₁₄₀. - '1phb1cyelr,' - // #285: 9 fonts: HK₄₁, JP₄₉, KR₅₁, New Tai Lue₈₆, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂, Tai Le₁₂₅, Yi₁₄₀. - '1phb1iselco,' - // #286: 6 fonts: HK₄₁, JP₄₉, KR₅₁, Phags Pa₁₀₅, SC₁₁₀, TC₁₂₂. - '1phb2bel,' - // #287: 2 fonts: HK₄₁, SC₁₁₀. - '1p2q,' - // #288: 1 font: Hanunoo₄₂. - '1q,' - // #289: 1 font: Indic Siyaq Numbers₄₆. - '1u,' - // #290: 1 font: Lycian₆₅. - '2n,' - // #291: 1 font: Mahajani₆₇. - '2p,' - // #292: 2 fonts: Math₇₃, Old Permic₉₄. - '2vu,' - // #293: 1 font: Medefaidrin₇₅. - '2x,' - // #294: 1 font: Ogham₈₉. - '3l,' - // #295: 1 font: Ol Chiki₉₀. + // #280: 1 font: Elymaic₃₄. + '1i,' + // #281: 1 font: Gothic₃₇. + '1l,' + // #282: 2 fonts: Gujarati₃₉, Khojki₅₈. + '1ns,' + // #283: 2 fonts: Gurmukhi₄₁, Multani₈₃. + '1p1p,' + // #284: 11 fonts: HK₄₂, JP₅₀, KR₅₂, Lisu₆₅, Mongolian₈₁, New Tai Lue₈₇, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃, Tai Le₁₂₆, Yi₁₄₁. + '1qhbmpfselco,' + // #285: 10 fonts: HK₄₂, JP₅₀, KR₅₂, Mongolian₈₁, New Tai Lue₈₇, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃, Tai Le₁₂₆, Yi₁₄₁. + '1qhb1cfselco,' + // #286: 8 fonts: HK₄₂, JP₅₀, KR₅₂, Mongolian₈₁, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃, Yi₁₄₁. + '1qhb1cyelr,' + // #287: 9 fonts: HK₄₂, JP₅₀, KR₅₂, New Tai Lue₈₇, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃, Tai Le₁₂₆, Yi₁₄₁. + '1qhb1iselco,' + // #288: 6 fonts: HK₄₂, JP₅₀, KR₅₂, Phags Pa₁₀₆, SC₁₁₁, TC₁₂₃. + '1qhb2bel,' + // #289: 2 fonts: HK₄₂, SC₁₁₁. + '1q2q,' + // #290: 1 font: Hanunoo₄₃. + '1r,' + // #291: 1 font: Indic Siyaq Numbers₄₇. + '1v,' + // #292: 1 font: Lycian₆₆. + '2o,' + // #293: 1 font: Mahajani₆₈. + '2q,' + // #294: 2 fonts: Math₇₄, Old Permic₉₅. + '2wu,' + // #295: 1 font: Medefaidrin₇₆. + '2y,' + // #296: 1 font: Ogham₉₀. '3m,' - // #296: 1 font: Old North Arabian₉₃. - '3p,' - // #297: 1 font: Old Permic₉₄. + // #297: 1 font: Ol Chiki₉₁. + '3n,' + // #298: 1 font: Old North Arabian₉₄. '3q,' - // #298: 1 font: Old Sogdian₉₆. - '3s,' - // #299: 1 font: Old South Arabian₉₇. + // #299: 1 font: Old Permic₉₅. + '3r,' + // #300: 1 font: Old Sogdian₉₇. '3t,' - // #300: 1 font: Old Turkic₉₈. + // #301: 1 font: Old South Arabian₉₈. '3u,' - // #301: 1 font: Palmyrene₁₀₃. - '3z,' - // #302: 1 font: Pau Cin Hau₁₀₄. + // #302: 1 font: Old Turkic₉₉. + '3v,' + // #303: 1 font: Palmyrene₁₀₄. '4a,' - // #303: 1 font: Phags Pa₁₀₅. + // #304: 1 font: Pau Cin Hau₁₀₅. '4b,' - // #304: 1 font: Runic₁₀₉. - '4f,' - // #305: 1 font: Sharada₁₁₂. - '4i,' - // #306: 1 font: Shavian₁₁₃. + // #305: 1 font: Phags Pa₁₀₆. + '4c,' + // #306: 1 font: Runic₁₁₀. + '4g,' + // #307: 1 font: Sharada₁₁₃. '4j,' - // #307: 1 font: Sogdian₁₁₆. - '4m,' - // #308: 1 font: Soyombo₁₁₈. - '4o,' - // #309: 1 font: Syloti Nagri₁₂₀. - '4q,' - // #310: 1 font: Thaana₁₃₂. - '5c,' - // #311: 1 font: Vai₁₃₇. - '5h,' - // #312: 1 font: Zanabazar Square₁₄₁. - '5l' + // #308: 1 font: Shavian₁₁₄. + '4k,' + // #309: 1 font: Sogdian₁₁₇. + '4n,' + // #310: 1 font: Soyombo₁₁₉. + '4p,' + // #311: 1 font: Syloti Nagri₁₂₁. + '4r,' + // #312: 1 font: Thaana₁₃₃. + '5d,' + // #313: 1 font: Vai₁₃₈. + '5i,' + // #314: 1 font: Zanabazar Square₁₄₂. + '5m' ; -// 21915 ranges encoded in 31616 characters +// 21903 ranges encoded in 31599 characters const String encodedFontSetRanges = '1eE' // 0-1f - '6W' // 20 #178 - '2W' // 21 #74 - '1Q' // 22 #42 - '6Z' // 23 #181 - '1Q' // 24 #42 - '1V' // 25 #47 - '1Q' // 26 #42 - 'b1V' // 27-29 #47 - '6Y' // 2a #180 - '1V' // 2b #47 - '2W' // 2c #74 - '7E' // 2d #186 - '2W' // 2e #74 - '1V' // 2f #47 - 'i6X' // 30-39 #179 - '2W' // 3a #74 - '1Q' // 3b #42 - 'b1V' // 3c-3e #47 - '7G' // 3f #188 - 'd1Q' // 40-44 #42 - '2X' // 45 #75 - 'b1Q' // 46-48 #42 - '2X' // 49 #75 - 'd1Q' // 4a-4e #42 - '2X' // 4f #75 - 'd1Q' // 50-54 #42 - '2X' // 55 #75 - '1n1Q' // 56-7e #42 + '6X' // 20 #179 + '2U' // 21 #72 + '1H' // 22 #33 + '4I' // 23 #112 + 'e1H' // 24-29 #33 + '4I' // 2a #112 + '1H' // 2b #33 + '2U' // 2c #72 + '7D' // 2d #185 + '2U' // 2e #72 + '1H' // 2f #33 + 'i6Y' // 30-39 #180 + '2U' // 3a #72 + 'c1H' // 3b-3e #33 + '7F' // 3f #187 + 'd1H' // 40-44 #33 + '2V' // 45 #73 + 'b1H' // 46-48 #33 + '2V' // 49 #73 + 'd1H' // 4a-4e #33 + '2V' // 4f #73 + 'd1H' // 50-54 #33 + '2V' // 55 #73 + '1n1H' // 56-7e #33 'M' // 7f #12 '1eE' // 80-9f - '7D' // a0 #185 + '7C' // a0 #184 'bV' // a1-a3 #21 'S' // a4 #18 'V' // a5 #21 'S' // a6 #18 - '1Q' // a7 #42 + '1H' // a7 #33 'V' // a8 #21 '3H' // a9 #85 'V' // aa #21 '4J' // ab #113 - '1W' // ac #48 - '8A' // ad #208 + '1V' // ac #47 + '7Z' // ad #207 '3H' // ae #85 'aV' // af-b0 #21 - '1W' // b1 #48 + '1V' // b1 #47 'a4R' // b2-b3 #121 'V' // b4 #21 'S' // b5 #18 @@ -838,9 +839,9 @@ const String encodedFontSetRanges = '4J' // bb #113 'bS' // bc-be #18 'wV' // bf-d6 #21 - '1V' // d7 #47 + '1H' // d7 #33 '1dV' // d8-f6 #21 - '1V' // f7 #47 + '1H' // f7 #33 'kV' // f8-103 #21 'cY' // 104-107 #24 'aL' // 108-109 #11 @@ -888,7 +889,7 @@ const String encodedFontSetRanges = 'lL' // 1a2-1ae #11 'aS' // 1af-1b0 #18 '1aL' // 1b1-1cc #11 - 'a7K' // 1cd-1ce #192 + 'a7J' // 1cd-1ce #191 'mS' // 1cf-1dc #18 'zL' // 1dd-1f7 #11 'aS' // 1f8-1f9 #18 @@ -904,15 +905,15 @@ const String encodedFontSetRanges = '1O' // 2b9 #40 'L' // 2ba #11 'S' // 2bb #18 - '7Y' // 2bc #206 + '7X' // 2bc #205 'hL' // 2bd-2c5 #11 'Y' // 2c6 #24 'V' // 2c7 #21 'L' // 2c8 #11 - '7I' // 2c9 #190 + '7H' // 2c9 #189 'aS' // 2ca-2cb #18 'L' // 2cc #11 - '8Y' // 2cd #232 + '8X' // 2cd #231 'hL' // 2ce-2d6 #11 '9K' // 2d7 #244 'Y' // 2d8 #24 @@ -923,10 +924,10 @@ const String encodedFontSetRanges = 'sL' // 2ec-2ff #11 'aV' // 300-301 #21 'a4L' // 302-303 #115 - '7F' // 304 #187 - '8H' // 305 #215 + '7E' // 304 #186 + '8G' // 305 #214 'Y' // 306 #24 - '1Q' // 307 #42 + '1H' // 307 #33 '4L' // 308 #115 '4S' // 309 #122 'aY' // 30a-30b #24 @@ -934,29 +935,29 @@ const String encodedFontSetRanges = 'cL' // 30d-310 #11 '1O' // 311 #40 'Y' // 312 #24 - '9E' // 313 #238 + '9F' // 313 #239 'aL' // 314-315 #11 - 'a2N' // 316-317 #65 + 'a2M' // 316-317 #64 'gL' // 318-31f #11 '3K' // 320 #88 'aL' // 321-322 #11 - '8E' // 323 #212 - '8F' // 324 #213 + '8D' // 323 #211 + '8E' // 324 #212 '3K' // 325 #88 'bY' // 326-328 #24 'cL' // 329-32c #11 'a3K' // 32d-32e #88 - '2N' // 32f #65 - '8G' // 330 #214 - '8B' // 331 #209 + '2M' // 32f #64 + '8F' // 330 #213 + '8A' // 331 #208 'lL' // 332-33e #11 '1O' // 33f #40 'nL' // 340-34e #11 - '7Q' // 34f #198 + '7P' // 34f #197 'gL' // 350-357 #11 - '9G' // 358 #240 + '9H' // 358 #241 'L' // 359 #11 - '9F' // 35a #239 + '9G' // 35a #240 'bL' // 35b-35d #11 '1O' // 35e #40 'aL' // 35f-360 #11 @@ -976,24 +977,24 @@ const String encodedFontSetRanges = 'E' // 3a2 'f4Q' // 3a3-3a9 #120 'fL' // 3aa-3b0 #11 - 'x1W' // 3b1-3c9 #48 + 'x1V' // 3b1-3c9 #47 'fL' // 3ca-3d0 #11 - '1B' // 3d1 #27 + '1A' // 3d1 #26 'bL' // 3d2-3d4 #11 - 'a1B' // 3d5-3d6 #27 + 'a1A' // 3d5-3d6 #26 'bL' // 3d7-3d9 #11 - '3A' // 3da #78 + '2Y' // 3da #76 'L' // 3db #11 - '3A' // 3dc #78 + '2Y' // 3dc #76 'L' // 3dd #11 - '3A' // 3de #78 + '2Y' // 3de #76 'L' // 3df #11 - '3A' // 3e0 #78 + '2Y' // 3e0 #76 'L' // 3e1 #11 - 'm3S' // 3e2-3ef #96 - 'a1B' // 3f0-3f1 #27 + 'm3R' // 3e2-3ef #95 + 'a1A' // 3f0-3f1 #26 'aL' // 3f2-3f3 #11 - 'a1B' // 3f4-3f5 #27 + 'a1A' // 3f4-3f5 #26 'jL' // 3f6-400 #11 'S' // 401 #18 'mL' // 402-40f #11 @@ -1001,19 +1002,19 @@ const String encodedFontSetRanges = 'L' // 450 #11 'S' // 451 #18 '1vL' // 452-482 #11 - '8S' // 483 #226 + '8R' // 483 #225 '3J' // 484 #87 'aL' // 485-486 #11 '3J' // 487 #87 '6kL' // 488-52f #11 'E' // 530 - '1k2P' // 531-556 #67 + '1k2O' // 531-556 #66 'aE' // 557-558 - '1u2P' // 559-588 #67 - '10H' // 589 #267 - '2P' // 58a #67 + '1u2O' // 559-588 #66 + '10J' // 589 #269 + '2O' // 58a #66 'aE' // 58b-58c - 'b2P' // 58d-58f #67 + 'b2O' // 58d-58f #66 'E' // 590 '2b1U' // 591-5c7 #46 'gE' // 5c8-5cf @@ -1022,47 +1023,47 @@ const String encodedFontSetRanges = 'd1U' // 5f0-5f4 #46 'jE' // 5f5-5ff 'dZ' // 600-604 #25 - '10D' // 605 #263 + '10F' // 605 #265 'eZ' // 606-60b #25 - '3N' // 60c #91 + '3M' // 60c #90 'mZ' // 60d-61a #25 - '3N' // 61b #91 + '3M' // 61b #90 'Z' // 61c #25 'E' // 61d 'Z' // 61e #25 - '10B' // 61f #261 + '10D' // 61f #263 'Z' // 620 #25 - '3O' // 621 #92 + '3N' // 621 #91 'dZ' // 622-626 #25 - '4U' // 627 #124 + '4V' // 627 #125 'wZ' // 628-63f #25 - '10A' // 640 #260 + '10C' // 640 #262 'iZ' // 641-64a #25 - 'j3O' // 64b-655 #92 + 'j3N' // 64b-655 #91 'iZ' // 656-65f #25 - 'i10E' // 660-669 #264 - '3N' // 66a #91 - 'a10G' // 66b-66c #266 + 'i10G' // 660-669 #266 + '3M' // 66a #90 + 'a10I' // 66b-66c #268 'bZ' // 66d-66f #25 - '3O' // 670 #92 + '3N' // 670 #91 '4vZ' // 671-6ef #25 - 'i4U' // 6f0-6f9 #124 + 'i4V' // 6f0-6f9 #125 'eZ' // 6fa-6ff #25 - 'm4G' // 700-70d #110 + 'm4F' // 700-70d #109 'E' // 70e - '2g4G' // 70f-74a #110 + '2g4F' // 70f-74a #109 'aE' // 74b-74c - 'b4G' // 74d-74f #110 + 'b4F' // 74d-74f #109 '1uZ' // 750-77f #25 - '1w11Y' // 780-7b1 #310 + '1w12A' // 780-7b1 #312 'mE' // 7b2-7bf - '2f5V' // 7c0-7fa #151 + '2f5W' // 7c0-7fa #152 'aE' // 7fb-7fc - 'b5V' // 7fd-7ff #151 + 'b5W' // 7fd-7ff #152 '2kE' // 800-83f - '1a5R' // 840-85b #147 + '1a5S' // 840-85b #148 'aE' // 85c-85d - '5R' // 85e #147 + '5S' // 85e #148 '2lE' // 85f-89f 'tZ' // 8a0-8b4 #25 'E' // 8b5 @@ -1070,76 +1071,76 @@ const String encodedFontSetRanges = 'sE' // 8bf-8d2 '1rZ' // 8d3-8ff #25 '2h1J' // 900-93c #35 - '8O' // 93d #222 + '8N' // 93d #221 'r1J' // 93e-950 #35 - 'a7V' // 951-952 #203 + 'a7U' // 951-952 #202 'p1J' // 953-963 #35 - 'a7U' // 964-965 #202 - 'i8M' // 966-96f #220 + 'a7T' // 964-965 #201 + 'i8L' // 966-96f #219 'o1J' // 970-97f #35 - 'c1F' // 980-983 #31 + 'c1E' // 980-983 #30 'E' // 984 - 'g1F' // 985-98c #31 + 'g1E' // 985-98c #30 'aE' // 98d-98e - 'a1F' // 98f-990 #31 + 'a1E' // 98f-990 #30 'aE' // 991-992 - 'u1F' // 993-9a8 #31 + 'u1E' // 993-9a8 #30 'E' // 9a9 - 'f1F' // 9aa-9b0 #31 + 'f1E' // 9aa-9b0 #30 'E' // 9b1 - '1F' // 9b2 #31 + '1E' // 9b2 #30 'bE' // 9b3-9b5 - 'c1F' // 9b6-9b9 #31 + 'c1E' // 9b6-9b9 #30 'aE' // 9ba-9bb - 'h1F' // 9bc-9c4 #31 + 'h1E' // 9bc-9c4 #30 'aE' // 9c5-9c6 - 'a1F' // 9c7-9c8 #31 + 'a1E' // 9c7-9c8 #30 'aE' // 9c9-9ca - 'c1F' // 9cb-9ce #31 + 'c1E' // 9cb-9ce #30 'gE' // 9cf-9d6 - '1F' // 9d7 #31 + '1E' // 9d7 #30 'cE' // 9d8-9db - 'a1F' // 9dc-9dd #31 + 'a1E' // 9dc-9dd #30 'E' // 9de - 'd1F' // 9df-9e3 #31 + 'd1E' // 9df-9e3 #30 'aE' // 9e4-9e5 - 'i10I' // 9e6-9ef #268 - 'c1F' // 9f0-9f3 #31 - 'c10J' // 9f4-9f7 #269 - 'f1F' // 9f8-9fe #31 + 'i10K' // 9e6-9ef #270 + 'c1E' // 9f0-9f3 #30 + 'c10L' // 9f4-9f7 #271 + 'f1E' // 9f8-9fe #30 'aE' // 9ff-a00 - 'b1H' // a01-a03 #33 + 'b1G' // a01-a03 #32 'E' // a04 - 'e1H' // a05-a0a #33 + 'e1G' // a05-a0a #32 'cE' // a0b-a0e - 'a1H' // a0f-a10 #33 + 'a1G' // a0f-a10 #32 'aE' // a11-a12 - 'u1H' // a13-a28 #33 + 'u1G' // a13-a28 #32 'E' // a29 - 'f1H' // a2a-a30 #33 + 'f1G' // a2a-a30 #32 'E' // a31 - 'a1H' // a32-a33 #33 + 'a1G' // a32-a33 #32 'E' // a34 - 'a1H' // a35-a36 #33 + 'a1G' // a35-a36 #32 'E' // a37 - 'a1H' // a38-a39 #33 + 'a1G' // a38-a39 #32 'aE' // a3a-a3b - '1H' // a3c #33 + '1G' // a3c #32 'E' // a3d - 'd1H' // a3e-a42 #33 + 'd1G' // a3e-a42 #32 'cE' // a43-a46 - 'a1H' // a47-a48 #33 + 'a1G' // a47-a48 #32 'aE' // a49-a4a - 'b1H' // a4b-a4d #33 + 'b1G' // a4b-a4d #32 'bE' // a4e-a50 - '1H' // a51 #33 + '1G' // a51 #32 'fE' // a52-a58 - 'c1H' // a59-a5c #33 + 'c1G' // a59-a5c #32 'E' // a5d - '1H' // a5e #33 + '1G' // a5e #32 'fE' // a5f-a65 - 'i10V' // a66-a6f #281 - 'f1H' // a70-a76 #33 + 'i10X' // a66-a6f #283 + 'f1G' // a70-a76 #32 'iE' // a77-a80 'b1K' // a81-a83 #36 'E' // a84 @@ -1165,7 +1166,7 @@ const String encodedFontSetRanges = 'nE' // ad1-adf 'c1K' // ae0-ae3 #36 'aE' // ae4-ae5 - 'i10U' // ae6-aef #280 + 'i10W' // ae6-aef #282 'a1K' // af0-af1 #36 'fE' // af2-af8 'f1K' // af9-aff #36 @@ -1198,41 +1199,41 @@ const String encodedFontSetRanges = 'aE' // b64-b65 'q1L' // b66-b77 #37 'iE' // b78-b81 - 'a1E' // b82-b83 #30 + 'a1D' // b82-b83 #29 'E' // b84 - 'e1E' // b85-b8a #30 + 'e1D' // b85-b8a #29 'bE' // b8b-b8d - 'b1E' // b8e-b90 #30 + 'b1D' // b8e-b90 #29 'E' // b91 - 'c1E' // b92-b95 #30 + 'c1D' // b92-b95 #29 'bE' // b96-b98 - 'a1E' // b99-b9a #30 + 'a1D' // b99-b9a #29 'E' // b9b - '1E' // b9c #30 + '1D' // b9c #29 'E' // b9d - 'a1E' // b9e-b9f #30 + 'a1D' // b9e-b9f #29 'bE' // ba0-ba2 - 'a1E' // ba3-ba4 #30 + 'a1D' // ba3-ba4 #29 'bE' // ba5-ba7 - 'a1E' // ba8-ba9 #30 - '2K' // baa #62 + 'a1D' // ba8-ba9 #29 + '2J' // baa #61 'bE' // bab-bad - 'f1E' // bae-bb4 #30 - '2K' // bb5 #62 - 'c1E' // bb6-bb9 #30 + 'f1D' // bae-bb4 #29 + '2J' // bb5 #61 + 'c1D' // bb6-bb9 #29 'cE' // bba-bbd - 'd1E' // bbe-bc2 #30 + 'd1D' // bbe-bc2 #29 'bE' // bc3-bc5 - 'b1E' // bc6-bc8 #30 + 'b1D' // bc6-bc8 #29 'E' // bc9 - 'c1E' // bca-bcd #30 + 'c1D' // bca-bcd #29 'aE' // bce-bcf - '1E' // bd0 #30 + '1D' // bd0 #29 'eE' // bd1-bd6 - '1E' // bd7 #30 + '1D' // bd7 #29 'mE' // bd8-be5 - 'l2K' // be6-bf2 #62 - 'g1E' // bf3-bfa #30 + 'l2J' // be6-bf2 #61 + 'g1D' // bf3-bfa #29 'dE' // bfb-bff 'l1P' // c00-c0c #41 'E' // c0d @@ -1283,19 +1284,19 @@ const String encodedFontSetRanges = 'E' // cf0 'a1M' // cf1-cf2 #38 'lE' // cf3-cff - 'l2F' // d00-d0c #57 + 'l2E' // d00-d0c #56 'E' // d0d - 'b2F' // d0e-d10 #57 + 'b2E' // d0e-d10 #56 'E' // d11 - '1x2F' // d12-d44 #57 + '1x2E' // d12-d44 #56 'E' // d45 - 'b2F' // d46-d48 #57 + 'b2E' // d46-d48 #56 'E' // d49 - 'e2F' // d4a-d4f #57 + 'e2E' // d4a-d4f #56 'cE' // d50-d53 - 'o2F' // d54-d63 #57 + 'o2E' // d54-d63 #56 'aE' // d64-d65 - 'y2F' // d66-d7f #57 + 'y2E' // d66-d7f #56 'E' // d80 'b1N' // d81-d83 #39 'E' // d84 @@ -1321,49 +1322,35 @@ const String encodedFontSetRanges = 'aE' // df0-df1 'b1N' // df2-df4 #39 'kE' // df5-e00 - '2e6P' // e01-e3a #171 + '2e6Q' // e01-e3a #172 'cE' // e3b-e3e - '1b6P' // e3f-e5b #171 + '1b6Q' // e3f-e5b #172 '1jE' // e5c-e80 - 'a1D' // e81-e82 #29 + 'a1Q' // e81-e82 #42 'E' // e83 - '1D' // e84 #29 - 'aE' // e85-e86 - 'a1D' // e87-e88 #29 - 'E' // e89 - '1D' // e8a #29 - 'aE' // e8b-e8c - '1D' // e8d #29 - 'eE' // e8e-e93 - 'c1D' // e94-e97 #29 - 'E' // e98 - 'f1D' // e99-e9f #29 - 'E' // ea0 - 'b1D' // ea1-ea3 #29 + '1Q' // e84 #42 + 'E' // e85 + 'd1Q' // e86-e8a #42 + 'E' // e8b + 'w1Q' // e8c-ea3 #42 'E' // ea4 - '1D' // ea5 #29 + '1Q' // ea5 #42 'E' // ea6 - '1D' // ea7 #29 - 'aE' // ea8-ea9 - 'a1D' // eaa-eab #29 - 'E' // eac - 'l1D' // ead-eb9 #29 - 'E' // eba - 'b1D' // ebb-ebd #29 + 'v1Q' // ea7-ebd #42 'aE' // ebe-ebf - 'd1D' // ec0-ec4 #29 + 'd1Q' // ec0-ec4 #42 'E' // ec5 - '1D' // ec6 #29 + '1Q' // ec6 #42 'E' // ec7 - 'e1D' // ec8-ecd #29 - 'aE' // ece-ecf - 'i1D' // ed0-ed9 #29 + 'f1Q' // ec8-ece #42 + 'E' // ecf + 'i1Q' // ed0-ed9 #42 'aE' // eda-edb - 'c1D' // edc-edf #29 + 'c1Q' // edc-edf #42 '11aE' // ee0-fff - '2k3G' // 1000-103f #84 - 'i10O' // 1040-1049 #274 - '3g3G' // 104a-109f #84 + '2k3F' // 1000-103f #83 + 'i10Q' // 1040-1049 #276 + '3g3F' // 104a-109f #83 '1k1S' // 10a0-10c5 #44 'E' // 10c6 '1S' // 10c7 #44 @@ -1373,141 +1360,139 @@ const String encodedFontSetRanges = '1u1S' // 10d0-10ff #44 '9uR' // 1100-11ff #17 '15yE' // 1200-139f - '3g3R' // 13a0-13f5 #95 + '3g3Q' // 13a0-13f5 #94 'aE' // 13f6-13f7 - 'e3R' // 13f8-13fd #95 + 'e3Q' // 13f8-13fd #94 'aE' // 13fe-13ff - '24o3Q' // 1400-167f #94 - '1b11I' // 1680-169c #294 + '24o3P' // 1400-167f #93 + '1b11K' // 1680-169c #296 'bE' // 169d-169f - '3j11S' // 16a0-16f8 #304 + '3j11U' // 16a0-16f8 #306 'fE' // 16f9-16ff - 'u6K' // 1700-1715 #166 + 'u6L' // 1700-1715 #167 'hE' // 1716-171e - '6K' // 171f #166 - 't11C' // 1720-1734 #288 - 'a10M' // 1735-1736 #272 + '6L' // 171f #167 + 't11E' // 1720-1734 #290 + 'a10O' // 1735-1736 #274 'hE' // 1737-173f - 's10L' // 1740-1753 #271 + 's10N' // 1740-1753 #273 'kE' // 1754-175f - 'l4H' // 1760-176c #111 + 'l4G' // 1760-176c #110 'E' // 176d - 'b4H' // 176e-1770 #111 + 'b4G' // 176e-1770 #110 'E' // 1771 - 'a4H' // 1772-1773 #111 + 'a4G' // 1772-1773 #110 'kE' // 1774-177f - '3o3F' // 1780-17dd #83 + '3o3E' // 1780-17dd #82 'aE' // 17de-17df - 'i3F' // 17e0-17e9 #83 + 'i3E' // 17e0-17e9 #82 'eE' // 17ea-17ef - 'i3F' // 17f0-17f9 #83 + 'i3E' // 17f0-17f9 #82 'eE' // 17fa-17ff - '2H' // 1800 #59 - 'b5U' // 1801-1803 #150 - '2H' // 1804 #59 - '5U' // 1805 #150 - 'h2H' // 1806-180e #59 + '2G' // 1800 #58 + 'b5V' // 1801-1803 #151 + '2G' // 1804 #58 + '5V' // 1805 #151 + 'h2G' // 1806-180e #58 'E' // 180f - 'i2H' // 1810-1819 #59 + 'i2G' // 1810-1819 #58 'eE' // 181a-181f - '3j2H' // 1820-1878 #59 + '3j2G' // 1820-1878 #58 'fE' // 1879-187f - '1p2H' // 1880-18aa #59 + '1p2G' // 1880-18aa #58 'dE' // 18ab-18af - '2q3Q' // 18b0-18f5 #94 + '2q3P' // 18b0-18f5 #93 'iE' // 18f6-18ff - '1d2R' // 1900-191e #69 + '1d2Q' // 1900-191e #68 'E' // 191f - 'k2R' // 1920-192b #69 + 'k2Q' // 1920-192b #68 'cE' // 192c-192f - 'k2R' // 1930-193b #69 + 'k2Q' // 1930-193b #68 'cE' // 193c-193f - '2R' // 1940 #69 + '2Q' // 1940 #68 'bE' // 1941-1943 - 'k2R' // 1944-194f #69 - '1c6L' // 1950-196d #167 + 'k2Q' // 1944-194f #68 + '1c6M' // 1950-196d #168 'aE' // 196e-196f - 'd6L' // 1970-1974 #167 + 'd6M' // 1970-1974 #168 'jE' // 1975-197f - '1q2T' // 1980-19ab #71 + '1q3G' // 1980-19ab #84 'cE' // 19ac-19af - 'y2T' // 19b0-19c9 #71 + 'y3G' // 19b0-19c9 #84 'eE' // 19ca-19cf - 'a2T' // 19d0-19d1 #71 - 'E' // 19d2 - 'g2T' // 19d3-19da #71 + 'j3G' // 19d0-19da #84 'bE' // 19db-19dd - 'a2T' // 19de-19df #71 - '1e3F' // 19e0-19ff #83 - '1a5B' // 1a00-1a1b #131 + 'a3G' // 19de-19df #84 + '1e3E' // 19e0-19ff #82 + '1a5C' // 1a00-1a1b #132 'aE' // 1a1c-1a1d - 'a5B' // 1a1e-1a1f #131 - '2j2V' // 1a20-1a5e #73 + 'a5C' // 1a1e-1a1f #132 + '2j2T' // 1a20-1a5e #71 'E' // 1a5f - '1b2V' // 1a60-1a7c #73 + '1b2T' // 1a60-1a7c #71 'aE' // 1a7d-1a7e - 'j2V' // 1a7f-1a89 #73 + 'j2T' // 1a7f-1a89 #71 'eE' // 1a8a-1a8f - 'i2V' // 1a90-1a99 #73 + 'i2T' // 1a90-1a99 #71 'eE' // 1a9a-1a9f - 'm2V' // 1aa0-1aad #73 + 'm2T' // 1aa0-1aad #71 'aE' // 1aae-1aaf 'pL' // 1ab0-1ac0 #11 '2jE' // 1ac1-1aff - '2w4X' // 1b00-1b4b #127 + '2w4Y' // 1b00-1b4b #128 'cE' // 1b4c-1b4f - '1r4X' // 1b50-1b7c #127 + '1r4Y' // 1b50-1b7c #128 'bE' // 1b7d-1b7f - '2k6J' // 1b80-1bbf #165 - '1y5A' // 1bc0-1bf3 #130 + '2k6K' // 1b80-1bbf #166 + '1y5B' // 1bc0-1bf3 #131 'gE' // 1bf4-1bfb - 'c5A' // 1bfc-1bff #130 - '2c3X' // 1c00-1c37 #101 + 'c5B' // 1bfc-1bff #131 + '2c3W' // 1c00-1c37 #100 'bE' // 1c38-1c3a - 'n3X' // 1c3b-1c49 #101 + 'n3W' // 1c3b-1c49 #100 'bE' // 1c4a-1c4c - 'b3X' // 1c4d-1c4f #101 - '1u11J' // 1c50-1c7f #295 + 'b3W' // 1c4d-1c4f #100 + '1u11L' // 1c50-1c7f #297 'hL' // 1c80-1c88 #11 'fE' // 1c89-1c8f '1p1S' // 1c90-1cba #44 'aE' // 1cbb-1cbc 'b1S' // 1cbd-1cbf #44 - 'g6J' // 1cc0-1cc7 #165 + 'g6K' // 1cc0-1cc7 #166 'gE' // 1cc8-1ccf '4O' // 1cd0 #118 '1J' // 1cd1 #35 '4O' // 1cd2 #118 - '2Y' // 1cd3 #76 + '2W' // 1cd3 #74 '1J' // 1cd4 #35 - 'a2B' // 1cd5-1cd6 #53 - '2Z' // 1cd7 #77 - '2B' // 1cd8 #53 - '2Z' // 1cd9 #77 - '8N' // 1cda #221 + 'a2A' // 1cd5-1cd6 #52 + '2X' // 1cd7 #75 + '2A' // 1cd8 #52 + '2X' // 1cd9 #75 + '8M' // 1cda #220 '1J' // 1cdb #35 - 'a2Z' // 1cdc-1cdd #77 + 'a2X' // 1cdc-1cdd #75 'a1J' // 1cde-1cdf #35 - '2Z' // 1ce0 #77 - '2B' // 1ce1 #53 + '2X' // 1ce0 #75 + '2A' // 1ce1 #52 'g1J' // 1ce2-1ce9 #35 - '2B' // 1cea #53 + '2A' // 1cea #52 'a1J' // 1ceb-1cec #35 - '2B' // 1ced #53 + '2A' // 1ced #52 'c1J' // 1cee-1cf1 #35 - '7W' // 1cf2 #204 - '2Y' // 1cf3 #76 - '8I' // 1cf4 #216 - '7Z' // 1cf5 #207 - '2B' // 1cf6 #53 - '1F' // 1cf7 #31 - 'a2Y' // 1cf8-1cf9 #76 + '7V' // 1cf2 #203 + '2W' // 1cf3 #74 + '8H' // 1cf4 #215 + '7Y' // 1cf5 #206 + '2A' // 1cf6 #52 + '1E' // 1cf7 #30 + 'a2W' // 1cf8-1cf9 #74 'eE' // 1cfa-1cff '7vL' // 1d00-1dcc #11 '1O' // 1dcd #40 '1qL' // 1dce-1df9 #11 'E' // 1dfa - '9D' // 1dfb #237 + '9C' // 1dfb #236 '2mL' // 1dfc-1e3d #11 'aS' // 1e3e-1e3f #18 '2kL' // 1e40-1e7f #11 @@ -1551,16 +1536,16 @@ const String encodedFontSetRanges = 'hL' // 1ff6-1ffe #11 'E' // 1fff 'L' // 2000 #11 - '2N' // 2001 #65 + '2M' // 2001 #64 'S' // 2002 #18 - '8Q' // 2003 #224 + '8P' // 2003 #223 'fL' // 2004-200a #11 - '7M' // 200b #194 - '7O' // 200c #196 - '7A' // 200d #182 - 'a7R' // 200e-200f #199 - '7N' // 2010 #195 - '7P' // 2011 #197 + '7L' // 200b #193 + '7N' // 200c #195 + '6Z' // 200d #181 + 'a7Q' // 200e-200f #198 + '7M' // 2010 #194 + '7O' // 2011 #196 'S' // 2012 #18 'aV' // 2013-2014 #21 '3I' // 2015 #86 @@ -1575,31 +1560,31 @@ const String encodedFontSetRanges = 'a3I' // 2020-2021 #86 'V' // 2022 #21 'L' // 2023 #11 - '7S' // 2024 #200 - '8U' // 2025 #228 - '7H' // 2026 #189 + '7R' // 2024 #199 + '8T' // 2025 #227 + '7G' // 2026 #188 'S' // 2027 #18 'eL' // 2028-202d #11 '4S' // 202e #122 - '9B' // 202f #235 + '9A' // 202f #234 '3I' // 2030 #86 'L' // 2031 #11 - 'a1W' // 2032-2033 #48 - '1B' // 2034 #27 - '1W' // 2035 #48 - 'a1B' // 2036-2037 #27 + 'a1V' // 2032-2033 #47 + '1A' // 2034 #26 + '1V' // 2035 #47 + 'a1A' // 2036-2037 #26 'L' // 2038 #11 'aV' // 2039-203a #21 'S' // 203b #18 - '7C' // 203c #184 + '7B' // 203c #183 'dL' // 203d-2041 #11 'S' // 2042 #18 'L' // 2043 #11 - '7L' // 2044 #193 + '7K' // 2044 #192 'aL' // 2045-2046 #11 'S' // 2047 #18 - '8T' // 2048 #227 - '7B' // 2049 #183 + '8S' // 2048 #226 + '7A' // 2049 #182 'dL' // 204a-204e #11 '4N' // 204f #117 'L' // 2050 #11 @@ -1607,13 +1592,15 @@ const String encodedFontSetRanges = 'L' // 2052 #11 '1O' // 2053 #40 'L' // 2054 #11 - '9H' // 2055 #241 + '9I' // 2055 #242 '1O' // 2056 #40 - '1B' // 2057 #27 + '1A' // 2057 #26 'a1O' // 2058-2059 #40 - 'bL' // 205a-205c #11 - '9A' // 205d #234 - 'fL' // 205e-2064 #11 + '9E' // 205a #238 + 'aL' // 205b-205c #11 + '8Z' // 205d #233 + '9D' // 205e #237 + 'eL' // 205f-2064 #11 'E' // 2065 'kL' // 2066-2071 #11 'aE' // 2072-2073 @@ -1626,81 +1613,81 @@ const String encodedFontSetRanges = 'bE' // 209d-209f 'hL' // 20a0-20a8 #11 'S' // 20a9 #18 - '8V' // 20aa #229 + '8U' // 20aa #228 'S' // 20ab #18 'V' // 20ac #21 - '8X' // 20ad #231 + '8W' // 20ad #230 'jL' // 20ae-20b8 #11 - '7X' // 20b9 #205 + '7W' // 20b9 #204 'cL' // 20ba-20bd #11 - '8R' // 20be #225 + '8Q' // 20be #224 'L' // 20bf #11 'oE' // 20c0-20cf 'jO' // 20d0-20da #14 - '11G' // 20db #292 + '11I' // 20db #294 'O' // 20dc #14 - '9T' // 20dd #253 - '1A' // 20de #26 + '9V' // 20dd #255 + '1B' // 20de #27 'aT' // 20df-20e0 #19 'O' // 20e1 #14 '4T' // 20e2 #123 '9L' // 20e3 #245 'T' // 20e4 #19 'jO' // 20e5-20ef #14 - '2Y' // 20f0 #76 + '2W' // 20f0 #74 'nE' // 20f1-20ff 'S' // 2100 #18 'L' // 2101 #11 - '1B' // 2102 #27 + '1A' // 2102 #26 'S' // 2103 #18 'L' // 2104 #11 'S' // 2105 #18 'bL' // 2106-2108 #11 'S' // 2109 #18 - '1W' // 210a #48 - 'c1B' // 210b-210e #27 + '1V' // 210a #47 + 'c1A' // 210b-210e #26 'S' // 210f #18 - 'b1B' // 2110-2112 #27 + 'b1A' // 2110-2112 #26 'S' // 2113 #18 'L' // 2114 #11 - '1B' // 2115 #27 + '1A' // 2115 #26 'S' // 2116 #18 'aL' // 2117-2118 #11 - 'd1B' // 2119-211d #27 + 'd1A' // 2119-211d #26 'bL' // 211e-2120 #11 'S' // 2121 #18 '3H' // 2122 #85 'L' // 2123 #11 - '1B' // 2124 #27 + '1A' // 2124 #26 'L' // 2125 #11 'aS' // 2126-2127 #18 - '1B' // 2128 #27 + '1A' // 2128 #26 'aL' // 2129-212a #11 'S' // 212b #18 - 'a1B' // 212c-212d #27 + 'a1A' // 212c-212d #26 'S' // 212e #18 - 'b1B' // 212f-2131 #27 + 'b1A' // 212f-2131 #26 'L' // 2132 #11 - 'a1B' // 2133-2134 #27 - '1W' // 2135 #48 - 'b1B' // 2136-2138 #27 - '6V' // 2139 #177 + 'a1A' // 2133-2134 #26 + '1V' // 2135 #47 + 'b1A' // 2136-2138 #26 + '6W' // 2139 #178 'L' // 213a #11 'S' // 213b #18 - 'd1B' // 213c-2140 #27 + 'd1A' // 213c-2140 #26 'cL' // 2141-2144 #11 - 'd1B' // 2145-2149 #27 + 'd1A' // 2145-2149 #26 'uL' // 214a-215f #11 - 'k1A' // 2160-216b #26 + 'k1B' // 2160-216b #27 'cT' // 216c-216f #19 - 'k1A' // 2170-217b #26 + 'k1B' // 2170-217b #27 'gT' // 217c-2183 #19 'L' // 2184 #11 'cT' // 2185-2188 #19 'L' // 2189 #11 'aT' // 218a-218b #19 'cE' // 218c-218f - 'c3L' // 2190-2193 #89 + 'c4U' // 2190-2193 #124 'e9N' // 2194-2199 #247 'nO' // 219a-21a8 #14 'a9R' // 21a9-21aa #251 @@ -1735,13 +1722,13 @@ const String encodedFontSetRanges = 'U' // 220f #20 'O' // 2210 #14 'U' // 2211 #20 - '1V' // 2212 #47 + '1H' // 2212 #33 'U' // 2213 #20 'O' // 2214 #14 - '1W' // 2215 #48 + '1V' // 2215 #47 'aO' // 2216-2217 #14 - '2O' // 2218 #66 - '9Y' // 2219 #258 + '2N' // 2218 #65 + '10A' // 2219 #260 'U' // 221a #20 'aO' // 221b-221c #14 'cU' // 221d-2220 #20 @@ -1779,7 +1766,7 @@ const String encodedFontSetRanges = 'aU' // 228a-228b #20 'hO' // 228c-2294 #14 'cU' // 2295-2298 #20 - '2C' // 2299 #54 + '2B' // 2299 #53 'eO' // 229a-229f #14 'U' // 22a0 #20 'cO' // 22a1-22a4 #14 @@ -1787,53 +1774,53 @@ const String encodedFontSetRanges = 'xO' // 22a6-22be #14 'U' // 22bf #20 'cO' // 22c0-22c3 #14 - 'b2O' // 22c4-22c6 #66 + 'b2N' // 22c4-22c6 #65 'rO' // 22c7-22d9 #14 'aU' // 22da-22db #20 'rO' // 22dc-22ee #14 'U' // 22ef #20 'oO' // 22f0-22ff #14 'dT' // 2300-2304 #19 - 'b1A' // 2305-2307 #26 - 'c2I' // 2308-230b #60 + 'b1B' // 2305-2307 #27 + 'c2H' // 2308-230b #59 'cT' // 230c-230f #19 'O' // 2310 #14 'T' // 2311 #19 - '1A' // 2312 #26 + '1B' // 2312 #27 'bT' // 2313-2315 #19 'M' // 2316 #12 'T' // 2317 #19 'W' // 2318 #22 'O' // 2319 #14 'aN' // 231a-231b #13 - 'c2I' // 231c-231f #60 + 'c2H' // 231c-231f #59 'aO' // 2320-2321 #14 'aT' // 2322-2323 #19 'cM' // 2324-2327 #12 'N' // 2328 #13 - 'a1A' // 2329-232a #26 + 'a1B' // 2329-232a #27 'M' // 232b #12 'iT' // 232c-2335 #19 '2pO' // 2336-237a #14 'M' // 237b #12 - '2I' // 237c #60 + '2H' // 237c #59 'bM' // 237d-237f #12 'sT' // 2380-2393 #19 '4T' // 2394 #123 'O' // 2395 #14 'dT' // 2396-239a #19 'sO' // 239b-23ae #14 - '2I' // 23af #60 + '2H' // 23af #59 'aU' // 23b0-23b1 #20 'dO' // 23b2-23b6 #14 'fE' // 23b7-23bd - 'n1A' // 23be-23cc #26 + 'n1B' // 23be-23cc #27 'T' // 23cd #19 'W' // 23ce #22 'N' // 23cf #13 - '2I' // 23d0 #60 + '2H' // 23d0 #59 'hT' // 23d1-23d9 #19 - 'a1A' // 23da-23db #26 + 'a1B' // 23da-23db #27 'eO' // 23dc-23e1 #14 'fT' // 23e2-23e8 #19 'aN' // 23e9-23ea #13 @@ -1849,35 +1836,35 @@ const String encodedFontSetRanges = 'xE' // 2427-243f 'jM' // 2440-244a #12 'tE' // 244b-245f - 's9V' // 2460-2473 #255 - 'a3L' // 2474-2475 #89 - '2w1A' // 2476-24c1 #26 - '1X' // 24c2 #49 - '2h1A' // 24c3-24ff #26 + 's9X' // 2460-2473 #257 + 'a4U' // 2474-2475 #124 + '2w1B' // 2476-24c1 #27 + '1W' // 24c2 #48 + '2h1B' // 24c3-24ff #27 '6cA' // 2500-259f #0 'iW' // 25a0-25a9 #22 'a1I' // 25aa-25ab #34 'bM' // 25ac-25ae #12 - '2O' // 25af #66 + '2N' // 25af #65 'M' // 25b0 #12 'aW' // 25b1-25b2 #22 - '2C' // 25b3 #54 + '2B' // 25b3 #53 'aM' // 25b4-25b5 #12 '1I' // 25b6 #34 - '2C' // 25b7 #54 + '2B' // 25b7 #53 'cM' // 25b8-25bb #12 'W' // 25bc #22 - '2C' // 25bd #54 + '2B' // 25bd #53 'aM' // 25be-25bf #12 '1I' // 25c0 #34 - '2C' // 25c1 #54 + '2B' // 25c1 #53 'cM' // 25c2-25c5 #12 'aW' // 25c6-25c7 #22 'M' // 25c8 #12 'W' // 25c9 #22 - '2C' // 25ca #54 + '2B' // 25ca #53 'W' // 25cb #22 - '7J' // 25cc #191 + '7I' // 25cc #190 'M' // 25cd #12 'eW' // 25ce-25d3 #22 'mM' // 25d4-25e1 #12 @@ -1915,18 +1902,18 @@ const String encodedFontSetRanges = 'bT' // 2627-2629 #19 '1C' // 262a #28 'T' // 262b #19 - '9U' // 262c #254 + '9W' // 262c #256 'T' // 262d #19 '1C' // 262e #28 - '1X' // 262f #49 + '1W' // 262f #48 'gM' // 2630-2637 #12 'b1C' // 2638-263a #28 'T' // 263b #19 'M' // 263c #12 'bT' // 263d-263f #19 - '1X' // 2640 #49 - '1A' // 2641 #26 - '1X' // 2642 #49 + '1W' // 2640 #48 + '1B' // 2641 #27 + '1W' // 2642 #48 'dT' // 2643-2647 #19 'k1C' // 2648-2653 #28 'jM' // 2654-265e #12 @@ -1938,12 +1925,12 @@ const String encodedFontSetRanges = 'a1I' // 2665-2666 #34 'W' // 2667 #22 '1I' // 2668 #34 - 'c1A' // 2669-266c #26 - 'b3L' // 266d-266f #89 - 'a9W' // 2670-2671 #256 - 'h1A' // 2672-267a #26 - '1X' // 267b #49 - 'a1A' // 267c-267d #26 + 'c9U' // 2669-266c #254 + 'b9T' // 266d-266f #253 + 'a9Y' // 2670-2671 #258 + 'h1B' // 2672-267a #27 + '1W' // 267b #48 + 'a1B' // 267c-267d #27 '1C' // 267e #28 'N' // 267f #13 'oM' // 2680-268f #12 @@ -2033,7 +2020,7 @@ const String encodedFontSetRanges = 'jM' // 2758-2762 #12 'aN' // 2763-2764 #13 'pM' // 2765-2775 #12 - '1c1A' // 2776-2793 #26 + '1c1B' // 2776-2793 #27 'M' // 2794 #12 'bP' // 2795-2797 #15 'hM' // 2798-27a0 #12 @@ -2045,15 +2032,15 @@ const String encodedFontSetRanges = '2kO' // 27c0-27ff #14 '9uM' // 2800-28ff #12 '1fO' // 2900-2920 #14 - 'a2I' // 2921-2922 #60 + 'a2H' // 2921-2922 #59 'pO' // 2923-2933 #14 'a9Q' // 2934-2935 #250 '2vO' // 2936-2980 #14 - '2O' // 2981 #66 + '2N' // 2981 #65 '2hO' // 2982-29be #14 - '2C' // 29bf #54 + '2B' // 29bf #53 '1pO' // 29c0-29ea #14 - '2O' // 29eb #66 + '2N' // 29eb #65 'mO' // 29ec-29f9 #14 'aU' // 29fa-29fb #20 '9yO' // 29fc-2aff #14 @@ -2078,41 +2065,41 @@ const String encodedFontSetRanges = '3xM' // 2b97-2bfd #12 'O' // 2bfe #14 'M' // 2bff #12 - '1t2D' // 2c00-2c2e #55 + '1t2C' // 2c00-2c2e #54 'E' // 2c2f - '1t2D' // 2c30-2c5e #55 + '1t2C' // 2c30-2c5e #54 'E' // 2c5f '1eL' // 2c60-2c7f #11 - '4k3S' // 2c80-2cf3 #96 + '4k3R' // 2c80-2cf3 #95 'dE' // 2cf4-2cf8 - 'f3S' // 2cf9-2cff #96 + 'f3R' // 2cf9-2cff #95 '1k1S' // 2d00-2d25 #44 'E' // 2d26 '1S' // 2d27 #44 'dE' // 2d28-2d2c '1S' // 2d2d #44 'aE' // 2d2e-2d2f - '2c4I' // 2d30-2d67 #112 + '2c4H' // 2d30-2d67 #111 'fE' // 2d68-2d6e - 'a4I' // 2d6f-2d70 #112 + 'a4H' // 2d6f-2d70 #111 'mE' // 2d71-2d7e - '4I' // 2d7f #112 + '4H' // 2d7f #111 '3qE' // 2d80-2ddf '2bL' // 2de0-2e16 #11 '1O' // 2e17 #40 'cL' // 2e18-2e1b #11 - 'a9C' // 2e1c-2e1d #236 + 'a9B' // 2e1c-2e1d #235 'iL' // 2e1e-2e27 #11 'a4M' // 2e28-2e29 #116 'eL' // 2e2a-2e2f #11 - 'a7T' // 2e30-2e31 #201 + 'a7S' // 2e30-2e31 #200 'L' // 2e32 #11 'a1O' // 2e33-2e34 #40 'dL' // 2e35-2e39 #11 'aS' // 2e3a-2e3b #18 - '2N' // 2e3c #65 + '2M' // 2e3c #64 'bL' // 2e3d-2e3f #11 - '2N' // 2e40 #65 + '2M' // 2e40 #64 '4N' // 2e41 #117 'pL' // 2e42-2e52 #11 '1rE' // 2e53-2e7f @@ -2124,16 +2111,16 @@ const String encodedFontSetRanges = 'yE' // 2fd6-2fef 'kA' // 2ff0-2ffb #0 'cE' // 2ffc-2fff - '3T' // 3000 #97 - 'a10X' // 3001-3002 #283 + '3S' // 3000 #96 + 'a10Z' // 3001-3002 #285 'cA' // 3003-3006 #0 - '11A' // 3007 #286 - 'a10Z' // 3008-3009 #285 - 'a10W' // 300a-300b #282 - 'c10Y' // 300c-300f #284 - 'a5F' // 3010-3011 #135 + '11C' // 3007 #288 + 'a11B' // 3008-3009 #287 + 'a10Y' // 300a-300b #284 + 'c11A' // 300c-300f #286 + 'a5G' // 3010-3011 #136 'aA' // 3012-3013 #0 - 'g5F' // 3014-301b #135 + 'g5G' // 3014-301b #136 'sA' // 301c-302f #0 '1R' // 3030 #43 'kA' // 3031-303c #0 @@ -2143,7 +2130,7 @@ const String encodedFontSetRanges = '3gA' // 3041-3096 #0 'aE' // 3097-3098 '3sA' // 3099-30fa #0 - '3U' // 30fb #98 + '3T' // 30fb #97 'cA' // 30fc-30ff #0 'dE' // 3100-3104 '1pA' // 3105-312f #0 @@ -2613,7 +2600,7 @@ const String encodedFontSetRanges = 'iD' // 3acc-3ad5 #3 'aF' // 3ad6-3ad7 #5 'aD' // 3ad8-3ad9 #3 - '6F' // 3ada #161 + '6G' // 3ada #162 'D' // 3adb #3 'K' // 3adc #10 'D' // 3add #3 @@ -6227,7 +6214,7 @@ const String encodedFontSetRanges = 'F' // 5c7d #5 'B' // 5c7e #1 'cD' // 5c7f-5c82 #3 - '11B' // 5c83 #287 + '11D' // 5c83 #289 'D' // 5c84 #3 'aB' // 5c85-5c86 #1 'F' // 5c87 #5 @@ -17762,77 +17749,75 @@ const String encodedFontSetRanges = 'B' // 9fd0 #1 '1dD' // 9fd1-9fef #3 'oE' // 9ff0-9fff - '44t6U' // a000-a48c #176 + '44t6V' // a000-a48c #177 'bE' // a48d-a48f - '2b6U' // a490-a4c6 #176 + '2b6V' // a490-a4c6 #177 'hE' // a4c7-a4cf - '1u5P' // a4d0-a4ff #145 - '11m11Z' // a500-a62b #311 + '1u5Q' // a4d0-a4ff #146 + '11m12B' // a500-a62b #313 'sE' // a62c-a63f '1tL' // a640-a66e #11 '3J' // a66f #87 '1uL' // a670-a69f #11 - '3i4Y' // a6a0-a6f7 #128 + '3i4Z' // a6a0-a6f7 #129 'gE' // a6f8-a6ff - '5hL' // a700-a78a #11 - 'a9I' // a78b-a78c #242 - '1xL' // a78d-a7bf #11 + '7iL' // a700-a7bf #11 'aE' // a7c0-a7c1 'hL' // a7c2-a7ca #11 '1oE' // a7cb-a7f4 'jL' // a7f5-a7ff #11 - '1r11X' // a800-a82c #309 + '1r11Z' // a800-a82c #311 'bE' // a82d-a82f - 'b8J' // a830-a832 #217 - 'b8K' // a833-a835 #218 - 'c8L' // a836-a839 #219 + 'b8I' // a830-a832 #216 + 'b8J' // a833-a835 #217 + 'c8K' // a836-a839 #218 'eE' // a83a-a83f - '2c11R' // a840-a877 #303 + '2c11T' // a840-a877 #305 'gE' // a878-a87f - '2q6G' // a880-a8c5 #162 + '2q6H' // a880-a8c5 #163 'gE' // a8c6-a8cd - 'k6G' // a8ce-a8d9 #162 + 'k6H' // a8ce-a8d9 #163 'eE' // a8da-a8df 'p1J' // a8e0-a8f0 #35 - '2B' // a8f1 #53 + '2A' // a8f1 #52 '1J' // a8f2 #35 - '8P' // a8f3 #223 + '8O' // a8f3 #222 'k1J' // a8f4-a8ff #35 - '1s5L' // a900-a92d #141 - '8W' // a92e #230 - '5L' // a92f #141 - '1i6E' // a930-a953 #160 + '1s5M' // a900-a92d #142 + '8V' // a92e #229 + '5M' // a92f #142 + '1i6F' // a930-a953 #161 'jE' // a954-a95e - '6E' // a95f #160 + '6F' // a95f #161 '1bR' // a960-a97c #17 'bE' // a97d-a97f - '2y3W' // a980-a9cd #100 + '2y3V' // a980-a9cd #99 'E' // a9ce - '10K' // a9cf #270 - 'i3W' // a9d0-a9d9 #100 + '10M' // a9cf #272 + 'i3V' // a9d0-a9d9 #99 'cE' // a9da-a9dd - 'a3W' // a9de-a9df #100 - '1d3G' // a9e0-a9fe #84 + 'a3V' // a9de-a9df #99 + '1d3F' // a9e0-a9fe #83 'E' // a9ff - '2b3C' // aa00-aa36 #80 + '2b3B' // aa00-aa36 #79 'hE' // aa37-aa3f - 'm3C' // aa40-aa4d #80 + 'm3B' // aa40-aa4d #79 'aE' // aa4e-aa4f - 'i3C' // aa50-aa59 #80 + 'i3B' // aa50-aa59 #79 'aE' // aa5a-aa5b - 'c3C' // aa5c-aa5f #80 - '1e3G' // aa60-aa7f #84 - '2n6M' // aa80-aac2 #168 + 'c3B' // aa5c-aa5f #79 + '1e3F' // aa60-aa7f #83 + '2n6N' // aa80-aac2 #169 'wE' // aac3-aada - 'd6M' // aadb-aadf #168 - 'v4A' // aae0-aaf6 #104 + 'd6N' // aadb-aadf #169 + 'v3Z' // aae0-aaf6 #103 '2dE' // aaf7-ab2f '2gL' // ab30-ab6b #11 'cE' // ab6c-ab6f - '3a3R' // ab70-abbf #95 - '1s4A' // abc0-abed #104 + '3a3Q' // ab70-abbf #94 + '1s3Z' // abc0-abed #103 'aE' // abee-abef - 'i4A' // abf0-abf9 #104 + 'i3Z' // abf0-abf9 #103 'eE' // abfa-abff '429qR' // ac00-d7a3 #17 'kE' // d7a4-d7af @@ -17841,13 +17826,13 @@ const String encodedFontSetRanges = '1vR' // d7cb-d7fb #17 '325aE' // d7fc-f8ff 'cA' // f900-f903 #0 - '3E' // f904 #82 + '3D' // f904 #81 'aA' // f905-f906 #0 '1T' // f907 #45 - '3E' // f908 #82 + '3D' // f908 #81 'aQ' // f909-f90a #16 'A' // f90b #0 - '1Y' // f90c #50 + '1X' // f90c #49 '1T' // f90d #45 'fQ' // f90e-f914 #16 'A' // f915 #0 @@ -17864,14 +17849,14 @@ const String encodedFontSetRanges = 'Q' // f92e #16 'J' // f92f #9 'Q' // f930 #16 - '5J' // f931 #139 + '5K' // f931 #140 'aJ' // f932-f933 #9 - '1Y' // f934 #50 + '1X' // f934 #49 'J' // f935 #9 'Q' // f936 #16 'A' // f937 #0 'J' // f938 #9 - '5J' // f939 #139 + '5K' // f939 #140 'A' // f93a #0 'gQ' // f93b-f942 #16 'A' // f943 #0 @@ -17896,7 +17881,7 @@ const String encodedFontSetRanges = 'J' // f966 #9 'A' // f967 #0 'dQ' // f968-f96c #16 - '3E' // f96d #82 + '3D' // f96d #81 'J' // f96e #9 'bQ' // f96f-f971 #16 'A' // f972 #0 @@ -17908,7 +17893,7 @@ const String encodedFontSetRanges = '1T' // f978 #45 'A' // f979 #0 'J' // f97a #9 - '1Y' // f97b #50 + '1X' // f97b #49 'aQ' // f97c-f97d #16 'A' // f97e #0 'J' // f97f #9 @@ -17929,7 +17914,7 @@ const String encodedFontSetRanges = 'J' // f998 #9 'bQ' // f999-f99b #16 '1T' // f99c #45 - '3E' // f99d #82 + '3D' // f99d #81 'Q' // f99e #16 '1T' // f99f #45 'iQ' // f9a0-f9a9 #16 @@ -17946,7 +17931,7 @@ const String encodedFontSetRanges = 'A' // f9bb #0 'Q' // f9bc #16 'A' // f9bd #0 - '1Y' // f9be #50 + '1X' // f9be #49 'Q' // f9bf #16 'J' // f9c0 #9 'cQ' // f9c1-f9c4 #16 @@ -17954,16 +17939,16 @@ const String encodedFontSetRanges = 'Q' // f9c7 #16 '1T' // f9c8 #45 'fQ' // f9c9-f9cf #16 - '1Y' // f9d0 #50 + '1X' // f9d0 #49 'fQ' // f9d1-f9d7 #16 'A' // f9d8 #0 - '1Y' // f9d9 #50 + '1X' // f9d9 #49 'aQ' // f9da-f9db #16 'bA' // f9dc-f9de #0 'J' // f9df #9 'A' // f9e0 #0 'Q' // f9e1 #16 - 'a1Y' // f9e2-f9e3 #50 + 'a1X' // f9e2-f9e3 #49 'A' // f9e4 #0 'aQ' // f9e5-f9e6 #16 'A' // f9e7 #0 @@ -18039,7 +18024,7 @@ const String encodedFontSetRanges = 'dS' // fb00-fb04 #18 'aL' // fb05-fb06 #11 'kE' // fb07-fb12 - 'd2P' // fb13-fb17 #67 + 'd2O' // fb13-fb17 #66 'dE' // fb18-fb1c 'y1U' // fb1d-fb36 #46 'E' // fb37 @@ -18055,30 +18040,30 @@ const String encodedFontSetRanges = '4iZ' // fb50-fbc1 #25 'pE' // fbc2-fbd2 '13xZ' // fbd3-fd3d #25 - 'a10F' // fd3e-fd3f #265 + 'a10H' // fd3e-fd3f #267 'oE' // fd40-fd4f '2kZ' // fd50-fd8f #25 'aE' // fd90-fd91 '2aZ' // fd92-fdc7 #25 '1mE' // fdc8-fdef 'aZ' // fdf0-fdf1 #25 - '4V' // fdf2 #125 + '4W' // fdf2 #126 'iZ' // fdf3-fdfc #25 - '4V' // fdfd #125 + '4W' // fdfd #126 'aE' // fdfe-fdff - '8Z' // fe00 #233 + '8Y' // fe00 #232 'lE' // fe01-fe0d 'a9S' // fe0e-fe0f #252 'iA' // fe10-fe19 #0 'eE' // fe1a-fe1f 'c4P' // fe20-fe23 #119 - 'b8C' // fe24-fe26 #210 + 'b8B' // fe24-fe26 #209 'f4P' // fe27-fe2d #119 - 'a8D' // fe2e-fe2f #211 + 'a8C' // fe2e-fe2f #210 'lA' // fe30-fe3c #0 - 'a5E' // fe3d-fe3e #134 + 'a5F' // fe3d-fe3e #135 'aA' // fe3f-fe40 #0 - 'c5E' // fe41-fe44 #134 + 'c5F' // fe41-fe44 #135 'mA' // fe45-fe52 #0 'E' // fe53 'rA' // fe54-fe66 #0 @@ -18091,26 +18076,26 @@ const String encodedFontSetRanges = 'aE' // fefd-fefe 'L' // feff #11 'E' // ff00 - '2M' // ff01 #64 + '2L' // ff01 #63 'eA' // ff02-ff07 #0 - 'a3T' // ff08-ff09 #97 + 'a3S' // ff08-ff09 #96 'aA' // ff0a-ff0b #0 - '2M' // ff0c #64 + '2L' // ff0c #63 'A' // ff0d #0 - '3T' // ff0e #97 + '3S' // ff0e #96 'jA' // ff0f-ff19 #0 - 'a2M' // ff1a-ff1b #64 + 'a2L' // ff1a-ff1b #63 'bA' // ff1c-ff1e #0 - '2M' // ff1f #64 + '2L' // ff1f #63 '2fA' // ff20-ff5a #0 'U' // ff5b #20 'A' // ff5c #0 'U' // ff5d #20 'bA' // ff5e-ff60 #0 - '2M' // ff61 #64 - 'a3U' // ff62-ff63 #98 - '2M' // ff64 #64 - '3U' // ff65 #98 + '2L' // ff61 #63 + 'a3T' // ff62-ff63 #97 + '2L' // ff64 #63 + '3T' // ff65 #97 '2eA' // ff66-ff9f #0 'R' // ffa0 #17 '1cA' // ffa1-ffbe #0 @@ -18130,25 +18115,25 @@ const String encodedFontSetRanges = 'bM' // fff9-fffb #12 'aL' // fffc-fffd #11 'aE' // fffe-ffff - 'k2A' // 10000-1000b #52 + 'k1Z' // 10000-1000b #51 'E' // 1000c - 'y2A' // 1000d-10026 #52 + 'y1Z' // 1000d-10026 #51 'E' // 10027 - 'r2A' // 10028-1003a #52 + 'r1Z' // 10028-1003a #51 'E' // 1003b - 'a2A' // 1003c-1003d #52 + 'a1Z' // 1003c-1003d #51 'E' // 1003e - 'n2A' // 1003f-1004d #52 + 'n1Z' // 1003f-1004d #51 'aE' // 1004e-1004f - 'm2A' // 10050-1005d #52 + 'm1Z' // 10050-1005d #51 '1gE' // 1005e-1007f - '4r2A' // 10080-100fa #52 + '4r1Z' // 10080-100fa #51 'dE' // 100fb-100ff - 'b5O' // 10100-10102 #144 + 'b5P' // 10100-10102 #145 'cE' // 10103-10106 - '1r5O' // 10107-10133 #144 + '1r5P' // 10107-10133 #145 'bE' // 10134-10136 - 'h2A' // 10137-1013f #52 + 'h1Z' // 10137-1013f #51 '2zM' // 10140-1018e #12 'E' // 1018f 'lM' // 10190-1019c #12 @@ -18157,350 +18142,358 @@ const String encodedFontSetRanges = '1tE' // 101a1-101cf '1sM' // 101d0-101fd #12 '4yE' // 101fe-1027f - '1b11E' // 10280-1029c #290 + '1b11G' // 10280-1029c #292 'bE' // 1029d-1029f - '1v10N' // 102a0-102d0 #273 + '1v10P' // 102a0-102d0 #275 'nE' // 102d1-102df - '1a9X' // 102e0-102fb #257 + '1a9Z' // 102e0-102fb #259 'cE' // 102fc-102ff - '1i5Z' // 10300-10323 #155 + '1i6A' // 10300-10323 #156 'hE' // 10324-1032c - 'b5Z' // 1032d-1032f #155 - 'z10T' // 10330-1034a #279 + 'b6A' // 1032d-1032f #156 + 'z10V' // 10330-1034a #281 'dE' // 1034b-1034f - '1p11L' // 10350-1037a #297 + '1p11N' // 10350-1037a #299 'dE' // 1037b-1037f - '1c6R' // 10380-1039d #173 + '1c6S' // 10380-1039d #174 'E' // 1039e - '6R' // 1039f #173 - '1i6A' // 103a0-103c3 #156 + '6S' // 1039f #174 + '1i6B' // 103a0-103c3 #157 'cE' // 103c4-103c7 - 'm6A' // 103c8-103d5 #156 + 'm6B' // 103c8-103d5 #157 '1oE' // 103d6-103ff - '3a10P' // 10400-1044f #275 - '1u11U' // 10450-1047f #306 - '1c6C' // 10480-1049d #158 + '3a10R' // 10400-1044f #277 + '1u11W' // 10450-1047f #308 + '1c6D' // 10480-1049d #159 'aE' // 1049e-1049f - 'i6C' // 104a0-104a9 #158 + 'i6D' // 104a0-104a9 #159 'eE' // 104aa-104af - '1i6B' // 104b0-104d3 #157 + '1i6C' // 104b0-104d3 #158 'cE' // 104d4-104d7 - '1i6B' // 104d8-104fb #157 + '1i6C' // 104d8-104fb #158 'cE' // 104fc-104ff - '1m10R' // 10500-10527 #277 + '1m10T' // 10500-10527 #279 'gE' // 10528-1052f - '1y5C' // 10530-10563 #132 + '1y5D' // 10530-10563 #133 'jE' // 10564-1056e - '5C' // 1056f #132 + '5D' // 1056f #133 '5mE' // 10570-105ff - '11x3Y' // 10600-10736 #102 + '11x3X' // 10600-10736 #101 'hE' // 10737-1073f - 'u3Y' // 10740-10755 #102 + 'u3X' // 10740-10755 #101 'iE' // 10756-1075f - 'g3Y' // 10760-10767 #102 + 'g3X' // 10760-10767 #101 '5uE' // 10768-107ff - 'e2J' // 10800-10805 #61 + 'e2I' // 10800-10805 #60 'aE' // 10806-10807 - '2J' // 10808 #61 + '2I' // 10808 #60 'E' // 10809 - '1q2J' // 1080a-10835 #61 + '1q2I' // 1080a-10835 #60 'E' // 10836 - 'a2J' // 10837-10838 #61 + 'a2I' // 10837-10838 #60 'bE' // 10839-1083b - '2J' // 1083c #61 + '2I' // 1083c #60 'aE' // 1083d-1083e - '2J' // 1083f #61 - 'u5G' // 10840-10855 #136 + '2I' // 1083f #60 + 'u5H' // 10840-10855 #137 'E' // 10856 - 'h5G' // 10857-1085f #136 - '1e11P' // 10860-1087f #301 - '1d5W' // 10880-1089e #152 + 'h5H' // 10857-1085f #137 + '1e11R' // 10860-1087f #303 + '1d5X' // 10880-1089e #153 'gE' // 1089f-108a6 - 'h5W' // 108a7-108af #152 + 'h5X' // 108a7-108af #153 '1uE' // 108b0-108df - 'r3V' // 108e0-108f2 #99 + 'r3U' // 108e0-108f2 #98 'E' // 108f3 - 'a3V' // 108f4-108f5 #99 + 'a3U' // 108f4-108f5 #98 'dE' // 108f6-108fa - 'd3V' // 108fb-108ff #99 - '1a6D' // 10900-1091b #159 + 'd3U' // 108fb-108ff #98 + '1a6E' // 10900-1091b #160 'bE' // 1091c-1091e - '6D' // 1091f #159 - 'y5Q' // 10920-10939 #146 + '6E' // 1091f #160 + 'y5R' // 10920-10939 #147 'dE' // 1093a-1093e - '5Q' // 1093f #146 + '5R' // 1093f #147 '2kE' // 10940-1097f - '2c4B' // 10980-109b7 #105 + '2c4A' // 10980-109b7 #104 'cE' // 109b8-109bb - 's4B' // 109bc-109cf #105 + 's4A' // 109bc-109cf #104 'aE' // 109d0-109d1 - '1s4B' // 109d2-109ff #105 - 'c1Z' // 10a00-10a03 #51 + '1s4A' // 109d2-109ff #104 + 'c1Y' // 10a00-10a03 #50 'E' // 10a04 - 'a1Z' // 10a05-10a06 #51 + 'a1Y' // 10a05-10a06 #50 'dE' // 10a07-10a0b - 'g1Z' // 10a0c-10a13 #51 + 'g1Y' // 10a0c-10a13 #50 'E' // 10a14 - 'b1Z' // 10a15-10a17 #51 + 'b1Y' // 10a15-10a17 #50 'E' // 10a18 - '1b1Z' // 10a19-10a35 #51 + '1b1Y' // 10a19-10a35 #50 'aE' // 10a36-10a37 - 'b1Z' // 10a38-10a3a #51 + 'b1Y' // 10a38-10a3a #50 'cE' // 10a3b-10a3e - 'i1Z' // 10a3f-10a48 #51 + 'i1Y' // 10a3f-10a48 #50 'fE' // 10a49-10a4f - 'h1Z' // 10a50-10a58 #51 + 'h1Y' // 10a50-10a58 #50 'fE' // 10a59-10a5f - '1e11N' // 10a60-10a7f #299 - '1e11K' // 10a80-10a9f #296 + '1e11P' // 10a60-10a7f #301 + '1e11M' // 10a80-10a9f #298 '1eE' // 10aa0-10abf - '1l5S' // 10ac0-10ae6 #148 + '1l5T' // 10ac0-10ae6 #149 'cE' // 10ae7-10aea - 'k5S' // 10aeb-10af6 #148 + 'k5T' // 10aeb-10af6 #149 'hE' // 10af7-10aff - '2a4W' // 10b00-10b35 #126 + '2a4X' // 10b00-10b35 #127 'bE' // 10b36-10b38 - 'f4W' // 10b39-10b3f #126 - 'u5I' // 10b40-10b55 #138 + 'f4X' // 10b39-10b3f #127 + 'u5J' // 10b40-10b55 #139 'aE' // 10b56-10b57 - 'g5I' // 10b58-10b5f #138 - 'r5H' // 10b60-10b72 #137 + 'g5J' // 10b58-10b5f #139 + 'r5I' // 10b60-10b72 #138 'dE' // 10b73-10b77 - 'g5H' // 10b78-10b7f #137 - 'q4F' // 10b80-10b91 #109 + 'g5I' // 10b78-10b7f #138 + 'q4E' // 10b80-10b91 #108 'fE' // 10b92-10b98 - 'c4F' // 10b99-10b9c #109 + 'c4E' // 10b99-10b9c #108 'kE' // 10b9d-10ba8 - 'f4F' // 10ba9-10baf #109 + 'f4E' // 10ba9-10baf #108 '3aE' // 10bb0-10bff - '2t11O' // 10c00-10c48 #300 + '2t11Q' // 10c00-10c48 #302 '2bE' // 10c49-10c7f - '1x4E' // 10c80-10cb2 #108 + '1x4D' // 10c80-10cb2 #107 'lE' // 10cb3-10cbf - '1x4E' // 10cc0-10cf2 #108 + '1x4D' // 10cc0-10cf2 #107 'fE' // 10cf3-10cf9 - 'e4E' // 10cfa-10cff #108 + 'e4D' // 10cfa-10cff #107 '13mE' // 10d00-10e5f '1dM' // 10e60-10e7e #12 '4xE' // 10e7f-10eff - '1m11M' // 10f00-10f27 #298 + '1m11O' // 10f00-10f27 #300 'gE' // 10f28-10f2f - '1o11V' // 10f30-10f59 #307 + '1o11X' // 10f30-10f59 #309 '5cE' // 10f5a-10fdf - 'v10S' // 10fe0-10ff6 #278 + 'v10U' // 10fe0-10ff6 #280 'hE' // 10ff7-10fff - '2y3P' // 11000-1104d #93 + '2y3O' // 11000-1104d #92 'cE' // 1104e-11051 - '1c3P' // 11052-1106f #93 + '1c3O' // 11052-1106f #92 'nE' // 11070-1107e - '3P' // 1107f #93 - '2m5K' // 11080-110c1 #140 + '3O' // 1107f #92 + '2m5L' // 11080-110c1 #141 'jE' // 110c2-110cc - '5K' // 110cd #140 + '5L' // 110cd #141 'aE' // 110ce-110cf - 'x6I' // 110d0-110e8 #164 + 'x6J' // 110d0-110e8 #165 'fE' // 110e9-110ef - 'i6I' // 110f0-110f9 #164 + 'i6J' // 110f0-110f9 #165 'eE' // 110fa-110ff - '1z5D' // 11100-11134 #133 + '1z5E' // 11100-11134 #134 'E' // 11135 - 'q5D' // 11136-11147 #133 + 'q5E' // 11136-11147 #134 'gE' // 11148-1114f - '1l11F' // 11150-11176 #291 + '1l11H' // 11150-11176 #293 'hE' // 11177-1117f - '3q11T' // 11180-111df #305 + '3q11V' // 11180-111df #307 'E' // 111e0 's1N' // 111e1-111f4 #39 'jE' // 111f5-111ff - 'q5M' // 11200-11211 #142 + 'q5N' // 11200-11211 #143 'E' // 11212 - '1q5M' // 11213-1123e #142 + '1q5N' // 11213-1123e #143 '2lE' // 1123f-1127f - 'f2S' // 11280-11286 #70 + 'f2R' // 11280-11286 #69 'E' // 11287 - '2S' // 11288 #70 + '2R' // 11288 #69 'E' // 11289 - 'c2S' // 1128a-1128d #70 + 'c2R' // 1128a-1128d #69 'E' // 1128e - 'n2S' // 1128f-1129d #70 + 'n2R' // 1128f-1129d #69 'E' // 1129e - 'j2S' // 1129f-112a9 #70 + 'j2R' // 1129f-112a9 #69 'eE' // 112aa-112af - '2f5N' // 112b0-112ea #143 + '2f5O' // 112b0-112ea #144 'dE' // 112eb-112ef - 'i5N' // 112f0-112f9 #143 + 'i5O' // 112f0-112f9 #144 'eE' // 112fa-112ff - '1G' // 11300 #32 - '2K' // 11301 #62 - '1G' // 11302 #32 - '2K' // 11303 #62 + '1F' // 11300 #31 + '2J' // 11301 #61 + '1F' // 11302 #31 + '2J' // 11303 #61 'E' // 11304 - 'g1G' // 11305-1130c #32 + 'g1F' // 11305-1130c #31 'aE' // 1130d-1130e - 'a1G' // 1130f-11310 #32 + 'a1F' // 1130f-11310 #31 'aE' // 11311-11312 - 'u1G' // 11313-11328 #32 + 'u1F' // 11313-11328 #31 'E' // 11329 - 'f1G' // 1132a-11330 #32 + 'f1F' // 1132a-11330 #31 'E' // 11331 - 'a1G' // 11332-11333 #32 + 'a1F' // 11332-11333 #31 'E' // 11334 - 'd1G' // 11335-11339 #32 + 'd1F' // 11335-11339 #31 'E' // 1133a - 'a2K' // 1133b-1133c #62 - 'g1G' // 1133d-11344 #32 + 'a2J' // 1133b-1133c #61 + 'g1F' // 1133d-11344 #31 'aE' // 11345-11346 - 'a1G' // 11347-11348 #32 + 'a1F' // 11347-11348 #31 'aE' // 11349-1134a - 'b1G' // 1134b-1134d #32 + 'b1F' // 1134b-1134d #31 'aE' // 1134e-1134f - '1G' // 11350 #32 + '1F' // 11350 #31 'eE' // 11351-11356 - '1G' // 11357 #32 + '1F' // 11357 #31 'dE' // 11358-1135c - 'f1G' // 1135d-11363 #32 + 'f1F' // 1135d-11363 #31 'aE' // 11364-11365 - 'f1G' // 11366-1136c #32 + 'f1F' // 11366-1136c #31 'bE' // 1136d-1136f - 'd1G' // 11370-11374 #32 + 'd1F' // 11370-11374 #31 '5hE' // 11375-113ff - '3m5X' // 11400-1145b #153 + '3m5Y' // 11400-1145b #154 'E' // 1145c - 'd5X' // 1145d-11461 #153 + 'd5Y' // 1145d-11461 #154 '1cE' // 11462-1147f - '2s6Q' // 11480-114c7 #172 + '2s6R' // 11480-114c7 #173 'gE' // 114c8-114cf - 'i6Q' // 114d0-114d9 #172 + 'i6R' // 114d0-114d9 #173 '6iE' // 114da-1157f - '2a6H' // 11580-115b5 #163 + '2a6I' // 11580-115b5 #164 'aE' // 115b6-115b7 - '1k6H' // 115b8-115dd #163 + '1k6I' // 115b8-115dd #164 '1gE' // 115de-115ff - '2p5T' // 11600-11644 #149 + '2p5U' // 11600-11644 #150 'jE' // 11645-1164f - 'i5T' // 11650-11659 #149 + 'i5U' // 11650-11659 #150 'eE' // 1165a-1165f - 'l2H' // 11660-1166c #59 + 'l2G' // 11660-1166c #58 'rE' // 1166d-1167f - '2e6N' // 11680-116b9 #169 + '2e6O' // 11680-116b9 #170 'eE' // 116ba-116bf - 'i6N' // 116c0-116c9 #169 + 'i6O' // 116c0-116c9 #170 '18aE' // 116ca-1189f - '3d6T' // 118a0-118f2 #175 + '3d6U' // 118a0-118f2 #176 'kE' // 118f3-118fe - '6T' // 118ff #175 + '6U' // 118ff #176 '9uE' // 11900-119ff - '2s12A' // 11a00-11a47 #312 + '2s12C' // 11a00-11a47 #314 'gE' // 11a48-11a4f - '3d11W' // 11a50-11aa2 #308 + '3d11Y' // 11a50-11aa2 #310 'lE' // 11aa3-11aaf - 'o3Q' // 11ab0-11abf #94 - '2d11Q' // 11ac0-11af8 #302 + 'o3P' // 11ab0-11abf #93 + '2d11S' // 11ac0-11af8 #304 '10bE' // 11af9-11bff - 'h3B' // 11c00-11c08 #79 + 'h3A' // 11c00-11c08 #78 'E' // 11c09 - '1r3B' // 11c0a-11c36 #79 + '1r3A' // 11c0a-11c36 #78 'E' // 11c37 - 'm3B' // 11c38-11c45 #79 + 'm3A' // 11c38-11c45 #78 'iE' // 11c46-11c4f - '1b3B' // 11c50-11c6c #79 + '1b3A' // 11c50-11c6c #78 'bE' // 11c6d-11c6f - '1e3Z' // 11c70-11c8f #103 + '1e3Y' // 11c70-11c8f #102 'aE' // 11c90-11c91 - 'u3Z' // 11c92-11ca7 #103 + 'u3Y' // 11c92-11ca7 #102 'E' // 11ca8 - 'm3Z' // 11ca9-11cb6 #103 + 'm3Y' // 11ca9-11cb6 #102 '2tE' // 11cb7-11cff - 'f2G' // 11d00-11d06 #58 + 'f2F' // 11d00-11d06 #57 'E' // 11d07 - 'a2G' // 11d08-11d09 #58 + 'a2F' // 11d08-11d09 #57 'E' // 11d0a - '1q2G' // 11d0b-11d36 #58 + '1q2F' // 11d0b-11d36 #57 'bE' // 11d37-11d39 - '2G' // 11d3a #58 + '2F' // 11d3a #57 'E' // 11d3b - 'a2G' // 11d3c-11d3d #58 + 'a2F' // 11d3c-11d3d #57 'E' // 11d3e - 'h2G' // 11d3f-11d47 #58 + 'h2F' // 11d3f-11d47 #57 'gE' // 11d48-11d4f - 'i2G' // 11d50-11d59 #58 + 'i2F' // 11d50-11d59 #57 'eE' // 11d5a-11d5f - 'e2L' // 11d60-11d65 #63 + 'e2K' // 11d60-11d65 #62 'E' // 11d66 - 'a2L' // 11d67-11d68 #63 + 'a2K' // 11d67-11d68 #62 'E' // 11d69 - '1j2L' // 11d6a-11d8e #63 + '1j2K' // 11d6a-11d8e #62 'E' // 11d8f - 'a2L' // 11d90-11d91 #63 + 'a2K' // 11d90-11d91 #62 'E' // 11d92 - 'e2L' // 11d93-11d98 #63 + 'e2K' // 11d93-11d98 #62 'fE' // 11d99-11d9f - 'i2L' // 11da0-11da9 #63 + 'i2K' // 11da0-11da9 #62 '19wE' // 11daa-11faf - '5P' // 11fb0 #145 + '5Q' // 11fb0 #146 'nE' // 11fb1-11fbf - '1w6O' // 11fc0-11ff1 #170 + '1w6P' // 11fc0-11ff1 #171 'lE' // 11ff2-11ffe - '6O' // 11fff #170 - '35k3D' // 12000-12399 #81 + '6P' // 11fff #171 + '35k3C' // 12000-12399 #80 '3wE' // 1239a-123ff - '4f3D' // 12400-1246e #81 + '4f3C' // 12400-1246e #80 'E' // 1246f - 'd3D' // 12470-12474 #81 + 'd3C' // 12470-12474 #80 'jE' // 12475-1247f - '7m3D' // 12480-12543 #81 + '7m3C' // 12480-12543 #80 '105qE' // 12544-12fff - '41d10Q' // 13000-1342e #276 + '41d10S' // 13000-1342e #278 '155rE' // 1342f-143ff - '22j10C' // 14400-14646 #262 + '22j10E' // 14400-14646 #264 '331zE' // 14647-167ff - '21v4Y' // 16800-16a38 #128 + '21v4Z' // 16800-16a38 #129 'fE' // 16a39-16a3f - '1d4D' // 16a40-16a5e #107 + '1d4C' // 16a40-16a5e #106 'E' // 16a5f - 'i4D' // 16a60-16a69 #107 + 'i4C' // 16a60-16a69 #106 'cE' // 16a6a-16a6d - 'a4D' // 16a6e-16a6f #107 + 'a4C' // 16a6e-16a6f #106 '3qE' // 16a70-16acf - '1c4Z' // 16ad0-16aed #129 + '1c5A' // 16ad0-16aed #130 'aE' // 16aee-16aef - 'e4Z' // 16af0-16af5 #129 + 'e5A' // 16af0-16af5 #130 'iE' // 16af6-16aff - '2q2U' // 16b00-16b45 #72 + '2q2S' // 16b00-16b45 #70 'iE' // 16b46-16b4f - 'i2U' // 16b50-16b59 #72 + 'i2S' // 16b50-16b59 #70 'E' // 16b5a - 'f2U' // 16b5b-16b61 #72 + 'f2S' // 16b5b-16b61 #70 'E' // 16b62 - 't2U' // 16b63-16b77 #72 + 't2S' // 16b63-16b77 #70 'dE' // 16b78-16b7c - 'r2U' // 16b7d-16b8f #72 + 'r2S' // 16b7d-16b8f #70 '26kE' // 16b90-16e3f - '3l11H' // 16e40-16e9a #293 + '3l11J' // 16e40-16e9a #295 '3vE' // 16e9b-16eff - '2v4C' // 16f00-16f4a #106 + '2v4B' // 16f00-16f4a #105 'cE' // 16f4b-16f4e - '2d4C' // 16f4f-16f87 #106 + '2d4B' // 16f4f-16f87 #105 'fE' // 16f88-16f8e - 'p4C' // 16f8f-16f9f #106 + 'p4B' // 16f8f-16f9f #105 '2lE' // 16fa0-16fe0 - '5Y' // 16fe1 #154 + '5Z' // 16fe1 #155 '645kE' // 16fe2-1b16f - '15e5Y' // 1b170-1b2fb #154 + '15e5Z' // 1b170-1b2fb #155 '88sE' // 1b2fc-1bbff - '4b2Q' // 1bc00-1bc6a #68 + '4b2P' // 1bc00-1bc6a #67 'dE' // 1bc6b-1bc6f - 'l2Q' // 1bc70-1bc7c #68 + 'l2P' // 1bc70-1bc7c #67 'bE' // 1bc7d-1bc7f - 'h2Q' // 1bc80-1bc88 #68 + 'h2P' // 1bc80-1bc88 #67 'fE' // 1bc89-1bc8f - 'i2Q' // 1bc90-1bc99 #68 + 'i2P' // 1bc90-1bc99 #67 'aE' // 1bc9a-1bc9b - 'g2Q' // 1bc9c-1bca3 #68 - '217qE' // 1bca4-1d2bf + 'g2P' // 1bc9c-1bca3 #67 + '190oE' // 1bca4-1cfff + '9k2Z' // 1d000-1d0f5 #77 + 'iE' // 1d0f6-1d0ff + '1l2Z' // 1d100-1d126 #77 + 'aE' // 1d127-1d128 + '7k2Z' // 1d129-1d1ea #77 + 'tE' // 1d1eb-1d1ff + '2q2Z' // 1d200-1d245 #77 + '4qE' // 1d246-1d2bf 'sM' // 1d2c0-1d2d3 #12 'kE' // 1d2d4-1d2df - 's9Z' // 1d2e0-1d2f3 #259 + 's10B' // 1d2e0-1d2f3 #261 'kE' // 1d2f4-1d2ff '3hM' // 1d300-1d356 #12 'hE' // 1d357-1d35f @@ -18548,27 +18541,27 @@ const String encodedFontSetRanges = 'aE' // 1d7cc-1d7cd '1wO' // 1d7ce-1d7ff #14 '78sE' // 1d800-1dfff - 'f2D' // 1e000-1e006 #55 + 'f2C' // 1e000-1e006 #54 'E' // 1e007 - 'p2D' // 1e008-1e018 #55 + 'p2C' // 1e008-1e018 #54 'aE' // 1e019-1e01a - 'f2D' // 1e01b-1e021 #55 + 'f2C' // 1e01b-1e021 #54 'E' // 1e022 - 'a2D' // 1e023-1e024 #55 + 'a2C' // 1e023-1e024 #54 'E' // 1e025 - 'd2D' // 1e026-1e02a #55 + 'd2C' // 1e026-1e02a #54 '25jE' // 1e02b-1e2bf - '2e6S' // 1e2c0-1e2f9 #174 + '2e6T' // 1e2c0-1e2f9 #175 'dE' // 1e2fa-1e2fe - '6S' // 1e2ff #174 + '6T' // 1e2ff #175 '59aE' // 1e300-1e8ff - '2w3M' // 1e900-1e94b #90 + '2w3L' // 1e900-1e94b #89 'cE' // 1e94c-1e94f - 'i3M' // 1e950-1e959 #90 + 'i3L' // 1e950-1e959 #89 'cE' // 1e95a-1e95d - 'a3M' // 1e95e-1e95f #90 + 'a3L' // 1e95e-1e95f #89 '30dE' // 1e960-1ec70 - '2o11D' // 1ec71-1ecb4 #289 + '2o11F' // 1ec71-1ecb4 #291 '12rE' // 1ecb5-1edff 'cO' // 1ee00-1ee03 #14 'E' // 1ee04 @@ -18653,18 +18646,18 @@ const String encodedFontSetRanges = 'E' // 1f0d0 '1jM' // 1f0d1-1f0f5 #12 'iE' // 1f0f6-1f0ff - 'l1A' // 1f100-1f10c #26 + 'l1B' // 1f100-1f10c #27 'bE' // 1f10d-1f10f - '3n1A' // 1f110-1f16c #26 + '3n1B' // 1f110-1f16c #27 'bE' // 1f16d-1f16f - 'a1X' // 1f170-1f171 #49 - 'k1A' // 1f172-1f17d #26 - 'a1X' // 1f17e-1f17f #49 - 'm1A' // 1f180-1f18d #26 - '1X' // 1f18e #49 - 'a1A' // 1f18f-1f190 #26 + 'a1W' // 1f170-1f171 #48 + 'k1B' // 1f172-1f17d #27 + 'a1W' // 1f17e-1f17f #48 + 'm1B' // 1f180-1f18d #27 + '1W' // 1f18e #48 + 'a1B' // 1f18f-1f190 #27 'i1R' // 1f191-1f19a #43 - 'q1A' // 1f19b-1f1ac #26 + 'q1B' // 1f19b-1f1ac #27 '2dE' // 1f1ad-1f1e5 'yP' // 1f1e6-1f1ff #15 'A' // 1f200 #0 @@ -18882,8 +18875,8 @@ const String encodedFontSetRanges = 'bE' // 1f6fd-1f6ff '4kT' // 1f700-1f773 #19 'kE' // 1f774-1f77f - '3jM' // 1f780-1f7d8 #12 - 'fE' // 1f7d9-1f7df + '3kM' // 1f780-1f7d9 #12 + 'eE' // 1f7da-1f7df 'kN' // 1f7e0-1f7eb #13 'cE' // 1f7ec-1f7ef 'P' // 1f7f0 #15 @@ -19098,7 +19091,7 @@ const String encodedFontSetRanges = 'nE' // 205b4-205c2 'G' // 205c3 #6 'eE' // 205c4-205c9 - '2E' // 205ca #56 + '2D' // 205ca #55 'dE' // 205cb-205cf 'G' // 205d0 #6 'cE' // 205d1-205d4 @@ -19802,7 +19795,7 @@ const String encodedFontSetRanges = 'dE' // 2248c-22490 'G' // 22491 #6 '1cE' // 22492-224af - '2E' // 224b0 #56 + '2D' // 224b0 #55 'jE' // 224b1-224bb 'G' // 224bc #6 'cE' // 224bd-224c0 @@ -19812,7 +19805,7 @@ const String encodedFontSetRanges = 'aE' // 224ca-224cb 'G' // 224cc #6 '1eE' // 224cd-224ec - '2E' // 224ed #56 + '2D' // 224ed #55 '1jE' // 224ee-22512 'G' // 22513 #6 'fE' // 22514-2251a @@ -19885,7 +19878,7 @@ const String encodedFontSetRanges = 'qE' // 22927-22938 'G' // 22939 #6 'tE' // 2293a-2294e - '2E' // 2294f #56 + '2D' // 2294f #55 'vE' // 22950-22966 'G' // 22967 #6 'bE' // 22968-2296a @@ -20527,7 +20520,7 @@ const String encodedFontSetRanges = '3cE' // 24897-248e8 'A' // 248e9 #0 'eE' // 248ea-248ef - '2E' // 248f0 #56 + '2D' // 248f0 #55 'bG' // 248f1-248f3 #6 'fE' // 248f4-248fa 'G' // 248fb #6 @@ -20593,7 +20586,7 @@ const String encodedFontSetRanges = 'kE' // 24a02-24a0d 'G' // 24a0e #6 'bE' // 24a0f-24a11 - '2E' // 24a12 #56 + '2D' // 24a12 #55 'G' // 24a13 #6 'E' // 24a14 'G' // 24a15 #6 @@ -21952,7 +21945,7 @@ const String encodedFontSetRanges = 'jE' // 2919d-291a7 'G' // 291a8 #6 '1qE' // 291a9-291d4 - '2E' // 291d5 #56 + '2D' // 291d5 #55 'lE' // 291d6-291e2 'R' // 291e3 #17 'fE' // 291e4-291ea @@ -22681,8 +22674,8 @@ const String encodedFontSetRanges = '129yE' // 2f9f5-30728 'R' // 30729 #17 '75tE' // 3072a-30edc - '6F' // 30edd #161 - '1Y' // 30ede #50 + '6G' // 30edd #162 + '1X' // 30ede #49 '15fE' // 30edf-3106b 'C' // 3106c #2 '27566vE' // 3106d-e002f diff --git a/lib/web_ui/lib/src/engine/initialization.dart b/lib/web_ui/lib/src/engine/initialization.dart index 5e94e5a78b77b..11bdf0879fd2a 100644 --- a/lib/web_ui/lib/src/engine/initialization.dart +++ b/lib/web_ui/lib/src/engine/initialization.dart @@ -226,7 +226,6 @@ Future initializeEngineUi() async { _initializationState = DebugEngineInitializationState.initializingUi; RawKeyboard.initialize(onMacOs: operatingSystem == OperatingSystem.macOs); - MouseCursor.initialize(); ensureFlutterViewEmbedderInitialized(); _initializationState = DebugEngineInitializationState.initialized; } @@ -271,8 +270,3 @@ set debugDisableFontFallbacks(bool value) { _debugDisableFontFallbacks = value; } bool _debugDisableFontFallbacks = false; - -/// The shared instance of PlatformViewManager shared across the engine to handle -/// rendering of PlatformViews into the web app. -// TODO(dit): How to make this overridable from tests? -final PlatformViewManager platformViewManager = PlatformViewManager(); diff --git a/lib/web_ui/lib/src/engine/js_interop/js_loader.dart b/lib/web_ui/lib/src/engine/js_interop/js_loader.dart index 096fb57915a15..eafb921c5135c 100644 --- a/lib/web_ui/lib/src/engine/js_interop/js_loader.dart +++ b/lib/web_ui/lib/src/engine/js_interop/js_loader.dart @@ -73,7 +73,7 @@ abstract class FlutterEngineInitializer{ @JS() @anonymous @staticInterop -abstract class FlutterAppRunner extends JSObject { +abstract class FlutterAppRunner implements JSObject { factory FlutterAppRunner({required RunAppFn runApp,}) => FlutterAppRunner._( runApp: (([RunAppFnParameters? args]) => futureToPromise(runApp(args))).toJS ); @@ -101,7 +101,7 @@ typedef RunAppFn = Future Function([RunAppFnParameters?]); @JS() @anonymous @staticInterop -abstract class FlutterApp extends JSObject { +abstract class FlutterApp implements JSObject { /// Cleans a Flutter app external factory FlutterApp(); } diff --git a/lib/web_ui/lib/src/engine/mouse/context_menu.dart b/lib/web_ui/lib/src/engine/mouse/context_menu.dart new file mode 100644 index 0000000000000..b34e3d64abe72 --- /dev/null +++ b/lib/web_ui/lib/src/engine/mouse/context_menu.dart @@ -0,0 +1,42 @@ +// 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. + +import '../dom.dart'; +import 'prevent_default.dart'; + +/// Controls the browser's context menu in the given [element]. +class ContextMenu { + ContextMenu(this.element); + + final DomElement element; + + /// False when the context menu has been disabled, otherwise true. + bool _enabled = true; + + /// Disables the browser's context menu for this [element]. + /// + /// By default, when a Flutter web app starts, the context menu is enabled. + /// + /// Can be re-enabled by calling [enable]. + void disable() { + if (!_enabled) { + return; + } + _enabled = false; + element.addEventListener('contextmenu', preventDefaultListener); + } + + /// Enables the browser's context menu for this [element]. + /// + /// By default, when a Flutter web app starts, the context menu is already + /// enabled. Typically, this method would be used after calling + /// [disable] to first disable it. + void enable() { + if (_enabled) { + return; + } + _enabled = true; + element.removeEventListener('contextmenu', preventDefaultListener); + } +} diff --git a/lib/web_ui/lib/src/engine/mouse_cursor.dart b/lib/web_ui/lib/src/engine/mouse/cursor.dart similarity index 73% rename from lib/web_ui/lib/src/engine/mouse_cursor.dart rename to lib/web_ui/lib/src/engine/mouse/cursor.dart index 0ccfbed03b129..589891173b5fe 100644 --- a/lib/web_ui/lib/src/engine/mouse_cursor.dart +++ b/lib/web_ui/lib/src/engine/mouse/cursor.dart @@ -2,23 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'embedder.dart'; -import 'util.dart'; +import '../dom.dart'; -/// Provides mouse cursor bindings, such as the `flutter/mousecursor` channel. +/// Controls the mouse cursor in the given [element]. class MouseCursor { - MouseCursor._(); + MouseCursor(this.element); - /// Initializes the [MouseCursor] singleton. - /// - /// Use the [instance] getter to get the singleton after calling this method. - static void initialize() { - _instance ??= MouseCursor._(); - } - - /// The [MouseCursor] singleton. - static MouseCursor? get instance => _instance; - static MouseCursor? _instance; + final DomElement element; // Map from Flutter's kind values to CSS's cursor values. // @@ -61,15 +51,12 @@ class MouseCursor { 'zoomIn': 'zoom-in', 'zoomOut': 'zoom-out', }; + static String _mapKindToCssValue(String? kind) { return _kindToCssValueMap[kind] ?? 'default'; } void activateSystemCursor(String? kind) { - setElementStyle( - flutterViewEmbedder.flutterViewElement, - 'cursor', - _mapKindToCssValue(kind), - ); + element.style.cursor = _mapKindToCssValue(kind); } } diff --git a/lib/web_ui/lib/src/engine/mouse/prevent_default.dart b/lib/web_ui/lib/src/engine/mouse/prevent_default.dart new file mode 100644 index 0000000000000..2371deae14001 --- /dev/null +++ b/lib/web_ui/lib/src/engine/mouse/prevent_default.dart @@ -0,0 +1,10 @@ +// 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. + +import '../dom.dart'; + +/// Listener for DOM events that prevents the default browser behavior. +final DomEventListener preventDefaultListener = createDomEventListener((DomEvent event) { + event.preventDefault(); +}); diff --git a/lib/web_ui/lib/src/engine/navigation/history.dart b/lib/web_ui/lib/src/engine/navigation/history.dart index dce3ba42131f8..ef90ad9c9f282 100644 --- a/lib/web_ui/lib/src/engine/navigation/history.dart +++ b/lib/web_ui/lib/src/engine/navigation/history.dart @@ -217,7 +217,12 @@ class MultiEntriesBrowserHistory extends BrowserHistory { _isTornDown = true; // Restores the html browser history. - assert(_hasSerialCount(currentState)); + assert( + _hasSerialCount(currentState), + currentState == null + ? 'unexpected null history state' + : "history state is missing field 'serialCount'", + ); final int backCount = _currentSerialCount; if (backCount > 0) { await urlStrategy!.go(-backCount); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 89bbab28f046b..5d667c51ccf28 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -21,6 +21,9 @@ ui.VoidCallback? scheduleFrameCallback; typedef HighContrastListener = void Function(bool enabled); typedef _KeyDataResponseCallback = void Function(bool handled); +const StandardMethodCodec standardCodec = StandardMethodCodec(); +const JSONMethodCodec jsonCodec = JSONMethodCodec(); + /// Determines if high contrast is enabled using media query 'forced-colors: active' for Windows class HighContrastSupport { static HighContrastSupport instance = HighContrastSupport(); @@ -129,13 +132,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// The current list of windows. @override - Iterable get views => viewData.values; - final Map viewData = {}; + Iterable get views => viewData.values; + final Map viewData = {}; /// Returns the [FlutterView] with the provided ID if one exists, or null /// otherwise. @override - ui.FlutterView? view({required int id}) => viewData[id]; + EngineFlutterView? view({required int id}) => viewData[id]; /// A map of opaque platform window identifiers to window configurations. /// @@ -171,7 +174,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [PlatformDisptacher.views] for a list of all [FlutterView]s provided /// by the platform. @override - ui.FlutterView? get implicitView => viewData[kImplicitViewId]; + EngineFlutterWindow? get implicitView => viewData[kImplicitViewId] as EngineFlutterWindow?; /// A callback that is invoked whenever the platform's [devicePixelRatio], /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] @@ -432,8 +435,6 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { }; } - PlatformViewMessageHandler? _platformViewMessageHandler; - void _sendPlatformMessage( String name, ByteData? data, @@ -472,8 +473,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// This should be in sync with shell/common/shell.cc case 'flutter/skia': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); + final MethodCall decoded = jsonCodec.decodeMethodCall(data); switch (decoded.method) { case 'Skia.setResourceCacheMaxBytes': if (renderer is CanvasKitRenderer) { @@ -488,7 +488,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // Also respond in HTML mode. Otherwise, apps would have to detect // CanvasKit vs HTML before invoking this method. replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope([true])); + callback, jsonCodec.encodeSuccessEnvelope([true])); } return; @@ -498,25 +498,21 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { return; case 'flutter/platform': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); + final MethodCall decoded = jsonCodec.decodeMethodCall(data); switch (decoded.method) { case 'SystemNavigator.pop': // TODO(a-wallen): As multi-window support expands, the pop call // will need to include the view ID. Right now only one view is // supported. - (viewData[kImplicitViewId]! as EngineFlutterWindow) - .browserHistory - .exit() - .then((_) { + implicitView!.browserHistory.exit().then((_) { replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); + callback, jsonCodec.encodeSuccessEnvelope(true)); }); return; case 'HapticFeedback.vibrate': final String? type = decoded.arguments as String?; vibrate(_getHapticFeedbackDuration(type)); - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setApplicationSwitcherDescription': final Map arguments = decoded.arguments as Map; @@ -525,24 +521,24 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { final int primaryColor = arguments['primaryColor'] as int? ?? 0xFF000000; domDocument.title = label; setThemeColor(ui.Color(primaryColor)); - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setSystemUIOverlayStyle': final Map arguments = decoded.arguments as Map; final int? statusBarColor = arguments['statusBarColor'] as int?; setThemeColor(statusBarColor == null ? null : ui.Color(statusBarColor)); - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setPreferredOrientations': final List arguments = decoded.arguments as List; - flutterViewEmbedder.setPreferredOrientation(arguments).then((bool success) { + ScreenOrientation.instance.setPreferredOrientation(arguments).then((bool success) { replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(success)); + callback, jsonCodec.encodeSuccessEnvelope(success)); }); return; case 'SystemSound.play': // There are no default system sounds on web. - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'Clipboard.setData': ClipboardMessageHandler().setDataMethodCall(decoded, callback); @@ -565,46 +561,44 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { return; case 'flutter/contextmenu': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); + final MethodCall decoded = jsonCodec.decodeMethodCall(data); switch (decoded.method) { case 'enableContextMenu': - flutterViewEmbedder.enableContextMenu(); - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + implicitView!.contextMenu.enable(); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'disableContextMenu': - flutterViewEmbedder.disableContextMenu(); - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + implicitView!.contextMenu.disable(); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; } return; case 'flutter/mousecursor': - const MethodCodec codec = StandardMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); + final MethodCall decoded = standardCodec.decodeMethodCall(data); final Map arguments = decoded.arguments as Map; switch (decoded.method) { case 'activateSystemCursor': - MouseCursor.instance!.activateSystemCursor(arguments.tryString('kind')); + implicitView!.mouseCursor.activateSystemCursor(arguments.tryString('kind')); } return; case 'flutter/web_test_e2e': - const MethodCodec codec = JSONMethodCodec(); replyToPlatformMessage( callback, - codec.encodeSuccessEnvelope( - _handleWebTestEnd2EndMessage(codec, data))); + jsonCodec.encodeSuccessEnvelope( + _handleWebTestEnd2EndMessage(jsonCodec, data))); return; case 'flutter/platform_views': - _platformViewMessageHandler ??= PlatformViewMessageHandler( - contentManager: platformViewManager, - contentHandler: (DomElement content) { - flutterViewEmbedder.glassPaneElement.append(content); - }, - ); - _platformViewMessageHandler!.handlePlatformViewCall(data, callback!); + final MethodCall(:String method, :dynamic arguments) = standardCodec.decodeMethodCall(data); + final int? flutterViewId = tryViewId(arguments); + if (flutterViewId == null) { + implicitView!.platformViewMessageHandler.handleLegacyPlatformViewCall(method, arguments, callback!); + return; + } + arguments as Map; + viewData[flutterViewId]!.platformViewMessageHandler.handlePlatformViewCall(method, arguments, callback!); return; case 'flutter/accessibility': @@ -618,12 +612,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // TODO(a-wallen): As multi-window support expands, the navigation call // will need to include the view ID. Right now only one view is // supported. - (viewData[kImplicitViewId]! as EngineFlutterWindow) - .handleNavigationMessage(data) - .then((bool handled) { + implicitView!.handleNavigationMessage(data).then((bool handled) { if (handled) { - const MethodCodec codec = JSONMethodCodec(); - replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); } else { callback?.call(null); } @@ -1231,8 +1222,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// requests from the embedder. @override String get defaultRouteName { - return _defaultRouteName ??= - (viewData[kImplicitViewId]! as EngineFlutterWindow).browserHistory.currentPath; + return _defaultRouteName ??= implicitView!.browserHistory.currentPath; } /// Lazily initialized when the `defaultRouteName` getter is invoked. @@ -1364,7 +1354,7 @@ class ViewConfiguration { }); ViewConfiguration copyWith({ - ui.FlutterView? view, + EngineFlutterView? view, double? devicePixelRatio, ui.Rect? geometry, bool? visible, @@ -1389,7 +1379,7 @@ class ViewConfiguration { ); } - final ui.FlutterView? view; + final EngineFlutterView? view; final double devicePixelRatio; final ui.Rect geometry; final bool visible; diff --git a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart index 73d951596a65d..222e349e0910d 100644 --- a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart +++ b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart @@ -4,9 +4,7 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web; -import '../browser_detection.dart'; import '../dom.dart'; -import '../embedder.dart'; import '../util.dart'; import 'slots.dart'; @@ -38,6 +36,11 @@ class PlatformViewManager { ); } + /// The shared instance of PlatformViewManager shared across the engine to handle + /// rendering of PlatformViews into the web app. + // TODO(dit): How to make this overridable from tests? + static final PlatformViewManager instance = PlatformViewManager(); + // The factory functions, indexed by the viewType final Map _factories = {}; @@ -49,7 +52,7 @@ class PlatformViewManager { /// Returns `true` if the passed in `viewType` has been registered before. /// - /// See [registerViewFactory] to understand how factories are registered. + /// See [registerFactory] to understand how factories are registered. bool knowsViewType(String viewType) { return _factories.containsKey(viewType); } @@ -156,38 +159,11 @@ class PlatformViewManager { /// Removes a PlatformView by its `viewId` from the manager, and from the DOM. /// - /// Once a view has been cleared, calls [knowsViewId] will fail, as if it had + /// Once a view has been cleared, calls to [knowsViewId] will fail, as if it had /// never been rendered before. void clearPlatformView(int viewId) { // Remove from our cache, and then from the DOM... - final DomElement? element = _contents.remove(viewId); - _safelyRemoveSlottedElement(element); - } - - // We need to remove slotted elements like this because of a Safari bug that - // gets triggered when a slotted element is removed in a JS event different - // than its slot (after the slot is removed). - // - // TODO(web): Cleanup https://github.com/flutter/flutter/issues/85816 - void _safelyRemoveSlottedElement(DomElement? element) { - if (element == null) { - return; - } - if (browserEngine != BrowserEngine.webkit) { - element.remove(); - return; - } - final String tombstoneName = "tombstone-${element.getAttribute('slot')}"; - // Create and inject a new slot in the shadow root - final DomElement slot = domDocument.createElement('slot') - ..style.display = 'none' - ..setAttribute('name', tombstoneName); - flutterViewEmbedder.glassPaneShadow.append(slot); - // Link the element to the new slot - element.setAttribute('slot', tombstoneName); - // Delete both the element, and the new slot - element.remove(); - slot.remove(); + _contents.remove(viewId)?.remove(); } /// Attempt to ensure that the contents of the user-supplied DOM element will @@ -244,5 +220,7 @@ DomElement _defaultFactory( }) { params!; params as Map; - return domDocument.createElement(params.readString('tagName')); + return domDocument.createElement(params.readString('tagName')) + ..style.width = '100%' + ..style.height = '100%'; } diff --git a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart index c55380ec6a325..9e20b3674ada3 100644 --- a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart +++ b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart @@ -5,9 +5,11 @@ import 'dart:typed_data'; import '../dom.dart'; +import '../platform_dispatcher.dart'; import '../services.dart'; import '../util.dart'; import 'content_manager.dart'; +import 'slots.dart'; /// The signature for a callback for a Platform Message. From the `ui` package. /// Copied here so there's no circular dependencies. @@ -40,72 +42,68 @@ typedef PlatformViewContentHandler = void Function(DomElement); /// [HtmlViewEmbedder.disposeViews] class PlatformViewMessageHandler { PlatformViewMessageHandler({ - required PlatformViewManager contentManager, - PlatformViewContentHandler? contentHandler, - }) : _contentManager = contentManager, - _contentHandler = contentHandler; + required DomElement platformViewsContainer, + PlatformViewManager? contentManager, + }) : _contentManager = contentManager ?? PlatformViewManager.instance, + _platformViewsContainer = platformViewsContainer; final MethodCodec _codec = const StandardMethodCodec(); final PlatformViewManager _contentManager; - final PlatformViewContentHandler? _contentHandler; + final DomElement _platformViewsContainer; /// Handle a `create` Platform View message. /// - /// This will attempt to render the `contents` and of a Platform View, if its + /// This will attempt to render the `contents` of a Platform View, if its /// `viewType` has been registered previously. /// - /// (See [PlatformViewContentManager.registerFactory] for more details.) + /// (See [PlatformViewManager.registerFactory] for more details.) /// - /// The `contents` are delegated to a [_contentHandler] function, so the - /// active rendering backend can inject them in the right place of the DOM. + /// The `contents` are inserted into the [_platformViewsContainer]. /// /// If all goes well, this function will `callback` with an empty success envelope. /// In case of error, this will `callback` with an error envelope describing the error. void _createPlatformView( - MethodCall methodCall, - _PlatformMessageResponseCallback callback, - ) { - final Map args = methodCall.arguments as Map; - final int viewId = args.readInt('id'); - final String viewType = args.readString('viewType'); - final Object? params = args['params']; - - if (!_contentManager.knowsViewType(viewType)) { + _PlatformMessageResponseCallback callback, { + required int platformViewId, + required String platformViewType, + required Object? params, + }) { + if (!_contentManager.knowsViewType(platformViewType)) { callback(_codec.encodeErrorEnvelope( code: 'unregistered_view_type', message: 'A HtmlElementView widget is trying to create a platform view ' - 'with an unregistered type: <$viewType>.', + 'with an unregistered type: <$platformViewType>.', details: 'If you are the author of the PlatformView, make sure ' '`registerViewFactory` is invoked.', )); return; } - if (_contentManager.knowsViewId(viewId)) { + if (_contentManager.knowsViewId(platformViewId)) { callback(_codec.encodeErrorEnvelope( code: 'recreating_view', message: 'trying to create an already created view', - details: 'view id: $viewId', + details: 'view id: $platformViewId', )); return; } final DomElement content = _contentManager.renderContent( - viewType, - viewId, + platformViewType, + platformViewId, params, ); // For now, we don't need anything fancier. If needed, this can be converted // to a PlatformViewStrategy class for each web-renderer backend? - _contentHandler?.call(content); + _platformViewsContainer.append(content); callback(_codec.encodeSuccessEnvelope(null)); } /// Handle a `dispose` Platform View message. /// /// This will clear the cached information that the framework has about a given - /// `viewId`, through the [_contentManager]. + /// `platformViewId`, through the [_contentManager]. /// /// Once that's done, the dispose call is delegated to the [_disposeHandler] /// function, so the active rendering backend can dispose of whatever resources @@ -113,34 +111,67 @@ class PlatformViewMessageHandler { /// /// This function should always `callback` with an empty success envelope. void _disposePlatformView( - MethodCall methodCall, - _PlatformMessageResponseCallback callback, - ) { - final int viewId = methodCall.arguments as int; - + _PlatformMessageResponseCallback callback, { + required int platformViewId, + }) { // The contentManager removes the slot and the contents from its internal // cache, and the DOM. - _contentManager.clearPlatformView(viewId); + _contentManager.clearPlatformView(platformViewId); callback(_codec.encodeSuccessEnvelope(null)); } + /// Handles legacy PlatformViewCalls that don't contain a Flutter View ID. + /// + /// This is transitional code to support the old platform view channel. As + /// soon as the framework code is updated to send the Flutter View ID, this + /// method can be removed. + void handleLegacyPlatformViewCall( + String method, + dynamic arguments, + _PlatformMessageResponseCallback callback, + ) { + switch (method) { + case 'create': + arguments as Map; + _createPlatformView( + callback, + platformViewId: arguments.readInt('id'), + platformViewType: arguments.readString('viewType'), + params: arguments['params'], + ); + return; + case 'dispose': + _disposePlatformView(callback, platformViewId: arguments as int); + return; + } + callback(null); + } + /// Handles a PlatformViewCall to the `flutter/platform_views` channel. /// /// This method handles two possible messages: /// * `create`: See [_createPlatformView] /// * `dispose`: See [_disposePlatformView] void handlePlatformViewCall( - ByteData? data, + String method, + Map arguments, _PlatformMessageResponseCallback callback, ) { - final MethodCall decoded = _codec.decodeMethodCall(data); - switch (decoded.method) { + switch (method) { case 'create': - _createPlatformView(decoded, callback); + _createPlatformView( + callback, + platformViewId: arguments.readInt('platformViewId'), + platformViewType: arguments.readString('platformViewType'), + params: arguments['params'], + ); return; case 'dispose': - _disposePlatformView(decoded, callback); + _disposePlatformView( + callback, + platformViewId: arguments.readInt('platformViewId'), + ); return; } callback(null); diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index e3f43fde447c4..ce20224042438 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:js_interop'; import 'dart:math' as math; @@ -26,7 +25,7 @@ const bool _debugLogPointerEvents = false; const bool _debugLogFlutterEvents = false; /// The signature of a callback that handles pointer events. -typedef _PointerDataCallback = void Function(DomEvent event, List); +typedef _PointerDataCallback = void Function(Iterable); // The mask for the bitfield of event buttons. Buttons not contained in this // mask are cut off. @@ -104,14 +103,11 @@ class PointerBinding { } } - final ClickDebouncer clickDebouncer = ClickDebouncer(); - /// Performs necessary clean up for PointerBinding including removing event listeners /// and clearing the existing pointer state void dispose() { _adapter.clearListeners(); _pointerDataConverter.clearPointerState(); - clickDebouncer.reset(); } final DomElement flutterViewElement; @@ -160,247 +156,20 @@ class PointerBinding { // TODO(dit): remove old API fallbacks, https://github.com/flutter/flutter/issues/116141 _BaseAdapter _createAdapter() { if (_detector.hasPointerEvents) { - return _PointerAdapter(clickDebouncer.onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter); + return _PointerAdapter(_onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter); } // Fallback for Safari Mobile < 13. To be removed. if (_detector.hasTouchEvents) { - return _TouchAdapter(clickDebouncer.onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter); + return _TouchAdapter(_onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter); } // Fallback for Safari Desktop < 13. To be removed. if (_detector.hasMouseEvents) { - return _MouseAdapter(clickDebouncer.onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter); + return _MouseAdapter(_onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter); } throw UnsupportedError('This browser does not support pointer, touch, or mouse events.'); } -} - -@visibleForTesting -typedef QueuedEvent = ({ DomEvent event, Duration timeStamp, List data }); - -@visibleForTesting -typedef DebounceState = ({ - DomElement target, - Timer timer, - List queue, -}); - -/// Disambiguates taps and clicks that are produced both by the framework from -/// `pointerdown`/`pointerup` events and those detected as DOM "click" events by -/// the browser. -/// -/// The implementation is waiting for a `pointerdown`, and as soon as it sees -/// one stops forwarding pointer events to the framework, and instead queues -/// them in a list. The queuing process stops as soon as one of the following -/// two conditions happens first: -/// -/// * 200ms passes after the `pointerdown` event. Most clicks, even slow ones, -/// are typically done by then. Importantly, screen readers simulate clicks -/// much faster than 200ms. So if the timer expires, it is likely the user is -/// not interested in producing a click, so the debouncing process stops and -/// all queued events are forwarded to the framework. If, for example, a -/// tappable node is inside a scrollable viewport, the events can be -/// intrepreted by the framework to initiate scrolling. -/// * A `click` event arrives. If the event queue has not been flushed to the -/// framework, the event is forwarded to the framework as a -/// `SemanticsAction.tap`, and all the pointer events are dropped. If, by the -/// time the click event arrives, the queue was flushed (but no more than 50ms -/// ago), then the click event is dropped instead under the assumption that -/// the flushed pointer events are interpreted by the framework as the desired -/// gesture. -/// -/// This mechanism is in place to deal with https://github.com/flutter/flutter/issues/130162. -class ClickDebouncer { - DebounceState? _state; - - @visibleForTesting - DebounceState? get debugState => _state; - - // The timestamp of the last "pointerup" DOM event that was flushed. - // - // Not to be confused with the time when it was flushed. The two may be far - // apart because the flushing can happen after a delay due to timer, or events - // that happen after the said "pointerup". - Duration? _lastFlushedPointerUpTimeStamp; - - /// Returns true if the debouncer has a non-empty queue of pointer events that - /// were withheld from the framework. - /// - /// This value is normally false, and it flips to true when the first - /// pointerdown is observed that lands on a tappable semantics node, denoted - /// by the presence of the `flt-tappable` attribute. - bool get isDebouncing => _state != null; - - /// Processes a pointer event. - /// - /// If semantics are off, simply forwards the event to the framework. - /// - /// If currently debouncing events (see [isDebouncing]), adds the event to - /// the debounce queue, unless the target of the event is different from the - /// target that initiated the debouncing process, in which case stops - /// debouncing and flushes pointer events to the framework. - /// - /// If the event is a `pointerdown` and the target is `flt-tappable`, begins - /// debouncing events. - /// - /// In all other situations forwards the event to the framework. - void onPointerData(DomEvent event, List data) { - if (!EnginePlatformDispatcher.instance.semanticsEnabled) { - _sendToFramework(event, data); - return; - } - - if (isDebouncing) { - _debounce(event, data); - } else if (event.type == 'pointerdown') { - _startDebouncing(event, data); - } else { - _sendToFramework(event, data); - } - } - - /// Notifies the debouncer of the browser-detected "click" DOM event. - /// - /// Forwards the event to the framework, unless it is deduplicated because - /// the corresponding pointer down/up events were recently flushed to the - /// framework already. - void onClick(DomEvent click, int semanticsNodeId, bool isListening) { - assert(click.type == 'click'); - - if (!isDebouncing) { - // There's no pending queue of pointer events that are being debounced. It - // is a standalone click event. Unless pointer down/up were flushed - // recently and if the node is currently listening to event, forward to - // the framework. - if (isListening && _shouldSendClickEventToFramework(click)) { - EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - semanticsNodeId, ui.SemanticsAction.tap, null); - } - return; - } - - if (isListening) { - // There's a pending queue of pointer events. Prefer sending the tap action - // instead of pointer events, because the pointer events may not land on the - // combined semantic node and miss the click/tap. - final DebounceState state = _state!; - _state = null; - state.timer.cancel(); - EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - semanticsNodeId, ui.SemanticsAction.tap, null); - } else { - // The semantic node is not listening to taps. Flush the pointer events - // for the framework to figure out what to do with them. It's possible - // the framework is interested in gestures other than taps. - _flush(); - } - } - - void _startDebouncing(DomEvent event, List data) { - assert( - _state == null, - 'Cannot start debouncing. Already debouncing.' - ); - assert( - event.type == 'pointerdown', - 'Click debouncing must begin with a pointerdown' - ); - - final DomEventTarget? target = event.target; - if (target is DomElement && target.hasAttribute('flt-tappable')) { - _state = ( - target: target, - // The 200ms duration was chosen empirically by testing tapping, mouse - // clicking, trackpad tapping and clicking, as well as the following - // screen readers: TalkBack on Android, VoiceOver on macOS, Narrator/ - // NVDA/JAWS on Windows. 200ms seemed to hit the sweet spot by - // satisfying the following: - // * It was short enough that delaying the `pointerdown` still allowed - // drag gestures to begin reasonably soon (e.g. scrolling). - // * It was long enough to register taps and clicks. - // * It was successful at detecting taps generated by all tested - // screen readers. - timer: Timer(const Duration(milliseconds: 200), _onTimerExpired), - queue: [( - event: event, - timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp!), - data: data, - )], - ); - } else { - // The event landed on an non-tappable target. Assume this won't lead to - // double clicks and forward the event to the framework. - _sendToFramework(event, data); - } - } - - void _debounce(DomEvent event, List data) { - assert( - _state != null, - 'Cannot debounce event. Debouncing state not established by _startDebouncing.' - ); - - final DebounceState state = _state!; - state.queue.add(( - event: event, - timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp!), - data: data, - )); - - // It's only interesting to debounce clicks when both `pointerdown` and - // `pointerup` land on the same element. - if (event.type == 'pointerup') { - // TODO(yjbanov): this is a bit mouthful, but see https://github.com/dart-lang/sdk/issues/53070 - final DomEventTarget? eventTarget = event.target; - final DomElement stateTarget = state.target; - final bool targetChanged = eventTarget != stateTarget; - if (targetChanged) { - _flush(); - } - } - } - - void _onTimerExpired() { - if (!isDebouncing) { - return; - } - _flush(); - } - - // If the click event happens soon after the last `pointerup` event that was - // already flushed to the framework, the click event is dropped to avoid - // double click. - bool _shouldSendClickEventToFramework(DomEvent click) { - final Duration? lastFlushedPointerUpTimeStamp = _lastFlushedPointerUpTimeStamp; - if (lastFlushedPointerUpTimeStamp == null) { - // We haven't seen a pointerup. It's standalone click event. Let it through. - return true; - } - - final Duration clickTimeStamp = _BaseAdapter._eventTimeStampToDuration(click.timeStamp!); - final Duration delta = clickTimeStamp - lastFlushedPointerUpTimeStamp; - return delta >= const Duration(milliseconds: 50); - } - - void _flush() { - assert(_state != null); - - final DebounceState state = _state!; - state.timer.cancel(); - - final List aggregateData = []; - for (final QueuedEvent queuedEvent in state.queue) { - if (queuedEvent.event.type == 'pointerup') { - _lastFlushedPointerUpTimeStamp = queuedEvent.timeStamp; - } - aggregateData.addAll(queuedEvent.data); - } - - _sendToFramework(null, aggregateData); - _state = null; - } - - void _sendToFramework(DomEvent? event, List data) { + void _onPointerData(Iterable data) { final ui.PointerDataPacket packet = ui.PointerDataPacket(data: data.toList()); if (_debugLogFlutterEvents) { for(final ui.PointerData datum in data) { @@ -409,16 +178,6 @@ class ClickDebouncer { } EnginePlatformDispatcher.instance.invokeOnPointerDataPacket(packet); } - - /// Cancels any pending debounce process and forgets anything that happened so - /// far. - /// - /// This object can be used as if it was just initialized. - void reset() { - _state?.timer.cancel(); - _state = null; - _lastFlushedPointerUpTimeStamp = null; - } } class PointerSupportDetector { @@ -438,51 +197,55 @@ class _Listener { required this.event, required this.target, required this.handler, - required this.useCapture, + required this.isNative, }); - /// Registers a listener for the given `event` on a `target`. - /// - /// If `passive` is null uses the default behavior determined by the event - /// type. If `passive` is true, marks the handler as non-blocking for the - /// built-in browser behavior. This means the browser will not wait for the - /// handler to finish execution before performing the default action - /// associated with this event. If `passive` is false, the browser will wait - /// for the handler to finish execution before performing the respective - /// action. + /// Registers a listener for the given [event] on [target] using the Dart-to-JS API. factory _Listener.register({ required String event, required DomEventTarget target, required DartDomEventListener handler, - bool capture = false, - bool? passive, }) { final DomEventListener jsHandler = createDomEventListener(handler); - if (passive == null) { - target.addEventListener(event, jsHandler, capture); - } else { - final Map eventOptions = { - 'capture': capture, - 'passive': passive, - }; - target.addEventListenerWithOptions(event, jsHandler, eventOptions); - } + final _Listener listener = _Listener._( + event: event, + target: target, + handler: jsHandler, + isNative: false, + ); + target.addEventListener(event, jsHandler); + return listener; + } - return _Listener._( + /// Registers a listener for the given [event] on [target] using the native JS API. + factory _Listener.registerNative({ + required String event, + required DomEventTarget target, + required DomEventListener jsHandler, + bool passive = false, + }) { + final Map eventOptions = { + 'passive': passive, + }; + final _Listener listener = _Listener._( event: event, target: target, handler: jsHandler, - useCapture: capture, + isNative: true, ); + target.addEventListenerWithOptions(event, jsHandler, eventOptions); + return listener; } final String event; + final DomEventTarget target; final DomEventListener handler; - final bool useCapture; + + final bool isNative; void unregister() { - target.removeEventListener(event, handler, useCapture); + target.removeEventListener(event, handler); } } @@ -523,18 +286,11 @@ abstract class _BaseAdapter { /// as the [target], while move and up events should use [domWindow] /// instead, because the browser doesn't fire the latter two for DOM elements /// when the pointer is outside the window. - /// - /// If [useCapture] is set to false, the event will be handled in the - /// bubbling phase instead of the capture phase. - /// See [DOM Level 3 Events][events] for a detailed explanation. - /// - /// [events]: https://www.w3.org/TR/DOM-Level-3-Events/#event-flow void addEventListener( DomEventTarget target, String eventName, - DartDomEventListener handler, { - bool useCapture = true, - }) { + DartDomEventListener handler, + ) { JSVoid loggedHandler(DomEvent event) { if (_debugLogPointerEvents) { if (domInstanceOfString(event, 'PointerEvent')) { @@ -558,7 +314,6 @@ abstract class _BaseAdapter { event: eventName, target: target, handler: loggedHandler, - capture: useCapture, )); } @@ -722,11 +477,10 @@ mixin _WheelEventListenerMixin on _BaseAdapter { } void _addWheelEventListener(DartDomEventListener handler) { - _listeners.add(_Listener.register( + _listeners.add(_Listener.registerNative( event: 'wheel', target: flutterViewElement, - handler: handler, - passive: false, + jsHandler: createDomEventListener(handler), )); } @@ -736,7 +490,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter { if (_debugLogPointerEvents) { print(event.type); } - _callback(e, _convertWheelEventToPointerData(event)); + _callback(_convertWheelEventToPointerData(event)); // Prevent default so mouse wheel event doesn't get converted to // a scroll event that semantic nodes would process. // @@ -947,7 +701,6 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { DomEventTarget target, String eventName, _PointerEventListener handler, { - bool useCapture = true, bool checkModifiers = true, }) { addEventListener(target, eventName, (DomEvent event) { @@ -956,7 +709,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { _checkModifiersState(event); } handler(pointerEvent); - }, useCapture: useCapture); + }); } void _checkModifiersState(DomPointerEvent event) { @@ -986,7 +739,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { buttons: event.buttons!.toInt(), ); _convertEventsToPointerData(data: pointerData, event: event, details: down); - _callback(event, pointerData); + _callback(pointerData); }); // Why `domWindow` you ask? See this fiddle: https://jsfiddle.net/ditman/7towxaqp @@ -1003,7 +756,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _SanitizedDetails move = sanitizer.sanitizeMoveEvent(buttons: event.buttons!.toInt()); _convertEventsToPointerData(data: pointerData, event: event, details: move); } - _callback(event, pointerData); + _callback(pointerData); }); _addPointerEventListener(flutterViewElement, 'pointerleave', (DomPointerEvent event) { @@ -1013,9 +766,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _SanitizedDetails? details = sanitizer.sanitizeLeaveEvent(buttons: event.buttons!.toInt()); if (details != null) { _convertEventsToPointerData(data: pointerData, event: event, details: details); - _callback(event, pointerData); + _callback(pointerData); } - }, useCapture: false, checkModifiers: false); + }, checkModifiers: false); // TODO(dit): This must happen in the flutterViewElement, https://github.com/flutter/flutter/issues/116561 _addPointerEventListener(domWindow, 'pointerup', (DomPointerEvent event) { @@ -1026,7 +779,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { _removePointerIfUnhoverable(event); if (details != null) { _convertEventsToPointerData(data: pointerData, event: event, details: details); - _callback(event, pointerData); + _callback(pointerData); } } }); @@ -1042,7 +795,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _SanitizedDetails details = _getSanitizer(device).sanitizeCancelEvent(); _removePointerIfUnhoverable(event); _convertEventsToPointerData(data: pointerData, event: event, details: details); - _callback(event, pointerData); + _callback(pointerData); } }, checkModifiers: false); @@ -1181,7 +934,7 @@ class _TouchAdapter extends _BaseAdapter { ); } } - _callback(event, pointerData); + _callback(pointerData); }); _addTouchEventListener(flutterViewElement, 'touchmove', (DomTouchEvent event) { @@ -1200,7 +953,7 @@ class _TouchAdapter extends _BaseAdapter { ); } } - _callback(event, pointerData); + _callback(pointerData); }); _addTouchEventListener(flutterViewElement, 'touchend', (DomTouchEvent event) { @@ -1222,7 +975,7 @@ class _TouchAdapter extends _BaseAdapter { ); } } - _callback(event, pointerData); + _callback(pointerData); }); _addTouchEventListener(flutterViewElement, 'touchcancel', (DomTouchEvent event) { @@ -1241,7 +994,7 @@ class _TouchAdapter extends _BaseAdapter { ); } } - _callback(event, pointerData); + _callback(pointerData); }); } @@ -1302,7 +1055,6 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { DomEventTarget target, String eventName, _MouseEventListener handler, { - bool useCapture = true, bool checkModifiers = true, }) { addEventListener(target, eventName, (DomEvent event) { @@ -1311,7 +1063,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { _checkModifiersState(event); } handler(mouseEvent); - }, useCapture: useCapture); + }); } void _checkModifiersState(DomMouseEvent event) { @@ -1339,7 +1091,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { buttons: event.buttons!.toInt(), ); _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); - _callback(event, pointerData); + _callback(pointerData); }); // Why `domWindow` you ask? See this fiddle: https://jsfiddle.net/ditman/7towxaqp @@ -1351,7 +1103,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { } final _SanitizedDetails move = _sanitizer.sanitizeMoveEvent(buttons: event.buttons!.toInt()); _convertEventsToPointerData(data: pointerData, event: event, details: move); - _callback(event, pointerData); + _callback(pointerData); }); _addMouseEventListener(flutterViewElement, 'mouseleave', (DomMouseEvent event) { @@ -1359,9 +1111,9 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _SanitizedDetails? details = _sanitizer.sanitizeLeaveEvent(buttons: event.buttons!.toInt()); if (details != null) { _convertEventsToPointerData(data: pointerData, event: event, details: details); - _callback(event, pointerData); + _callback(pointerData); } - }, useCapture: false); + }); // TODO(dit): This must happen in the flutterViewElement, https://github.com/flutter/flutter/issues/116561 _addMouseEventListener(domWindow, 'mouseup', (DomMouseEvent event) { @@ -1369,7 +1121,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _SanitizedDetails? sanitizedDetails = _sanitizer.sanitizeUpEvent(buttons: event.buttons?.toInt()); if (sanitizedDetails != null) { _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); - _callback(event, pointerData); + _callback(pointerData); } }); diff --git a/lib/web_ui/lib/src/engine/scene_view.dart b/lib/web_ui/lib/src/engine/scene_view.dart index f1c75c0939d25..41ac3fab96cd0 100644 --- a/lib/web_ui/lib/src/engine/scene_view.dart +++ b/lib/web_ui/lib/src/engine/scene_view.dart @@ -16,6 +16,20 @@ abstract class PictureRenderer { FutureOr renderPicture(ScenePicture picture); } +class _SceneRender { + _SceneRender(this.scene, this._completer) { + scene.beginRender(); + } + + final EngineScene scene; + final Completer _completer; + + void done() { + scene.endRender(); + _completer.complete(); + } +} + // This class builds a DOM tree that composites an `EngineScene`. class EngineSceneView { factory EngineSceneView(PictureRenderer pictureRenderer) { @@ -30,16 +44,38 @@ class EngineSceneView { List containers = []; - int queuedRenders = 0; - static const int kMaxQueuedRenders = 3; + _SceneRender? _currentRender; + _SceneRender? _nextRender; + + Future renderScene(EngineScene scene) { + if (_currentRender != null) { + // If a scene is already queued up, drop it and queue this one up instead + // so that the scene view always displays the most recently requested scene. + _nextRender?.done(); + final Completer completer = Completer(); + _nextRender = _SceneRender(scene, completer); + return completer.future; + } + final Completer completer = Completer(); + _currentRender = _SceneRender(scene, completer); + _kickRenderLoop(); + return completer.future; + } - Future renderScene(EngineScene scene) async { - if (queuedRenders >= kMaxQueuedRenders) { + Future _kickRenderLoop() async { + final _SceneRender current = _currentRender!; + await _renderScene(current.scene); + current.done(); + _currentRender = _nextRender; + _nextRender = null; + if (_currentRender == null) { return; + } else { + return _kickRenderLoop(); } - queuedRenders += 1; + } - scene.beginRender(); + Future _renderScene(EngineScene scene) async { final List slices = scene.rootLayer.slices; final Iterable> renderFutures = slices.map( (LayerSlice slice) async => switch (slice) { @@ -113,9 +149,6 @@ class EngineSceneView { sceneElement.removeChild(currentElement); currentElement = sibling; } - scene.endRender(); - - queuedRenders -= 1; } } diff --git a/lib/web_ui/lib/src/engine/semantics.dart b/lib/web_ui/lib/src/engine/semantics.dart index d1d58ce4a688e..0ba3230ee96a1 100644 --- a/lib/web_ui/lib/src/engine/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics.dart @@ -8,6 +8,7 @@ export 'semantics/focusable.dart'; export 'semantics/image.dart'; export 'semantics/incrementable.dart'; export 'semantics/label_and_value.dart'; +export 'semantics/link.dart'; export 'semantics/live_region.dart'; export 'semantics/platform_view.dart'; export 'semantics/scrollable.dart'; diff --git a/lib/web_ui/lib/src/engine/semantics/checkable.dart b/lib/web_ui/lib/src/engine/semantics/checkable.dart index e6f5b2efcc223..d50f51c512c64 100644 --- a/lib/web_ui/lib/src/engine/semantics/checkable.dart +++ b/lib/web_ui/lib/src/engine/semantics/checkable.dart @@ -13,7 +13,6 @@ import 'package:ui/ui.dart' as ui; -import '../dom.dart'; import 'semantics.dart'; /// The specific type of checkable control. @@ -63,18 +62,18 @@ class Checkable extends PrimaryRoleManager { if (semanticsObject.isFlagsDirty) { switch (_kind) { case _CheckableKind.checkbox: - semanticsObject.setAriaRole('checkbox'); + setAriaRole('checkbox'); case _CheckableKind.radio: - semanticsObject.setAriaRole('radio'); + setAriaRole('radio'); case _CheckableKind.toggle: - semanticsObject.setAriaRole('switch'); + setAriaRole('switch'); } /// Adding disabled and aria-disabled attribute to notify the assistive /// technologies of disabled elements. _updateDisabledAttribute(); - semanticsObject.element.setAttribute( + setAttribute( 'aria-checked', (semanticsObject.hasFlag(ui.SemanticsFlag.isChecked) || semanticsObject.hasFlag(ui.SemanticsFlag.isToggled)) @@ -92,17 +91,15 @@ class Checkable extends PrimaryRoleManager { void _updateDisabledAttribute() { if (semanticsObject.enabledState() == EnabledState.disabled) { - final DomElement element = semanticsObject.element; - element - ..setAttribute('aria-disabled', 'true') - ..setAttribute('disabled', 'true'); + setAttribute('aria-disabled', 'true'); + setAttribute('disabled', 'true'); } else { _removeDisabledAttribute(); } } void _removeDisabledAttribute() { - final DomElement element = semanticsObject.element; - element..removeAttribute('aria-disabled')..removeAttribute('disabled'); + removeAttribute('aria-disabled'); + removeAttribute('disabled'); } } diff --git a/lib/web_ui/lib/src/engine/semantics/dialog.dart b/lib/web_ui/lib/src/engine/semantics/dialog.dart index 8155ba4838415..9f64e42e7acff 100644 --- a/lib/web_ui/lib/src/engine/semantics/dialog.dart +++ b/lib/web_ui/lib/src/engine/semantics/dialog.dart @@ -38,8 +38,8 @@ class Dialog extends PrimaryRoleManager { } return true; }()); - semanticsObject.element.setAttribute('aria-label', label ?? ''); - semanticsObject.setAriaRole('dialog'); + setAttribute('aria-label', label ?? ''); + setAriaRole('dialog'); } } @@ -51,8 +51,8 @@ class Dialog extends PrimaryRoleManager { return; } - semanticsObject.setAriaRole('dialog'); - semanticsObject.element.setAttribute( + setAriaRole('dialog'); + setAttribute( 'aria-describedby', routeName.semanticsObject.element.id, ); @@ -61,7 +61,10 @@ class Dialog extends PrimaryRoleManager { /// Supplies a description for the nearest ancestor [Dialog]. class RouteName extends RoleManager { - RouteName(SemanticsObject semanticsObject) : super(Role.routeName, semanticsObject); + RouteName( + SemanticsObject semanticsObject, + PrimaryRoleManager owner, + ) : super(Role.routeName, semanticsObject, owner); Dialog? _dialog; diff --git a/lib/web_ui/lib/src/engine/semantics/focusable.dart b/lib/web_ui/lib/src/engine/semantics/focusable.dart index 4b3285dbdf399..4caf56f3f3eac 100644 --- a/lib/web_ui/lib/src/engine/semantics/focusable.dart +++ b/lib/web_ui/lib/src/engine/semantics/focusable.dart @@ -28,9 +28,9 @@ import 'semantics.dart'; /// /// * https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets class Focusable extends RoleManager { - Focusable(SemanticsObject semanticsObject) + Focusable(SemanticsObject semanticsObject, PrimaryRoleManager owner) : _focusManager = AccessibilityFocusManager(semanticsObject.owner), - super(Role.focusable, semanticsObject); + super(Role.focusable, semanticsObject, owner); final AccessibilityFocusManager _focusManager; @@ -38,7 +38,7 @@ class Focusable extends RoleManager { void update() { if (semanticsObject.isFocusable) { if (!_focusManager.isManaging) { - _focusManager.manage(semanticsObject.id, semanticsObject.element); + _focusManager.manage(semanticsObject.id, owner.element); } _focusManager.changeFocus(semanticsObject.hasFocus && (!semanticsObject.hasEnabledState || semanticsObject.isEnabled)); } else { diff --git a/lib/web_ui/lib/src/engine/semantics/image.dart b/lib/web_ui/lib/src/engine/semantics/image.dart index 0f0eb298b0ad3..efe1d7cdb414b 100644 --- a/lib/web_ui/lib/src/engine/semantics/image.dart +++ b/lib/web_ui/lib/src/engine/semantics/image.dart @@ -49,14 +49,14 @@ class ImageRoleManager extends PrimaryRoleManager { ..height = '${semanticsObject.rect!.height}px'; } _auxiliaryImageElement!.style.fontSize = '6px'; - semanticsObject.element.append(_auxiliaryImageElement!); + append(_auxiliaryImageElement!); } _auxiliaryImageElement!.setAttribute('role', 'img'); _setLabel(_auxiliaryImageElement); } else if (semanticsObject.isVisualOnly) { - semanticsObject.setAriaRole('img'); - _setLabel(semanticsObject.element); + setAriaRole('img'); + _setLabel(element); _cleanUpAuxiliaryElement(); } else { _cleanUpAuxiliaryElement(); @@ -78,7 +78,7 @@ class ImageRoleManager extends PrimaryRoleManager { } void _cleanupElement() { - semanticsObject.element.removeAttribute('aria-label'); + removeAttribute('aria-label'); } @override diff --git a/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/lib/web_ui/lib/src/engine/semantics/incrementable.dart index f1de98d026982..7ad693b628584 100644 --- a/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -29,7 +29,7 @@ class Incrementable extends PrimaryRoleManager { addRouteName(); addLabelAndValue(); - semanticsObject.element.append(_element); + append(_element); _element.type = 'range'; _element.setAttribute('role', 'slider'); diff --git a/lib/web_ui/lib/src/engine/semantics/label_and_value.dart b/lib/web_ui/lib/src/engine/semantics/label_and_value.dart index 23421e13590d1..3a5f7300734fb 100644 --- a/lib/web_ui/lib/src/engine/semantics/label_and_value.dart +++ b/lib/web_ui/lib/src/engine/semantics/label_and_value.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import '../dom.dart'; import 'semantics.dart'; /// Renders [SemanticsObject.label] and/or [SemanticsObject.value] to the semantics DOM. @@ -26,8 +25,8 @@ import 'semantics.dart'; /// This role manager does not manage images and text fields. See /// [ImageRoleManager] and [TextField]. class LabelAndValue extends RoleManager { - LabelAndValue(SemanticsObject semanticsObject) - : super(Role.labelAndValue, semanticsObject); + LabelAndValue(SemanticsObject semanticsObject, PrimaryRoleManager owner) + : super(Role.labelAndValue, semanticsObject, owner); @override void update() { @@ -62,12 +61,11 @@ class LabelAndValue extends RoleManager { combinedValue.write(semanticsObject.value); } - semanticsObject.element - .setAttribute('aria-label', combinedValue.toString()); + owner.setAttribute('aria-label', combinedValue.toString()); } void _cleanUpDom() { - semanticsObject.element.removeAttribute('aria-label'); + owner.removeAttribute('aria-label'); } @override diff --git a/lib/web_ui/lib/src/engine/semantics/link.dart b/lib/web_ui/lib/src/engine/semantics/link.dart new file mode 100644 index 0000000000000..00dcdfcad54c5 --- /dev/null +++ b/lib/web_ui/lib/src/engine/semantics/link.dart @@ -0,0 +1,21 @@ +// 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. + +import '../dom.dart'; +import '../semantics.dart'; + +/// Provides accessibility for links. +class Link extends PrimaryRoleManager { + Link(SemanticsObject semanticsObject) : super.withBasics(PrimaryRole.link, semanticsObject); + + @override + DomElement createElement() { + final DomElement element = domDocument.createElement('a'); + // TODO(chunhtai): Fill in the real link once the framework sends entire uri. + // https://github.com/flutter/flutter/issues/102535. + element.setAttribute('href', '#'); + element.style.display = 'block'; + return element; + } +} diff --git a/lib/web_ui/lib/src/engine/semantics/live_region.dart b/lib/web_ui/lib/src/engine/semantics/live_region.dart index c922cddd717b7..cea9c997e1d63 100644 --- a/lib/web_ui/lib/src/engine/semantics/live_region.dart +++ b/lib/web_ui/lib/src/engine/semantics/live_region.dart @@ -15,8 +15,8 @@ import 'semantics.dart'; /// label of the element. See [LabelAndValue]. If there is no label provided /// no content will be read. class LiveRegion extends RoleManager { - LiveRegion(SemanticsObject semanticsObject) - : super(Role.liveRegion, semanticsObject); + LiveRegion(SemanticsObject semanticsObject, PrimaryRoleManager owner) + : super(Role.liveRegion, semanticsObject, owner); String? _lastAnnouncement; diff --git a/lib/web_ui/lib/src/engine/semantics/platform_view.dart b/lib/web_ui/lib/src/engine/semantics/platform_view.dart index 80321fc77e03e..7502694390dc3 100644 --- a/lib/web_ui/lib/src/engine/semantics/platform_view.dart +++ b/lib/web_ui/lib/src/engine/semantics/platform_view.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import '../dom.dart'; import '../platform_views/slots.dart'; import 'semantics.dart'; @@ -30,13 +29,13 @@ class PlatformViewRoleManager extends PrimaryRoleManager { if (semanticsObject.isPlatformView) { if (semanticsObject.isPlatformViewIdDirty) { - semanticsObject.element.setAttribute( + setAttribute( 'aria-owns', getPlatformViewDomId(semanticsObject.platformViewId), ); } } else { - semanticsObject.element.removeAttribute('aria-owns'); + removeAttribute('aria-owns'); } } } diff --git a/lib/web_ui/lib/src/engine/semantics/scrollable.dart b/lib/web_ui/lib/src/engine/semantics/scrollable.dart index 82b9308816ab3..61f912304e1c0 100644 --- a/lib/web_ui/lib/src/engine/semantics/scrollable.dart +++ b/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -30,7 +30,7 @@ class Scrollable extends PrimaryRoleManager { ..transformOrigin = '0 0 0' // Ignore pointer events since this is a dummy element. ..pointerEvents = 'none'; - semanticsObject.element.append(_scrollOverflowElement); + append(_scrollOverflowElement); } /// Disables browser-driven scrolling in the presence of pointer events. @@ -112,7 +112,7 @@ class Scrollable extends PrimaryRoleManager { // This is effective only in Chrome. Safari does not implement this // CSS property. In Safari the `PointerBinding` uses `preventDefault` // to prevent browser scrolling. - semanticsObject.element.style.touchAction = 'none'; + element.style.touchAction = 'none'; _gestureModeDidChange(); // Memoize the tear-off because Dart does not guarantee that two @@ -126,17 +126,17 @@ class Scrollable extends PrimaryRoleManager { _scrollListener = createDomEventListener((_) { _recomputeScrollPosition(); }); - semanticsObject.element.addEventListener('scroll', _scrollListener); + addEventListener('scroll', _scrollListener); } } /// The value of "scrollTop" or "scrollLeft", depending on the scroll axis. int get _domScrollPosition { if (semanticsObject.isVerticalScrollContainer) { - return semanticsObject.element.scrollTop.toInt(); + return element.scrollTop.toInt(); } else { assert(semanticsObject.isHorizontalScrollContainer); - return semanticsObject.element.scrollLeft.toInt(); + return element.scrollLeft.toInt(); } } @@ -153,7 +153,6 @@ class Scrollable extends PrimaryRoleManager { void _neutralizeDomScrollPosition() { // This value is arbitrary. const int canonicalNeutralScrollPosition = 10; - final DomElement element = semanticsObject.element; final ui.Rect? rect = semanticsObject.rect; if (rect == null) { printWarning('Warning! the rect attribute of semanticsObject is null'); @@ -197,7 +196,6 @@ class Scrollable extends PrimaryRoleManager { } void _gestureModeDidChange() { - final DomElement element = semanticsObject.element; switch (semanticsObject.owner.gestureMode) { case GestureMode.browserGestures: // overflow:scroll will cause the browser report "scroll" events when @@ -227,13 +225,13 @@ class Scrollable extends PrimaryRoleManager { @override void dispose() { super.dispose(); - final DomCSSStyleDeclaration style = semanticsObject.element.style; + final DomCSSStyleDeclaration style = element.style; assert(_gestureModeListener != null); style.removeProperty('overflowY'); style.removeProperty('overflowX'); style.removeProperty('touch-action'); if (_scrollListener != null) { - semanticsObject.element.removeEventListener('scroll', _scrollListener); + removeEventListener('scroll', _scrollListener); } semanticsObject.owner.removeGestureModeListener(_gestureModeListener); _gestureModeListener = null; diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 5fa474c8b2b4c..8a6ec7c0d35eb 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -24,6 +24,7 @@ import 'focusable.dart'; import 'image.dart'; import 'incrementable.dart'; import 'label_and_value.dart'; +import 'link.dart'; import 'live_region.dart'; import 'platform_view.dart'; import 'scrollable.dart'; @@ -382,6 +383,9 @@ enum PrimaryRole { /// /// Provides a label or a value. generic, + + /// Contains a link. + link, } /// Identifies one of the secondary [RoleManager]s of a [PrimaryRoleManager]. @@ -437,6 +441,8 @@ abstract class PrimaryRoleManager { /// management intereferes with the widget's functionality. PrimaryRoleManager.blank(this.role, this.semanticsObject); + late final DomElement element = _initElement(createElement(), semanticsObject); + /// The primary role identifier. final PrimaryRole role; @@ -453,29 +459,82 @@ abstract class PrimaryRoleManager { @visibleForTesting List get debugSecondaryRoles => _secondaryRoleManagers?.map((RoleManager manager) => manager.role).toList() ?? const []; + @protected + DomElement createElement() => domDocument.createElement('flt-semantics'); + + static DomElement _initElement(DomElement element, SemanticsObject semanticsObject) { + // DOM nodes created for semantics objects are positioned absolutely using + // transforms. + element.style.position = 'absolute'; + element.setAttribute('id', 'flt-semantic-node-${semanticsObject.id}'); + + // The root node has some properties that other nodes do not. + if (semanticsObject.id == 0 && !configuration.debugShowSemanticsNodes) { + // Make all semantics transparent. Use `filter` instead of `opacity` + // attribute because `filter` is stronger. `opacity` does not apply to + // some elements, particularly on iOS, such as the slider thumb and track. + // + // Use transparency instead of "visibility:hidden" or "display:none" + // so that a screen reader does not ignore these elements. + element.style.filter = 'opacity(0%)'; + + // Make text explicitly transparent to signal to the browser that no + // rasterization needs to be done. + element.style.color = 'rgba(0,0,0,0)'; + } + + // Make semantic elements visible for debugging by outlining them using a + // green border. Do not use `border` attribute because it affects layout + // (`outline` does not). + if (configuration.debugShowSemanticsNodes) { + element.style.outline = '1px solid green'; + } + return element; + } + + /// Sets the `role` ARIA attribute. + void setAriaRole(String ariaRoleName) { + setAttribute('role', ariaRoleName); + } + + /// Sets the `role` ARIA attribute. + void setAttribute(String name, Object value) { + element.setAttribute(name, value); + } + + void append(DomElement child) { + element.append(child); + } + + void removeAttribute(String name) => element.removeAttribute(name); + + void addEventListener(String type, DomEventListener? listener, [bool? useCapture]) => element.addEventListener(type, listener, useCapture); + + void removeEventListener(String type, DomEventListener? listener, [bool? useCapture]) => element.removeEventListener(type, listener, useCapture); + /// Adds generic focus management features. void addFocusManagement() { - addSecondaryRole(Focusable(semanticsObject)); + addSecondaryRole(Focusable(semanticsObject, this)); } /// Adds generic live region features. void addLiveRegion() { - addSecondaryRole(LiveRegion(semanticsObject)); + addSecondaryRole(LiveRegion(semanticsObject, this)); } /// Adds generic route name features. void addRouteName() { - addSecondaryRole(RouteName(semanticsObject)); + addSecondaryRole(RouteName(semanticsObject, this)); } /// Adds generic label features. void addLabelAndValue() { - addSecondaryRole(LabelAndValue(semanticsObject)); + addSecondaryRole(LabelAndValue(semanticsObject, this)); } /// Adds generic functionality for handling taps and clicks. void addTappable() { - addSecondaryRole(Tappable(semanticsObject)); + addSecondaryRole(Tappable(semanticsObject, this)); } /// Adds a secondary role to this primary role manager. @@ -525,7 +584,7 @@ abstract class PrimaryRoleManager { /// gesture mode changes. @mustCallSuper void dispose() { - semanticsObject.element.removeAttribute('role'); + removeAttribute('role'); _isDisposed = true; } } @@ -566,11 +625,11 @@ final class GenericRole extends PrimaryRoleManager { // Flutter renders into canvas, so the focus ring looks wrong. // - Read out the same label multiple times. if (semanticsObject.hasChildren) { - semanticsObject.setAriaRole('group'); + setAriaRole('group'); } else if (semanticsObject.hasFlag(ui.SemanticsFlag.isHeader)) { - semanticsObject.setAriaRole('heading'); + setAriaRole('heading'); } else { - semanticsObject.setAriaRole('text'); + setAriaRole('text'); } } } @@ -588,7 +647,7 @@ abstract class RoleManager { /// Initializes a secondary role for [semanticsObject]. /// /// A single role object manages exactly one [SemanticsObject]. - RoleManager(this.role, this.semanticsObject); + RoleManager(this.role, this.semanticsObject, this.owner); /// Role identifier. final Role role; @@ -596,6 +655,8 @@ abstract class RoleManager { /// The semantics object managed by this role. final SemanticsObject semanticsObject; + final PrimaryRoleManager owner; + /// Called immediately after the [semanticsObject] updates some of its fields. /// /// A concrete implementation of this method would typically use some of the @@ -627,34 +688,7 @@ abstract class RoleManager { /// information to the browser. class SemanticsObject { /// Creates a semantics tree node with the given [id] and [owner]. - SemanticsObject(this.id, this.owner) { - // DOM nodes created for semantics objects are positioned absolutely using - // transforms. - element.style.position = 'absolute'; - element.setAttribute('id', 'flt-semantic-node-$id'); - - // The root node has some properties that other nodes do not. - if (id == 0 && !configuration.debugShowSemanticsNodes) { - // Make all semantics transparent. Use `filter` instead of `opacity` - // attribute because `filter` is stronger. `opacity` does not apply to - // some elements, particularly on iOS, such as the slider thumb and track. - // - // Use transparency instead of "visibility:hidden" or "display:none" - // so that a screen reader does not ignore these elements. - element.style.filter = 'opacity(0%)'; - - // Make text explicitly transparent to signal to the browser that no - // rasterization needs to be done. - element.style.color = 'rgba(0,0,0,0)'; - } - - // Make semantic elements visible for debugging by outlining them using a - // green border. Do not use `border` attribute because it affects layout - // (`outline` does not). - if (configuration.debugShowSemanticsNodes) { - element.style.outline = '1px solid green'; - } - } + SemanticsObject(this.id, this.owner); /// See [ui.SemanticsUpdateBuilder.updateNode]. int get flags => _flags; @@ -981,9 +1015,6 @@ class SemanticsObject { /// Controls the semantics tree that this node participates in. final EngineSemanticsOwner owner; - /// The DOM element used to convey semantics information to the browser. - final DomElement element = domDocument.createElement('flt-semantics'); - /// Bitfield showing which fields have been updated but have not yet been /// applied to the DOM. /// @@ -996,6 +1027,9 @@ class SemanticsObject { /// Whether the field corresponding to the [fieldIndex] has been updated. bool _isDirty(int fieldIndex) => (_dirtyFields & fieldIndex) != 0; + /// The dom element of this semantics object. + DomElement get element => primaryRole!.element; + /// Returns the HTML element that contains the HTML elements of direct /// children of this object. /// @@ -1079,6 +1113,9 @@ class SemanticsObject { /// Whether this object represents an editable text field. bool get isTextField => hasFlag(ui.SemanticsFlag.isTextField); + /// Whether this object represents an editable text field. + bool get isLink => hasFlag(ui.SemanticsFlag.isLink); + /// Whether this object needs screen readers attention right away. bool get isLiveRegion => hasFlag(ui.SemanticsFlag.isLiveRegion) && @@ -1456,11 +1493,6 @@ class SemanticsObject { _currentChildrenInRenderOrder = childrenInRenderOrder; } - /// Sets the `role` ARIA attribute. - void setAriaRole(String ariaRoleName) { - element.setAttribute('role', ariaRoleName); - } - /// The primary role of this node. /// /// The primary role is assigned by [updateSelf] based on the combination of @@ -1485,6 +1517,8 @@ class SemanticsObject { return PrimaryRole.scrollable; } else if (scopesRoute) { return PrimaryRole.dialog; + } else if (isLink) { + return PrimaryRole.link; } else { return PrimaryRole.generic; } @@ -1500,6 +1534,7 @@ class SemanticsObject { PrimaryRole.dialog => Dialog(this), PrimaryRole.image => ImageRoleManager(this), PrimaryRole.platformView => PlatformViewRoleManager(this), + PrimaryRole.link => Link(this), PrimaryRole.generic => GenericRole(this), }; } @@ -1509,6 +1544,7 @@ class SemanticsObject { void _updateRoles() { PrimaryRoleManager? currentPrimaryRole = primaryRole; final PrimaryRole roleId = _getPrimaryRoleIdentifier(); + final DomElement? previousElement = primaryRole?.element; if (currentPrimaryRole != null) { if (currentPrimaryRole.role == roleId) { @@ -1535,6 +1571,19 @@ class SemanticsObject { primaryRole = currentPrimaryRole; currentPrimaryRole.update(); } + + // Reparent element. + if (previousElement != element) { + final DomElement? container = _childContainerElement; + if (container != null) { + element.append(container); + } + final DomElement? parent = previousElement?.parent; + if (parent != null) { + parent.insertBefore(element, previousElement); + previousElement!.remove(); + } + } } /// Whether the object represents an UI element with "increase" or "decrease" @@ -1980,9 +2029,8 @@ class EngineSemanticsOwner { } /// Receives DOM events from the pointer event system to correlate with the - /// semantics events. - /// - /// Returns true if the event should be forwarded to the framework. + /// semantics events; returns true if the event should be forwarded to the + /// framework. /// /// The browser sends us both raw pointer events and gestures from /// [SemanticsObject.element]s. There could be three possibilities: diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index 1bbf179c06ff5..cba0fafeb7650 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -2,13 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../dom.dart'; +import '../platform_dispatcher.dart'; +import 'semantics.dart'; + /// Sets the "button" ARIA role. class Button extends PrimaryRoleManager { Button(SemanticsObject semanticsObject) : super.withBasics(PrimaryRole.button, semanticsObject) { - semanticsObject.setAriaRole('button'); + setAriaRole('button'); } @override @@ -16,9 +19,9 @@ class Button extends PrimaryRoleManager { super.update(); if (semanticsObject.enabledState() == EnabledState.disabled) { - semanticsObject.element.setAttribute('aria-disabled', 'true'); + setAttribute('aria-disabled', 'true'); } else { - semanticsObject.element.removeAttribute('aria-disabled'); + removeAttribute('aria-disabled'); } } } @@ -30,45 +33,43 @@ class Button extends PrimaryRoleManager { /// the browser may not send us pointer events. In that mode we forward HTML /// click as [ui.SemanticsAction.tap]. class Tappable extends RoleManager { - Tappable(SemanticsObject semanticsObject) : super(Role.tappable, semanticsObject) { - _clickListener = createDomEventListener((DomEvent click) { - PointerBinding.instance!.clickDebouncer.onClick( - click, - semanticsObject.id, - _isListening, - ); - }); - semanticsObject.element.addEventListener('click', _clickListener); - } + Tappable(SemanticsObject semanticsObject, PrimaryRoleManager owner) + : super(Role.tappable, semanticsObject, owner); DomEventListener? _clickListener; - bool _isListening = false; @override void update() { - final bool wasListening = _isListening; - _isListening = semanticsObject.enabledState() != EnabledState.disabled && semanticsObject.isTappable; - if (wasListening != _isListening) { - _updateAttribute(); + if (_clickListener == null) { + _clickListener = createDomEventListener((DomEvent event) { + // Stop dom from reacting since it will be handled entirely on the + // flutter side. + event.preventDefault(); + if (!semanticsObject.isTappable || semanticsObject.enabledState() == EnabledState.disabled) { + return; + } + if (semanticsObject.owner.gestureMode != GestureMode.browserGestures) { + return; + } + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( + semanticsObject.id, ui.SemanticsAction.tap, null); + }); + owner.addEventListener('click', _clickListener); } } - void _updateAttribute() { - // The `flt-tappable` attribute marks the element for the ClickDebouncer to - // to know that it should debounce click events on this element. The - // contract is that the element that has this attribute is also the element - // that receives pointer and "click" events. - if (_isListening) { - semanticsObject.element.setAttribute('flt-tappable', ''); - } else { - semanticsObject.element.removeAttribute('flt-tappable'); + void _stopListening() { + if (_clickListener == null) { + return; } + + owner.removeEventListener('click', _clickListener); + _clickListener = null; } @override void dispose() { - semanticsObject.element.removeEventListener('click', _clickListener); - _clickListener = null; super.dispose(); + _stopListening(); } } diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 9d6a88a9ccb46..243e09cab6f8f 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -275,7 +275,7 @@ class TextField extends PrimaryRoleManager { ..left = '0' ..width = '${semanticsObject.rect!.width}px' ..height = '${semanticsObject.rect!.height}px'; - semanticsObject.element.append(activeEditableElement); + append(activeEditableElement); } void _setupDomElement() { @@ -336,22 +336,21 @@ class TextField extends PrimaryRoleManager { return; } - semanticsObject.element - ..setAttribute('role', 'textbox') - ..setAttribute('contenteditable', 'false') - ..setAttribute('tabindex', '0'); + setAttribute('role', 'textbox'); + setAttribute('contenteditable', 'false'); + setAttribute('tabindex', '0'); num? lastPointerDownOffsetX; num? lastPointerDownOffsetY; - semanticsObject.element.addEventListener('pointerdown', + addEventListener('pointerdown', createDomEventListener((DomEvent event) { final DomPointerEvent pointerEvent = event as DomPointerEvent; lastPointerDownOffsetX = pointerEvent.clientX; lastPointerDownOffsetY = pointerEvent.clientY; }), true); - semanticsObject.element.addEventListener('pointerup', + addEventListener('pointerup', createDomEventListener((DomEvent event) { final DomPointerEvent pointerEvent = event as DomPointerEvent; @@ -399,17 +398,17 @@ class TextField extends PrimaryRoleManager { // represent the same text field. It will confuse VoiceOver, so `role` needs to // be assigned and removed, based on whether or not editableElement exists. activeEditableElement.focus(); - semanticsObject.element.removeAttribute('role'); + removeAttribute('role'); activeEditableElement.addEventListener('blur', createDomEventListener((DomEvent event) { - semanticsObject.element.setAttribute('role', 'textbox'); + setAttribute('role', 'textbox'); activeEditableElement.remove(); SemanticsTextEditingStrategy._instance?.deactivate(this); // Focus on semantics element before removing the editable element, so that // the user can continue navigating the page with the assistive technology. - semanticsObject.element.focus(); + element.focus(); editableElement = null; })); } @@ -447,7 +446,7 @@ class TextField extends PrimaryRoleManager { } } - final DomElement element = editableElement ?? semanticsObject.element; + final DomElement element = editableElement ?? this.element; if (semanticsObject.hasLabel) { element.setAttribute( 'aria-label', diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart index 1f767354b8355..57a30d9cc5bbf 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:js_interop'; - import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; @@ -21,7 +19,7 @@ class SkwasmImageDecoder extends BrowserImageDecoder { final int height = frame.codedHeight.toInt(); final SkwasmSurface surface = (renderer as SkwasmRenderer).surface; return SkwasmImage(imageCreateFromTextureSource( - frame as JSAny, + frame, width, height, surface.handle, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart index 8b6c968e9375c..c0cd2e25db51c 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart @@ -5,6 +5,7 @@ @DefaultAsset('skwasm') library skwasm_impl; +import 'dart:_wasm'; import 'dart:ffi'; import 'dart:js_interop'; @@ -39,45 +40,27 @@ external ImageHandle imageCreateFromPixels( int rowByteCount, ); -// We actually want this function to look something like this: -// -// @Native(symbol: 'image_createFromTextureSource', isLeaf: true) -// external ImageHandle imageCreateFromTextureSource( -// JSAny textureSource, -// int width, -// int height, -// SurfaceHandle handle, -// ); -// -// However, this doesn't work because we cannot use extern refs as part of @Native -// annotations currently. For now, we can use JS interop to expose this function -// instead. -extension SkwasmImageExtension on SkwasmInstance { - @JS('wasmExports.image_createFromTextureSource') - external JSNumber imageCreateFromTextureSource( - JSAny textureSource, - JSNumber width, - JSNumber height, - JSNumber surfaceHandle, - ); -} +// We use a wasm import directly here instead of @Native since this uses an externref +// in the function signature. ImageHandle imageCreateFromTextureSource( JSAny frame, int width, int height, SurfaceHandle handle ) => ImageHandle.fromAddress( - skwasmInstance.imageCreateFromTextureSource( - frame, - width.toJS, - height.toJS, - handle.address.toJS, - ).toDartInt + imageCreateFromTextureSourceImpl( + externRefForJSAny(frame), + width.toWasmI32(), + height.toWasmI32(), + handle.address.toWasmI32(), + ).toIntUnsigned() +); +@pragma('wasm:import', 'skwasm.image_createFromTextureSource') +external WasmI32 imageCreateFromTextureSourceImpl( + WasmExternRef? frame, + WasmI32 width, + WasmI32 height, + WasmI32 surfaceHandle, ); @Native(symbol:'image_ref', isLeaf: true) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart index 3063a557aaed1..d162f190dbe70 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:_wasm'; import 'dart:js_interop'; @JS() @@ -17,37 +18,11 @@ extension WebAssemblyMemoryExtension on WebAssemblyMemory { class SkwasmInstance {} extension SkwasmInstanceExtension on SkwasmInstance { - external JSNumber getEmptyTableSlot(); - - // The function here *must* be a directly exported wasm function, not a - // JavaScript function. If you actually need to add a JavaScript function, - // use `addFunction` instead. - external void setWasmTableEntry(JSNumber index, JSAny function); - - external JSNumber addFunction(WebAssemblyFunction function); - external void removeFunction(JSNumber functionPointer); - external WebAssemblyMemory get wasmMemory; } @JS('window._flutter_skwasmInstance') external SkwasmInstance get skwasmInstance; -@JS() -@staticInterop -@anonymous -class WebAssemblyFunctionType { - external factory WebAssemblyFunctionType({ - required JSArray parameters, - required JSArray results, - }); -} - -@JS('WebAssembly.Function') -@staticInterop -class WebAssemblyFunction { - external factory WebAssemblyFunction( - WebAssemblyFunctionType functionType, - JSFunction function - ); -} +@pragma('wasm:import', 'skwasmWrapper.addFunction') +external WasmI32 addFunction(WasmFuncRef function); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index 5b2636d8a8b7f..4cdaf6805a3ed 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -391,7 +391,7 @@ class SkwasmRenderer implements Renderer { } final SkwasmImageDecoder decoder = SkwasmImageDecoder( contentType: contentType, - dataSource: response.body as JSAny, + dataSource: response.body, debugSource: uri.toString(), ); await decoder.initialize(); @@ -452,7 +452,7 @@ class SkwasmRenderer implements Renderer { @override ui.Image createImageFromImageBitmap(DomImageBitmap imageSource) { return SkwasmImage(imageCreateFromTextureSource( - imageSource as JSAny, + imageSource, imageSource.width.toDartInt, imageSource.height.toDartInt, surface.handle, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart index bce7a8a62edf0..34af06d0d1fd7 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:_wasm'; import 'dart:async'; import 'dart:ffi'; import 'dart:js_interop'; @@ -11,7 +12,52 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; +@pragma('wasm:export') +WasmVoid callbackHandler(WasmI32 callbackId, WasmI32 context, WasmExternRef? jsContext) { + // Actually hide this call behind whether skwasm is enabled. Otherwise, the SkwasmCallbackHandler + // won't actually be tree-shaken, and we end up with skwasm imports in non-skwasm builds. + if (FlutterConfiguration.flutterWebUseSkwasm) { + SkwasmCallbackHandler.instance.handleCallback(callbackId, context, jsContext); + } + return WasmVoid(); +} + +// This class handles callbacks coming from Skwasm by keeping a map of callback IDs to Completers +class SkwasmCallbackHandler { + SkwasmCallbackHandler._withCallbackPointer(this.callbackPointer); + + factory SkwasmCallbackHandler._() { + final WasmFuncRef wasmFunction = WasmFunction.fromFunction(callbackHandler); + final int functionIndex = addFunction(wasmFunction).toIntUnsigned(); + return SkwasmCallbackHandler._withCallbackPointer( + OnRenderCallbackHandle.fromAddress(functionIndex) + ); + } + static SkwasmCallbackHandler instance = SkwasmCallbackHandler._(); + final OnRenderCallbackHandle callbackPointer; + final Map> _pendingCallbacks = >{}; + + // Returns a future that will resolve when Skwasm calls back with the given callbackID + Future registerCallback(int callbackId) { + final Completer completer = Completer(); + _pendingCallbacks[callbackId] = completer; + return completer.future; + } + + void handleCallback(WasmI32 callbackId, WasmI32 context, WasmExternRef? jsContext) { + // Skwasm can either callback with a JS object (an externref) or it can call back + // with a simple integer, which usually refers to a pointer on its heap. In order + // to coerce these into a single type, we just make the completers take a JSAny + // that either contains the JS object or a JSNumber that contains the integer value. + final Completer completer = _pendingCallbacks.remove(callbackId.toIntUnsigned())!; + if (!jsContext.isNull) { + completer.complete(jsContext!.toJS); + } else { + completer.complete(context.toIntUnsigned().toJS); + } + } +} class SkwasmSurface { factory SkwasmSurface() { @@ -25,32 +71,16 @@ class SkwasmSurface { SkwasmSurface._fromHandle(this.handle) : threadId = surfaceGetThreadId(handle); final SurfaceHandle handle; - OnRenderCallbackHandle _callbackHandle = nullptr; - final Map> _pendingCallbacks = >{}; final int threadId; void _initialize() { - final WebAssemblyFunction wasmFunction = WebAssemblyFunction( - WebAssemblyFunctionType( - parameters: [ - 'i32'.toJS, - 'i32'.toJS, - 'externref'.toJS - ].toJS, - results: [].toJS - ), - _callbackHandler.toJS, - ); - _callbackHandle = OnRenderCallbackHandle.fromAddress( - skwasmInstance.addFunction(wasmFunction).toDartInt, - ); - surfaceSetCallbackHandler(handle, _callbackHandle); + surfaceSetCallbackHandler(handle, SkwasmCallbackHandler.instance.callbackPointer); } Future renderPicture(SkwasmPicture picture) async { final int callbackId = surfaceRenderPicture(handle, picture.handle); - final DomImageBitmap bitmap = (await _registerCallback(callbackId)) as DomImageBitmap; + final DomImageBitmap bitmap = (await SkwasmCallbackHandler.instance.registerCallback(callbackId)) as DomImageBitmap; return bitmap; } @@ -60,7 +90,7 @@ class SkwasmSurface { image.handle, format.index, ); - final int context = (await _registerCallback(callbackId) as JSNumber).toDartInt; + final int context = (await SkwasmCallbackHandler.instance.registerCallback(callbackId) as JSNumber).toDartInt; final SkDataHandle dataHandle = SkDataHandle.fromAddress(context); final int byteCount = skDataGetSize(dataHandle); final Pointer dataPointer = skDataGetConstPointer(dataHandle).cast(); @@ -72,23 +102,7 @@ class SkwasmSurface { return ByteData.sublistView(output); } - Future _registerCallback(int callbackId) { - final Completer completer = Completer(); - _pendingCallbacks[callbackId] = completer; - return completer.future; - } - - void _callbackHandler(JSNumber callbackId, JSNumber context, JSAny jsContext) { - final Completer completer = _pendingCallbacks.remove(callbackId.toDartInt)!; - if (jsContext.isUndefinedOrNull) { - completer.complete(context); - } else { - completer.complete(jsContext); - } - } - void dispose() { surfaceDestroy(handle); - skwasmInstance.removeFunction(_callbackHandle.address.toJS); } } diff --git a/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart b/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart index fe3874785c607..f527d576bc41b 100644 --- a/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart @@ -648,6 +648,12 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder { return; } + final double? letterSpacing = style.letterSpacing; + if (letterSpacing != null && letterSpacing != 0.0) { + _canDrawOnCanvas = false; + return; + } + final ui.TextDecoration? decoration = style.decoration; if (decoration != null && decoration != ui.TextDecoration.none) { _canDrawOnCanvas = false; diff --git a/lib/web_ui/lib/src/engine/text/paint_service.dart b/lib/web_ui/lib/src/engine/text/paint_service.dart index 8b2084cca6f8f..7c1de48489c73 100644 --- a/lib/web_ui/lib/src/engine/text/paint_service.dart +++ b/lib/web_ui/lib/src/engine/text/paint_service.dart @@ -4,7 +4,6 @@ import 'package:ui/ui.dart' as ui; -import '../dom.dart'; import '../html/bitmap_canvas.dart'; import '../html/painting.dart'; import 'canvas_paragraph.dart'; @@ -78,23 +77,7 @@ class TextPaintService { final EngineTextStyle style = fragment.style; final String text = fragment.getText(paragraph); - final double? letterSpacing = style.letterSpacing; - if (letterSpacing == null || letterSpacing == 0.0) { - canvas.drawText(text, x, y, - style: style.foreground?.style, shadows: style.shadows); - } else { - // TODO(mdebbar): Implement letter-spacing on canvas more efficiently: - // https://github.com/flutter/flutter/issues/51234 - double charX = x; - final int len = text.length; - for (int i = 0; i < len; i++) { - final String char = text[i]; - canvas.drawText(char, charX.roundToDouble(), y, - style: style.foreground?.style, - shadows: style.shadows); - charX += letterSpacing + canvas.measureText(char).width!; - } - } + canvas.drawText(text, x, y, style: style.foreground?.style, shadows: style.shadows); canvas.tearDownPaint(); } diff --git a/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart b/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart index 7ee1cfb732431..392998111c048 100644 --- a/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart +++ b/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart @@ -33,7 +33,7 @@ class BrowserAutofillHints { 'impp': 'impp', 'jobTitle': 'organization-title', 'language': 'language', - 'middleName': 'middleName', + 'middleName': 'additional-name', 'name': 'name', 'namePrefix': 'honorific-prefix', 'nameSuffix': 'honorific-suffix', diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index bc094ea1cb215..3be53ed83cedc 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -12,6 +12,7 @@ import 'package:ui/ui.dart' as ui; import '../browser_detection.dart'; import '../dom.dart'; import '../embedder.dart'; +import '../mouse/prevent_default.dart'; import '../platform_dispatcher.dart'; import '../safe_browser_api.dart'; import '../semantics.dart'; @@ -210,9 +211,7 @@ class EngineAutofillForm { formElement.noValidate = true; formElement.method = 'post'; formElement.action = '#'; - formElement.addEventListener('submit', createDomEventListener((DomEvent e) { - e.preventDefault(); - })); + formElement.addEventListener('submit', preventDefaultListener); // We need to explicitly disable pointer events on the form in Safari Desktop, // so that we don't have pointer event collisions if users hover over or click diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index bd4ad5b0ef333..d6581459b60c2 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -14,6 +14,7 @@ import 'package:ui/ui.dart' as ui; import 'browser_detection.dart'; import 'dom.dart'; import 'safe_browser_api.dart'; +import 'services.dart'; import 'vector_math.dart'; /// Generic callback signature, used by [_futurize]. @@ -627,6 +628,27 @@ extension JsonExtensions on Map { } } +/// Extracts view ID from the [MethodCall.arguments] map. +/// +/// Throws if the view ID is not present or if [arguments] is not a map. +int readViewId(Object? arguments) { + final int? viewId = tryViewId(arguments); + if (viewId == null) { + throw Exception('Could not find a `viewId` in the arguments: $arguments'); + } + return viewId; +} + +/// Extracts view ID from the [MethodCall.arguments] map. +/// +/// Returns null if the view ID is not present or if [arguments] is not a map. +int? tryViewId(Object? arguments) { + if (arguments is Map) { + return arguments.tryInt('viewId'); + } + return null; +} + /// Prints a list of bytes in hex format. /// /// Bytes are separated by one space and are padded on the left to always show diff --git a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart index 572bdb8ce33b2..94a211b23a21c 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart @@ -4,13 +4,14 @@ import 'package:ui/src/engine/dom.dart'; +import '../hot_restart_cache_handler.dart' show registerElementForCleanup; import 'embedding_strategy.dart'; /// An [EmbeddingStrategy] that renders flutter inside a target host element. /// /// This strategy attempts to minimize DOM modifications outside of the host /// element, so it plays "nice" with other web frameworks. -class CustomElementEmbeddingStrategy extends EmbeddingStrategy { +class CustomElementEmbeddingStrategy implements EmbeddingStrategy { /// Creates a [CustomElementEmbeddingStrategy] to embed a Flutter view into [_hostElement]. CustomElementEmbeddingStrategy(this._hostElement) { _hostElement.clearChildren(); @@ -51,12 +52,6 @@ class CustomElementEmbeddingStrategy extends EmbeddingStrategy { registerElementForCleanup(resourceHost); } - @override - void disableContextMenu() => disableContextMenuOn(_hostElement); - - @override - void enableContextMenu() => enableContextMenuOn(_hostElement); - void _setHostAttribute(String name, String value) { _hostElement.setAttribute(name, value); } diff --git a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart index 8a3e401816bf9..f9f358be21518 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:meta/meta.dart'; - import 'package:ui/src/engine/dom.dart'; -import 'package:ui/src/engine/view_embedder/hot_restart_cache_handler.dart'; import 'custom_element_embedding_strategy.dart'; import 'full_page_embedding_strategy.dart'; @@ -20,15 +17,7 @@ import 'full_page_embedding_strategy.dart'; /// * [CustomElementEmbeddingStrategy] - Flutter is rendered inside a custom host /// element, provided by the web app programmer through the engine /// initialization. -abstract class EmbeddingStrategy with _ContextMenu { - EmbeddingStrategy() { - // Initialize code to handle hot-restart (debug only). - assert(() { - _hotRestartCache = HotRestartCacheHandler(); - return true; - }()); - } - +abstract class EmbeddingStrategy { factory EmbeddingStrategy.create({DomElement? hostElement}) { if (hostElement != null) { return CustomElementEmbeddingStrategy(hostElement); @@ -37,9 +26,6 @@ abstract class EmbeddingStrategy with _ContextMenu { } } - /// Keeps a list of elements to be cleaned up at hot-restart. - HotRestartCacheHandler? _hotRestartCache; - void initialize({ Map? hostElementAttributes, }); @@ -49,78 +35,4 @@ abstract class EmbeddingStrategy with _ContextMenu { /// Attaches the resourceHost element into the hostElement. void attachResourcesHost(DomElement resourceHost, {DomElement? nextTo}); - - /// Registers a [DomElement] to be cleaned up after hot restart. - @mustCallSuper - void registerElementForCleanup(DomElement element) { - _hotRestartCache?.registerElement(element); - } -} - -/// Provides functionality to disable and enable the browser's context menu. -mixin _ContextMenu { - /// False when the context menu has been disabled, otherwise true. - bool _contextMenuEnabled = true; - - /// Listener for contextmenu events that prevents the browser's context menu - /// from being shown. - final DomEventListener _disablingContextMenuListener = createDomEventListener((DomEvent event) { - event.preventDefault(); - }); - - /// Disables the browser's context menu for this part of the DOM. - /// - /// By default, when a Flutter web app starts, the context menu is enabled. - /// - /// Can be re-enabled by calling [enableContextMenu]. - /// - /// See also: - /// - /// * [disableContextMenuOn], which is like this but takes the relevant - /// [DomElement] as a parameter. - void disableContextMenu(); - - /// Disables the browser's context menu for the given [DomElement]. - /// - /// See also: - /// - /// * [disableContextMenu], which is like this but is not passed a - /// [DomElement]. - @protected - void disableContextMenuOn(DomEventTarget element) { - if (!_contextMenuEnabled) { - return; - } - - element.addEventListener('contextmenu', _disablingContextMenuListener); - _contextMenuEnabled = false; - } - - /// Enables the browser's context menu for this part of the DOM. - /// - /// By default, when a Flutter web app starts, the context menu is already - /// enabled. Typically, this method would be used after calling - /// [disableContextMenu] to first disable it. - /// - /// See also: - /// - /// * [enableContextMenuOn], which is like this but takes the relevant - /// [DomElement] as a parameter. - void enableContextMenu(); - - /// Enables the browser's context menu for the given [DomElement]. - /// - /// See also: - /// - /// * [enableContextMenu], which is like this but is not passed a - /// [DomElement]. - @protected - void enableContextMenuOn(DomEventTarget element) { - if (_contextMenuEnabled) { - return; - } - - element.removeEventListener('contextmenu', _disablingContextMenuListener); - _contextMenuEnabled = true; - } } diff --git a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart index d8db4f82f2ac1..fdb7028fe991a 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart @@ -5,13 +5,14 @@ import 'package:ui/src/engine/dom.dart'; import 'package:ui/src/engine/util.dart' show setElementStyle; +import '../hot_restart_cache_handler.dart' show registerElementForCleanup; import 'embedding_strategy.dart'; /// An [EmbeddingStrategy] that takes over the whole web page. /// /// This strategy takes over the element, modifies the viewport meta-tag, /// and ensures that the root Flutter view covers the whole screen. -class FullPageEmbeddingStrategy extends EmbeddingStrategy { +class FullPageEmbeddingStrategy implements EmbeddingStrategy { @override void initialize({ Map? hostElementAttributes, @@ -48,12 +49,6 @@ class FullPageEmbeddingStrategy extends EmbeddingStrategy { registerElementForCleanup(resourceHost); } - @override - void disableContextMenu() => disableContextMenuOn(domWindow); - - @override - void enableContextMenu() => enableContextMenuOn(domWindow); - void _setHostAttribute(String name, String value) { domDocument.body!.setAttribute(name, value); } diff --git a/lib/web_ui/lib/src/engine/view_embedder/hot_restart_cache_handler.dart b/lib/web_ui/lib/src/engine/view_embedder/hot_restart_cache_handler.dart index c37aff2504c4d..1751743280ed5 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/hot_restart_cache_handler.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/hot_restart_cache_handler.dart @@ -4,6 +4,7 @@ import 'dart:js_interop'; +import 'package:meta/meta.dart'; import 'package:ui/src/engine.dart'; import '../dom.dart'; @@ -12,59 +13,61 @@ import '../dom.dart'; /// to clear. Delay removal of old visible state to make the /// transition appear smooth. @JS('window.__flutterState') -external JSArray? get _hotRestartStore; -List? get hotRestartStore => - _hotRestartStore?.toObjectShallow as List?; +external JSArray? get _jsHotRestartStore; @JS('window.__flutterState') -external set _hotRestartStore(JSArray? nodes); -set hotRestartStore(List? nodes) => - _hotRestartStore = nodes?.toJSAnyShallow as JSArray?; +external set _jsHotRestartStore(JSArray? nodes); /// Handles [DomElement]s that need to be removed after a hot-restart. /// -/// Elements are stored in an [_elements] list, backed by a global JS variable, -/// named [defaultCacheName]. +/// This class shouldn't be used directly. It's only made public for testing +/// purposes. Instead, use [registerElementForCleanup]. +/// +/// Elements are stored in a [JSArray] stored globally at `window.__flutterState`. /// /// When the app hot-restarts (and a new instance of this class is created), -/// everything in [_elements] is removed from the DOM. +/// all elements in the global [JSArray] is removed from the DOM. class HotRestartCacheHandler { + @visibleForTesting HotRestartCacheHandler() { - if (_elements.isNotEmpty) { - // We are in a post hot-restart world, clear the elements now. - _clearAllElements(); - } + _resetHotRestartStore(); } - /// The js-interop layer backing [_elements]. - /// - /// Elements are stored in a JS global array named [defaultCacheName]. - late List? _jsElements; - - /// The elements that need to be cleaned up after hot-restart. - List get _elements { - _jsElements = hotRestartStore; - if (_jsElements == null) { - _jsElements = []; - hotRestartStore = _jsElements; - } - return _jsElements!; - } + /// Removes every element that was registered prior to the hot-restart from + /// the DOM. + void _resetHotRestartStore() { + final JSArray? jsStore = _jsHotRestartStore; - /// Removes every element from [_elements] and empties the list. - void _clearAllElements() { - for (final Object? element in _elements) { - if (element is DomElement) { - element.remove(); + if (jsStore != null) { + // We are in a post hot-restart world, clear the elements now. + final List store = jsStore.toObjectShallow as List; + for (final Object? element in store) { + if (element != null) { + (element as DomElement).remove(); + } } } - hotRestartStore = []; + _jsHotRestartStore = JSArray(); } /// Registers a [DomElement] to be removed after hot-restart. + @visibleForTesting void registerElement(DomElement element) { - final List elements = _elements; - elements.add(element); - hotRestartStore = elements; + _jsHotRestartStore!.push(element); } } + +final HotRestartCacheHandler? _hotRestartCache = () { + // In release mode, we don't need a hot restart cache, so we leave it null. + HotRestartCacheHandler? cache; + assert(() { + cache = HotRestartCacheHandler(); + return true; + }()); + return cache; +}(); + +/// Registers a [DomElement] to be cleaned up after hot restart. +void registerElementForCleanup(DomElement element) { + _hotRestartCache?.registerElement(element); +} diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 47a1215fd1f9b..55b24574b2674 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -16,8 +16,12 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../engine.dart' show DimensionsProvider, registerHotRestartListener, renderer; import 'display.dart'; import 'dom.dart'; +import 'embedder.dart'; +import 'mouse/context_menu.dart'; +import 'mouse/cursor.dart'; import 'navigation/history.dart'; import 'platform_dispatcher.dart'; +import 'platform_views/message_handler.dart'; import 'services.dart'; import 'util.dart'; @@ -29,8 +33,19 @@ const bool debugPrintPlatformMessages = false; /// The view ID for the implicit flutter view provided by the platform. const int kImplicitViewId = 0; +/// Represents all views in the Flutter Web Engine. +/// +/// In addition to everything defined in [ui.FlutterView], this class adds +/// a few web-specific properties. +abstract interface class EngineFlutterView extends ui.FlutterView { + ContextMenu get contextMenu; + MouseCursor get mouseCursor; + PlatformViewMessageHandler get platformViewMessageHandler; + DomElement get rootElement; +} + /// The Web implementation of [ui.SingletonFlutterWindow]. -class EngineFlutterWindow extends ui.SingletonFlutterWindow { +class EngineFlutterWindow extends ui.SingletonFlutterWindow implements EngineFlutterView { EngineFlutterWindow(this.viewId, this.platformDispatcher) { platformDispatcher.viewData[viewId] = this; platformDispatcher.windowConfigurations[viewId] = const ViewConfiguration(); @@ -53,6 +68,19 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow { @override final EnginePlatformDispatcher platformDispatcher; + @override + late final MouseCursor mouseCursor = MouseCursor(rootElement); + + @override + late final ContextMenu contextMenu = ContextMenu(rootElement); + + @override + DomElement get rootElement => flutterViewEmbedder.flutterViewElement; + + @override + late final PlatformViewMessageHandler platformViewMessageHandler = + PlatformViewMessageHandler(platformViewsContainer: flutterViewEmbedder.glassPaneElement); + /// Handles the browser history integration to allow users to use the back /// button, etc. BrowserHistory get browserHistory { diff --git a/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart b/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart index e33e8ebc53199..fc17cfb1ee3d3 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart @@ -42,7 +42,7 @@ class PlatformViewRegistry { Function viewFactory, { bool isVisible = true, }) { - return platformViewManager.registerFactory( + return PlatformViewManager.instance.registerFactory( viewType, viewFactory, isVisible: isVisible, @@ -53,6 +53,6 @@ class PlatformViewRegistry { /// /// Throws if no view has been created for [viewId]. Object getViewById(int viewId) { - return platformViewManager.getViewById(viewId); + return PlatformViewManager.instance.getViewById(viewId); } } diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index b15d7862b331c..1b53ead9bd9c8 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none # Keep the SDK version range in sync with pubspecs under web_sdk environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: js: 0.6.4 @@ -18,34 +18,34 @@ dependencies: path: ../../third_party/web_test_fonts dev_dependencies: - archive: 3.1.2 + archive: 3.4.2 args: any async: any convert: any crypto: any - html: 0.15.0 - http: 0.13.5 + html: 0.15.4 + http: 1.1.0 http_multi_server: any image: 3.0.1 - matcher: 0.12.14 + matcher: 0.12.16 package_config: any path: 1.8.0 pool: any - quiver: 3.0.0 + quiver: 3.2.1 shelf: any shelf_packages_handler: any shelf_static: any shelf_web_socket: any stack_trace: any stream_channel: 2.1.1 - test: 1.22.1 + test: 1.24.8 test_api: any test_core: any typed_data: any - uuid: 3.0.6 + uuid: 4.1.0 watcher: 1.1.0 web_socket_channel: any - webdriver: 3.0.1 + webdriver: 3.0.3 webkit_inspection_protocol: any yaml: 3.0.0 web_test_utils: diff --git a/lib/web_ui/skwasm/contour_measure.cpp b/lib/web_ui/skwasm/contour_measure.cpp index 1e732420e0e55..4af486ffe3d3a 100644 --- a/lib/web_ui/skwasm/contour_measure.cpp +++ b/lib/web_ui/skwasm/contour_measure.cpp @@ -6,6 +6,7 @@ #include "helpers.h" #include "third_party/skia/include/core/SkContourMeasure.h" +#include "third_party/skia/include/core/SkPath.h" using namespace Skwasm; diff --git a/lib/web_ui/skwasm/image.cpp b/lib/web_ui/skwasm/image.cpp index 74df647625600..def216398afa5 100644 --- a/lib/web_ui/skwasm/image.cpp +++ b/lib/web_ui/skwasm/image.cpp @@ -12,6 +12,7 @@ #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/ganesh/GrExternalTextureGenerator.h" @@ -90,7 +91,7 @@ class TextureSourceImageGenerator : public GrExternalTextureGenerator { std::unique_ptr generateExternalTexture( GrRecordingContext* context, - GrMipMapped mipmapped) override { + skgpu::Mipmapped mipmapped) override { GrGLTextureInfo glInfo; glInfo.fID = skwasm_createGlTextureFromTextureSource( _textureSourceWrapper->getTextureSource(), fInfo.width(), @@ -100,6 +101,10 @@ class TextureSourceImageGenerator : public GrExternalTextureGenerator { auto backendTexture = GrBackendTextures::MakeGL( fInfo.width(), fInfo.height(), mipmapped, glInfo); + + // In order to bind the image source to the texture, makeTexture has changed + // which texture is "in focus" for the WebGL context. + GrAsDirectContext(context)->resetContext(kTextureBinding_GrGLBackendState); return std::make_unique( backendTexture, glInfo.fID, emscripten_webgl_get_current_context()); } diff --git a/lib/web_ui/skwasm/library_skwasm_support.js b/lib/web_ui/skwasm/library_skwasm_support.js index d1c3459d10809..5e62614e009c4 100644 --- a/lib/web_ui/skwasm/library_skwasm_support.js +++ b/lib/web_ui/skwasm/library_skwasm_support.js @@ -15,7 +15,7 @@ mergeInto(LibraryManager.library, { skwasmMessage: 'setAssociatedObject', pointer, object, - }); + }, [object]); }; _skwasm_getAssociatedObject = function(pointer) { return associatedObjectsMap.get(pointer); @@ -27,6 +27,9 @@ mergeInto(LibraryManager.library, { return; } switch (skwasmMessage) { + case 'renderPicture': + _surface_renderPictureOnWorker(data.surface, data.picture, data.callbackId); + return; case 'onRenderComplete': _surface_onRenderComplete(data.surface, data.callbackId, data.imageBitmap); return; @@ -34,13 +37,15 @@ mergeInto(LibraryManager.library, { associatedObjectsMap.set(data.pointer, data.object); return; case 'disposeAssociatedObject': - const object = { data }; + const pointer = data.pointer; + const object = associatedObjectsMap.get(pointer); if (object.close) { object.close(); } - associatedObjectsMap.delete(data.pointer); + associatedObjectsMap.delete(pointer); + return; default: - console.warn('unrecognized skwasm message'); + console.warn(`unrecognized skwasm message: ${skwasmMessage}`); } }; if (!threadId) { @@ -49,6 +54,14 @@ mergeInto(LibraryManager.library, { PThread.pthreads[threadId].addEventListener("message", eventListener); } }; + _skwasm_dispatchRenderPicture = function(threadId, surfaceHandle, pictureHandle, callbackId) { + PThread.pthreads[threadId].postMessage({ + skwasmMessage: 'renderPicture', + surface: surfaceHandle, + picture: pictureHandle, + callbackId, + }); + }; _skwasm_createOffscreenCanvas = function(width, height) { const canvas = new OffscreenCanvas(width, height); var contextAttributes = { @@ -80,7 +93,7 @@ mergeInto(LibraryManager.library, { surface: surfaceHandle, callbackId, imageBitmap, - }); + }, [imageBitmap]); }; _skwasm_createGlTextureFromTextureSource = function(textureSource, width, height) { const glCtx = GL.currentContext.GLctx; @@ -97,10 +110,10 @@ mergeInto(LibraryManager.library, { GL.textures[textureId] = newTexture; return textureId; }; - _skwasm_disposeAssociatedObjectOnThread = function(threadId, object) { + _skwasm_disposeAssociatedObjectOnThread = function(threadId, pointer) { PThread.pthreads[threadId].postMessage({ skwasmMessage: 'disposeAssociatedObject', - object, + pointer, }); }; }, @@ -112,6 +125,8 @@ mergeInto(LibraryManager.library, { skwasm_disposeAssociatedObjectOnThread__deps: ['$skwasm_support_setup'], skwasm_registerMessageListener: function() {}, skwasm_registerMessageListener__deps: ['$skwasm_support_setup'], + skwasm_dispatchRenderPicture: function() {}, + skwasm_dispatchRenderPicture__deps: ['$skwasm_support_setup'], skwasm_createOffscreenCanvas: function () {}, skwasm_createOffscreenCanvas__deps: ['$skwasm_support_setup'], skwasm_resizeCanvas: function () {}, diff --git a/lib/web_ui/skwasm/skwasm_support.h b/lib/web_ui/skwasm/skwasm_support.h index 21c790d6507eb..0c9cbaaa845c8 100644 --- a/lib/web_ui/skwasm/skwasm_support.h +++ b/lib/web_ui/skwasm/skwasm_support.h @@ -4,6 +4,7 @@ #include #include +#include "third_party/skia/include/core/SkPicture.h" namespace Skwasm { class Surface; @@ -19,6 +20,10 @@ extern SkwasmObject skwasm_getAssociatedObject(void* pointer); extern void skwasm_disposeAssociatedObjectOnThread(unsigned long threadId, void* pointer); extern void skwasm_registerMessageListener(pthread_t threadId); +extern void skwasm_dispatchRenderPicture(unsigned long threadId, + Skwasm::Surface* surface, + SkPicture* picture, + uint32_t callbackId); extern uint32_t skwasm_createOffscreenCanvas(int width, int height); extern void skwasm_resizeCanvas(uint32_t contextHandle, int width, int height); extern void skwasm_captureImageBitmap(Skwasm::Surface* surfaceHandle, diff --git a/lib/web_ui/skwasm/surface.cpp b/lib/web_ui/skwasm/surface.cpp index f1601bbef976c..28b64bd25a328 100644 --- a/lib/web_ui/skwasm/surface.cpp +++ b/lib/web_ui/skwasm/surface.cpp @@ -8,6 +8,7 @@ #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h" using namespace Skwasm; @@ -42,9 +43,7 @@ uint32_t Surface::renderPicture(SkPicture* picture) { assert(emscripten_is_main_browser_thread()); uint32_t callbackId = ++_currentCallbackId; picture->ref(); - emscripten_dispatch_to_thread(_thread, EM_FUNC_SIG_VIII, - reinterpret_cast(fRenderPicture), - nullptr, this, picture, callbackId); + skwasm_dispatchRenderPicture(_thread, this, picture, callbackId); return callbackId; } @@ -91,7 +90,7 @@ void Surface::_init() { makeCurrent(_glContext); emscripten_webgl_enable_extension(_glContext, "WEBGL_debug_renderer_info"); - _grContext = GrDirectContext::MakeGL(GrGLMakeNativeInterface()); + _grContext = GrDirectContexts::MakeGL(GrGLMakeNativeInterface()); // WebGL should already be clearing the color and stencil buffers, but do it // again here to ensure Skia receives them in the expected state. @@ -137,7 +136,7 @@ void Surface::_recreateSurface() { } // Worker thread only -void Surface::_renderPicture(const SkPicture* picture, uint32_t callbackId) { +void Surface::renderPictureOnWorker(SkPicture* picture, uint32_t callbackId) { SkRect pictureRect = picture->cullRect(); SkIRect roundedOutRect; pictureRect.roundOut(&roundedOutRect); @@ -194,13 +193,6 @@ void Surface::fDispose(Surface* surface) { surface->_dispose(); } -void Surface::fRenderPicture(Surface* surface, - SkPicture* picture, - uint32_t callbackId) { - surface->_renderPicture(picture, callbackId); - picture->unref(); -} - void Surface::fOnRasterizeComplete(Surface* surface, SkData* imageData, uint32_t callbackId) { @@ -238,6 +230,13 @@ SKWASM_EXPORT uint32_t surface_renderPicture(Surface* surface, return surface->renderPicture(picture); } +SKWASM_EXPORT void surface_renderPictureOnWorker(Surface* surface, + SkPicture* picture, + uint32_t callbackId) { + surface->renderPictureOnWorker(picture, callbackId); + picture->unref(); +} + SKWASM_EXPORT uint32_t surface_rasterizeImage(Surface* surface, SkImage* image, ImageByteFormat format) { diff --git a/lib/web_ui/skwasm/surface.h b/lib/web_ui/skwasm/surface.h index c7cd137a00fb5..d99164831e523 100644 --- a/lib/web_ui/skwasm/surface.h +++ b/lib/web_ui/skwasm/surface.h @@ -70,13 +70,15 @@ class Surface { std::unique_ptr createTextureSourceWrapper( SkwasmObject textureSource); + // Worker thread + void renderPictureOnWorker(SkPicture* picture, uint32_t callbackId); + private: void _runWorker(); void _init(); void _dispose(); void _resizeCanvasToFit(int width, int height); void _recreateSurface(); - void _renderPicture(const SkPicture* picture, uint32_t callbackId); void _rasterizeImage(SkImage* image, ImageByteFormat format, uint32_t callbackId); @@ -99,9 +101,6 @@ class Surface { pthread_t _thread; static void fDispose(Surface* surface); - static void fRenderPicture(Surface* surface, - SkPicture* picture, - uint32_t callbackId); static void fOnRenderComplete(Surface* surface, uint32_t callbackId, SkwasmObject imageBitmap); diff --git a/lib/web_ui/test/canvaskit/canvas_golden_test.dart b/lib/web_ui/test/canvaskit/canvas_golden_test.dart index 361e823b6e37e..46a436db87726 100644 --- a/lib/web_ui/test/canvaskit/canvas_golden_test.dart +++ b/lib/web_ui/test/canvaskit/canvas_golden_test.dart @@ -163,7 +163,7 @@ void testMain() { // Regression test for https://github.com/flutter/flutter/issues/121758 test('resources used in temporary surfaces for Image.toByteData can cross to rendering overlays', () async { final Rasterizer rasterizer = CanvasKitRenderer.instance.rasterizer; - SurfaceFactory.instance.debugClear(); + RenderCanvasFactory.instance.debugClear(); ui_web.platformViewRegistry.registerViewFactory( 'test-platform-view', diff --git a/lib/web_ui/test/canvaskit/common.dart b/lib/web_ui/test/canvaskit/common.dart index a8a2b04ce9469..bf0fd59602968 100644 --- a/lib/web_ui/test/canvaskit/common.dart +++ b/lib/web_ui/test/canvaskit/common.dart @@ -24,7 +24,7 @@ void setUpCanvasKitTest() { tearDown(() { HtmlViewEmbedder.instance.debugClear(); - SurfaceFactory.instance.debugClear(); + RenderCanvasFactory.instance.debugClear(); }); setUp(() => diff --git a/lib/web_ui/test/canvaskit/embedded_views_test.dart b/lib/web_ui/test/canvaskit/embedded_views_test.dart index 58b34661fcb4d..40b5060fb5f53 100644 --- a/lib/web_ui/test/canvaskit/embedded_views_test.dart +++ b/lib/web_ui/test/canvaskit/embedded_views_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:js_interop'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -42,10 +41,10 @@ void testMain() { // The platform view is now split in two parts. The contents live // as a child of the glassPane, and the slot lives in the glassPane // shadow root. The slot is the one that has pointer events auto. - final DomElement contents = flutterViewEmbedder.glassPaneElement - .querySelector('#view-0')!; - final DomElement slot = flutterViewEmbedder.sceneElement! - .querySelector('slot')!; + final DomElement contents = + flutterViewEmbedder.glassPaneElement.querySelector('#view-0')!; + final DomElement slot = + flutterViewEmbedder.sceneElement!.querySelector('slot')!; final DomElement contentsHost = contents.parent!; final DomElement slotHost = slot.parent!; @@ -292,8 +291,7 @@ void testMain() { }); test('renders overlays on top of platform views', () async { - expect(SurfaceFactory.instance.debugCacheSize, 0); - expect(configuration.canvasKitMaximumSurfaces, 8); + expect(RenderCanvasFactory.instance.debugCacheSize, 0); final CkPicture testPicture = paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); @@ -339,8 +337,8 @@ void testMain() { _platformView, _overlay, _platformView, - _overlay, _platformView, + _overlay, ]); // Frame 2: @@ -372,7 +370,7 @@ void testMain() { ]); // Frame 4: - // Render: more platform views than max cache size. + // Render: more platform views than max overlay count. // Expect: main canvas, backup overlay, maximum overlays. await Future.delayed(Duration.zero); renderTestScene(viewCount: 16); @@ -391,7 +389,6 @@ void testMain() { _platformView, _overlay, _platformView, - _overlay, _platformView, _platformView, _platformView, @@ -401,6 +398,7 @@ void testMain() { _platformView, _platformView, _platformView, + _overlay, ]); // Frame 5: @@ -477,7 +475,6 @@ void testMain() { // Render: Views 1-10 // Expect: main canvas plus platform view overlays; empty cache. renderTestScene([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - expect(SurfaceFactory.instance.numAvailableOverlays, 0); _expectSceneMatches(<_EmbeddedViewMarker>[ _overlay, _platformView, @@ -493,10 +490,10 @@ void testMain() { _platformView, _overlay, _platformView, - _overlay, _platformView, _platformView, _platformView, + _overlay, ]); // Frame 2: @@ -504,7 +501,6 @@ void testMain() { // Expect: main canvas plus platform view overlays; empty cache. await Future.delayed(Duration.zero); renderTestScene([2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - expect(SurfaceFactory.instance.numAvailableOverlays, 0); _expectSceneMatches(<_EmbeddedViewMarker>[ _overlay, _platformView, @@ -520,10 +516,10 @@ void testMain() { _platformView, _overlay, _platformView, - _overlay, _platformView, _platformView, _platformView, + _overlay, ]); // Frame 3: @@ -546,10 +542,10 @@ void testMain() { _platformView, _overlay, _platformView, - _overlay, _platformView, _platformView, _platformView, + _overlay, ]); // Frame 4: @@ -572,10 +568,10 @@ void testMain() { _platformView, _overlay, _platformView, - _overlay, _platformView, _platformView, _platformView, + _overlay, ]); // TODO(yjbanov): skipped due to https://github.com/flutter/flutter/issues/73867 @@ -599,8 +595,7 @@ void testMain() { ]); expect( - flutterViewEmbedder.glassPaneElement - .querySelector('flt-platform-view'), + flutterViewEmbedder.glassPaneElement.querySelector('flt-platform-view'), isNotNull, ); @@ -615,13 +610,14 @@ void testMain() { ]); expect( - flutterViewEmbedder.glassPaneElement - .querySelector('flt-platform-view'), + flutterViewEmbedder.glassPaneElement.querySelector('flt-platform-view'), isNull, ); }); - test('does not crash when resizing the window after textures have been registered', () async { + test( + 'does not crash when resizing the window after textures have been registered', + () async { ui_web.platformViewRegistry.registerViewFactory( 'test-platform-view', (int viewId) => createDomHTMLDivElement()..id = 'view-0', @@ -664,7 +660,7 @@ void testMain() { window.debugPhysicalSizeOverride = null; window.debugForceResize(); - // ImageDecoder is not supported in Safari or Firefox. + // ImageDecoder is not supported in Safari or Firefox. }, skip: isSafari || isFirefox); test('removed the DOM node of an unrendered platform view', () async { @@ -686,8 +682,7 @@ void testMain() { ]); expect( - flutterViewEmbedder.glassPaneElement - .querySelector('flt-platform-view'), + flutterViewEmbedder.glassPaneElement.querySelector('flt-platform-view'), isNotNull, ); @@ -744,8 +739,8 @@ void testMain() { rasterizer.draw(sb.build().layerTree); } - final DomNode skPathDefs = flutterViewEmbedder.sceneElement! - .querySelector('#sk_path_defs')!; + final DomNode skPathDefs = + flutterViewEmbedder.sceneElement!.querySelector('#sk_path_defs')!; expect(skPathDefs.childNodes, hasLength(0)); @@ -782,121 +777,6 @@ void testMain() { ]); }); - test('does not crash when overlays are disabled', () async { - final Rasterizer rasterizer = CanvasKitRenderer.instance.rasterizer; - HtmlViewEmbedder.debugDisableOverlays = true; - ui_web.platformViewRegistry.registerViewFactory( - 'test-platform-view', - (int viewId) => createDomHTMLDivElement()..id = 'view-0', - ); - await createPlatformView(0, 'test-platform-view'); - - final LayerSceneBuilder sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - sb.addPlatformView(0, width: 10, height: 10); - sb.pop(); - // The below line should not throw an error. - rasterizer.draw(sb.build().layerTree); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - ]); - HtmlViewEmbedder.debugDisableOverlays = false; - }); - - test('works correctly with max overlays == 2', () async { - final Rasterizer rasterizer = CanvasKitRenderer.instance.rasterizer; - debugOverrideJsConfiguration( - { - 'canvasKitMaximumSurfaces': 2, - }.jsify() as JsFlutterConfiguration? - ); - expect(configuration.canvasKitMaximumSurfaces, 2); - expect(configuration.canvasKitVariant, isNot(CanvasKitVariant.auto)); - - SurfaceFactory.instance.debugClear(); - - expect(SurfaceFactory.instance.maximumSurfaces, 2); - expect(SurfaceFactory.instance.maximumOverlays, 1); - - ui_web.platformViewRegistry.registerViewFactory( - 'test-platform-view', - (int viewId) => createDomHTMLDivElement()..id = 'view-0', - ); - await createPlatformView(0, 'test-platform-view'); - await createPlatformView(1, 'test-platform-view'); - - LayerSceneBuilder sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - sb.addPlatformView(0, width: 10, height: 10); - sb.pop(); - // The below line should not throw an error. - rasterizer.draw(sb.build().layerTree); - - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - _overlay, - ]); - - sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - sb.addPlatformView(1, width: 10, height: 10); - sb.addPlatformView(0, width: 10, height: 10); - sb.pop(); - // The below line should not throw an error. - rasterizer.draw(sb.build().layerTree); - - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - _overlay, - _platformView, - ]); - - // Reset configuration - debugOverrideJsConfiguration(null); - }); - - test( - 'correctly renders when overlays are disabled and a subset ' - 'of views is used', () async { - final Rasterizer rasterizer = CanvasKitRenderer.instance.rasterizer; - HtmlViewEmbedder.debugDisableOverlays = true; - ui_web.platformViewRegistry.registerViewFactory( - 'test-platform-view', - (int viewId) => createDomHTMLDivElement()..id = 'view-0', - ); - await createPlatformView(0, 'test-platform-view'); - await createPlatformView(1, 'test-platform-view'); - - LayerSceneBuilder sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - sb.addPlatformView(0, width: 10, height: 10); - sb.addPlatformView(1, width: 10, height: 10); - sb.pop(); - // The below line should not throw an error. - rasterizer.draw(sb.build().layerTree); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - _platformView, - ]); - - sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - sb.addPlatformView(1, width: 10, height: 10); - sb.pop(); - // The below line should not throw an error. - rasterizer.draw(sb.build().layerTree); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - ]); - - HtmlViewEmbedder.debugDisableOverlays = false; - }); - test('does not create overlays for invisible platform views', () async { final Rasterizer rasterizer = CanvasKitRenderer.instance.rasterizer; ui_web.platformViewRegistry.registerViewFactory( @@ -917,8 +797,8 @@ void testMain() { await createPlatformView(5, 'test-invisible-view'); await createPlatformView(6, 'test-invisible-view'); - expect(platformViewManager.isInvisible(0), isFalse); - expect(platformViewManager.isInvisible(1), isTrue); + expect(PlatformViewManager.instance.isInvisible(0), isFalse); + expect(PlatformViewManager.instance.isInvisible(1), isTrue); LayerSceneBuilder sb = LayerSceneBuilder(); sb.pushOffset(0, 0); @@ -957,7 +837,9 @@ void testMain() { _overlay, _platformView, _overlay, - ], reason: 'Overlays created after each group containing a visible view.'); + ], + reason: + 'Overlays created after each group containing a visible view.'); sb = LayerSceneBuilder(); sb.pushOffset(0, 0); @@ -1059,7 +941,9 @@ void testMain() { _platformView, _platformView, _platformView, - ], reason: 'Many invisible views can be rendered on top of the base overlay.'); + ], + reason: + 'Many invisible views can be rendered on top of the base overlay.'); sb = LayerSceneBuilder(); sb.pushOffset(0, 0); @@ -1108,19 +992,22 @@ enum _EmbeddedViewMarker { _EmbeddedViewMarker get _overlay => _EmbeddedViewMarker.overlay; _EmbeddedViewMarker get _platformView => _EmbeddedViewMarker.platformView; -const Map _tagToViewMarker = { +const Map _tagToViewMarker = + { 'flt-canvas-container': _EmbeddedViewMarker.overlay, 'flt-platform-view-slot': _EmbeddedViewMarker.platformView, }; -void _expectSceneMatches(List<_EmbeddedViewMarker> expectedMarkers, { +void _expectSceneMatches( + List<_EmbeddedViewMarker> expectedMarkers, { String? reason, }) { // Convert the scene elements to its corresponding array of _EmbeddedViewMarker final List<_EmbeddedViewMarker> sceneElements = flutterViewEmbedder .sceneElement!.children .where((DomElement element) => element.tagName != 'svg') - .map((DomElement element) => _tagToViewMarker[element.tagName.toLowerCase()]!) + .map((DomElement element) => + _tagToViewMarker[element.tagName.toLowerCase()]!) .toList(); expect(sceneElements, expectedMarkers, reason: reason); diff --git a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart index 8f6d74cc09469..f4adbc5398af2 100644 --- a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart +++ b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart @@ -17,7 +17,6 @@ void testMain() { expect(findGlassPane(), isNull); expect(RawKeyboard.instance, isNull); - expect(MouseCursor.instance, isNull); expect(KeyboardBinding.instance, isNull); expect(PointerBinding.instance, isNull); @@ -28,7 +27,6 @@ void testMain() { expect(findGlassPane(), isNull); expect(RawKeyboard.instance, isNull); - expect(MouseCursor.instance, isNull); expect(KeyboardBinding.instance, isNull); expect(PointerBinding.instance, isNull); @@ -36,7 +34,6 @@ void testMain() { await initializeEngineUi(); expect(findGlassPane(), isNotNull); expect(RawKeyboard.instance, isNotNull); - expect(MouseCursor.instance, isNotNull); expect(KeyboardBinding.instance, isNotNull); expect(PointerBinding.instance, isNotNull); }); diff --git a/lib/web_ui/test/canvaskit/render_canvas_factory_test.dart b/lib/web_ui/test/canvaskit/render_canvas_factory_test.dart new file mode 100644 index 0000000000000..70aa4e6073ffa --- /dev/null +++ b/lib/web_ui/test/canvaskit/render_canvas_factory_test.dart @@ -0,0 +1,95 @@ +// 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. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import 'common.dart'; + +const MethodCodec codec = StandardMethodCodec(); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$RenderCanvasFactory', () { + setUpCanvasKitTest(); + + test('getCanvas', () { + final RenderCanvasFactory factory = RenderCanvasFactory(); + expect(factory.baseCanvas, isNotNull); + + expect(factory.debugSurfaceCount, equals(1)); + + // Get a canvas from the factory, it should be unique. + final RenderCanvas newCanvas = factory.getCanvas(); + expect(newCanvas, isNot(equals(factory.baseCanvas))); + + expect(factory.debugSurfaceCount, equals(2)); + + // Get another canvas from the factory. Now we are at maximum capacity. + final RenderCanvas anotherCanvas = factory.getCanvas(); + expect(anotherCanvas, isNot(equals(factory.baseCanvas))); + + expect(factory.debugSurfaceCount, equals(3)); + }); + + test('releaseCanvas', () { + final RenderCanvasFactory factory = RenderCanvasFactory(); + + // Create a new canvas and immediately release it. + final RenderCanvas canvas = factory.getCanvas(); + factory.releaseCanvas(canvas); + + // If we create a new canvas, it should be the same as the one we + // just created. + final RenderCanvas newCanvas = factory.getCanvas(); + expect(newCanvas, equals(canvas)); + }); + + test('isLive', () { + final RenderCanvasFactory factory = RenderCanvasFactory(); + + expect(factory.isLive(factory.baseCanvas), isTrue); + + final RenderCanvas canvas = factory.getCanvas(); + expect(factory.isLive(canvas), isTrue); + + factory.releaseCanvas(canvas); + expect(factory.isLive(canvas), isFalse); + }); + + test('hot restart', () { + void expectDisposed(RenderCanvas canvas) { + expect(canvas.canvasElement.isConnected, isFalse); + } + + final RenderCanvasFactory originalFactory = RenderCanvasFactory.instance; + expect(RenderCanvasFactory.debugUninitializedInstance, isNotNull); + + // Cause the surface and its canvas to be attached to the page + CanvasKitRenderer.instance.sceneHost! + .prepend(originalFactory.baseCanvas.htmlElement); + expect(originalFactory.baseCanvas.canvasElement.isConnected, isTrue); + + // Create a few overlay canvases + final List overlays = []; + for (int i = 0; i < 3; i++) { + final RenderCanvas canvas = originalFactory.getCanvas(); + CanvasKitRenderer.instance.sceneHost!.prepend(canvas.htmlElement); + overlays.add(canvas); + } + expect(originalFactory.debugSurfaceCount, 4); + + // Trigger hot restart clean-up logic and check that we indeed clean up. + debugEmulateHotRestart(); + expect(RenderCanvasFactory.debugUninitializedInstance, isNull); + expectDisposed(originalFactory.baseCanvas); + overlays.forEach(expectDisposed); + expect(originalFactory.debugSurfaceCount, 1); + }); + }); +} diff --git a/lib/web_ui/test/canvaskit/render_canvas_test.dart b/lib/web_ui/test/canvaskit/render_canvas_test.dart new file mode 100644 index 0000000000000..75a0cfba2897c --- /dev/null +++ b/lib/web_ui/test/canvaskit/render_canvas_test.dart @@ -0,0 +1,62 @@ +// 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. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CanvasKit', () { + setUpCanvasKitTest(); + setUp(() async { + window.debugOverrideDevicePixelRatio(1.0); + }); + + Future newBitmap(int width, int height) async { + return (await createSizedImageBitmapFromImageData( + createBlankDomImageData(width, height), + 0, + 0, + width, + height, + ))!; + } + + // Regression test for https://github.com/flutter/flutter/issues/75286 + test('updates canvas logical size when device-pixel ratio changes', + () async { + final RenderCanvas canvas = RenderCanvas(); + canvas.render(await newBitmap(10, 16)); + + expect(canvas.canvasElement.width, 10); + expect(canvas.canvasElement.height, 16); + expect(canvas.canvasElement.style.width, '10px'); + expect(canvas.canvasElement.style.height, '16px'); + + // Increase device-pixel ratio: this makes CSS pixels bigger, so we need + // fewer of them to cover the browser window. + window.debugOverrideDevicePixelRatio(2.0); + canvas.render(await newBitmap(10, 16)); + expect(canvas.canvasElement.width, 10); + expect(canvas.canvasElement.height, 16); + expect(canvas.canvasElement.style.width, '5px'); + expect(canvas.canvasElement.style.height, '8px'); + + // Decrease device-pixel ratio: this makes CSS pixels smaller, so we need + // more of them to cover the browser window. + window.debugOverrideDevicePixelRatio(0.5); + canvas.render(await newBitmap(10, 16)); + expect(canvas.canvasElement.width, 10); + expect(canvas.canvasElement.height, 16); + expect(canvas.canvasElement.style.width, '20px'); + expect(canvas.canvasElement.style.height, '32px'); + }); + }); +} diff --git a/lib/web_ui/test/canvaskit/surface_factory_test.dart b/lib/web_ui/test/canvaskit/surface_factory_test.dart deleted file mode 100644 index 05db21472386c..0000000000000 --- a/lib/web_ui/test/canvaskit/surface_factory_test.dart +++ /dev/null @@ -1,103 +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. - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import 'common.dart'; - -const MethodCodec codec = StandardMethodCodec(); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('$SurfaceFactory', () { - setUpCanvasKitTest(); - - test('cannot be created with size less than 1', () { - expect(SurfaceFactory(-1).maximumSurfaces, 1); - expect(SurfaceFactory(0).maximumSurfaces, 1); - expect(SurfaceFactory(1).maximumSurfaces, 1); - expect(SurfaceFactory(2).maximumSurfaces, 2); - }); - - test('getSurface', () { - final SurfaceFactory factory = SurfaceFactory(3); - expect(factory.baseSurface, isNotNull); - - expect(factory.debugSurfaceCount, equals(1)); - - // Get a surface from the factory, it should be unique. - final Surface? newSurface = factory.getSurface(); - expect(newSurface, isNot(equals(factory.baseSurface))); - - expect(factory.debugSurfaceCount, equals(2)); - - // Get another surface from the factory. Now we are at maximum capacity. - final Surface? anotherSurface = factory.getSurface(); - expect(anotherSurface, isNot(equals(factory.baseSurface))); - - expect(factory.debugSurfaceCount, equals(3)); - }); - - test('releaseSurface', () { - final SurfaceFactory factory = SurfaceFactory(3); - - // Create a new surface and immediately release it. - final Surface? surface = factory.getSurface(); - factory.releaseSurface(surface!); - - // If we create a new surface, it should be the same as the one we - // just created. - final Surface? newSurface = factory.getSurface(); - expect(newSurface, equals(surface)); - }); - - test('isLive', () { - final SurfaceFactory factory = SurfaceFactory(3); - - expect(factory.isLive(factory.baseSurface), isTrue); - - final Surface? surface = factory.getSurface(); - expect(factory.isLive(surface!), isTrue); - - factory.releaseSurface(surface); - expect(factory.isLive(surface), isFalse); - }); - - test('hot restart', () { - void expectDisposed(Surface surface) { - expect(surface.htmlCanvas!.isConnected, isFalse); - } - - final SurfaceFactory originalFactory = SurfaceFactory.instance; - expect(SurfaceFactory.debugUninitializedInstance, isNotNull); - - // Cause the surface and its canvas to be attached to the page - originalFactory.baseSurface.acquireFrame(const ui.Size(10, 10)); - originalFactory.baseSurface.addToScene(); - expect(originalFactory.baseSurface.htmlCanvas!.isConnected, isTrue); - - // Create a few overlay surfaces - final List overlays = []; - for (int i = 0; i < 3; i++) { - overlays.add(originalFactory.getSurface()! - ..acquireFrame(const ui.Size(10, 10)) - ..addToScene()); - } - expect(originalFactory.debugSurfaceCount, 4); - - // Trigger hot restart clean-up logic and check that we indeed clean up. - debugEmulateHotRestart(); - expect(SurfaceFactory.debugUninitializedInstance, isNull); - expectDisposed(originalFactory.baseSurface); - overlays.forEach(expectDisposed); - expect(originalFactory.debugSurfaceCount, 1); - }); - }); -} diff --git a/lib/web_ui/test/canvaskit/surface_test.dart b/lib/web_ui/test/canvaskit/surface_test.dart index e31ca43bf8079..06e0d04ed1bbe 100644 --- a/lib/web_ui/test/canvaskit/surface_test.dart +++ b/lib/web_ui/test/canvaskit/surface_test.dart @@ -23,17 +23,14 @@ void testMain() { }); test('Surface allocates canvases efficiently', () { - final Surface? surface = SurfaceFactory.instance.getSurface(); + final Surface surface = Surface(); final CkSurface originalSurface = - surface!.acquireFrame(const ui.Size(9, 19)).skiaSurface; - final DomCanvasElement original = surface.htmlCanvas!; + surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + final DomOffscreenCanvas original = surface.debugOffscreenCanvas!; // Expect exact requested dimensions. expect(original.width, 9); expect(original.height, 19); - expect(original.style.width, '9px'); - expect(original.style.height, '19px'); - expect(original.style.transform, _isTranslate('0', '0')); expect(originalSurface.width(), 9); expect(originalSurface.height(), 19); @@ -41,11 +38,8 @@ void testMain() { // Skia renders into the visible area. final CkSurface shrunkSurface = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; - final DomCanvasElement shrunk = surface.htmlCanvas!; + final DomOffscreenCanvas shrunk = surface.debugOffscreenCanvas!; expect(shrunk, same(original)); - expect(shrunk.style.width, '9px'); - expect(shrunk.style.height, '19px'); - expect(shrunk.style.transform, _isTranslate('0', '-4')); expect(shrunkSurface, isNot(same(originalSurface))); expect(shrunkSurface.width(), 5); expect(shrunkSurface.height(), 15); @@ -54,52 +48,42 @@ void testMain() { // by 40% to accommodate future increases. final CkSurface firstIncreaseSurface = surface.acquireFrame(const ui.Size(10, 20)).skiaSurface; - final DomCanvasElement firstIncrease = surface.htmlCanvas!; + final DomOffscreenCanvas firstIncrease = surface.debugOffscreenCanvas!; expect(firstIncrease, same(original)); expect(firstIncreaseSurface, isNot(same(shrunkSurface))); // Expect overallocated dimensions expect(firstIncrease.width, 14); expect(firstIncrease.height, 28); - expect(firstIncrease.style.width, '14px'); - expect(firstIncrease.style.height, '28px'); - expect(firstIncrease.style.transform, _isTranslate('0', '-8')); expect(firstIncreaseSurface.width(), 10); expect(firstIncreaseSurface.height(), 20); // Subsequent increases within 40% reuse the old canvas. final CkSurface secondIncreaseSurface = surface.acquireFrame(const ui.Size(11, 22)).skiaSurface; - final DomCanvasElement secondIncrease = surface.htmlCanvas!; + final DomOffscreenCanvas secondIncrease = surface.debugOffscreenCanvas!; expect(secondIncrease, same(firstIncrease)); - expect(secondIncrease.style.transform, _isTranslate('0', '-6')); expect(secondIncreaseSurface, isNot(same(firstIncreaseSurface))); expect(secondIncreaseSurface.width(), 11); expect(secondIncreaseSurface.height(), 22); // Increases beyond the 40% limit will cause a new allocation. final CkSurface hugeSurface = surface.acquireFrame(const ui.Size(20, 40)).skiaSurface; - final DomCanvasElement huge = surface.htmlCanvas!; + final DomOffscreenCanvas huge = surface.debugOffscreenCanvas!; expect(huge, same(secondIncrease)); expect(hugeSurface, isNot(same(secondIncreaseSurface))); // Also over-allocated expect(huge.width, 28); expect(huge.height, 56); - expect(huge.style.width, '28px'); - expect(huge.style.height, '56px'); - expect(huge.style.transform, _isTranslate('0', '-16')); expect(hugeSurface.width(), 20); expect(hugeSurface.height(), 40); // Shrink again. Reuse the last allocated surface. final CkSurface shrunkSurface2 = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; - final DomCanvasElement shrunk2 = surface.htmlCanvas!; + final DomOffscreenCanvas shrunk2 = surface.debugOffscreenCanvas!; expect(shrunk2, same(huge)); - expect(shrunk2.style.width, '28px'); - expect(shrunk2.style.height, '56px'); - expect(shrunk2.style.transform, _isTranslate('0', '-41')); expect(shrunkSurface2, isNot(same(hugeSurface))); expect(shrunkSurface2.width(), 5); expect(shrunkSurface2.height(), 15); @@ -109,11 +93,8 @@ void testMain() { window.debugOverrideDevicePixelRatio(2.0); final CkSurface dpr2Surface2 = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; - final DomCanvasElement dpr2Canvas = surface.htmlCanvas!; + final DomOffscreenCanvas dpr2Canvas = surface.debugOffscreenCanvas!; expect(dpr2Canvas, same(huge)); - expect(dpr2Canvas.style.width, '14px'); - expect(dpr2Canvas.style.height, '28px'); - expect(dpr2Canvas.style.transform, _isTranslate('0', '-20.5')); expect(dpr2Surface2, isNot(same(hugeSurface))); expect(dpr2Surface2.width(), 5); expect(dpr2Surface2.height(), 15); @@ -123,13 +104,13 @@ void testMain() { // which cannot be a different size from the canvas. // TODO(hterkelsen): See if we can give a custom size for software // surfaces. - }, skip: isFirefox); + }, skip: isFirefox || !Surface.offscreenCanvasSupported); test( 'Surface creates new context when WebGL context is restored', () async { - final Surface? surface = SurfaceFactory.instance.getSurface(); - expect(surface!.debugForceNewContext, isTrue); + final Surface surface = Surface(); + expect(surface.debugForceNewContext, isTrue); final CkSurface before = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; expect(surface.debugForceNewContext, isFalse); @@ -142,15 +123,14 @@ void testMain() { expect(afterAcquireFrame, same(before)); // Emulate WebGL context loss. - final DomCanvasElement canvas = - surface.htmlElement.children.single as DomCanvasElement; + final DomOffscreenCanvas canvas = surface.debugOffscreenCanvas!; final Object ctx = canvas.getContext('webgl2')!; final Object loseContextExtension = js_util.callMethod( ctx, 'getExtension', ['WEBGL_lose_context'], ); - js_util.callMethod(loseContextExtension, 'loseContext', const []); + js_util.callMethod(loseContextExtension, 'loseContext', const []); // Pump a timer to allow the "lose context" event to propagate. await Future.delayed(Duration.zero); @@ -160,7 +140,7 @@ void testMain() { expect(isContextLost, isTrue); // Emulate WebGL context restoration. - js_util.callMethod(loseContextExtension, 'restoreContext', const []); + js_util.callMethod(loseContextExtension, 'restoreContext', const []); // Pump a timer to allow the "restore context" event to propagate. await Future.delayed(Duration.zero); @@ -172,7 +152,7 @@ void testMain() { expect(afterContextLost, isNot(same(before))); }, // Firefox can't create a WebGL2 context in headless mode. - skip: isFirefox, + skip: isFirefox || !Surface.offscreenCanvasSupported, ); // Regression test for https://github.com/flutter/flutter/issues/75286 @@ -183,9 +163,8 @@ void testMain() { expect(original.width(), 10); expect(original.height(), 16); - expect(surface.htmlCanvas!.style.width, '10px'); - expect(surface.htmlCanvas!.style.height, '16px'); - expect(surface.htmlCanvas!.style.transform, _isTranslate('0', '0')); + expect(surface.debugOffscreenCanvas!.width, 10); + expect(surface.debugOffscreenCanvas!.height, 16); // Increase device-pixel ratio: this makes CSS pixels bigger, so we need // fewer of them to cover the browser window. @@ -194,9 +173,8 @@ void testMain() { surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; expect(highDpr.width(), 10); expect(highDpr.height(), 16); - expect(surface.htmlCanvas!.style.width, '5px'); - expect(surface.htmlCanvas!.style.height, '8px'); - expect(surface.htmlCanvas!.style.transform, _isTranslate('0', '0')); + expect(surface.debugOffscreenCanvas!.width, 10); + expect(surface.debugOffscreenCanvas!.height, 16); // Decrease device-pixel ratio: this makes CSS pixels smaller, so we need // more of them to cover the browser window. @@ -205,9 +183,8 @@ void testMain() { surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; expect(lowDpr.width(), 10); expect(lowDpr.height(), 16); - expect(surface.htmlCanvas!.style.width, '20px'); - expect(surface.htmlCanvas!.style.height, '32px'); - expect(surface.htmlCanvas!.style.transform, _isTranslate('0', '0')); + expect(surface.debugOffscreenCanvas!.width, 10); + expect(surface.debugOffscreenCanvas!.height, 16); // See https://github.com/flutter/flutter/issues/77084#issuecomment-1120151172 window.debugOverrideDevicePixelRatio(2.0); @@ -215,28 +192,10 @@ void testMain() { surface.acquireFrame(const ui.Size(9.9, 15.9)).skiaSurface; expect(changeRatioAndSize.width(), 10); expect(changeRatioAndSize.height(), 16); - expect(surface.htmlCanvas!.style.width, '5px'); - expect(surface.htmlCanvas!.style.height, '8px'); - expect(surface.htmlCanvas!.style.transform, _isTranslate('0', '0')); - }); - }); -} - -/// Checks that the CSS 'transform' property is a translation in a cross-browser way. -/// -/// Takes strings directly to avoid issues with floating point or differences -/// in stringification of numeric values across JS and Wasm targets. -Matcher _isTranslate(String x, String y) { - // When the y coordinate is zero, Firefox omits it, e.g.: - // Chrome/Safari/Edge: translate(0px, 0px) - // Firefox: translate(0px) - final String fullFormat = 'translate(${x}px, ${y}px)'; - if (y != '0') { - return equals(fullFormat); - } else { - return anyOf( - fullFormat, // Non-Firefox browsers use this format. - 'translate(${x}px)', // Firefox omits y when it's zero. + expect(surface.debugOffscreenCanvas!.width, 10); + expect(surface.debugOffscreenCanvas!.height, 16); + }, + skip: !Surface.offscreenCanvasSupported, ); - } + }); } diff --git a/lib/web_ui/test/common/fake_asset_manager.dart b/lib/web_ui/test/common/fake_asset_manager.dart index d87b071da59b1..b6f8c176e5c8a 100644 --- a/lib/web_ui/test/common/fake_asset_manager.dart +++ b/lib/web_ui/test/common/fake_asset_manager.dart @@ -81,7 +81,7 @@ class FakeAssetScope { return fetcher(); } if (_parent != null) { - return _parent!.getAssetData(assetKey); + return _parent.getAssetData(assetKey); } return null; } diff --git a/lib/web_ui/test/common/matchers.dart b/lib/web_ui/test/common/matchers.dart index 169baaef8af95..a2bd9fe66102a 100644 --- a/lib/web_ui/test/common/matchers.dart +++ b/lib/web_ui/test/common/matchers.dart @@ -293,23 +293,17 @@ String canonicalizeHtml( html_package.Element.tag(replacementTag); if (mode != HtmlComparisonMode.noAttributes) { - // Sort the attributes so tests are not sensitive to their order, which - // does not matter in terms of functionality. - final List attributeNames = original.attributes.keys.cast().toList(); - attributeNames.sort(); - for (final String name in attributeNames) { - final String value = original.attributes[name]!; + original.attributes.forEach((dynamic name, String value) { + if (name is! String) { + throw ArgumentError('"$name" should be String but was ${name.runtimeType}.'); + } if (name == 'style') { - // The style attribute is handled separately because it contains substructure. - continue; + return; } - - // These are the only attributes we're interested in testing. This list - // can change over time. - if (name.startsWith('aria-') || name.startsWith('flt-') || name == 'role') { + if (name.startsWith('aria-')) { replacement.attributes[name] = value; } - } + }); if (original.attributes.containsKey('style')) { final String styleValue = original.attributes['style']!; diff --git a/lib/web_ui/test/engine/mouse/context_menu_test.dart b/lib/web_ui/test/engine/mouse/context_menu_test.dart new file mode 100644 index 0000000000000..a432678657927 --- /dev/null +++ b/lib/web_ui/test/engine/mouse/context_menu_test.dart @@ -0,0 +1,104 @@ +// 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. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$ContextMenu', () { + test('can disable context menu', () { + final DomElement rootViewElement = createDomElement('div'); + final ContextMenu contextMenu = ContextMenu(rootViewElement); + + // When the app starts, contextmenu events are not prevented. + DomEvent event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + + // Disabling the context menu causes contextmenu events to be prevented. + contextMenu.disable(); + event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isTrue); + + // Disabling again has no effect. + contextMenu.disable(); + event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isTrue); + }); + + test('does not disable context menu outside root view element', () { + final DomElement rootViewElement = createDomElement('div'); + final ContextMenu contextMenu = ContextMenu(rootViewElement); + + contextMenu.disable(); + + // Dispatching on a DOM element outside of target's subtree has no effect. + final DomEvent event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + domDocument.body!.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + }); + + test('can enable context menu after disabling', () { + final DomElement rootViewElement = createDomElement('div'); + final ContextMenu contextMenu = ContextMenu(rootViewElement); + + // When the app starts, contextmenu events are not prevented. + DomEvent event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + + // Disabling the context menu causes contextmenu events to be prevented. + contextMenu.disable(); + event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isTrue); + + // Enabling the context menu means that contextmenu events are back to not + // being prevented. + contextMenu.enable(); + event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + + // Enabling again has no effect. + contextMenu.enable(); + event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + }); + + test('enabling before disabling has no effect', () { + final DomElement rootViewElement = createDomElement('div'); + final ContextMenu contextMenu = ContextMenu(rootViewElement); + + // When the app starts, contextmenu events are not prevented. + DomEvent event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + + // Enabling has no effect. + contextMenu.enable(); + event = createDomEvent('Event', 'contextmenu'); + expect(event.defaultPrevented, isFalse); + rootViewElement.dispatchEvent(event); + expect(event.defaultPrevented, isFalse); + }); + }); +} diff --git a/lib/web_ui/test/engine/mouse/cursor_test.dart b/lib/web_ui/test/engine/mouse/cursor_test.dart new file mode 100644 index 0000000000000..f81012ba61efa --- /dev/null +++ b/lib/web_ui/test/engine/mouse/cursor_test.dart @@ -0,0 +1,40 @@ +// 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. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$MouseCursor', () { + test('sets correct `cursor` style on root element', () { + final DomElement rootViewElement = createDomElement('div'); + final MouseCursor mouseCursor = MouseCursor(rootViewElement); + + mouseCursor.activateSystemCursor('alias'); + expect(rootViewElement.style.cursor, 'alias'); + + mouseCursor.activateSystemCursor('move'); + expect(rootViewElement.style.cursor, 'move'); + + mouseCursor.activateSystemCursor('precise'); + expect(rootViewElement.style.cursor, 'crosshair'); + + mouseCursor.activateSystemCursor('resizeDownRight'); + expect(rootViewElement.style.cursor, 'se-resize'); + }); + + test('handles unknown cursor type', () { + final DomElement rootViewElement = createDomElement('div'); + final MouseCursor mouseCursor = MouseCursor(rootViewElement); + + mouseCursor.activateSystemCursor('unknown'); + expect(rootViewElement.style.cursor, 'default'); + }); + }); +} diff --git a/lib/web_ui/test/engine/platform_views/content_manager_test.dart b/lib/web_ui/test/engine/platform_views/content_manager_test.dart index 6de84dbb3ab99..445094c5bccc1 100644 --- a/lib/web_ui/test/engine/platform_views/content_manager_test.dart +++ b/lib/web_ui/test/engine/platform_views/content_manager_test.dart @@ -51,13 +51,14 @@ void testMain() { test('forgets viewIds after clearing them', () { contentManager.registerFactory(viewType, (int id) => createDomHTMLDivElement()); - contentManager.renderContent(viewType, viewId, null); + final DomElement view = contentManager.renderContent(viewType, viewId, null); expect(contentManager.knowsViewId(viewId), isTrue); contentManager.clearPlatformView(viewId); expect(contentManager.knowsViewId(viewId), isFalse); + expect(view.parentNode, isNull); }); }); diff --git a/lib/web_ui/test/engine/platform_views/legacy_message_handler_test.dart b/lib/web_ui/test/engine/platform_views/legacy_message_handler_test.dart new file mode 100644 index 0000000000000..2c9f47725438f --- /dev/null +++ b/lib/web_ui/test/engine/platform_views/legacy_message_handler_test.dart @@ -0,0 +1,265 @@ +// 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. + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const MethodCodec codec = StandardMethodCodec(); + +typedef PlatformViewFactoryCall = ({int viewId, Object? params}); + +void testMain() { + group('PlatformViewMessageHandler', () { + group('handlePlatformViewCall', () { + const String viewType = 'forTest'; + const int viewId = 6; + late PlatformViewManager contentManager; + late Completer completer; + + setUp(() { + contentManager = PlatformViewManager(); + completer = Completer(); + }); + + group('"create" message', () { + test('unregistered viewType, fails with descriptive exception', + () async { + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + final Map arguments = _getCreateArguments(viewType, viewId); + + messageHandler.handleLegacyPlatformViewCall('create', arguments, completer.complete); + + final ByteData? response = await completer.future; + try { + codec.decodeEnvelope(response!); + } on PlatformException catch (e) { + expect(e.code, 'unregistered_view_type'); + expect(e.message, contains(viewType)); + expect(e.details, contains('registerViewFactory')); + } + }); + + test('duplicate viewId, fails with descriptive exception', () async { + contentManager.registerFactory( + viewType, (int id) => createDomHTMLDivElement()); + contentManager.renderContent(viewType, viewId, null); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + final Map arguments = _getCreateArguments(viewType, viewId); + + messageHandler.handleLegacyPlatformViewCall('create', arguments, completer.complete); + + final ByteData? response = await completer.future; + try { + codec.decodeEnvelope(response!); + } on PlatformException catch (e) { + expect(e.code, 'recreating_view'); + expect(e.details, contains('$viewId')); + } + }); + + test('returns a successEnvelope when the view is created normally', + () async { + contentManager.registerFactory( + viewType, (int id) => createDomHTMLDivElement()..id = 'success'); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + final Map arguments = _getCreateArguments(viewType, viewId); + + messageHandler.handleLegacyPlatformViewCall('create', arguments, completer.complete); + + final ByteData? response = await completer.future; + expect(codec.decodeEnvelope(response!), isNull, + reason: + 'The response should be a success envelope, with null in it.'); + }); + + test('inserts the created view into the platformViewsContainer', + () async { + final DomElement platformViewsContainer = createDomElement('pv-container'); + contentManager.registerFactory( + viewType, (int id) => createDomHTMLDivElement()..id = 'success'); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: platformViewsContainer, + contentManager: contentManager, + ); + final Map arguments = _getCreateArguments(viewType, viewId); + + messageHandler.handleLegacyPlatformViewCall('create', arguments, completer.complete); + + final ByteData? response = await completer.future; + + expect( + platformViewsContainer.children.single, + isNotNull, + reason: 'The container has a single child, the created view.', + ); + final DomElement platformView = platformViewsContainer.children.single; + expect( + platformView.querySelector('div#success'), + isNotNull, + reason: 'The element created by the factory should be present in the created view.', + ); + expect( + codec.decodeEnvelope(response!), + isNull, + reason: 'The response should be a success envelope, with null in it.', + ); + }); + + test('passes creation params to the factory', () async { + final List factoryCalls = []; + contentManager.registerFactory(viewType, (int viewId, {Object? params}) { + factoryCalls.add((viewId: viewId, params: params)); + return createDomHTMLDivElement(); + }); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + + final List> completers = >[]; + + completers.add(Completer()); + messageHandler.handleLegacyPlatformViewCall( + 'create', + _getCreateArguments(viewType, 111), + completers.last.complete, + ); + + completers.add(Completer()); + messageHandler.handleLegacyPlatformViewCall( + 'create', + _getCreateArguments(viewType, 222, {'foo': 'bar'}), + completers.last.complete, + ); + + completers.add(Completer()); + messageHandler.handleLegacyPlatformViewCall( + 'create', + _getCreateArguments(viewType, 333, 'foobar'), + completers.last.complete, + ); + + completers.add(Completer()); + messageHandler.handleLegacyPlatformViewCall( + 'create', + _getCreateArguments(viewType, 444, [1, null, 'str']), + completers.last.complete, + ); + + final List responses = await Future.wait( + completers.map((Completer c) => c.future), + ); + + for (final ByteData? response in responses) { + expect( + codec.decodeEnvelope(response!), + isNull, + reason: 'The response should be a success envelope, with null in it.', + ); + } + + expect(factoryCalls, hasLength(4)); + expect(factoryCalls[0].viewId, 111); + expect(factoryCalls[0].params, isNull); + expect(factoryCalls[1].viewId, 222); + expect(factoryCalls[1].params, {'foo': 'bar'}); + expect(factoryCalls[2].viewId, 333); + expect(factoryCalls[2].params, 'foobar'); + expect(factoryCalls[3].viewId, 444); + expect(factoryCalls[3].params, [1, null, 'str']); + }); + + test('fails if the factory returns a non-DOM object', () async { + contentManager.registerFactory(viewType, (int viewId) { + // Return an object that's not a DOM element. + return Object(); + }); + + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + final Map arguments = _getCreateArguments(viewType, viewId); + + expect(() { + messageHandler.handleLegacyPlatformViewCall('create', arguments, (_) {}); + }, throwsA(isA())); + }); + }); + + group('"dispose" message', () { + late Completer viewIdCompleter; + + setUp(() { + viewIdCompleter = Completer(); + }); + + test('never fails, even for unknown viewIds', () async { + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + + messageHandler.handleLegacyPlatformViewCall('dispose', viewId, completer.complete); + + final ByteData? response = await completer.future; + expect(codec.decodeEnvelope(response!), isNull, + reason: + 'The response should be a success envelope, with null in it.'); + }); + + test('never fails, even for unknown viewIds', () async { + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: _FakePlatformViewManager(viewIdCompleter.complete), + ); + + messageHandler.handleLegacyPlatformViewCall('dispose', viewId, completer.complete); + + final int disposedViewId = await viewIdCompleter.future; + expect(disposedViewId, viewId, + reason: + 'The viewId to dispose should be passed to the contentManager'); + }); + }); + }); + }); +} + +class _FakePlatformViewManager extends PlatformViewManager { + _FakePlatformViewManager(void Function(int) clearFunction) + : _clearPlatformView = clearFunction; + + final void Function(int) _clearPlatformView; + + @override + void clearPlatformView(int viewId) { + return _clearPlatformView(viewId); + } +} + +Map _getCreateArguments(String viewType, int viewId, [Object? params]) { + return { + 'id': viewId, + 'viewType': viewType, + if (params != null) 'params': params, + }; +} diff --git a/lib/web_ui/test/engine/platform_views/message_handler_test.dart b/lib/web_ui/test/engine/platform_views/message_handler_test.dart index 2f30e7c5e13f4..344de91e04ae4 100644 --- a/lib/web_ui/test/engine/platform_views/message_handler_test.dart +++ b/lib/web_ui/test/engine/platform_views/message_handler_test.dart @@ -20,68 +20,81 @@ typedef PlatformViewFactoryCall = ({int viewId, Object? params}); void testMain() { group('PlatformViewMessageHandler', () { group('handlePlatformViewCall', () { - const String viewType = 'forTest'; - const int viewId = 6; + const String platformViewType = 'forTest'; + const int platformViewId = 6; late PlatformViewManager contentManager; late Completer completer; - late Completer contentCompleter; setUp(() { contentManager = PlatformViewManager(); completer = Completer(); - contentCompleter = Completer(); }); group('"create" message', () { test('unregistered viewType, fails with descriptive exception', () async { final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), contentManager: contentManager, ); - final ByteData? message = _getCreateMessage(viewType, viewId); + final Map arguments = _getCreateArguments( + platformViewType: platformViewType, + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); - messageHandler.handlePlatformViewCall(message, completer.complete); + messageHandler.handlePlatformViewCall('create', arguments, completer.complete); final ByteData? response = await completer.future; try { codec.decodeEnvelope(response!); } on PlatformException catch (e) { expect(e.code, 'unregistered_view_type'); - expect(e.message, contains(viewType)); + expect(e.message, contains(platformViewType)); expect(e.details, contains('registerViewFactory')); } }); test('duplicate viewId, fails with descriptive exception', () async { contentManager.registerFactory( - viewType, (int id) => createDomHTMLDivElement()); - contentManager.renderContent(viewType, viewId, null); + platformViewType, (int id) => createDomHTMLDivElement()); + contentManager.renderContent(platformViewType, platformViewId, null); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), contentManager: contentManager, ); - final ByteData? message = _getCreateMessage(viewType, viewId); + final Map arguments = _getCreateArguments( + platformViewType: platformViewType, + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); - messageHandler.handlePlatformViewCall(message, completer.complete); + messageHandler.handlePlatformViewCall('create', arguments, completer.complete); final ByteData? response = await completer.future; try { codec.decodeEnvelope(response!); } on PlatformException catch (e) { expect(e.code, 'recreating_view'); - expect(e.details, contains('$viewId')); + expect(e.details, contains('$platformViewId')); } }); test('returns a successEnvelope when the view is created normally', () async { contentManager.registerFactory( - viewType, (int id) => createDomHTMLDivElement()..id = 'success'); + platformViewType, (int id) => createDomHTMLDivElement()..id = 'success'); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), contentManager: contentManager, ); - final ByteData? message = _getCreateMessage(viewType, viewId); + final Map arguments = _getCreateArguments( + platformViewType: platformViewType, + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); - messageHandler.handlePlatformViewCall(message, completer.complete); + messageHandler.handlePlatformViewCall('create', arguments, completer.complete); final ByteData? response = await completer.future; expect(codec.decodeEnvelope(response!), isNull, @@ -89,36 +102,51 @@ void testMain() { 'The response should be a success envelope, with null in it.'); }); - test('calls a contentHandler with the result of creating a view', + test('inserts the created view into the platformViewsContainer', () async { + final DomElement platformViewsContainer = createDomElement('pv-container'); contentManager.registerFactory( - viewType, (int id) => createDomHTMLDivElement()..id = 'success'); + platformViewType, (int id) => createDomHTMLDivElement()..id = 'success'); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: platformViewsContainer, contentManager: contentManager, - contentHandler: contentCompleter.complete, ); - final ByteData? message = _getCreateMessage(viewType, viewId); + final Map arguments = _getCreateArguments( + platformViewType: platformViewType, + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); - messageHandler.handlePlatformViewCall(message, completer.complete); + messageHandler.handlePlatformViewCall('create', arguments, completer.complete); - final DomElement contents = await contentCompleter.future; final ByteData? response = await completer.future; - expect(contents.querySelector('div#success'), isNotNull, - reason: - 'The element created by the factory should be present in the created view.'); - expect(codec.decodeEnvelope(response!), isNull, - reason: - 'The response should be a success envelope, with null in it.'); + expect( + platformViewsContainer.children.single, + isNotNull, + reason: 'The container has a single child, the created view.', + ); + final DomElement platformView = platformViewsContainer.children.single; + expect( + platformView.querySelector('div#success'), + isNotNull, + reason: 'The element created by the factory should be present in the created view.', + ); + expect( + codec.decodeEnvelope(response!), + isNull, + reason: 'The response should be a success envelope, with null in it.', + ); }); test('passes creation params to the factory', () async { final List factoryCalls = []; - contentManager.registerFactory(viewType, (int viewId, {Object? params}) { + contentManager.registerFactory(platformViewType, (int viewId, {Object? params}) { factoryCalls.add((viewId: viewId, params: params)); return createDomHTMLDivElement(); }); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), contentManager: contentManager, ); @@ -126,25 +154,48 @@ void testMain() { completers.add(Completer()); messageHandler.handlePlatformViewCall( - _getCreateMessage(viewType, 111), + 'create', + _getCreateArguments( + platformViewType: platformViewType, + platformViewId: 111, + viewId: kImplicitViewId, + ), completers.last.complete, ); completers.add(Completer()); messageHandler.handlePlatformViewCall( - _getCreateMessage(viewType, 222, {'foo': 'bar'}), + 'create', + _getCreateArguments( + platformViewType: platformViewType, + platformViewId: 222, + viewId: kImplicitViewId, + params: {'foo': 'bar'}, + ), completers.last.complete, ); completers.add(Completer()); messageHandler.handlePlatformViewCall( - _getCreateMessage(viewType, 333, 'foobar'), + 'create', + _getCreateArguments( + platformViewType: platformViewType, + platformViewId: 333, + viewId: kImplicitViewId, + params: 'foobar', + ), completers.last.complete, ); completers.add(Completer()); messageHandler.handlePlatformViewCall( - _getCreateMessage(viewType, 444, [1, null, 'str']), + 'create', + _getCreateArguments( + platformViewType: platformViewType, + platformViewId: 444, + viewId: kImplicitViewId, + params: [1, null, 'str'], + ), completers.last.complete, ); @@ -172,17 +223,23 @@ void testMain() { }); test('fails if the factory returns a non-DOM object', () async { - contentManager.registerFactory(viewType, (int viewId) { + contentManager.registerFactory(platformViewType, (int viewId) { // Return an object that's not a DOM element. return Object(); }); - final PlatformViewMessageHandler messageHandler = - PlatformViewMessageHandler(contentManager: contentManager); - final ByteData? message = _getCreateMessage(viewType, viewId); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), + contentManager: contentManager, + ); + final Map arguments = _getCreateArguments( + platformViewType: platformViewType, + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); expect(() { - messageHandler.handlePlatformViewCall(message, (_) {}); + messageHandler.handlePlatformViewCall('create', arguments, (_) {}); }, throwsA(isA())); }); }); @@ -196,11 +253,15 @@ void testMain() { test('never fails, even for unknown viewIds', () async { final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), contentManager: contentManager, ); - final ByteData? message = _getDisposeMessage(viewId); + final Map arguments = _getDisposeArguments( + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); - messageHandler.handlePlatformViewCall(message, completer.complete); + messageHandler.handlePlatformViewCall('dispose', arguments, completer.complete); final ByteData? response = await completer.future; expect(codec.decodeEnvelope(response!), isNull, @@ -210,14 +271,18 @@ void testMain() { test('never fails, even for unknown viewIds', () async { final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + platformViewsContainer: createDomElement('div'), contentManager: _FakePlatformViewManager(viewIdCompleter.complete), ); - final ByteData? message = _getDisposeMessage(viewId); + final Map arguments = _getDisposeArguments( + platformViewId: platformViewId, + viewId: kImplicitViewId, + ); - messageHandler.handlePlatformViewCall(message, completer.complete); + messageHandler.handlePlatformViewCall('dispose', arguments, completer.complete); final int disposedViewId = await viewIdCompleter.future; - expect(disposedViewId, viewId, + expect(disposedViewId, platformViewId, reason: 'The viewId to dispose should be passed to the contentManager'); }); @@ -238,20 +303,26 @@ class _FakePlatformViewManager extends PlatformViewManager { } } -ByteData? _getCreateMessage(String viewType, int viewId, [Object? params]) { - return codec.encodeMethodCall(MethodCall( - 'create', - { - 'id': viewId, - 'viewType': viewType, - if (params != null) 'params': params, - }, - )); +Map _getCreateArguments({ + required String platformViewType, + required int platformViewId, + required int viewId, + Object? params, +}) { + return { + 'platformViewId': platformViewId, + 'platformViewType': platformViewType, + if (params != null) 'params': params, + 'viewId': viewId, + }; } -ByteData? _getDisposeMessage(int viewId) { - return codec.encodeMethodCall(MethodCall( - 'dispose', - viewId, - )); +Map _getDisposeArguments({ + required int platformViewId, + required int viewId, +}) { + return { + 'platformViewId': platformViewId, + 'viewId': viewId, + }; } diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index 8543210d30240..e85b84bf1003a 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -477,6 +477,47 @@ void testMain() { // ALL ADAPTERS + // The reason we listen for pointer events in the bubble phase instead of the + // capture phase is to allow platform views and native text fields to receive + // the event first. This way, they can potentially handle the event and stop + // its propagation to prevent Flutter from receiving and handling it. + _testEach( + <_BasicEventContext>[ + _PointerEventContext(), + _MouseEventContext(), + _TouchEventContext(), + ], + 'event listeners are attached to the bubble phase', + (_BasicEventContext context) { + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + final DomElement child = createDomHTMLDivElement(); + flutterViewElement.append(child); + + final DomEventListener stopPropagationListener = createDomEventListener((DomEvent event) { + event.stopPropagation(); + }); + + // The event reaches `PointerBinding` as expected. + child.dispatchEvent(context.primaryDown()); + expect(packets, isNotEmpty); + packets.clear(); + + // The child stops propagation so the event doesn't reach `PointerBinding`. + final DomEvent event = context.primaryDown(); + child.addEventListener(event.type, stopPropagationListener); + child.dispatchEvent(event); + expect(packets, isEmpty); + packets.clear(); + + child.remove(); + }, + ); + _testEach<_BasicEventContext>( <_BasicEventContext>[ _PointerEventContext(), @@ -3087,385 +3128,6 @@ void testMain() { packets.clear(); }, ); - - group('ClickDebouncer', () { - _testClickDebouncer(); - }); -} - -typedef CapturedSemanticsEvent = ({ - ui.SemanticsAction type, - int nodeId, -}); - -void _testClickDebouncer() { - final DateTime testTime = DateTime(2018, 12, 17); - late List pointerPackets; - late List semanticsActions; - late _PointerEventContext context; - late PointerBinding binding; - - void testWithSemantics( - String description, - Future Function() body, - ) { - test( - description, - () async { - EngineSemanticsOwner.instance - ..debugOverrideTimestampFunction(() => testTime) - ..semanticsEnabled = true; - await body(); - EngineSemanticsOwner.instance.semanticsEnabled = false; - }, - ); - } - - setUp(() { - context = _PointerEventContext(); - pointerPackets = []; - semanticsActions = []; - ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { - for (final ui.PointerData data in packet.data) { - pointerPackets.add(data.change); - } - }; - EnginePlatformDispatcher.instance.onSemanticsActionEvent = (ui.SemanticsActionEvent event) { - semanticsActions.add((type: event.type, nodeId: event.nodeId)); - }; - binding = PointerBinding.instance!; - binding.debugOverrideDetector(context); - binding.clickDebouncer.reset(); - }); - - tearDown(() { - binding.clickDebouncer.reset(); - }); - - test('Forwards to framework when semantics is off', () { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, false); - expect(binding.clickDebouncer.isDebouncing, false); - flutterViewEmbedder.flutterViewElement.dispatchEvent(context.primaryDown()); - expect(pointerPackets, [ - ui.PointerChange.add, - ui.PointerChange.down, - ]); - expect(binding.clickDebouncer.isDebouncing, false); - expect(semanticsActions, isEmpty); - }); - - testWithSemantics('Forwards to framework when not debouncing', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(binding.clickDebouncer.isDebouncing, false); - - // This test DOM element is missing the `flt-tappable` attribute on purpose - // so that the debouncer does not debounce events and simply lets - // everything through. - final DomElement testElement = createDomElement('flt-semantics'); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - - testElement.dispatchEvent(context.primaryDown()); - testElement.dispatchEvent(context.primaryUp()); - expect(binding.clickDebouncer.isDebouncing, false); - - expect(pointerPackets, [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ]); - expect(semanticsActions, isEmpty); - }); - - testWithSemantics('Accumulates pointer events starting from pointerdown', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - - testElement.dispatchEvent(context.primaryDown()); - expect( - reason: 'Should start debouncing at first pointerdown', - binding.clickDebouncer.isDebouncing, - true, - ); - - testElement.dispatchEvent(context.primaryUp()); - expect( - reason: 'Should still be debouncing after pointerup', - binding.clickDebouncer.isDebouncing, - true, - ); - - expect( - reason: 'Events are withheld from the framework while debouncing', - pointerPackets, - [], - ); - expect( - binding.clickDebouncer.debugState!.target, - testElement, - ); - expect( - binding.clickDebouncer.debugState!.timer.isActive, - isTrue, - ); - expect( - binding.clickDebouncer.debugState!.queue.map((QueuedEvent e) => e.event.type), - ['pointerdown', 'pointerup'], - ); - - await Future.delayed(const Duration(milliseconds: 250)); - expect( - reason: 'Should stop debouncing after timer expires.', - binding.clickDebouncer.isDebouncing, - false, - ); - expect( - reason: 'Queued up events should be flushed to the framework.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], - ); - expect(semanticsActions, isEmpty); - }); - - testWithSemantics('Flushes events to framework when target changes', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - - testElement.dispatchEvent(context.primaryDown()); - expect( - reason: 'Should start debouncing at first pointerdown', - binding.clickDebouncer.isDebouncing, - true, - ); - - final DomElement newTarget = createDomElement('flt-semantics'); - newTarget.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(newTarget); - newTarget.dispatchEvent(context.primaryUp()); - - expect( - reason: 'Should stop debouncing when target changes.', - binding.clickDebouncer.isDebouncing, - false, - ); - expect( - reason: 'The state should be cleaned up after stopping debouncing.', - binding.clickDebouncer.debugState, - isNull, - ); - expect( - reason: 'Queued up events should be flushed to the framework.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], - ); - expect(semanticsActions, isEmpty); - }); - - testWithSemantics('Forwards click to framework when not debouncing but listening', () async { - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); - - binding.clickDebouncer.onClick(click, 42, true); - expect(binding.clickDebouncer.isDebouncing, false); - expect(pointerPackets, isEmpty); - expect(semanticsActions, [ - (type: ui.SemanticsAction.tap, nodeId: 42) - ]); - }); - - testWithSemantics('Forwards click to framework when debouncing and listening', () async { - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - testElement.dispatchEvent(context.primaryDown()); - expect(binding.clickDebouncer.isDebouncing, true); - - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); - - binding.clickDebouncer.onClick(click, 42, true); - expect(pointerPackets, isEmpty); - expect(semanticsActions, [ - (type: ui.SemanticsAction.tap, nodeId: 42) - ]); - }); - - testWithSemantics('Dedupes click if debouncing but not listening', () async { - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - testElement.dispatchEvent(context.primaryDown()); - expect(binding.clickDebouncer.isDebouncing, true); - - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); - - binding.clickDebouncer.onClick(click, 42, false); - expect( - reason: 'When tappable declares that it is not listening to click events ' - 'the debouncer flushes the pointer events to the framework and ' - 'lets it sort it out.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ], - ); - expect(semanticsActions, isEmpty); - }); - - testWithSemantics('Dedupes click if pointer down/up flushed recently', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - - testElement.dispatchEvent(context.primaryDown()); - - // Simulate the user holding the pointer down for some time before releasing, - // such that the pointerup event happens close to timer expiration. This - // will create the situation that the click event arrives just after the - // pointerup is flushed. Forwarding the click to the framework would look - // like a double-click, so the click event is deduped. - await Future.delayed(const Duration(milliseconds: 190)); - - testElement.dispatchEvent(context.primaryUp()); - expect(binding.clickDebouncer.isDebouncing, true); - expect( - reason: 'Timer has not expired yet', - pointerPackets, isEmpty, - ); - - // Wait for the timer to expire to make sure pointer events are flushed. - await Future.delayed(const Duration(milliseconds: 11)); - - expect( - reason: 'Queued up events should be flushed to the framework because the ' - 'time expired before the click event arrived.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], - ); - - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); - binding.clickDebouncer.onClick(click, 42, true); - - expect( - reason: 'Because the DOM click event was deduped.', - semanticsActions, - isEmpty, - ); - }); - - - testWithSemantics('Forwards click if enough time passed after the last flushed pointerup', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(binding.clickDebouncer.isDebouncing, false); - - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - flutterViewEmbedder.semanticsHostElement!.appendChild(testElement); - - testElement.dispatchEvent(context.primaryDown()); - - // Simulate the user holding the pointer down for some time before releasing, - // such that the pointerup event happens close to timer expiration. This - // makes it possible for the click to arrive early. However, this test in - // particular will delay the click to check that the delay is checked - // correctly. The inverse situation was already tested in the previous test. - await Future.delayed(const Duration(milliseconds: 190)); - - testElement.dispatchEvent(context.primaryUp()); - expect(binding.clickDebouncer.isDebouncing, true); - expect( - reason: 'Timer has not expired yet', - pointerPackets, isEmpty, - ); - - // Wait for the timer to expire to make sure pointer events are flushed. - await Future.delayed(const Duration(milliseconds: 100)); - - expect( - reason: 'Queued up events should be flushed to the framework because the ' - 'time expired before the click event arrived.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], - ); - - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); - binding.clickDebouncer.onClick(click, 42, true); - - expect( - reason: 'The DOM click should still be sent to the framework because it ' - 'happened far enough from the last pointerup that it is unlikely ' - 'to be a duplicate.', - semanticsActions, - [ - (type: ui.SemanticsAction.tap, nodeId: 42) - ], - ); - }); } class MockSafariPointerEventWorkaround implements SafariPointerEventWorkaround { @@ -3606,7 +3268,7 @@ mixin _ButtonedEventMixin on _BasicEventContext { }); // timeStamp can't be set in the constructor, need to override the getter. if (timeStamp != null) { - js_util.callMethod( + js_util.callMethod( objectConstructor, 'defineProperty', [ @@ -3713,6 +3375,7 @@ class _TouchEventContext extends _BasicEventContext return createDomTouchEvent( eventType, { + 'bubbles': true, 'changedTouches': touches .map( (_TouchDetails details) => _createTouch( @@ -3929,6 +3592,7 @@ class _PointerEventContext extends _BasicEventContext String? pointerType, }) { return createDomPointerEvent('pointerdown', { + 'bubbles': true, 'pointerId': pointer, 'button': button, 'buttons': buttons, @@ -3983,6 +3647,7 @@ class _PointerEventContext extends _BasicEventContext String? pointerType, }) { return createDomPointerEvent('pointermove', { + 'bubbles': true, 'pointerId': pointer, 'button': button, 'buttons': buttons, @@ -4018,6 +3683,7 @@ class _PointerEventContext extends _BasicEventContext String? pointerType, }) { return createDomPointerEvent('pointerleave', { + 'bubbles': true, 'pointerId': pointer, 'button': button, 'buttons': buttons, @@ -4082,6 +3748,7 @@ class _PointerEventContext extends _BasicEventContext return touches .map((_TouchDetails details) => createDomPointerEvent('pointercancel', { + 'bubbles': true, 'pointerId': details.pointer, 'button': 0, 'buttons': 0, diff --git a/lib/web_ui/test/engine/scene_view_test.dart b/lib/web_ui/test/engine/scene_view_test.dart index 0b475d8842824..9c593e7421f77 100644 --- a/lib/web_ui/test/engine/scene_view_test.dart +++ b/lib/web_ui/test/engine/scene_view_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:js_interop'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -19,25 +18,31 @@ void main() { } class StubPictureRenderer implements PictureRenderer { - final DomCanvasElement scratchCanvasElement = createDomCanvasElement( - width: 500, height: 500 - ); + final DomCanvasElement scratchCanvasElement = + createDomCanvasElement(width: 500, height: 500); @override Future renderPicture(ScenePicture picture) async { + renderedPictures.add(picture); final ui.Rect cullRect = picture.cullRect; - final DomImageBitmap bitmap = (await createImageBitmap( - scratchCanvasElement as JSAny, - (x: 0, y: 0, width: cullRect.width.toInt(), height: cullRect.height.toInt()) - ).toDart)! as DomImageBitmap; + final DomImageBitmap bitmap = (await createSizedImageBitmap( + scratchCanvasElement, + 0, + 0, + cullRect.width.toInt(), + cullRect.height.toInt()))!; return bitmap; } + + List renderedPictures = []; } void testMain() { late EngineSceneView sceneView; + late StubPictureRenderer stubPictureRenderer; setUp(() { - sceneView = EngineSceneView(StubPictureRenderer()); + stubPictureRenderer = StubPictureRenderer(); + sceneView = EngineSceneView(stubPictureRenderer); }); test('SceneView places canvas according to device-pixel ratio', () async { @@ -58,9 +63,11 @@ void testMain() { final List children = sceneElement.children.toList(); expect(children.length, 1); final DomElement containerElement = children.first; - expect(containerElement.tagName, equalsIgnoringCase('flt-canvas-container')); + expect( + containerElement.tagName, equalsIgnoringCase('flt-canvas-container')); - final List containerChildren = containerElement.children.toList(); + final List containerChildren = + containerElement.children.toList(); expect(containerChildren.length, 1); final DomElement canvasElement = containerChildren.first; final DomCSSStyleDeclaration style = canvasElement.style; @@ -72,16 +79,15 @@ void testMain() { debugOverrideDevicePixelRatio(null); }); - test('SceneView places canvas according to device-pixel ratio', () async { + test('SceneView places platform view according to device-pixel ratio', () async { debugOverrideDevicePixelRatio(2.0); final PlatformView platformView = PlatformView( - 1, - const ui.Size(100, 120), - const PlatformViewStyling( - position: PlatformViewPosition.offset(ui.Offset(50, 80)), - ) - ); + 1, + const ui.Size(100, 120), + const PlatformViewStyling( + position: PlatformViewPosition.offset(ui.Offset(50, 80)), + )); final EngineRootLayer rootLayer = EngineRootLayer(); rootLayer.slices.add(PlatformViewSlice([platformView], null)); final EngineScene scene = EngineScene(rootLayer); @@ -91,7 +97,8 @@ void testMain() { final List children = sceneElement.children.toList(); expect(children.length, 1); final DomElement containerElement = children.first; - expect(containerElement.tagName, equalsIgnoringCase('flt-platform-view-slot')); + expect( + containerElement.tagName, equalsIgnoringCase('flt-platform-view-slot')); final DomCSSStyleDeclaration style = containerElement.style; expect(style.left, '25px'); @@ -101,4 +108,28 @@ void testMain() { debugOverrideDevicePixelRatio(null); }); + + test('SceneView always renders most recent picture and skips intermediate pictures', () async { + final List pictures = []; + final List> renderFutures = >[]; + for (int i = 1; i < 20; i++) { + final StubPicture picture = StubPicture(const ui.Rect.fromLTWH( + 50, + 80, + 100, + 120, + )); + pictures.add(picture); + final EngineRootLayer rootLayer = EngineRootLayer(); + rootLayer.slices.add(PictureSlice(picture)); + final EngineScene scene = EngineScene(rootLayer); + renderFutures.add(sceneView.renderScene(scene)); + } + await Future.wait(renderFutures); + + // Should just render the first and last pictures and skip the one inbetween. + expect(stubPictureRenderer.renderedPictures.length, 2); + expect(stubPictureRenderer.renderedPictures.first, pictures.first); + expect(stubPictureRenderer.renderedPictures.last, pictures.last); + }); } diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index e5f652ea623ad..88cc01db8ce40 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -92,6 +92,9 @@ void runSemanticsTests() { group('focusable', () { _testFocusable(); }); + group('link', () { + _testLink(); + }); } void _testRoleManagerLifecycle() { @@ -337,7 +340,11 @@ void _testEngineSemanticsOwner() { expect(placeholder.isConnected, isFalse); }); - void renderSemantics({String? label, String? tooltip}) { + void renderSemantics({String? label, String? tooltip, Set flags = const {}}) { + int flagValues = 0; + for (final ui.SemanticsFlag flag in flags) { + flagValues = flagValues | flag.index; + } final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); updateNode( builder, @@ -351,6 +358,7 @@ void _testEngineSemanticsOwner() { id: 1, label: label ?? '', tooltip: tooltip ?? '', + flags: flagValues, transform: Matrix4.identity().toFloat64(), rect: const ui.Rect.fromLTRB(0, 0, 20, 20), ); @@ -377,7 +385,7 @@ void _testEngineSemanticsOwner() { expectSemanticsTree(''' - + '''); @@ -387,7 +395,7 @@ void _testEngineSemanticsOwner() { expectSemanticsTree(''' - + '''); @@ -397,9 +405,48 @@ void _testEngineSemanticsOwner() { expectSemanticsTree(''' - + + +'''); + + semantics().semanticsEnabled = false; + }); + + test('can switch role', () async { + semantics().semanticsEnabled = true; + + // Create + renderSemantics(label: 'Hello'); + + Map tree = semantics().debugSemanticsTree!; + expect(tree.length, 2); + expect(tree[1]!.element.tagName.toLowerCase(), 'flt-semantics'); + expect(tree[1]!.id, 1); + expect(tree[1]!.label, 'Hello'); + final DomElement existingParent = tree[1]!.element.parent!; + + expectSemanticsTree(''' + + + + +'''); + + // Update + renderSemantics(label: 'Hello', flags: { ui.SemanticsFlag.isLink }); + + tree = semantics().debugSemanticsTree!; + expect(tree.length, 2); + expect(tree[1]!.id, 1); + expect(tree[1]!.label, 'Hello'); + expect(tree[1]!.element.tagName.toLowerCase(), 'a'); + expectSemanticsTree(''' + + + '''); + expect(existingParent, tree[1]!.element.parent); semantics().semanticsEnabled = false; }); @@ -430,7 +477,7 @@ void _testEngineSemanticsOwner() { expectSemanticsTree(''' - + '''); @@ -440,7 +487,7 @@ void _testEngineSemanticsOwner() { expectSemanticsTree(''' - + '''); @@ -1388,7 +1435,7 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); final SemanticsObject node = semantics().debugSemanticsTree![0]!; @@ -1421,7 +1468,7 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); final DomHTMLInputElement input = @@ -1454,7 +1501,7 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); final DomHTMLInputElement input = @@ -1489,7 +1536,7 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; @@ -1632,7 +1679,7 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); final SemanticsObject node = semantics().debugSemanticsTree![0]!; @@ -1690,7 +1737,7 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; @@ -1716,7 +1763,7 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; @@ -1766,7 +1813,7 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; @@ -1793,7 +1840,7 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; @@ -1845,7 +1892,7 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; @@ -1918,7 +1965,7 @@ void _testTappable() { tester.apply(); expectSemanticsTree(''' - + '''); final SemanticsObject node = semantics().debugSemanticsTree![0]!; @@ -1979,14 +2026,14 @@ void _testTappable() { ''); updateTappable(enabled: true); - expectSemanticsTree(''); + expectSemanticsTree(''); updateTappable(enabled: false); expectSemanticsTree( ''); updateTappable(enabled: true); - expectSemanticsTree(''); + expectSemanticsTree(''); semantics().semanticsEnabled = false; }); @@ -2623,11 +2670,11 @@ void _testDialog() { tester.apply(); expectSemanticsTree(''' - + - + @@ -2716,7 +2763,7 @@ void _testDialog() { - + @@ -2853,9 +2900,9 @@ void _testFocusable() { } expectSemanticsTree(''' - + - + '''); @@ -2892,6 +2939,28 @@ void _testFocusable() { }); } +void _testLink() { + test('nodes with link: true creates anchor tag', () { + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; + + SemanticsObject pumpSemantics() { + final SemanticsTester tester = SemanticsTester(semantics()); + tester.updateNode( + id: 0, + isLink: true, + rect: const ui.Rect.fromLTRB(0, 0, 100, 50), + ); + tester.apply(); + return tester.getSemanticsObject(0); + } + + final SemanticsObject object = pumpSemantics(); + expect(object.element.tagName.toLowerCase(), 'a'); + }); +} + /// A facade in front of [ui.SemanticsUpdateBuilder.updateNode] that /// supplies default values for semantics attributes. void updateNode( diff --git a/lib/web_ui/test/engine/text_editing_test.dart b/lib/web_ui/test/engine/text_editing_test.dart index 1c3df325cd450..a6e5f02c0a33b 100644 --- a/lib/web_ui/test/engine/text_editing_test.dart +++ b/lib/web_ui/test/engine/text_editing_test.dart @@ -2614,6 +2614,12 @@ Future testMain() async { expect(testInputElement.getAttribute('autocomplete'),'on'); expect(testInputElement.placeholder, 'enter your password'); }); + + // Regression test for https://github.com/flutter/flutter/issues/135542 + test('autofill with middleName hint', () { + expect(BrowserAutofillHints.instance.flutterToEngine('middleName'), + 'additional-name'); + }); }); group('EditingState', () { diff --git a/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart b/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart index 850a2a66a2376..b28fbc3d7fe1c 100644 --- a/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart @@ -122,60 +122,4 @@ void doTests() { reason: 'Should be injected `nextTo` the passed element.'); }); }); - - group('context menu', () { - setUp(() { - target = createDomElement('this-is-the-target'); - domDocument.body!.append(target); - strategy = CustomElementEmbeddingStrategy(target); - strategy.initialize(); - }); - - tearDown(() { - target.remove(); - }); - - test('disableContextMenu and enableContextMenu can toggle the context menu', () { - // When the app starts, contextmenu events are not prevented. - DomEvent event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - - // Disabling the context menu causes contextmenu events to be prevented. - strategy.disableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isTrue); - - // Disabling again has no effect. - strategy.disableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isTrue); - - // Dispatching on a DOM element outside of target's subtree has no effect. - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - domDocument.body!.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - - // Enabling the context menu means that contextmenu events are back to not - // being prevented. - strategy.enableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - - // Enabling again has no effect. - strategy.enableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - }); - }); } diff --git a/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart b/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart index f13cf67c4cb4b..f254455ecd58a 100644 --- a/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart @@ -11,7 +11,6 @@ import 'package:ui/src/engine/dom.dart'; import 'package:ui/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart'; import 'package:ui/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart'; import 'package:ui/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart'; -import 'package:ui/src/engine/view_embedder/hot_restart_cache_handler.dart'; void main() { internalBootstrapBrowserTest(() => doTests); @@ -35,24 +34,4 @@ void doTests() { expect(strategy, isA()); }); }); - - group('registerElementForCleanup', () { - test('stores elements in a global domCache', () async { - final EmbeddingStrategy strategy = EmbeddingStrategy.create(); - - final DomElement toBeCached = createDomElement('some-element-to-cache'); - final DomElement other = createDomElement('other-element-to-cache'); - final DomElement another = createDomElement('another-element-to-cache'); - - strategy.registerElementForCleanup(toBeCached); - strategy.registerElementForCleanup(other); - strategy.registerElementForCleanup(another); - - final List cache = hotRestartStore!; - - expect(cache, hasLength(3)); - expect(cache.first, toBeCached); - expect(cache.last, another); - }); - }); } diff --git a/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart b/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart index 70f2fbd1e92bd..5bf979105e142 100644 --- a/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart @@ -130,56 +130,4 @@ void doTests() { reason: 'Should be injected `nextTo` the passed element.'); }); }); - - group('context menu', () { - setUp(() { - strategy = FullPageEmbeddingStrategy(); - strategy.initialize(); - }); - - test('disableContextMenu and enableContextMenu can toggle the context menu', () { - final FullPageEmbeddingStrategy strategy = FullPageEmbeddingStrategy(); - - // When the app starts, contextmenu events are not prevented. - DomEvent event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - - // Disabling the context menu causes contextmenu events to be prevented. - strategy.disableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isTrue); - - // Disabling again has no effect. - strategy.disableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isTrue); - - // Dispatching on the document body is still disabled. - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - domDocument.body!.dispatchEvent(event); - expect(event.defaultPrevented, isTrue); - - // Enabling the context menu means that contextmenu events are back to not - // being prevented. - strategy.enableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - - // Enabling again has no effect. - strategy.enableContextMenu(); - event = createDomEvent('Event', 'contextmenu'); - expect(event.defaultPrevented, isFalse); - target.dispatchEvent(event); - expect(event.defaultPrevented, isFalse); - }); - }); } diff --git a/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart b/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart index 7525b571d9acc..934718ce75e70 100644 --- a/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart +++ b/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart @@ -5,31 +5,58 @@ @TestOn('browser') library; +import 'dart:js_interop'; + import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine/dom.dart'; import 'package:ui/src/engine/view_embedder/hot_restart_cache_handler.dart'; +@JS('window.__flutterState') +external JSArray? get _jsHotRestartStore; + +@JS('window.__flutterState') +external set _jsHotRestartStore(JSArray? nodes); + void main() { internalBootstrapBrowserTest(() => doTests); } void doTests() { - group('Constructor', () { - test('Creates a cache in the JS environment', () async { - final HotRestartCacheHandler cache = HotRestartCacheHandler(); + tearDown(() { + _jsHotRestartStore = null; + }); - expect(cache, isNotNull); + group('registerElementForCleanup', () { + test('stores elements in a global cache', () async { + final DomElement toBeCached = createDomElement('some-element-to-cache'); + final DomElement other = createDomElement('other-element-to-cache'); + final DomElement another = createDomElement('another-element-to-cache'); + + registerElementForCleanup(toBeCached); + registerElementForCleanup(other); + registerElementForCleanup(another); + + expect(_jsHotRestartStore!.toDart, [ + toBeCached, + other, + another, + ]); + }); + }); - final List? domCache = hotRestartStore; + group('HotRestartCacheHandler Constructor', () { + test('Creates a cache in the JS environment', () async { + HotRestartCacheHandler(); - expect(domCache, isNotNull); - expect(domCache, isEmpty); + expect(_jsHotRestartStore, isNotNull); + // For dart2wasm, we have to check the length this way. + expect(_jsHotRestartStore!.length, 0.toJS); }); }); - group('registerElement', () { - HotRestartCacheHandler? cache; + group('HotRestartCacheHandler.registerElement', () { + late HotRestartCacheHandler cache; setUp(() { cache = HotRestartCacheHandler(); @@ -37,22 +64,18 @@ void doTests() { test('Registers an element in the DOM cache', () async { final DomElement element = createDomElement('for-test'); - cache!.registerElement(element); + cache.registerElement(element); - final List? domCache = hotRestartStore; - expect(domCache, hasLength(1)); - expect(domCache!.last, element); + expect(_jsHotRestartStore!.toDart, [element]); }); test('Registers elements in the DOM cache', () async { final DomElement element = createDomElement('for-test'); domDocument.body!.append(element); - cache!.registerElement(element); + cache.registerElement(element); - final List? domCache = hotRestartStore; - expect(domCache, hasLength(1)); - expect(domCache!.last, element); + expect(_jsHotRestartStore!.toDart, [element]); }); test('Clears registered elements from the DOM and the cache upon restart', @@ -62,7 +85,7 @@ void doTests() { domDocument.body!.append(element); domDocument.body!.append(element2); - cache!.registerElement(element); + cache.registerElement(element); expect(element.isConnected, isTrue); expect(element2.isConnected, isTrue); @@ -70,8 +93,8 @@ void doTests() { // Simulate a hot restart... cache = HotRestartCacheHandler(); - final List? domCache = hotRestartStore; - expect(domCache, hasLength(0)); + // For dart2wasm, we have to check the length this way. + expect(_jsHotRestartStore!.length, 0.toJS); expect(element.isConnected, isFalse); // Removed expect(element2.isConnected, isTrue); }); diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart index df39afb17adde..ff93016a59056 100644 --- a/lib/web_ui/test/engine/window_test.dart +++ b/lib/web_ui/test/engine/window_test.dart @@ -358,25 +358,25 @@ Future testMain() async { unlockCount = 0; expect(await sendSetPreferredOrientations(['DeviceOrientation.portraitUp']), isTrue); - expect(lockCalls, [FlutterViewEmbedder.orientationLockTypePortraitPrimary]); + expect(lockCalls, [ScreenOrientation.lockTypePortraitPrimary]); expect(unlockCount, 0); lockCalls.clear(); unlockCount = 0; expect(await sendSetPreferredOrientations(['DeviceOrientation.portraitDown']), isTrue); - expect(lockCalls, [FlutterViewEmbedder.orientationLockTypePortraitSecondary]); + expect(lockCalls, [ScreenOrientation.lockTypePortraitSecondary]); expect(unlockCount, 0); lockCalls.clear(); unlockCount = 0; expect(await sendSetPreferredOrientations(['DeviceOrientation.landscapeLeft']), isTrue); - expect(lockCalls, [FlutterViewEmbedder.orientationLockTypeLandscapePrimary]); + expect(lockCalls, [ScreenOrientation.lockTypeLandscapePrimary]); expect(unlockCount, 0); lockCalls.clear(); unlockCount = 0; expect(await sendSetPreferredOrientations(['DeviceOrientation.landscapeRight']), isTrue); - expect(lockCalls, [FlutterViewEmbedder.orientationLockTypeLandscapeSecondary]); + expect(lockCalls, [ScreenOrientation.lockTypeLandscapeSecondary]); expect(unlockCount, 0); lockCalls.clear(); unlockCount = 0; @@ -389,7 +389,7 @@ Future testMain() async { simulateError = true; expect(await sendSetPreferredOrientations(['DeviceOrientation.portraitDown']), isFalse); - expect(lockCalls, [FlutterViewEmbedder.orientationLockTypePortraitSecondary]); + expect(lockCalls, [ScreenOrientation.lockTypePortraitSecondary]); expect(unlockCount, 0); js_util.setProperty(domWindow, 'screen', original); diff --git a/lib/web_ui/test/html/paragraph/general_golden_test.dart b/lib/web_ui/test/html/paragraph/general_golden_test.dart index 34325eb7b7ab1..4c8b5e153a06b 100644 --- a/lib/web_ui/test/html/paragraph/general_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/general_golden_test.dart @@ -337,6 +337,28 @@ Future testMain() async { return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing'); }); + test('letter-spacing Thai', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const String yes = '\u0e43\u0e0a\u0e48'; + const String no = '\u0e44\u0e21\u0e48'; + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 36), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('$yes $no '); + builder.pushStyle(EngineTextStyle.only(color: green, letterSpacing: 1)); + builder.addText('$yes $no '); + builder.pushStyle(EngineTextStyle.only(color: red, letterSpacing: 3)); + builder.addText('$yes $no'); + }, + )..layout(constrain(double.infinity)); + canvas.drawParagraph(paragraph, const Offset(20, 20)); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing_thai'); + }); + test('draws text decorations', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); final List decorationStyles = [ diff --git a/lib/web_ui/test/ui/fallback_fonts_golden_test.dart b/lib/web_ui/test/ui/fallback_fonts_golden_test.dart index 5822591aa9b70..a0ac847838c6d 100644 --- a/lib/web_ui/test/ui/fallback_fonts_golden_test.dart +++ b/lib/web_ui/test/ui/fallback_fonts_golden_test.dart @@ -165,59 +165,55 @@ void testMain() { // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 }); - // Regression test for https://github.com/flutter/flutter/issues/75836 - // When we had this bug our font fallback resolution logic would end up in an - // infinite loop and this test would freeze and time out. - test( - 'Can find fonts for two adjacent unmatched code points from different fonts', - () async { + /// Attempts to render [text] and verifies that [expectedFamilies] are downloaded. + /// + /// Then it does the same, but asserts that the families aren't downloaded again + /// (because they already exist in memory). + Future checkDownloadedFamiliesForString(String text, List expectedFamilies) async { // Try rendering text that requires fallback fonts, initially before the fonts are loaded. - ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('ヽಠ'); + pb.addText(text); pb.build().layout(const ui.ParagraphConstraints(width: 1000)); await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); expect( downloadedFontFamilies, - [ - 'Noto Sans SC', - 'Noto Sans Kannada', - ], + expectedFamilies, ); // Do the same thing but this time with loaded fonts. downloadedFontFamilies.clear(); pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('ヽಠ'); + pb.addText(text); pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); expect(downloadedFontFamilies, isEmpty); + } + + // Regression test for https://github.com/flutter/flutter/issues/75836 + // When we had this bug our font fallback resolution logic would end up in an + // infinite loop and this test would freeze and time out. + test( + 'can find fonts for two adjacent unmatched code points from different fonts', + () async { + await checkDownloadedFamiliesForString('ヽಠ', [ + 'Noto Sans SC', + 'Noto Sans Kannada', + ]); }); test('can find glyph for 2/3 symbol', () async { - // Try rendering text that requires fallback fonts, initially before the fonts are loaded. - - ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('⅔'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - expect( - downloadedFontFamilies, - [ - 'Noto Sans', - ], - ); - - // Do the same thing but this time with loaded fonts. - downloadedFontFamilies.clear(); - pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('⅔'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + await checkDownloadedFamiliesForString('⅔', [ + 'Noto Sans', + ]); + }); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - expect(downloadedFontFamilies, isEmpty); + // https://github.com/flutter/devtools/issues/6149 + test('can find glyph for treble clef', () async { + await checkDownloadedFamiliesForString('𝄞', [ + 'Noto Music', + ]); }); test('findMinimumFontsForCodePoints for all supported code points', () async { @@ -248,8 +244,9 @@ void testMain() { expect( testedFonts, unorderedEquals({ - 'Noto Sans', 'Noto Color Emoji', + 'Noto Music', + 'Noto Sans', 'Noto Sans Symbols', 'Noto Sans Symbols 2', 'Noto Sans Adlam', diff --git a/lib/web_ui/test/ui/filters_test.dart b/lib/web_ui/test/ui/filters_test.dart index de8cbec340644..a2151002c8e92 100644 --- a/lib/web_ui/test/ui/filters_test.dart +++ b/lib/web_ui/test/ui/filters_test.dart @@ -78,6 +78,16 @@ Future testMain() async { await matchGoldenFile('ui_filter_matrix_imagefilter.png', region: region); }); + test('resizing matrix filter', () async { + await drawTestImageWithPaint(ui.Paint() + ..imageFilter = ui.ImageFilter.matrix( + Matrix4.diagonal3Values(0.5, 0.5, 1).toFloat64(), + filterQuality: ui.FilterQuality.high, + )); + await matchGoldenFile('ui_filter_matrix_imagefilter_scaled.png', + region: region); + }); + test('composed filters', () async { final ui.ImageFilter filter = ui.ImageFilter.compose( outer: ui.ImageFilter.matrix( diff --git a/lib/web_ui/test/ui/image_golden_test.dart b/lib/web_ui/test/ui/image_golden_test.dart index fccb86c8b2951..7c84839466606 100644 --- a/lib/web_ui/test/ui/image_golden_test.dart +++ b/lib/web_ui/test/ui/image_golden_test.dart @@ -318,8 +318,18 @@ Future testMain() async { image.src = url; await completer.future; - final DomImageBitmap bitmap = (await createImageBitmap(image as JSAny).toDart)! as DomImageBitmap; - return renderer.createImageFromImageBitmap(bitmap); + final DomImageBitmap bitmap = (await createImageBitmap(image as JSAny))!; + + expect(bitmap.width.toDartInt, 150); + expect(bitmap.height.toDartInt, 150); + final ui.Image uiImage = await renderer.createImageFromImageBitmap(bitmap); + + if (isSkwasm) { + // Skwasm transfers the bitmap to the web worker, so it should be disposed/consumed. + expect(bitmap.width.toDartInt, 0); + expect(bitmap.height.toDartInt, 0); + } + return uiImage; }); } diff --git a/lib/web_ui/test/ui/platform_view_test.dart b/lib/web_ui/test/ui/platform_view_test.dart index 7c934caf6087b..be7f643186e60 100644 --- a/lib/web_ui/test/ui/platform_view_test.dart +++ b/lib/web_ui/test/ui/platform_view_test.dart @@ -38,7 +38,7 @@ Future testMain() async { }); tearDown(() { - platformViewManager.debugClear(); + PlatformViewManager.instance.debugClear(); }); test('picture + overlapping platformView', () async { diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index 48715c5cc2eec..23e59cf93b0f2 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -11,6 +11,7 @@ #include "flutter/common/settings.h" #include "flutter/fml/compiler_specific.h" +#include "flutter/fml/cpu_affinity.h" #include "flutter/fml/logging.h" #include "flutter/fml/mapping.h" #include "flutter/fml/size.h" @@ -94,9 +95,8 @@ static const char* kDartEndlessTraceBufferArgs[]{ "--timeline_recorder=endless", }; -// This is the same as --timeline_recorder=systrace. static const char* kDartSystraceTraceBufferArgs[] = { - "--systrace_timeline", + "--timeline_recorder=systrace", }; static std::string DartFileRecorderArgs(const std::string& path) { @@ -219,22 +219,22 @@ static std::vector ProfilingFlags(bool enable_profiling) { // flags. if (enable_profiling) { return { - // This is the default. But just be explicit. - "--profiler", - // This instructs the profiler to walk C++ frames, and to include - // them in the profile. - "--profile-vm", + // This is the default. But just be explicit. + "--profiler", + // This instructs the profiler to walk C++ frames, and to include + // them in the profile. + "--profile-vm", #if FML_OS_IOS && FML_ARCH_CPU_ARM_FAMILY && FML_ARCH_CPU_ARMEL - // Set the profiler interrupt period to 500Hz instead of the - // default 1000Hz on 32-bit iOS devices to reduce average and worst - // case frame build times. - // - // Note: profile_period is time in microseconds between sampling - // events, not frequency. Frequency is calculated 1/period (or - // 1,000,000 / 2,000 -> 500Hz in this case). - "--profile_period=2000", + // Set the profiler interrupt period to 500Hz instead of the + // default 1000Hz on 32-bit iOS devices to reduce average and worst + // case frame build times. + // + // Note: profile_period is time in microseconds between sampling + // events, not frequency. Frequency is calculated 1/period (or + // 1,000,000 / 2,000 -> 500Hz in this case). + "--profile_period=2000", #else - "--profile_period=1000", + "--profile_period=1000", #endif // FML_OS_IOS && FML_ARCH_CPU_ARM_FAMILY && FML_ARCH_CPU_ARMEL }; } else { @@ -285,7 +285,9 @@ size_t DartVM::GetVMLaunchCount() { DartVM::DartVM(const std::shared_ptr& vm_data, std::shared_ptr isolate_name_server) : settings_(vm_data->GetSettings()), - concurrent_message_loop_(fml::ConcurrentMessageLoop::Create()), + concurrent_message_loop_(fml::ConcurrentMessageLoop::Create( + fml::EfficiencyCoreCount().value_or( + std::thread::hardware_concurrency()))), skia_concurrent_executor_( [runner = concurrent_message_loop_->GetTaskRunner()]( const fml::closure& work) { runner->PostTask(work); }), diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 9dc6a944de7cc..5ee24c9985dad 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -341,15 +341,14 @@ void RuntimeController::ScheduleFrame() { } // |PlatformConfigurationClient| -void RuntimeController::Render(Scene* scene) { - // TODO(dkwingsmt): Currently only supports a single window. - int64_t view_id = kFlutterImplicitViewId; +void RuntimeController::Render(int64_t view_id, Scene* scene) { const ViewportMetrics* view_metrics = UIDartState::Current()->platform_configuration()->GetMetrics(view_id); if (view_metrics == nullptr) { return; } - client_.Render(scene->takeLayerTree(view_metrics->physical_width, + client_.Render(view_id, + scene->takeLayerTree(view_metrics->physical_width, view_metrics->physical_height), view_metrics->device_pixel_ratio); } diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 7a65f2b074282..7a29a3616e3cb 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -658,7 +658,7 @@ class RuntimeController : public PlatformConfigurationClient { void ScheduleFrame() override; // |PlatformConfigurationClient| - void Render(Scene* scene) override; + void Render(int64_t view_id, Scene* scene) override; // |PlatformConfigurationClient| void UpdateSemantics(SemanticsUpdate* update) override; diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index a036d4723cb34..809fa46ac0aff 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -23,9 +23,10 @@ class RuntimeDelegate { public: virtual std::string DefaultRouteName() = 0; - virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; + virtual void ScheduleFrame(bool regenerate_layer_trees = true) = 0; - virtual void Render(std::unique_ptr layer_tree, + virtual void Render(int64_t view_id, + std::unique_ptr layer_tree, float device_pixel_ratio) = 0; virtual void UpdateSemantics(SemanticsNodeUpdates update, diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index c5a4db91e4333..31d89012a58d3 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -143,6 +143,7 @@ source_set("common") { "//flutter/fml", "//flutter/lib/ui", "//flutter/runtime", + "//flutter/shell/common:base64", "//flutter/shell/profiling", "//third_party/dart/runtime:dart_api", "//third_party/skia", @@ -158,6 +159,15 @@ source_set("common") { } } +# These are in their own source_set to avoid a dependency cycle with //common/graphics +source_set("base64") { + sources = [ + "base64.cc", + "base64.h", + ] + deps = [ "//flutter/fml" ] +} + template("shell_host_executable") { executable(target_name) { testonly = true @@ -293,6 +303,7 @@ if (enable_unittests) { sources = [ "animator_unittests.cc", + "base64_unittests.cc", "context_options_unittests.cc", "dl_op_spy_unittests.cc", "engine_unittests.cc", @@ -312,6 +323,7 @@ if (enable_unittests) { ":shell_unittests_fixtures", "//flutter/assets", "//flutter/common/graphics", + "//flutter/shell/common:base64", "//flutter/shell/profiling:profiling_unittests", "//flutter/shell/version", "//flutter/testing:fixture_test", diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 29c21c1ae5592..1696e5009db24 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -4,6 +4,7 @@ #include "flutter/shell/common/animator.h" +#include "flutter/common/constants.h" #include "flutter/flow/frame_timings.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" @@ -28,12 +29,12 @@ Animator::Animator(Delegate& delegate, task_runners_(task_runners), waiter_(std::move(waiter)), #if SHELL_ENABLE_METAL - layer_tree_pipeline_(std::make_shared(2)), + layer_tree_pipeline_(std::make_shared(2)), #else // SHELL_ENABLE_METAL // TODO(dnfield): We should remove this logic and set the pipeline depth // back to 2 in this case. See // https://github.com/flutter/engine/pull/9132 for discussion. - layer_tree_pipeline_(std::make_shared( + layer_tree_pipeline_(std::make_shared( task_runners.GetPlatformTaskRunner() == task_runners.GetRasterTaskRunner() ? 1 @@ -59,6 +60,10 @@ void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) { void Animator::BeginFrame( std::unique_ptr frame_timings_recorder) { + // Both frame_timings_recorder_ and layer_trees_tasks_ must be empty if not + // between BeginFrame and EndFrame. + FML_DCHECK(frame_timings_recorder_ == nullptr); + FML_DCHECK(layer_trees_tasks_.empty()); TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_request_number_); frame_request_number_++; @@ -84,7 +89,7 @@ void Animator::BeginFrame( } frame_scheduled_ = false; - regenerate_layer_tree_ = false; + regenerate_layer_trees_ = false; pending_frame_semaphore_.Signal(); if (!producer_continuation_) { @@ -111,6 +116,33 @@ void Animator::BeginFrame( dart_frame_deadline_ = frame_target_time.ToEpochDelta(); uint64_t frame_number = frame_timings_recorder_->GetFrameNumber(); delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number); +} + +void Animator::EndFrame() { + FML_CHECK(frame_timings_recorder_ != nullptr); + if (!layer_trees_tasks_.empty()) { + // The build is completed in OnAnimatorBeginFrame. + frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now()); + + delegate_.OnAnimatorUpdateLatestFrameTargetTime( + frame_timings_recorder_->GetVsyncTargetTime()); + + // Commit the pending continuation. + PipelineProduceResult result = + producer_continuation_.Complete(std::make_unique( + std::move(layer_trees_tasks_), std::move(frame_timings_recorder_))); + + if (!result.success) { + FML_DLOG(INFO) << "Failed to commit to the pipeline"; + } else if (!result.is_first_item) { + // Do nothing. It has been successfully pushed to the pipeline but not as + // the first item. Eventually the 'Rasterizer' will consume it, so we + // don't need to notify the delegate. + } else { + delegate_.OnAnimatorDraw(layer_tree_pipeline_); + } + } + frame_timings_recorder_ = nullptr; // Ensure it's cleared. if (!frame_scheduled_ && has_rendered_) { // Wait a tad more than 3 60hz frames before reporting a big idle period. @@ -123,61 +155,48 @@ void Animator::BeginFrame( if (!self) { return; } - auto now = fml::TimeDelta::FromMicroseconds(Dart_TimelineGetMicros()); // If there's a frame scheduled, bail. // If there's no frame scheduled, but we're not yet past the last // vsync deadline, bail. - if (!self->frame_scheduled_ && now > self->dart_frame_deadline_) { - TRACE_EVENT0("flutter", "BeginFrame idle callback"); - self->delegate_.OnAnimatorNotifyIdle( - now + fml::TimeDelta::FromMilliseconds(100)); + if (!self->frame_scheduled_) { + auto now = + fml::TimeDelta::FromMicroseconds(Dart_TimelineGetMicros()); + if (now > self->dart_frame_deadline_) { + TRACE_EVENT0("flutter", "BeginFrame idle callback"); + self->delegate_.OnAnimatorNotifyIdle( + now + fml::TimeDelta::FromMilliseconds(100)); + } } }, kNotifyIdleTaskWaitTime); } + // Both frame_timings_recorder_ and layer_trees_tasks_ must be empty if not + // between BeginFrame and EndFrame. + FML_DCHECK(layer_trees_tasks_.empty()); + FML_DCHECK(frame_timings_recorder_ == nullptr); } -void Animator::Render(std::unique_ptr layer_tree, +void Animator::Render(int64_t view_id, + std::unique_ptr layer_tree, float device_pixel_ratio) { - has_rendered_ = true; - last_layer_tree_size_ = layer_tree->frame_size(); - - if (!frame_timings_recorder_) { - // Framework can directly call render with a built scene. - frame_timings_recorder_ = std::make_unique(); - const fml::TimePoint placeholder_time = fml::TimePoint::Now(); - frame_timings_recorder_->RecordVsync(placeholder_time, placeholder_time); - frame_timings_recorder_->RecordBuildStart(placeholder_time); + // Animator::Render should be called between BeginFrame and EndFrame, + // which is indicated by frame_timings_recorder_ being non-null. + // This might happen on release build, and is guarded by PlatformDispatcher on + // debug build. + // TODO(dkwingsmt): We should change this skip into an assertion. + // https://github.com/flutter/flutter/issues/137073 + if (frame_timings_recorder_ == nullptr) { + return; } + has_rendered_ = true; + TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter", "Animator::Render", /*flow_id_count=*/0, /*flow_ids=*/nullptr); - frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now()); - - delegate_.OnAnimatorUpdateLatestFrameTargetTime( - frame_timings_recorder_->GetVsyncTargetTime()); - - auto layer_tree_item = std::make_unique( - std::move(layer_tree), std::move(frame_timings_recorder_), - device_pixel_ratio); - // Commit the pending continuation. - PipelineProduceResult result = - producer_continuation_.Complete(std::move(layer_tree_item)); - - if (!result.success) { - FML_DLOG(INFO) << "No pending continuation to commit"; - return; - } - - if (!result.is_first_item) { - // It has been successfully pushed to the pipeline but not as the first - // item. Eventually the 'Rasterizer' will consume it, so we don't need to - // notify the delegate. - return; - } - delegate_.OnAnimatorDraw(layer_tree_pipeline_); + layer_trees_tasks_.push_back(std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio)); } const std::weak_ptr Animator::GetVsyncWaiter() const { @@ -185,15 +204,15 @@ const std::weak_ptr Animator::GetVsyncWaiter() const { return weak; } -bool Animator::CanReuseLastLayerTree() { - return !regenerate_layer_tree_; +bool Animator::CanReuseLastLayerTrees() { + return !regenerate_layer_trees_; } -void Animator::DrawLastLayerTree( +void Animator::DrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { // This method is very cheap, but this makes it explicitly clear in trace // files. - TRACE_EVENT0("flutter", "Animator::DrawLastLayerTree"); + TRACE_EVENT0("flutter", "Animator::DrawLastLayerTrees"); pending_frame_semaphore_.Signal(); // In this case BeginFrame doesn't get called, we need to @@ -203,18 +222,18 @@ void Animator::DrawLastLayerTree( const auto now = fml::TimePoint::Now(); frame_timings_recorder->RecordBuildStart(now); frame_timings_recorder->RecordBuildEnd(now); - delegate_.OnAnimatorDrawLastLayerTree(std::move(frame_timings_recorder)); + delegate_.OnAnimatorDrawLastLayerTrees(std::move(frame_timings_recorder)); } -void Animator::RequestFrame(bool regenerate_layer_tree) { - if (regenerate_layer_tree) { +void Animator::RequestFrame(bool regenerate_layer_trees) { + if (regenerate_layer_trees) { // This event will be closed by BeginFrame. BeginFrame will only be called - // if regenerating the layer tree. If a frame has been requested to update + // if regenerating the layer trees. If a frame has been requested to update // an external texture, this will be false and no BeginFrame call will // happen. TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_request_number_); - regenerate_layer_tree_ = true; + regenerate_layer_trees_ = true; } if (!pending_frame_semaphore_.TryWait()) { @@ -245,10 +264,11 @@ void Animator::AwaitVSync() { [self = weak_factory_.GetWeakPtr()]( std::unique_ptr frame_timings_recorder) { if (self) { - if (self->CanReuseLastLayerTree()) { - self->DrawLastLayerTree(std::move(frame_timings_recorder)); + if (self->CanReuseLastLayerTrees()) { + self->DrawLastLayerTrees(std::move(frame_timings_recorder)); } else { self->BeginFrame(std::move(frame_timings_recorder)); + self->EndFrame(); } } }); diff --git a/shell/common/animator.h b/shell/common/animator.h index 4e6295957bdcc..5358ed6b14001 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -39,10 +39,9 @@ class Animator final { virtual void OnAnimatorUpdateLatestFrameTargetTime( fml::TimePoint frame_target_time) = 0; - virtual void OnAnimatorDraw( - std::shared_ptr pipeline) = 0; + virtual void OnAnimatorDraw(std::shared_ptr pipeline) = 0; - virtual void OnAnimatorDrawLastLayerTree( + virtual void OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) = 0; }; @@ -52,9 +51,17 @@ class Animator final { ~Animator(); - void RequestFrame(bool regenerate_layer_tree = true); + void RequestFrame(bool regenerate_layer_trees = true); - void Render(std::unique_ptr layer_tree, + //-------------------------------------------------------------------------- + /// @brief Tells the Animator that this frame needs to render another view. + /// + /// This method must be called during a vsync callback, or + /// technically, between Animator::BeginFrame and Animator::EndFrame + /// (both private methods). Otherwise, this call will be ignored. + /// + void Render(int64_t view_id, + std::unique_ptr layer_tree, float device_pixel_ratio); const std::weak_ptr GetVsyncWaiter() const; @@ -83,11 +90,17 @@ class Animator final { void EnqueueTraceFlowId(uint64_t trace_flow_id); private: + // Animator's work during a vsync is split into two methods, BeginFrame and + // EndFrame. The two methods should be called synchronously back-to-back to + // avoid being interrupted by a regular vsync. The reason to split them is to + // allow ShellTest::PumpOneFrame to insert a Render in between. + void BeginFrame(std::unique_ptr frame_timings_recorder); + void EndFrame(); - bool CanReuseLastLayerTree(); + bool CanReuseLastLayerTrees(); - void DrawLastLayerTree( + void DrawLastLayerTrees( std::unique_ptr frame_timings_recorder); void AwaitVSync(); @@ -100,14 +113,14 @@ class Animator final { std::shared_ptr waiter_; std::unique_ptr frame_timings_recorder_; + std::vector> layer_trees_tasks_; uint64_t frame_request_number_ = 1; fml::TimeDelta dart_frame_deadline_; - std::shared_ptr layer_tree_pipeline_; + std::shared_ptr layer_tree_pipeline_; fml::Semaphore pending_frame_semaphore_; - LayerTreePipeline::ProducerContinuation producer_continuation_; - bool regenerate_layer_tree_ = false; + FramePipeline::ProducerContinuation producer_continuation_; + bool regenerate_layer_trees_ = false; bool frame_scheduled_ = false; - SkISize last_layer_tree_size_ = {0, 0}; std::deque trace_flow_ids_; bool has_rendered_ = false; diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 459c77b0ec672..80fd30596b9b1 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -23,6 +23,8 @@ namespace flutter { namespace testing { +constexpr int64_t kImplicitViewId = 0; + class FakeAnimatorDelegate : public Animator::Delegate { public: MOCK_METHOD(void, @@ -41,10 +43,10 @@ class FakeAnimatorDelegate : public Animator::Delegate { MOCK_METHOD(void, OnAnimatorDraw, - (std::shared_ptr pipeline), + (std::shared_ptr pipeline), (override)); - void OnAnimatorDrawLastLayerTree( + void OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) override {} bool notify_idle_called_ = false; @@ -158,20 +160,30 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) { latch.Wait(); ASSERT_FALSE(delegate.notify_idle_called_); + fml::AutoResetWaitableEvent render_latch; // Validate it has not notified idle and try to render. task_runners.GetUITaskRunner()->PostDelayedTask( [&] { ASSERT_FALSE(delegate.notify_idle_called_); - auto layer_tree = std::make_unique(LayerTree::Config(), - SkISize::Make(600, 800)); - animator->Render(std::move(layer_tree), 1.0); + EXPECT_CALL(delegate, OnAnimatorBeginFrame).WillOnce([&] { + auto layer_tree = std::make_unique( + LayerTree::Config(), SkISize::Make(600, 800)); + animator->Render(kImplicitViewId, std::move(layer_tree), 1.0); + render_latch.Signal(); + }); + // Request a frame that builds a layer tree and renders a frame. + // When the frame is rendered, render_latch will be signaled. + animator->RequestFrame(true); task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task); }, // See kNotifyIdleTaskWaitTime in animator.cc. fml::TimeDelta::FromMilliseconds(60)); latch.Wait(); + render_latch.Wait(); - // Still hasn't notified idle because there has been no frame request. + // A frame has been rendered, and the next frame request will notify idle. + // But at the moment there isn't another frame request, therefore it still + // hasn't notified idle. task_runners.GetUITaskRunner()->PostTask([&] { ASSERT_FALSE(delegate.notify_idle_called_); // False to avoid getting cals to BeginFrame that will request more frames @@ -224,11 +236,6 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) { }); fml::AutoResetWaitableEvent begin_frame_latch; - EXPECT_CALL(delegate, OnAnimatorBeginFrame) - .WillRepeatedly( - [&](fml::TimePoint frame_target_time, uint64_t frame_number) { - begin_frame_latch.Signal(); - }); // It must always be called when the method 'Animator::Render' is called, // regardless of whether the pipeline is empty or not. EXPECT_CALL(delegate, OnAnimatorUpdateLatestFrameTargetTime).Times(2); @@ -239,16 +246,16 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) { for (int i = 0; i < 2; i++) { task_runners.GetUITaskRunner()->PostTask([&] { + EXPECT_CALL(delegate, OnAnimatorBeginFrame).WillOnce([&] { + auto layer_tree = std::make_unique(LayerTree::Config(), + SkISize::Make(600, 800)); + animator->Render(kImplicitViewId, std::move(layer_tree), 1.0); + begin_frame_latch.Signal(); + }); animator->RequestFrame(); task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task); }); begin_frame_latch.Wait(); - - PostTaskSync(task_runners.GetUITaskRunner(), [&] { - auto layer_tree = std::make_unique(LayerTree::Config(), - SkISize::Make(600, 800)); - animator->Render(std::move(layer_tree), 1.0); - }); } PostTaskSync(task_runners.GetUITaskRunner(), [&] { animator.reset(); }); diff --git a/shell/common/base64.cc b/shell/common/base64.cc new file mode 100644 index 0000000000000..3232446705b39 --- /dev/null +++ b/shell/common/base64.cc @@ -0,0 +1,156 @@ +// 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/common/base64.h" + +#include "flutter/fml/logging.h" + +#include + +#define DecodePad -2 +#define EncodePad 64 + +static const char kDefaultEncode[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/="; + +static const signed char kDecodeData[] = { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, + -1, -1, DecodePad, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + +namespace flutter { + +Base64::Error Base64::Decode(const void* srcv, + size_t srcLength, + void* dstv, + size_t* dstLength) { + const unsigned char* src = static_cast(srcv); + unsigned char* dst = static_cast(dstv); + + int i = 0; + bool padTwo = false; + bool padThree = false; + char unsigned const* const end = src + srcLength; + while (src < end) { + unsigned char bytes[4] = {0, 0, 0, 0}; + int byte = 0; + do { + unsigned char srcByte = *src++; + if (srcByte == 0) { + *dstLength = i; + return Error::kNone; + } + if (srcByte <= ' ') { + continue; // treat as white space + } + if (srcByte < '+' || srcByte > 'z') { + return Error::kBadChar; + } + signed char decoded = kDecodeData[srcByte - '+']; + bytes[byte] = decoded; + if (decoded != DecodePad) { + if (decoded < 0) { + return Error::kBadChar; + } + byte++; + if (*src) { + continue; + } + if (byte == 0) { + *dstLength = i; + return Error::kNone; + } + if (byte == 4) { + break; + } + } + // As an optimization, if we find an equals sign + // we assume all future bytes to read are the + // appropriate number of padding equals signs. + if (byte < 2) { + return Error::kBadPadding; + } + padThree = true; + if (byte == 2) { + padTwo = true; + } + break; + } while (byte < 4); + int two = 0; + int three = 0; + if (dst) { + int one = (uint8_t)(bytes[0] << 2); + two = bytes[1]; + one |= two >> 4; + two = (uint8_t)((two << 4) & 0xFF); + three = bytes[2]; + two |= three >> 2; + three = (uint8_t)((three << 6) & 0xFF); + three |= bytes[3]; + FML_DCHECK(one < 256 && two < 256 && three < 256); + dst[i] = (unsigned char)one; + } + i++; + if (padTwo) { + break; + } + if (dst) { + dst[i] = (unsigned char)two; + } + i++; + if (padThree) { + break; + } + if (dst) { + dst[i] = (unsigned char)three; + } + i++; + } + *dstLength = i; + return Error::kNone; +} + +size_t Base64::Encode(const void* srcv, size_t length, void* dstv) { + FML_DCHECK(dstv); + const unsigned char* src = static_cast(srcv); + unsigned char* dst = static_cast(dstv); + + const char* encode = kDefaultEncode; + size_t remainder = length % 3; + char unsigned const* const end = &src[length - remainder]; + while (src < end) { + unsigned a = *src++; + unsigned b = *src++; + unsigned c = *src++; + int d = c & 0x3F; + c = (c >> 6 | b << 2) & 0x3F; + b = (b >> 4 | a << 4) & 0x3F; + a = a >> 2; + *dst++ = encode[a]; + *dst++ = encode[b]; + *dst++ = encode[c]; + *dst++ = encode[d]; + } + if (remainder > 0) { + int k1 = 0; + int k2 = EncodePad; + int a = (uint8_t)*src++; + if (remainder == 2) { + int b = *src++; + k1 = b >> 4; + k2 = (b << 2) & 0x3F; + } + *dst++ = encode[a >> 2]; + *dst++ = encode[(k1 | a << 4) & 0x3F]; + *dst++ = encode[k2]; + *dst++ = encode[EncodePad]; + } + return EncodedSize(length); +} + +} // namespace flutter diff --git a/shell/common/base64.h b/shell/common/base64.h new file mode 100644 index 0000000000000..18f8a56dad539 --- /dev/null +++ b/shell/common/base64.h @@ -0,0 +1,59 @@ +// 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_COMMON_BASE64_H_ +#define FLUTTER_SHELL_COMMON_BASE64_H_ + +#include + +namespace flutter { + +struct Base64 { + public: + enum class Error { + kNone, + kBadPadding, + kBadChar, + }; + + /** + Base64 encodes src into dst. + + @param dst a pointer to a buffer large enough to receive the result. + + @return the required length of dst for encoding. + */ + static size_t Encode(const void* src, size_t length, void* dst); + + /** + Returns the length of the buffer that needs to be allocated to encode + srcDataLength bytes. + */ + static size_t EncodedSize(size_t srcDataLength) { + // Take the floor of division by 3 to find the number of groups that need to + // be encoded. Each group takes 4 bytes to be represented in base64. + return ((srcDataLength + 2) / 3) * 4; + } + + /** + Base64 decodes src into dst. + + This can be called once with 'dst' nullptr to get the required size, + then again with an allocated 'dst' pointer to do the actual decoding. + + @param dst nullptr or a pointer to a buffer large enough to receive the + result + + @param dstLength assigned the length dst is required to be. Must not be + nullptr. + */ + [[nodiscard]] static Error Decode(const void* src, + size_t srcLength, + void* dst, + size_t* dstLength); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_COMMON_BASE64_H_ diff --git a/shell/common/base64_unittests.cc b/shell/common/base64_unittests.cc new file mode 100644 index 0000000000000..c9b3a04d5e4a1 --- /dev/null +++ b/shell/common/base64_unittests.cc @@ -0,0 +1,122 @@ +// 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/common/base64.h" + +#include "fml/logging.h" +#include "gtest/gtest.h" + +#include + +namespace flutter { +namespace testing { + +TEST(Base64, EncodeStrings) { + auto test = [](const std::string& input, const std::string& output) { + char buffer[256]; + size_t len = Base64::Encode(input.c_str(), input.length(), &buffer); + FML_CHECK(len <= 256); + ASSERT_EQ(len, Base64::EncodedSize(input.length())); + std::string actual(buffer, len); + ASSERT_STREQ(actual.c_str(), output.c_str()); + }; + // Some arbitrary strings + test("apple", "YXBwbGU="); + test("BANANA", "QkFOQU5B"); + test("Cherry Pie", "Q2hlcnJ5IFBpZQ=="); + test("fLoCcInAuCiNiHiLiPiLiFiCaTiOn", + "ZkxvQ2NJbkF1Q2lOaUhpTGlQaUxpRmlDYVRpT24="); + test("", ""); +} + +TEST(Base64, EncodeBytes) { + auto test = [](const uint8_t input[], size_t num, const std::string& output) { + char buffer[512]; + size_t len = Base64::Encode(input, num, &buffer); + FML_CHECK(len <= 512); + ASSERT_EQ(len, Base64::EncodedSize(num)); + std::string actual(buffer, len); + ASSERT_STREQ(actual.c_str(), output.c_str()); + }; + // Some arbitrary raw bytes + uint8_t e[] = {0x02, 0x71, 0x82, 0x81, 0x82, 0x84, 0x59}; + test(e, sizeof(e), "AnGCgYKEWQ=="); + + uint8_t pi[] = {0x03, 0x24, 0x3F, 0x6A, 0x88, 0x85}; + test(pi, sizeof(pi), "AyQ/aoiF"); + + uint8_t bytes[256]; + for (int i = 0; i < 256; i++) { + bytes[i] = i; + } + test(bytes, sizeof(bytes), + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gIS" + "IjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFV" + "WV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJ" + "iouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8v" + "b6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8P" + "Hy8/T19vf4+fr7/P3+/w=="); +} + +TEST(Base64, DecodeStringsSuccess) { + auto test = [](const std::string& input, const std::string& output) { + char buffer[256]; + size_t len = 0; + auto err = Base64::Decode(input.c_str(), input.length(), &buffer, &len); + ASSERT_EQ(err, Base64::Error::kNone); + FML_CHECK(len <= 256); + std::string actual(buffer, len); + ASSERT_STREQ(actual.c_str(), output.c_str()); + }; + // Some arbitrary strings + test("ZGF0ZQ==", "date"); + test("RWdncGxhbnQ=", "Eggplant"); + test("RmlzaCAmIENoaXBz", "Fish & Chips"); + test("U3VQZVJjQWxJZlJhR2lMaVN0SWNFeFBpQWxJZE9jSW9Vcw==", + "SuPeRcAlIfRaGiLiStIcExPiAlIdOcIoUs"); + + // Spaces are ignored + test("Y X Bwb GU=", "apple"); +} + +TEST(Base64, DecodeStringsHasErrors) { + auto test = [](const std::string& input, Base64::Error expectedError) { + char buffer[256]; + size_t len = 0; + auto err = Base64::Decode(input.c_str(), input.length(), &buffer, &len); + ASSERT_EQ(err, expectedError) << input; + }; + + test("Nuts&Bolts", Base64::Error::kBadChar); + test("Error!", Base64::Error::kBadChar); + test(":", Base64::Error::kBadChar); + + test("RmlzaCAmIENoaXBz=", Base64::Error::kBadPadding); + // Some cases of bad padding may be ignored due to an internal optimization + // test("ZGF0ZQ=", Base64::Error::kBadPadding); + // test("RWdncGxhbnQ", Base64::Error::kBadPadding); +} + +TEST(Base64, DecodeBytes) { + auto test = [](const std::string& input, const uint8_t output[], size_t num) { + char buffer[256]; + size_t len = 0; + auto err = Base64::Decode(input.c_str(), input.length(), &buffer, &len); + ASSERT_EQ(err, Base64::Error::kNone); + FML_CHECK(len <= 256); + ASSERT_EQ(num, len) << input; + for (int i = 0; i < int(len); i++) { + ASSERT_EQ(uint8_t(buffer[i]), output[i]) << input << i; + } + }; + // Some arbitrary raw bytes, same as the byte output above + uint8_t e[] = {0x02, 0x71, 0x82, 0x81, 0x82, 0x84, 0x59}; + test("AnGCgYKEWQ==", e, sizeof(e)); + + uint8_t pi[] = {0x03, 0x24, 0x3F, 0x6A, 0x88, 0x85}; + test("AyQ/aoiF", pi, sizeof(pi)); +} + +} // namespace testing +} // namespace flutter \ No newline at end of file diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 920122c9a23dc..1dcf9d4b128ee 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -455,11 +455,12 @@ std::string Engine::DefaultRouteName() { return "/"; } -void Engine::ScheduleFrame(bool regenerate_layer_tree) { - animator_->RequestFrame(regenerate_layer_tree); +void Engine::ScheduleFrame(bool regenerate_layer_trees) { + animator_->RequestFrame(regenerate_layer_trees); } -void Engine::Render(std::unique_ptr layer_tree, +void Engine::Render(int64_t view_id, + std::unique_ptr layer_tree, float device_pixel_ratio) { if (!layer_tree) { return; @@ -470,7 +471,7 @@ void Engine::Render(std::unique_ptr layer_tree, return; } - animator_->Render(std::move(layer_tree), device_pixel_ratio); + animator_->Render(view_id, std::move(layer_tree), device_pixel_ratio); } void Engine::UpdateSemantics(SemanticsNodeUpdates update, diff --git a/shell/common/engine.h b/shell/common/engine.h index f7d888de425f2..17ba3cc6fd884 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -29,7 +29,6 @@ #include "flutter/shell/common/display_manager.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/pointer_data_dispatcher.h" -#include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell_io_manager.h" @@ -828,7 +827,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { void SetAccessibilityFeatures(int32_t flags); // |RuntimeDelegate| - void ScheduleFrame(bool regenerate_layer_tree) override; + void ScheduleFrame(bool regenerate_layer_trees) override; /// Schedule a frame with the default parameter of regenerating the layer /// tree. @@ -960,7 +959,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { std::string DefaultRouteName() override; // |RuntimeDelegate| - void Render(std::unique_ptr layer_tree, + void Render(int64_t view_id, + std::unique_ptr layer_tree, float device_pixel_ratio) override; // |RuntimeDelegate| diff --git a/shell/common/engine_unittests.cc b/shell/common/engine_unittests.cc index b0393cb2d2490..7ffa0a6f7714a 100644 --- a/shell/common/engine_unittests.cc +++ b/shell/common/engine_unittests.cc @@ -7,6 +7,8 @@ #include #include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/shell_test.h" #include "flutter/shell/common/thread_host.h" #include "flutter/testing/fixture_test.h" #include "flutter/testing/testing.h" @@ -19,6 +21,19 @@ namespace flutter { namespace { +using ::testing::Invoke; +using ::testing::ReturnRef; + +static void PostSync(const fml::RefPtr& task_runner, + const fml::closure& task) { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] { + task(); + latch.Signal(); + }); + latch.Wait(); +} + class MockDelegate : public Engine::Delegate { public: MOCK_METHOD(void, @@ -65,7 +80,7 @@ class MockRuntimeDelegate : public RuntimeDelegate { MOCK_METHOD(void, ScheduleFrame, (bool), (override)); MOCK_METHOD(void, Render, - (std::unique_ptr, float), + (int64_t, std::unique_ptr, float), (override)); MOCK_METHOD(void, UpdateSemantics, @@ -117,6 +132,51 @@ class MockRuntimeController : public RuntimeController { MOCK_METHOD(bool, NotifyIdle, (fml::TimeDelta), (override)); }; +class MockAnimatorDelegate : public Animator::Delegate { + public: + /* Animator::Delegate */ + MOCK_METHOD(void, + OnAnimatorBeginFrame, + (fml::TimePoint frame_target_time, uint64_t frame_number), + (override)); + MOCK_METHOD(void, + OnAnimatorNotifyIdle, + (fml::TimeDelta deadline), + (override)); + MOCK_METHOD(void, + OnAnimatorUpdateLatestFrameTargetTime, + (fml::TimePoint frame_target_time), + (override)); + MOCK_METHOD(void, + OnAnimatorDraw, + (std::shared_ptr pipeline), + (override)); + MOCK_METHOD(void, + OnAnimatorDrawLastLayerTrees, + (std::unique_ptr frame_timings_recorder), + (override)); +}; + +class MockPlatformMessageHandler : public PlatformMessageHandler { + public: + MOCK_METHOD(void, + HandlePlatformMessage, + (std::unique_ptr message), + (override)); + MOCK_METHOD(bool, + DoesHandlePlatformMessageOnPlatformThread, + (), + (const, override)); + MOCK_METHOD(void, + InvokePlatformMessageResponseCallback, + (int response_id, std::unique_ptr mapping), + (override)); + MOCK_METHOD(void, + InvokePlatformMessageEmptyResponseCallback, + (int response_id), + (override)); +}; + std::unique_ptr MakePlatformMessage( const std::string& channel, const std::map& values, @@ -185,6 +245,97 @@ class EngineTest : public testing::FixtureTest { std::shared_ptr image_decoder_task_runner_; fml::TaskRunnerAffineWeakPtr snapshot_delegate_; }; + +// A class that can launch an Engine with the specified Engine::Delegate. +// +// To use this class, contruct this class with Create, call Run, and use the +// engine with EngineTaskSync(). +class EngineContext { + public: + using EngineCallback = std::function; + + [[nodiscard]] static std::unique_ptr Create( + Engine::Delegate& delegate, // + Settings settings, // + const TaskRunners& task_runners, // + std::unique_ptr animator) { + auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings); + FML_CHECK(vm) << "Must be able to initialize the VM."; + // Construct the class with `new` because `make_unique` has no access to the + // private constructor. + EngineContext* raw_pointer = + new EngineContext(delegate, settings, task_runners, std::move(animator), + std::move(vm), isolate_snapshot); + return std::unique_ptr(raw_pointer); + } + + void Run(RunConfiguration configuration) { + PostSync(task_runners_.GetUITaskRunner(), [this, &configuration] { + Engine::RunStatus run_status = engine_->Run(std::move(configuration)); + FML_CHECK(run_status == Engine::RunStatus::Success) + << "Engine failed to run."; + (void)run_status; // Suppress unused-variable warning + }); + } + + // Run a task that operates the Engine on the UI thread, and wait for the + // task to end. + // + // If called on the UI thread, the task is executed synchronously. + void EngineTaskSync(EngineCallback task) { + ASSERT_TRUE(engine_); + ASSERT_TRUE(task); + auto runner = task_runners_.GetUITaskRunner(); + if (runner->RunsTasksOnCurrentThread()) { + task(*engine_); + } else { + PostSync(task_runners_.GetUITaskRunner(), [&]() { task(*engine_); }); + } + } + + ~EngineContext() { + PostSync(task_runners_.GetUITaskRunner(), [this] { engine_.reset(); }); + } + + private: + EngineContext(Engine::Delegate& delegate, // + Settings settings, // + const TaskRunners& task_runners, // + std::unique_ptr animator, // + DartVMRef vm, // + fml::RefPtr isolate_snapshot) + : task_runners_(task_runners), vm_(std::move(vm)) { + PostSync(task_runners.GetUITaskRunner(), [this, &settings, &animator, + &delegate, &isolate_snapshot] { + auto dispatcher_maker = + [](DefaultPointerDataDispatcher::Delegate& delegate) { + return std::make_unique(delegate); + }; + engine_ = std::make_unique( + /*delegate=*/delegate, + /*dispatcher_maker=*/dispatcher_maker, + /*vm=*/*&vm_, + /*isolate_snapshot=*/std::move(isolate_snapshot), + /*task_runners=*/task_runners_, + /*platform_data=*/PlatformData(), + /*settings=*/settings, + /*animator=*/std::move(animator), + /*io_manager=*/io_manager_, + /*unref_queue=*/nullptr, + /*snapshot_delegate=*/snapshot_delegate_, + /*volatile_path_tracker=*/nullptr, + /*gpu_disabled_switch=*/std::make_shared()); + }); + } + + TaskRunners task_runners_; + DartVMRef vm_; + std::unique_ptr engine_; + + fml::WeakPtr io_manager_; + fml::TaskRunnerAffineWeakPtr snapshot_delegate_; +}; + } // namespace TEST_F(EngineTest, Create) { @@ -418,4 +569,68 @@ TEST_F(EngineTest, PassesLoadDartDeferredLibraryErrorToRuntime) { }); } +TEST_F(EngineTest, AnimatorAcceptsMultipleRenders) { + MockAnimatorDelegate animator_delegate; + std::unique_ptr engine_context; + + std::shared_ptr platform_message_handler = + std::make_shared(); + EXPECT_CALL(delegate_, GetPlatformMessageHandler) + .WillOnce(ReturnRef(platform_message_handler)); + + fml::AutoResetWaitableEvent draw_latch; + EXPECT_CALL(animator_delegate, OnAnimatorDraw) + .WillOnce( + Invoke([&draw_latch](const std::shared_ptr& pipeline) { + auto status = + pipeline->Consume([&](std::unique_ptr item) { + EXPECT_EQ(item->layer_tree_tasks.size(), 2u); + EXPECT_EQ(item->layer_tree_tasks[0]->view_id, 1); + EXPECT_EQ(item->layer_tree_tasks[1]->view_id, 2); + }); + EXPECT_EQ(status, PipelineConsumeResult::Done); + draw_latch.Signal(); + })); + EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) + .WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time, + uint64_t frame_number) { + engine_context->EngineTaskSync([&](Engine& engine) { + engine.BeginFrame(frame_target_time, frame_number); + }); + })); + + static fml::AutoResetWaitableEvent callback_ready_latch; + callback_ready_latch.Reset(); + AddNativeCallback("NotifyNative", + [](auto args) { callback_ready_latch.Signal(); }); + + std::unique_ptr animator; + PostSync(task_runners_.GetUITaskRunner(), + [&animator, &animator_delegate, &task_runners = task_runners_] { + animator = std::make_unique( + animator_delegate, task_runners, + static_cast>( + std::make_unique( + task_runners))); + }); + + engine_context = EngineContext::Create(delegate_, settings_, task_runners_, + std::move(animator)); + + auto configuration = RunConfiguration::InferFromSettings(settings_); + configuration.SetEntrypoint("onBeginFrameRendersMultipleViews"); + engine_context->Run(std::move(configuration)); + + engine_context->EngineTaskSync([](Engine& engine) { + engine.AddView(1, {1, 10, 10, 22, 0}); + engine.AddView(2, {1, 10, 10, 22, 0}); + }); + + callback_ready_latch.Wait(); + + engine_context->EngineTaskSync( + [](Engine& engine) { engine.ScheduleFrame(); }); + draw_latch.Wait(); +} + } // namespace flutter diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index e4b0d98833f44..cc6e32731d898 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -547,3 +547,24 @@ void testReportViewWidths() { nativeReportViewWidthsCallback(getCurrentViewWidths()); }; } + +@pragma('vm:entry-point') +void onBeginFrameRendersMultipleViews() { + PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) { + for (final FlutterView view in PlatformDispatcher.instance.views) { + final SceneBuilder builder = SceneBuilder(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawPaint(Paint()..color = const Color(0xFFABCDEF)); + final Picture picture = recorder.endRecording(); + builder.addPicture(Offset.zero, picture); + + final Scene scene = builder.build(); + view.render(scene); + + scene.dispose(); + picture.dispose(); + } + }; + notifyNative(); +} diff --git a/shell/common/input_events_unittests.cc b/shell/common/input_events_unittests.cc index 3824dc4d92bae..66a2b64a39e02 100644 --- a/shell/common/input_events_unittests.cc +++ b/shell/common/input_events_unittests.cc @@ -127,11 +127,11 @@ static void TestSimulatedInputEvents( ShellTest::DispatchFakePointerData(shell.get()); i += 1; } - ShellTest::VSyncFlush(shell.get(), will_draw_new_frame); + ShellTest::VSyncFlush(shell.get(), &will_draw_new_frame); } // Finally, issue a vsync for the pending event that may be generated duing // the last vsync. - ShellTest::VSyncFlush(shell.get(), will_draw_new_frame); + ShellTest::VSyncFlush(shell.get(), &will_draw_new_frame); }); simulation.wait(); @@ -345,8 +345,7 @@ TEST_F(ShellTest, CanCorrectlyPipePointerPacket) { CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0); packet->SetPointerData(5, data); ShellTest::DispatchPointerData(shell.get(), std::move(packet)); - bool will_draw_new_frame; - ShellTest::VSyncFlush(shell.get(), will_draw_new_frame); + ShellTest::VSyncFlush(shell.get()); reportLatch.Wait(); size_t expect_length = 6; @@ -407,8 +406,7 @@ TEST_F(ShellTest, CanCorrectlySynthesizePointerPacket) { CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0); packet->SetPointerData(3, data); ShellTest::DispatchPointerData(shell.get(), std::move(packet)); - bool will_draw_new_frame; - ShellTest::VSyncFlush(shell.get(), will_draw_new_frame); + ShellTest::VSyncFlush(shell.get()); reportLatch.Wait(); size_t expect_length = 6; diff --git a/shell/common/persistent_cache_unittests.cc b/shell/common/persistent_cache_unittests.cc index e184bf2d57eef..427da231efed0 100644 --- a/shell/common/persistent_cache_unittests.cc +++ b/shell/common/persistent_cache_unittests.cc @@ -42,7 +42,7 @@ static void CheckTwoSkSLsAreLoaded() { TEST_F(PersistentCacheTest, CanLoadSkSLsFromAsset) { // Avoid polluting unit tests output by hiding INFO level logging. - fml::LogSettings warning_only = {fml::LOG_WARNING}; + fml::LogSettings warning_only = {fml::kLogWarning}; fml::ScopedSetLogSettings scoped_set_log_settings(warning_only); // The SkSL key is Base32 encoded. "IE" is the encoding of "A" and "II" is the diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index c76ccbedbcd49..72401eec40cd5 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -253,20 +253,6 @@ class Pipeline { FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; -struct LayerTreeItem { - LayerTreeItem(std::unique_ptr layer_tree, - std::unique_ptr frame_timings_recorder, - float device_pixel_ratio) - : layer_tree(std::move(layer_tree)), - frame_timings_recorder(std::move(frame_timings_recorder)), - device_pixel_ratio(device_pixel_ratio) {} - std::unique_ptr layer_tree; - std::unique_ptr frame_timings_recorder; - float device_pixel_ratio; -}; - -using LayerTreePipeline = Pipeline; - } // namespace flutter #endif // FLUTTER_SHELL_COMMON_PIPELINE_H_ diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 4ed0b2fb4fd31..523bf53d350b6 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -14,6 +14,7 @@ #include "flutter/flow/layers/offscreen_surface.h" #include "flutter/fml/time/time_delta.h" #include "flutter/fml/time/time_point.h" +#include "flutter/shell/common/base64.h" #include "flutter/shell/common/serialization_callbacks.h" #include "fml/make_copyable.h" #include "third_party/skia/include/core/SkColorSpace.h" @@ -27,11 +28,11 @@ #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/encode/SkPngEncoder.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/GrTypes.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" -#include "third_party/skia/include/utils/SkBase64.h" namespace flutter { @@ -41,7 +42,8 @@ static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000); Rasterizer::Rasterizer(Delegate& delegate, MakeGpuImageBehavior gpu_image_behavior) - : delegate_(delegate), + : is_torn_down_(false), + delegate_(delegate), gpu_image_behavior_(gpu_image_behavior), compositor_context_(std::make_unique(*this)), user_override_resource_cache_bytes_(false), @@ -107,6 +109,7 @@ void Rasterizer::TeardownExternalViewEmbedder() { } void Rasterizer::Teardown() { + is_torn_down_ = true; if (surface_) { auto context_switch = surface_->MakeRenderContextCurrent(); if (context_switch->GetResult()) { @@ -118,7 +121,7 @@ void Rasterizer::Teardown() { surface_.reset(); } - last_layer_tree_.reset(); + view_records_.clear(); if (raster_thread_merger_.get() != nullptr && raster_thread_merger_.get()->IsMerged()) { @@ -128,6 +131,20 @@ void Rasterizer::Teardown() { } } +bool Rasterizer::IsTornDown() { + return is_torn_down_; +} + +std::optional Rasterizer::GetLastDrawStatus( + int64_t view_id) { + auto found = view_records_.find(view_id); + if (found != view_records_.end()) { + return found->second.last_draw_status; + } else { + return std::optional(); + } +} + void Rasterizer::EnableThreadMergerIfNeeded() { if (raster_thread_merger_) { raster_thread_merger_->Enable(); @@ -160,11 +177,7 @@ void Rasterizer::NotifyLowMemoryWarning() const { } void Rasterizer::CollectView(int64_t view_id) { - // TODO(dkwingsmt): When Rasterizer supports multi-view, this method should - // correctly clear the view corresponding to the ID. - if (view_id == kFlutterImplicitViewId) { - last_layer_tree_.reset(); - } + view_records_.erase(view_id); } std::shared_ptr Rasterizer::GetTextureRegistry() { @@ -175,74 +188,80 @@ GrDirectContext* Rasterizer::GetGrContext() { return surface_ ? surface_->GetContext() : nullptr; } -flutter::LayerTree* Rasterizer::GetLastLayerTree() { - return last_layer_tree_.get(); +flutter::LayerTree* Rasterizer::GetLastLayerTree(int64_t view_id) { + auto found = view_records_.find(view_id); + if (found == view_records_.end()) { + return nullptr; + } + auto& last_task = found->second.last_successful_task; + if (last_task == nullptr) { + return nullptr; + } + return last_task->layer_tree.get(); } -void Rasterizer::DrawLastLayerTree( +void Rasterizer::DrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { - if (!last_layer_tree_ || !surface_) { + if (!surface_) { + return; + } + std::vector> tasks; + for (auto& [view_id, view_record] : view_records_) { + if (view_record.last_successful_task) { + tasks.push_back(std::move(view_record.last_successful_task)); + } + } + if (tasks.empty()) { return; } - RasterStatus raster_status = DrawToSurface( - *frame_timings_recorder, *last_layer_tree_, last_device_pixel_ratio_); + + DoDrawResult result = + DrawToSurfaces(*frame_timings_recorder, std::move(tasks)); // EndFrame should perform cleanups for the external_view_embedder. if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) { - bool should_resubmit_frame = ShouldResubmitFrame(raster_status); + bool should_resubmit_frame = ShouldResubmitFrame(result); external_view_embedder_->SetUsedThisFrame(false); external_view_embedder_->EndFrame(should_resubmit_frame, raster_thread_merger_); } } -RasterStatus Rasterizer::Draw( - const std::shared_ptr& pipeline) { +DrawStatus Rasterizer::Draw(const std::shared_ptr& pipeline) { TRACE_EVENT0("flutter", "GPURasterizer::Draw"); if (raster_thread_merger_ && !raster_thread_merger_->IsOnRasterizingThread()) { // we yield and let this frame be serviced on the right thread. - return RasterStatus::kYielded; + return DrawStatus::kYielded; } FML_DCHECK(delegate_.GetTaskRunners() .GetRasterTaskRunner() ->RunsTasksOnCurrentThread()); DoDrawResult draw_result; - LayerTreePipeline::Consumer consumer = - [&draw_result, this, - &delegate = delegate_](std::unique_ptr item) { - // TODO(dkwingsmt): Use a proper view ID when Rasterizer supports - // multi-view. - int64_t view_id = kFlutterImplicitViewId; - std::unique_ptr layer_tree = std::move(item->layer_tree); - std::unique_ptr frame_timings_recorder = - std::move(item->frame_timings_recorder); - float device_pixel_ratio = item->device_pixel_ratio; - if (delegate.ShouldDiscardLayerTree(view_id, *layer_tree.get())) { - draw_result.raster_status = RasterStatus::kDiscarded; - } else { - draw_result = DoDraw(std::move(frame_timings_recorder), - std::move(layer_tree), device_pixel_ratio); - } - }; + FramePipeline::Consumer consumer = [&draw_result, + this](std::unique_ptr item) { + draw_result = DoDraw(std::move(item->frame_timings_recorder), + std::move(item->layer_tree_tasks)); + }; PipelineConsumeResult consume_result = pipeline->Consume(consumer); if (consume_result == PipelineConsumeResult::NoneAvailable) { - return RasterStatus::kFailed; + return DrawStatus::kPipelineEmpty; } // if the raster status is to resubmit the frame, we push the frame to the // front of the queue and also change the consume status to more available. - bool should_resubmit_frame = ShouldResubmitFrame(draw_result.raster_status); + bool should_resubmit_frame = ShouldResubmitFrame(draw_result); if (should_resubmit_frame) { + FML_CHECK(draw_result.resubmitted_item); auto front_continuation = pipeline->ProduceIfEmpty(); - PipelineProduceResult pipeline_result = front_continuation.Complete( - std::move(draw_result.resubmitted_layer_tree_item)); + PipelineProduceResult pipeline_result = + front_continuation.Complete(std::move(draw_result.resubmitted_item)); if (pipeline_result.success) { consume_result = PipelineConsumeResult::MoreAvailable; } - } else if (draw_result.raster_status == RasterStatus::kEnqueuePipeline) { + } else if (draw_result.status == DoDrawStatus::kEnqueuePipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } @@ -269,12 +288,29 @@ RasterStatus Rasterizer::Draw( break; } - return draw_result.raster_status; + return ToDrawStatus(draw_result.status); +} + +bool Rasterizer::ShouldResubmitFrame(const DoDrawResult& result) { + if (result.resubmitted_item) { + FML_CHECK(!result.resubmitted_item->layer_tree_tasks.empty()); + return true; + } + return false; } -bool Rasterizer::ShouldResubmitFrame(const RasterStatus& raster_status) { - return raster_status == RasterStatus::kResubmit || - raster_status == RasterStatus::kSkipAndRetry; +DrawStatus Rasterizer::ToDrawStatus(DoDrawStatus status) { + switch (status) { + case DoDrawStatus::kEnqueuePipeline: + return DrawStatus::kDone; + case DoDrawStatus::kNotSetUp: + return DrawStatus::kNotSetUp; + case DoDrawStatus::kGpuUnavailable: + return DrawStatus::kGpuUnavailable; + case DoDrawStatus::kDone: + return DrawStatus::kDone; + } + FML_UNREACHABLE(); } namespace { @@ -346,7 +382,7 @@ std::unique_ptr Rasterizer::MakeSkiaGpuImage( GrBackendTexture texture = context->createBackendTexture( image_info.width(), image_info.height(), image_info.colorType(), - GrMipmapped::kNo, GrRenderable::kYes); + skgpu::Mipmapped::kNo, GrRenderable::kYes); if (!texture.isValid()) { result = std::make_unique( GrBackendTexture(), nullptr, nullptr, @@ -392,42 +428,31 @@ fml::Milliseconds Rasterizer::GetFrameBudget() const { Rasterizer::DoDrawResult Rasterizer::DoDraw( std::unique_ptr frame_timings_recorder, - std::unique_ptr layer_tree, - float device_pixel_ratio) { + std::vector> tasks) { TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter", "Rasterizer::DoDraw", /*flow_id_count=*/0, /*flow_ids=*/nullptr); FML_DCHECK(delegate_.GetTaskRunners() .GetRasterTaskRunner() ->RunsTasksOnCurrentThread()); + frame_timings_recorder->AssertInState(FrameTimingsRecorder::State::kBuildEnd); - if (!layer_tree || !surface_) { - return DoDrawResult{ - .raster_status = RasterStatus::kFailed, - }; + if (tasks.empty()) { + return DoDrawResult{DoDrawStatus::kDone}; + } + if (!surface_) { + return DoDrawResult{DoDrawStatus::kNotSetUp}; } PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); persistent_cache->ResetStoredNewShaders(); - RasterStatus raster_status = - DrawToSurface(*frame_timings_recorder, *layer_tree, device_pixel_ratio); - if (raster_status == RasterStatus::kSuccess) { - last_layer_tree_ = std::move(layer_tree); - last_device_pixel_ratio_ = device_pixel_ratio; - } else if (ShouldResubmitFrame(raster_status)) { - return DoDrawResult{ - .raster_status = raster_status, - .resubmitted_layer_tree_item = std::make_unique( - std::move(layer_tree), - frame_timings_recorder->CloneUntil( - FrameTimingsRecorder::State::kBuildEnd), - device_pixel_ratio), - }; - } else if (raster_status == RasterStatus::kDiscarded) { - return DoDrawResult{ - .raster_status = raster_status, - }; + DoDrawResult result = + DrawToSurfaces(*frame_timings_recorder, std::move(tasks)); + + FML_DCHECK(result.status != DoDrawStatus::kEnqueuePipeline); + if (result.status == DoDrawStatus::kGpuUnavailable) { + return DoDrawResult{DoDrawStatus::kGpuUnavailable}; } if (persistent_cache->IsDumpingSkp() && @@ -496,73 +521,162 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( if (raster_thread_merger_->DecrementLease() == fml::RasterThreadStatus::kUnmergedNow) { return DoDrawResult{ - .raster_status = RasterStatus::kEnqueuePipeline, + .status = DoDrawStatus::kEnqueuePipeline, + .resubmitted_item = std::move(result.resubmitted_item), }; } } - return DoDrawResult{ - .raster_status = raster_status, - }; + return result; } -RasterStatus Rasterizer::DrawToSurface( +Rasterizer::DoDrawResult Rasterizer::DrawToSurfaces( FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio) { - TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface"); + std::vector> tasks) { + TRACE_EVENT0("flutter", "Rasterizer::DrawToSurfaces"); FML_DCHECK(surface_); + frame_timings_recorder.AssertInState(FrameTimingsRecorder::State::kBuildEnd); - RasterStatus raster_status; + DoDrawResult result{ + .status = DoDrawStatus::kDone, + }; if (surface_->AllowsDrawingWhenGpuDisabled()) { - raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree, - device_pixel_ratio); + result.resubmitted_item = + DrawToSurfacesUnsafe(frame_timings_recorder, std::move(tasks)); } else { delegate_.GetIsGpuDisabledSyncSwitch()->Execute( fml::SyncSwitch::Handlers() - .SetIfTrue([&] { raster_status = RasterStatus::kDiscarded; }) + .SetIfTrue([&] { + result.status = DoDrawStatus::kGpuUnavailable; + frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); + frame_timings_recorder.RecordRasterEnd(); + }) .SetIfFalse([&] { - raster_status = DrawToSurfaceUnsafe( - frame_timings_recorder, layer_tree, device_pixel_ratio); + result.resubmitted_item = DrawToSurfacesUnsafe( + frame_timings_recorder, std::move(tasks)); })); } + frame_timings_recorder.AssertInState(FrameTimingsRecorder::State::kRasterEnd); - return raster_status; + return result; } -/// Unsafe because it assumes we have access to the GPU which isn't the case -/// when iOS is backgrounded, for example. -/// \see Rasterizer::DrawToSurface -RasterStatus Rasterizer::DrawToSurfaceUnsafe( +std::unique_ptr Rasterizer::DrawToSurfacesUnsafe( FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio) { - FML_DCHECK(surface_); + std::vector> tasks) { + // TODO(dkwingsmt): The rasterizer only supports rendering a single view + // and that view must be the implicit view. Properly support multi-view + // in the future. + // See https://github.com/flutter/flutter/issues/135530, item 2 & 4. + FML_CHECK(tasks.size() == 1u) << "Unexpected size of " << tasks.size(); + FML_DCHECK(tasks.front()->view_id == kFlutterImplicitViewId); compositor_context_->ui_time().SetLapTime( frame_timings_recorder.GetBuildDuration()); - DlCanvas* embedder_root_canvas = nullptr; + // First traverse: Filter out discarded trees + auto task_iter = tasks.begin(); + while (task_iter != tasks.end()) { + LayerTreeTask& task = **task_iter; + if (delegate_.ShouldDiscardLayerTree(task.view_id, *task.layer_tree)) { + EnsureViewRecord(task.view_id).last_draw_status = + DrawSurfaceStatus::kDiscarded; + task_iter = tasks.erase(task_iter); + } else { + ++task_iter; + } + } + if (tasks.empty()) { + frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); + frame_timings_recorder.RecordRasterEnd(); + return nullptr; + } + if (external_view_embedder_) { FML_DCHECK(!external_view_embedder_->GetUsedThisFrame()); external_view_embedder_->SetUsedThisFrame(true); external_view_embedder_->BeginFrame( - layer_tree.frame_size(), surface_->GetContext(), device_pixel_ratio, - raster_thread_merger_); - embedder_root_canvas = external_view_embedder_->GetRootCanvas(); + // TODO(dkwingsmt): Add all views here. + // See https://github.com/flutter/flutter/issues/135530, item 4. + tasks.front()->layer_tree->frame_size(), surface_->GetContext(), + tasks.front()->device_pixel_ratio, raster_thread_merger_); + } + + std::optional presentation_time = std::nullopt; + // TODO (https://github.com/flutter/flutter/issues/105596): this can be in + // the past and might need to get snapped to future as this frame could + // have been resubmitted. `presentation_time` on SubmitInfo is not set + // in this case. + { + const auto vsync_target_time = frame_timings_recorder.GetVsyncTargetTime(); + if (vsync_target_time > fml::TimePoint::Now()) { + presentation_time = vsync_target_time; + } } frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); + // Second traverse: draw all layer trees. + std::vector> resubmitted_tasks; + for (std::unique_ptr& task : tasks) { + int64_t view_id = task->view_id; + std::unique_ptr layer_tree = std::move(task->layer_tree); + float device_pixel_ratio = task->device_pixel_ratio; + + DrawSurfaceStatus status = DrawToSurfaceUnsafe( + view_id, *layer_tree, device_pixel_ratio, presentation_time); + FML_DCHECK(status != DrawSurfaceStatus::kDiscarded); + + auto& view_record = EnsureViewRecord(task->view_id); + view_record.last_draw_status = status; + if (status == DrawSurfaceStatus::kSuccess) { + view_record.last_successful_task = std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio); + } else if (status == DrawSurfaceStatus::kRetry) { + resubmitted_tasks.push_back(std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio)); + } + } + // TODO(dkwingsmt): Pass in raster cache(s) for all views. + // See https://github.com/flutter/flutter/issues/135530, item 4. + frame_timings_recorder.RecordRasterEnd(&compositor_context_->raster_cache()); + FireNextFrameCallbackIfPresent(); + + if (surface_->GetContext()) { + surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); + } + + if (resubmitted_tasks.empty()) { + return nullptr; + } else { + return std::make_unique( + std::move(resubmitted_tasks), + frame_timings_recorder.CloneUntil( + FrameTimingsRecorder::State::kBuildEnd)); + } +} + +/// \see Rasterizer::DrawToSurfaces +DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe( + int64_t view_id, + flutter::LayerTree& layer_tree, + float device_pixel_ratio, + std::optional presentation_time) { + FML_DCHECK(surface_); + + DlCanvas* embedder_root_canvas = nullptr; + if (external_view_embedder_) { + // TODO(dkwingsmt): Add view ID here. + embedder_root_canvas = external_view_embedder_->GetRootCanvas(); + } + // On Android, the external view embedder deletes surfaces in `BeginFrame`. // // Deleting a surface also clears the GL context. Therefore, acquire the // frame after calling `BeginFrame` as this operation resets the GL context. auto frame = surface_->AcquireFrame(layer_tree.frame_size()); if (frame == nullptr) { - frame_timings_recorder.RecordRasterEnd( - &compositor_context_->raster_cache()); - return RasterStatus::kFailed; + return DrawSurfaceStatus::kFailed; } // If the external view embedder has specified an optional root surface, the @@ -603,7 +717,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( damage = std::make_unique(); auto existing_damage = frame->framebuffer_info().existing_damage; if (existing_damage.has_value() && !force_full_repaint) { - damage->SetPreviousLayerTree(last_layer_tree_.get()); + damage->SetPreviousLayerTree(GetLastLayerTree(view_id)); damage->AddAdditionalDamage(existing_damage.value()); damage->SetClipAlignment( frame->framebuffer_info().horizontal_clip_alignment, @@ -617,25 +731,17 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( ignore_raster_cache = false; } - RasterStatus raster_status = + RasterStatus frame_status = compositor_frame->Raster(layer_tree, // layer tree ignore_raster_cache, // ignore raster cache damage.get() // frame damage ); - if (raster_status == RasterStatus::kFailed || - raster_status == RasterStatus::kSkipAndRetry) { - return raster_status; + if (frame_status == RasterStatus::kSkipAndRetry) { + return DrawSurfaceStatus::kRetry; } SurfaceFrame::SubmitInfo submit_info; - // TODO (https://github.com/flutter/flutter/issues/105596): this can be in - // the past and might need to get snapped to future as this frame could - // have been resubmitted. `presentation_time` on `submit_info` is not set - // in this case. - const auto presentation_time = frame_timings_recorder.GetVsyncTargetTime(); - if (presentation_time > fml::TimePoint::Now()) { - submit_info.presentation_time = presentation_time; - } + submit_info.presentation_time = presentation_time; if (damage) { submit_info.frame_damage = damage->GetFrameDamage(); submit_info.buffer_damage = damage->GetBufferDamage(); @@ -654,22 +760,23 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // Do not update raster cache metrics for kResubmit because that status // indicates that the frame was not actually painted. - if (raster_status != RasterStatus::kResubmit) { + if (frame_status != RasterStatus::kResubmit) { compositor_context_->raster_cache().EndFrame(); } - frame_timings_recorder.RecordRasterEnd( - &compositor_context_->raster_cache()); - FireNextFrameCallbackIfPresent(); - - if (surface_->GetContext()) { - surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); + if (frame_status == RasterStatus::kResubmit) { + return DrawSurfaceStatus::kRetry; + } else { + FML_CHECK(frame_status == RasterStatus::kSuccess); + return DrawSurfaceStatus::kSuccess; } - - return raster_status; } - return RasterStatus::kFailed; + return DrawSurfaceStatus::kFailed; +} + +Rasterizer::ViewRecord& Rasterizer::EnsureViewRecord(int64_t view_id) { + return view_records_[view_id]; } static sk_sp ScreenshotLayerTreeAsPicture( @@ -759,7 +866,11 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { - auto* layer_tree = GetLastLayerTree(); + // TODO(dkwingsmt): Support screenshotting all last layer trees + // when the shell protocol supports multi-views. + // https://github.com/flutter/flutter/issues/135534 + // https://github.com/flutter/flutter/issues/135535 + auto* layer_tree = GetLastLayerTree(kFlutterImplicitViewId); if (layer_tree == nullptr) { FML_LOG(ERROR) << "Last layer tree was null when screenshotting."; return {}; @@ -800,9 +911,9 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( } if (base64_encode) { - size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr); + size_t b64_size = Base64::EncodedSize(data->size()); auto b64_data = SkData::MakeUninitialized(b64_size); - SkBase64::Encode(data->data(), data->size(), b64_data->writable_data()); + Base64::Encode(data->data(), data->size(), b64_data->writable_data()); return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format}; } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index a54dd677a2fdb..e5acbaf8357a9 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -7,6 +7,7 @@ #include #include +#include #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" @@ -48,6 +49,54 @@ class AiksContext; namespace flutter { +// The result status of Rasterizer::Draw. This is only used for unit tests. +enum class DrawStatus { + // The drawing was done without any specified status. + kDone, + // Failed to rasterize the frame because the Rasterizer is not set up. + kNotSetUp, + // Nothing was done, because the call was not on the raster thread. Yielded to + // let this frame be serviced on the right thread. + kYielded, + // Nothing was done, because the pipeline was empty. + kPipelineEmpty, + // Nothing was done, because the GPU was unavailable. + kGpuUnavailable, +}; + +// The result status of drawing to a view. This is only used for unit tests. +enum class DrawSurfaceStatus { + // The layer tree was successfully rasterized. + kSuccess, + // The layer tree must be submitted again. + // + // This can occur on Android when switching the background surface to + // FlutterImageView. On Android, the first frame doesn't make the image + // available to the ImageReader right away. The second frame does. + // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 + // + // This can also occur when the frame is dropped to wait for the thread + // merger to merge the raster and platform threads. + kRetry, + // Failed to rasterize the frame. + kFailed, + // Layer tree was discarded because its size does not match the view size. + // This typically occurs during resizing. + kDiscarded, +}; + +// The information to draw to all views of a frame. +struct FrameItem { + FrameItem(std::vector> tasks, + std::unique_ptr frame_timings_recorder) + : layer_tree_tasks(std::move(tasks)), + frame_timings_recorder(std::move(frame_timings_recorder)) {} + std::vector> layer_tree_tasks; + std::unique_ptr frame_timings_recorder; +}; + +using FramePipeline = Pipeline; + //------------------------------------------------------------------------------ /// The rasterizer is a component owned by the shell that resides on the raster /// task runner. Each shell owns exactly one instance of a rasterizer. The @@ -180,6 +229,7 @@ class Rasterizer final : public SnapshotDelegate, /// collects associated resources. No more rendering may occur /// till the next call to `Rasterizer::Setup` with a new render /// surface. Calling a teardown without a setup is user error. + /// Calling this method multiple times is safe. /// void Teardown(); @@ -211,45 +261,46 @@ class Rasterizer final : public SnapshotDelegate, //---------------------------------------------------------------------------- /// @brief Deallocate the resources for displaying a view. /// - /// This method should be called when a view is removed. + /// This method must be called when a view is removed. /// /// The rasterizer don't need views to be registered. Last-frame /// states for views are recorded when layer trees are rasterized - /// to the view and used during `Rasterizer::DrawLastLayerTree`. + /// to the view and used during `Rasterizer::DrawLastLayerTrees`. /// /// @param[in] view_id The ID of the view. /// void CollectView(int64_t view_id); //---------------------------------------------------------------------------- - /// @brief Sometimes, it may be necessary to render the same frame again - /// without having to wait for the framework to build a whole new - /// layer tree describing the same contents. One such case is when - /// external textures (video or camera streams for example) are - /// updated in an otherwise static layer tree. To support this use - /// case, the rasterizer holds onto the last rendered layer tree. + /// @brief Returns the last successfully drawn layer tree for the given + /// view, or nullptr if there isn't any. This is useful during + /// `DrawLastLayerTrees` and computing frame damage. /// /// @bug https://github.com/flutter/flutter/issues/33939 /// /// @return A pointer to the last layer or `nullptr` if this rasterizer - /// has never rendered a frame. + /// has never rendered a frame to the given view. /// - flutter::LayerTree* GetLastLayerTree(); + flutter::LayerTree* GetLastLayerTree(int64_t view_id); //---------------------------------------------------------------------------- - /// @brief Draws a last layer tree to the render surface. This may seem - /// entirely redundant at first glance. After all, on surface loss - /// and re-acquisition, the framework generates a new layer tree. - /// Otherwise, why render the same contents to the screen again? - /// This is used as an optimization in cases where there are - /// external textures (video or camera streams for example) in - /// referenced in the layer tree. These textures may be updated at - /// a cadence different from that of the Flutter application. - /// Flutter can re-render the layer tree with just the updated - /// textures instead of waiting for the framework to do the work - /// to generate the layer tree describing the same contents. - /// - void DrawLastLayerTree( + /// @brief Draws the last layer trees with their last configuration. This + /// may seem entirely redundant at first glance. After all, on + /// surface loss and re-acquisition, the framework generates a new + /// layer tree. Otherwise, why render the same contents to the + /// screen again? This is used as an optimization in cases where + /// there are external textures (video or camera streams for + /// example) in referenced in the layer tree. These textures may + /// be updated at a cadence different from that of the Flutter + /// application. Flutter can re-render the layer tree with just + /// the updated textures instead of waiting for the framework to + /// do the work to generate the layer tree describing the same + /// contents. + /// + /// Calling this method clears all last layer trees + /// (GetLastLayerTree). + /// + void DrawLastLayerTrees( std::unique_ptr frame_timings_recorder); // |SnapshotDelegate| @@ -286,7 +337,7 @@ class Rasterizer final : public SnapshotDelegate, /// @param[in] pipeline The layer tree pipeline to take the next layer tree /// to render from. /// - RasterStatus Draw(const std::shared_ptr& pipeline); + DrawStatus Draw(const std::shared_ptr& pipeline); //---------------------------------------------------------------------------- /// @brief The type of the screenshot to obtain of the previously @@ -511,18 +562,54 @@ class Rasterizer final : public SnapshotDelegate, /// void DisableThreadMergerIfNeeded(); + //---------------------------------------------------------------------------- + /// @brief Returns whether TearDown has been called. + /// + /// This method is used only in unit tests. + /// + bool IsTornDown(); + + //---------------------------------------------------------------------------- + /// @brief Returns the last status of drawing the specific view. + /// + /// This method is used only in unit tests. + /// + std::optional GetLastDrawStatus(int64_t view_id); + private: - // The result of `DoDraw`. - // - // Normally `DoDraw` returns simply a raster status. However, sometimes we - // need to attempt to rasterize the layer tree again. This happens when - // layer_tree has not successfully rasterized due to changes in the thread - // configuration, in which case the resubmitted task will be inserted to the - // front of the pipeline. + // The result status of DoDraw, DrawToSurfaces, and DrawToSurfacesUnsafe. + enum class DoDrawStatus { + // The drawing was done without any specified status. + kDone, + // Frame has been successfully rasterized, but there are additional items + // in the pipeline waiting to be consumed. This is currently only used when + // thread configuration change occurs. + kEnqueuePipeline, + // Failed to rasterize the frame because the Rasterizer is not set up. + kNotSetUp, + // Nothing was done, because GPU was unavailable. + kGpuUnavailable, + }; + + // The result of DoDraw. struct DoDrawResult { - RasterStatus raster_status = RasterStatus::kFailed; + // The overall status of the drawing process. + // + // The status of drawing a specific view is available at GetLastDrawStatus. + DoDrawStatus status = DoDrawStatus::kDone; + + // The frame item that needs to be submitted again. + // + // See RasterStatus::kResubmit and kSkipAndRetry for when it happens. + // + // If `resubmitted_item` is not null, its `tasks` is guaranteed to be + // non-empty. + std::unique_ptr resubmitted_item; + }; - std::unique_ptr resubmitted_layer_tree_item; + struct ViewRecord { + std::unique_ptr last_successful_task; + std::optional last_draw_status; }; // |SnapshotDelegate| @@ -580,32 +667,58 @@ class Rasterizer final : public SnapshotDelegate, GrDirectContext* surface_context, bool compressed); + // This method starts with the frame timing recorder at build end. This + // method might push it to raster end and get the recorded time, or abort in + // the middle and not get the recorded time. DoDrawResult DoDraw( std::unique_ptr frame_timings_recorder, - std::unique_ptr layer_tree, - float device_pixel_ratio); + std::vector> tasks); - RasterStatus DrawToSurface(FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio); + // This method pushes the frame timing recorder from build end to raster end. + DoDrawResult DrawToSurfaces( + FrameTimingsRecorder& frame_timings_recorder, + std::vector> tasks); + + // Draws the specified layer trees to views, assuming we have access to the + // GPU. + // + // If any layer trees need resubmitting, this method returns the frame item to + // be resubmitted. Otherwise, it returns nullptr. + // + // Unsafe because it assumes we have access to the GPU which isn't the case + // when iOS is backgrounded, for example. + // + // This method pushes the frame timing recorder from build end to raster end. + std::unique_ptr DrawToSurfacesUnsafe( + FrameTimingsRecorder& frame_timings_recorder, + std::vector> tasks); + + // Draws the layer tree to the specified view, assuming we have access to the + // GPU. + // + // This method is not affiliated with the frame timing recorder, but must be + // included between the RasterStart and RasterEnd. + DrawSurfaceStatus DrawToSurfaceUnsafe( + int64_t view_id, + flutter::LayerTree& layer_tree, + float device_pixel_ratio, + std::optional presentation_time); - RasterStatus DrawToSurfaceUnsafe(FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio); + ViewRecord& EnsureViewRecord(int64_t view_id); void FireNextFrameCallbackIfPresent(); - static bool ShouldResubmitFrame(const RasterStatus& raster_status); + static bool ShouldResubmitFrame(const DoDrawResult& result); + static DrawStatus ToDrawStatus(DoDrawStatus status); + bool is_torn_down_; Delegate& delegate_; MakeGpuImageBehavior gpu_image_behavior_; std::weak_ptr impeller_context_; std::unique_ptr surface_; std::unique_ptr snapshot_surface_producer_; std::unique_ptr compositor_context_; - // This is the last successfully rasterized layer tree. - std::unique_ptr last_layer_tree_; - float last_device_pixel_ratio_; + std::unordered_map view_records_; fml::closure next_frame_callback_; bool user_override_resource_cache_bytes_; std::optional max_cache_bytes_; diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 545df3b6d3854..0bf32b8a78e50 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -32,6 +32,17 @@ namespace flutter { namespace { constexpr float kDevicePixelRatio = 2.0f; +constexpr int64_t kImplicitViewId = 0; + +std::vector> SingleLayerTreeList( + int64_t view_id, + std::unique_ptr layer_tree, + float pixel_ratio) { + std::vector> tasks; + tasks.push_back(std::make_unique( + view_id, std::move(layer_tree), pixel_ratio)); + return tasks; +} class MockDelegate : public Rasterizer::Delegate { public: @@ -152,7 +163,7 @@ TEST(RasterizerTest, drawEmptyPipeline) { rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); rasterizer->Draw(pipeline); latch.Signal(); @@ -188,7 +199,8 @@ TEST(RasterizerTest, framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, framebuffer_info, + /*surface=*/ + nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); @@ -214,13 +226,14 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -259,7 +272,8 @@ TEST( SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, framebuffer_info, + /*surface=*/ + nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); @@ -281,12 +295,13 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -330,7 +345,8 @@ TEST( framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, framebuffer_info, + /*surface=*/ + nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); @@ -353,11 +369,13 @@ TEST( rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -397,16 +415,18 @@ TEST(RasterizerTest, framebuffer_info.supports_readback = true; auto surface_frame1 = std::make_unique( - /*surface=*/nullptr, framebuffer_info, + /*surface=*/ + nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); auto surface_frame2 = std::make_unique( - /*surface=*/nullptr, framebuffer_info, + /*surface=*/ + nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); - // Prepare two frames for Draw() and DrawLastLayerTree(). + // Prepare two frames for Draw() and DrawLastLayerTrees(). EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame1)))) .WillOnce(Return(ByMove(std::move(surface_frame2)))); @@ -427,11 +447,13 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -441,9 +463,9 @@ TEST(RasterizerTest, ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); rasterizer->Draw(pipeline); - // The DrawLastLayerTree() will respectively call BeginFrame(), SubmitFrame() + // The DrawLastLayerTrees() will respectively call BeginFrame(), SubmitFrame() // and EndFrame() one more time, totally 2 times. - rasterizer->DrawLastLayerTree(CreateFinishedBuildRecorder()); + rasterizer->DrawLastLayerTrees(CreateFinishedBuildRecorder()); } TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { @@ -476,12 +498,13 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -507,6 +530,10 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings)); EXPECT_CALL(delegate, GetTaskRunners()) .WillRepeatedly(ReturnRef(task_runners)); + auto is_gpu_disabled_sync_switch = + std::make_shared(false); + ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) + .WillByDefault(Return(is_gpu_disabled_sync_switch)); auto rasterizer = std::make_unique(delegate); auto surface = std::make_unique>(); @@ -532,19 +559,22 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); // Always discard the layer tree. ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(true)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kDiscarded); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); + EXPECT_EQ(rasterizer->GetLastDrawStatus(kImplicitViewId), + DrawSurfaceStatus::kDiscarded); latch.Signal(); }); latch.Wait(); @@ -585,10 +615,10 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kFailed); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kPipelineEmpty); latch.Signal(); }); latch.Wait(); @@ -620,7 +650,8 @@ TEST(RasterizerTest, SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, + /*surface=*/ + nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); @@ -635,12 +666,13 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -678,7 +710,8 @@ TEST( framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, + /*surface=*/ + nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); @@ -693,18 +726,19 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kSuccess); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); latch.Signal(); }); latch.Wait(); @@ -737,7 +771,8 @@ TEST( framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, + /*surface=*/ + nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); @@ -751,18 +786,19 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kSuccess); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); latch.Signal(); }); latch.Wait(); @@ -795,7 +831,8 @@ TEST( framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( - /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, + /*surface=*/ + nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); @@ -808,18 +845,19 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kDiscarded); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kGpuUnavailable); latch.Signal(); }); latch.Wait(); @@ -864,18 +902,21 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kFailed); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); + EXPECT_EQ(rasterizer->GetLastDrawStatus(kImplicitViewId), + DrawSurfaceStatus::kFailed); latch.Signal(); }); latch.Wait(); @@ -913,7 +954,8 @@ TEST(RasterizerTest, SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; return std::make_unique( - /*surface=*/nullptr, framebuffer_info, + /*surface=*/ + nullptr, framebuffer_info, /*submit_callback=*/ [](const SurfaceFrame& frame, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); @@ -942,13 +984,14 @@ TEST(RasterizerTest, thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i]), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -1113,13 +1156,14 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i]), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -1196,12 +1240,13 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(first_timestamp), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder(first_timestamp)); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 261a41fff2767..731cdcc19d137 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -23,6 +23,7 @@ #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/base64.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" @@ -39,7 +40,6 @@ #include "third_party/skia/include/codec/SkWbmpDecoder.h" #include "third_party/skia/include/codec/SkWebpDecoder.h" #include "third_party/skia/include/core/SkGraphics.h" -#include "third_party/skia/include/utils/SkBase64.h" #include "third_party/tonic/common/log.h" namespace flutter { @@ -104,7 +104,7 @@ void PerformInitializationTasks(Settings& settings) { { fml::LogSettings log_settings; log_settings.min_log_level = - settings.verbose_logging ? fml::LOG_INFO : fml::LOG_ERROR; + settings.verbose_logging ? fml::kLogInfo : fml::kLogError; fml::SetLogSettings(log_settings); } @@ -144,31 +144,36 @@ void PerformInitializationTasks(Settings& settings) { } // namespace -std::unique_ptr Shell::Create( - const PlatformData& platform_data, - const TaskRunners& task_runners, - Settings settings, - const Shell::CreateCallback& on_create_platform_view, - const Shell::CreateCallback& on_create_rasterizer, - bool is_gpu_disabled) { - // This must come first as it initializes tracing. - PerformInitializationTasks(settings); - - TRACE_EVENT0("flutter", "Shell::Create"); - +std::pair> +Shell::InferVmInitDataFromSettings(Settings& settings) { // Always use the `vm_snapshot` and `isolate_snapshot` provided by the // settings to launch the VM. If the VM is already running, the snapshot // arguments are ignored. auto vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings); auto isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings); auto vm = DartVMRef::Create(settings, vm_snapshot, isolate_snapshot); - FML_CHECK(vm) << "Must be able to initialize the VM."; // If the settings did not specify an `isolate_snapshot`, fall back to the // one the VM was launched with. if (!isolate_snapshot) { isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot(); } + return {std::move(vm), isolate_snapshot}; +} + +std::unique_ptr Shell::Create( + const PlatformData& platform_data, + const TaskRunners& task_runners, + Settings settings, + const Shell::CreateCallback& on_create_platform_view, + const Shell::CreateCallback& on_create_rasterizer, + bool is_gpu_disabled) { + // This must come first as it initializes tracing. + PerformInitializationTasks(settings); + + TRACE_EVENT0("flutter", "Shell::Create"); + + auto [vm, isolate_snapshot] = InferVmInitDataFromSettings(settings); auto resource_cache_limit_calculator = std::make_shared( settings.resource_cache_max_bytes_threshold); @@ -1215,16 +1220,16 @@ void Shell::OnAnimatorUpdateLatestFrameTargetTime( } // |Animator::Delegate| -void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { +void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { FML_DCHECK(is_set_up_); task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( [&waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), - weak_pipeline = std::weak_ptr(pipeline)]() mutable { + weak_pipeline = std::weak_ptr(pipeline)]() mutable { if (rasterizer) { - std::shared_ptr pipeline = weak_pipeline.lock(); + std::shared_ptr pipeline = weak_pipeline.lock(); if (pipeline) { rasterizer->Draw(pipeline); } @@ -1238,7 +1243,7 @@ void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { } // |Animator::Delegate| -void Shell::OnAnimatorDrawLastLayerTree( +void Shell::OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { FML_DCHECK(is_set_up_); @@ -1246,7 +1251,7 @@ void Shell::OnAnimatorDrawLastLayerTree( [rasterizer = rasterizer_->GetWeakPtr(), frame_timings_recorder = std::move(frame_timings_recorder)]() mutable { if (rasterizer) { - rasterizer->DrawLastLayerTree(std::move(frame_timings_recorder)); + rasterizer->DrawLastLayerTrees(std::move(frame_timings_recorder)); } }); @@ -1690,6 +1695,11 @@ bool Shell::OnServiceProtocolScreenshotSKP( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); + if (settings_.enable_impeller) { + ServiceProtocolFailureError( + response, "Cannot capture SKP screenshot with Impeller enabled."); + return false; + } auto screenshot = rasterizer_->ScreenshotLastLayerTree( Rasterizer::ScreenshotType::SkiaPicture, true); if (screenshot.data) { @@ -1835,11 +1845,10 @@ bool Shell::OnServiceProtocolGetSkSLs( PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); std::vector sksls = persistent_cache->LoadSkSLs(); for (const auto& sksl : sksls) { - size_t b64_size = - SkBase64::Encode(sksl.value->data(), sksl.value->size(), nullptr); + size_t b64_size = Base64::EncodedSize(sksl.value->size()); sk_sp b64_data = SkData::MakeUninitialized(b64_size + 1); char* b64_char = static_cast(b64_data->writable_data()); - SkBase64::Encode(sksl.value->data(), sksl.value->size(), b64_char); + Base64::Encode(sksl.value->data(), sksl.value->size(), b64_char); b64_char[b64_size] = 0; // make it null terminated for printing rapidjson::Value shader_value(b64_char, response->GetAllocator()); std::string_view key_view(reinterpret_cast(sksl.key->data()), @@ -1963,10 +1972,18 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( rapidjson::Document* response) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); + // Impeller does not support this protocol method. + if (io_manager_->GetImpellerContext()) { + const char* error = "Raster status not supported on Impeller backend."; + ServiceProtocolFailureError(response, error); + return false; + } + // TODO(dkwingsmt): This method only handles view #0, including the snapshot // and the frame size. We need to adapt this method to multi-view. // https://github.com/flutter/flutter/issues/131892 - if (auto last_layer_tree = rasterizer_->GetLastLayerTree()) { + int64_t view_id = kFlutterImplicitViewId; + if (auto last_layer_tree = rasterizer_->GetLastLayerTree(view_id)) { auto& allocator = response->GetAllocator(); response->SetObject(); response->AddMember("type", "RenderFrameWithRasterStats", allocator); @@ -1981,7 +1998,7 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( frame_timings_recorder->RecordBuildEnd(now); last_layer_tree->enable_leaf_layer_tracing(true); - rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder)); + rasterizer_->DrawLastLayerTrees(std::move(frame_timings_recorder)); last_layer_tree->enable_leaf_layer_tracing(false); rapidjson::Value snapshots; @@ -1997,7 +2014,7 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( response->AddMember("snapshots", snapshots, allocator); - const auto& frame_size = ExpectedFrameSize(kFlutterImplicitViewId); + const auto& frame_size = ExpectedFrameSize(view_id); response->AddMember("frame_width", frame_size.width(), allocator); response->AddMember("frame_height", frame_size.height(), allocator); diff --git a/shell/common/shell.h b/shell/common/shell.h index b8c9c31052df6..9eb5bd1e9b226 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -438,15 +438,29 @@ class Shell final : public PlatformView::Delegate, const std::shared_ptr GetConcurrentWorkerTaskRunner() const; + // Infer the VM ref and the isolate snapshot based on the settings. + // + // If the VM is already running, the settings are ignored, but the returned + // isolate snapshot always prioritize what is specified by the settings, and + // falls back to the one VM was launched with. + // + // This function is what Shell::Create uses to infer snapshot settings. + // + // TODO(dkwingsmt): Extracting this method is part of a bigger change. If the + // entire change is not eventually landed, we should merge this method back + // to Create. https://github.com/flutter/flutter/issues/136826 + static std::pair> + InferVmInitDataFromSettings(Settings& settings); + private: using ServiceProtocolHandler = std::function; /// A collection of message channels (by name) that have sent at least one - /// message from a non-platform thread. Used to prevent printing the error log - /// more than once per channel, as a badly behaving plugin may send multiple - /// messages per second indefinitely. + /// message from a non-platform thread. Used to prevent printing the error + /// log more than once per channel, as a badly behaving plugin may send + /// multiple messages per second indefinitely. std::mutex misbehaving_message_channels_mutex_; std::set misbehaving_message_channels_; const TaskRunners task_runners_; @@ -497,19 +511,20 @@ class Shell final : public PlatformView::Delegate, bool frame_timings_report_scheduled_ = false; // Vector of FrameTiming::kCount * n timestamps for n frames whose timings - // have not been reported yet. Vector of ints instead of FrameTiming is stored - // here for easier conversions to Dart objects. + // have not been reported yet. Vector of ints instead of FrameTiming is + // stored here for easier conversions to Dart objects. std::vector unreported_timings_; - /// Manages the displays. This class is thread safe, can be accessed from any - /// of the threads. + /// Manages the displays. This class is thread safe, can be accessed from + /// any of the threads. std::unique_ptr display_manager_; // protects expected_frame_size_ which is set on platform thread and read on // raster thread std::mutex resize_mutex_; - // used to discard wrong size layer tree produced during interactive resizing + // used to discard wrong size layer tree produced during interactive + // resizing std::unordered_map expected_frame_sizes_; // Used to communicate the right frame bounds via service protocol. @@ -640,10 +655,10 @@ class Shell final : public PlatformView::Delegate, fml::TimePoint frame_target_time) override; // |Animator::Delegate| - void OnAnimatorDraw(std::shared_ptr pipeline) override; + void OnAnimatorDraw(std::shared_ptr pipeline) override; // |Animator::Delegate| - void OnAnimatorDrawLastLayerTree( + void OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) override; // |Engine::Delegate| @@ -746,7 +761,8 @@ class Shell final : public PlatformView::Delegate, // Service protocol handler // - // The returned SkSLs are base64 encoded. Decode before storing them to files. + // The returned SkSLs are base64 encoded. Decode before storing them to + // files. bool OnServiceProtocolGetSkSLs( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response); @@ -767,8 +783,8 @@ class Shell final : public PlatformView::Delegate, // Service protocol handler // - // Forces the FontCollection to reload the font manifest. Used to support hot - // reload for fonts. + // Forces the FontCollection to reload the font manifest. Used to support + // hot reload for fonts. bool OnServiceProtocolReloadAssetFonts( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response); diff --git a/shell/common/shell_io_manager.cc b/shell/common/shell_io_manager.cc index 4a51c2f8d96d9..59de967e1cc0f 100644 --- a/shell/common/shell_io_manager.cc +++ b/shell/common/shell_io_manager.cc @@ -8,21 +8,23 @@ #include "flutter/fml/message_loop.h" #include "flutter/shell/common/context_options.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" +#include "third_party/skia/include/gpu/gl/GrGLTypes.h" namespace flutter { sk_sp ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend backend, + GrBackendApi backend, const sk_sp& gl_interface) { #if SK_GL - if (backend != GrBackend::kOpenGL_GrBackend) { + if (backend != GrBackendApi::kOpenGL) { return nullptr; } const auto options = MakeDefaultContextOptions(ContextType::kResource); - if (auto context = GrDirectContext::MakeGL(gl_interface, options)) { + if (auto context = GrDirectContexts::MakeGL(gl_interface, options)) { // Do not cache textures created by the image decoder. These textures // should be deleted when they are no longer referenced by an SkImage. context->setResourceCacheLimit(0); diff --git a/shell/common/shell_io_manager.h b/shell/common/shell_io_manager.h index b178541939771..9c105a80d174c 100644 --- a/shell/common/shell_io_manager.h +++ b/shell/common/shell_io_manager.h @@ -12,6 +12,9 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/io_manager.h" #include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/GrTypes.h" + +struct GrGLInterface; namespace flutter { @@ -21,7 +24,7 @@ class ShellIOManager final : public IOManager { // supply to the IOManager. The platforms may create the context themselves if // they so desire. static sk_sp CreateCompatibleResourceLoadingContext( - GrBackend backend, + GrBackendApi backend, const sk_sp& gl_interface); ShellIOManager( diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 1ea8c7f0b3248..bd952747ca58f 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -22,6 +22,39 @@ namespace testing { constexpr int64_t kImplicitViewId = 0; +FrameContent ViewContent::NoViews() { + return std::map(); +} + +FrameContent ViewContent::DummyView(double width, double height) { + FrameContent result; + result[kImplicitViewId] = ViewContent{ + .viewport_metrics = {1.0, width, height, 22, 0}, + .builder = {}, + }; + return result; +} + +FrameContent ViewContent::DummyView(flutter::ViewportMetrics viewport_metrics) { + FrameContent result; + result[kImplicitViewId] = ViewContent{ + .viewport_metrics = std::move(viewport_metrics), + .builder = {}, + }; + return result; +} + +FrameContent ViewContent::ImplicitView(double width, + double height, + LayerTreeBuilder builder) { + FrameContent result; + result[kImplicitViewId] = ViewContent{ + .viewport_metrics = {1.0, width, height, 22, 0}, + .builder = std::move(builder), + }; + return result; +} + ShellTest::ShellTest() : thread_host_("io.flutter.test." + GetCurrentTestName() + ".", ThreadHost::Type::Platform | ThreadHost::Type::IO | @@ -92,16 +125,18 @@ void ShellTest::RestartEngine(Shell* shell, RunConfiguration configuration) { ASSERT_TRUE(restarted.get_future().get()); } -void ShellTest::VSyncFlush(Shell* shell, bool& will_draw_new_frame) { +void ShellTest::VSyncFlush(Shell* shell, bool* will_draw_new_frame) { fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), - [shell, &will_draw_new_frame, &latch] { + [shell, will_draw_new_frame, &latch] { // The following UI task ensures that all previous UI tasks are flushed. fml::AutoResetWaitableEvent ui_latch; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&ui_latch, &will_draw_new_frame]() { - will_draw_new_frame = true; + [&ui_latch, will_draw_new_frame]() { + if (will_draw_new_frame != nullptr) { + *will_draw_new_frame = true; + } ui_latch.Signal(); }); @@ -154,6 +189,7 @@ void ShellTest::SetViewportMetrics(Shell* shell, double width, double height) { std::make_unique(); recorder->RecordVsync(frame_begin_time, frame_end_time); engine->animator_->BeginFrame(std::move(recorder)); + engine->animator_->EndFrame(); } latch.Signal(); }); @@ -172,23 +208,22 @@ void ShellTest::NotifyIdle(Shell* shell, fml::TimeDelta deadline) { latch.Wait(); } -void ShellTest::PumpOneFrame(Shell* shell, - double width, - double height, - LayerTreeBuilder builder) { - PumpOneFrame(shell, {1.0, width, height, 22, 0}, std::move(builder)); +void ShellTest::PumpOneFrame(Shell* shell) { + PumpOneFrame(shell, ViewContent::DummyView()); } -void ShellTest::PumpOneFrame(Shell* shell, - const flutter::ViewportMetrics& viewport_metrics, - LayerTreeBuilder builder) { +void ShellTest::PumpOneFrame(Shell* shell, FrameContent frame_content) { // Set viewport to nonempty, and call Animator::BeginFrame to make the layer // tree pipeline nonempty. Without either of this, the layer tree below // won't be rasterized. fml::AutoResetWaitableEvent latch; + fml::WeakPtr runtime_delegate = shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, engine = shell->weak_engine_, viewport_metrics]() { - engine->SetViewportMetrics(kImplicitViewId, viewport_metrics); + [&latch, engine = shell->weak_engine_, &frame_content, + runtime_delegate]() { + for (auto& [view_id, view_content] : frame_content) { + engine->SetViewportMetrics(view_id, view_content.viewport_metrics); + } const auto frame_begin_time = fml::TimePoint::Now(); const auto frame_end_time = frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0); @@ -196,28 +231,28 @@ void ShellTest::PumpOneFrame(Shell* shell, std::make_unique(); recorder->RecordVsync(frame_begin_time, frame_end_time); engine->animator_->BeginFrame(std::move(recorder)); - latch.Signal(); - }); - latch.Wait(); - latch.Reset(); - // Call |Render| to rasterize a layer tree and trigger |OnFrameRasterized| - fml::WeakPtr runtime_delegate = shell->weak_engine_; - shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, runtime_delegate, &builder, viewport_metrics]() { - SkMatrix identity; - identity.setIdentity(); - auto root_layer = std::make_shared(identity); - auto layer_tree = std::make_unique( - LayerTree::Config{.root_layer = root_layer}, - SkISize::Make(viewport_metrics.physical_width, - viewport_metrics.physical_height)); - float device_pixel_ratio = - static_cast(viewport_metrics.device_pixel_ratio); - if (builder) { - builder(root_layer); + // The BeginFrame phase and the EndFrame phase must be performed in a + // single task, otherwise a normal vsync might be inserted in between, + // causing flaky assertion errors. + + for (auto& [view_id, view_content] : frame_content) { + SkMatrix identity; + identity.setIdentity(); + auto root_layer = std::make_shared(identity); + auto layer_tree = std::make_unique( + LayerTree::Config{.root_layer = root_layer}, + SkISize::Make(view_content.viewport_metrics.physical_width, + view_content.viewport_metrics.physical_height)); + float device_pixel_ratio = static_cast( + view_content.viewport_metrics.device_pixel_ratio); + if (view_content.builder) { + view_content.builder(root_layer); + } + runtime_delegate->Render(view_id, std::move(layer_tree), + device_pixel_ratio); } - runtime_delegate->Render(std::move(layer_tree), device_pixel_ratio); + engine->animator_->EndFrame(); latch.Signal(); }); latch.Wait(); @@ -382,5 +417,9 @@ size_t ShellTest::GetLiveTrackedPathCount( }); } +void ShellTest::TurnOffGPU(Shell* shell, bool value) { + shell->is_gpu_disabled_sync_switch_->SetSwitch(value); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 1fd588e3b8447..c11ad1174dc88 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -29,6 +29,38 @@ namespace flutter { namespace testing { +// The signature of ViewContent::builder. +using LayerTreeBuilder = + std::function root)>; +struct ViewContent; +// Defines the content to be rendered to all views of a frame in PumpOneFrame. +using FrameContent = std::map; +// Defines the content to be rendered to a view in PumpOneFrame. +struct ViewContent { + flutter::ViewportMetrics viewport_metrics; + // Given the root layer, this callback builds the layer tree to be rasterized + // in PumpOneFrame. + LayerTreeBuilder builder; + + // Build a frame with no views. This is useful when PumpOneFrame is used just + // to schedule the frame while the frame content is defined by other means. + static FrameContent NoViews(); + + // Build a frame with a single implicit view with the specific size and no + // content. + static FrameContent DummyView(double width = 1, double height = 1); + + // Build a frame with a single implicit view with the specific viewport + // metrics and no content. + static FrameContent DummyView(flutter::ViewportMetrics viewport_metrics); + + // Build a frame with a single implicit view with the specific size and + // content. + static FrameContent ImplicitView(double width, + double height, + LayerTreeBuilder builder); +}; + class ShellTest : public FixtureTest { public: struct Config { @@ -70,24 +102,14 @@ class ShellTest : public FixtureTest { static void RestartEngine(Shell* shell, RunConfiguration configuration); /// Issue as many VSYNC as needed to flush the UI tasks so far, and reset - /// the `will_draw_new_frame` to true. - static void VSyncFlush(Shell* shell, bool& will_draw_new_frame); - - /// Given the root layer, this callback builds the layer tree to be rasterized - /// in PumpOneFrame. - using LayerTreeBuilder = - std::function root)>; + /// the content of `will_draw_new_frame` to true if it's not nullptr. + static void VSyncFlush(Shell* shell, bool* will_draw_new_frame = nullptr); static void SetViewportMetrics(Shell* shell, double width, double height); static void NotifyIdle(Shell* shell, fml::TimeDelta deadline); - static void PumpOneFrame(Shell* shell, - double width = 1, - double height = 1, - LayerTreeBuilder = {}); - static void PumpOneFrame(Shell* shell, - const flutter::ViewportMetrics& viewport_metrics, - LayerTreeBuilder = {}); + static void PumpOneFrame(Shell* shell); + static void PumpOneFrame(Shell* shell, FrameContent frame_content); static void DispatchFakePointerData(Shell* shell); static void DispatchPointerData(Shell* shell, std::unique_ptr packet); @@ -135,6 +157,8 @@ class ShellTest : public FixtureTest { static size_t GetLiveTrackedPathCount( const std::shared_ptr& tracker); + static void TurnOffGPU(Shell* shell, bool value); + private: ThreadHost thread_host_; diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 38170118e2072..d8ce19114f140 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -14,15 +14,12 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" #if OS_FUCHSIA #define VULKAN_SO_PATH "libvulkan.so" -#elif FML_OS_MACOSX -#define VULKAN_SO_PATH "libvk_swiftshader.dylib" -#elif FML_OS_WIN -#define VULKAN_SO_PATH "vk_swiftshader.dll" #else -#define VULKAN_SO_PATH "libvk_swiftshader.so" +#include "flutter/vulkan/swiftshader_path.h" #endif namespace flutter { @@ -142,7 +139,7 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaGrContext() { MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kVulkan); sk_sp context = - GrDirectContext::MakeVulkan(backend_context, options); + GrDirectContexts::MakeVulkan(backend_context, options); if (context == nullptr) { FML_DLOG(ERROR) << "Failed to create GrDirectContext"; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2371fa9113aa7..83c8bd3488c50 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -42,6 +42,7 @@ #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiter_fallback.h" +#include "flutter/shell/common/vsync_waiters_test.h" #include "flutter/shell/version/version.h" #include "flutter/testing/mock_canvas.h" #include "flutter/testing/testing.h" @@ -310,30 +311,26 @@ static bool ValidateShell(Shell* shell) { return true; } -static bool RasterizerHasLayerTree(Shell* shell) { +static bool RasterizerIsTornDown(Shell* shell) { fml::AutoResetWaitableEvent latch; - bool has_layer_tree = false; + bool is_torn_down = false; fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetRasterTaskRunner(), - [shell, &latch, &has_layer_tree]() { - has_layer_tree = shell->GetRasterizer()->GetLastLayerTree() != nullptr; + [shell, &latch, &is_torn_down]() { + is_torn_down = shell->GetRasterizer()->IsTornDown(); latch.Signal(); }); latch.Wait(); - return has_layer_tree; + return is_torn_down; } static void ValidateDestroyPlatformView(Shell* shell) { ASSERT_TRUE(shell != nullptr); ASSERT_TRUE(shell->IsSetup()); - // To validate destroy platform view, we must ensure the rasterizer has a - // layer tree before the platform view is destroyed. - ASSERT_TRUE(RasterizerHasLayerTree(shell)); - + ASSERT_FALSE(RasterizerIsTornDown(shell)); ShellTest::PlatformViewNotifyDestroyed(shell); - // Validate the layer tree is destroyed - ASSERT_FALSE(RasterizerHasLayerTree(shell)); + ASSERT_TRUE(RasterizerIsTornDown(shell)); } static std::string CreateFlagsString(std::vector& flags) { @@ -879,7 +876,7 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) { root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); end_frame_latch.Wait(); ASSERT_TRUE(end_frame_called); @@ -953,7 +950,7 @@ TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) { backdrop_filter_layer->Add(platform_view_layer2); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); end_frame_latch.Wait(); ASSERT_EQ(visited_platform_views, (std::vector{50, 75})); ASSERT_TRUE(stack_75.is_empty()); @@ -1014,7 +1011,7 @@ TEST_F(ShellTest, root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); end_frame_latch.Wait(); ASSERT_TRUE(end_frame_called); @@ -1060,9 +1057,12 @@ TEST_F(ShellTest, OnPlatformViewDestroyDisablesThreadMerger) { root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); auto result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); + // Wait for the rasterizer to process the frame. WaitForFirstFrame only waits + // for the Animator, but end_frame_callback is called by the Rasterizer. + PostSync(shell->GetTaskRunners().GetRasterTaskRunner(), [] {}); ASSERT_TRUE(result.ok()) << "Result: " << static_cast(result.code()) << ": " << result.message(); @@ -1127,12 +1127,12 @@ TEST_F(ShellTest, OnPlatformViewDestroyAfterMergingThreads) { root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); // Pump one frame to trigger thread merging. end_frame_latch.Wait(); // Pump another frame to ensure threads are merged and a regular layer tree is // submitted. - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); // Threads are merged here. PlatformViewNotifyDestroy should be executed // successfully. ASSERT_TRUE(fml::TaskRunnerChecker::RunsOnTheSameThread( @@ -1196,7 +1196,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) { root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); // Pump one frame and threads aren't merged end_frame_latch.Wait(); ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread( @@ -1207,7 +1207,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) { // threads external_view_embedder->UpdatePostPrerollResult( PostPrerollResult::kResubmitFrame); - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); // Now destroy the platform view immediately. // Two things can happen here: @@ -1263,7 +1263,7 @@ TEST_F(ShellTest, SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false); root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); end_frame_latch.Wait(); // Threads should not be merged. @@ -1302,7 +1302,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) { SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false); root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); // Threads should not be merged. ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread( @@ -1368,7 +1368,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithStaticThreadMerging) { SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false); root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); end_frame_latch.Wait(); ValidateDestroyPlatformView(shell.get()); @@ -1414,7 +1414,7 @@ TEST_F(ShellTest, GetUsedThisFrameShouldBeSetBeforeEndFrame) { SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false); root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); end_frame_latch.Wait(); ASSERT_FALSE(used_this_frame); @@ -1564,10 +1564,11 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { configuration.SetEntrypoint("emptyMain"); RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get(), {1.0, 0.0, 0.0, 22, 0}); + PumpOneFrame(shell.get(), ViewContent::DummyView({1.0, 0.0, 0.0, 22, 0})); fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); - ASSERT_FALSE(result.ok()); - ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); + EXPECT_FALSE(result.ok()); + EXPECT_EQ(result.message(), "timeout"); + EXPECT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); DestroyShell(std::move(shell)); } @@ -2083,6 +2084,7 @@ TEST_F(ShellTest, CanScheduleFrameFromPlatform) { TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) { bool is_on_begin_frame_called = false; bool is_secondary_callback_called = false; + bool test_started = false; Settings settings = CreateSettingsForFixture(); TaskRunners task_runners = GetTaskRunnersForFixture(); fml::AutoResetWaitableEvent latch; @@ -2092,12 +2094,18 @@ TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) { fml::CountDownLatch count_down_latch(2); AddNativeCallback("NativeOnBeginFrame", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + if (!test_started) { + return; + } EXPECT_FALSE(is_on_begin_frame_called); EXPECT_FALSE(is_secondary_callback_called); is_on_begin_frame_called = true; count_down_latch.CountDown(); })); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .task_runners = task_runners, + }); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -2110,12 +2118,16 @@ TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetUITaskRunner(), [&]() { shell->GetEngine()->ScheduleSecondaryVsyncCallback(0, [&]() { + if (!test_started) { + return; + } EXPECT_TRUE(is_on_begin_frame_called); EXPECT_FALSE(is_secondary_callback_called); is_secondary_callback_called = true; count_down_latch.CountDown(); }); shell->GetEngine()->ScheduleFrame(); + test_started = true; }); count_down_latch.Wait(); EXPECT_TRUE(is_on_begin_frame_called); @@ -2160,7 +2172,7 @@ TEST_F(ShellTest, Screenshot) { root->Add(display_list_layer); }; - PumpOneFrame(shell.get(), 100, 100, builder); + PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder)); firstFrameLatch.Wait(); std::promise screenshot_promise; @@ -2620,7 +2632,13 @@ TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsWorks) { configuration.SetEntrypoint("scene_with_red_box"); RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get()); + // Set a non-zero viewport metrics, otherwise the scene would be discarded. + PostSync(shell->GetTaskRunners().GetUITaskRunner(), + [engine = shell->GetEngine()]() { + engine->SetViewportMetrics(kImplicitViewId, + ViewportMetrics{1, 1, 1, 22, 0}); + }); + PumpOneFrame(shell.get(), ViewContent::NoViews()); ServiceProtocol::Handler::ServiceProtocolMap empty_params; rapidjson::Document document; @@ -2713,6 +2731,47 @@ TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsWorks) { DestroyShell(std::move(shell)); } +#if defined(FML_OS_MACOSX) +TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsDisableImpeller) { + auto settings = CreateSettingsForFixture(); + settings.enable_impeller = true; + std::unique_ptr shell = CreateShell({ + .settings = settings, + .platform_view_create_callback = ShellTestPlatformViewBuilder({ + .rendering_backend = + ShellTestPlatformView::BackendType::kMetalBackend, + }), + }); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("scene_with_red_box"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + + ServiceProtocol::Handler::ServiceProtocolMap empty_params; + rapidjson::Document document; + OnServiceProtocol( + shell.get(), ServiceProtocolEnum::kRenderFrameWithRasterStats, + shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + std::string actual_json = buffer.GetString(); + std::string expected_json = + "{\"code\":-32000,\"message\":\"Raster status not supported on Impeller " + "backend.\"}"; + + ASSERT_EQ(actual_json, expected_json); + + PlatformViewNotifyDestroyed(shell.get()); + DestroyShell(std::move(shell)); +} +#endif // FML_OS_MACOSX + // TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to // flakiness. // TODO(https://github.com/flutter/flutter/issues/100299): Fix it when @@ -2755,14 +2814,16 @@ TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) { RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get(), static_cast(wrong_size.width()), - static_cast(wrong_size.height())); + PumpOneFrame(shell.get(), ViewContent::DummyView( + static_cast(wrong_size.width()), + static_cast(wrong_size.height()))); end_frame_latch.Wait(); // Wrong size, no frames are submitted. ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount()); - PumpOneFrame(shell.get(), static_cast(expected_size.width()), - static_cast(expected_size.height())); + PumpOneFrame(shell.get(), ViewContent::DummyView( + static_cast(expected_size.width()), + static_cast(expected_size.height()))); end_frame_latch.Wait(); // Expected size, 1 frame submitted. ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount()); @@ -2833,8 +2894,9 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get(), static_cast(origin_size.width()), - static_cast(origin_size.height())); + PumpOneFrame(shell.get(), ViewContent::DummyView( + static_cast(origin_size.width()), + static_cast(origin_size.height()))); end_frame_latch.Wait(); ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount()); @@ -2855,8 +2917,9 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount()); // Threads will be merged at the end of this frame. - PumpOneFrame(shell.get(), static_cast(new_size.width()), - static_cast(new_size.height())); + PumpOneFrame(shell.get(), + ViewContent::DummyView(static_cast(new_size.width()), + static_cast(new_size.height()))); end_frame_latch.Wait(); ASSERT_TRUE(raster_thread_merger_ref->IsMerged()); diff --git a/shell/common/switches.cc b/shell/common/switches.cc index f9cbb548cc0f5..8ed53d09ff13b 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -78,12 +78,11 @@ static const std::string kAllowedDartFlags[] = { // of the engine's own symbols on some older versions of Android. #if FML_OS_ANDROID extern uint8_t _binary_icudtl_dat_start[]; -extern uint8_t _binary_icudtl_dat_end[]; +extern size_t _binary_icudtl_dat_size; static std::unique_ptr GetICUStaticMapping() { - return std::make_unique( - _binary_icudtl_dat_start, - _binary_icudtl_dat_end - _binary_icudtl_dat_start); + return std::make_unique(_binary_icudtl_dat_start, + _binary_icudtl_dat_size); } #endif @@ -455,6 +454,17 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { } } + { + std::string disable_image_reader_platform_views_value; + if (command_line.GetOptionValue( + FlagForSwitch(Switch::DisableImageReaderPlatformViews), + &disable_image_reader_platform_views_value)) { + settings.disable_image_reader_platform_views = + disable_image_reader_platform_views_value.empty() || + "true" == disable_image_reader_platform_views_value; + } + } + { std::string impeller_backend_value; if (command_line.GetOptionValue(FlagForSwitch(Switch::ImpellerBackend), @@ -467,6 +477,8 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.enable_vulkan_validation = command_line.HasOption(FlagForSwitch(Switch::EnableVulkanValidation)); + settings.enable_opengl_gpu_tracing = + command_line.HasOption(FlagForSwitch(Switch::EnableOpenGLGPUTracing)); settings.enable_embedder_api = command_line.HasOption(FlagForSwitch(Switch::EnableEmbedderAPI)); diff --git a/shell/common/switches.h b/shell/common/switches.h index 7dd04fc7e12e4..ddc4b3c8827aa 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -275,11 +275,18 @@ DEF_SWITCH(EnableVulkanValidation, "Enable loading Vulkan validation layers. The layers must be " "available to the application and loadable. On non-Vulkan backends, " "this flag does nothing.") +DEF_SWITCH(EnableOpenGLGPUTracing, + "enable-opengl-gpu-tracing", + "Enable tracing of GPU execution time when using the Impeller " + "OpenGLES backend.") DEF_SWITCH(LeakVM, "leak-vm", "When the last shell shuts down, the shared VM is leaked by default " "(the leak_vm in VM settings is true). To clean up the leak VM, set " "this value to false.") +DEF_SWITCH(DisableImageReaderPlatformViews, + "disable-image-reader-platform-views", + "Disables the use of ImageReader backed Platform Views on Android.") DEF_SWITCH( MsaaSamples, "msaa-samples", diff --git a/shell/common/switches_unittests.cc b/shell/common/switches_unittests.cc index 617c0a891cfa7..dc80e87a93b5d 100644 --- a/shell/common/switches_unittests.cc +++ b/shell/common/switches_unittests.cc @@ -123,6 +123,25 @@ TEST(SwitchesTest, NoEnableImpeller) { } } +TEST(SwitchesTest, DisableImageReaderPlatformViews) { + { + // enable + fml::CommandLine command_line = fml::CommandLineFromInitializerList( + {"command", "--disable-image-reader-platform-views"}); + EXPECT_TRUE(command_line.HasOption("disable-image-reader-platform-views")); + Settings settings = SettingsFromCommandLine(command_line); + EXPECT_EQ(settings.disable_image_reader_platform_views, true); + } + { + // disable + fml::CommandLine command_line = fml::CommandLineFromInitializerList( + {"command", "--disable-image-reader-platform-views=false"}); + EXPECT_TRUE(command_line.HasOption("disable-image-reader-platform-views")); + Settings settings = SettingsFromCommandLine(command_line); + EXPECT_EQ(settings.disable_image_reader_platform_views, false); + } +} + } // namespace testing } // namespace flutter diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dee0c682969a0..39a58e6a708a6 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -27,8 +27,6 @@ struct GLFrameInfo { struct GLFBOInfo { // The frame buffer's ID. uint32_t fbo_id; - // This boolean flags whether the returned FBO supports partial repaint. - const bool partial_repaint_enabled; // The frame buffer's existing damage (i.e. damage since it was last used). const std::optional existing_damage; }; diff --git a/shell/gpu/gpu_surface_gl_impeller.h b/shell/gpu/gpu_surface_gl_impeller.h index 048c8570e1140..c5327147ccc72 100644 --- a/shell/gpu/gpu_surface_gl_impeller.h +++ b/shell/gpu/gpu_surface_gl_impeller.h @@ -32,7 +32,7 @@ class GPUSurfaceGLImpeller final : public Surface { std::shared_ptr impeller_renderer_; std::shared_ptr aiks_context_; bool is_valid_ = false; - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; // |Surface| std::unique_ptr AcquireFrame(const SkISize& size) override; diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index 3118bee005134..04c708808e0a9 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -20,6 +20,7 @@ #include "third_party/skia/include/gpu/GrContextOptions.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" // These are common defines present on all OpenGL headers. However, we don't @@ -51,7 +52,7 @@ sk_sp GPUSurfaceGLSkia::MakeGLContext( const auto options = MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kOpenGL); - auto context = GrDirectContext::MakeGL(delegate->GetGLInterface(), options); + auto context = GrDirectContexts::MakeGL(delegate->GetGLInterface(), options); if (!context) { FML_LOG(ERROR) << "Failed to set up Skia Gr context."; diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index ed884151d0ab7..652891c9acbd6 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -24,6 +24,7 @@ #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurfaceProps.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" #include "third_party/skia/include/ports/SkCFObject.h" @@ -44,7 +45,7 @@ SkSurface::ReleaseContext release_context) { GrMtlTextureInfo info; info.fTexture.reset([texture retain]); - GrBackendTexture backend_texture(texture.width, texture.height, GrMipmapped::kNo, info); + GrBackendTexture backend_texture(texture.width, texture.height, skgpu::Mipmapped::kNo, info); return SkSurfaces::WrapBackendTexture( context, backend_texture, origin, static_cast(sample_cnt), color_type, std::move(color_space), props, release_proc, release_context); diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index a8976d677afd0..63f1359722c17 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -104,6 +104,7 @@ sk_sp GPUSurfaceVulkan::CreateSurfaceFromVulkanImage( const VkImage image, const VkFormat format, const SkISize& size) { +#ifdef SK_VULKAN GrVkImageInfo image_info = { .fImage = image, .fImageTiling = VK_IMAGE_TILING_OPTIMAL, @@ -130,6 +131,9 @@ sk_sp GPUSurfaceVulkan::CreateSurfaceFromVulkanImage( SkColorSpace::MakeSRGB(), // color space &surface_properties // surface properties ); +#else + return nullptr; +#endif // SK_VULKAN } SkColorType GPUSurfaceVulkan::ColorTypeFromFormat(const VkFormat format) { diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index 3b6ec9d876ec0..917b5f0e6e11d 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -60,6 +60,11 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( auto& context_vk = impeller::SurfaceContextVK::Cast(*impeller_context_); std::unique_ptr surface = context_vk.AcquireNextSurface(); + if (!surface) { + FML_LOG(ERROR) << "No surface available."; + return nullptr; + } + SurfaceFrame::SubmitCallback submit_callback = fml::MakeCopyable([renderer = impeller_renderer_, // aiks_context = aiks_context_, // diff --git a/shell/platform/android/AndroidManifest.xml b/shell/platform/android/AndroidManifest.xml index 5fbd0d8553c2b..97c77ee7ef57f 100644 --- a/shell/platform/android/AndroidManifest.xml +++ b/shell/platform/android/AndroidManifest.xml @@ -24,4 +24,12 @@ + + + + + + + + diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 06e4c9de02985..35a1f31e4c4cc 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/android/config.gni") import("//build/toolchain/clang.gni") +import("//flutter/build/bin_to_obj.gni") import("//flutter/build/zip_bundle.gni") import("//flutter/common/config.gni") import("//flutter/impeller/tools/impeller.gni") @@ -63,11 +64,18 @@ shared_library("flutter_shell_native") { ldflags = [ "-Wl,--version-script=" + rebase_path("android_exports.lst") ] } +bin_to_assembly("icudtl_asm") { + deps = [] + input = "//third_party/icu/flutter/icudtl.dat" + symbol = "_binary_icudtl_dat_start" + size_symbol = "_binary_icudtl_dat_size" + executable = false +} + source_set("flutter_shell_native_src") { visibility = [ ":*" ] sources = [ - "$root_build_dir/flutter_icu/icudtl.o", "android_choreographer.cc", "android_choreographer.h", "android_context_gl_impeller.cc", @@ -96,12 +104,12 @@ source_set("flutter_shell_native_src") { "apk_asset_provider.h", "flutter_main.cc", "flutter_main.h", - "hardware_buffer_external_texture.cc", - "hardware_buffer_external_texture.h", - "hardware_buffer_external_texture_gl.cc", - "hardware_buffer_external_texture_gl.h", - "hardware_buffer_external_texture_vk.cc", - "hardware_buffer_external_texture_vk.h", + "image_external_texture.cc", + "image_external_texture.h", + "image_external_texture_gl.cc", + "image_external_texture_gl.h", + "image_external_texture_vk.cc", + "image_external_texture_vk.h", "library_loader.cc", "ndk_helpers.cc", "ndk_helpers.h", @@ -121,9 +129,11 @@ source_set("flutter_shell_native_src") { "vsync_waiter_android.h", ] + sources += get_target_outputs(":icudtl_asm") + public_deps = [ ":android_gpu_configuration", - ":icudtl_object", + ":icudtl_asm", ":image_generator", "//flutter/assets", "//flutter/common", @@ -265,6 +275,7 @@ android_java_sources = [ "io/flutter/embedding/engine/systemchannels/NavigationChannel.java", "io/flutter/embedding/engine/systemchannels/PlatformChannel.java", "io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java", + "io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java", "io/flutter/embedding/engine/systemchannels/RestorationChannel.java", "io/flutter/embedding/engine/systemchannels/SettingsChannel.java", "io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java", @@ -312,6 +323,7 @@ android_java_sources = [ "io/flutter/plugin/platform/SingleViewPresentation.java", "io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java", "io/flutter/plugin/platform/VirtualDisplayController.java", + "io/flutter/plugin/text/ProcessTextPlugin.java", "io/flutter/util/HandlerCompat.java", "io/flutter/util/PathUtils.java", "io/flutter/util/Preconditions.java", @@ -424,28 +436,6 @@ action("flutter_shell_java") { ] } -action("icudtl_object") { - script = "//flutter/sky/tools/objcopy.py" - - icudtl_input = "//third_party/icu/flutter/icudtl.dat" - icudtl_output = "$root_build_dir/flutter_icu/icudtl.o" - - inputs = [ "$icudtl_input" ] - - outputs = [ "$icudtl_output" ] - - args = [ - "--objcopy", - rebase_path(android_objcopy), - "--input", - rebase_path(icudtl_input), - "--output", - rebase_path(icudtl_output), - "--arch", - current_cpu, - ] -} - action("android_jar") { script = "//build/android/gyp/create_flutter_jar.py" diff --git a/shell/platform/android/android_context_gl_impeller.cc b/shell/platform/android/android_context_gl_impeller.cc index a2a555000ae09..8c9b80645d7ab 100644 --- a/shell/platform/android/android_context_gl_impeller.cc +++ b/shell/platform/android/android_context_gl_impeller.cc @@ -10,6 +10,7 @@ #include "flutter/impeller/toolkit/egl/context.h" #include "flutter/impeller/toolkit/egl/surface.h" #include "impeller/entity/gles/entity_shaders_gles.h" +#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" #if IMPELLER_ENABLE_3D #include "impeller/scene/shaders/gles/scene_shaders_gles.h" // nogcncheck @@ -48,7 +49,8 @@ class AndroidContextGLImpeller::ReactorWorker final }; static std::shared_ptr CreateImpellerContext( - const std::shared_ptr& worker) { + const std::shared_ptr& worker, + bool enable_gpu_tracing) { auto proc_table = std::make_unique( impeller::egl::CreateProcAddressResolver()); @@ -58,16 +60,20 @@ static std::shared_ptr CreateImpellerContext( } std::vector> shader_mappings = { - std::make_shared(impeller_entity_shaders_gles_data, - impeller_entity_shaders_gles_length), + std::make_shared( + impeller_entity_shaders_gles_data, + impeller_entity_shaders_gles_length), + std::make_shared( + impeller_framebuffer_blend_shaders_gles_data, + impeller_framebuffer_blend_shaders_gles_length), #if IMPELLER_ENABLE_3D - std::make_shared(impeller_scene_shaders_gles_data, - impeller_scene_shaders_gles_length), + std::make_shared( + impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), #endif // IMPELLER_ENABLE_3D }; - auto context = - impeller::ContextGLES::Create(std::move(proc_table), shader_mappings); + auto context = impeller::ContextGLES::Create( + std::move(proc_table), shader_mappings, enable_gpu_tracing); if (!context) { FML_LOG(ERROR) << "Could not create OpenGLES Impeller Context."; return nullptr; @@ -82,7 +88,8 @@ static std::shared_ptr CreateImpellerContext( } AndroidContextGLImpeller::AndroidContextGLImpeller( - std::unique_ptr display) + std::unique_ptr display, + bool enable_gpu_tracing) : AndroidContext(AndroidRenderingAPI::kOpenGLES), reactor_worker_(std::shared_ptr(new ReactorWorker())), display_(std::move(display)) { @@ -143,7 +150,8 @@ AndroidContextGLImpeller::AndroidContextGLImpeller( return; } - auto impeller_context = CreateImpellerContext(reactor_worker_); + auto impeller_context = + CreateImpellerContext(reactor_worker_, enable_gpu_tracing); if (!impeller_context) { FML_DLOG(ERROR) << "Could not create Impeller context."; diff --git a/shell/platform/android/android_context_gl_impeller.h b/shell/platform/android/android_context_gl_impeller.h index 90466db683a8a..234ff58d2c91e 100644 --- a/shell/platform/android/android_context_gl_impeller.h +++ b/shell/platform/android/android_context_gl_impeller.h @@ -13,8 +13,8 @@ namespace flutter { class AndroidContextGLImpeller : public AndroidContext { public: - explicit AndroidContextGLImpeller( - std::unique_ptr display); + AndroidContextGLImpeller(std::unique_ptr display, + bool enable_gpu_tracing); ~AndroidContextGLImpeller(); diff --git a/shell/platform/android/android_context_gl_impeller_unittests.cc b/shell/platform/android/android_context_gl_impeller_unittests.cc index 452c55fc76261..e8f52832d8f94 100644 --- a/shell/platform/android/android_context_gl_impeller_unittests.cc +++ b/shell/platform/android/android_context_gl_impeller_unittests.cc @@ -45,7 +45,8 @@ TEST(AndroidContextGLImpeller, MSAAFirstAttempt) { .WillOnce(Return(ByMove(std::move(second_result)))); ON_CALL(*display, ChooseConfig(_)) .WillByDefault(Return(ByMove(std::unique_ptr()))); - auto context = std::make_unique(std::move(display)); + auto context = + std::make_unique(std::move(display), true); ASSERT_TRUE(context); } @@ -76,7 +77,8 @@ TEST(AndroidContextGLImpeller, FallbackForEmulator) { .WillOnce(Return(ByMove(std::move(third_result)))); ON_CALL(*display, ChooseConfig(_)) .WillByDefault(Return(ByMove(std::unique_ptr()))); - auto context = std::make_unique(std::move(display)); + auto context = + std::make_unique(std::move(display), true); ASSERT_TRUE(context); } } // namespace testing diff --git a/shell/platform/android/android_context_vulkan_impeller.h b/shell/platform/android/android_context_vulkan_impeller.h index 02e9a305cd039..1800fe00bccc1 100644 --- a/shell/platform/android/android_context_vulkan_impeller.h +++ b/shell/platform/android/android_context_vulkan_impeller.h @@ -14,7 +14,7 @@ namespace flutter { class AndroidContextVulkanImpeller : public AndroidContext { public: - AndroidContextVulkanImpeller(bool enable_validation); + explicit AndroidContextVulkanImpeller(bool enable_validation); ~AndroidContextVulkanImpeller(); diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 80eb4839fc439..4ae3c82203bee 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -16,6 +16,7 @@ #include #include +#include "flutter/fml/cpu_affinity.h" #include "flutter/fml/logging.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/message_loop.h" @@ -41,18 +42,21 @@ static void AndroidPlatformThreadConfigSetter( // set thread priority switch (config.priority) { case fml::Thread::ThreadPriority::BACKGROUND: { + fml::RequestAffinity(fml::CpuAffinity::kEfficiency); if (::setpriority(PRIO_PROCESS, 0, 10) != 0) { FML_LOG(ERROR) << "Failed to set IO task runner priority"; } break; } case fml::Thread::ThreadPriority::DISPLAY: { + fml::RequestAffinity(fml::CpuAffinity::kPerformance); if (::setpriority(PRIO_PROCESS, 0, -1) != 0) { FML_LOG(ERROR) << "Failed to set UI task runner priority"; } break; } case fml::Thread::ThreadPriority::RASTER: { + fml::RequestAffinity(fml::CpuAffinity::kPerformance); // Android describes -8 as "most important display threads, for // compositing the screen and retrieving input events". Conservatively // set the raster thread to slightly lower priority than it. @@ -66,6 +70,7 @@ static void AndroidPlatformThreadConfigSetter( break; } default: + fml::RequestAffinity(fml::CpuAffinity::kNotPerformance); if (::setpriority(PRIO_PROCESS, 0, 0) != 0) { FML_LOG(ERROR) << "Failed to set priority"; } diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index d6ec0c3cf3d96..6a5dda8a06082 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -166,7 +166,6 @@ GLFBOInfo AndroidSurfaceGLSkia::GLContextFBO(GLFrameInfo frame_info) const { // The default window bound framebuffer on Android. return GLFBOInfo{ .fbo_id = 0, - .partial_repaint_enabled = onscreen_surface_->SupportsPartialRepaint(), .existing_damage = onscreen_surface_->InitialDamage(), }; } diff --git a/shell/platform/android/hardware_buffer_external_texture_gl.cc b/shell/platform/android/hardware_buffer_external_texture_gl.cc deleted file mode 100644 index 2dfe278433d08..0000000000000 --- a/shell/platform/android/hardware_buffer_external_texture_gl.cc +++ /dev/null @@ -1,173 +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. - -#include "flutter/shell/platform/android/hardware_buffer_external_texture_gl.h" - -#include -#include -#include "flutter/common/graphics/texture.h" -#include "flutter/shell/platform/android/ndk_helpers.h" -#include "impeller/core/formats.h" -#include "impeller/display_list/dl_image_impeller.h" -#include "impeller/renderer/backend/gles/texture_gles.h" -#include "impeller/toolkit/egl/image.h" -#include "impeller/toolkit/gles/texture.h" - -#include "flutter/display_list/effects/dl_color_source.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/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrDirectContext.h" -#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" -#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" -#include "third_party/skia/include/gpu/gl/GrGLTypes.h" - -namespace flutter { - -void HardwareBufferExternalTextureGL::Detach() { - image_.reset(); - texture_.reset(); -} - -void HardwareBufferExternalTextureGL::ProcessFrame(PaintContext& context, - const SkRect& bounds) { - if (state_ == AttachmentState::kUninitialized) { - GLuint texture_name; - glGenTextures(1, &texture_name); - texture_.reset(impeller::GLTexture{texture_name}); - state_ = AttachmentState::kAttached; - } - glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_.get().texture_name); - - EGLDisplay display = eglGetCurrentDisplay(); - FML_CHECK(display != EGL_NO_DISPLAY); - - image_.reset(); - - AHardwareBuffer* latest_hardware_buffer = GetLatestHardwareBuffer(); - if (latest_hardware_buffer == nullptr) { - FML_LOG(WARNING) << "GetLatestHardwareBuffer returned null."; - return; - } - - EGLClientBuffer client_buffer = - NDKHelpers::eglGetNativeClientBufferANDROID(latest_hardware_buffer); - if (client_buffer == nullptr) { - FML_LOG(WARNING) << "eglGetNativeClientBufferAndroid returned null."; - NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer); - return; - } - FML_CHECK(client_buffer != nullptr); - image_.reset(impeller::EGLImageKHRWithDisplay{ - eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - client_buffer, 0), - display}); - FML_CHECK(image_.get().image != EGL_NO_IMAGE_KHR); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, - (GLeglImageOES)image_.get().image); - - // Drop our temporary reference to the hardware buffer as the call to - // eglCreateImageKHR now has the reference. - NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer); - - GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, - texture_.get().texture_name, GL_RGBA8_OES}; - auto backendTexture = - GrBackendTextures::MakeGL(1, 1, skgpu::Mipmapped::kNo, textureInfo); - dl_image_ = DlImage::Make(SkImages::BorrowTextureFrom( - context.gr_context, backendTexture, kTopLeft_GrSurfaceOrigin, - kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr)); -} - -HardwareBufferExternalTextureGL::HardwareBufferExternalTextureGL( - const std::shared_ptr& context, - int64_t id, - const fml::jni::ScopedJavaGlobalRef& image_texture_entry, - const std::shared_ptr& jni_facade) - : HardwareBufferExternalTexture(id, image_texture_entry, jni_facade) {} - -HardwareBufferExternalTextureGL::~HardwareBufferExternalTextureGL() {} - -HardwareBufferExternalTextureImpellerGL:: - HardwareBufferExternalTextureImpellerGL( - const std::shared_ptr& context, - int64_t id, - const fml::jni::ScopedJavaGlobalRef& - hardware_buffer_texture_entry, - const std::shared_ptr& jni_facade) - : HardwareBufferExternalTexture(id, - hardware_buffer_texture_entry, - jni_facade), - impeller_context_(context) {} - -HardwareBufferExternalTextureImpellerGL:: - ~HardwareBufferExternalTextureImpellerGL() {} - -void HardwareBufferExternalTextureImpellerGL::Detach() { - egl_image_.reset(); -} - -void HardwareBufferExternalTextureImpellerGL::ProcessFrame( - PaintContext& context, - const SkRect& bounds) { - EGLDisplay display = eglGetCurrentDisplay(); - FML_CHECK(display != EGL_NO_DISPLAY); - - if (state_ == AttachmentState::kUninitialized) { - // First processed frame we are attached. - state_ = AttachmentState::kAttached; - } - - AHardwareBuffer* latest_hardware_buffer = GetLatestHardwareBuffer(); - if (latest_hardware_buffer == nullptr) { - FML_LOG(ERROR) << "GetLatestHardwareBuffer returned null."; - return; - } - - EGLClientBuffer client_buffer = - NDKHelpers::eglGetNativeClientBufferANDROID(latest_hardware_buffer); - if (client_buffer == nullptr) { - FML_LOG(ERROR) << "eglGetNativeClientBufferAndroid returned null."; - NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer); - return; - } - - FML_CHECK(client_buffer != nullptr); - egl_image_.reset(impeller::EGLImageKHRWithDisplay{ - eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - client_buffer, 0), - display}); - FML_CHECK(egl_image_.get().image != EGL_NO_IMAGE_KHR); - - // Create the texture. - impeller::TextureDescriptor desc; - desc.type = impeller::TextureType::kTextureExternalOES; - desc.storage_mode = impeller::StorageMode::kDevicePrivate; - desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt; - desc.size = {static_cast(bounds.width()), - static_cast(bounds.height())}; - desc.mip_count = 1; - auto texture = std::make_shared( - impeller_context_->GetReactor(), desc, - impeller::TextureGLES::IsWrapped::kWrapped); - texture->SetCoordinateSystem( - impeller::TextureCoordinateSystem::kUploadFromHost); - if (!texture->Bind()) { - FML_LOG(ERROR) << "Could not bind texture."; - NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer); - return; - } - // Associate the hardware buffer image with the texture. - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, - (GLeglImageOES)egl_image_.get().image); - - dl_image_ = impeller::DlImageImpeller::Make(texture); - - // Release the reference acquired by GetLatestHardwareBuffer. - NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer); -} - -} // namespace flutter diff --git a/shell/platform/android/hardware_buffer_external_texture_gl.h b/shell/platform/android/hardware_buffer_external_texture_gl.h deleted file mode 100644 index c8e05e0c750e7..0000000000000 --- a/shell/platform/android/hardware_buffer_external_texture_gl.h +++ /dev/null @@ -1,67 +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. - -#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_GL_H_ -#define FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_GL_H_ - -#include "flutter/shell/platform/android/hardware_buffer_external_texture.h" - -#include "flutter/impeller/renderer/backend/gles/context_gles.h" -#include "flutter/impeller/renderer/backend/gles/gles.h" -#include "flutter/impeller/renderer/backend/gles/texture_gles.h" -#include "flutter/impeller/toolkit/egl/egl.h" -#include "flutter/impeller/toolkit/egl/image.h" -#include "flutter/impeller/toolkit/gles/texture.h" - -#include "flutter/shell/platform/android/android_context_gl_skia.h" - -namespace flutter { - -class HardwareBufferExternalTextureGL : public HardwareBufferExternalTexture { - public: - HardwareBufferExternalTextureGL( - const std::shared_ptr& context, - int64_t id, - const fml::jni::ScopedJavaGlobalRef& - hardware_buffer_texture_entry, - const std::shared_ptr& jni_facade); - - ~HardwareBufferExternalTextureGL() override; - - private: - void ProcessFrame(PaintContext& context, const SkRect& bounds) override; - void Detach() override; - - impeller::UniqueEGLImageKHR image_; - impeller::UniqueGLTexture texture_; - - FML_DISALLOW_COPY_AND_ASSIGN(HardwareBufferExternalTextureGL); -}; - -class HardwareBufferExternalTextureImpellerGL - : public HardwareBufferExternalTexture { - public: - HardwareBufferExternalTextureImpellerGL( - const std::shared_ptr& context, - int64_t id, - const fml::jni::ScopedJavaGlobalRef& - hardware_buffer_texture_entry, - const std::shared_ptr& jni_facade); - - ~HardwareBufferExternalTextureImpellerGL() override; - - private: - void ProcessFrame(PaintContext& context, const SkRect& bounds) override; - void Detach() override; - - const std::shared_ptr impeller_context_; - - impeller::UniqueEGLImageKHR egl_image_; - - FML_DISALLOW_COPY_AND_ASSIGN(HardwareBufferExternalTextureImpellerGL); -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_GL_H_ diff --git a/shell/platform/android/hardware_buffer_external_texture.cc b/shell/platform/android/image_external_texture.cc similarity index 51% rename from shell/platform/android/hardware_buffer_external_texture.cc rename to shell/platform/android/image_external_texture.cc index 13d76e8d871ea..439fb95e13d57 100644 --- a/shell/platform/android/hardware_buffer_external_texture.cc +++ b/shell/platform/android/image_external_texture.cc @@ -1,13 +1,15 @@ -#include "flutter/shell/platform/android/hardware_buffer_external_texture.h" +#include "flutter/shell/platform/android/image_external_texture.h" #include #include + +#include "flutter/shell/platform/android/jni/platform_view_android_jni.h" #include "flutter/shell/platform/android/ndk_helpers.h" namespace flutter { -HardwareBufferExternalTexture::HardwareBufferExternalTexture( +ImageExternalTexture::ImageExternalTexture( int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, const std::shared_ptr& jni_facade) @@ -16,13 +18,14 @@ HardwareBufferExternalTexture::HardwareBufferExternalTexture( jni_facade_(jni_facade) {} // Implementing flutter::Texture. -void HardwareBufferExternalTexture::Paint(PaintContext& context, - const SkRect& bounds, - bool freeze, - const DlImageSampling sampling) { +void ImageExternalTexture::Paint(PaintContext& context, + const SkRect& bounds, + bool freeze, + const DlImageSampling sampling) { if (state_ == AttachmentState::kDetached) { return; } + Attach(context); const bool should_process_frame = (!freeze && new_frame_ready_) || dl_image_ == nullptr; if (should_process_frame) { @@ -39,69 +42,72 @@ void HardwareBufferExternalTexture::Paint(PaintContext& context, flutter::DlCanvas::SrcRectConstraint::kStrict // enforce edges ); } else { - FML_LOG(WARNING) - << "No DlImage available for HardwareBufferExternalTexture to paint."; + FML_LOG(INFO) << "No DlImage available for ImageExternalTexture to paint."; } } // Implementing flutter::Texture. -void HardwareBufferExternalTexture::MarkNewFrameAvailable() { +void ImageExternalTexture::MarkNewFrameAvailable() { new_frame_ready_ = true; } // Implementing flutter::Texture. -void HardwareBufferExternalTexture::OnTextureUnregistered() {} +void ImageExternalTexture::OnTextureUnregistered() {} // Implementing flutter::ContextListener. -void HardwareBufferExternalTexture::OnGrContextCreated() { +void ImageExternalTexture::OnGrContextCreated() { state_ = AttachmentState::kUninitialized; } -AHardwareBuffer* HardwareBufferExternalTexture::GetLatestHardwareBuffer() { +// Implementing flutter::ContextListener. +void ImageExternalTexture::OnGrContextDestroyed() { + if (state_ == AttachmentState::kAttached) { + dl_image_.reset(); + Detach(); + } + state_ = AttachmentState::kDetached; +} + +JavaLocalRef ImageExternalTexture::AcquireLatestImage() { JNIEnv* env = fml::jni::AttachCurrentThread(); FML_CHECK(env != nullptr); // ImageTextureEntry.acquireLatestImage. JavaLocalRef image_java = jni_facade_->ImageTextureEntryAcquireLatestImage( JavaLocalRef(image_texture_entry_)); - if (image_java.obj() == nullptr) { - return nullptr; - } + return image_java; +} - // Image.getHardwareBuffer. - JavaLocalRef hardware_buffer_java = - jni_facade_->ImageGetHardwareBuffer(image_java); - if (hardware_buffer_java.obj() == nullptr) { - jni_facade_->ImageClose(image_java); - return nullptr; +void ImageExternalTexture::CloseImage(const fml::jni::JavaRef& image) { + if (image.obj() == nullptr) { + return; } + jni_facade_->ImageClose(JavaLocalRef(image)); +} - // Convert into NDK HardwareBuffer. - AHardwareBuffer* latest_hardware_buffer = - NDKHelpers::AHardwareBuffer_fromHardwareBuffer( - env, hardware_buffer_java.obj()); - if (latest_hardware_buffer == nullptr) { - return nullptr; +void ImageExternalTexture::CloseHardwareBuffer( + const fml::jni::JavaRef& hardware_buffer) { + if (hardware_buffer.obj() == nullptr) { + return; } - - // Keep hardware buffer alive. - NDKHelpers::AHardwareBuffer_acquire(latest_hardware_buffer); - - // Now that we have referenced the native hardware buffer, close the Java - // Image and HardwareBuffer objects. - jni_facade_->HardwareBufferClose(hardware_buffer_java); - jni_facade_->ImageClose(image_java); - - return latest_hardware_buffer; + jni_facade_->HardwareBufferClose(JavaLocalRef(hardware_buffer)); } -// Implementing flutter::ContextListener. -void HardwareBufferExternalTexture::OnGrContextDestroyed() { - if (state_ == AttachmentState::kAttached) { - dl_image_.reset(); - Detach(); +JavaLocalRef ImageExternalTexture::HardwareBufferFor( + const fml::jni::JavaRef& image) { + if (image.obj() == nullptr) { + return JavaLocalRef(); } - state_ = AttachmentState::kDetached; + // Image.getHardwareBuffer. + return jni_facade_->ImageGetHardwareBuffer(JavaLocalRef(image)); +} + +AHardwareBuffer* ImageExternalTexture::AHardwareBufferFor( + const fml::jni::JavaRef& hardware_buffer) { + JNIEnv* env = fml::jni::AttachCurrentThread(); + FML_CHECK(env != nullptr); + return NDKHelpers::AHardwareBuffer_fromHardwareBuffer(env, + hardware_buffer.obj()); } } // namespace flutter diff --git a/shell/platform/android/hardware_buffer_external_texture.h b/shell/platform/android/image_external_texture.h similarity index 62% rename from shell/platform/android/hardware_buffer_external_texture.h rename to shell/platform/android/image_external_texture.h index 5f93923fb9fa1..3de077706fff4 100644 --- a/shell/platform/android/hardware_buffer_external_texture.h +++ b/shell/platform/android/image_external_texture.h @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_H_ -#define FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_H_ +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_H_ #include "flutter/common/graphics/texture.h" #include "flutter/fml/logging.h" +#include "flutter/shell/platform/android/jni/platform_view_android_jni.h" #include "flutter/shell/platform/android/platform_view_android_jni_impl.h" #include @@ -16,14 +17,15 @@ namespace flutter { // External texture peered to a sequence of android.hardware.HardwareBuffers. // -class HardwareBufferExternalTexture : public flutter::Texture { +class ImageExternalTexture : public flutter::Texture { public: - explicit HardwareBufferExternalTexture( + explicit ImageExternalTexture( int64_t id, - const fml::jni::ScopedJavaGlobalRef& - hardware_buffer_texture_entry, + const fml::jni::ScopedJavaGlobalRef& image_texture_entry, const std::shared_ptr& jni_facade); + virtual ~ImageExternalTexture() = default; + // |flutter::Texture|. void Paint(PaintContext& context, const SkRect& bounds, @@ -43,10 +45,16 @@ class HardwareBufferExternalTexture : public flutter::Texture { void OnGrContextDestroyed() override; protected: - virtual void ProcessFrame(PaintContext& context, const SkRect& bounds) = 0; + virtual void Attach(PaintContext& context) = 0; virtual void Detach() = 0; + virtual void ProcessFrame(PaintContext& context, const SkRect& bounds) = 0; - AHardwareBuffer* GetLatestHardwareBuffer(); + JavaLocalRef AcquireLatestImage(); + void CloseImage(const fml::jni::JavaRef& image); + JavaLocalRef HardwareBufferFor(const fml::jni::JavaRef& image); + void CloseHardwareBuffer(const fml::jni::JavaRef& hardware_buffer); + AHardwareBuffer* AHardwareBufferFor( + const fml::jni::JavaRef& hardware_buffer); fml::jni::ScopedJavaGlobalRef image_texture_entry_; std::shared_ptr jni_facade_; @@ -57,9 +65,9 @@ class HardwareBufferExternalTexture : public flutter::Texture { sk_sp dl_image_; - FML_DISALLOW_COPY_AND_ASSIGN(HardwareBufferExternalTexture); + FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTexture); }; } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_H_ +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_H_ diff --git a/shell/platform/android/image_external_texture_gl.cc b/shell/platform/android/image_external_texture_gl.cc new file mode 100644 index 0000000000000..fd966c8a10ee6 --- /dev/null +++ b/shell/platform/android/image_external_texture_gl.cc @@ -0,0 +1,208 @@ +// 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/android/image_external_texture_gl.h" + +#include +#include + +#include "flutter/common/graphics/texture.h" +#include "flutter/display_list/effects/dl_color_source.h" +#include "flutter/flow/layers/layer.h" +#include "flutter/impeller/core/formats.h" +#include "flutter/impeller/display_list/dl_image_impeller.h" +#include "flutter/impeller/renderer/backend/gles/texture_gles.h" +#include "flutter/impeller/toolkit/egl/image.h" +#include "flutter/impeller/toolkit/gles/texture.h" +#include "flutter/shell/platform/android/ndk_helpers.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/gpu/GrBackendSurface.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" +#include "third_party/skia/include/gpu/gl/GrGLTypes.h" + +namespace flutter { + +ImageExternalTextureGL::ImageExternalTextureGL( + int64_t id, + const fml::jni::ScopedJavaGlobalRef& image_texture_entry, + const std::shared_ptr& jni_facade) + : ImageExternalTexture(id, image_texture_entry, jni_facade) {} + +void ImageExternalTextureGL::Attach(PaintContext& context) { + if (state_ == AttachmentState::kUninitialized) { + if (!android_image_.is_null()) { + JavaLocalRef hardware_buffer = HardwareBufferFor(android_image_); + AHardwareBuffer* hardware_buffer_ahw = + AHardwareBufferFor(hardware_buffer); + egl_image_ = CreateEGLImage(hardware_buffer_ahw); + CloseHardwareBuffer(hardware_buffer); + } + state_ = AttachmentState::kAttached; + } +} + +void ImageExternalTextureGL::Detach() { + egl_image_.reset(); +} + +bool ImageExternalTextureGL::MaybeSwapImages() { + JavaLocalRef image = AcquireLatestImage(); + if (image.is_null()) { + return false; + } + // NOTE: In the following code it is important that old_android_image is + // not closed until after the update of egl_image_ otherwise the image might + // be closed before the old EGLImage referencing it has been deleted. After + // an image is closed the underlying HardwareBuffer may be recycled and used + // for a future frame. + JavaLocalRef old_android_image(android_image_); + android_image_.Reset(image); + JavaLocalRef hardware_buffer = HardwareBufferFor(image); + egl_image_ = CreateEGLImage(AHardwareBufferFor(hardware_buffer)); + CloseHardwareBuffer(hardware_buffer); + // IMPORTANT: We only close the old image after egl_image_ stops referencing + // it. + CloseImage(old_android_image); + return true; +} + +impeller::UniqueEGLImageKHR ImageExternalTextureGL::CreateEGLImage( + AHardwareBuffer* hardware_buffer) { + if (hardware_buffer == nullptr) { + return impeller::UniqueEGLImageKHR(); + } + + EGLDisplay display = eglGetCurrentDisplay(); + FML_CHECK(display != EGL_NO_DISPLAY); + + EGLClientBuffer client_buffer = + NDKHelpers::eglGetNativeClientBufferANDROID(hardware_buffer); + FML_DCHECK(client_buffer != nullptr); + if (client_buffer == nullptr) { + FML_LOG(ERROR) << "eglGetNativeClientBufferAndroid returned null."; + return impeller::UniqueEGLImageKHR(); + } + + impeller::EGLImageKHRWithDisplay maybe_image = + impeller::EGLImageKHRWithDisplay{ + eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + client_buffer, 0), + display}; + + return impeller::UniqueEGLImageKHR(maybe_image); +} + +ImageExternalTextureGLSkia::ImageExternalTextureGLSkia( + const std::shared_ptr& context, + int64_t id, + const fml::jni::ScopedJavaGlobalRef& image_texture_entry, + const std::shared_ptr& jni_facade) + : ImageExternalTextureGL(id, image_texture_entry, jni_facade) {} + +void ImageExternalTextureGLSkia::Attach(PaintContext& context) { + if (state_ == AttachmentState::kUninitialized) { + // After this call state_ will be AttachmentState::kAttached and egl_image_ + // will have been created if we still have an Image associated with us. + ImageExternalTextureGL::Attach(context); + GLuint texture_name; + glGenTextures(1, &texture_name); + texture_.reset(impeller::GLTexture{texture_name}); + } +} + +void ImageExternalTextureGLSkia::Detach() { + ImageExternalTextureGL::Detach(); + texture_.reset(); +} + +void ImageExternalTextureGLSkia::ProcessFrame(PaintContext& context, + const SkRect& bounds) { + const bool swapped = MaybeSwapImages(); + if (!swapped && !egl_image_.is_valid()) { + // Nothing to do. + return; + } + BindImageToTexture(egl_image_, texture_.get().texture_name); + dl_image_ = CreateDlImage(context, bounds); +} + +void ImageExternalTextureGLSkia::BindImageToTexture( + const impeller::UniqueEGLImageKHR& image, + GLuint tex) { + if (!image.is_valid() || tex == 0) { + return; + } + glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + (GLeglImageOES)image.get().image); +} + +sk_sp ImageExternalTextureGLSkia::CreateDlImage( + PaintContext& context, + const SkRect& bounds) { + GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, + texture_.get().texture_name, GL_RGBA8_OES}; + auto backendTexture = + GrBackendTextures::MakeGL(1, 1, skgpu::Mipmapped::kNo, textureInfo); + return DlImage::Make(SkImages::BorrowTextureFrom( + context.gr_context, backendTexture, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr)); +} + +ImageExternalTextureGLImpeller::ImageExternalTextureGLImpeller( + const std::shared_ptr& context, + int64_t id, + const fml::jni::ScopedJavaGlobalRef& image_textury_entry, + const std::shared_ptr& jni_facade) + : ImageExternalTextureGL(id, image_textury_entry, jni_facade), + impeller_context_(context) {} + +void ImageExternalTextureGLImpeller::Detach() {} + +void ImageExternalTextureGLImpeller::Attach(PaintContext& context) { + if (state_ == AttachmentState::kUninitialized) { + ImageExternalTextureGL::Attach(context); + } +} + +void ImageExternalTextureGLImpeller::ProcessFrame(PaintContext& context, + const SkRect& bounds) { + const bool swapped = MaybeSwapImages(); + if (!swapped && !egl_image_.is_valid()) { + // Nothing to do. + return; + } + dl_image_ = CreateDlImage(context, bounds); +} + +sk_sp ImageExternalTextureGLImpeller::CreateDlImage( + PaintContext& context, + const SkRect& bounds) { + impeller::TextureDescriptor desc; + desc.type = impeller::TextureType::kTextureExternalOES; + desc.storage_mode = impeller::StorageMode::kDevicePrivate; + desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt; + desc.size = {static_cast(bounds.width()), + static_cast(bounds.height())}; + desc.mip_count = 1; + auto texture = std::make_shared( + impeller_context_->GetReactor(), desc, + impeller::TextureGLES::IsWrapped::kWrapped); + texture->SetCoordinateSystem( + impeller::TextureCoordinateSystem::kUploadFromHost); + if (!texture->Bind()) { + return nullptr; + } + // Associate the hardware buffer image with the texture. + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + (GLeglImageOES)egl_image_.get().image); + return impeller::DlImageImpeller::Make(texture); +} + +} // namespace flutter diff --git a/shell/platform/android/image_external_texture_gl.h b/shell/platform/android/image_external_texture_gl.h new file mode 100644 index 0000000000000..bdaa50ac605df --- /dev/null +++ b/shell/platform/android/image_external_texture_gl.h @@ -0,0 +1,91 @@ +// 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_ANDROID_IMAGE_EXTERNAL_TEXTURE_GL_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_GL_H_ + +#include "flutter/fml/platform/android/scoped_java_ref.h" +#include "flutter/shell/platform/android/image_external_texture.h" + +#include "flutter/impeller/renderer/backend/gles/context_gles.h" +#include "flutter/impeller/renderer/backend/gles/gles.h" +#include "flutter/impeller/renderer/backend/gles/texture_gles.h" +#include "flutter/impeller/toolkit/egl/egl.h" +#include "flutter/impeller/toolkit/egl/image.h" +#include "flutter/impeller/toolkit/gles/texture.h" + +#include "flutter/shell/platform/android/android_context_gl_skia.h" +#include "flutter/shell/platform/android/ndk_helpers.h" + +namespace flutter { + +class ImageExternalTextureGL : public ImageExternalTexture { + public: + ImageExternalTextureGL( + int64_t id, + const fml::jni::ScopedJavaGlobalRef& image_textury_entry, + const std::shared_ptr& jni_facade); + + protected: + void Attach(PaintContext& context) override; + void Detach() override; + + // Returns true if a new image was acquired and android_image_ and egl_image_ + // were updated. + bool MaybeSwapImages(); + impeller::UniqueEGLImageKHR CreateEGLImage(AHardwareBuffer* buffer); + + fml::jni::ScopedJavaGlobalRef android_image_; + impeller::UniqueEGLImageKHR egl_image_; + + FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTextureGL); +}; + +class ImageExternalTextureGLSkia : public ImageExternalTextureGL { + public: + ImageExternalTextureGLSkia( + const std::shared_ptr& context, + int64_t id, + const fml::jni::ScopedJavaGlobalRef& image_textury_entry, + const std::shared_ptr& jni_facade); + + private: + void Attach(PaintContext& context) override; + void Detach() override; + void ProcessFrame(PaintContext& context, const SkRect& bounds) override; + + void BindImageToTexture(const impeller::UniqueEGLImageKHR& image, GLuint tex); + sk_sp CreateDlImage(PaintContext& context, + const SkRect& bounds); + + impeller::UniqueGLTexture texture_; + + FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTextureGLSkia); +}; + +class ImageExternalTextureGLImpeller : public ImageExternalTextureGL { + public: + ImageExternalTextureGLImpeller( + const std::shared_ptr& context, + int64_t id, + const fml::jni::ScopedJavaGlobalRef& + hardware_buffer_texture_entry, + const std::shared_ptr& jni_facade); + + private: + void Attach(PaintContext& context) override; + void ProcessFrame(PaintContext& context, const SkRect& bounds) override; + void Detach() override; + + sk_sp CreateDlImage(PaintContext& context, + const SkRect& bounds); + + const std::shared_ptr impeller_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTextureGLImpeller); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_GL_H_ diff --git a/shell/platform/android/hardware_buffer_external_texture_vk.cc b/shell/platform/android/image_external_texture_vk.cc similarity index 59% rename from shell/platform/android/hardware_buffer_external_texture_vk.cc rename to shell/platform/android/image_external_texture_vk.cc index 9ba52e43e7547..8843b1774fc81 100644 --- a/shell/platform/android/hardware_buffer_external_texture_vk.cc +++ b/shell/platform/android/image_external_texture_vk.cc @@ -1,37 +1,44 @@ -#include "flutter/shell/platform/android/hardware_buffer_external_texture_vk.h" +#include "flutter/shell/platform/android/image_external_texture_vk.h" +#include "flutter/impeller/core/formats.h" +#include "flutter/impeller/core/texture_descriptor.h" +#include "flutter/impeller/display_list/dl_image_impeller.h" #include "flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_texture_source_vk.h" #include "flutter/impeller/renderer/backend/vulkan/texture_vk.h" #include "flutter/shell/platform/android/ndk_helpers.h" -#include "impeller/core/formats.h" -#include "impeller/core/texture_descriptor.h" -#include "impeller/display_list/dl_image_impeller.h" namespace flutter { -HardwareBufferExternalTextureVK::HardwareBufferExternalTextureVK( +ImageExternalTextureVK::ImageExternalTextureVK( const std::shared_ptr& impeller_context, int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, const std::shared_ptr& jni_facade) - : HardwareBufferExternalTexture(id, image_texture_entry, jni_facade), + : ImageExternalTexture(id, image_texture_entry, jni_facade), impeller_context_(impeller_context) {} -HardwareBufferExternalTextureVK::~HardwareBufferExternalTextureVK() {} +ImageExternalTextureVK::~ImageExternalTextureVK() {} -void HardwareBufferExternalTextureVK::ProcessFrame(PaintContext& context, - const SkRect& bounds) { +void ImageExternalTextureVK::Attach(PaintContext& context) { if (state_ == AttachmentState::kUninitialized) { // First processed frame we are attached. state_ = AttachmentState::kAttached; } +} + +void ImageExternalTextureVK::Detach() {} - AHardwareBuffer* latest_hardware_buffer = GetLatestHardwareBuffer(); - if (latest_hardware_buffer == nullptr) { - FML_LOG(WARNING) << "GetLatestHardwareBuffer returned null."; +void ImageExternalTextureVK::ProcessFrame(PaintContext& context, + const SkRect& bounds) { + JavaLocalRef image = AcquireLatestImage(); + if (image.is_null()) { return; } + JavaLocalRef old_android_image(android_image_); + android_image_.Reset(image); + JavaLocalRef hardware_buffer = HardwareBufferFor(android_image_); + AHardwareBuffer* latest_hardware_buffer = AHardwareBufferFor(hardware_buffer); AHardwareBuffer_Desc hb_desc = {}; flutter::NDKHelpers::AHardwareBuffer_describe(latest_hardware_buffer, @@ -54,11 +61,10 @@ void HardwareBufferExternalTextureVK::ProcessFrame(PaintContext& context, std::make_shared(impeller_context_, texture_source); dl_image_ = impeller::DlImageImpeller::Make(texture); - - // GetLatestHardwareBuffer keeps a reference on the hardware buffer, drop it. - NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer); + CloseHardwareBuffer(hardware_buffer); + // IMPORTANT: We only close the old image after texture stops referencing + // it. + CloseImage(old_android_image); } -void HardwareBufferExternalTextureVK::Detach() {} - } // namespace flutter diff --git a/shell/platform/android/hardware_buffer_external_texture_vk.h b/shell/platform/android/image_external_texture_vk.h similarity index 65% rename from shell/platform/android/hardware_buffer_external_texture_vk.h rename to shell/platform/android/image_external_texture_vk.h index 76c2779eff28d..a6c531e07945e 100644 --- a/shell/platform/android/hardware_buffer_external_texture_vk.h +++ b/shell/platform/android/image_external_texture_vk.h @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_VK_H_ -#define FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_VK_H_ +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_VK_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_VK_H_ -#include "flutter/shell/platform/android/hardware_buffer_external_texture.h" +#include "flutter/shell/platform/android/image_external_texture.h" #include "flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_texture_source_vk.h" #include "flutter/impeller/renderer/backend/vulkan/context_vk.h" @@ -14,24 +14,27 @@ namespace flutter { -class HardwareBufferExternalTextureVK : public HardwareBufferExternalTexture { +class ImageExternalTextureVK : public ImageExternalTexture { public: - HardwareBufferExternalTextureVK( + ImageExternalTextureVK( const std::shared_ptr& impeller_context, int64_t id, const fml::jni::ScopedJavaGlobalRef& hardware_buffer_texture_entry, const std::shared_ptr& jni_facade); - ~HardwareBufferExternalTextureVK() override; + ~ImageExternalTextureVK() override; private: + void Attach(PaintContext& context) override; void ProcessFrame(PaintContext& context, const SkRect& bounds) override; void Detach() override; const std::shared_ptr impeller_context_; + + fml::jni::ScopedJavaGlobalRef android_image_; }; } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_ANDROID_HARDWARE_BUFFER_EXTERNAL_TEXTURE_VK_H_ +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_VK_H_ diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 9596dc6cc4a26..6b8b0c44aafde 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -714,6 +714,10 @@ public void detachFromFlutterEngine() { * */ void onDetach() { + if (!isAttached) { + // Already detached. + return; + } Log.v(TAG, "onDetach()"); ensureAlive(); diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index a3a463be04553..cb00ec42b363c 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -31,6 +31,7 @@ import io.flutter.embedding.engine.systemchannels.MouseCursorChannel; import io.flutter.embedding.engine.systemchannels.NavigationChannel; import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import io.flutter.embedding.engine.systemchannels.ProcessTextChannel; import io.flutter.embedding.engine.systemchannels.RestorationChannel; import io.flutter.embedding.engine.systemchannels.SettingsChannel; import io.flutter.embedding.engine.systemchannels.SpellCheckChannel; @@ -38,6 +39,7 @@ import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.plugin.localization.LocalizationPlugin; import io.flutter.plugin.platform.PlatformViewsController; +import io.flutter.plugin.text.ProcessTextPlugin; import io.flutter.util.ViewUtils; import java.util.HashSet; import java.util.List; @@ -95,6 +97,7 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater { @NonNull private final NavigationChannel navigationChannel; @NonNull private final RestorationChannel restorationChannel; @NonNull private final PlatformChannel platformChannel; + @NonNull private final ProcessTextChannel processTextChannel; @NonNull private final SettingsChannel settingsChannel; @NonNull private final SpellCheckChannel spellCheckChannel; @NonNull private final SystemChannel systemChannel; @@ -329,6 +332,7 @@ public FlutterEngine( mouseCursorChannel = new MouseCursorChannel(dartExecutor); navigationChannel = new NavigationChannel(dartExecutor); platformChannel = new PlatformChannel(dartExecutor); + processTextChannel = new ProcessTextChannel(dartExecutor, context.getPackageManager()); restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData); settingsChannel = new SettingsChannel(dartExecutor); spellCheckChannel = new SpellCheckChannel(dartExecutor); @@ -367,6 +371,8 @@ public FlutterEngine( this.renderer = new FlutterRenderer(flutterJNI); this.platformViewsController = platformViewsController; + this.platformViewsController.setDisableImageReaderPlatformViews( + flutterJNI.getDisableImageReaderPlatformViews()); this.platformViewsController.onAttachedToJNI(); this.pluginRegistry = @@ -382,6 +388,9 @@ public FlutterEngine( } ViewUtils.calculateMaximumDisplayMetrics(context, this); + + ProcessTextPlugin processTextPlugin = new ProcessTextPlugin(this.getProcessTextChannel()); + this.pluginRegistry.add(processTextPlugin); } private void attachToJni() { @@ -543,6 +552,12 @@ public PlatformChannel getPlatformChannel() { return platformChannel; } + /** System channel that sends text processing requests from Flutter to Android. */ + @NonNull + public ProcessTextChannel getProcessTextChannel() { + return processTextChannel; + } + /** * System channel to exchange restoration data between framework and engine. * diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 1d66eeff9dc45..e4337e97b637d 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -237,6 +237,17 @@ public boolean getIsSoftwareRenderingEnabled() { return nativeGetIsSoftwareRenderingEnabled(); } + private native boolean nativeGetDisableImageReaderPlatformViews(); + + /** + * Checks launch settings for whether image reader platform views are disabled. + * + *

The value is the same per program. + */ + @UiThread + public boolean getDisableImageReaderPlatformViews() { + return nativeGetDisableImageReaderPlatformViews(); + } /** * VM Service URI for the VM instance. * diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java index b3775217a86fe..b1b6bc139a7fc 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java @@ -20,6 +20,7 @@ */ @SuppressWarnings({"WeakerAccess", "unused"}) public class FlutterShellArgs { + private static final String TAG = "FlutterShellArgs"; public static final String ARG_KEY_TRACE_STARTUP = "trace-startup"; public static final String ARG_TRACE_STARTUP = "--trace-startup"; public static final String ARG_KEY_START_PAUSED = "start-paused"; @@ -38,6 +39,10 @@ public class FlutterShellArgs { public static final String ARG_SKIA_DETERMINISTIC_RENDERING = "--skia-deterministic-rendering"; public static final String ARG_KEY_TRACE_SKIA = "trace-skia"; public static final String ARG_TRACE_SKIA = "--trace-skia"; + public static final String ARG_KEY_DISABLE_IMAGE_READER_PLATFORM_VIEWS = + "disable-image-reader-platform-views"; + public static final String ARG_DISABLE_IMAGE_READER_PLATFORM_VIEWS = + "--disable-image-reader-platform-views"; public static final String ARG_KEY_TRACE_SKIA_ALLOWLIST = "trace-skia-allowlist"; public static final String ARG_TRACE_SKIA_ALLOWLIST = "--trace-skia-allowlist="; public static final String ARG_KEY_TRACE_SYSTRACE = "trace-systrace"; @@ -128,6 +133,9 @@ public static FlutterShellArgs fromIntent(@NonNull Intent intent) { if (intent.getBooleanExtra(ARG_KEY_ENABLE_IMPELLER, false)) { args.add(ARG_ENABLE_IMPELLER); } + if (intent.getBooleanExtra(ARG_KEY_DISABLE_IMAGE_READER_PLATFORM_VIEWS, false)) { + args.add(ARG_DISABLE_IMAGE_READER_PLATFORM_VIEWS); + } if (intent.getBooleanExtra(ARG_KEY_ENABLE_VULKAN_VALIDATION, false)) { args.add(ARG_ENABLE_VULKAN_VALIDATION); } diff --git a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index 2b880ba3e9883..1bbeefe9c0ae9 100644 --- a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -45,6 +45,10 @@ public class FlutterLoader { "io.flutter.embedding.android.EnableVulkanValidation"; private static final String IMPELLER_BACKEND_META_DATA_KEY = "io.flutter.embedding.android.ImpellerBackend"; + private static final String IMPELLER_OPENGL_GPU_TRACING_DATA_KEY = + "io.flutter.embedding.android.EnableOpenGLGPUTracing"; + private static final String DISABLE_IMAGE_READER_PLATFORM_VIEWS_KEY = + "io.flutter.embedding.android.DisableImageReaderPlatformViews"; /** * Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's @@ -331,10 +335,16 @@ public void ensureInitializationComplete( if (metaData.getBoolean(ENABLE_IMPELLER_META_DATA_KEY, false)) { shellArgs.add("--enable-impeller"); } + if (metaData.getBoolean(DISABLE_IMAGE_READER_PLATFORM_VIEWS_KEY, false)) { + shellArgs.add("--disable-image-reader-platform-views"); + } if (metaData.getBoolean( ENABLE_VULKAN_VALIDATION_META_DATA_KEY, areValidationLayersOnByDefault())) { shellArgs.add("--enable-vulkan-validation"); } + if (metaData.getBoolean(IMPELLER_OPENGL_GPU_TRACING_DATA_KEY, false)) { + shellArgs.add("--enable-opengl-gpu-tracing"); + } String backend = metaData.getString(IMPELLER_BACKEND_META_DATA_KEY); if (backend != null) { shellArgs.add("--impeller-backend=" + backend); diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index f1faeab73e1ef..8b77bc7378cb8 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -8,6 +8,7 @@ import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.hardware.SyncFence; import android.media.Image; import android.os.Build; import android.os.Handler; @@ -18,9 +19,8 @@ import androidx.annotation.VisibleForTesting; import io.flutter.Log; import io.flutter.embedding.engine.FlutterJNI; -import io.flutter.embedding.engine.renderer.FlutterRenderer.ImageTextureRegistryEntry; import io.flutter.view.TextureRegistry; -import io.flutter.view.TextureRegistry.ImageTextureEntry; +import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -223,15 +223,18 @@ public void run() { this.textureWrapper = new SurfaceTextureWrapper(surfaceTexture, onFrameConsumed); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // The callback relies on being executed on the UI thread (unsynchronised read of + // The callback relies on being executed on the UI thread (unsynchronised read + // of // mNativeView // and also the engine code check for platform thread in // Shell::OnPlatformViewMarkTextureFrameAvailable), // so we explicitly pass a Handler for the current thread. this.surfaceTexture().setOnFrameAvailableListener(onFrameListener, new Handler()); } else { - // Android documentation states that the listener can be called on an arbitrary thread. - // But in practice, versions of Android that predate the newer API will call the listener + // Android documentation states that the listener can be called on an arbitrary + // thread. + // But in practice, versions of Android that predate the newer API will call the + // listener // on the thread where the SurfaceTexture was constructed. this.surfaceTexture().setOnFrameAvailableListener(onFrameListener); } @@ -339,6 +342,7 @@ final class ImageTextureRegistryEntry implements TextureRegistry.ImageTextureEnt private static final String TAG = "ImageTextureRegistryEntry"; private final long id; private boolean released; + private boolean ignoringFence = false; private Image image; ImageTextureRegistryEntry(long id) { @@ -351,17 +355,25 @@ public long id() { } @Override + @TargetApi(19) public void release() { if (released) { return; } released = true; + if (image != null) { + image.close(); + image = null; + } unregisterTexture(id); } @Override @TargetApi(19) public void pushImage(Image image) { + if (released) { + return; + } Image toClose; synchronized (this) { toClose = this.image; @@ -369,6 +381,7 @@ public void pushImage(Image image) { } // Close the previously pushed buffer. if (toClose != null) { + Log.e(TAG, "Dropping PlatformView Frame"); toClose.close(); } if (image != null) { @@ -377,23 +390,61 @@ public void pushImage(Image image) { } } + @TargetApi(33) + private void waitOnFence(Image image) { + try { + SyncFence fence = image.getFence(); + boolean signaled = fence.awaitForever(); + if (!signaled) { + Log.e(TAG, "acquireLatestImage image's fence was never signalled."); + } + } catch (IOException e) { + // Drop. + } + } + + @TargetApi(29) + private void maybeWaitOnFence(Image image) { + if (image == null) { + return; + } + if (Build.VERSION.SDK_INT >= 33) { + // The fence API is only available on Android >= 33. + waitOnFence(image); + return; + } + if (!ignoringFence) { + // Log once per ImageTextureEntry. + ignoringFence = true; + Log.w(TAG, "ImageTextureEntry can't wait on the fence on Android < 33"); + } + } + @Override + @TargetApi(29) public Image acquireLatestImage() { Image r; synchronized (this) { r = this.image; this.image = null; } + maybeWaitOnFence(r); return r; } @Override + @TargetApi(19) protected void finalize() throws Throwable { try { if (released) { return; } - + if (image != null) { + // Be sure to finalize any cached image. + image.close(); + image = null; + } + released = true; handler.post(new TextureFinalizerRunnable(id, flutterJNI)); } finally { super.finalize(); @@ -468,9 +519,12 @@ public void stopRenderingToSurface() { if (surface != null) { flutterJNI.onSurfaceDestroyed(); - // TODO(mattcarroll): the source of truth for this call should be FlutterJNI, which is where - // the call to onFlutterUiDisplayed() comes from. However, no such native callback exists yet, - // so until the engine and FlutterJNI are configured to call us back when rendering stops, + // TODO(mattcarroll): the source of truth for this call should be FlutterJNI, + // which is where + // the call to onFlutterUiDisplayed() comes from. However, no such native + // callback exists yet, + // so until the engine and FlutterJNI are configured to call us back when + // rendering stops, // we will manually monitor that change here. if (isDisplayingFlutterUi) { flutterUiDisplayListener.onFlutterUiNoLongerDisplayed(); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java new file mode 100644 index 0000000000000..f1d12bc681111 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java @@ -0,0 +1,122 @@ +// 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. + +package io.flutter.embedding.engine.systemchannels; + +import android.content.pm.PackageManager; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.StandardMethodCodec; +import java.util.ArrayList; +import java.util.Map; + +/** + * {@link ProcessTextChannel} is a platform channel that is used by the framework to initiate text + * processing feature in the embedding and for the embedding to send back the results. + * + *

When the framework needs to query the list of text processing actions (for instance to expose + * them in the selected text context menu), it will send to the embedding the message {@code + * ProcessText.queryTextActions}. In response, the {@link io.flutter.plugin.text.ProcessTextPlugin} + * will return a map of all activities that can process text. The map keys are generated IDs and the + * values are the activities labels. On the first request, the {@link + * io.flutter.plugin.text.ProcessTextPlugin} will make a call to Android's package manager to query + * all activities that can be performed for the {@code Intent.ACTION_PROCESS_TEXT} intent. + * + *

When a text processing action has to be executed, the framework will send to the embedding the + * message {@code ProcessText.processTextAction} with the {@code int id} of the choosen text action + * and the {@code String} of text to process as arguments. In response, the {@link + * io.flutter.plugin.text.ProcessTextPlugin} will make a call to the Android application activity to + * start the activity exposing the text action. The {@link io.flutter.plugin.text.ProcessTextPlugin} + * will return the processed text if there is one, or null if the activity did not return a + * transformed text. + * + *

{@link io.flutter.plugin.text.ProcessTextPlugin} implements {@link ProcessTextMethodHandler} + * that parses incoming messages from Flutter. + */ +public class ProcessTextChannel { + private static final String TAG = "ProcessTextChannel"; + private static final String CHANNEL_NAME = "flutter/processtext"; + private static final String METHOD_QUERY_TEXT_ACTIONS = "ProcessText.queryTextActions"; + private static final String METHOD_PROCESS_TEXT_ACTION = "ProcessText.processTextAction"; + + public final MethodChannel channel; + public final PackageManager packageManager; + private ProcessTextMethodHandler processTextMethodHandler; + + @NonNull + public final MethodChannel.MethodCallHandler parsingMethodHandler = + new MethodChannel.MethodCallHandler() { + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + if (processTextMethodHandler == null) { + return; + } + String method = call.method; + Object args = call.arguments; + switch (method) { + case METHOD_QUERY_TEXT_ACTIONS: + try { + Map actions = processTextMethodHandler.queryTextActions(); + result.success(actions); + } catch (IllegalStateException exception) { + result.error("error", exception.getMessage(), null); + } + break; + case METHOD_PROCESS_TEXT_ACTION: + try { + final ArrayList argumentList = (ArrayList) args; + String id = (String) (argumentList.get(0)); + String text = (String) (argumentList.get(1)); + boolean readOnly = (boolean) (argumentList.get(2)); + processTextMethodHandler.processTextAction(id, text, readOnly, result); + } catch (IllegalStateException exception) { + result.error("error", exception.getMessage(), null); + } + break; + default: + result.notImplemented(); + break; + } + } + }; + + public ProcessTextChannel( + @NonNull DartExecutor dartExecutor, @NonNull PackageManager packageManager) { + this.packageManager = packageManager; + channel = new MethodChannel(dartExecutor, CHANNEL_NAME, StandardMethodCodec.INSTANCE); + channel.setMethodCallHandler(parsingMethodHandler); + } + + /** + * Sets the {@link ProcessTextMethodHandler} which receives all requests to the text processing + * feature sent through this channel. + */ + public void setMethodHandler(@Nullable ProcessTextMethodHandler processTextMethodHandler) { + this.processTextMethodHandler = processTextMethodHandler; + } + + public interface ProcessTextMethodHandler { + /** Requests the map of text actions. Each text action has a unique id and a localized label. */ + Map queryTextActions(); + + /** + * Requests to run a text action on a given input text. + * + * @param id The ID of the text action returned by {@code ProcessText.queryTextActions}. + * @param input The text to be processed. + * @param readOnly Indicates to the activity if the processed text will be used as read-only. + * see + * https://developer.android.com/reference/android/content/Intent#EXTRA_PROCESS_TEXT_READONLY + * @param result The method channel result instance used to reply. + */ + void processTextAction( + @NonNull String id, + @NonNull String input, + @NonNull boolean readOnly, + @NonNull MethodChannel.Result result); + } +} diff --git a/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java b/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java index fa21c71e95072..ea6612d782b36 100644 --- a/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java +++ b/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java @@ -144,11 +144,11 @@ public void resizeChannelBuffer(int newSize) { /** * Toggles whether the channel should show warning messages when discarding messages due to - * overflow. When 'allowed' is true the channel is expected to overflow and warning messages will + * overflow. When 'warns' is false the channel is expected to overflow and warning messages will * not be shown. */ - public void allowChannelBufferOverflow(boolean allowed) { - allowChannelBufferOverflow(messenger, name, allowed); + public void setWarnsOnChannelOverflow(boolean warns) { + setWarnsOnChannelOverflow(messenger, name, warns); } private static ByteBuffer packetFromEncodedMessage(ByteBuffer message) { @@ -182,13 +182,13 @@ public static void resizeChannelBuffer( /** * Toggles whether the channel should show warning messages when discarding messages due to - * overflow. When 'allowed' is true the channel is expected to overflow and warning messages will + * overflow. When 'warns' is false the channel is expected to overflow and warning messages will * not be shown. */ - public static void allowChannelBufferOverflow( - @NonNull BinaryMessenger messenger, @NonNull String channel, boolean allowed) { + public static void setWarnsOnChannelOverflow( + @NonNull BinaryMessenger messenger, @NonNull String channel, boolean warns) { final StandardMethodCodec codec = StandardMethodCodec.INSTANCE; - Object[] arguments = {channel, allowed}; + Object[] arguments = {channel, !warns}; MethodCall methodCall = new MethodCall("overflow", Arrays.asList(arguments)); ByteBuffer message = codec.encodeMethodCall(methodCall); ByteBuffer packet = packetFromEncodedMessage(message); diff --git a/shell/platform/android/io/flutter/plugin/common/MethodChannel.java b/shell/platform/android/io/flutter/plugin/common/MethodChannel.java index 3aa2698ad460e..3191aa4e3ad66 100644 --- a/shell/platform/android/io/flutter/plugin/common/MethodChannel.java +++ b/shell/platform/android/io/flutter/plugin/common/MethodChannel.java @@ -159,11 +159,11 @@ public void resizeChannelBuffer(int newSize) { /** * Toggles whether the channel should show warning messages when discarding messages due to - * overflow. When 'allowed' is true the channel is expected to overflow and warning messages will + * overflow. When 'warns' is false the channel is expected to overflow and warning messages will * not be shown. */ - public void allowChannelBufferOverflow(boolean allowed) { - BasicMessageChannel.allowChannelBufferOverflow(messenger, name, allowed); + public void setWarnsOnChannelOverflow(boolean warns) { + BasicMessageChannel.setWarnsOnChannelOverflow(messenger, name, warns); } /** A handler of incoming method calls. */ diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index f337132b1c810..4897eaf2f13cd 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -272,7 +272,11 @@ private static int inputTypeFromTextInputType( textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD; } else { if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; - if (!enableSuggestions) textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + if (!enableSuggestions) { + // Note: both required. Some devices ignore TYPE_TEXT_FLAG_NO_SUGGESTIONS. + textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + textType |= InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + } } if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) { diff --git a/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java b/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java index 655f7ee3bcc49..29443aeb63aae 100644 --- a/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java +++ b/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java @@ -9,6 +9,7 @@ import android.os.Build; import android.os.Handler; import android.view.Surface; +import io.flutter.Log; import io.flutter.view.TextureRegistry.ImageTextureEntry; @TargetApi(29) @@ -18,6 +19,7 @@ public class ImageReaderPlatformViewRenderTarget implements PlatformViewRenderTa private int bufferWidth = 0; private int bufferHeight = 0; private static final String TAG = "ImageReaderPlatformViewRenderTarget"; + private static final int MAX_IMAGES = 4; private void closeReader() { if (this.reader != null) { @@ -34,7 +36,12 @@ private void closeReader() { new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { - final Image image = reader.acquireLatestImage(); + Image image = null; + try { + image = reader.acquireLatestImage(); + } catch (IllegalStateException e) { + Log.e(TAG, "onImageAvailable acquireLatestImage failed: " + e.toString()); + } if (image == null) { return; } @@ -46,13 +53,16 @@ public void onImageAvailable(ImageReader reader) { protected ImageReader createImageReader33() { final ImageReader.Builder builder = new ImageReader.Builder(bufferWidth, bufferHeight); // Allow for double buffering. - builder.setMaxImages(3); + builder.setMaxImages(MAX_IMAGES); // Use PRIVATE image format so that we can support video decoding. // TODO(johnmccutchan): Should we always use PRIVATE here? It may impact our - // ability to read back texture data. If we don't always want to use it, how do we - // decide when to use it or not? Perhaps PlatformViews can indicate if they may contain + // ability to read back texture data. If we don't always want to use it, how do + // we + // decide when to use it or not? Perhaps PlatformViews can indicate if they may + // contain // DRM'd content. - // I need to investigate how PRIVATE impacts our ability to take screenshots or capture + // I need to investigate how PRIVATE impacts our ability to take screenshots or + // capture // the output of Flutter application. builder.setImageFormat(ImageFormat.PRIVATE); // Hint that consumed images will only be read by GPU. @@ -69,7 +79,7 @@ protected ImageReader createImageReader29() { bufferWidth, bufferHeight, ImageFormat.PRIVATE, - 2, + MAX_IMAGES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); reader.setOnImageAvailableListener(this.onImageAvailableListener, onImageAvailableHandler); return reader; diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index b783128900e2c..820a8629b4350 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -147,6 +147,8 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega // Whether software rendering is used. private boolean usesSoftwareRendering = false; + private static boolean enableHardwareBufferRenderingTarget = true; + private final PlatformViewsChannel.PlatformViewsHandler channelHandler = new PlatformViewsChannel.PlatformViewsHandler() { @@ -179,12 +181,14 @@ public long createForTextureLayer( } if (textureRegistry == null) { throw new IllegalStateException( - "Texture registry is null. This means that platform views controller was detached, view id: " + "Texture registry is null. This means that platform views controller was detached," + + " view id: " + viewId); } if (flutterView == null) { throw new IllegalStateException( - "Flutter view is null. This means the platform views controller doesn't have an attached view, view id: " + "Flutter view is null. This means the platform views controller doesn't have an" + + " attached view, view id: " + viewId); } @@ -193,7 +197,8 @@ public long createForTextureLayer( final View embeddedView = platformView.getView(); if (embeddedView.getParent() != null) { throw new IllegalStateException( - "The Android view returned from PlatformView#getView() was already added to a parent view."); + "The Android view returned from PlatformView#getView() was already added to a" + + " parent view."); } // The newer Texture Layer Hybrid Composition mode isn't suppported if any of the @@ -771,6 +776,10 @@ public void setSoftwareRendering(boolean useSoftwareRendering) { usesSoftwareRendering = useSoftwareRendering; } + public void setDisableImageReaderPlatformViews(boolean disableImageReaderPlatformViews) { + enableHardwareBufferRenderingTarget = !disableImageReaderPlatformViews; + } + /** * Detaches this platform views controller. * @@ -968,11 +977,13 @@ private void unlockInputConnection(@NonNull VirtualDisplayController controller) private static PlatformViewRenderTarget makePlatformViewRenderTarget( TextureRegistry textureRegistry) { - if (Build.VERSION.SDK_INT >= 29) { + if (enableHardwareBufferRenderingTarget && Build.VERSION.SDK_INT >= 33) { final TextureRegistry.ImageTextureEntry textureEntry = textureRegistry.createImageTexture(); + Log.i(TAG, "PlatformView is using ImageReader backend"); return new ImageReaderPlatformViewRenderTarget(textureEntry); } final TextureRegistry.SurfaceTextureEntry textureEntry = textureRegistry.createSurfaceTexture(); + Log.i(TAG, "PlatformView is using SurfaceTexture backend"); return new SurfaceTexturePlatformViewRenderTarget(textureEntry); } @@ -1090,7 +1101,8 @@ void initializePlatformViewIfNeeded(int viewId) { } if (embeddedView.getParent() != null) { throw new IllegalStateException( - "The Android view returned from PlatformView#getView() was already added to a parent view."); + "The Android view returned from PlatformView#getView() was already added to a parent" + + " view."); } final FlutterMutatorView parentView = new FlutterMutatorView( diff --git a/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java b/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java new file mode 100644 index 0000000000000..3338ed2cd9bcf --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java @@ -0,0 +1,197 @@ +// 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. + +package io.flutter.plugin.text; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.systemchannels.ProcessTextChannel; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.PluginRegistry.ActivityResultListener; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ProcessTextPlugin + implements FlutterPlugin, + ActivityAware, + ActivityResultListener, + ProcessTextChannel.ProcessTextMethodHandler { + private static final String TAG = "ProcessTextPlugin"; + + @NonNull private final ProcessTextChannel processTextChannel; + @NonNull private final PackageManager packageManager; + @Nullable private ActivityPluginBinding activityBinding; + private Map resolveInfosById; + + @NonNull + private Map requestsByCode = + new HashMap(); + + public ProcessTextPlugin(@NonNull ProcessTextChannel processTextChannel) { + this.processTextChannel = processTextChannel; + this.packageManager = processTextChannel.packageManager; + + processTextChannel.setMethodHandler(this); + } + + @Override + public Map queryTextActions() { + if (resolveInfosById == null) { + cacheResolveInfos(); + } + Map result = new HashMap(); + for (String id : resolveInfosById.keySet()) { + final ResolveInfo info = resolveInfosById.get(id); + result.put(id, info.loadLabel(packageManager).toString()); + } + return result; + } + + @Override + public void processTextAction( + @NonNull String id, + @NonNull String text, + @NonNull boolean readOnly, + @NonNull MethodChannel.Result result) { + if (activityBinding == null) { + result.error("error", "Plugin not bound to an Activity", null); + return; + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + result.error("error", "Android version not supported", null); + return; + } + + if (resolveInfosById == null) { + result.error("error", "Can not process text actions before calling queryTextActions", null); + return; + } + + final ResolveInfo info = resolveInfosById.get(id); + if (info == null) { + result.error("error", "Text processing activity not found", null); + return; + } + + Integer requestCode = result.hashCode(); + requestsByCode.put(requestCode, result); + + Intent intent = new Intent(); + intent.setClassName(info.activityInfo.packageName, info.activityInfo.name); + intent.setAction(Intent.ACTION_PROCESS_TEXT); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_PROCESS_TEXT, text); + intent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, readOnly); + + // Start the text processing activity. When the activity completes, the onActivityResult + // callback + // is called. + activityBinding.getActivity().startActivityForResult(intent, requestCode); + } + + private void cacheResolveInfos() { + resolveInfosById = new HashMap(); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return; + } + + Intent intent = new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain"); + + List infos; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + infos = packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0)); + } else { + infos = packageManager.queryIntentActivities(intent, 0); + } + + for (ResolveInfo info : infos) { + final String id = info.activityInfo.name; + final String label = info.loadLabel(packageManager).toString(); + resolveInfosById.put(id, info); + } + } + + /** + * Executed when a text processing activity terminates. + * + *

When an activity returns a value, the request is completed successfully and returns the + * processed text. + * + *

When an activity does not return a value. the request is completed successfully and returns + * null. + */ + @TargetApi(Build.VERSION_CODES.M) + @RequiresApi(Build.VERSION_CODES.M) + public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) { + // Return early if the result is not related to a request sent by this plugin. + if (!requestsByCode.containsKey(requestCode)) { + return false; + } + + String result = null; + if (resultCode == Activity.RESULT_OK) { + result = intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT); + } + requestsByCode.remove(requestCode).success(result); + return true; + } + + /** + * Unregisters this {@code ProcessTextPlugin} as the {@code + * ProcessTextChannel.ProcessTextMethodHandler}, for the {@link + * io.flutter.embedding.engine.systemchannels.ProcessTextChannel}. + * + *

Do not invoke any methods on a {@code ProcessTextPlugin} after invoking this method. + */ + public void destroy() { + processTextChannel.setMethodHandler(null); + } + + // FlutterPlugin interface implementation. + + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + // Nothing to do because this plugin is instantiated by the engine. + } + + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + // Nothing to do because this plugin is instantiated by the engine. + } + + // ActivityAware interface implementation. + // + // Store the binding and manage the activity result listener. + + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + this.activityBinding = binding; + this.activityBinding.addActivityResultListener(this); + }; + + public void onDetachedFromActivityForConfigChanges() { + this.activityBinding.removeActivityResultListener(this); + this.activityBinding = null; + } + + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + this.activityBinding = binding; + this.activityBinding.addActivityResultListener(this); + } + + public void onDetachedFromActivity() { + this.activityBinding.removeActivityResultListener(this); + this.activityBinding = null; + } +} diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index ac6111c343ada..c4617c4b99cea 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -17,11 +17,11 @@ #include "flutter/shell/platform/android/android_surface_gl_impeller.h" #include "flutter/shell/platform/android/android_surface_gl_skia.h" #include "flutter/shell/platform/android/android_surface_software.h" -#include "flutter/shell/platform/android/hardware_buffer_external_texture_gl.h" +#include "flutter/shell/platform/android/image_external_texture_gl.h" #include "flutter/shell/platform/android/surface_texture_external_texture_gl.h" #if IMPELLER_ENABLE_VULKAN // b/258506856 for why this is behind an if #include "flutter/shell/platform/android/android_surface_vulkan_impeller.h" -#include "flutter/shell/platform/android/hardware_buffer_external_texture_vk.h" +#include "flutter/shell/platform/android/image_external_texture_vk.h" #endif #include "flutter/shell/platform/android/context/android_context.h" #include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h" @@ -70,7 +70,8 @@ static std::shared_ptr CreateAndroidContext( uint8_t msaa_samples, bool enable_impeller, const std::optional& impeller_backend, - bool enable_vulkan_validation) { + bool enable_vulkan_validation, + bool enable_opengl_gpu_tracing) { if (use_software_rendering) { return std::make_shared(AndroidRenderingAPI::kSoftware); } @@ -90,7 +91,8 @@ static std::shared_ptr CreateAndroidContext( switch (backend) { case AndroidRenderingAPI::kOpenGLES: return std::make_unique( - std::make_unique()); + std::make_unique(), + enable_opengl_gpu_tracing); case AndroidRenderingAPI::kVulkan: return std::make_unique( enable_vulkan_validation); @@ -99,7 +101,8 @@ static std::shared_ptr CreateAndroidContext( enable_vulkan_validation); if (!vulkan_backend->IsValid()) { return std::make_unique( - std::make_unique()); + std::make_unique(), + enable_opengl_gpu_tracing); } return vulkan_backend; } @@ -131,7 +134,9 @@ PlatformViewAndroid::PlatformViewAndroid( msaa_samples, delegate.OnPlatformViewGetSettings().enable_impeller, delegate.OnPlatformViewGetSettings().impeller_backend, - delegate.OnPlatformViewGetSettings().enable_vulkan_validation)) {} + delegate.OnPlatformViewGetSettings().enable_vulkan_validation, + delegate.OnPlatformViewGetSettings().enable_opengl_gpu_tracing)) { +} PlatformViewAndroid::PlatformViewAndroid( PlatformView::Delegate& delegate, @@ -324,18 +329,18 @@ void PlatformViewAndroid::RegisterImageTexture( if (android_context_->RenderingApi() == AndroidRenderingAPI::kOpenGLES) { if (android_context_->GetImpellerContext()) { // Impeller GLES. - RegisterTexture(std::make_shared( + RegisterTexture(std::make_shared( std::static_pointer_cast( android_context_->GetImpellerContext()), texture_id, image_texture_entry, jni_facade_)); } else { // Legacy GL. - RegisterTexture(std::make_shared( + RegisterTexture(std::make_shared( std::static_pointer_cast(android_context_), texture_id, image_texture_entry, jni_facade_)); } } else if (android_context_->RenderingApi() == AndroidRenderingAPI::kVulkan) { - RegisterTexture(std::make_shared( + RegisterTexture(std::make_shared( std::static_pointer_cast( android_context_->GetImpellerContext()), texture_id, image_texture_entry, jni_facade_)); @@ -386,7 +391,7 @@ sk_sp PlatformViewAndroid::CreateResourceContext() const { // the OpenGL surface will be able to make a resource context current. If // this changes, this assumption breaks. Handle the same. resource_context = ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, + GrBackendApi::kOpenGL, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } else { FML_DLOG(ERROR) << "Could not make the resource context current."; diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index aefb58e6a1da0..a8d3bd94d5b88 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -31,7 +31,7 @@ #include "flutter/shell/platform/android/android_shell_holder.h" #include "flutter/shell/platform/android/apk_asset_provider.h" #include "flutter/shell/platform/android/flutter_main.h" -#include "flutter/shell/platform/android/hardware_buffer_external_texture_gl.h" +#include "flutter/shell/platform/android/image_external_texture_gl.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" #include "flutter/shell/platform/android/platform_view_android.h" #include "flutter/shell/platform/android/surface_texture_external_texture_gl.h" @@ -483,6 +483,11 @@ static jboolean GetIsSoftwareRendering(JNIEnv* env, jobject jcaller) { return FlutterMain::Get().GetSettings().enable_software_rendering; } +static jboolean GetDisableImageReaderPlatformViews(JNIEnv* env, + jobject jcaller) { + return FlutterMain::Get().GetSettings().disable_image_reader_platform_views; +} + static void RegisterTexture(JNIEnv* env, jobject jcaller, jlong shell_holder, @@ -778,6 +783,11 @@ bool RegisterApi(JNIEnv* env) { .signature = "()Z", .fnPtr = reinterpret_cast(&GetIsSoftwareRendering), }, + { + .name = "nativeGetDisableImageReaderPlatformViews", + .signature = "()Z", + .fnPtr = reinterpret_cast(&GetDisableImageReaderPlatformViews), + }, { .name = "nativeRegisterTexture", .signature = "(JJLjava/lang/ref/" diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 7b37b4b54451c..26a0a04098649 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -65,6 +65,7 @@ public class FlutterActivityAndFragmentDelegateTest { private final Context ctx = ApplicationProvider.getApplicationContext(); private FlutterEngine mockFlutterEngine; private FlutterActivityAndFragmentDelegate.Host mockHost; + private FlutterActivityAndFragmentDelegate.Host mockHost2; @SuppressWarnings("deprecation") // Robolectric.setupActivity @@ -94,6 +95,24 @@ public void setup() { when(mockHost.shouldDestroyEngineWithHost()).thenReturn(true); when(mockHost.shouldDispatchAppLifecycleState()).thenReturn(true); when(mockHost.attachToEngineAutomatically()).thenReturn(true); + + mockHost2 = mock(FlutterActivityAndFragmentDelegate.Host.class); + when(mockHost2.getContext()).thenReturn(ctx); + when(mockHost2.getActivity()).thenReturn(Robolectric.setupActivity(Activity.class)); + when(mockHost2.getLifecycle()).thenReturn(mock(Lifecycle.class)); + when(mockHost2.getFlutterShellArgs()).thenReturn(new FlutterShellArgs(new String[] {})); + when(mockHost2.getDartEntrypointFunctionName()).thenReturn("main"); + when(mockHost2.getDartEntrypointArgs()).thenReturn(null); + when(mockHost2.getAppBundlePath()).thenReturn("/fake/path"); + when(mockHost2.getInitialRoute()).thenReturn("/"); + when(mockHost2.getRenderMode()).thenReturn(RenderMode.surface); + when(mockHost2.getTransparencyMode()).thenReturn(TransparencyMode.transparent); + when(mockHost2.provideFlutterEngine(any(Context.class))).thenReturn(mockFlutterEngine); + when(mockHost2.shouldAttachEngineToActivity()).thenReturn(true); + when(mockHost2.shouldHandleDeeplinking()).thenReturn(false); + when(mockHost2.shouldDestroyEngineWithHost()).thenReturn(true); + when(mockHost2.shouldDispatchAppLifecycleState()).thenReturn(true); + when(mockHost2.attachToEngineAutomatically()).thenReturn(true); } @Test @@ -1275,6 +1294,72 @@ public void itDoesNotAttachFlutterViewToEngine() { assertFalse(delegate.flutterView.isAttachedToFlutterEngine()); } + @Test + public void itDoesNotDetachTwice() { + FlutterEngine cachedEngine = mockFlutterEngine(); + FlutterEngineCache.getInstance().put("my_flutter_engine", cachedEngine); + + // Engine is a cached singleton that isn't owned by either hosts. + when(mockHost.shouldDestroyEngineWithHost()).thenReturn(false); + when(mockHost2.shouldDestroyEngineWithHost()).thenReturn(false); + + // Adjust fake hosts to request cached engine. + when(mockHost.getCachedEngineId()).thenReturn("my_flutter_engine"); + when(mockHost2.getCachedEngineId()).thenReturn("my_flutter_engine"); + + // Create the real objects that we're testing. + FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost); + FlutterActivityAndFragmentDelegate delegate2 = + new FlutterActivityAndFragmentDelegate(mockHost2); + + // This test is written to recreate the following scenario: + // 1. We have a FlutterFragment_A attached to a singleton cached engine. + // 2. An intent arrives that spawns FlutterFragment_B. + // 3. FlutterFragment_B starts and steals the engine from FlutterFragment_A while attaching. + // Via a call to FlutterActivityAndFragmentDelegate.detachFromFlutterEngine(). + // 4. FlutterFragment_A is forcibly detached from the engine. + // 5. FlutterFragment_B is attached to the engine. + // 6. FlutterFragment_A is detached from the engine. + // Note that the second detach for FlutterFragment_A is done unconditionally when the Fragment + // is being + // torn down. + + // At this point the engine's life cycle channel receives a message (triggered by + // FlutterFragment_A's second detach) + // that indicates the app is detached. This breaks FlutterFragment_B. + + // Below is a sequence of calls that mimicks the calls that the above scenario would trigger + // without + // relying on an intent to trigger the behaviour. + + // FlutterFragment_A is attached to the engine. + delegate.onAttach(ctx); + + // NOTE: The following two calls happen in a slightly different order in reality. That is, via, + // a call to host.detachFromFlutterEngine, delegate2.onAttach ends up invoking + // delegate.onDetach. + // To keep this regression test simple, we call them directly. + + // Detach FlutterFragment_A. + delegate.onDetach(); + + verify(cachedEngine.getLifecycleChannel(), times(1)).appIsDetached(); + + // Attaches to the engine FlutterFragment_B. + delegate2.onAttach(ctx); + delegate2.onResume(); + + verify(cachedEngine.getLifecycleChannel(), times(1)).appIsResumed(); + verify(cachedEngine.getLifecycleChannel(), times(1)).appIsDetached(); + + // A second Detach of FlutterFragment_A happens when the Fragment is detached. + delegate.onDetach(); + + // IMPORTANT: The bug we fixed would have resulted in the engine thinking the app + // is detached twice instead of once. + verify(cachedEngine.getLifecycleChannel(), times(1)).appIsDetached(); + } + /** * Creates a mock {@link io.flutter.embedding.engine.FlutterEngine}. * diff --git a/shell/platform/android/test/io/flutter/plugin/common/MethodChannelTest.java b/shell/platform/android/test/io/flutter/plugin/common/MethodChannelTest.java index 8f8836f6148df..df1fa2108c296 100644 --- a/shell/platform/android/test/io/flutter/plugin/common/MethodChannelTest.java +++ b/shell/platform/android/test/io/flutter/plugin/common/MethodChannelTest.java @@ -56,7 +56,7 @@ public void overflowChannelBufferMessageIsWellformed() { String channel = "flutter/test"; MethodChannel rawChannel = new MethodChannel(dartExecutor, channel); - rawChannel.allowChannelBufferOverflow(true); + rawChannel.setWarnsOnChannelOverflow(false); // Created from the following Dart code: // MethodCall methodCall = const MethodCall('overflow', ['flutter/test', true]); diff --git a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java index 881dffc18882c..c4eebd33b939d 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -1306,6 +1306,42 @@ public void showTextInput_textInputTypeNone() { assertEquals(testImm.isSoftInputVisible(), false); } + @Test + public void inputConnection_textInputTypeMultilineAndSuggestionsDisabled() { + // Regression test for https://github.com/flutter/flutter/issues/71679. + View testView = new View(ctx); + DartExecutor dartExecutor = mock(DartExecutor.class); + TextInputChannel textInputChannel = new TextInputChannel(dartExecutor); + TextInputPlugin textInputPlugin = + new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class)); + textInputPlugin.setTextInputClient( + 0, + new TextInputChannel.Configuration( + false, + false, + false, // Disable suggestions. + true, + false, + TextInputChannel.TextCapitalization.NONE, + new TextInputChannel.InputType(TextInputChannel.TextInputType.MULTILINE, false, false), + null, + null, + null, + null, + null)); + + EditorInfo editorInfo = new EditorInfo(); + InputConnection connection = + textInputPlugin.createInputConnection(testView, mock(KeyboardManager.class), editorInfo); + + assertEquals( + editorInfo.inputType, + InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_FLAG_MULTI_LINE + | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS + | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + } + // -------- Start: Autofill Tests ------- @Test public void autofill_enabledByDefault() { diff --git a/shell/platform/android/test/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTargetTest.java b/shell/platform/android/test/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTargetTest.java new file mode 100644 index 0000000000000..64a0fd1e13306 --- /dev/null +++ b/shell/platform/android/test/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTargetTest.java @@ -0,0 +1,105 @@ +// 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. + +package io.flutter.plugin.platform; + +import static android.os.Looper.getMainLooper; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.robolectric.Shadows.shadowOf; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.media.Image; +import android.view.View; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import io.flutter.view.TextureRegistry.ImageTextureEntry; +import org.junit.Test; +import org.junit.runner.RunWith; + +@TargetApi(29) +@RunWith(AndroidJUnit4.class) +public class ImageReaderPlatformViewRenderTargetTest { + private final Context ctx = ApplicationProvider.getApplicationContext(); + + class TestImageTextureEntry implements ImageTextureEntry { + private Image lastPushedImage; + + public long id() { + return 1; + } + + public void release() { + if (this.lastPushedImage != null) { + this.lastPushedImage.close(); + } + } + + public void pushImage(Image image) { + if (this.lastPushedImage != null) { + this.lastPushedImage.close(); + } + this.lastPushedImage = image; + } + + public Image acquireLatestImage() { + Image r = this.lastPushedImage; + this.lastPushedImage = null; + return r; + } + } + + @Test + public void viewDraw_writesToBuffer() { + final TestImageTextureEntry textureEntry = new TestImageTextureEntry(); + final ImageReaderPlatformViewRenderTarget renderTarget = + new ImageReaderPlatformViewRenderTarget(textureEntry); + // Custom view. + final View platformView = + new View(ctx) { + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + canvas.drawColor(Color.RED); + } + }; + final int size = 100; + platformView.measure(size, size); + platformView.layout(0, 0, size, size); + renderTarget.resize(size, size); + + // We don't have an image in the texture entry. + assertNull(textureEntry.acquireLatestImage()); + + // Start rendering a frame. + final Canvas targetCanvas = renderTarget.lockHardwareCanvas(); + assertNotNull(targetCanvas); + + try { + // Fill the render target with transparent pixels. This is needed for platform views that + // expect a transparent background. + targetCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + // Override the canvas that this subtree of views will use to draw. + platformView.draw(targetCanvas); + } finally { + // Finish rendering a frame. + renderTarget.unlockCanvasAndPost(targetCanvas); + } + + // Pump the UI thread task loop. This is needed so that the OnImageAvailable callback + // gets invoked (resulting in textureEntry.pushImage being invoked). + shadowOf(getMainLooper()).idle(); + + // An image was pushed into the texture entry and it has the correct dimensions. + Image pushedImage = textureEntry.acquireLatestImage(); + assertNotNull(pushedImage); + assertEquals(pushedImage.getWidth(), size); + assertEquals(pushedImage.getHeight(), size); + } +} diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index fe8c7e46967d7..3a77e6ae68cc1 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -1,3 +1,7 @@ +// 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. + package io.flutter.plugin.platform; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java index ffb50fb65b510..29ad40daae637 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java @@ -1,3 +1,7 @@ +// 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. + package io.flutter.plugin.platform; import static android.view.View.OnFocusChangeListener; diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 13ec6823f1301..3bb1dc7ba7ce0 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -1,3 +1,7 @@ +// 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. + package io.flutter.plugin.platform; import static android.os.Looper.getMainLooper; diff --git a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java index 20e6bb3b4731c..046682797623f 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java @@ -1,3 +1,7 @@ +// 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. + package io.flutter.plugin.platform; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; diff --git a/shell/platform/android/test/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTargetTest.java b/shell/platform/android/test/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTargetTest.java index a1ab6d75e1f29..020ef090f3ae3 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTargetTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTargetTest.java @@ -1,3 +1,7 @@ +// 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. + package io.flutter.plugin.platform; import static org.junit.Assert.*; diff --git a/shell/platform/android/test/io/flutter/plugin/text/ProcessTextPluginTest.java b/shell/platform/android/test/io/flutter/plugin/text/ProcessTextPluginTest.java new file mode 100644 index 0000000000000..e582f9b8c8fb8 --- /dev/null +++ b/shell/platform/android/test/io/flutter/plugin/text/ProcessTextPluginTest.java @@ -0,0 +1,280 @@ +package io.flutter.plugin.text; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import androidx.annotation.RequiresApi; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.systemchannels.ProcessTextChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.StandardMethodCodec; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +@RunWith(AndroidJUnit4.class) +@TargetApi(Build.VERSION_CODES.N) +@RequiresApi(Build.VERSION_CODES.N) +public class ProcessTextPluginTest { + + private static void sendToBinaryMessageHandler( + BinaryMessenger.BinaryMessageHandler binaryMessageHandler, String method, Object args) { + MethodCall methodCall = new MethodCall(method, args); + ByteBuffer encodedMethodCall = StandardMethodCodec.INSTANCE.encodeMethodCall(methodCall); + binaryMessageHandler.onMessage( + (ByteBuffer) encodedMethodCall.flip(), mock(BinaryMessenger.BinaryReply.class)); + } + + @SuppressWarnings("deprecation") + // setMessageHandler is deprecated. + @Test + public void respondsToProcessTextChannelMessage() { + ArgumentCaptor binaryMessageHandlerCaptor = + ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class); + DartExecutor mockBinaryMessenger = mock(DartExecutor.class); + ProcessTextChannel.ProcessTextMethodHandler mockHandler = + mock(ProcessTextChannel.ProcessTextMethodHandler.class); + PackageManager mockPackageManager = mock(PackageManager.class); + ProcessTextChannel processTextChannel = + new ProcessTextChannel(mockBinaryMessenger, mockPackageManager); + + processTextChannel.setMethodHandler(mockHandler); + + verify(mockBinaryMessenger, times(1)) + .setMessageHandler(any(String.class), binaryMessageHandlerCaptor.capture()); + + BinaryMessenger.BinaryMessageHandler binaryMessageHandler = + binaryMessageHandlerCaptor.getValue(); + + sendToBinaryMessageHandler(binaryMessageHandler, "ProcessText.queryTextActions", null); + + verify(mockHandler).queryTextActions(); + } + + @SuppressWarnings("deprecation") + // setMessageHandler is deprecated. + @Test + public void performQueryTextActions() { + DartExecutor mockBinaryMessenger = mock(DartExecutor.class); + PackageManager mockPackageManager = mock(PackageManager.class); + ProcessTextChannel processTextChannel = + new ProcessTextChannel(mockBinaryMessenger, mockPackageManager); + + // Set up mocked result for PackageManager.queryIntentActivities. + ResolveInfo action1 = createFakeResolveInfo("Action1", mockPackageManager); + ResolveInfo action2 = createFakeResolveInfo("Action2", mockPackageManager); + List infos = new ArrayList(Arrays.asList(action1, action2)); + Intent intent = new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain"); + when(mockPackageManager.queryIntentActivities( + any(Intent.class), any(PackageManager.ResolveInfoFlags.class))) + .thenReturn(infos); + + // ProcessTextPlugin should retrieve the mocked text actions. + ProcessTextPlugin processTextPlugin = new ProcessTextPlugin(processTextChannel); + Map textActions = processTextPlugin.queryTextActions(); + final String action1Id = "mockActivityName.Action1"; + final String action2Id = "mockActivityName.Action2"; + assertEquals(textActions, Map.of(action1Id, "Action1", action2Id, "Action2")); + } + + @SuppressWarnings("deprecation") + // setMessageHandler is deprecated. + @Test + public void performProcessTextActionWithNoReturnedValue() { + DartExecutor mockBinaryMessenger = mock(DartExecutor.class); + PackageManager mockPackageManager = mock(PackageManager.class); + ProcessTextChannel processTextChannel = + new ProcessTextChannel(mockBinaryMessenger, mockPackageManager); + + // Set up mocked result for PackageManager.queryIntentActivities. + ResolveInfo action1 = createFakeResolveInfo("Action1", mockPackageManager); + ResolveInfo action2 = createFakeResolveInfo("Action2", mockPackageManager); + List infos = new ArrayList(Arrays.asList(action1, action2)); + when(mockPackageManager.queryIntentActivities( + any(Intent.class), any(PackageManager.ResolveInfoFlags.class))) + .thenReturn(infos); + + // ProcessTextPlugin should retrieve the mocked text actions. + ProcessTextPlugin processTextPlugin = new ProcessTextPlugin(processTextChannel); + Map textActions = processTextPlugin.queryTextActions(); + final String action1Id = "mockActivityName.Action1"; + final String action2Id = "mockActivityName.Action2"; + assertEquals(textActions, Map.of(action1Id, "Action1", action2Id, "Action2")); + + // Set up the activity binding. + ActivityPluginBinding mockActivityPluginBinding = mock(ActivityPluginBinding.class); + Activity mockActivity = mock(Activity.class); + when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity); + processTextPlugin.onAttachedToActivity(mockActivityPluginBinding); + + // Execute th first action. + String textToBeProcessed = "Flutter!"; + MethodChannel.Result result = mock(MethodChannel.Result.class); + processTextPlugin.processTextAction(action1Id, textToBeProcessed, false, result); + + // Activity.startActivityForResult should have been called. + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mockActivity, times(1)).startActivityForResult(intentCaptor.capture(), anyInt()); + Intent intent = intentCaptor.getValue(); + assertEquals(intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT), textToBeProcessed); + + // Simulate an Android activity answer which does not return a value. + Intent resultIntent = new Intent(); + processTextPlugin.onActivityResult(result.hashCode(), Activity.RESULT_OK, resultIntent); + + // Success with no returned value is expected. + verify(result).success(null); + } + + @SuppressWarnings("deprecation") + // setMessageHandler is deprecated. + @Test + public void performProcessTextActionWithReturnedValue() { + DartExecutor mockBinaryMessenger = mock(DartExecutor.class); + PackageManager mockPackageManager = mock(PackageManager.class); + ProcessTextChannel processTextChannel = + new ProcessTextChannel(mockBinaryMessenger, mockPackageManager); + + // Set up mocked result for PackageManager.queryIntentActivities. + ResolveInfo action1 = createFakeResolveInfo("Action1", mockPackageManager); + ResolveInfo action2 = createFakeResolveInfo("Action2", mockPackageManager); + List infos = new ArrayList(Arrays.asList(action1, action2)); + when(mockPackageManager.queryIntentActivities( + any(Intent.class), any(PackageManager.ResolveInfoFlags.class))) + .thenReturn(infos); + + // ProcessTextPlugin should retrieve the mocked text actions. + ProcessTextPlugin processTextPlugin = new ProcessTextPlugin(processTextChannel); + Map textActions = processTextPlugin.queryTextActions(); + final String action1Id = "mockActivityName.Action1"; + final String action2Id = "mockActivityName.Action2"; + assertEquals(textActions, Map.of(action1Id, "Action1", action2Id, "Action2")); + + // Set up the activity binding. + ActivityPluginBinding mockActivityPluginBinding = mock(ActivityPluginBinding.class); + Activity mockActivity = mock(Activity.class); + when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity); + processTextPlugin.onAttachedToActivity(mockActivityPluginBinding); + + // Execute the first action. + String textToBeProcessed = "Flutter!"; + MethodChannel.Result result = mock(MethodChannel.Result.class); + processTextPlugin.processTextAction(action1Id, textToBeProcessed, false, result); + + // Activity.startActivityForResult should have been called. + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mockActivity, times(1)).startActivityForResult(intentCaptor.capture(), anyInt()); + Intent intent = intentCaptor.getValue(); + assertEquals(intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT), textToBeProcessed); + + // Simulate an Android activity answer which returns a transformed text. + String processedText = "Flutter!!!"; + Intent resultIntent = new Intent(); + resultIntent.putExtra(Intent.EXTRA_PROCESS_TEXT, processedText); + processTextPlugin.onActivityResult(result.hashCode(), Activity.RESULT_OK, resultIntent); + + // Success with the transformed text is expected. + verify(result).success(processedText); + } + + @SuppressWarnings("deprecation") + // setMessageHandler is deprecated. + @Test + public void doNotCrashOnNonRelatedActivityResult() { + DartExecutor mockBinaryMessenger = mock(DartExecutor.class); + PackageManager mockPackageManager = mock(PackageManager.class); + ProcessTextChannel processTextChannel = + new ProcessTextChannel(mockBinaryMessenger, mockPackageManager); + + // Set up mocked result for PackageManager.queryIntentActivities. + ResolveInfo action1 = createFakeResolveInfo("Action1", mockPackageManager); + ResolveInfo action2 = createFakeResolveInfo("Action2", mockPackageManager); + List infos = new ArrayList(Arrays.asList(action1, action2)); + when(mockPackageManager.queryIntentActivities( + any(Intent.class), any(PackageManager.ResolveInfoFlags.class))) + .thenReturn(infos); + + // ProcessTextPlugin should retrieve the mocked text actions. + ProcessTextPlugin processTextPlugin = new ProcessTextPlugin(processTextChannel); + Map textActions = processTextPlugin.queryTextActions(); + final String action1Id = "mockActivityName.Action1"; + final String action2Id = "mockActivityName.Action2"; + assertEquals(textActions, Map.of(action1Id, "Action1", action2Id, "Action2")); + + // Set up the activity binding. + ActivityPluginBinding mockActivityPluginBinding = mock(ActivityPluginBinding.class); + Activity mockActivity = mock(Activity.class); + when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity); + processTextPlugin.onAttachedToActivity(mockActivityPluginBinding); + + // Execute the first action. + String textToBeProcessed = "Flutter!"; + MethodChannel.Result result = mock(MethodChannel.Result.class); + processTextPlugin.processTextAction(action1Id, textToBeProcessed, false, result); + + // Activity.startActivityForResult should have been called. + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mockActivity, times(1)).startActivityForResult(intentCaptor.capture(), anyInt()); + Intent intent = intentCaptor.getValue(); + assertEquals(intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT), textToBeProcessed); + + // Result to a request not sent by this plugin should be ignored. + final int externalRequestCode = 42; + processTextPlugin.onActivityResult(externalRequestCode, Activity.RESULT_OK, new Intent()); + + // Simulate an Android activity answer which returns a transformed text. + String processedText = "Flutter!!!"; + Intent resultIntent = new Intent(); + resultIntent.putExtra(Intent.EXTRA_PROCESS_TEXT, processedText); + processTextPlugin.onActivityResult(result.hashCode(), Activity.RESULT_OK, resultIntent); + + // Success with the transformed text is expected. + verify(result).success(processedText); + } + + private ResolveInfo createFakeResolveInfo(String label, PackageManager mockPackageManager) { + ResolveInfo resolveInfo = mock(ResolveInfo.class); + ActivityInfo activityInfo = new ActivityInfo(); + when(resolveInfo.loadLabel(mockPackageManager)).thenReturn(label); + + // Use Java reflection to set required member variables. + try { + Field activityField = ResolveInfo.class.getDeclaredField("activityInfo"); + activityField.setAccessible(true); + activityField.set(resolveInfo, activityInfo); + Field packageNameField = PackageItemInfo.class.getDeclaredField("packageName"); + packageNameField.setAccessible(true); + packageNameField.set(activityInfo, "mockActivityPackageName"); + Field nameField = PackageItemInfo.class.getDeclaredField("name"); + nameField.setAccessible(true); + nameField.set(activityInfo, "mockActivityName." + label); + } catch (Exception ex) { + // Test will failed if reflection APIs throw. + } + + return resolveInfo; + } +} diff --git a/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h b/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h index c0819465c5d79..4109c59a0a615 100644 --- a/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h +++ b/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h @@ -7,6 +7,7 @@ #include #include +#include #include "binary_messenger.h" #include "message_codec.h" @@ -58,7 +59,8 @@ class BasicMessageChannel { void Send(const T& message, BinaryReply reply) { std::unique_ptr> raw_message = codec_->EncodeMessage(message); - messenger_->Send(name_, raw_message->data(), raw_message->size(), reply); + messenger_->Send(name_, raw_message->data(), raw_message->size(), + std::move(reply)); } // Registers a handler that should be called any time a message is @@ -77,7 +79,7 @@ class BasicMessageChannel { BinaryMessageHandler binary_handler = [handler, codec, channel_name]( const uint8_t* binary_message, const size_t binary_message_size, - BinaryReply binary_reply) { + const BinaryReply& binary_reply) { // Use this channel's codec to decode the message and build a reply // handler. std::unique_ptr message = diff --git a/shell/platform/common/client_wrapper/include/flutter/event_channel.h b/shell/platform/common/client_wrapper/include/flutter/event_channel.h index 6a3ef09b8e120..1ff577a76d8c6 100644 --- a/shell/platform/common/client_wrapper/include/flutter/event_channel.h +++ b/shell/platform/common/client_wrapper/include/flutter/event_channel.h @@ -69,7 +69,7 @@ class EventChannel { // Mutable state to track the handler's listening status. is_listening = bool(false)](const uint8_t* message, const size_t message_size, - BinaryReply reply) mutable { + const BinaryReply& reply) mutable { constexpr char kOnListenMethod[] = "listen"; constexpr char kOnCancelMethod[] = "cancel"; diff --git a/shell/platform/common/client_wrapper/include/flutter/event_stream_handler.h b/shell/platform/common/client_wrapper/include/flutter/event_stream_handler.h index 9c1d89418d3a0..166733dd0661b 100644 --- a/shell/platform/common/client_wrapper/include/flutter/event_stream_handler.h +++ b/shell/platform/common/client_wrapper/include/flutter/event_stream_handler.h @@ -20,8 +20,8 @@ struct StreamHandlerError { const std::string error_message; const std::unique_ptr error_details; - StreamHandlerError(const std::string error_code, - const std::string error_message, + StreamHandlerError(const std::string& error_code, + const std::string& error_message, std::unique_ptr&& error_details) : error_code(error_code), error_message(error_message), diff --git a/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h b/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h index a19cc349d1911..57f6d0e9b8862 100644 --- a/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h +++ b/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h @@ -7,6 +7,7 @@ #include #include +#include #include "method_result.h" @@ -36,7 +37,7 @@ class MethodResultFunctions : public MethodResult { ResultHandlerNotImplemented on_not_implemented) : on_success_(on_success), on_error_(on_error), - on_not_implemented_(on_not_implemented) {} + on_not_implemented_(std::move(on_not_implemented)) {} virtual ~MethodResultFunctions() = default; diff --git a/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h b/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h index 47daf7c42fbcd..3f812d17406ce 100644 --- a/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h +++ b/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace flutter { @@ -28,7 +29,7 @@ class PixelBufferTexture { // take care of proper synchronization. It also needs to be ensured that the // returned buffer isn't released prior to unregistering this texture. explicit PixelBufferTexture(CopyBufferCallback copy_buffer_callback) - : copy_buffer_callback_(copy_buffer_callback) {} + : copy_buffer_callback_(std::move(copy_buffer_callback)) {} // Returns the callback-provided FlutterDesktopPixelBuffer that contains the // actual pixel data. The intended surface size is specified by |width| and diff --git a/shell/platform/common/client_wrapper/testing/test_codec_extensions.h b/shell/platform/common/client_wrapper/testing/test_codec_extensions.h index 29f81a4a82b99..7b10dd47fbe32 100644 --- a/shell/platform/common/client_wrapper/testing/test_codec_extensions.h +++ b/shell/platform/common/client_wrapper/testing/test_codec_extensions.h @@ -32,7 +32,7 @@ class Point { // variable-length type that includes types handled by the core standard codec. class SomeData { public: - SomeData(const std::string label, const std::vector& data) + SomeData(const std::string& label, const std::vector& data) : label_(label), data_(data) {} ~SomeData() = default; diff --git a/shell/platform/darwin/common/framework/Headers/FlutterChannels.h b/shell/platform/darwin/common/framework/Headers/FlutterChannels.h index 9b84cd64c7178..cdbf140091e8f 100644 --- a/shell/platform/darwin/common/framework/Headers/FlutterChannels.h +++ b/shell/platform/darwin/common/framework/Headers/FlutterChannels.h @@ -143,9 +143,46 @@ FLUTTER_DARWIN_EXPORT * Adjusts the number of messages that will get buffered when sending messages to * channels that aren't fully set up yet. For example, the engine isn't running * yet or the channel's message handler isn't set up on the Dart side yet. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param newSize The number of messages that will get buffered. + */ ++ (void)resizeChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger + size:(NSInteger)newSize; + +/** + * Adjusts the number of messages that will get buffered when sending messages to + * channels that aren't fully set up yet. For example, the engine isn't running + * yet or the channel's message handler isn't set up on the Dart side yet. + * + * @param newSize The number of messages that will get buffered. */ - (void)resizeChannelBuffer:(NSInteger)newSize; +/** + * Defines whether the channel should show warning messages when discarding messages + * due to overflow. + * + * @param warns When false, the channel is expected to overflow and warning messages + * will not be shown. + * @param name The channel name. + * @param messenger The binary messenger. + */ ++ (void)setWarnsOnOverflow:(BOOL)warns + forChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger; + +/** + * Defines whether the channel should show warning messages when discarding messages + * due to overflow. + * + * @param warns When false, the channel is expected to overflow and warning messages + * will not be shown. + */ +- (void)setWarnsOnOverflow:(BOOL)warns; + @end /** diff --git a/shell/platform/darwin/common/framework/Source/FlutterChannels.mm b/shell/platform/darwin/common/framework/Source/FlutterChannels.mm index f360144d3748f..78d5c08404227 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterChannels.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterChannels.mm @@ -9,12 +9,41 @@ #pragma mark - Basic message channel static NSString* const kFlutterChannelBuffersChannel = @"dev.flutter/channel-buffers"; +static NSString* const kResizeMethod = @"resize"; +static NSString* const kOverflowMethod = @"overflow"; static void ResizeChannelBuffer(NSObject* binaryMessenger, NSString* channel, NSInteger newSize) { - NSString* messageString = [NSString stringWithFormat:@"resize\r%@\r%@", channel, @(newSize)]; - NSData* message = [messageString dataUsingEncoding:NSUTF8StringEncoding]; + NSCAssert(newSize >= 0, @"Channel buffer size must be non-negative"); + // Cast newSize to int because the deserialization logic handles only 32 bits values, + // see + // https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L495. + NSArray* args = @[ channel, @(static_cast(newSize)) ]; + FlutterMethodCall* resizeMethodCall = [FlutterMethodCall methodCallWithMethodName:kResizeMethod + arguments:args]; + NSObject* codec = [FlutterStandardMethodCodec sharedInstance]; + NSData* message = [codec encodeMethodCall:resizeMethodCall]; + [binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message]; +} + +/** + * Defines whether a channel should show warning messages when discarding messages + * due to overflow. + * + * @param binaryMessenger The binary messenger. + * @param channel The channel name. + * @param warns When false, the channel is expected to overflow and warning messages + * will not be shown. + */ +static void SetWarnsOnOverflow(NSObject* binaryMessenger, + NSString* channel, + BOOL warns) { + FlutterMethodCall* overflowMethodCall = + [FlutterMethodCall methodCallWithMethodName:kOverflowMethod + arguments:@[ channel, @(!warns) ]]; + NSObject* codec = [FlutterStandardMethodCodec sharedInstance]; + NSData* message = [codec encodeMethodCall:overflowMethodCall]; [binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message]; } @@ -114,10 +143,26 @@ - (void)setMessageHandler:(FlutterMessageHandler)handler { _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue); } ++ (void)resizeChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger + size:(NSInteger)newSize { + ResizeChannelBuffer(messenger, name, newSize); +} + - (void)resizeChannelBuffer:(NSInteger)newSize { ResizeChannelBuffer(_messenger, _name, newSize); } ++ (void)setWarnsOnOverflow:(BOOL)warns + forChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger { + SetWarnsOnOverflow(messenger, name, warns); +} + +- (void)setWarnsOnOverflow:(BOOL)warns { + SetWarnsOnOverflow(_messenger, _name, warns); +} + @end #pragma mark - Method channel diff --git a/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m b/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m index 767c762d86449..fef4a1075cc43 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m +++ b/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m @@ -147,7 +147,31 @@ - (void)testCallMethodHandler { } - (void)testResize { - NSString* channelName = @"foo"; + NSString* channelName = @"flutter/test"; + id binaryMessenger = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger)); + id codec = OCMProtocolMock(@protocol(FlutterMethodCodec)); + FlutterBasicMessageChannel* channel = + [[FlutterBasicMessageChannel alloc] initWithName:channelName + binaryMessenger:binaryMessenger + codec:codec]; + XCTAssertNotNil(channel); + + // The expected content was created from the following Dart code: + // MethodCall call = MethodCall('resize', ['flutter/test',3]); + // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List(); + const unsigned char bytes[] = {7, 6, 114, 101, 115, 105, 122, 101, 12, 2, + 7, 12, 102, 108, 117, 116, 116, 101, 114, 47, + 116, 101, 115, 116, 3, 3, 0, 0, 0}; + NSData* expectedMessage = [NSData dataWithBytes:bytes length:sizeof(bytes)]; + + OCMExpect([binaryMessenger sendOnChannel:@"dev.flutter/channel-buffers" message:expectedMessage]); + [channel resizeChannelBuffer:3]; + OCMVerifyAll(binaryMessenger); + [binaryMessenger stopMocking]; +} + +- (bool)testSetWarnsOnOverflow { + NSString* channelName = @"flutter/test"; id binaryMessenger = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger)); id codec = OCMProtocolMock(@protocol(FlutterMethodCodec)); FlutterBasicMessageChannel* channel = @@ -156,11 +180,15 @@ - (void)testResize { codec:codec]; XCTAssertNotNil(channel); - NSString* expectedMessageString = - [NSString stringWithFormat:@"resize\r%@\r%@", channelName, @100]; - NSData* expectedMessage = [expectedMessageString dataUsingEncoding:NSUTF8StringEncoding]; + // The expected content was created from the following Dart code: + // MethodCall call = MethodCall('overflow',['flutter/test', true]); + // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List(); + const unsigned char bytes[] = {7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 12, + 102, 108, 117, 116, 116, 101, 114, 47, 116, 101, 115, 116, 1}; + NSData* expectedMessage = [NSData dataWithBytes:bytes length:sizeof(bytes)]; + OCMExpect([binaryMessenger sendOnChannel:@"dev.flutter/channel-buffers" message:expectedMessage]); - [channel resizeChannelBuffer:100]; + [channel setWarnsOnOverflow:NO]; OCMVerifyAll(binaryMessenger); [binaryMessenger stopMocking]; } diff --git a/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h b/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h index e9c9f323ccb6a..9e0ea22e5fadc 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h +++ b/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h @@ -48,7 +48,7 @@ NSString* FLTAssetPath(NSBundle* bundle); // If the key is not set, `flutter_assets` is used as the raw path value. // // If no valid asset is found under the raw path, returns nil. -NSURL* FLTAssetsURLFromBundle(NSBundle* bundle); +NSString* FLTAssetsPathFromBundle(NSBundle* bundle); NS_ASSUME_NONNULL_END diff --git a/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm b/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm index bf0002d9ade34..c96b1a9af181e 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm @@ -9,6 +9,7 @@ FLUTTER_ASSERT_ARC const NSString* kDefaultAssetPath = @"Frameworks/App.framework/flutter_assets"; +static NSString* GetFlutterAssetsPathFromBundle(NSBundle* bundle, NSString* relativeAssetsPath); NSBundle* FLTFrameworkBundleInternal(NSString* flutterFrameworkBundleID, NSURL* searchURL) { NSDirectoryEnumerator* frameworkEnumerator = [NSFileManager.defaultManager @@ -29,7 +30,7 @@ } NSBundle* FLTGetApplicationBundle() { - NSBundle* mainBundle = [NSBundle mainBundle]; + NSBundle* mainBundle = NSBundle.mainBundle; // App extension bundle is in .app/PlugIns/Extension.appex. if ([mainBundle.bundleURL.pathExtension isEqualToString:@"appex"]) { // Up two levels. @@ -48,7 +49,7 @@ flutterFrameworkBundle = [NSBundle bundleWithIdentifier:flutterFrameworkBundleID]; } if (flutterFrameworkBundle == nil) { - flutterFrameworkBundle = [NSBundle mainBundle]; + flutterFrameworkBundle = NSBundle.mainBundle; } return flutterFrameworkBundle; } @@ -57,12 +58,24 @@ return [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"] ?: kDefaultAssetPath; } -NSURL* FLTAssetsURLFromBundle(NSBundle* bundle) { - NSString* flutterAssetsPath = FLTAssetPath(bundle); - NSURL* assets = [bundle URLForResource:flutterAssetsPath withExtension:nil]; +NSString* FLTAssetsPathFromBundle(NSBundle* bundle) { + NSString* relativeAssetsPath = FLTAssetPath(bundle); + NSString* flutterAssetsPath = GetFlutterAssetsPathFromBundle(bundle, relativeAssetsPath); + if (flutterAssetsPath.length == 0) { + flutterAssetsPath = GetFlutterAssetsPathFromBundle(NSBundle.mainBundle, relativeAssetsPath); + } + return flutterAssetsPath; +} - if (!assets) { - assets = [[NSBundle mainBundle] URLForResource:flutterAssetsPath withExtension:nil]; +static NSString* GetFlutterAssetsPathFromBundle(NSBundle* bundle, NSString* relativeAssetsPath) { + // Use the raw path solution so that asset path can be returned from unloaded bundles. + // See https://github.com/flutter/engine/pull/46073 + NSString* assetsPath = [bundle pathForResource:relativeAssetsPath ofType:nil]; + if (assetsPath.length == 0) { + // In app extension, using full relative path (kDefaultAssetPath) + // returns nil when the app bundle is not loaded. Try to use + // the sub folder name, which can successfully return a valid path. + assetsPath = [bundle pathForResource:@"flutter_assets" ofType:nil]; } - return assets; + return assetsPath; } diff --git a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm index 9c0190e827dec..55ca7cb906408 100644 --- a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm +++ b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm @@ -10,6 +10,7 @@ #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkYUVAInfo.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/GrYUVABackendTextures.h" @@ -295,7 +296,7 @@ @implementation FlutterDarwinExternalTextureSkImageWrapper GrBackendTexture skiaBackendTextures[2]; skiaBackendTextures[0] = GrBackendTexture(/*width=*/width, /*height=*/height, - /*mipMapped=*/GrMipMapped::kNo, + /*mipMapped=*/skgpu::Mipmapped::kNo, /*textureInfo=*/ySkiaTextureInfo); GrMtlTextureInfo uvSkiaTextureInfo; @@ -303,7 +304,7 @@ @implementation FlutterDarwinExternalTextureSkImageWrapper skiaBackendTextures[1] = GrBackendTexture(/*width=*/width, /*height=*/height, - /*mipMapped=*/GrMipMapped::kNo, + /*mipMapped=*/skgpu::Mipmapped::kNo, /*textureInfo=*/uvSkiaTextureInfo); SkYUVAInfo yuvaInfo(skiaBackendTextures[0].dimensions(), SkYUVAInfo::PlaneConfig::kY_UV, SkYUVAInfo::Subsampling::k444, colorSpace); @@ -324,7 +325,7 @@ GrYUVABackendTextures yuvaBackendTextures(yuvaInfo, skiaBackendTextures, GrBackendTexture skiaBackendTexture(/*width=*/width, /*height=*/height, - /*mipMapped=*/GrMipMapped ::kNo, + /*mipMapped=*/skgpu::Mipmapped ::kNo, /*textureInfo=*/skiaTextureInfo); return SkImages::BorrowTextureFrom(grContext, skiaBackendTexture, kTopLeft_GrSurfaceOrigin, diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index fe180c2af422f..b8bd60198ae0a 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -238,8 +238,11 @@ source_set("ios_test_flutter_mrc") { "framework/Source/FlutterEngineTest_mrc.mm", "framework/Source/FlutterPlatformPluginTest.mm", "framework/Source/FlutterPlatformViewsTest.mm", + "framework/Source/FlutterTouchInterceptingView_Test.h", "framework/Source/FlutterViewControllerTest_mrc.mm", "framework/Source/FlutterViewTest.mm", + "framework/Source/SemanticsObjectTestMocks.h", + "framework/Source/SemanticsObjectTest_mrc.mm", "framework/Source/VsyncWaiterIosTest.mm", "framework/Source/accessibility_bridge_test.mm", "platform_message_handler_ios_test.mm", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 3d434eb2179f2..70c1ac7a4742d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -8,6 +8,7 @@ #include +#import #include #include @@ -22,6 +23,8 @@ #import "flutter/shell/platform/darwin/common/command_line.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +FLUTTER_ASSERT_NOT_ARC + extern "C" { #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG // Used for debugging dart:* sources. @@ -32,6 +35,23 @@ static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin"; +static BOOL DoesHardwareSupportWideGamut() { + static BOOL result = NO; + static dispatch_once_t once_token = 0; + dispatch_once(&once_token, ^{ + id device = MTLCreateSystemDefaultDevice(); + if (@available(iOS 13.0, *)) { + // MTLGPUFamilyApple2 = A9/A10 + result = [device supportsFamily:MTLGPUFamilyApple2]; + } else { + // A9/A10 on iOS 10+ + result = [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]; + } + [device release]; + }); + return result; +} + flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* processInfoOrNil) { auto command_line = flutter::CommandLineFromNSProcessInfo(processInfoOrNil); @@ -119,19 +139,20 @@ // Checks to see if the flutter assets directory is already present. if (settings.assets_path.empty()) { - NSURL* assetsURL = FLTAssetsURLFromBundle(bundle); + NSString* assetsPath = FLTAssetsPathFromBundle(bundle); - if (!assetsURL) { + if (assetsPath.length == 0) { NSLog(@"Failed to find assets path for \"%@\"", bundle); } else { - settings.assets_path = assetsURL.path.UTF8String; + settings.assets_path = assetsPath.UTF8String; // Check if there is an application kernel snapshot in the assets directory we could // potentially use. Looking for the snapshot makes sense only if we have a VM that can use // it. if (!flutter::DartVM::IsRunningPrecompiledCode()) { NSURL* applicationKernelSnapshotURL = - [assetsURL URLByAppendingPathComponent:@(kApplicationKernelSnapshotFileName)]; + [NSURL URLWithString:@(kApplicationKernelSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; NSError* error; if ([applicationKernelSnapshotURL checkResourceIsReachableAndReturnError:&error]) { settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String; @@ -153,9 +174,12 @@ // As of Xcode 14.1, the wide gamut surface pixel formats are not supported by // the simulator. settings.enable_wide_gamut = false; + // Removes unused function warning. + (void)DoesHardwareSupportWideGamut; #else NSNumber* nsEnableWideGamut = [mainBundle objectForInfoDictionaryKey:@"FLTEnableWideGamut"]; - BOOL enableWideGamut = nsEnableWideGamut ? nsEnableWideGamut.boolValue : YES; + BOOL enableWideGamut = + (nsEnableWideGamut ? nsEnableWideGamut.boolValue : YES) && DoesHardwareSupportWideGamut(); settings.enable_wide_gamut = enableWideGamut; #endif diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm index a28aaf8beeab9..7de95d4771ae8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm @@ -89,31 +89,139 @@ - (void)testFLTGetApplicationBundleWhenCurrentTargetIsExtension { - (void)testFLTAssetsURLFromBundle { { - // Found asset path in info.plist (even not reachable) + // Found asset path in info.plist id mockBundle = OCMClassMock([NSBundle class]); OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); - NSURL* mockAssetsURL = OCMClassMock([NSURL class]); - OCMStub([mockBundle URLForResource:@"foo/assets" withExtension:nil]).andReturn(mockAssetsURL); - OCMStub([mockAssetsURL checkResourceIsReachableAndReturnError:NULL]).andReturn(NO); - OCMStub([mockAssetsURL path]).andReturn(@"foo/assets"); - NSURL* url = FLTAssetsURLFromBundle(mockBundle); - XCTAssertEqualObjects(url.path, @"foo/assets"); + NSString* resultAssetsPath = @"path/to/foo/assets"; + OCMStub([mockBundle pathForResource:@"foo/assets" ofType:nil]).andReturn(resultAssetsPath); + NSString* path = FLTAssetsPathFromBundle(mockBundle); + XCTAssertEqualObjects(path, @"path/to/foo/assets"); + } + { + // Found asset path in info.plist, is not overriden by main bundle + id mockBundle = OCMClassMock([NSBundle class]); + id mockMainBundle = OCMPartialMock(NSBundle.mainBundle); + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); + OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil); + NSString* resultAssetsPath = @"path/to/foo/assets"; + OCMStub([mockBundle pathForResource:@"foo/assets" ofType:nil]).andReturn(resultAssetsPath); + NSString* path = FLTAssetsPathFromBundle(mockBundle); + XCTAssertEqualObjects(path, @"path/to/foo/assets"); + [mockMainBundle stopMocking]; } { // No asset path in info.plist, defaults to main bundle id mockBundle = OCMClassMock([NSBundle class]); id mockMainBundle = OCMPartialMock([NSBundle mainBundle]); - NSURL* mockAssetsURL = OCMClassMock([NSURL class]); - OCMStub([mockBundle URLForResource:@"Frameworks/App.framework/flutter_assets" - withExtension:nil]) + NSString* resultAssetsPath = @"path/to/foo/assets"; + OCMStub([mockBundle pathForResource:@"Frameworks/App.framework/flutter_assets" ofType:nil]) .andReturn(nil); - OCMStub([mockAssetsURL checkResourceIsReachableAndReturnError:NULL]).andReturn(NO); - OCMStub([mockAssetsURL path]).andReturn(@"path/to/foo/assets"); - OCMStub([mockMainBundle URLForResource:@"Frameworks/App.framework/flutter_assets" - withExtension:nil]) - .andReturn(mockAssetsURL); - NSURL* url = FLTAssetsURLFromBundle(mockBundle); - XCTAssertEqualObjects(url.path, @"path/to/foo/assets"); + OCMStub([mockMainBundle pathForResource:@"Frameworks/App.framework/flutter_assets" ofType:nil]) + .andReturn(resultAssetsPath); + NSString* path = FLTAssetsPathFromBundle(mockBundle); + XCTAssertEqualObjects(path, @"path/to/foo/assets"); + [mockMainBundle stopMocking]; + } +} + +- (void)testFLTAssetPathReturnsTheCorrectValue { + { + // Found assets path in info.plist + id mockBundle = OCMClassMock([NSBundle class]); + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); + XCTAssertEqualObjects(FLTAssetPath(mockBundle), @"foo/assets"); + } + { + // No assets path in info.plist, use default value + id mockBundle = OCMClassMock([NSBundle class]); + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil); + XCTAssertEqualObjects(FLTAssetPath(mockBundle), kDefaultAssetPath); + } +} + +- (void)testLookUpForAssets { + { + id mockBundle = OCMPartialMock([NSBundle mainBundle]); + // Found assets path in info.plist + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar"]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, @"foo/assets/bar"); + [mockBundle stopMocking]; + } + { + id mockBundle = OCMPartialMock([NSBundle mainBundle]); + // No assets path in info.plist, use default value + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar"]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, @"Frameworks/App.framework/flutter_assets/bar"); + [mockBundle stopMocking]; + } +} + +- (void)testLookUpForAssetsFromBundle { + { + id mockBundle = OCMClassMock([NSBundle class]); + // Found assets path in info.plist + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromBundle:mockBundle]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, @"foo/assets/bar"); + } + { + // No assets path in info.plist, use default value + id mockBundle = OCMClassMock([NSBundle class]); + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromBundle:mockBundle]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, @"Frameworks/App.framework/flutter_assets/bar"); + } +} + +- (void)testLookUpForAssetsFromPackage { + { + id mockBundle = OCMPartialMock([NSBundle mainBundle]); + // Found assets path in info.plist + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromPackage:@"bar_package"]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, @"foo/assets/packages/bar_package/bar"); + [mockBundle stopMocking]; + } + { + id mockBundle = OCMPartialMock([NSBundle mainBundle]); + // No assets path in info.plist, use default value + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromPackage:@"bar_package"]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, + @"Frameworks/App.framework/flutter_assets/packages/bar_package/bar"); + [mockBundle stopMocking]; + } +} + +- (void)testLookUpForAssetsFromPackageFromBundle { + { + id mockBundle = OCMClassMock([NSBundle class]); + // Found assets path in info.plist + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets"); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" + fromPackage:@"bar_package" + fromBundle:mockBundle]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, @"foo/assets/packages/bar_package/bar"); + } + { + id mockBundle = OCMClassMock([NSBundle class]); + // No assets path in info.plist, use default value + OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil); + NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" + fromPackage:@"bar_package" + fromBundle:mockBundle]; + // This is testing public API, changing this assert is likely to break plugins. + XCTAssertEqualObjects(assetsPath, + @"Frameworks/App.framework/flutter_assets/packages/bar_package/bar"); } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index df4bd038036a5..e6c81339ef709 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -49,15 +49,18 @@ static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig& confi // set thread priority switch (config.priority) { case fml::Thread::ThreadPriority::BACKGROUND: { + pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0); [[NSThread currentThread] setThreadPriority:0]; break; } case fml::Thread::ThreadPriority::NORMAL: { + pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0); [[NSThread currentThread] setThreadPriority:0.5]; break; } case fml::Thread::ThreadPriority::RASTER: case fml::Thread::ThreadPriority::DISPLAY: { + pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0); [[NSThread currentThread] setThreadPriority:1.0]; sched_param param; int policy; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 0b5526a5341a2..51c76156b4f98 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -65,6 +65,7 @@ @implementation FlutterEnginePlatformViewTest - (void)setUp { fml::MessageLoop::EnsureInitializedForCurrentThread(); auto thread_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + auto sync_switch = std::make_shared(); flutter::TaskRunners runners(/*label=*/self.name.UTF8String, /*platform=*/thread_task_runner, /*raster=*/thread_task_runner, @@ -76,7 +77,7 @@ - (void)setUp { /*platform_views_controller=*/nil, /*task_runners=*/runners, /*worker_task_runner=*/nil, - /*is_gpu_disabled_sync_switch=*/nil); + /*is_gpu_disabled_sync_switch=*/sync_switch); weak_factory = std::make_unique>(platform_view.get()); } @@ -98,6 +99,7 @@ - (void)testMsaaSampleCount { fake_delegate.settings_.msaa_samples = 4; auto thread_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + auto sync_switch = std::make_shared(); flutter::TaskRunners runners(/*label=*/self.name.UTF8String, /*platform=*/thread_task_runner, /*raster=*/thread_task_runner, @@ -109,7 +111,7 @@ - (void)testMsaaSampleCount { /*platform_views_controller=*/nil, /*task_runners=*/runners, /*worker_task_runner=*/nil, - /*is_gpu_disabled_sync_switch=*/nil); + /*is_gpu_disabled_sync_switch=*/sync_switch); XCTAssertEqual(msaa_4x_platform_view->GetIosContext()->GetMsaaSampleCount(), MsaaSampleCount::kFour); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index bf7e607878d1b..6c3bcd9ccdaab 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -406,10 +406,15 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } UIView* FlutterPlatformViewsController::GetPlatformViewByID(int64_t view_id) { + return [GetFlutterTouchInterceptingViewByID(view_id) embeddedView]; +} + +FlutterTouchInterceptingView* FlutterPlatformViewsController::GetFlutterTouchInterceptingViewByID( + int64_t view_id) { if (views_.empty()) { return nil; } - return [touch_interceptors_[view_id].get() embeddedView]; + return touch_interceptors_[view_id].get(); } long FlutterPlatformViewsController::FindFirstResponderPlatformViewId() { @@ -957,6 +962,10 @@ @implementation FlutterTouchInterceptingView { fml::scoped_nsobject _delayingRecognizer; FlutterPlatformViewGestureRecognizersBlockingPolicy _blockingPolicy; UIView* _embeddedView; + // The used as the accessiblityContainer. + // The `accessiblityContainer` is used in UIKit to determine the parent of this accessibility + // node. + NSObject* _flutterAccessibilityContainer; } - (instancetype)initWithEmbeddedView:(UIView*)embeddedView platformViewsController: @@ -1035,6 +1044,14 @@ - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { } +- (void)setFlutterAccessibilityContainer:(NSObject*)flutterAccessibilityContainer { + _flutterAccessibilityContainer = flutterAccessibilityContainer; +} + +- (id)accessibilityContainer { + return _flutterAccessibilityContainer; +} + @end @implementation DelayingGestureRecognizer { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 1e6dad56089aa..c55111136df9b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -11,6 +11,7 @@ #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" @@ -3099,4 +3100,12 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); } +- (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { + FlutterTouchInterceptingView* touchInteceptorView = + [[[FlutterTouchInterceptingView alloc] init] autorelease]; + NSObject* container = [[[NSObject alloc] init] autorelease]; + [touchInteceptorView setFlutterAccessibilityContainer:container]; + XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container); +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 43cb43cf4e33b..e18569868115f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -12,6 +12,7 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" #import "flutter/shell/platform/darwin/ios/ios_context.h" @class FlutterTouchInterceptingView; @@ -230,10 +231,17 @@ class FlutterPlatformViewsController { // Returns the `FlutterPlatformView`'s `view` object associated with the view_id. // // If the `FlutterPlatformViewsController` does not contain any `FlutterPlatformView` object or - // a `FlutterPlatformView` object asscociated with the view_id cannot be found, the method + // a `FlutterPlatformView` object associated with the view_id cannot be found, the method // returns nil. UIView* GetPlatformViewByID(int64_t view_id); + // Returns the `FlutterTouchInterceptingView` with the view_id. + // + // If the `FlutterPlatformViewsController` does not contain any `FlutterPlatformView` object or + // a `FlutterPlatformView` object associated with the view_id cannot be found, the method + // returns nil. + FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id); + PostPrerollResult PostPrerollAction( const fml::RefPtr& raster_thread_merger); @@ -424,6 +432,9 @@ class FlutterPlatformViewsController { // Get embedded view - (UIView*)embeddedView; + +// Sets flutterAccessibilityContainer as this view's accessibilityContainer. +- (void)setFlutterAccessibilityContainer:(NSObject*)flutterAccessibilityContainer; @end @interface UIView (FirstResponder) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h index 96eff563e0850..a7a996cb39995 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h @@ -128,7 +128,6 @@ FLUTTER_DARWIN_EXPORT // UITextInput @property(nonatomic, readonly) NSMutableString* text; -@property(nonatomic, readonly) NSMutableString* markedText; @property(readwrite, copy) UITextRange* selectedTextRange; @property(nonatomic, strong) UITextRange* markedTextRange; @property(nonatomic, copy) NSDictionary* markedTextStyle; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index c47a5566e5839..c206e7a5a6513 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -814,7 +814,6 @@ - (instancetype)initWithOwner:(FlutterTextInputPlugin*)textInputPlugin { // UITextInput _text = [[NSMutableString alloc] init]; - _markedText = [[NSMutableString alloc] init]; _selectedTextRange = [[FlutterTextRange alloc] initWithNSRange:NSMakeRange(0, 0)]; _markedRect = kInvalidFirstRect; _cachedFirstRect = kInvalidFirstRect; @@ -878,9 +877,11 @@ - (void)configureWithDictionary:(NSDictionary*)configuration { self.keyboardAppearance = UIKeyboardAppearanceDefault; } NSString* autocorrect = configuration[kAutocorrectionType]; - self.autocorrectionType = autocorrect && ![autocorrect boolValue] - ? UITextAutocorrectionTypeNo - : UITextAutocorrectionTypeDefault; + bool autocorrectIsDisabled = autocorrect && ![autocorrect boolValue]; + self.autocorrectionType = + autocorrectIsDisabled ? UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault; + self.spellCheckingType = + autocorrectIsDisabled ? UITextSpellCheckingTypeNo : UITextSpellCheckingTypeDefault; self.autofillId = AutofillIdFromDictionary(configuration); if (autofill == nil) { self.textContentType = @""; @@ -900,7 +901,14 @@ - (UITextContentType)textContentType { // Prevent UIKit from showing selection handles or highlights. This is needed // because Scribble interactions require the view to have it's actual frame on -// the screen. +// the screen. They're not needed on iOS 17 with the new +// UITextSelectionDisplayInteraction API. +// +// These are undocumented methods. On iOS 17, the insertion point color is also +// used as the highlighted background of the selected IME candidate: +// https://github.com/flutter/flutter/issues/132548 +// So the respondsToSelector method is overridden to return NO for this method +// on iOS 17+. - (UIColor*)insertionPointColor { return [UIColor clearColor]; } @@ -928,6 +936,16 @@ - (UIInputViewController*)inputViewController { return _textInputPlugin.textInputDelegate; } +- (BOOL)respondsToSelector:(SEL)selector { + if (@available(iOS 17.0, *)) { + // See the comment on this method. + if (selector == @selector(insertionPointColor)) { + return NO; + } + } + return [super respondsToSelector:selector]; +} + - (void)setTextInputClient:(int)client { _textInputClient = client; _hasPlaceholder = NO; @@ -1230,25 +1248,17 @@ - (NSString*)textInRange:(UITextRange*)range { // Replace the text within the specified range with the given text, // without notifying the framework. - (void)replaceRangeLocal:(NSRange)range withText:(NSString*)text { - NSRange selectedRange = _selectedTextRange.range; - - // Adjust the text selection: - // * reduce the length by the intersection length - // * adjust the location by newLength - oldLength + intersectionLength - NSRange intersectionRange = NSIntersectionRange(range, selectedRange); - if (range.location <= selectedRange.location) { - selectedRange.location += text.length - range.length; - } - if (intersectionRange.location != NSNotFound) { - selectedRange.location += intersectionRange.length; - selectedRange.length -= intersectionRange.length; - } - [self.text replaceCharactersInRange:[self clampSelection:range forText:self.text] withString:text]; - [self setSelectedTextRangeLocal:[FlutterTextRange - rangeWithNSRange:[self clampSelection:selectedRange - forText:self.text]]]; + + // Adjust the selected range and the marked text range. There's no + // documentation but UITextField always sets markedTextRange to nil, + // and collapses the selection to the end of the new replacement text. + const NSRange newSelectionRange = + [self clampSelection:NSMakeRange(range.location + text.length, 0) forText:self.text]; + + [self setSelectedTextRangeLocal:[FlutterTextRange rangeWithNSRange:newSelectionRange]]; + self.markedTextRange = nil; } - (void)replaceRange:(UITextRange*)range withText:(NSString*)text { @@ -1326,11 +1336,10 @@ - (BOOL)shouldChangeTextInRange:(UITextRange*)range replacementText:(NSString*)t return YES; } +// Either replaces the existing marked text or, if none is present, inserts it in +// place of the current selection. - (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelectedRange { NSString* textBeforeChange = [self.text copy]; - NSRange selectedRange = _selectedTextRange.range; - NSRange markedTextRange = ((FlutterTextRange*)self.markedTextRange).range; - NSRange actualReplacedRange; if (_scribbleInteractionStatus != FlutterScribbleInteractionStatusNone || _scribbleFocusStatus != FlutterScribbleFocusStatusUnfocused) { @@ -1341,26 +1350,24 @@ - (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelecte markedText = @""; } - if (markedTextRange.length > 0) { - // Replace text in the marked range with the new text. - [self replaceRangeLocal:markedTextRange withText:markedText]; - actualReplacedRange = markedTextRange; - markedTextRange.length = markedText.length; - } else { - // Replace text in the selected range with the new text. - actualReplacedRange = selectedRange; - [self replaceRangeLocal:selectedRange withText:markedText]; - markedTextRange = NSMakeRange(selectedRange.location, markedText.length); - } + const FlutterTextRange* currentMarkedTextRange = (FlutterTextRange*)self.markedTextRange; + const NSRange& actualReplacedRange = currentMarkedTextRange && !currentMarkedTextRange.isEmpty + ? currentMarkedTextRange.range + : _selectedTextRange.range; + // No need to call replaceRangeLocal as this method always adjusts the + // selected/marked text ranges anyways. + [self.text replaceCharactersInRange:actualReplacedRange withString:markedText]; + const NSRange newMarkedRange = NSMakeRange(actualReplacedRange.location, markedText.length); self.markedTextRange = - markedTextRange.length > 0 ? [FlutterTextRange rangeWithNSRange:markedTextRange] : nil; - - NSUInteger selectionLocation = markedSelectedRange.location + markedTextRange.location; - selectedRange = NSMakeRange(selectionLocation, markedSelectedRange.length); - [self setSelectedTextRangeLocal:[FlutterTextRange - rangeWithNSRange:[self clampSelection:selectedRange - forText:self.text]]]; + newMarkedRange.length > 0 ? [FlutterTextRange rangeWithNSRange:newMarkedRange] : nil; + + [self setSelectedTextRangeLocal: + [FlutterTextRange + rangeWithNSRange:[self clampSelection:NSMakeRange(markedSelectedRange.location + + newMarkedRange.location, + markedSelectedRange.length) + forText:self.text]]]; if (_enableDeltaModel) { NSRange nextReplaceRange = [self clampSelection:actualReplacedRange forText:textBeforeChange]; [self updateEditingStateWithDelta:flutter::TextEditingDelta( diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm index 701621273cd3f..f98571d5c279f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm @@ -769,6 +769,53 @@ - (void)testInputActionContinueAction { OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/textinput" message:encodedMethodCall]); } +- (void)testDisablingAutocorrectDisablesSpellChecking { + FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin]; + + // Disable the interactive selection. + NSDictionary* config = self.mutableTemplateCopy; + [inputView configureWithDictionary:config]; + + XCTAssertEqual(inputView.autocorrectionType, UITextAutocorrectionTypeDefault); + XCTAssertEqual(inputView.spellCheckingType, UITextSpellCheckingTypeDefault); + + [config setValue:@(NO) forKey:@"autocorrect"]; + [inputView configureWithDictionary:config]; + + XCTAssertEqual(inputView.autocorrectionType, UITextAutocorrectionTypeNo); + XCTAssertEqual(inputView.spellCheckingType, UITextSpellCheckingTypeNo); +} + +- (void)testReplaceTestLocalAdjustSelectionAndMarkedTextRange { + FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin]; + [inputView setMarkedText:@"test text" selectedRange:NSMakeRange(0, 5)]; + NSRange selectedTextRange = ((FlutterTextRange*)inputView.selectedTextRange).range; + const NSRange markedTextRange = ((FlutterTextRange*)inputView.markedTextRange).range; + XCTAssertEqual(selectedTextRange.location, 0ul); + XCTAssertEqual(selectedTextRange.length, 5ul); + XCTAssertEqual(markedTextRange.location, 0ul); + XCTAssertEqual(markedTextRange.length, 9ul); + + // Replaces space with space. + [inputView replaceRange:[FlutterTextRange rangeWithNSRange:NSMakeRange(4, 1)] withText:@" "]; + selectedTextRange = ((FlutterTextRange*)inputView.selectedTextRange).range; + + XCTAssertEqual(selectedTextRange.location, 5ul); + XCTAssertEqual(selectedTextRange.length, 0ul); + XCTAssertEqual(inputView.markedTextRange, nil); +} + +- (void)testFlutterTextInputViewOnlyRespondsToInsertionPointColorBelowIOS17 { + FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin]; + BOOL respondsToInsertionPointColor = + [inputView respondsToSelector:@selector(insertionPointColor)]; + if (@available(iOS 17, *)) { + XCTAssertFalse(respondsToInsertionPointColor); + } else { + XCTAssertTrue(respondsToInsertionPointColor); + } +} + #pragma mark - TextEditingDelta tests - (void)testTextEditingDeltasAreGeneratedOnTextInput { FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h new file mode 100644 index 0000000000000..52f4f465c0f16 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h @@ -0,0 +1,14 @@ +// 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. + +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" + +#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_TOUCH_INTERCEPTING_VIEW_TEST_H_ +#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_TOUCH_INTERCEPTING_VIEW_TEST_H_ + +@interface FlutterTouchInterceptingView (Tests) +- (id)accessibilityContainer; +@end + +#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_TOUCH_INTERCEPTING_VIEW_TESTS_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.mm index 488704ea4aeb9..b8f4b65f0f561 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.mm @@ -104,9 +104,9 @@ - (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) { // This is needed to notify the iPadOS keyboard that it needs to update the // state of the UIBarButtons. Otherwise, the state changes to NSUndoManager // will not show up until the next keystroke (or other trigger). - id inputDelegate = - _viewController.engine.textInputPlugin.textInputView.inputDelegate; - [inputDelegate selectionDidChange:_viewController.engine.textInputPlugin.textInputView]; + UITextInputAssistantItem* assistantItem = + _viewController.engine.textInputPlugin.textInputView.inputAssistantItem; + assistantItem.leadingBarButtonGroups = assistantItem.leadingBarButtonGroups; } [self undoManager].groupsByEvent = groupsByEvent; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPluginTest.mm index 24f9bba023582..c9edad986370e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPluginTest.mm @@ -10,11 +10,13 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" FLUTTER_ASSERT_ARC @interface FlutterEngine () - (nonnull FlutterUndoManagerPlugin*)undoManagerPlugin; +- (nonnull FlutterTextInputPlugin*)textInputPlugin; @end @interface FlutterUndoManagerPluginForTest : FlutterUndoManagerPlugin @@ -50,7 +52,6 @@ - (void)setUp { - (void)tearDown { [self.undoManager removeAllActionsWithTarget:self.undoManagerPlugin]; - self.undoManagerPlugin = nil; self.engine = nil; self.viewController = nil; self.undoManager = nil; @@ -140,4 +141,26 @@ - (void)testSetUndoState { XCTAssertEqual(2, delegateRedoCount); } +- (void)testSetUndoStateDoesInteractWithInputDelegate { + // Regression test for https://github.com/flutter/flutter/issues/133424 + FlutterViewController* viewController = OCMPartialMock(self.viewController); + self.undoManagerPlugin.viewController = self.viewController; + + FlutterTextInputPlugin* textInputPlugin = OCMClassMock([FlutterTextInputPlugin class]); + FlutterTextInputView* textInputView = OCMClassMock([FlutterTextInputView class]); + + OCMStub([viewController engine]).andReturn(self.engine); + OCMStub([self.engine textInputPlugin]).andReturn(textInputPlugin); + OCMStub([textInputPlugin textInputView]).andReturn(textInputView); + + FlutterMethodCall* setUndoStateCall = + [FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState" + arguments:@{@"canUndo" : @NO, @"canRedo" : @NO}]; + [self.undoManagerPlugin handleMethodCall:setUndoStateCall + result:^(id _Nullable result){ + }]; + + OCMVerify(never(), [textInputView inputDelegate]); +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.h b/shell/platform/darwin/ios/framework/Source/FlutterView.h index 0dd9f610b9d4c..daebc0df5314c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -46,6 +46,8 @@ opaque:(BOOL)opaque enableWideGamut:(BOOL)isWideGamutEnabled NS_DESIGNATED_INITIALIZER; +- (UIScreen*)screen; + // Set by FlutterEngine or FlutterViewController to override software rendering. @property(class, nonatomic) BOOL forceSoftwareRendering; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index daef8b2ab30d3..dfb25224e0e2a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -48,6 +48,8 @@ - (BOOL)isWideGamutSupported { return NO; } + FML_DCHECK(self.screen); + // This predicates the decision on the capabilities of the iOS device's // display. This means external displays will not support wide gamut if the // device's display doesn't support it. It practice that should be never. @@ -68,10 +70,6 @@ - (instancetype)initWithDelegate:(id)delegate if (self) { _delegate = delegate; _isWideGamutEnabled = isWideGamutEnabled; - if (_isWideGamutEnabled && !self.isWideGamutSupported) { - FML_DLOG(WARNING) << "Rendering wide gamut colors is turned on but isn't " - "supported, downgrading the color gamut to sRGB."; - } self.layer.opaque = opaque; // This line is necessary. CoreAnimation(or UIKit) may take this to do @@ -84,6 +82,16 @@ - (instancetype)initWithDelegate:(id)delegate return self; } +static void PrintWideGamutWarningOnce() { + static BOOL did_print = NO; + if (did_print) { + return; + } + FML_DLOG(WARNING) << "Rendering wide gamut colors is turned on but isn't " + "supported, downgrading the color gamut to sRGB."; + did_print = YES; +} + - (void)layoutSubviews { if ([self.layer isKindOfClass:NSClassFromString(@"CAMetalLayer")]) { // It is a known Apple bug that CAMetalLayer incorrectly reports its supported @@ -92,12 +100,13 @@ - (void)layoutSubviews { #pragma clang diagnostic ignored "-Wunguarded-availability-new" CAMetalLayer* layer = (CAMetalLayer*)self.layer; #pragma clang diagnostic pop - CGFloat screenScale = [UIScreen mainScreen].scale; + CGFloat screenScale = self.screen.scale; layer.allowsGroupOpacity = YES; layer.contentsScale = screenScale; layer.rasterizationScale = screenScale; layer.framebufferOnly = flutter::Settings::kSurfaceDataAccessible ? NO : YES; - if (_isWideGamutEnabled && self.isWideGamutSupported) { + BOOL isWideGamutSupported = self.isWideGamutSupported; + if (_isWideGamutEnabled && isWideGamutSupported) { CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB); layer.colorspace = srgb; CFRelease(srgb); @@ -106,6 +115,8 @@ - (void)layoutSubviews { // F16 was chosen over BGRA10_XR since Skia does not support decoding // BGRA10_XR. layer.pixelFormat = MTLPixelFormatRGBA16Float; + } else if (_isWideGamutEnabled && !isWideGamutSupported) { + PrintWideGamutWarningOnce(); } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index f78ca85f79ee4..c3223766d4c01 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -717,7 +717,7 @@ - (void)setSplashScreenView:(UIView*)view { } - (void)setFlutterViewDidRenderCallback:(void (^)(void))callback { - _flutterViewRenderedCallback.reset(callback, fml::OwnershipPolicy::Retain); + _flutterViewRenderedCallback.reset(callback, fml::OwnershipPolicy::kRetain); } #pragma mark - Surface creation and teardown updates diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm index 78f95210e37e6..b5a0c1002961a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm @@ -69,4 +69,17 @@ - (void)testIgnoreWideColorWithoutImpeller { XCTAssertEqual(layer.pixelFormat, MTLPixelFormatBGRA8Unorm); } +- (void)testLayerScalesMatchScreenAfterLayoutSubviews { + FakeDelegate* delegate = [[[FakeDelegate alloc] init] autorelease]; + FlutterView* view = [[FlutterView alloc] initWithDelegate:delegate opaque:NO enableWideGamut:NO]; + view.layer.contentsScale = CGFloat(-99.0); + view.layer.rasterizationScale = CGFloat(-99.0); + UIScreen* screen = [view screen]; + XCTAssertNotEqual(view.layer.contentsScale, screen.scale); + XCTAssertNotEqual(view.layer.rasterizationScale, screen.scale); + [view layoutSubviews]; + XCTAssertEqual(view.layer.contentsScale, screen.scale); + XCTAssertEqual(view.layer.rasterizationScale, screen.scale); +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h index f7c611dec2ced..8a431193d1a8d 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h @@ -18,6 +18,7 @@ constexpr float kScrollExtentMaxForInf = 1000; @class FlutterCustomAccessibilityAction; @class FlutterPlatformViewSemanticsContainer; +@class FlutterTouchInterceptingView; /** * A node in the iOS semantics tree. This object is a wrapper over a native accessibiliy @@ -171,7 +172,8 @@ constexpr float kScrollExtentMaxForInf = 1000; - (instancetype)initWithBridge:(fml::WeakPtr)bridge uid:(int32_t)uid - platformView:(UIView*)platformView NS_DESIGNATED_INITIALIZER; + platformView:(FlutterTouchInterceptingView*)platformView + NS_DESIGNATED_INITIALIZER; @end diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm index 5889a32463892..8f278fe31a721 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm @@ -157,9 +157,7 @@ @interface FlutterScrollableSemanticsObject () @property(nonatomic, retain) FlutterSemanticsScrollView* scrollView; @end -@implementation FlutterScrollableSemanticsObject { - fml::scoped_nsobject _container; -} +@implementation FlutterScrollableSemanticsObject - (instancetype)initWithBridge:(fml::WeakPtr)bridge uid:(int32_t)uid { @@ -865,9 +863,10 @@ @implementation FlutterPlatformViewSemanticsContainer - (instancetype)initWithBridge:(fml::WeakPtr)bridge uid:(int32_t)uid - platformView:(nonnull UIView*)platformView { + platformView:(nonnull FlutterTouchInterceptingView*)platformView { if (self = [super initWithBridge:bridge uid:uid]) { _platformView = [platformView retain]; + [platformView setFlutterAccessibilityContainer:self]; } return self; } @@ -882,12 +881,6 @@ - (id)nativeAccessibility { return _platformView; } -#pragma mark - UIAccessibilityContainer overrides - -- (NSArray*)accessibilityElements { - return @[ _platformView ]; -} - @end @implementation SemanticsObjectContainer { diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm index 2a3f7e799e40f..3d7f2cdf0163e 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm @@ -7,97 +7,13 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h" FLUTTER_ASSERT_ARC -const CGRect kScreenSize = CGRectMake(0, 0, 600, 800); - -namespace flutter { -namespace { - -class SemanticsActionObservation { - public: - SemanticsActionObservation(int32_t observed_id, SemanticsAction observed_action) - : id(observed_id), action(observed_action) {} - - int32_t id; - SemanticsAction action; -}; - -class MockAccessibilityBridge : public AccessibilityBridgeIos { - public: - MockAccessibilityBridge() : observations({}) { - view_ = [[UIView alloc] initWithFrame:kScreenSize]; - window_ = [[UIWindow alloc] initWithFrame:kScreenSize]; - [window_ addSubview:view_]; - } - bool isVoiceOverRunning() const override { return isVoiceOverRunningValue; } - UIView* view() const override { return view_; } - UIView* textInputView() override { return nil; } - void DispatchSemanticsAction(int32_t id, SemanticsAction action) override { - SemanticsActionObservation observation(id, action); - observations.push_back(observation); - } - void DispatchSemanticsAction(int32_t id, - SemanticsAction action, - fml::MallocMapping args) override { - SemanticsActionObservation observation(id, action); - observations.push_back(observation); - } - void AccessibilityObjectDidBecomeFocused(int32_t id) override {} - void AccessibilityObjectDidLoseFocus(int32_t id) override {} - std::shared_ptr GetPlatformViewsController() const override { - return nil; - } - std::vector observations; - bool isVoiceOverRunningValue; - - private: - UIView* view_; - UIWindow* window_; -}; - -class MockAccessibilityBridgeNoWindow : public AccessibilityBridgeIos { - public: - MockAccessibilityBridgeNoWindow() : observations({}) { - view_ = [[UIView alloc] initWithFrame:kScreenSize]; - } - bool isVoiceOverRunning() const override { return isVoiceOverRunningValue; } - UIView* view() const override { return view_; } - UIView* textInputView() override { return nil; } - void DispatchSemanticsAction(int32_t id, SemanticsAction action) override { - SemanticsActionObservation observation(id, action); - observations.push_back(observation); - } - void DispatchSemanticsAction(int32_t id, - SemanticsAction action, - fml::MallocMapping args) override { - SemanticsActionObservation observation(id, action); - observations.push_back(observation); - } - void AccessibilityObjectDidBecomeFocused(int32_t id) override {} - void AccessibilityObjectDidLoseFocus(int32_t id) override {} - std::shared_ptr GetPlatformViewsController() const override { - return nil; - } - std::vector observations; - bool isVoiceOverRunningValue; - - private: - UIView* view_; -}; -} // namespace -} // namespace flutter - @interface SemanticsObjectTest : XCTestCase @end -@interface SemanticsObject (Tests) -- (BOOL)accessibilityScrollToVisible; -- (BOOL)accessibilityScrollToVisibleWithChild:(id)child; -- (id)_accessibilityHitTest:(CGPoint)point withEvent:(UIEvent*)event; -@end - @implementation SemanticsObjectTest - (void)testCreate { @@ -203,54 +119,6 @@ - (void)testAccessibilityHitTestNoFocusableItem { XCTAssertNil(hitTestResult); } -- (void)testAccessibilityHitTestSearchCanReturnPlatformView { - fml::WeakPtrFactory factory( - new flutter::MockAccessibilityBridge()); - fml::WeakPtr bridge = factory.GetWeakPtr(); - SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0]; - SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1]; - SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3]; - UIView* platformView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; - FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer = - [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge - uid:1 - platformView:platformView]; - - object0.children = @[ object1 ]; - object0.childrenInHitTestOrder = @[ object1 ]; - object1.children = @[ platformViewSemanticsContainer, object3 ]; - object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ]; - - flutter::SemanticsNode node0; - node0.id = 0; - node0.rect = SkRect::MakeXYWH(0, 0, 200, 200); - node0.label = "0"; - [object0 setSemanticsNode:&node0]; - - flutter::SemanticsNode node1; - node1.id = 1; - node1.rect = SkRect::MakeXYWH(0, 0, 200, 200); - node1.label = "1"; - [object1 setSemanticsNode:&node1]; - - flutter::SemanticsNode node2; - node2.id = 2; - node2.rect = SkRect::MakeXYWH(0, 0, 100, 100); - node2.label = "2"; - [platformViewSemanticsContainer setSemanticsNode:&node2]; - - flutter::SemanticsNode node3; - node3.id = 3; - node3.rect = SkRect::MakeXYWH(0, 0, 200, 200); - node3.label = "3"; - [object3 setSemanticsNode:&node3]; - - CGPoint point = CGPointMake(10, 10); - id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil]; - - XCTAssertEqual(hitTestResult, platformView); -} - - (void)testAccessibilityScrollToVisible { fml::WeakPtrFactory factory( new flutter::MockAccessibilityBridge()); @@ -897,27 +765,6 @@ - (void)testShouldDispatchShowOnScreenActionForHidden { XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen); } -- (void)testFlutterPlatformViewSemanticsContainer { - fml::WeakPtrFactory factory( - new flutter::MockAccessibilityBridge()); - fml::WeakPtr bridge = factory.GetWeakPtr(); - __weak UIView* weakPlatformView; - @autoreleasepool { - UIView* platformView = [[UIView alloc] init]; - - FlutterPlatformViewSemanticsContainer* container = - [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge - uid:1 - platformView:platformView]; - XCTAssertEqualObjects(container.accessibilityElements, @[ platformView ]); - weakPlatformView = platformView; - XCTAssertNotNil(weakPlatformView); - } - // Check if there's no more strong references to `platformView` after container and platformView - // are released. - XCTAssertNil(weakPlatformView); -} - - (void)testFlutterSwitchSemanticsObjectMatchesUISwitch { fml::WeakPtrFactory factory( new flutter::MockAccessibilityBridge()); diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h new file mode 100644 index 0000000000000..0f0c0303d4a98 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h @@ -0,0 +1,95 @@ +// 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 SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_SEMANTICS_OBJECT_TEST_MOCKS_H_ +#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_SEMANTICS_OBJECT_TEST_MOCKS_H_ + +#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" + +const CGRect kScreenSize = CGRectMake(0, 0, 600, 800); + +namespace flutter { +namespace { + +class SemanticsActionObservation { + public: + SemanticsActionObservation(int32_t observed_id, SemanticsAction observed_action) + : id(observed_id), action(observed_action) {} + + int32_t id; + SemanticsAction action; +}; + +class MockAccessibilityBridge : public AccessibilityBridgeIos { + public: + MockAccessibilityBridge() : observations({}) { + view_ = [[UIView alloc] initWithFrame:kScreenSize]; + window_ = [[UIWindow alloc] initWithFrame:kScreenSize]; + [window_ addSubview:view_]; + } + bool isVoiceOverRunning() const override { return isVoiceOverRunningValue; } + UIView* view() const override { return view_; } + UIView* textInputView() override { return nil; } + void DispatchSemanticsAction(int32_t id, SemanticsAction action) override { + SemanticsActionObservation observation(id, action); + observations.push_back(observation); + } + void DispatchSemanticsAction(int32_t id, + SemanticsAction action, + fml::MallocMapping args) override { + SemanticsActionObservation observation(id, action); + observations.push_back(observation); + } + void AccessibilityObjectDidBecomeFocused(int32_t id) override {} + void AccessibilityObjectDidLoseFocus(int32_t id) override {} + std::shared_ptr GetPlatformViewsController() const override { + return nil; + } + std::vector observations; + bool isVoiceOverRunningValue; + + private: + UIView* view_; + UIWindow* window_; +}; + +class MockAccessibilityBridgeNoWindow : public AccessibilityBridgeIos { + public: + MockAccessibilityBridgeNoWindow() : observations({}) { + view_ = [[UIView alloc] initWithFrame:kScreenSize]; + } + bool isVoiceOverRunning() const override { return isVoiceOverRunningValue; } + UIView* view() const override { return view_; } + UIView* textInputView() override { return nil; } + void DispatchSemanticsAction(int32_t id, SemanticsAction action) override { + SemanticsActionObservation observation(id, action); + observations.push_back(observation); + } + void DispatchSemanticsAction(int32_t id, + SemanticsAction action, + fml::MallocMapping args) override { + SemanticsActionObservation observation(id, action); + observations.push_back(observation); + } + void AccessibilityObjectDidBecomeFocused(int32_t id) override {} + void AccessibilityObjectDidLoseFocus(int32_t id) override {} + std::shared_ptr GetPlatformViewsController() const override { + return nil; + } + std::vector observations; + bool isVoiceOverRunningValue; + + private: + UIView* view_; +}; +} // namespace +} // namespace flutter + +@interface SemanticsObject (Tests) +- (BOOL)accessibilityScrollToVisible; +- (BOOL)accessibilityScrollToVisibleWithChild:(id)child; +- (id)_accessibilityHitTest:(CGPoint)point withEvent:(UIEvent*)event; +@end + +#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_SEMANTICS_OBJECT_TEST_MOCKS_H_ diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest_mrc.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest_mrc.mm new file mode 100644 index 0000000000000..0567e37c0e30e --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest_mrc.mm @@ -0,0 +1,89 @@ +// 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. + +#import +#import + +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h" + +FLUTTER_ASSERT_NOT_ARC + +@interface SemanticsObjectTestMRC : XCTestCase +@end + +@implementation SemanticsObjectTestMRC + +- (void)testAccessibilityHitTestSearchCanReturnPlatformView { + fml::WeakPtrFactory factory( + new flutter::MockAccessibilityBridge()); + fml::WeakPtr bridge = factory.GetWeakPtr(); + SemanticsObject* object0 = [[[SemanticsObject alloc] initWithBridge:bridge uid:0] autorelease]; + SemanticsObject* object1 = [[[SemanticsObject alloc] initWithBridge:bridge uid:1] autorelease]; + SemanticsObject* object3 = [[[SemanticsObject alloc] initWithBridge:bridge uid:3] autorelease]; + FlutterTouchInterceptingView* platformView = + [[[FlutterTouchInterceptingView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease]; + FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer = + [[[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge + uid:1 + platformView:platformView] autorelease]; + + object0.children = @[ object1 ]; + object0.childrenInHitTestOrder = @[ object1 ]; + object1.children = @[ platformViewSemanticsContainer, object3 ]; + object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ]; + + flutter::SemanticsNode node0; + node0.id = 0; + node0.rect = SkRect::MakeXYWH(0, 0, 200, 200); + node0.label = "0"; + [object0 setSemanticsNode:&node0]; + + flutter::SemanticsNode node1; + node1.id = 1; + node1.rect = SkRect::MakeXYWH(0, 0, 200, 200); + node1.label = "1"; + [object1 setSemanticsNode:&node1]; + + flutter::SemanticsNode node2; + node2.id = 2; + node2.rect = SkRect::MakeXYWH(0, 0, 100, 100); + node2.label = "2"; + [platformViewSemanticsContainer setSemanticsNode:&node2]; + + flutter::SemanticsNode node3; + node3.id = 3; + node3.rect = SkRect::MakeXYWH(0, 0, 200, 200); + node3.label = "3"; + [object3 setSemanticsNode:&node3]; + + CGPoint point = CGPointMake(10, 10); + id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil]; + + XCTAssertEqual(hitTestResult, platformView); +} + +- (void)testFlutterPlatformViewSemanticsContainer { + fml::WeakPtrFactory factory( + new flutter::MockAccessibilityBridge()); + fml::WeakPtr bridge = factory.GetWeakPtr(); + FlutterTouchInterceptingView* platformView = + [[[FlutterTouchInterceptingView alloc] init] autorelease]; + @autoreleasepool { + FlutterPlatformViewSemanticsContainer* container = + [[[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge + uid:1 + platformView:platformView] autorelease]; + XCTAssertEqualObjects(platformView.accessibilityContainer, container); + XCTAssertEqual(platformView.retainCount, 2u); + } + // Check if there's no more strong references to `platformView` after container and platformView + // are released. + XCTAssertEqual(platformView.retainCount, 1u); +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index bf19109e92d07..27b33ad4ca3d4 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -279,7 +279,7 @@ static void ReplaceSemanticsObject(SemanticsObject* oldObject, return [[[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:weak_ptr uid:node.id - platformView:weak_ptr->GetPlatformViewsController()->GetPlatformViewByID( + platformView:weak_ptr->GetPlatformViewsController()->GetFlutterTouchInterceptingViewByID( node.platformViewId)] autorelease]; } else { return [[[FlutterSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id] autorelease]; diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm index 53b04e484d6fb..ffe0f60802608 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm +++ b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm @@ -9,7 +9,7 @@ PlatformMessageResponseDarwin::PlatformMessageResponseDarwin( PlatformMessageResponseCallback callback, fml::RefPtr platform_task_runner) - : callback_(callback, fml::OwnershipPolicy::Retain), + : callback_(callback, fml::OwnershipPolicy::kRetain), platform_task_runner_(std::move(platform_task_runner)) {} PlatformMessageResponseDarwin::~PlatformMessageResponseDarwin() = default; diff --git a/shell/platform/darwin/ios/platform_message_handler_ios.mm b/shell/platform/darwin/ios/platform_message_handler_ios.mm index 8a1b2ebe0d66b..b9011009798c8 100644 --- a/shell/platform/darwin/ios/platform_message_handler_ios.mm +++ b/shell/platform/darwin/ios/platform_message_handler_ios.mm @@ -125,7 +125,7 @@ - (void)dispatch:(dispatch_block_t)block { message_handlers_[channel] = { .task_queue = fml::scoped_nsprotocol([task_queue retain]), .handler = - fml::ScopedBlock{handler, fml::OwnershipPolicy::Retain}, + fml::ScopedBlock{handler, fml::OwnershipPolicy::kRetain}, }; } } diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h b/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h index bb12d5cd2ae76..bc0cb71097360 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h @@ -16,7 +16,7 @@ #import "FlutterPluginRegistrarMacOS.h" #import "FlutterTexture.h" -// TODO: Merge this file with the iOS FlutterEngine.h. +// TODO(stuartmorgan): Merge this file with the iOS FlutterEngine.h. @class FlutterViewController; diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h b/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h index 16768776afb0c..338896041d6bd 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h @@ -11,8 +11,8 @@ #import "FlutterPluginMacOS.h" #import "FlutterTexture.h" -// TODO: Merge this file and FlutterPluginMacOS.h with the iOS FlutterPlugin.h, sharing all but -// the platform-specific methods. +// TODO(stuartmorgan): Merge this file and FlutterPluginMacOS.h with the iOS FlutterPlugin.h, +// sharing all but the platform-specific methods. /** * The protocol for an object managing registration for a plugin. It provides access to application diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h index b0d7a9f8bd86e..3a28e94a85631 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h @@ -14,13 +14,26 @@ */ typedef NS_ENUM(NSInteger, FlutterMouseTrackingMode) { // Hover events will never be sent to Flutter. - FlutterMouseTrackingModeNone = 0, + kFlutterMouseTrackingModeNone = 0, + // NOLINTNEXTLINE(readability-identifier-naming) + FlutterMouseTrackingModeNone __attribute__((deprecated)) = kFlutterMouseTrackingModeNone, + // Hover events will be sent to Flutter when the view is in the key window. - FlutterMouseTrackingModeInKeyWindow, + kFlutterMouseTrackingModeInKeyWindow = 1, + // NOLINTNEXTLINE(readability-identifier-naming) + FlutterMouseTrackingModeInKeyWindow + __attribute__((deprecated)) = kFlutterMouseTrackingModeInKeyWindow, + // Hover events will be sent to Flutter when the view is in the active app. - FlutterMouseTrackingModeInActiveApp, + kFlutterMouseTrackingModeInActiveApp = 2, + // NOLINTNEXTLINE(readability-identifier-naming) + FlutterMouseTrackingModeInActiveApp + __attribute__((deprecated)) = kFlutterMouseTrackingModeInActiveApp, + // Hover events will be sent to Flutter regardless of window and app focus. - FlutterMouseTrackingModeAlways, + kFlutterMouseTrackingModeAlways = 3, + // NOLINTNEXTLINE(readability-identifier-naming) + FlutterMouseTrackingModeAlways __attribute__((deprecated)) = kFlutterMouseTrackingModeAlways, }; /** diff --git a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm index 536a62dceabe6..1b3c10a00230e 100644 --- a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm +++ b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm @@ -61,7 +61,6 @@ @implementation AccessibilityBridgeTestViewController TEST(AccessibilityBridgeMacTest, SendsAccessibilityCreateNotificationToWindowOfFlutterView) { FlutterViewController* viewController = CreateTestViewController(); FlutterEngine* engine = viewController.engine; - [viewController loadView]; NSWindow* expectedTarget = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask @@ -105,10 +104,10 @@ @implementation AccessibilityBridgeTestViewController bridge->OnAccessibilityEvent(targeted_event); - EXPECT_EQ(bridge->actual_notifications.size(), 1u); - EXPECT_EQ( - bridge->actual_notifications.find([NSAccessibilityCreatedNotification UTF8String])->second, - expectedTarget); + ASSERT_EQ(bridge->actual_notifications.size(), 1u); + auto target = bridge->actual_notifications.find([NSAccessibilityCreatedNotification UTF8String]); + ASSERT_NE(target, bridge->actual_notifications.end()); + EXPECT_EQ(target->second, expectedTarget); [engine shutDownEngine]; } @@ -122,7 +121,6 @@ @implementation AccessibilityBridgeTestViewController TEST(AccessibilityBridgeMacTest, NonZeroRootNodeId) { FlutterViewController* viewController = CreateTestViewController(); FlutterEngine* engine = viewController.engine; - [viewController loadView]; NSWindow* expectedTarget = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask @@ -192,7 +190,7 @@ @implementation AccessibilityBridgeTestViewController TEST(AccessibilityBridgeMacTest, DoesNotSendAccessibilityCreateNotificationWhenHeadless) { FlutterViewController* viewController = CreateTestViewController(); FlutterEngine* engine = viewController.engine; - [viewController loadView]; + // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. engine.semanticsEnabled = YES; @@ -238,7 +236,6 @@ @implementation AccessibilityBridgeTestViewController TEST(AccessibilityBridgeMacTest, DoesNotSendAccessibilityCreateNotificationWhenNoWindow) { FlutterViewController* viewController = CreateTestViewController(); FlutterEngine* engine = viewController.engine; - [viewController loadView]; // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.mm b/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.mm index bcb7aa370ea76..98abd505d4d9b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.mm @@ -40,6 +40,74 @@ - (nonnull instancetype)initWithChannel:(nonnull FlutterBasicMessageChannel*)cha return self; } +/// Checks single modifier flag from event flags and sends appropriate key event +/// if it is different from the previous state. +- (void)checkModifierFlag:(NSUInteger)targetMask + forEventFlags:(NSEventModifierFlags)eventFlags + keyCode:(NSUInteger)keyCode + timestamp:(NSTimeInterval)timestamp { + NSAssert((targetMask & (targetMask - 1)) == 0, @"targetMask must only have one bit set"); + if ((eventFlags & targetMask) != (_previouslyPressedFlags & targetMask)) { + uint64_t newFlags = (_previouslyPressedFlags & ~targetMask) | (eventFlags & targetMask); + + // Sets combined flag if either left or right modifier is pressed, unsets otherwise. + auto updateCombinedFlag = [&](uint64_t side1, uint64_t side2, NSEventModifierFlags flag) { + if (newFlags & (side1 | side2)) { + newFlags |= flag; + } else { + newFlags &= ~flag; + } + }; + updateCombinedFlag(flutter::kModifierFlagShiftLeft, flutter::kModifierFlagShiftRight, + NSEventModifierFlagShift); + updateCombinedFlag(flutter::kModifierFlagControlLeft, flutter::kModifierFlagControlRight, + NSEventModifierFlagControl); + updateCombinedFlag(flutter::kModifierFlagAltLeft, flutter::kModifierFlagAltRight, + NSEventModifierFlagOption); + updateCombinedFlag(flutter::kModifierFlagMetaLeft, flutter::kModifierFlagMetaRight, + NSEventModifierFlagCommand); + + NSEvent* event = [NSEvent keyEventWithType:NSEventTypeFlagsChanged + location:NSZeroPoint + modifierFlags:newFlags + timestamp:timestamp + windowNumber:0 + context:nil + characters:@"" + charactersIgnoringModifiers:@"" + isARepeat:NO + keyCode:keyCode]; + [self handleEvent:event + callback:^(BOOL){ + }]; + }; +} + +- (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags + timestamp:(NSTimeInterval)timestamp { + modifierFlags = modifierFlags & ~0x100; + if (_previouslyPressedFlags == modifierFlags) { + return; + } + + [flutter::modifierFlagToKeyCode + enumerateKeysAndObjectsUsingBlock:^(NSNumber* flag, NSNumber* keyCode, BOOL* stop) { + [self checkModifierFlag:[flag unsignedShortValue] + forEventFlags:modifierFlags + keyCode:[keyCode unsignedShortValue] + timestamp:timestamp]; + }]; + + // Caps lock is not included in the modifierFlagToKeyCode map. + [self checkModifierFlag:NSEventModifierFlagCapsLock + forEventFlags:modifierFlags + keyCode:0x00000039 // kVK_CapsLock + timestamp:timestamp]; + + // At the end we should end up with the same modifier flags as the event. + FML_DCHECK(_previouslyPressedFlags == modifierFlags); +} + - (void)handleEvent:(NSEvent*)event callback:(FlutterAsyncKeyCallback)callback { // Remove the modifier bits that Flutter is not interested in. NSEventModifierFlags modifierFlags = event.modifierFlags & ~0x100; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h index b61c790028a3b..2c8918d45d70c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h @@ -24,6 +24,15 @@ typedef void (^FlutterAsyncKeyCallback)(BOOL handled); @required - (void)handleEvent:(nonnull NSEvent*)event callback:(nonnull FlutterAsyncKeyCallback)callback; +/** + * Synchronize the modifier flags if necessary. The new modifier flag would usually come from mouse + * event and may be out of sync with current keyboard state if the modifier flags have changed while + * window was not key. + */ +@required +- (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags + timestamp:(NSTimeInterval)timestamp; + /* A map from macOS key code to logical keyboard. * * The map is assigned on initialization, and updated when the user changes diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm index 2238c300bf9a3..00ba4ac38cc39 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm @@ -275,7 +275,7 @@ - (void)buildLayout { std::map mandatoryGoalsByChar; std::map usLayoutGoalsByKeyCode; - for (const LayoutGoal& goal : flutter::layoutGoals) { + for (const LayoutGoal& goal : flutter::kLayoutGoals) { if (goal.mandatory) { mandatoryGoalsByChar[goal.keyChar] = goal; } else { @@ -337,10 +337,9 @@ - (void)buildLayout { - (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags timestamp:(NSTimeInterval)timestamp { - // The embedder responder is the first element in _primaryResponders. - FlutterEmbedderKeyResponder* embedderResponder = - (FlutterEmbedderKeyResponder*)_primaryResponders[0]; - [embedderResponder syncModifiersIfNeeded:modifierFlags timestamp:timestamp]; + for (id responder in _primaryResponders) { + [responder syncModifiersIfNeeded:modifierFlags timestamp:timestamp]; + } } /** diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerTest.mm index 4bb5766791102..5e57509f704c6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerTest.mm @@ -276,7 +276,9 @@ - (nonnull instancetype)init { OCMStub([viewDelegateMock onTextInputKeyEvent:[OCMArg any]]) .andCall(self, @selector(handleTextInputKeyEvent:)); OCMStub([viewDelegateMock getBinaryMessenger]).andReturn(_messengerMock); - OCMStub([viewDelegateMock sendKeyEvent:FlutterKeyEvent {} callback:nil userData:nil]) + OCMStub([viewDelegateMock sendKeyEvent:*(const FlutterKeyEvent*)[OCMArg anyPointer] + callback:nil + userData:nil]) .ignoringNonObjectArgs() .andCall(self, @selector(handleEmbedderEvent:callback:userData:)); OCMStub([viewDelegateMock subscribeToKeyboardLayoutChange:[OCMArg any]]) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.mm b/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.mm index 3c079c1a6ba42..0c5c4af7b3e3a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.mm @@ -5,12 +5,13 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.h" #include - #include #include "flutter/fml/logging.h" #include "flutter/shell/platform/embedder/embedder.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h" + @interface FlutterMutatorView () { // Each of these views clips to a CGPathRef. These views, if present, // are nested (first is child of FlutterMutatorView and last is parent of @@ -395,6 +396,7 @@ - (instancetype)initWithPlatformView:(NSView*)platformView { _platformView = platformView; _pathClipViews = [NSMutableArray array]; self.wantsLayer = YES; + self.clipsToBounds = YES; } return self; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMutatorViewTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterMutatorViewTest.mm index da734aea678a1..5699b1480d664 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMutatorViewTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMutatorViewTest.mm @@ -6,6 +6,8 @@ #include "third_party/googletest/googletest/include/gtest/gtest.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h" + @interface FlutterMutatorView (Private) @property(readonly, nonatomic, nonnull) NSMutableArray* pathClipViews; @@ -96,6 +98,12 @@ void ExpectTransform3DEqual(const CATransform3D& t, const CATransform3D& u) { EXPECT_NE(mutatorView.platformViewContainer, nil); } +TEST(FlutterMutatorViewTest, ClipsToBounds) { + NSView* platformView = [[NSView alloc] init]; + FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView]; + EXPECT_TRUE(mutatorView.clipsToBounds); +} + TEST(FlutterMutatorViewTest, TransformedFrameIsCorrect) { NSView* platformView = [[NSView alloc] init]; FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView]; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm index 63c4df7d5eec0..815336227f39c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm @@ -62,16 +62,17 @@ - (instancetype)initWithFlutterEngine:(nonnull FlutterEngine*)flutterEngine { - (FlutterRendererConfig)createRendererConfig { FlutterRendererConfig config = { .type = FlutterRendererType::kMetal, - .metal.struct_size = sizeof(FlutterMetalRendererConfig), - .metal.device = (__bridge FlutterMetalDeviceHandle)_device, - .metal.present_command_queue = (__bridge FlutterMetalCommandQueueHandle)_commandQueue, - .metal.get_next_drawable_callback = - reinterpret_cast(OnGetNextDrawable), - .metal.present_drawable_callback = - reinterpret_cast(OnPresentDrawable), - .metal.external_texture_frame_callback = - reinterpret_cast(OnAcquireExternalTexture), - }; + .metal = { + .struct_size = sizeof(FlutterMetalRendererConfig), + .device = (__bridge FlutterMetalDeviceHandle)_device, + .present_command_queue = (__bridge FlutterMetalCommandQueueHandle)_commandQueue, + .get_next_drawable_callback = + reinterpret_cast(OnGetNextDrawable), + .present_drawable_callback = + reinterpret_cast(OnPresentDrawable), + .external_texture_frame_callback = + reinterpret_cast(OnAcquireExternalTexture), + }}; return config; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h index dedfc661884f6..d5e23c8d5aadc 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h @@ -64,5 +64,6 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange; - (NSDictionary*)editingState; +@property(nonatomic) NSTextInputContext* textInputContext; @property(readwrite, nonatomic) NSString* customRunLoopMode; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index f6460f2309f37..80e7fb946a58a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -16,6 +16,7 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h" static NSString* const kTextInputChannel = @"flutter/textinput"; @@ -342,6 +343,7 @@ - (instancetype)initWithViewController:(FlutterViewController*)viewController { // The view needs an empty frame otherwise it is visible on dark background. // https://github.com/flutter/flutter/issues/118504 self = [super initWithFrame:NSZeroRect]; + self.clipsToBounds = YES; if (self != nil) { _flutterViewController = viewController; _channel = [FlutterMethodChannel methodChannelWithName:kTextInputChannel @@ -383,10 +385,7 @@ - (void)dealloc { - (void)resignAndRemoveFromSuperview { if (self.superview != nil) { - // With accessiblity enabled TextInputPlugin is inside _client, so take the - // nextResponder from the _client. - NSResponder* nextResponder = _client != nil ? _client.nextResponder : self.nextResponder; - [self.window makeFirstResponder:nextResponder]; + [self.window makeFirstResponder:_flutterViewController.flutterView]; [self removeFromSuperview]; } } @@ -620,7 +619,15 @@ - (BOOL)handleKeyEvent:(NSEvent*)event { // text command (indicated by calling doCommandBySelector) or might not (for example, Cmd+Q). In // the latter case, this command somehow has not been executed yet and Flutter must dispatch it to // the next responder. See https://github.com/flutter/flutter/issues/106354 . - if (event.isKeyEquivalent && !_eventProducedOutput) { + // The event is also not redispatched if there is IME composition active, because it might be + // handled by the IME. See https://github.com/flutter/flutter/issues/134699 + + // both NSEventModifierFlagNumericPad and NSEventModifierFlagFunction are set for arrow keys. + bool is_navigation = event.modifierFlags & NSEventModifierFlagFunction && + event.modifierFlags & NSEventModifierFlagNumericPad; + bool is_navigation_in_ime = is_navigation && self.hasMarkedText; + + if (event.isKeyEquivalent && !is_navigation_in_ime && !_eventProducedOutput) { return NO; } return res; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index 00bbb68dc69ae..1202813a746e0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -9,6 +9,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h" #import #import "flutter/testing/testing.h" @@ -1361,6 +1362,89 @@ - (bool)testPerformKeyEquivalent { return true; } +- (bool)handleArrowKeyWhenImePopoverIsActive { + id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [engineMock binaryMessenger]) + .andReturn(binaryMessengerMock); + OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {} + callback:nil + userData:nil]); + + NSTextInputContext* textInputContext = OCMClassMock([NSTextInputContext class]); + OCMStub([textInputContext handleEvent:[OCMArg any]]).andReturn(YES); + + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock + nibName:@"" + bundle:nil]; + + FlutterTextInputPlugin* plugin = + [[FlutterTextInputPlugin alloc] initWithViewController:viewController]; + + plugin.textInputContext = textInputContext; + + NSDictionary* setClientConfig = @{ + @"inputAction" : @"action", + @"enableDeltaModel" : @"true", + @"inputType" : @{@"name" : @"inputName"}, + }; + [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"TextInput.setClient" + arguments:@[ @(1), setClientConfig ]] + result:^(id){ + }]; + + [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"TextInput.show" + arguments:@[]] + result:^(id){ + }]; + + // Set marked text, simulate active IME popover. + [plugin setMarkedText:@"m" + selectedRange:NSMakeRange(0, 1) + replacementRange:NSMakeRange(NSNotFound, 0)]; + + // Right arrow key. This, unlike the key below should be handled by the plugin. + NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyDown + location:NSZeroPoint + modifierFlags:0xa00100 + timestamp:0 + windowNumber:0 + context:nil + characters:@"\uF702" + charactersIgnoringModifiers:@"\uF702" + isARepeat:NO + keyCode:0x4]; + + // Plugin should mark the event as key equivalent. + [plugin performKeyEquivalent:event]; + + if ([plugin handleKeyEvent:event] != true) { + return false; + } + + // CTRL+H (delete backwards) + event = [NSEvent keyEventWithType:NSEventTypeKeyDown + location:NSZeroPoint + modifierFlags:0x40101 + timestamp:0 + windowNumber:0 + context:nil + characters:@"\uF702" + charactersIgnoringModifiers:@"\uF702" + isARepeat:NO + keyCode:0x4]; + + // Plugin should mark the event as key equivalent. + [plugin performKeyEquivalent:event]; + + if ([plugin handleKeyEvent:event] != false) { + return false; + } + + return true; +} + - (bool)unhandledKeyEquivalent { id engineMock = flutter::testing::CreateMockFlutterEngine(@""); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); @@ -1814,6 +1898,10 @@ - (bool)testSelectorsAreForwardedToFramework { ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testPerformKeyEquivalent]); } +TEST(FlutterTextInputPluginTest, HandleArrowKeyWhenImePopoverIsActive) { + ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] handleArrowKeyWhenImePopoverIsActive]); +} + TEST(FlutterTextInputPluginTest, UnhandledKeyEquivalent) { ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] unhandledKeyEquivalent]); } @@ -1962,7 +2050,42 @@ - (bool)testSelectorsAreForwardedToFramework { ASSERT_FALSE(window.firstResponder == viewController.textInputPlugin); } -TEST(FlutterTextInputPluginTest, HasZeroSize) { +TEST(FlutterTextInputPluginTest, FirstResponderIsCorrect) { + FlutterEngine* engine = CreateTestEngine(); + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + [viewController loadView]; + + NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + window.contentView = viewController.view; + + ASSERT_TRUE(viewController.flutterView.acceptsFirstResponder); + + [window makeFirstResponder:viewController.flutterView]; + + [viewController.textInputPlugin + handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"TextInput.show" arguments:@[]] + result:^(id){ + }]; + + ASSERT_TRUE(window.firstResponder == viewController.textInputPlugin); + + ASSERT_FALSE(viewController.flutterView.acceptsFirstResponder); + + [viewController.textInputPlugin + handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"TextInput.hide" arguments:@[]] + result:^(id){ + }]; + + ASSERT_TRUE(viewController.flutterView.acceptsFirstResponder); + ASSERT_TRUE(window.firstResponder == viewController.flutterView); +} + +TEST(FlutterTextInputPluginTest, HasZeroSizeAndClipsToBounds) { id engineMock = flutter::testing::CreateMockFlutterEngine(@""); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) @@ -1977,6 +2100,7 @@ - (bool)testSelectorsAreForwardedToFramework { [[FlutterTextInputPlugin alloc] initWithViewController:viewController]; ASSERT_TRUE(NSIsEmptyRect(plugin.frame)); + ASSERT_TRUE(plugin.clipsToBounds); } } // namespace flutter::testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 6948b880fcf9b..d82f34c51b0cb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -23,13 +23,19 @@ typedef int64_t FlutterViewId; constexpr FlutterViewId kFlutterImplicitViewId = 0ll; /** - * Listener for view resizing. + * Delegate for FlutterView. */ -@protocol FlutterViewReshapeListener +@protocol FlutterViewDelegate /** * Called when the view's backing store changes size. */ - (void)viewDidReshape:(nonnull NSView*)view; + +/** + * Called to determine whether the view should accept first responder status. + */ +- (BOOL)viewShouldAcceptFirstResponder:(nonnull NSView*)view; + @end /** @@ -43,7 +49,7 @@ constexpr FlutterViewId kFlutterImplicitViewId = 0ll; */ - (nullable instancetype)initWithMTLDevice:(nonnull id)device commandQueue:(nonnull id)commandQueue - reshapeListener:(nonnull id)reshapeListener + delegate:(nonnull id)delegate threadSynchronizer:(nonnull FlutterThreadSynchronizer*)threadSynchronizer viewId:(int64_t)viewId NS_DESIGNATED_INITIALIZER; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 89bbdb9153828..79607d0f759e8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -11,7 +11,7 @@ @interface FlutterView () { int64_t _viewId; - __weak id _reshapeListener; + __weak id _viewDelegate; FlutterThreadSynchronizer* _threadSynchronizer; FlutterSurfaceManager* _surfaceManager; } @@ -22,7 +22,7 @@ @implementation FlutterView - (instancetype)initWithMTLDevice:(id)device commandQueue:(id)commandQueue - reshapeListener:(id)reshapeListener + delegate:(id)delegate threadSynchronizer:(FlutterThreadSynchronizer*)threadSynchronizer viewId:(int64_t)viewId { self = [super initWithFrame:NSZeroRect]; @@ -31,7 +31,7 @@ - (instancetype)initWithMTLDevice:(id)device [self setBackgroundColor:[NSColor blackColor]]; [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize]; _viewId = viewId; - _reshapeListener = reshapeListener; + _viewDelegate = delegate; _threadSynchronizer = threadSynchronizer; _surfaceManager = [[FlutterSurfaceManager alloc] initWithDevice:device commandQueue:commandQueue @@ -54,7 +54,7 @@ - (void)reshaped { [_threadSynchronizer beginResizeForView:_viewId size:scaledSize notify:^{ - [_reshapeListener viewDidReshape:self]; + [_viewDelegate viewDidReshape:self]; }]; } @@ -89,7 +89,9 @@ - (BOOL)acceptsFirstMouse:(NSEvent*)event { } - (BOOL)acceptsFirstResponder { - return YES; + // This is to ensure that FlutterView does not take first responder status from TextInputPlugin + // on mouse clicks. + return [_viewDelegate viewShouldAcceptFirstResponder:self]; } - (void)cursorUpdate:(NSEvent*)event { @@ -104,7 +106,7 @@ - (void)cursorUpdate:(NSEvent*)event { - (void)viewDidChangeBackingProperties { [super viewDidChangeBackingProperties]; // Force redraw - [_reshapeListener viewDidReshape:self]; + [_viewDelegate viewDidReshape:self]; } - (BOOL)layer:(CALayer*)layer diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 62d81dbaa43bd..3ad17b76cbea4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -164,14 +164,12 @@ @interface FlutterViewWrapper : NSView - (void)setBackgroundColor:(NSColor*)color; -- (BOOL)performKeyEquivalent:(NSEvent*)event; - @end /** * Private interface declaration for FlutterViewController. */ -@interface FlutterViewController () +@interface FlutterViewController () /** * The tracking area used to generate hover events, if enabled. @@ -242,37 +240,6 @@ - (void)onKeyboardLayoutChanged; @end -#pragma mark - NSEvent (KeyEquivalentMarker) protocol - -@interface NSEvent (KeyEquivalentMarker) - -// Internally marks that the event was received through performKeyEquivalent:. -// When text editing is active, keyboard events that have modifier keys pressed -// are received through performKeyEquivalent: instead of keyDown:. If such event -// is passed to TextInputContext but doesn't result in a text editing action it -// needs to be forwarded by FlutterKeyboardManager to the next responder. -- (void)markAsKeyEquivalent; - -// Returns YES if the event is marked as a key equivalent. -- (BOOL)isKeyEquivalent; - -@end - -@implementation NSEvent (KeyEquivalentMarker) - -// This field doesn't need a value because only its address is used as a unique identifier. -static char markerKey; - -- (void)markAsKeyEquivalent { - objc_setAssociatedObject(self, &markerKey, @true, OBJC_ASSOCIATION_RETAIN); -} - -- (BOOL)isKeyEquivalent { - return [objc_getAssociatedObject(self, &markerKey) boolValue] == YES; -} - -@end - #pragma mark - Private dependant functions namespace { @@ -312,19 +279,15 @@ - (void)setBackgroundColor:(NSColor*)color { } - (BOOL)performKeyEquivalent:(NSEvent*)event { - if ([_controller isDispatchingKeyEvent:event]) { - // When NSWindow is nextResponder, keyboard manager will send to it - // unhandled events (through [NSWindow keyDown:]). If event has both - // control and cmd modifiers set (i.e. cmd+control+space - emoji picker) - // NSWindow will then send this event as performKeyEquivalent: to first - // responder, which might be FlutterTextInputPlugin. If that's the case, the - // plugin must not handle the event, otherwise the emoji picker would not - // work (due to first responder returning YES from performKeyEquivalent:) - // and there would be an infinite loop, because FlutterViewController will - // send the event back to [keyboardManager handleEvent:]. - return NO; + // Do not intercept the event if flutterView is not first responder, otherwise this would + // interfere with TextInputPlugin, which also handles key equivalents. + // + // Also do not intercept the event if key equivalent is a product of an event being + // redispatched by the TextInputPlugin, in which case it needs to bubble up so that menus + // can handle key equivalents. + if (self.window.firstResponder != _flutterView || [_controller isDispatchingKeyEvent:event]) { + return [super performKeyEquivalent:event]; } - [event markAsKeyEquivalent]; [_flutterView keyDown:event]; return YES; } @@ -400,7 +363,7 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) @"In unit tests, this is likely because either the FlutterViewController or " @"the FlutterEngine is mocked. Please subclass these classes instead.", controller.engine, controller.viewId); - controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; + controller->_mouseTrackingMode = kFlutterMouseTrackingModeInKeyWindow; controller->_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:controller]; [controller initializeKeyboard]; [controller notifySemanticsEnabledChanged]; @@ -642,17 +605,17 @@ - (void)configureTrackingArea { // the view is actually loaded. return; } - if (_mouseTrackingMode != FlutterMouseTrackingModeNone && self.flutterView) { + if (_mouseTrackingMode != kFlutterMouseTrackingModeNone && self.flutterView) { NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingEnabledDuringMouseDrag; switch (_mouseTrackingMode) { - case FlutterMouseTrackingModeInKeyWindow: + case kFlutterMouseTrackingModeInKeyWindow: options |= NSTrackingActiveInKeyWindow; break; - case FlutterMouseTrackingModeInActiveApp: + case kFlutterMouseTrackingModeInActiveApp: options |= NSTrackingActiveInActiveApp; break; - case FlutterMouseTrackingModeAlways: + case kFlutterMouseTrackingModeAlways: options |= NSTrackingActiveAlways; break; default: @@ -868,7 +831,7 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device commandQueue:(id)commandQueue { return [[FlutterView alloc] initWithMTLDevice:device commandQueue:commandQueue - reshapeListener:self + delegate:self threadSynchronizer:_threadSynchronizer viewId:_viewId]; } @@ -888,15 +851,24 @@ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; } -#pragma mark - FlutterViewReshapeListener +#pragma mark - FlutterViewDelegate /** * Responds to view reshape by notifying the engine of the change in dimensions. */ - (void)viewDidReshape:(NSView*)view { + FML_DCHECK(view == _flutterView); [_engine updateWindowMetricsForViewController:self]; } +- (BOOL)viewShouldAcceptFirstResponder:(NSView*)view { + FML_DCHECK(view == _flutterView); + // Only allow FlutterView to become first responder if TextInputPlugin is + // not active. Otherwise a mouse event inside FlutterView would cause the + // TextInputPlugin to lose first responder status. + return !_textInputPlugin.isFirstResponder; +} + #pragma mark - FlutterPluginRegistry - (id)registrarForPlugin:(NSString*)pluginName { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index 1628aa004ef4d..e5f5befd2ff2a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm @@ -38,6 +38,42 @@ - (void)dealloc { } @end +/// Responder wrapper that forwards key events to another responder. This is a necessary middle step +/// for mocking responder because when setting the responder to controller AppKit will access ivars +/// of the objects, which means it must extend NSResponder instead of just implementing the +/// selectors. +@interface FlutterResponderWrapper : NSResponder { + NSResponder* _responder; +} +@end + +@implementation FlutterResponderWrapper + +- (instancetype)initWithResponder:(NSResponder*)responder { + if (self = [super init]) { + _responder = responder; + } + return self; +} + +- (void)keyDown:(NSEvent*)event { + [_responder keyDown:event]; +} + +- (void)keyUp:(NSEvent*)event { + [_responder keyUp:event]; +} + +- (BOOL)performKeyEquivalent:(NSEvent*)event { + return [_responder performKeyEquivalent:event]; +} + +- (void)flagsChanged:(NSEvent*)event { + [_responder flagsChanged:event]; +} + +@end + // A FlutterViewController subclass for testing that mouseDown/mouseUp get called when // mouse events are sent to the associated view. @interface MouseEventFlutterViewController : FlutterViewController @@ -60,6 +96,7 @@ - (bool)testKeyEventsAreSentToFramework; - (bool)testKeyEventsArePropagatedIfNotHandled; - (bool)testKeyEventsAreNotPropagatedIfHandled; - (bool)testCtrlTabKeyEventIsPropagated; +- (bool)testKeyEquivalentIsPassedToTextInputPlugin; - (bool)testFlagsChangedEventsArePropagatedIfNotHandled; - (bool)testKeyboardIsRestartedOnEngineRestart; - (bool)testTrackpadGesturesAreSentToFramework; @@ -195,8 +232,8 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification, initWithAssetsPath:fixtures ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; - viewController.mouseTrackingMode = FlutterMouseTrackingModeInActiveApp; - ASSERT_EQ(viewController.mouseTrackingMode, FlutterMouseTrackingModeInActiveApp); + viewController.mouseTrackingMode = kFlutterMouseTrackingModeInActiveApp; + ASSERT_EQ(viewController.mouseTrackingMode, kFlutterMouseTrackingModeInActiveApp); } TEST(FlutterViewControllerTest, TestKeyEventsAreSentToFramework) { @@ -215,6 +252,10 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification, ASSERT_TRUE([[FlutterViewControllerTestObjC alloc] testCtrlTabKeyEventIsPropagated]); } +TEST(FlutterViewControllerTest, TestKeyEquivalentIsPassedToTextInputPlugin) { + ASSERT_TRUE([[FlutterViewControllerTestObjC alloc] testKeyEquivalentIsPassedToTextInputPlugin]); +} + TEST(FlutterViewControllerTest, TestFlagsChangedEventsArePropagatedIfNotHandled) { ASSERT_TRUE( [[FlutterViewControllerTestObjC alloc] testFlagsChangedEventsArePropagatedIfNotHandled]); @@ -331,6 +372,64 @@ - (bool)testCtrlTabKeyEventIsPropagated { const uint64_t kPhysicalKeyTab = 0x7002b; [viewController viewWillAppear]; // Initializes the event channel. + // Creates a NSWindow so that FlutterView view can be first responder. + NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + window.contentView = viewController.view; + [window makeFirstResponder:viewController.flutterView]; + [viewController.view performKeyEquivalent:event]; + + EXPECT_TRUE(called); + EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown); + EXPECT_EQ(last_event.physical, kPhysicalKeyTab); + return true; +} + +- (bool)testKeyEquivalentIsPassedToTextInputPlugin { + id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + __block bool called = false; + __block FlutterKeyEvent last_event; + OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {} + callback:nil + userData:nil]) + .andDo((^(NSInvocation* invocation) { + FlutterKeyEvent* event; + [invocation getArgument:&event atIndex:2]; + called = true; + last_event = *event; + })); + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock + nibName:@"" + bundle:nil]; + // Ctrl+tab + NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyDown + location:NSZeroPoint + modifierFlags:0x40101 + timestamp:0 + windowNumber:0 + context:nil + characters:@"" + charactersIgnoringModifiers:@"" + isARepeat:NO + keyCode:48]; + const uint64_t kPhysicalKeyTab = 0x7002b; + + [viewController viewWillAppear]; // Initializes the event channel. + + NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + window.contentView = viewController.view; + + [viewController.view addSubview:viewController.textInputPlugin]; + + // Make the textInputPlugin first responder. This should still result in + // view controller reporting the key event. + [window makeFirstResponder:viewController.textInputPlugin]; + [viewController.view performKeyEquivalent:event]; EXPECT_TRUE(called); @@ -354,7 +453,8 @@ - (bool)testKeyEventsArePropagatedIfNotHandled { nibName:@"" bundle:nil]; id responderMock = flutter::testing::mockResponder(); - viewController.nextResponder = responderMock; + id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock]; + viewController.nextResponder = responderWrapper; NSDictionary* expectedEvent = @{ @"keymap" : @"macos", @"type" : @"keydown", @@ -430,7 +530,8 @@ - (bool)testFlagsChangedEventsArePropagatedIfNotHandled { nibName:@"" bundle:nil]; id responderMock = flutter::testing::mockResponder(); - viewController.nextResponder = responderMock; + id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock]; + viewController.nextResponder = responderWrapper; NSDictionary* expectedEvent = @{ @"keymap" : @"macos", @"type" : @"keydown", @@ -483,7 +584,8 @@ - (bool)testKeyEventsAreNotPropagatedIfHandled { nibName:@"" bundle:nil]; id responderMock = flutter::testing::mockResponder(); - viewController.nextResponder = responderMock; + id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock]; + viewController.nextResponder = responderWrapper; NSDictionary* expectedEvent = @{ @"keymap" : @"macos", @"type" : @"keydown", @@ -762,7 +864,7 @@ - (bool)testTrackpadGesturesAreSentToFramework { CGEventRef cgEventDiscreteShift = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0); CGEventSetType(cgEventDiscreteShift, kCGEventScrollWheel); - CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift); + CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift | flutter::kModifierFlagShiftLeft); CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventIsContinuous, 0); CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis2, 0); // scroll_delta_x @@ -957,13 +1059,17 @@ - (bool)testMouseDownUpEventsSentToNextResponder { - (bool)testModifierKeysAreSynthesizedOnMouseMove { id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [engineMock binaryMessenger]) + .andReturn(binaryMessengerMock); + // Need to return a real renderer to allow view controller to load. FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock]; OCMStub([engineMock renderer]).andReturn(renderer_); // Capture calls to sendKeyEvent - __block NSMutableArray* events = - [[NSMutableArray alloc] init]; + __block NSMutableArray* events = [NSMutableArray array]; OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {} callback:nil userData:nil]) @@ -973,6 +1079,17 @@ - (bool)testModifierKeysAreSynthesizedOnMouseMove { [events addObject:[[KeyEventWrapper alloc] initWithEvent:event]]; })); + __block NSMutableArray* channelEvents = [NSMutableArray array]; + OCMStub([binaryMessengerMock sendOnChannel:@"flutter/keyevent" + message:[OCMArg any] + binaryReply:[OCMArg any]]) + .andDo((^(NSInvocation* invocation) { + NSData* data; + [invocation getArgument:&data atIndex:3]; + id event = [[FlutterJSONMessageCodec sharedInstance] decode:data]; + [channelEvents addObject:event]; + })); + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock nibName:@"" bundle:nil]; @@ -987,12 +1104,27 @@ - (bool)testModifierKeysAreSynthesizedOnMouseMove { // For each modifier key, check that key events are synthesized. for (NSNumber* keyCode in flutter::keyCodeToModifierFlag) { FlutterKeyEvent* event; + NSDictionary* channelEvent; NSNumber* logicalKey; NSNumber* physicalKey; - NSNumber* flag = flutter::keyCodeToModifierFlag[keyCode]; + NSEventModifierFlags flag = [flutter::keyCodeToModifierFlag[keyCode] unsignedLongValue]; + + // Cocoa event always contain combined flags. + if (flag & (flutter::kModifierFlagShiftLeft | flutter::kModifierFlagShiftRight)) { + flag |= NSEventModifierFlagShift; + } + if (flag & (flutter::kModifierFlagControlLeft | flutter::kModifierFlagControlRight)) { + flag |= NSEventModifierFlagControl; + } + if (flag & (flutter::kModifierFlagAltLeft | flutter::kModifierFlagAltRight)) { + flag |= NSEventModifierFlagOption; + } + if (flag & (flutter::kModifierFlagMetaLeft | flutter::kModifierFlagMetaRight)) { + flag |= NSEventModifierFlagCommand; + } // Should synthesize down event. - NSEvent* mouseEvent = flutter::testing::CreateMouseEvent([flag unsignedLongValue]); + NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(flag); [viewController mouseMoved:mouseEvent]; EXPECT_EQ([events count], 1u); event = events[0].data; @@ -1003,6 +1135,11 @@ - (bool)testModifierKeysAreSynthesizedOnMouseMove { EXPECT_EQ(event->physical, physicalKey.unsignedLongLongValue); EXPECT_EQ(event->synthesized, true); + channelEvent = channelEvents[0]; + EXPECT_TRUE([channelEvent[@"type"] isEqual:@"keydown"]); + EXPECT_TRUE([channelEvent[@"keyCode"] isEqual:keyCode]); + EXPECT_TRUE([channelEvent[@"modifiers"] isEqual:@(flag)]); + // Should synthesize up event. mouseEvent = flutter::testing::CreateMouseEvent(0x00); [viewController mouseMoved:mouseEvent]; @@ -1015,7 +1152,13 @@ - (bool)testModifierKeysAreSynthesizedOnMouseMove { EXPECT_EQ(event->physical, physicalKey.unsignedLongLongValue); EXPECT_EQ(event->synthesized, true); + channelEvent = channelEvents[1]; + EXPECT_TRUE([channelEvent[@"type"] isEqual:@"keyup"]); + EXPECT_TRUE([channelEvent[@"keyCode"] isEqual:keyCode]); + EXPECT_TRUE([channelEvent[@"modifiers"] isEqual:@(0)]); + [events removeAllObjects]; + [channelEvents removeAllObjects]; }; return true; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm index 0d6e7cb550400..65620a53824e4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm @@ -10,25 +10,29 @@ constexpr int64_t kImplicitViewId = 0ll; -@interface TestReshapeListener : NSObject +@interface TestFlutterViewDelegate : NSObject @end -@implementation TestReshapeListener +@implementation TestFlutterViewDelegate - (void)viewDidReshape:(nonnull NSView*)view { } +- (BOOL)viewShouldAcceptFirstResponder:(NSView*)view { + return YES; +} + @end TEST(FlutterView, ShouldInheritContentsScaleReturnsYes) { id device = MTLCreateSystemDefaultDevice(); id queue = [device newCommandQueue]; - TestReshapeListener* listener = [[TestReshapeListener alloc] init]; + TestFlutterViewDelegate* delegate = [[TestFlutterViewDelegate alloc] init]; FlutterThreadSynchronizer* threadSynchronizer = [[FlutterThreadSynchronizer alloc] init]; FlutterView* view = [[FlutterView alloc] initWithMTLDevice:device commandQueue:queue - reshapeListener:listener + delegate:delegate threadSynchronizer:threadSynchronizer viewId:kImplicitViewId]; EXPECT_EQ([view layer:view.layer shouldInheritContentsScale:3.0 fromWindow:view.window], YES); diff --git a/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm b/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm index 52229d518b0ab..6fc5c71c78d08 100644 --- a/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm +++ b/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm @@ -245,7 +245,7 @@ const uint64_t kCapsLockPhysicalKey = 0x00070039; const uint64_t kCapsLockLogicalKey = 0x100000104; -const std::vector layoutGoals = { +const std::vector kLayoutGoals = { LayoutGoal{0x31, 0x20, false}, // Space LayoutGoal{0x27, 0x22, false}, // Quote LayoutGoal{0x2b, 0x2c, false}, // Comma diff --git a/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h b/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h index 9a7e38659ff4e..15fe906c1bc03 100644 --- a/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h @@ -104,6 +104,6 @@ typedef struct { * All keys that Flutter wants to derive layout for, and guides on how to derive * them. */ -extern const std::vector layoutGoals; +extern const std::vector kLayoutGoals; } // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h b/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h new file mode 100644 index 0000000000000..b4669fbde7e3f --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h @@ -0,0 +1,10 @@ +// 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. + +#import + +@interface NSView (ClipsToBounds) +// This property is available since macOS 10.9 but only declared in macOS 14 SDK. +@property BOOL clipsToBounds API_AVAILABLE(macos(10.9)); +@end diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 90f3d1d9c66ca..829beb8d925bf 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -90,6 +90,7 @@ template("embedder_source_set") { "embedder_thread_host.cc", "embedder_thread_host.h", "pixel_formats.cc", + "pixel_formats.h", "platform_view_embedder.cc", "platform_view_embedder.h", "vsync_waiter_embedder.cc", diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index af32d9ffa3f1c..972390e7c3e8e 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -20,6 +20,7 @@ #include "third_party/dart/runtime/bin/elf_loader.h" #include "third_party/dart/runtime/include/dart_native_api.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" @@ -362,8 +363,7 @@ InferOpenGLPlatformViewCreationCallback( if (!populate_existing_damage) { return flutter::GLFBOInfo{ .fbo_id = static_cast(id), - .partial_repaint_enabled = false, - .existing_damage = SkIRect::MakeEmpty(), + .existing_damage = std::nullopt, }; } @@ -371,29 +371,22 @@ InferOpenGLPlatformViewCreationCallback( FlutterDamage existing_damage; populate_existing_damage(user_data, id, &existing_damage); - bool partial_repaint_enabled = true; - SkIRect existing_damage_rect; + std::optional existing_damage_rect = std::nullopt; // Verify that at least one damage rectangle was provided. if (existing_damage.num_rects <= 0 || existing_damage.damage == nullptr) { FML_LOG(INFO) << "No damage was provided. Forcing full repaint."; - existing_damage_rect = SkIRect::MakeEmpty(); - partial_repaint_enabled = false; - } else if (existing_damage.num_rects > 1) { - // Log message notifying users that multi-damage is not yet available in - // case they try to make use of it. - FML_LOG(INFO) << "Damage with multiple rectangles not yet supported. " - "Repainting the whole frame."; - existing_damage_rect = SkIRect::MakeEmpty(); - partial_repaint_enabled = false; } else { - existing_damage_rect = FlutterRectToSkIRect(*(existing_damage.damage)); + existing_damage_rect = SkIRect::MakeEmpty(); + for (size_t i = 0; i < existing_damage.num_rects; i++) { + existing_damage_rect->join( + FlutterRectToSkIRect(existing_damage.damage[i])); + } } // Pass the information about this FBO to the rendering backend. return flutter::GLFBOInfo{ .fbo_id = static_cast(id), - .partial_repaint_enabled = partial_repaint_enabled, .existing_damage = existing_damage_rect, }; }; @@ -924,10 +917,10 @@ static sk_sp MakeSkSurfaceFromBackingStore( sk_cfp mtl_texture; mtl_texture.retain(metal->texture.texture); texture_info.fTexture = mtl_texture; - GrBackendTexture backend_texture(config.size.width, // - config.size.height, // - GrMipMapped::kNo, // - texture_info // + GrBackendTexture backend_texture(config.size.width, // + config.size.height, // + skgpu::Mipmapped::kNo, // + texture_info // ); SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry); diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 44b1fddf808fb..cd32348e10306 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -107,7 +107,7 @@ sk_sp EmbedderSurfaceGL::CreateResourceContext() const { auto callback = gl_dispatch_table_.gl_make_resource_current_callback; if (callback && callback()) { if (auto context = ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GetGLInterface())) { + GrBackendApi::kOpenGL, GetGLInterface())) { return context; } else { FML_LOG(ERROR) diff --git a/shell/platform/embedder/embedder_surface_gl_impeller.cc b/shell/platform/embedder/embedder_surface_gl_impeller.cc index 20a2a5184e133..c061c091a912b 100644 --- a/shell/platform/embedder/embedder_surface_gl_impeller.cc +++ b/shell/platform/embedder/embedder_surface_gl_impeller.cc @@ -65,11 +65,12 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( gl_dispatch_table_.gl_make_current_callback(); std::vector> shader_mappings = { - std::make_shared(impeller_entity_shaders_gles_data, - impeller_entity_shaders_gles_length), + std::make_shared( + impeller_entity_shaders_gles_data, + impeller_entity_shaders_gles_length), #if IMPELLER_ENABLE_3D - std::make_shared(impeller_scene_shaders_gles_data, - impeller_scene_shaders_gles_length), + std::make_shared( + impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), #endif // IMPELLER_ENABLE_3D }; auto gl = std::make_unique( @@ -78,21 +79,21 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( return; } - impeller_context_ = - impeller::ContextGLES::Create(std::move(gl), shader_mappings); + impeller_context_ = impeller::ContextGLES::Create( + std::move(gl), shader_mappings, /*enable_gpu_tracing=*/false); if (!impeller_context_) { FML_LOG(ERROR) << "Could not create Impeller context."; return; } - worker_->SetReactionsAllowedOnCurrentThread(true); auto worker_id = impeller_context_->AddReactorWorker(worker_); if (!worker_id.has_value()) { FML_LOG(ERROR) << "Could not add reactor worker."; return; } + gl_dispatch_table_.gl_clear_current_callback(); FML_LOG(ERROR) << "Using the Impeller rendering backend (OpenGL)."; valid_ = true; } diff --git a/shell/platform/embedder/embedder_surface_vulkan.cc b/shell/platform/embedder/embedder_surface_vulkan.cc index d7191770025de..1ab4ca92942c3 100644 --- a/shell/platform/embedder/embedder_surface_vulkan.cc +++ b/shell/platform/embedder/embedder_surface_vulkan.cc @@ -14,6 +14,7 @@ #include "include/gpu/GrDirectContext.h" #include "include/gpu/vk/GrVkBackendContext.h" #include "include/gpu/vk/GrVkExtensions.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" namespace flutter { @@ -171,7 +172,7 @@ sk_sp EmbedderSurfaceVulkan::CreateGrContext( GrContextOptions options = MakeDefaultContextOptions(context_type, GrBackendApi::kVulkan); options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; - return GrDirectContext::MakeVulkan(backend_context, options); + return GrDirectContexts::MakeVulkan(backend_context, options); } } // namespace flutter diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index c48de1daaab81..81cc224868c11 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -1298,3 +1298,25 @@ void channel_listener_response() async { }); signalNativeTest(); } + +@pragma('vm:entry-point') +void render_gradient_retained() { + OffsetEngineLayer? offsetLayer; // Retain the offset layer. + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { + Size size = Size(800.0, 600.0); + + SceneBuilder builder = SceneBuilder(); + + offsetLayer = builder.pushOffset(0.0, 0.0, oldLayer: offsetLayer); + + // display_list_layer will comparing the display_list + // no need to retain the picture + builder.addPicture( + Offset(0.0, 0.0), CreateGradientBox(size)); // gradient - flutter + + builder.pop(); + + PlatformDispatcher.instance.views.first.render(builder.build()); + }; + PlatformDispatcher.instance.scheduleFrame(); +} diff --git a/shell/platform/embedder/tests/embedder_gl_unittests.cc b/shell/platform/embedder/tests/embedder_gl_unittests.cc index d233f836d9a74..dfca7ff6fb8a0 100644 --- a/shell/platform/embedder/tests/embedder_gl_unittests.cc +++ b/shell/platform/embedder/tests/embedder_gl_unittests.cc @@ -3655,7 +3655,7 @@ TEST_F(EmbedderTest, EmbedderConfigBuilder builder(context); builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetDartEntrypoint("render_gradient"); + builder.SetDartEntrypoint("render_gradient_retained"); builder.GetRendererConfig().open_gl.populate_existing_damage = [](void* context, const intptr_t id, FlutterDamage* existing_damage) -> void { @@ -3668,7 +3668,8 @@ TEST_F(EmbedderTest, .SetGLPopulateExistingDamageCallback( [](const intptr_t id, FlutterDamage* existing_damage_ptr) { const size_t num_rects = 1; - FlutterRect existing_damage_rects[num_rects] = { + // The array must be valid after the callback returns. + static FlutterRect existing_damage_rects[num_rects] = { FlutterRect{0, 0, 800, 600}}; existing_damage_ptr->num_rects = num_rects; existing_damage_ptr->damage = existing_damage_rects; @@ -3677,9 +3678,11 @@ TEST_F(EmbedderTest, auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); + fml::AutoResetWaitableEvent latch; + // First frame should be entirely rerendered. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { + [&](FlutterPresentInfo present_info) { const size_t num_rects = 1; ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); ASSERT_EQ(present_info.frame_damage.damage->left, 0); @@ -3692,6 +3695,8 @@ TEST_F(EmbedderTest, ASSERT_EQ(present_info.buffer_damage.damage->top, 0); ASSERT_EQ(present_info.buffer_damage.damage->right, 800); ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); }); // Send a window metrics events so frames may be scheduled. @@ -3702,12 +3707,13 @@ TEST_F(EmbedderTest, event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + latch.Wait(); // Because it's the same as the first frame, the second frame damage should // be empty but, because there was a full existing buffer damage, the buffer // damage should be the entire screen. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { + [&](FlutterPresentInfo present_info) { const size_t num_rects = 1; ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); ASSERT_EQ(present_info.frame_damage.damage->left, 0); @@ -3720,10 +3726,13 @@ TEST_F(EmbedderTest, ASSERT_EQ(present_info.buffer_damage.damage->top, 0); ASSERT_EQ(present_info.buffer_damage.damage->right, 800); ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); }); ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + latch.Wait(); } TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { @@ -3731,7 +3740,7 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { EmbedderConfigBuilder builder(context); builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetDartEntrypoint("render_gradient"); + builder.SetDartEntrypoint("render_gradient_retained"); builder.GetRendererConfig().open_gl.populate_existing_damage = [](void* context, const intptr_t id, FlutterDamage* existing_damage) -> void { @@ -3744,7 +3753,8 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { .SetGLPopulateExistingDamageCallback( [](const intptr_t id, FlutterDamage* existing_damage_ptr) { const size_t num_rects = 1; - FlutterRect existing_damage_rects[num_rects] = { + // The array must be valid after the callback returns. + static FlutterRect existing_damage_rects[num_rects] = { FlutterRect{0, 0, 0, 0}}; existing_damage_ptr->num_rects = num_rects; existing_damage_ptr->damage = existing_damage_rects; @@ -3753,9 +3763,11 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); + fml::AutoResetWaitableEvent latch; + // First frame should be entirely rerendered. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { + [&](FlutterPresentInfo present_info) { const size_t num_rects = 1; ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); ASSERT_EQ(present_info.frame_damage.damage->left, 0); @@ -3768,6 +3780,8 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { ASSERT_EQ(present_info.buffer_damage.damage->top, 0); ASSERT_EQ(present_info.buffer_damage.damage->right, 800); ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); }); // Send a window metrics events so frames may be scheduled. @@ -3778,11 +3792,12 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + latch.Wait(); // Because it's the same as the first frame, the second frame should not be // rerendered assuming there is no existing damage. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { + [&](FlutterPresentInfo present_info) { const size_t num_rects = 1; ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); ASSERT_EQ(present_info.frame_damage.damage->left, 0); @@ -3795,10 +3810,13 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { ASSERT_EQ(present_info.buffer_damage.damage->top, 0); ASSERT_EQ(present_info.buffer_damage.damage->right, 0); ASSERT_EQ(present_info.buffer_damage.damage->bottom, 0); + + latch.Signal(); }); ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + latch.Wait(); } TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { @@ -3806,7 +3824,7 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { EmbedderConfigBuilder builder(context); builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetDartEntrypoint("render_gradient"); + builder.SetDartEntrypoint("render_gradient_retained"); builder.GetRendererConfig().open_gl.populate_existing_damage = [](void* context, const intptr_t id, FlutterDamage* existing_damage) -> void { @@ -3817,9 +3835,10 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { // Return existing damage as only part of the screen on purpose. static_cast(context) .SetGLPopulateExistingDamageCallback( - [](const intptr_t id, FlutterDamage* existing_damage_ptr) { + [&](const intptr_t id, FlutterDamage* existing_damage_ptr) { const size_t num_rects = 1; - FlutterRect existing_damage_rects[num_rects] = { + // The array must be valid after the callback returns. + static FlutterRect existing_damage_rects[num_rects] = { FlutterRect{200, 150, 400, 300}}; existing_damage_ptr->num_rects = num_rects; existing_damage_ptr->damage = existing_damage_rects; @@ -3828,9 +3847,11 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); + fml::AutoResetWaitableEvent latch; + // First frame should be entirely rerendered. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { + [&](FlutterPresentInfo present_info) { const size_t num_rects = 1; ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); ASSERT_EQ(present_info.frame_damage.damage->left, 0); @@ -3843,6 +3864,8 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { ASSERT_EQ(present_info.buffer_damage.damage->top, 0); ASSERT_EQ(present_info.buffer_damage.damage->right, 800); ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); }); // Send a window metrics events so frames may be scheduled. @@ -3853,12 +3876,13 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + latch.Wait(); // Because it's the same as the first frame, the second frame damage should be // empty but, because there was a partial existing damage, the buffer damage // should represent that partial damage area. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { + [&](FlutterPresentInfo present_info) { const size_t num_rects = 1; ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); ASSERT_EQ(present_info.frame_damage.damage->left, 0); @@ -3871,10 +3895,13 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { ASSERT_EQ(present_info.buffer_damage.damage->top, 150); ASSERT_EQ(present_info.buffer_damage.damage->right, 400); ASSERT_EQ(present_info.buffer_damage.damage->bottom, 300); + + latch.Signal(); }); ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + latch.Wait(); } TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) { @@ -3882,7 +3909,7 @@ TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) { EmbedderConfigBuilder builder(context); builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetDartEntrypoint("render_gradient"); + builder.SetDartEntrypoint("render_gradient_retained"); builder.GetRendererConfig().open_gl.populate_existing_damage = [](void* context, const intptr_t id, FlutterDamage* existing_damage) -> void { @@ -3900,6 +3927,8 @@ TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) { [window_fbo_id = window_fbo_id](intptr_t id, FlutterDamage* existing_damage) { ASSERT_EQ(id, window_fbo_id); + existing_damage->num_rects = 0; + existing_damage->damage = nullptr; }); // Send a window metrics events so frames may be scheduled. @@ -3917,7 +3946,7 @@ TEST_F(EmbedderTest, PopulateExistingDamageReceivesInvalidID) { EmbedderConfigBuilder builder(context); builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetDartEntrypoint("render_gradient"); + builder.SetDartEntrypoint("render_gradient_retained"); builder.GetRendererConfig().open_gl.populate_existing_damage = [](void* context, const intptr_t id, FlutterDamage* existing_damage) -> void { @@ -3946,6 +3975,8 @@ TEST_F(EmbedderTest, PopulateExistingDamageReceivesInvalidID) { [window_fbo_id = window_fbo_id](intptr_t id, FlutterDamage* existing_damage) { ASSERT_NE(id, window_fbo_id); + existing_damage->num_rects = 0; + existing_damage->damage = nullptr; }); // Send a window metrics events so frames may be scheduled. @@ -4510,24 +4541,162 @@ TEST_F(EmbedderTest, ExternalTextureGLRefreshedTooOften) { glFinish(); } -TEST_F(EmbedderTest, - PresentInfoReceivesNoDamageWhenPopulateExistingDamageIsUndefined) { +TEST_F( + EmbedderTest, + PresentInfoReceivesFullScreenDamageWhenPopulateExistingDamageIsNotProvided) { auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); EmbedderConfigBuilder builder(context); builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetDartEntrypoint("render_gradient"); + builder.SetDartEntrypoint("render_gradient_retained"); builder.GetRendererConfig().open_gl.populate_existing_damage = nullptr; auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); - // No damage should be passed. + fml::AutoResetWaitableEvent latch; + + // First frame should be entirely rerendered. + static_cast(context).SetGLPresentCallback( + [&](FlutterPresentInfo present_info) { + const size_t num_rects = 1; + ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); + ASSERT_EQ(present_info.frame_damage.damage->left, 0); + ASSERT_EQ(present_info.frame_damage.damage->top, 0); + ASSERT_EQ(present_info.frame_damage.damage->right, 800); + ASSERT_EQ(present_info.frame_damage.damage->bottom, 600); + + ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects); + ASSERT_EQ(present_info.buffer_damage.damage->left, 0); + ASSERT_EQ(present_info.buffer_damage.damage->top, 0); + ASSERT_EQ(present_info.buffer_damage.damage->right, 800); + ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); + }); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + latch.Wait(); + + // Since populate_existing_damage is not provided, the partial repaint + // functionality is actually disabled. So, the next frame should be entirely + // new frame. + static_cast(context).SetGLPresentCallback( + [&](FlutterPresentInfo present_info) { + const size_t num_rects = 1; + ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); + ASSERT_EQ(present_info.frame_damage.damage->left, 0); + ASSERT_EQ(present_info.frame_damage.damage->top, 0); + ASSERT_EQ(present_info.frame_damage.damage->right, 800); + ASSERT_EQ(present_info.frame_damage.damage->bottom, 600); + + ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects); + ASSERT_EQ(present_info.buffer_damage.damage->left, 0); + ASSERT_EQ(present_info.buffer_damage.damage->top, 0); + ASSERT_EQ(present_info.buffer_damage.damage->right, 800); + ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); + }); + + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + latch.Wait(); +} + +TEST_F(EmbedderTest, + PresentInfoReceivesJoinedDamageWhenExistingDamageContainsMultipleRects) { + auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetDartEntrypoint("render_gradient_retained"); + builder.GetRendererConfig().open_gl.populate_existing_damage = + [](void* context, const intptr_t id, + FlutterDamage* existing_damage) -> void { + return reinterpret_cast(context) + ->GLPopulateExistingDamage(id, existing_damage); + }; + + // Return existing damage as the entire screen on purpose. + static_cast(context) + .SetGLPopulateExistingDamageCallback( + [](const intptr_t id, FlutterDamage* existing_damage_ptr) { + const size_t num_rects = 2; + // The array must be valid after the callback returns. + static FlutterRect existing_damage_rects[num_rects] = { + FlutterRect{100, 150, 200, 250}, + FlutterRect{200, 250, 300, 350}, + }; + existing_damage_ptr->num_rects = num_rects; + existing_damage_ptr->damage = existing_damage_rects; + }); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + + fml::AutoResetWaitableEvent latch; + + // First frame should be entirely rerendered. static_cast(context).SetGLPresentCallback( - [](FlutterPresentInfo present_info) { - ASSERT_EQ(present_info.frame_damage.damage, nullptr); - ASSERT_EQ(present_info.buffer_damage.damage, nullptr); + [&](FlutterPresentInfo present_info) { + const size_t num_rects = 1; + ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); + ASSERT_EQ(present_info.frame_damage.damage->left, 0); + ASSERT_EQ(present_info.frame_damage.damage->top, 0); + ASSERT_EQ(present_info.frame_damage.damage->right, 800); + ASSERT_EQ(present_info.frame_damage.damage->bottom, 600); + + ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects); + ASSERT_EQ(present_info.buffer_damage.damage->left, 0); + ASSERT_EQ(present_info.buffer_damage.damage->top, 0); + ASSERT_EQ(present_info.buffer_damage.damage->right, 800); + ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); + + latch.Signal(); }); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + latch.Wait(); + + // Because it's the same as the first frame, the second frame damage should + // be empty but, because there was a full existing buffer damage, the buffer + // damage should be the entire screen. + static_cast(context).SetGLPresentCallback( + [&](FlutterPresentInfo present_info) { + const size_t num_rects = 1; + ASSERT_EQ(present_info.frame_damage.num_rects, num_rects); + ASSERT_EQ(present_info.frame_damage.damage->left, 0); + ASSERT_EQ(present_info.frame_damage.damage->top, 0); + ASSERT_EQ(present_info.frame_damage.damage->right, 0); + ASSERT_EQ(present_info.frame_damage.damage->bottom, 0); + + ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects); + ASSERT_EQ(present_info.buffer_damage.damage->left, 100); + ASSERT_EQ(present_info.buffer_damage.damage->top, 150); + ASSERT_EQ(present_info.buffer_damage.damage->right, 300); + ASSERT_EQ(present_info.buffer_damage.damage->bottom, 350); + + latch.Signal(); + }); + + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + latch.Wait(); } INSTANTIATE_TEST_SUITE_P( diff --git a/shell/platform/embedder/tests/embedder_metal_unittests.mm b/shell/platform/embedder/tests/embedder_metal_unittests.mm index f3666d9fee4c4..c23a6553a5360 100644 --- a/shell/platform/embedder/tests/embedder_metal_unittests.mm +++ b/shell/platform/embedder/tests/embedder_metal_unittests.mm @@ -21,6 +21,7 @@ #include "flutter/testing/testing.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" @@ -63,8 +64,8 @@ void* texture) { GrMtlTextureInfo info; info.fTexture.reset([(id)texture retain]); - GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), GrMipmapped::kNo, - info); + GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), + skgpu::Mipmapped::kNo, info); return SkSurfaces::WrapBackendTexture(skia_context.get(), backend_texture, kTopLeft_GrSurfaceOrigin, 1, kBGRA_8888_SkColorType, diff --git a/shell/platform/embedder/tests/embedder_test_backingstore_producer.cc b/shell/platform/embedder/tests/embedder_test_backingstore_producer.cc index eb74388dac16c..0c67e3dccea2d 100644 --- a/shell/platform/embedder/tests/embedder_test_backingstore_producer.cc +++ b/shell/platform/embedder/tests/embedder_test_backingstore_producer.cc @@ -11,6 +11,7 @@ #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" @@ -281,7 +282,7 @@ bool EmbedderTestBackingStoreProducer::CreateMTLTexture( GrMtlTextureInfo skia_texture_info; skia_texture_info.fTexture.reset(SkCFSafeRetain(texture_info.texture)); GrBackendTexture backend_texture(surface_size.width(), surface_size.height(), - GrMipmapped::kNo, skia_texture_info); + skgpu::Mipmapped::kNo, skia_texture_info); sk_sp surface = SkSurfaces::WrapBackendTexture( context_.get(), backend_texture, kTopLeft_GrSurfaceOrigin, 1, diff --git a/shell/platform/fuchsia/dart-pkg/fuchsia/pubspec.yaml b/shell/platform/fuchsia/dart-pkg/fuchsia/pubspec.yaml index 16fe4cdd83d71..85d01ce1515b9 100644 --- a/shell/platform/fuchsia/dart-pkg/fuchsia/pubspec.yaml +++ b/shell/platform/fuchsia/dart-pkg/fuchsia/pubspec.yaml @@ -3,4 +3,4 @@ # found in the LICENSE file. environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart index adfac800bc986..81a6300f25ddd 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart @@ -8,7 +8,7 @@ part of zircon; // ignore_for_file: public_member_api_docs @pragma('vm:entry-point') -class Handle extends NativeFieldWrapperClass1 { +base class Handle extends NativeFieldWrapperClass1 { // No public constructor - this can only be created from native code. @pragma('vm:entry-point') Handle._(); diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_disposition.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_disposition.dart index bd3fa0363a70f..a3ce43bbc6cd8 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_disposition.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_disposition.dart @@ -8,7 +8,7 @@ part of zircon; // ignore_for_file: public_member_api_docs @pragma('vm:entry-point') -class HandleDisposition extends NativeFieldWrapperClass1 { +base class HandleDisposition extends NativeFieldWrapperClass1 { @pragma('vm:entry-point') HandleDisposition(int operation, Handle handle, int type, int rights) { _constructor(operation, handle, type, rights); diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_waiter.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_waiter.dart index dba823a13bb47..49bc437a5049f 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_waiter.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_waiter.dart @@ -10,7 +10,7 @@ part of zircon; typedef AsyncWaitCallback = void Function(int status, int pending); @pragma('vm:entry-point') -class HandleWaiter extends NativeFieldWrapperClass1 { +base class HandleWaiter extends NativeFieldWrapperClass1 { // Private constructor. @pragma('vm:entry-point') HandleWaiter._(); diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart index 9f6cec0b02ea5..ccf1b693608ce 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart @@ -193,7 +193,7 @@ class MapResult extends _Result { } @pragma('vm:entry-point') -class System extends NativeFieldWrapperClass1 { +base class System extends NativeFieldWrapperClass1 { // No public constructor - this only has static methods. System._(); diff --git a/shell/platform/fuchsia/dart-pkg/zircon/pubspec.yaml b/shell/platform/fuchsia/dart-pkg/zircon/pubspec.yaml index 121b528580c14..c75e7f8ff6a4a 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/pubspec.yaml +++ b/shell/platform/fuchsia/dart-pkg/zircon/pubspec.yaml @@ -5,7 +5,7 @@ name: zircon environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Uncomment block for local testing diff --git a/shell/platform/fuchsia/dart-pkg/zircon_ffi/pubspec.yaml b/shell/platform/fuchsia/dart-pkg/zircon_ffi/pubspec.yaml index 5e3507cef3c23..e30ca0196f5a7 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon_ffi/pubspec.yaml +++ b/shell/platform/fuchsia/dart-pkg/zircon_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: zircon_ffi environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: ffi: ^1.0.0 diff --git a/shell/platform/fuchsia/dart/BUILD.gn b/shell/platform/fuchsia/dart/BUILD.gn index 995f37bb60c4f..7ef6b7e661c6f 100644 --- a/shell/platform/fuchsia/dart/BUILD.gn +++ b/shell/platform/fuchsia/dart/BUILD.gn @@ -62,7 +62,7 @@ dart_library("async_helper") { package_root = "//third_party/dart/pkg/async_helper" package_name = "async_helper" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [] @@ -76,7 +76,7 @@ dart_library("meta") { package_root = "//third_party/dart/pkg/meta" package_name = "meta" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [] @@ -91,7 +91,7 @@ dart_library("expect") { package_root = "//third_party/dart/pkg/expect" package_name = "expect" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [ ":meta" ] @@ -106,7 +106,7 @@ dart_library("litetest") { package_root = "//flutter/testing/litetest" package_name = "litetest" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" source_dir = "lib" @@ -128,7 +128,7 @@ dart_library("args") { package_root = "//third_party/dart/third_party/pkg/args" package_name = "args" - language_version = "2.18" + pubspec = "$package_root/pubspec.yaml" deps = [] @@ -152,7 +152,7 @@ dart_library("collection") { package_root = "//third_party/dart/third_party/pkg/collection" package_name = "collection" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [] @@ -192,7 +192,7 @@ dart_library("logging") { package_root = "//third_party/dart/third_party/pkg/logging" package_name = "logging" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [] @@ -208,7 +208,7 @@ dart_library("path") { package_root = "//third_party/dart/third_party/pkg/path" package_name = "path" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [] @@ -233,7 +233,7 @@ dart_library("stack_trace") { package_root = "//third_party/dart/third_party/pkg/stack_trace" package_name = "stack_trace" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [ ":path" ] @@ -255,7 +255,7 @@ dart_library("matcher") { package_root = "//third_party/dart/third_party/pkg/matcher" package_name = "matcher" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [ ":stack_trace" ] @@ -299,8 +299,7 @@ dart_library("quiver") { package_root = "//third_party/pkg/quiver" package_name = "quiver" - # The current version of this library is not null safe - language_version = "2.17" + pubspec = "$package_root/pubspec.yaml" deps = [ ":matcher", @@ -368,7 +367,7 @@ dart_library("vector_math") { package_root = "//third_party/pkg/vector_math" package_name = "vector_math" - language_version = "2.12" + pubspec = "$package_root/pubspec.yaml" deps = [] diff --git a/shell/platform/fuchsia/dart_runner/dart_runner.cc b/shell/platform/fuchsia/dart_runner/dart_runner.cc index 508473e9f3982..012badf098820 100644 --- a/shell/platform/fuchsia/dart_runner/dart_runner.cc +++ b/shell/platform/fuchsia/dart_runner/dart_runner.cc @@ -41,7 +41,7 @@ namespace { const char* kDartVMArgs[] = { // clang-format off - "--systrace_timeline", + "--timeline_recorder=systrace", "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", #if defined(AOT_RUNTIME) diff --git a/shell/platform/fuchsia/dart_runner/embedder/pubspec.yaml b/shell/platform/fuchsia/dart_runner/embedder/pubspec.yaml index 0bda0be6f7c95..d1ccbe7700dc5 100644 --- a/shell/platform/fuchsia/dart_runner/embedder/pubspec.yaml +++ b/shell/platform/fuchsia/dart_runner/embedder/pubspec.yaml @@ -6,5 +6,5 @@ # template in BUILD.gn. environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/dart_runner/fidl/echo.fidl b/shell/platform/fuchsia/dart_runner/fidl/echo.fidl index eb27c91bad15b..6c39ac59f7db2 100644 --- a/shell/platform/fuchsia/dart_runner/fidl/echo.fidl +++ b/shell/platform/fuchsia/dart_runner/fidl/echo.fidl @@ -7,9 +7,9 @@ library dart.test; const MAX_STRING_LENGTH uint64 = 32; @discoverable -protocol Echo { +closed protocol Echo { /// Returns the input. - EchoString(struct { + strict EchoString(struct { value string:; }) -> (struct { response string:; diff --git a/shell/platform/fuchsia/dart_runner/vmservice/pubspec.yaml b/shell/platform/fuchsia/dart_runner/vmservice/pubspec.yaml index 09da15ac00b35..df2ce1ab10dbf 100644 --- a/shell/platform/fuchsia/dart_runner/vmservice/pubspec.yaml +++ b/shell/platform/fuchsia/dart_runner/vmservice/pubspec.yaml @@ -3,5 +3,5 @@ # found in the LICENSE file. environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/kernel/pubspec.yaml b/shell/platform/fuchsia/flutter/kernel/pubspec.yaml index 16fe4cdd83d71..85d01ce1515b9 100644 --- a/shell/platform/fuchsia/flutter/kernel/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/kernel/pubspec.yaml @@ -3,4 +3,4 @@ # found in the LICENSE file. environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/pubspec.yaml index fc1d582237cd2..89f565db2969b 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/pubspec.yaml @@ -5,4 +5,4 @@ name: child_view2 environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/pubspec.yaml index 201cf3fa56906..1ee7f0c30401c 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/pubspec.yaml @@ -5,4 +5,4 @@ name: parent-view2 environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/pubspec.yaml index c3196484ca75f..40e52bb3bbc54 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/pubspec.yaml @@ -5,4 +5,4 @@ name: mouse-input-view environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/pubspec.yaml index f15d93799943b..21d9b7d9c0917 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/pubspec.yaml @@ -5,4 +5,4 @@ name: text-input-view environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/pubspec.yaml index 5051384ec5bea..e929bdaaa7518 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/pubspec.yaml @@ -5,4 +5,4 @@ name: embedding-flutter-view environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml index 3e8e25f294cbf..7fc83f8c32fd0 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml @@ -5,4 +5,4 @@ name: touch-input-view environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/shell/platform/fuchsia/flutter/vulkan_surface.cc b/shell/platform/fuchsia/flutter/vulkan_surface.cc index dc640c84f1f6f..69bb592edebce 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface.cc @@ -508,7 +508,7 @@ void VulkanSurface::Reset() { VkFence fence = command_buffer_fence_; if (command_buffer_) { - VK_CALL_LOG_ERROR(vulkan_provider_.vk().WaitForFences( + VK_CALL_LOG_FATAL(vulkan_provider_.vk().WaitForFences( vulkan_provider_.vk_device(), 1, &fence, VK_TRUE, UINT64_MAX)); command_buffer_.reset(); } diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc b/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc index 24394e4cfc0fe..538827ef76cb5 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc @@ -20,6 +20,7 @@ #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h" #include "third_party/skia/include/gpu/vk/GrVkExtensions.h" #include "third_party/skia/include/gpu/vk/GrVkTypes.h" @@ -153,7 +154,7 @@ bool VulkanSurfaceProducer::Initialize() { GrContextOptions options; options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; - context_ = GrDirectContext::MakeVulkan(backend_context, options); + context_ = GrDirectContexts::MakeVulkan(backend_context, options); if (context_ == nullptr) { FML_LOG(ERROR) @@ -234,22 +235,21 @@ bool VulkanSurfaceProducer::TransitionSurfacesToExternal( } VkImageMemoryBarrier image_barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dstAccessMask = 0, - .oldLayout = imageInfo.fImageLayout, + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = 0, + .oldLayout = imageInfo.fImageLayout, // Understand why this is causing issues on Intel. TODO(fxb/53449) #if defined(__aarch64__) - .newLayout = imageInfo.fImageLayout, + .newLayout = imageInfo.fImageLayout, #else - .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, #endif - .srcQueueFamilyIndex = 0, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR, - .image = vk_surface->GetVkImage(), - .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} - }; + .srcQueueFamilyIndex = 0, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR, + .image = vk_surface->GetVkImage(), + .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; if (!command_buffer->InsertPipelineBarrier( VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, diff --git a/shell/platform/fuchsia/runtime/dart/profiler_symbols/pubspec.yaml b/shell/platform/fuchsia/runtime/dart/profiler_symbols/pubspec.yaml index 519e6f49fd1a5..7076e2a537f44 100644 --- a/shell/platform/fuchsia/runtime/dart/profiler_symbols/pubspec.yaml +++ b/shell/platform/fuchsia/runtime/dart/profiler_symbols/pubspec.yaml @@ -4,7 +4,7 @@ name: profiler_symbols environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' version: 0 description: Extracts a minimal symbols table for the Dart VM profiler author: Dart Team diff --git a/shell/platform/glfw/BUILD.gn b/shell/platform/glfw/BUILD.gn index 9d23979d957b1..1bf6d587f05b2 100644 --- a/shell/platform/glfw/BUILD.gn +++ b/shell/platform/glfw/BUILD.gn @@ -59,7 +59,7 @@ source_set("flutter_glfw") { "//flutter/shell/platform/common/client_wrapper:client_wrapper", "//flutter/shell/platform/embedder:embedder_as_internal_library", "//flutter/shell/platform/glfw/client_wrapper:client_wrapper_glfw", - "//third_party/glfw", + "//flutter/third_party/glfw", "//third_party/rapidjson", ] diff --git a/shell/platform/linux/fl_binary_messenger.cc b/shell/platform/linux/fl_binary_messenger.cc index dae96402cbb0b..3a1a998e59721 100644 --- a/shell/platform/linux/fl_binary_messenger.cc +++ b/shell/platform/linux/fl_binary_messenger.cc @@ -359,28 +359,28 @@ static void resize_channel(FlBinaryMessenger* messenger, nullptr); } -// Called when a response is received for the allow channel overflow message. -static void set_allow_channel_overflowl_response_cb(GObject* object, - GAsyncResult* result, - gpointer user_data) { +// Called when a response is received for the warns on overflow message. +static void set_warns_on_channel_overflow_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { g_autoptr(GError) error = nullptr; if (!finish_method(object, result, &error)) { - g_warning("Failed to set allow channel overflow: %s", error->message); + g_warning("Failed to set warns on channel overflow: %s", error->message); } } -static void set_allow_channel_overflow(FlBinaryMessenger* messenger, - const gchar* channel, - bool allowed) { +static void set_warns_on_channel_overflow(FlBinaryMessenger* messenger, + const gchar* channel, + bool warns) { g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); g_autoptr(FlValue) args = fl_value_new_list(); fl_value_append_take(args, fl_value_new_string(channel)); - fl_value_append_take(args, fl_value_new_bool(allowed)); + fl_value_append_take(args, fl_value_new_bool(!warns)); g_autoptr(GBytes) message = fl_method_codec_encode_method_call( FL_METHOD_CODEC(codec), kOverflowMethod, args, nullptr); fl_binary_messenger_send_on_channel( messenger, kControlChannelName, message, nullptr, - set_allow_channel_overflowl_response_cb, nullptr); + set_warns_on_channel_overflow_response_cb, nullptr); } static void fl_binary_messenger_impl_class_init( @@ -395,7 +395,7 @@ static void fl_binary_messenger_impl_iface_init( iface->send_on_channel = send_on_channel; iface->send_on_channel_finish = send_on_channel_finish; iface->resize_channel = resize_channel; - iface->set_allow_channel_overflow = set_allow_channel_overflow; + iface->set_warns_on_channel_overflow = set_warns_on_channel_overflow; } static void fl_binary_messenger_impl_init(FlBinaryMessengerImpl* self) { @@ -481,12 +481,12 @@ G_MODULE_EXPORT void fl_binary_messenger_resize_channel(FlBinaryMessenger* self, new_size); } -G_MODULE_EXPORT void fl_binary_messenger_set_allow_channel_overflow( +G_MODULE_EXPORT void fl_binary_messenger_set_warns_on_channel_overflow( FlBinaryMessenger* self, const gchar* channel, - bool allowed) { + bool warns) { g_return_if_fail(FL_IS_BINARY_MESSENGER(self)); - return FL_BINARY_MESSENGER_GET_IFACE(self)->set_allow_channel_overflow( - self, channel, allowed); + return FL_BINARY_MESSENGER_GET_IFACE(self)->set_warns_on_channel_overflow( + self, channel, warns); } diff --git a/shell/platform/linux/fl_binary_messenger_test.cc b/shell/platform/linux/fl_binary_messenger_test.cc index d045cd2c0e96c..d27e07d8af9bf 100644 --- a/shell/platform/linux/fl_binary_messenger_test.cc +++ b/shell/platform/linux/fl_binary_messenger_test.cc @@ -136,9 +136,9 @@ static void resize_channel(FlBinaryMessenger* messenger, // Fake implementation. Do nothing. } -static void set_allow_channel_overflow(FlBinaryMessenger* messenger, - const gchar* channel, - bool allowed) { +static void set_warns_on_channel_overflow(FlBinaryMessenger* messenger, + const gchar* channel, + bool warns) { // Fake implementation. Do nothing. } @@ -149,7 +149,7 @@ static void fl_fake_binary_messenger_iface_init( iface->send_on_channel = send_on_channel; iface->send_on_channel_finish = send_on_channel_finish; iface->resize_channel = resize_channel; - iface->set_allow_channel_overflow = set_allow_channel_overflow; + iface->set_warns_on_channel_overflow = set_warns_on_channel_overflow; } static void fl_fake_binary_messenger_init(FlFakeBinaryMessenger* self) {} @@ -455,7 +455,7 @@ TEST(FlBinaryMessengerTest, ResizeChannel) { } // Checks if the 'overflow' command is sent and is well-formed. -TEST(FlBinaryMessengerTest, AllowOverflowChannel) { +TEST(FlBinaryMessengerTest, WarnsOnOverflowChannel) { g_autoptr(FlEngine) engine = make_mock_engine(); FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine); @@ -495,8 +495,8 @@ TEST(FlBinaryMessengerTest, AllowOverflowChannel) { EXPECT_EQ(error, nullptr); FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); - fl_binary_messenger_set_allow_channel_overflow(messenger, "flutter/test", - true); + fl_binary_messenger_set_warns_on_channel_overflow(messenger, "flutter/test", + false); EXPECT_TRUE(called); } @@ -542,8 +542,8 @@ TEST(FlBinaryMessengerTest, ControlChannelErrorResponse) { return kInvalidArguments; })); - fl_binary_messenger_set_allow_channel_overflow(messenger, "flutter/test", - true); + fl_binary_messenger_set_warns_on_channel_overflow(messenger, "flutter/test", + false); EXPECT_TRUE(called); diff --git a/shell/platform/linux/fl_key_event.cc b/shell/platform/linux/fl_key_event.cc index db258f9187f20..228186f09cb65 100644 --- a/shell/platform/linux/fl_key_event.cc +++ b/shell/platform/linux/fl_key_event.cc @@ -9,21 +9,27 @@ static void dispose_origin_from_gdk_event(gpointer origin) { gdk_event_free(reinterpret_cast(origin)); } -FlKeyEvent* fl_key_event_new_from_gdk_event(GdkEvent* raw_event) { - g_return_val_if_fail(raw_event != nullptr, nullptr); - GdkEventKey* event = reinterpret_cast(raw_event); - GdkEventType type = event->type; +FlKeyEvent* fl_key_event_new_from_gdk_event(GdkEvent* event) { + g_return_val_if_fail(event != nullptr, nullptr); + GdkEventType type = gdk_event_get_event_type(event); g_return_val_if_fail(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE, nullptr); FlKeyEvent* result = g_new(FlKeyEvent, 1); - result->time = event->time; + guint16 keycode = 0; + gdk_event_get_keycode(event, &keycode); + guint keyval = 0; + gdk_event_get_keyval(event, &keyval); + GdkModifierType state = static_cast(0); + gdk_event_get_state(event, &state); + + result->time = gdk_event_get_time(event); result->is_press = type == GDK_KEY_PRESS; - result->keycode = event->hardware_keycode; - result->keyval = event->keyval; - result->state = event->state; - result->string = g_strdup(event->string); - result->group = event->group; + result->keycode = keycode; + result->keyval = keyval; + result->state = state; + result->string = g_strdup(event->key.string); + result->group = event->key.group; result->origin = event; result->dispose_origin = dispose_origin_from_gdk_event; diff --git a/shell/platform/linux/fl_keyboard_manager_test.cc b/shell/platform/linux/fl_keyboard_manager_test.cc index 89beae62e8ca6..2add32bd73f4f 100644 --- a/shell/platform/linux/fl_keyboard_manager_test.cc +++ b/shell/platform/linux/fl_keyboard_manager_test.cc @@ -222,10 +222,10 @@ static void fl_mock_binary_messenger_resize_channel( // Mock implementation. Do nothing. } -static void fl_mock_binary_messenger_set_allow_channel_overflow( +static void fl_mock_binary_messenger_set_warns_on_channel_overflow( FlBinaryMessenger* messenger, const gchar* channel, - bool allowed) { + bool warns) { // Mock implementation. Do nothing. } @@ -251,8 +251,8 @@ static void fl_mock_key_binary_messenger_iface_init( iface->send_on_channel_finish = fl_mock_key_binary_messenger_send_on_channel_finish; iface->resize_channel = fl_mock_binary_messenger_resize_channel; - iface->set_allow_channel_overflow = - fl_mock_binary_messenger_set_allow_channel_overflow; + iface->set_warns_on_channel_overflow = + fl_mock_binary_messenger_set_warns_on_channel_overflow; } static void fl_mock_key_binary_messenger_init(FlMockKeyBinaryMessenger* self) {} diff --git a/shell/platform/linux/fl_scrolling_manager.cc b/shell/platform/linux/fl_scrolling_manager.cc index f75efec4440c6..f819c6052a3f1 100644 --- a/shell/platform/linux/fl_scrolling_manager.cc +++ b/shell/platform/linux/fl_scrolling_manager.cc @@ -65,20 +65,31 @@ void fl_scrolling_manager_set_last_mouse_position(FlScrollingManager* self, } void fl_scrolling_manager_handle_scroll_event(FlScrollingManager* self, - GdkEventScroll* event, + GdkEventScroll* scroll_event, gint scale_factor) { + GdkEvent* event = reinterpret_cast(scroll_event); + + guint event_time = gdk_event_get_time(event); + gdouble event_x = 0.0, event_y = 0.0; + gdk_event_get_coords(event, &event_x, &event_y); gdouble scroll_delta_x = 0.0, scroll_delta_y = 0.0; - if (event->direction == GDK_SCROLL_SMOOTH) { - scroll_delta_x = event->delta_x; - scroll_delta_y = event->delta_y; - } else if (event->direction == GDK_SCROLL_UP) { - scroll_delta_y = -1; - } else if (event->direction == GDK_SCROLL_DOWN) { - scroll_delta_y = 1; - } else if (event->direction == GDK_SCROLL_LEFT) { - scroll_delta_x = -1; - } else if (event->direction == GDK_SCROLL_RIGHT) { - scroll_delta_x = 1; + GdkScrollDirection event_direction = GDK_SCROLL_SMOOTH; + if (gdk_event_get_scroll_direction(event, &event_direction)) { + if (event_direction == GDK_SCROLL_UP) { + scroll_delta_x = 0; + scroll_delta_y = -1; + } else if (event_direction == GDK_SCROLL_DOWN) { + scroll_delta_x = 0; + scroll_delta_y = 1; + } else if (event_direction == GDK_SCROLL_LEFT) { + scroll_delta_x = -1; + scroll_delta_y = 0; + } else if (event_direction == GDK_SCROLL_RIGHT) { + scroll_delta_x = 1; + scroll_delta_y = 0; + } + } else { + gdk_event_get_scroll_deltas(event, &scroll_delta_x, &scroll_delta_y); } // The multiplier is taken from the Chromium source @@ -87,14 +98,14 @@ void fl_scrolling_manager_handle_scroll_event(FlScrollingManager* self, scroll_delta_x *= kScrollOffsetMultiplier * scale_factor; scroll_delta_y *= kScrollOffsetMultiplier * scale_factor; - if (gdk_device_get_source(gdk_event_get_source_device( - reinterpret_cast(event))) == GDK_SOURCE_TOUCHPAD) { + if (gdk_device_get_source(gdk_event_get_source_device(event)) == + GDK_SOURCE_TOUCHPAD) { scroll_delta_x *= -1; scroll_delta_y *= -1; - if (event->is_stop) { + if (gdk_event_is_scroll_stop_event(event)) { fl_scrolling_view_delegate_send_pointer_pan_zoom_event( - self->view_delegate, event->time * kMicrosecondsPerMillisecond, - event->x * scale_factor, event->y * scale_factor, kPanZoomEnd, + self->view_delegate, event_time * kMicrosecondsPerMillisecond, + event_x * scale_factor, event_y * scale_factor, kPanZoomEnd, self->pan_x, self->pan_y, 0, 0); self->pan_started = FALSE; } else { @@ -102,28 +113,28 @@ void fl_scrolling_manager_handle_scroll_event(FlScrollingManager* self, self->pan_x = 0; self->pan_y = 0; fl_scrolling_view_delegate_send_pointer_pan_zoom_event( - self->view_delegate, event->time * kMicrosecondsPerMillisecond, - event->x * scale_factor, event->y * scale_factor, kPanZoomStart, 0, - 0, 0, 0); + self->view_delegate, event_time * kMicrosecondsPerMillisecond, + event_x * scale_factor, event_y * scale_factor, kPanZoomStart, 0, 0, + 0, 0); self->pan_started = TRUE; } self->pan_x += scroll_delta_x; self->pan_y += scroll_delta_y; fl_scrolling_view_delegate_send_pointer_pan_zoom_event( - self->view_delegate, event->time * kMicrosecondsPerMillisecond, - event->x * scale_factor, event->y * scale_factor, kPanZoomUpdate, + self->view_delegate, event_time * kMicrosecondsPerMillisecond, + event_x * scale_factor, event_y * scale_factor, kPanZoomUpdate, self->pan_x, self->pan_y, 1, 0); } } else { - self->last_x = event->x * scale_factor; - self->last_y = event->y * scale_factor; + self->last_x = event_x * scale_factor; + self->last_y = event_y * scale_factor; fl_scrolling_view_delegate_send_mouse_pointer_event( self->view_delegate, FlutterPointerPhase::kMove /* arbitrary value, phase will be ignored as this is a discrete scroll event */ , - event->time * kMicrosecondsPerMillisecond, event->x * scale_factor, - event->y * scale_factor, scroll_delta_x, scroll_delta_y, 0); + event_time * kMicrosecondsPerMillisecond, event_x * scale_factor, + event_y * scale_factor, scroll_delta_x, scroll_delta_y, 0); } } diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index b341114076de7..e41bbebdabec9 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -125,9 +125,18 @@ static void init_scrolling(FlView* self) { } // Converts a GDK button event into a Flutter event and sends it to the engine. -static gboolean send_pointer_button_event(FlView* self, GdkEventButton* event) { +static gboolean send_pointer_button_event(FlView* self, GdkEvent* event) { + guint event_time = gdk_event_get_time(event); + GdkEventType event_type = gdk_event_get_event_type(event); + GdkModifierType event_state = static_cast(0); + gdk_event_get_state(event, &event_state); + guint event_button = 0; + gdk_event_get_button(event, &event_button); + gdouble event_x = 0.0, event_y = 0.0; + gdk_event_get_coords(event, &event_x, &event_y); + int64_t button; - switch (event->button) { + switch (event_button) { case 1: button = kFlutterPointerButtonMousePrimary; break; @@ -142,7 +151,7 @@ static gboolean send_pointer_button_event(FlView* self, GdkEventButton* event) { } int old_button_state = self->button_state; FlutterPointerPhase phase = kMove; - if (event->type == GDK_BUTTON_PRESS) { + if (event_type == GDK_BUTTON_PRESS) { // Drop the event if Flutter already thinks the button is down. if ((self->button_state & button) != 0) { return FALSE; @@ -150,7 +159,7 @@ static gboolean send_pointer_button_event(FlView* self, GdkEventButton* event) { self->button_state ^= button; phase = old_button_state == 0 ? kDown : kMove; - } else if (event->type == GDK_BUTTON_RELEASE) { + } else if (event_type == GDK_BUTTON_RELEASE) { // Drop the event if Flutter already thinks the button is up. if ((self->button_state & button) == 0) { return FALSE; @@ -165,15 +174,13 @@ static gboolean send_pointer_button_event(FlView* self, GdkEventButton* event) { } gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); - fl_scrolling_manager_set_last_mouse_position(self->scrolling_manager, - event->x * scale_factor, - event->y * scale_factor); + fl_scrolling_manager_set_last_mouse_position( + self->scrolling_manager, event_x * scale_factor, event_y * scale_factor); fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager, - event->state, event->time); + event_state, event_time); fl_engine_send_mouse_pointer_event( - self->engine, phase, event->time * kMicrosecondsPerMillisecond, - event->x * scale_factor, event->y * scale_factor, 0, 0, - self->button_state); + self->engine, phase, event_time * kMicrosecondsPerMillisecond, + event_x * scale_factor, event_y * scale_factor, 0, 0, self->button_state); return TRUE; } @@ -286,8 +293,9 @@ static void fl_view_keyboard_delegate_iface_init( std::unique_ptr in_event) { FlKeyEvent* event = in_event.release(); GdkEvent* gdk_event = reinterpret_cast(event->origin); - GdkEventType type = gdk_event->type; - g_return_if_fail(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE); + GdkEventType event_type = gdk_event_get_event_type(gdk_event); + g_return_if_fail(event_type == GDK_KEY_PRESS || + event_type == GDK_KEY_RELEASE); gdk_event_put(gdk_event); fl_key_event_dispose(event); }; @@ -352,23 +360,27 @@ static void fl_view_text_input_delegate_iface_init( // Signal handler for GtkWidget::button-press-event static gboolean button_press_event_cb(GtkWidget* widget, - GdkEventButton* event, + GdkEventButton* button_event, FlView* self) { + GdkEvent* event = reinterpret_cast(button_event); + // Flutter doesn't handle double and triple click events. - if (event->type == GDK_DOUBLE_BUTTON_PRESS || - event->type == GDK_TRIPLE_BUTTON_PRESS) { + GdkEventType event_type = gdk_event_get_event_type(event); + if (event_type == GDK_DOUBLE_BUTTON_PRESS || + event_type == GDK_TRIPLE_BUTTON_PRESS) { return FALSE; } - check_pointer_inside(self, reinterpret_cast(event)); + check_pointer_inside(self, event); return send_pointer_button_event(self, event); } // Signal handler for GtkWidget::button-release-event static gboolean button_release_event_cb(GtkWidget* widget, - GdkEventButton* event, + GdkEventButton* button_event, FlView* self) { + GdkEvent* event = reinterpret_cast(button_event); return send_pointer_button_event(self, event); } @@ -387,44 +399,60 @@ static gboolean scroll_event_cb(GtkWidget* widget, // Signal handler for GtkWidget::motion-notify-event static gboolean motion_notify_event_cb(GtkWidget* widget, - GdkEventMotion* event, + GdkEventMotion* motion_event, FlView* self) { + GdkEvent* event = reinterpret_cast(motion_event); + if (self->engine == nullptr) { return FALSE; } - check_pointer_inside(self, reinterpret_cast(event)); + guint event_time = gdk_event_get_time(event); + GdkModifierType event_state = static_cast(0); + gdk_event_get_state(event, &event_state); + gdouble event_x = 0.0, event_y = 0.0; + gdk_event_get_coords(event, &event_x, &event_y); + + check_pointer_inside(self, event); gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager, - event->state, event->time); + event_state, event_time); fl_engine_send_mouse_pointer_event( self->engine, self->button_state != 0 ? kMove : kHover, - event->time * kMicrosecondsPerMillisecond, event->x * scale_factor, - event->y * scale_factor, 0, 0, self->button_state); + event_time * kMicrosecondsPerMillisecond, event_x * scale_factor, + event_y * scale_factor, 0, 0, self->button_state); return TRUE; } // Signal handler for GtkWidget::enter-notify-event static gboolean enter_notify_event_cb(GtkWidget* widget, - GdkEventCrossing* event, + GdkEventCrossing* crossing_event, FlView* self) { + GdkEvent* event = reinterpret_cast(crossing_event); + if (self->engine == nullptr) { return FALSE; } - check_pointer_inside(self, reinterpret_cast(event)); + check_pointer_inside(self, event); return TRUE; } // Signal handler for GtkWidget::leave-notify-event static gboolean leave_notify_event_cb(GtkWidget* widget, - GdkEventCrossing* event, + GdkEventCrossing* crossing_event, FlView* self) { - if (event->mode != GDK_CROSSING_NORMAL) { + GdkEvent* event = reinterpret_cast(crossing_event); + + guint event_time = gdk_event_get_time(event); + gdouble event_x = 0.0, event_y = 0.0; + gdk_event_get_coords(event, &event_x, &event_y); + + if (crossing_event->mode != GDK_CROSSING_NORMAL) { return FALSE; } @@ -438,8 +466,8 @@ static gboolean leave_notify_event_cb(GtkWidget* widget, if (self->pointer_inside && self->button_state == 0) { gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); fl_engine_send_mouse_pointer_event( - self->engine, kRemove, event->time * kMicrosecondsPerMillisecond, - event->x * scale_factor, event->y * scale_factor, 0, 0, + self->engine, kRemove, event_time * kMicrosecondsPerMillisecond, + event_x * scale_factor, event_y * scale_factor, 0, 0, self->button_state); self->pointer_inside = FALSE; } diff --git a/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h index e9f202d0db5f7..9509d572c33c6 100644 --- a/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h +++ b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h @@ -98,9 +98,9 @@ struct _FlBinaryMessengerInterface { const gchar* channel, int64_t new_size); - void (*set_allow_channel_overflow)(FlBinaryMessenger* messenger, - const gchar* channel, - bool allowed); + void (*set_warns_on_channel_overflow)(FlBinaryMessenger* messenger, + const gchar* channel, + bool warns); }; struct _FlBinaryMessengerResponseHandleClass { @@ -210,19 +210,19 @@ void fl_binary_messenger_resize_channel(FlBinaryMessenger* messenger, int64_t new_size); /** - * fl_binary_messenger_set_allow_channel_overflow: + * fl_binary_messenger_set_warns_on_channel_overflow: * @messenger: an #FlBinaryMessenger. * @channel: channel to be allowed to overflow silently. - * @allowed: when true the channel is expected to overflow and warning messages + * @warns: when false, the channel is expected to overflow and warning messages * will not be shown. * * Sends a message to the control channel asking to allow or disallow a channel * to overflow silently. */ -void fl_binary_messenger_set_allow_channel_overflow( +void fl_binary_messenger_set_warns_on_channel_overflow( FlBinaryMessenger* messenger, const gchar* channel, - bool allowed); + bool warns); G_END_DECLS diff --git a/shell/platform/linux/testing/mock_binary_messenger.cc b/shell/platform/linux/testing/mock_binary_messenger.cc index b836a49cc6e98..eacbee1d6e27c 100644 --- a/shell/platform/linux/testing/mock_binary_messenger.cc +++ b/shell/platform/linux/testing/mock_binary_messenger.cc @@ -131,14 +131,14 @@ static void fl_mock_binary_messenger_resize_channel( self->mock->fl_binary_messenger_resize_channel(messenger, channel, new_size); } -static void fl_mock_binary_messenger_set_allow_channel_overflow( +static void fl_mock_binary_messenger_set_warns_on_channel_overflow( FlBinaryMessenger* messenger, const gchar* channel, - bool allowed) { + bool warns) { g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger)); FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - self->mock->fl_binary_messenger_set_allow_channel_overflow(messenger, channel, - allowed); + self->mock->fl_binary_messenger_set_warns_on_channel_overflow(messenger, + channel, warns); } static void fl_mock_binary_messenger_iface_init( @@ -150,8 +150,8 @@ static void fl_mock_binary_messenger_iface_init( iface->send_on_channel_finish = fl_mock_binary_messenger_send_on_channel_finish; iface->resize_channel = fl_mock_binary_messenger_resize_channel; - iface->set_allow_channel_overflow = - fl_mock_binary_messenger_set_allow_channel_overflow; + iface->set_warns_on_channel_overflow = + fl_mock_binary_messenger_set_warns_on_channel_overflow; } static void fl_mock_binary_messenger_init(FlMockBinaryMessenger* self) {} diff --git a/shell/platform/linux/testing/mock_binary_messenger.h b/shell/platform/linux/testing/mock_binary_messenger.h index e26a3cf1c7463..61761276a8d88 100644 --- a/shell/platform/linux/testing/mock_binary_messenger.h +++ b/shell/platform/linux/testing/mock_binary_messenger.h @@ -59,10 +59,10 @@ class MockBinaryMessenger { int64_t new_size)); MOCK_METHOD(void, - fl_binary_messenger_set_allow_channel_overflow, + fl_binary_messenger_set_warns_on_channel_overflow, (FlBinaryMessenger * messenger, const gchar* channel, - bool allowed)); + bool warns)); bool HasMessageHandler(const gchar* channel) const; diff --git a/shell/platform/windows/angle_surface_manager.cc b/shell/platform/windows/angle_surface_manager.cc index 2165130ba158a..7005348482856 100644 --- a/shell/platform/windows/angle_surface_manager.cc +++ b/shell/platform/windows/angle_surface_manager.cc @@ -256,6 +256,11 @@ bool AngleSurfaceManager::CreateSurface(WindowsRenderTarget* render_target, surface_height_ = height; render_surface_ = surface; + if (!MakeCurrent()) { + LogEglError("Unable to make surface current to update the swap interval"); + return false; + } + SetVSyncEnabled(vsync_enabled); return true; } @@ -300,11 +305,20 @@ void AngleSurfaceManager::DestroySurface() { render_surface_ = EGL_NO_SURFACE; } +bool AngleSurfaceManager::HasContextCurrent() { + return eglGetCurrentContext() != EGL_NO_CONTEXT; +} + bool AngleSurfaceManager::MakeCurrent() { return (eglMakeCurrent(egl_display_, render_surface_, render_surface_, egl_context_) == EGL_TRUE); } +bool AngleSurfaceManager::ClearCurrent() { + return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT) == EGL_TRUE); +} + bool AngleSurfaceManager::ClearContext() { return (eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_) == EGL_TRUE); @@ -328,12 +342,6 @@ EGLSurface AngleSurfaceManager::CreateSurfaceFromHandle( } void AngleSurfaceManager::SetVSyncEnabled(bool enabled) { - if (eglMakeCurrent(egl_display_, render_surface_, render_surface_, - egl_context_) != EGL_TRUE) { - LogEglError("Unable to make surface current to update the swap interval"); - return; - } - // OpenGL swap intervals can be used to prevent screen tearing. // If enabled, the raster thread blocks until the v-blank. // This is unnecessary if DWM composition is enabled. @@ -343,13 +351,6 @@ void AngleSurfaceManager::SetVSyncEnabled(bool enabled) { LogEglError("Unable to update the swap interval"); return; } - - if (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT) != EGL_TRUE) { - LogEglError( - "Unable to release the context after updating the swap interval"); - return; - } } bool AngleSurfaceManager::GetDevice(ID3D11Device** device) { diff --git a/shell/platform/windows/angle_surface_manager.h b/shell/platform/windows/angle_surface_manager.h index 576704f630054..2f6302b9659ed 100644 --- a/shell/platform/windows/angle_surface_manager.h +++ b/shell/platform/windows/angle_surface_manager.h @@ -33,8 +33,10 @@ class AngleSurfaceManager { // Creates an EGLSurface wrapper and backing DirectX 11 SwapChain // associated with window, in the appropriate format for display. - // Target represents the visual entity to bind to. Width and + // Target represents the visual entity to bind to. Width and // height represent dimensions surface is created at. + // + // This binds |egl_context_| to the current thread. virtual bool CreateSurface(WindowsRenderTarget* render_target, EGLint width, EGLint height, @@ -42,8 +44,10 @@ class AngleSurfaceManager { // Resizes backing surface from current size to newly requested size // based on width and height for the specific case when width and height do - // not match current surface dimensions. Target represents the visual entity + // not match current surface dimensions. Target represents the visual entity // to bind to. + // + // This binds |egl_context_| to the current thread. virtual void ResizeSurface(WindowsRenderTarget* render_target, EGLint width, EGLint height, @@ -56,11 +60,17 @@ class AngleSurfaceManager { // Releases the pass-in EGLSurface wrapping and backing resources if not null. virtual void DestroySurface(); - // Binds egl_context_ to the current rendering thread and to the draw and read - // surfaces returning a boolean result reflecting success. + // Check if the current thread has a context bound. + bool HasContextCurrent(); + + // Binds |egl_context_| to the current rendering thread and to the draw and + // read surfaces returning a boolean result reflecting success. bool MakeCurrent(); - // Clears current egl_context_ + // Unbinds the current EGL context from the current thread. + bool ClearCurrent(); + + // Clears the |egl_context_| draw and read surfaces. bool ClearContext(); // Binds egl_resource_context_ to the current rendering thread and to the draw diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index fce1c0531e48c..6a5371c27c6d3 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -390,6 +390,10 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { args.aot_data = aot_data_.get(); } + // The platform thread creates OpenGL contexts. These + // must be released to be used by the engine's threads. + FML_DCHECK(!surface_manager_ || !surface_manager_->HasContextCurrent()); + FlutterRendererConfig renderer_config; if (enable_impeller_) { @@ -565,8 +569,7 @@ void FlutterWindowsEngine::HandlePlatformMessage( auto message = ConvertToDesktopMessage(*engine_message); - message_dispatcher_->HandleMessage( - message, [this] {}, [this] {}); + message_dispatcher_->HandleMessage(message, [this] {}, [this] {}); } void FlutterWindowsEngine::ReloadSystemFonts() { diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index d27c78c70507d..7b29748453fa9 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -909,6 +909,28 @@ TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) { 0); } +TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) { + FlutterWindowsEngineBuilder builder{GetContext()}; + + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + MockFlutterWindowsView view(std::move(window_binding_handler)); + view.SetEngine(builder.Build()); + FlutterWindowsEngine* engine = view.GetEngine(); + + EngineModifier modifier(engine); + modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; + auto handler = std::make_unique(engine); + ON_CALL(*handler, IsLastWindowOfProcess).WillByDefault([]() { + return false; + }); + EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(1); + modifier.SetLifecycleManager(std::move(handler)); + engine->lifecycle_manager()->BeginProcessingExit(); + + engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0); +} + TEST_F(FlutterWindowsEngineTest, AppStartsInResumedState) { FlutterWindowsEngineBuilder builder{GetContext()}; diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 20ac21558ec86..11effebe45f6f 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -594,6 +594,12 @@ void FlutterWindowsView::CreateRenderSurface() { engine_->surface_manager()->CreateSurface(GetRenderTarget(), bounds.width, bounds.height, enable_vsync); + // The EGL context cannot be current on multiple threads. + // Creating the render surface runs on the platform thread and + // makes the EGL context current. Thus, the EGL context must be + // released so that the raster thread can use it for rendering. + engine_->surface_manager()->ClearCurrent(); + resize_target_width_ = bounds.width; resize_target_height_ = bounds.height; } @@ -668,14 +674,24 @@ void FlutterWindowsView::UpdateSemanticsEnabled(bool enabled) { } void FlutterWindowsView::OnDwmCompositionChanged() { - if (engine_->surface_manager()) { - // Update the surface with the new composition state. - // Switch to the raster thread as this requires making the context current. - auto needs_vsync = binding_handler_->NeedsVSync(); - engine_->PostRasterThreadTask([this, needs_vsync]() { - engine_->surface_manager()->SetVSyncEnabled(needs_vsync); - }); + AngleSurfaceManager* surface_manager = engine_->surface_manager(); + if (!surface_manager) { + return; } + + // Update the surface with the new composition state. + // Switch to the raster thread as the render EGL context can only be + // current on a single thread a time. + auto needs_vsync = binding_handler_->NeedsVSync(); + engine_->PostRasterThreadTask([surface_manager, needs_vsync]() { + if (!surface_manager->MakeCurrent()) { + FML_LOG(ERROR) + << "Unable to make surface current to update the swap interval"; + return; + } + + surface_manager->SetVSyncEnabled(needs_vsync); + }); } void FlutterWindowsView::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc index 93a7f3024d88b..fee0333dbd048 100644 --- a/shell/platform/windows/flutter_windows_view_unittests.cc +++ b/shell/platform/windows/flutter_windows_view_unittests.cc @@ -110,8 +110,8 @@ class MockFlutterWindowsEngine : public FlutterWindowsEngine { public: MockFlutterWindowsEngine() : FlutterWindowsEngine(GetTestProject()) {} - MOCK_METHOD(bool, Stop, (), (override)); - MOCK_METHOD(bool, PostRasterThreadTask, (fml::closure), (override)); + MOCK_METHOD(bool, Stop, (), ()); + MOCK_METHOD(bool, PostRasterThreadTask, (fml::closure), ()); private: FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsEngine); diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index 340092e3edb79..08c0007a4d083 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -196,7 +196,7 @@ void TextInputPlugin::ComposeChangeHook(const std::u16string& text, std::string text_before_change = active_model_->GetText(); TextRange composing_before_change = active_model_->composing_range(); active_model_->AddText(text); - cursor_pos += active_model_->composing_range().extent(); + cursor_pos += active_model_->composing_range().start(); active_model_->UpdateComposingText(text); active_model_->SetSelection(TextRange(cursor_pos, cursor_pos)); std::string text_after_change = active_model_->GetText(); diff --git a/shell/platform/windows/text_input_plugin_unittest.cc b/shell/platform/windows/text_input_plugin_unittest.cc index 7d4705096441d..a99e0149aa286 100644 --- a/shell/platform/windows/text_input_plugin_unittest.cc +++ b/shell/platform/windows/text_input_plugin_unittest.cc @@ -36,6 +36,8 @@ static constexpr char kSelectionAffinityKey[] = "selectionAffinity"; static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional"; static constexpr char kComposingBaseKey[] = "composingBase"; static constexpr char kComposingExtentKey[] = "composingExtent"; +static constexpr char kUpdateEditingStateMethod[] = + "TextInputClient.updateEditingState"; static std::unique_ptr> CreateResponse(bool handled) { auto response_doc = @@ -243,7 +245,7 @@ TEST(TextInputPluginTest, VerifyInputActionNewlineInsertNewLine) { // Editing state should have been updated. auto encoded_arguments = EncodedEditingState("\n", TextRange(1)); auto update_state_message = codec.EncodeMethodCall( - {"TextInputClient.updateEditingState", std::move(encoded_arguments)}); + {kUpdateEditingStateMethod, std::move(encoded_arguments)}); EXPECT_TRUE(std::equal(update_state_message->begin(), update_state_message->end(), @@ -366,6 +368,65 @@ TEST(TextInputPluginTest, TextEditingWorksWithDeltaModel) { // Passes if it did not crash } +// Regression test for https://github.com/flutter/flutter/issues/123749 +TEST(TextInputPluginTest, CompositionCursorPos) { + int selection_base = -1; + TestBinaryMessenger messenger([&](const std::string& channel, + const uint8_t* message, size_t size, + BinaryReply reply) { + auto method = JsonMethodCodec::GetInstance().DecodeMethodCall( + std::vector(message, message + size)); + if (method->method_name() == kUpdateEditingStateMethod) { + const auto& args = *method->arguments(); + const auto& editing_state = args[1]; + auto base = editing_state.FindMember(kSelectionBaseKey); + auto extent = editing_state.FindMember(kSelectionExtentKey); + ASSERT_NE(base, editing_state.MemberEnd()); + ASSERT_TRUE(base->value.IsInt()); + ASSERT_NE(extent, editing_state.MemberEnd()); + ASSERT_TRUE(extent->value.IsInt()); + selection_base = base->value.GetInt(); + EXPECT_EQ(extent->value.GetInt(), selection_base); + } + }); + MockTextInputPluginDelegate delegate; + + TextInputPlugin plugin(&messenger, &delegate); + + auto args = std::make_unique(rapidjson::kArrayType); + auto& allocator = args->GetAllocator(); + args->PushBack(123, allocator); // client_id + rapidjson::Value client_config(rapidjson::kObjectType); + args->PushBack(client_config, allocator); + auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall( + MethodCall(kSetClientMethod, std::move(args))); + EXPECT_TRUE(messenger.SimulateEngineMessage( + kChannelName, encoded->data(), encoded->size(), + [](const uint8_t* reply, size_t reply_size) {})); + + plugin.ComposeBeginHook(); + EXPECT_EQ(selection_base, 0); + plugin.ComposeChangeHook(u"abc", 3); + EXPECT_EQ(selection_base, 3); + + plugin.ComposeCommitHook(); + plugin.ComposeEndHook(); + EXPECT_EQ(selection_base, 3); + + plugin.ComposeBeginHook(); + plugin.ComposeChangeHook(u"1", 1); + EXPECT_EQ(selection_base, 4); + + plugin.ComposeChangeHook(u"12", 2); + EXPECT_EQ(selection_base, 5); + + plugin.ComposeChangeHook(u"12", 1); + EXPECT_EQ(selection_base, 4); + + plugin.ComposeChangeHook(u"12", 2); + EXPECT_EQ(selection_base, 5); +} + TEST(TextInputPluginTest, TransformCursorRect) { // A position of `EditableText`. double view_x = 100; diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index a41b1617ba6c2..90d52f300b2dd 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -38,6 +38,30 @@ void WindowsLifecycleManager::DispatchMessage(HWND hwnd, PostMessage(hwnd, message, wparam, lparam); } +bool WindowsLifecycleManager::HandleCloseMessage(HWND hwnd, + WPARAM wparam, + LPARAM lparam) { + if (!process_exit_) { + return false; + } + auto key = std::make_tuple(hwnd, wparam, lparam); + auto itr = sent_close_messages_.find(key); + if (itr != sent_close_messages_.end()) { + if (itr->second == 1) { + sent_close_messages_.erase(itr); + } else { + sent_close_messages_[key]--; + } + return false; + } + if (IsLastWindowOfProcess()) { + engine_->RequestApplicationQuit(hwnd, wparam, lparam, + AppExitType::cancelable); + return true; + } + return false; +} + bool WindowsLifecycleManager::WindowProc(HWND hwnd, UINT msg, WPARAM wpar, @@ -48,27 +72,8 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd, // send a request to the framework to see if the app should exit. If it // is, we re-dispatch a new WM_CLOSE message. In order to allow the new // message to reach other delegates, we ignore it here. - case WM_CLOSE: { - if (!process_exit_) { - return false; - } - auto key = std::make_tuple(hwnd, wpar, lpar); - auto itr = sent_close_messages_.find(key); - if (itr != sent_close_messages_.end()) { - if (itr->second == 1) { - sent_close_messages_.erase(itr); - } else { - sent_close_messages_[key]--; - } - return false; - } - if (IsLastWindowOfProcess()) { - engine_->RequestApplicationQuit(hwnd, wpar, lpar, - AppExitType::cancelable); - return true; - } - break; - } + case WM_CLOSE: + return HandleCloseMessage(hwnd, wpar, lpar); // DWM composition can be disabled on Windows 7. // Notify the engine as this can result in screen tearing. @@ -285,6 +290,11 @@ std::optional WindowsLifecycleManager::ExternalWindowMessage( case WM_DESTROY: event = flutter::WindowStateEvent::kHide; break; + case WM_CLOSE: + if (HandleCloseMessage(hwnd, wparam, lparam)) { + return NULL; + } + break; } if (event.has_value()) { diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index c56bfc306b0fb..fc20b382467fa 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -99,6 +99,13 @@ class WindowsLifecycleManager { LPARAM lparam); private: + // Pass top-level window close notifications to the application lifecycle + // logic. If the last window of the process receives WM_CLOSE and a listener + // is registered for WidgetsBindingObserver.didRequestAppExit, the message is + // sent to the framework to query whether the application should be allowed + // to quit. + bool HandleCloseMessage(HWND hwnd, WPARAM wparam, LPARAM lparam); + FlutterWindowsEngine* engine_; std::map, int> sent_close_messages_; diff --git a/shell/testing/BUILD.gn b/shell/testing/BUILD.gn index ec6400ce0763a..8f4572310c519 100644 --- a/shell/testing/BUILD.gn +++ b/shell/testing/BUILD.gn @@ -3,6 +3,15 @@ # found in the LICENSE file. import("//build/fuchsia/sdk.gni") +import("//flutter/impeller/tools/impeller.gni") +import("//flutter/shell/gpu/gpu.gni") + +shell_gpu_configuration("tester_gpu_configuration") { + enable_software = true + enable_gl = true + enable_vulkan = true + enable_metal = false +} executable("testing") { output_name = "flutter_tester" @@ -13,8 +22,9 @@ executable("testing") { ] sources = [ "tester_main.cc" ] + libs = [] if (is_win) { - libs = [ + libs += [ "psapi.lib", "user32.lib", "FontSub.lib", @@ -36,6 +46,14 @@ executable("testing") { "//third_party/skia", ] + if (impeller_supports_rendering) { + deps += [ + ":tester_gpu_configuration", + "//flutter/impeller", + "//third_party/swiftshader", + ] + } + metadata = { entitlement_file_path = [ "flutter_tester" ] } diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 310ce62e01430..4be39c4d70b41 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -29,6 +29,95 @@ #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/skia/include/core/SkSurface.h" +// Impeller should only be enabled if the Vulkan backend is enabled. +#define ALLOW_IMPELLER (IMPELLER_SUPPORTS_RENDERING && IMPELLER_ENABLE_VULKAN) + +#if ALLOW_IMPELLER +#include // nogncheck +#include "flutter/vulkan/procs/vulkan_proc_table.h" // nogncheck +#include "flutter/vulkan/swiftshader_path.h" // nogncheck +#include "impeller/entity/vk/entity_shaders_vk.h" // nogncheck +#include "impeller/entity/vk/modern_shaders_vk.h" // nogncheck +#include "impeller/renderer/backend/vulkan/context_vk.h" // nogncheck +#include "impeller/renderer/backend/vulkan/surface_context_vk.h" // nogncheck +#include "impeller/renderer/context.h" // nogncheck +#include "impeller/renderer/vk/compute_shaders_vk.h" // nogncheck +#include "shell/gpu/gpu_surface_vulkan_impeller.h" // nogncheck +#if IMPELLER_ENABLE_3D +#include "impeller/scene/shaders/vk/scene_shaders_vk.h" // nogncheck +#endif // IMPELLER_ENABLE_3D + +static std::vector> ShaderLibraryMappings() { + return { + std::make_shared(impeller_entity_shaders_vk_data, + impeller_entity_shaders_vk_length), + std::make_shared(impeller_modern_shaders_vk_data, + impeller_modern_shaders_vk_length), +#if IMPELLER_ENABLE_3D + std::make_shared(impeller_scene_shaders_vk_data, + impeller_scene_shaders_vk_length), +#endif // IMPELLER_ENABLE_3D + std::make_shared( + impeller_compute_shaders_vk_data, impeller_compute_shaders_vk_length), + }; +} + +struct ImpellerVulkanContextHolder { + ImpellerVulkanContextHolder() = default; + ImpellerVulkanContextHolder(ImpellerVulkanContextHolder&&) = default; + fml::RefPtr vulkan_proc_table; + std::shared_ptr context; + std::shared_ptr surface_context; + + bool Initialize(bool enable_validation); +}; + +bool ImpellerVulkanContextHolder::Initialize(bool enable_validation) { + vulkan_proc_table = + fml::MakeRefCounted(VULKAN_SO_PATH); + if (!vulkan_proc_table->NativeGetInstanceProcAddr()) { + FML_LOG(ERROR) << "Could not load Swiftshader library."; + return false; + } + impeller::ContextVK::Settings context_settings; + context_settings.proc_address_callback = + vulkan_proc_table->NativeGetInstanceProcAddr(); + context_settings.shader_libraries_data = ShaderLibraryMappings(); + context_settings.cache_directory = fml::paths::GetCachesDirectory(); + context_settings.enable_validation = enable_validation; + + context = impeller::ContextVK::Create(std::move(context_settings)); + if (!context || !context->IsValid()) { + VALIDATION_LOG << "Could not create Vulkan context."; + return false; + } + + impeller::vk::SurfaceKHR vk_surface; + impeller::vk::HeadlessSurfaceCreateInfoEXT surface_create_info; + auto res = context->GetInstance().createHeadlessSurfaceEXT( + &surface_create_info, // surface create info + nullptr, // allocator + &vk_surface // surface + ); + if (res != impeller::vk::Result::eSuccess) { + VALIDATION_LOG << "Could not create surface for tester " + << impeller::vk::to_string(res); + return false; + } + + impeller::vk::UniqueSurfaceKHR surface{vk_surface, context->GetInstance()}; + surface_context = context->CreateSurfaceContext(); + if (!surface_context->SetWindowSurface(std::move(surface))) { + VALIDATION_LOG << "Could not set up surface for context."; + return false; + } + return true; +} + +#else +struct ImpellerVulkanContextHolder {}; +#endif // IMPELLER_SUPPORTS_RENDERING + #if defined(FML_OS_WIN) #include #endif // defined(FML_OS_WIN) @@ -81,11 +170,41 @@ class TesterGPUSurfaceSoftware : public GPUSurfaceSoftware { class TesterPlatformView : public PlatformView, public GPUSurfaceSoftwareDelegate { public: - TesterPlatformView(Delegate& delegate, const TaskRunners& task_runners) - : PlatformView(delegate, task_runners) {} + TesterPlatformView(Delegate& delegate, + const TaskRunners& task_runners, + ImpellerVulkanContextHolder&& impeller_context_holder) + : PlatformView(delegate, task_runners), + impeller_context_holder_(std::move(impeller_context_holder)) {} + + ~TesterPlatformView() { +#if ALLOW_IMPELLER + if (impeller_context_holder_.context) { + impeller_context_holder_.context->Shutdown(); + } +#endif + } + + // |PlatformView| + std::shared_ptr GetImpellerContext() const override { +#if ALLOW_IMPELLER + return std::static_pointer_cast( + impeller_context_holder_.context); +#else + return nullptr; +#endif // ALLOW_IMPELLER + } // |PlatformView| std::unique_ptr CreateRenderingSurface() override { +#if ALLOW_IMPELLER + if (delegate_.OnPlatformViewGetSettings().enable_impeller) { + FML_DCHECK(impeller_context_holder_.context); + auto surface = std::make_unique( + impeller_context_holder_.surface_context); + FML_DCHECK(surface->IsValid()); + return surface; + } +#endif // ALLOW_IMPELLER auto surface = std::make_unique( this, true /* render to surface */); FML_DCHECK(surface->IsValid()); @@ -126,6 +245,7 @@ class TesterPlatformView : public PlatformView, private: sk_sp sk_surface_ = nullptr; + [[maybe_unused]] ImpellerVulkanContextHolder impeller_context_holder_; std::shared_ptr external_view_embedder_ = std::make_shared(); }; @@ -235,11 +355,23 @@ int RunTester(const flutter::Settings& settings, io_task_runner // io ); + ImpellerVulkanContextHolder impeller_context_holder; + +#if ALLOW_IMPELLER + if (settings.enable_impeller) { + if (!impeller_context_holder.Initialize( + settings.enable_vulkan_validation)) { + return EXIT_FAILURE; + } + } +#endif // ALLOW_IMPELLER + Shell::CreateCallback on_create_platform_view = - [](Shell& shell) { - return std::make_unique(shell, - shell.GetTaskRunners()); - }; + fml::MakeCopyable([impeller_context_holder = std::move( + impeller_context_holder)](Shell& shell) mutable { + return std::make_unique( + shell, shell.GetTaskRunners(), std::move(impeller_context_holder)); + }); Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { return std::make_unique( diff --git a/shell/vmservice/pubspec.yaml b/shell/vmservice/pubspec.yaml index d8061c2f61dbb..c3cffd9d83aad 100644 --- a/shell/vmservice/pubspec.yaml +++ b/shell/vmservice/pubspec.yaml @@ -5,4 +5,4 @@ name: vmservice_snapshot publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index 37913ed761eae..f3c636c95a0f9 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -26454,36 +26454,6 @@ copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------- -vulkanmemoryallocator - -Copyright 2018 Google Inc. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -28118,6 +28088,38 @@ skia Copyright 2023 Google LLC. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +skia + +Copyright 2023 Google, LLC + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/sky/packages/sky_engine/pubspec.yaml b/sky/packages/sky_engine/pubspec.yaml index c613749d4bbc5..08863df240e90 100644 --- a/sky/packages/sky_engine/pubspec.yaml +++ b/sky/packages/sky_engine/pubspec.yaml @@ -5,4 +5,4 @@ description: Dart SDK extensions for dart:ui homepage: http://flutter.io # sky_engine requires sdk_ext support in the analyzer which was added in 1.11.x environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' diff --git a/sky/tools/create_full_ios_framework.py b/sky/tools/create_full_ios_framework.py index 5858ddefb7b12..9aac99671348f 100644 --- a/sky/tools/create_full_ios_framework.py +++ b/sky/tools/create_full_ios_framework.py @@ -106,10 +106,49 @@ def main(): args, dst, framework, arm64_framework, simulator_framework, simulator_x64_framework, simulator_arm64_framework ) + + extension_safe_dst = os.path.join(dst, 'extension_safe') + create_extension_safe_framework( + args, extension_safe_dst, '%s_extension_safe' % arm64_out_dir, + '%s_extension_safe' % simulator_x64_out_dir, + '%s_extension_safe' % simulator_arm64_out_dir + ) + generate_gen_snapshot(args, dst, x64_out_dir, arm64_out_dir) zip_archive(dst) return 0 +def create_extension_safe_framework( # pylint: disable=too-many-arguments + args, dst, arm64_out_dir, simulator_x64_out_dir, simulator_arm64_out_dir +): + framework = os.path.join(dst, 'Flutter.framework') + simulator_framework = os.path.join(dst, 'sim', 'Flutter.framework') + arm64_framework = os.path.join(arm64_out_dir, 'Flutter.framework') + simulator_x64_framework = os.path.join( + simulator_x64_out_dir, 'Flutter.framework' + ) + simulator_arm64_framework = os.path.join( + simulator_arm64_out_dir, 'Flutter.framework' + ) + + if not os.path.isdir(arm64_framework): + print( + 'Cannot find extension safe iOS arm64 Framework at %s' % arm64_framework + ) + return 1 + + if not os.path.isdir(simulator_x64_framework): + print( + 'Cannot find extension safe iOS x64 simulator Framework at %s' % + simulator_x64_framework + ) + return 1 + + create_framework( + args, dst, framework, arm64_framework, simulator_framework, + simulator_x64_framework, simulator_arm64_framework + ) + return 0 def create_framework( # pylint: disable=too-many-arguments args, dst, framework, arm64_framework, simulator_framework, @@ -173,7 +212,9 @@ def zip_archive(dst): ios_file_with_entitlements = ['gen_snapshot_arm64'] ios_file_without_entitlements = [ 'Flutter.xcframework/ios-arm64/Flutter.framework/Flutter', - 'Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter' + 'Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter', + 'extension_safe/Flutter.xcframework/ios-arm64/Flutter.framework/Flutter', + 'extension_safe/Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter' ] embed_codesign_configuration( os.path.join(dst, 'entitlements.txt'), ios_file_with_entitlements @@ -192,12 +233,19 @@ def zip_archive(dst): 'Flutter.xcframework', 'entitlements.txt', 'without_entitlements.txt', + 'extension_safe/Flutter.xcframework', ], cwd=dst) if os.path.exists(os.path.join(dst, 'Flutter.dSYM')): subprocess.check_call(['zip', '-r', 'Flutter.dSYM.zip', 'Flutter.dSYM'], cwd=dst) + if os.path.exists(os.path.join(dst, 'extension_safe', 'Flutter.dSYM')): + subprocess.check_call([ + 'zip', '-r', 'extension_safe_Flutter.dSYM.zip', 'Flutter.dSYM' + ], + cwd=dst) + def process_framework(args, dst, framework, framework_binary): if args.dsym: diff --git a/sky/tools/create_macos_framework.py b/sky/tools/create_macos_framework.py index c1382527ad4c2..bb78320bf29ac 100755 --- a/sky/tools/create_macos_framework.py +++ b/sky/tools/create_macos_framework.py @@ -81,6 +81,7 @@ def main(): shutil.rmtree(fat_framework, True) shutil.copytree(arm64_framework, fat_framework, symlinks=True) + regenerate_symlinks(fat_framework) fat_framework_binary = os.path.join( @@ -91,6 +92,21 @@ def main(): subprocess.check_call([ 'lipo', arm64_dylib, x64_dylib, '-create', '-output', fat_framework_binary ]) + + # Add group and other readability to all files. + versions_path = os.path.join(fat_framework, 'Versions') + subprocess.check_call(['chmod', '-R', 'og+r', versions_path]) + # Find all the files below the target dir with owner execute permission + find_subprocess = subprocess.Popen([ + 'find', versions_path, '-perm', '-100', '-print0' + ], + stdout=subprocess.PIPE) + # Add execute permission for other and group for all files that had it for owner. + xargs_subprocess = subprocess.Popen(['xargs', '-0', 'chmod', 'og+x'], + stdin=find_subprocess.stdout) + find_subprocess.wait() + xargs_subprocess.wait() + process_framework(dst, args, fat_framework, fat_framework_binary) return 0 diff --git a/sky/tools/objcopy.py b/sky/tools/objcopy.py deleted file mode 100755 index a1fdedd143468..0000000000000 --- a/sky/tools/objcopy.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -# -# 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. - -import argparse -import os -import subprocess -import sys - -# BFD architecture names recognized by objcopy. -BFD_ARCH = { - 'arm': 'arm', - 'arm64': 'aarch64', - 'x86': 'i386', - 'x64': 'i386:x86-64', -} - -# BFD target names recognized by objcopy. -BFD_TARGET = { - 'arm': 'elf32-littlearm', - 'arm64': 'elf64-littleaarch64', - 'x86': 'elf32-i386', - 'x64': 'elf64-x86-64', -} - - -def main(): - parser = argparse.ArgumentParser( - description='Convert a data file to an object file' - ) - parser.add_argument('--objcopy', type=str, required=True) - parser.add_argument('--input', type=str, required=True) - parser.add_argument('--output', type=str, required=True) - parser.add_argument('--arch', type=str, required=True) - - args = parser.parse_args() - - input_dir, input_file = os.path.split(args.input) - output_path = os.path.abspath(args.output) - - subprocess.check_call([ - args.objcopy, - '-I', - 'binary', - '-O', - BFD_TARGET[args.arch], - '-B', - BFD_ARCH[args.arch], - input_file, - output_path, - ], - cwd=input_dir) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/testing/android_background_image/pubspec.yaml b/testing/android_background_image/pubspec.yaml index 82a37fd9f4831..ff08f39de0489 100644 --- a/testing/android_background_image/pubspec.yaml +++ b/testing/android_background_image/pubspec.yaml @@ -5,7 +5,7 @@ name: android_background_image publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/dart/pkg, //third_party/dart/third_party/pkg, or diff --git a/testing/benchmark/pubspec.yaml b/testing/benchmark/pubspec.yaml index dc860ed38d7ca..545d455b0431d 100644 --- a/testing/benchmark/pubspec.yaml +++ b/testing/benchmark/pubspec.yaml @@ -5,7 +5,7 @@ name: flutter_engine_benchmark publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/testing/dart/BUILD.gn b/testing/dart/BUILD.gn index 2e1bb1ad38f67..d9bd962168333 100644 --- a/testing/dart/BUILD.gn +++ b/testing/dart/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/testing/dart/compile_test.gni") +import("//flutter/build/dart/rules.gni") tests = [ "assets_test.dart", @@ -48,10 +48,10 @@ tests = [ ] foreach(test, tests) { - compile_flutter_dart_test("compile_$test") { - dart_file = test - dart_kernel = "$root_gen_dir/$test.dill" - packages = ".dart_tool/package_config.json" + flutter_frontend_server("compile_$test") { + main_dart = test + kernel_output = "$root_gen_dir/$test.dill" + package_config = ".dart_tool/package_config.json" } } diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 716233fa8a783..d317e2f13e420 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -12,6 +12,8 @@ import 'package:litetest/litetest.dart'; import 'package:path/path.dart' as path; import 'package:vector_math/vector_math_64.dart'; +import 'impeller_enabled.dart'; + typedef CanvasCallback = void Function(Canvas canvas); Future createImage(int width, int height) { @@ -192,7 +194,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_toImage.png'); expect(areEqual, true); - }); + }, skip: impellerEnabled); Gradient makeGradient() { return Gradient.linear( @@ -202,28 +204,7 @@ void main() { ); } - test('Simple gradient', () async { - // TODO(matanl): While deprecated, we still don't want to accidentally - // change the behavior of the old API, - // https://github.com/flutter/flutter/issues/112498. - // ignore: deprecated_member_use - Paint.enableDithering = false; - final Image image = await toImage((Canvas canvas) { - final Paint paint = Paint()..shader = makeGradient(); - canvas.drawPaint(paint); - }, 100, 100); - expect(image.width, equals(100)); - expect(image.height, equals(100)); - - final bool areEqual = - await fuzzyGoldenImageCompare(image, 'canvas_test_gradient.png'); - expect(areEqual, true); - }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 - - test('Simple dithered gradient', () async { - // TODO(matanl): Reword this test once we remove the deprecated API. - // ignore: deprecated_member_use - Paint.enableDithering = true; + test('Simple gradient, which is implicitly dithered', () async { final Image image = await toImage((Canvas canvas) { final Paint paint = Paint()..shader = makeGradient(); canvas.drawPaint(paint); @@ -234,7 +215,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_dithered_gradient.png'); expect(areEqual, true); - }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 + }, skip: !Platform.isLinux || impellerEnabled); // https://github.com/flutter/flutter/issues/53784 test('Null values allowed for drawAtlas methods', () async { final Image image = await createImage(100, 100); @@ -370,7 +351,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'dotted_path_effect_mixed_with_stroked_geometry.png'); expect(areEqual, true); - }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 + }, skip: !Platform.isLinux || impellerEnabled); // https://github.com/flutter/flutter/issues/53784 test('Gradients with matrices in Paragraphs render correctly', () async { final Image image = await toImage((Canvas canvas) { @@ -422,7 +403,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'text_with_gradient_with_matrix.png'); expect(areEqual, true); - }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 + }, skip: !Platform.isLinux || impellerEnabled); // https://github.com/flutter/flutter/issues/53784 test('toImageSync - too big', () async { PictureRecorder recorder = PictureRecorder(); @@ -438,6 +419,12 @@ void main() { recorder = PictureRecorder(); canvas = Canvas(recorder); + if (impellerEnabled) { + // Impeller tries to automagically scale this. See + // https://github.com/flutter/flutter/issues/128885 + canvas.drawImage(image, Offset.zero, Paint()); + return; + } // On a slower CI machine, the raster thread may get behind the UI thread // here. However, once the image is in an error state it will immediately // throw on subsequent attempts. diff --git a/testing/dart/codec_test.dart b/testing/dart/codec_test.dart index fb07a63f61a21..702a5fd14e016 100644 --- a/testing/dart/codec_test.dart +++ b/testing/dart/codec_test.dart @@ -9,6 +9,8 @@ import 'dart:ui' as ui; import 'package:litetest/litetest.dart'; import 'package:path/path.dart' as path; +import 'impeller_enabled.dart'; + void main() { test('Animation metadata', () async { @@ -43,7 +45,11 @@ void main() { await codec.getNextFrame(); fail('exception not thrown'); } on Exception catch (e) { - expect(e.toString(), contains('Codec failed')); + if (impellerEnabled) { + expect(e.toString(), contains('Could not decompress image.')); + } else { + expect(e.toString(), contains('Codec failed')); + } } }); @@ -145,8 +151,9 @@ void main() { final ui.Image image = frameInfo.image; final ByteData imageData = (await image.toByteData(format: ui.ImageByteFormat.png))!; + final String fileName = impellerEnabled ? 'impeller_four_frame_with_reuse_end.png' : 'four_frame_with_reuse_end.png'; final Uint8List goldenData = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'four_frame_with_reuse_end.png'), + path.join('flutter', 'lib', 'ui', 'fixtures', fileName), ).readAsBytesSync(); expect(imageData.buffer.asUint8List(), goldenData); @@ -170,8 +177,10 @@ void main() { final ui.Image image = frameInfo.image; final ByteData imageData = (await image.toByteData(format: ui.ImageByteFormat.png))!; + final String fileName = impellerEnabled ? 'impeller_heart_end.png' : 'heart_end.png'; + final Uint8List goldenData = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'heart_end.png'), + path.join('flutter', 'lib', 'ui', 'fixtures', fileName), ).readAsBytesSync(); expect(imageData.buffer.asUint8List(), goldenData); @@ -194,8 +203,10 @@ void main() { final ui.Image image = frameInfo.image; final ByteData imageData = (await image.toByteData(format: ui.ImageByteFormat.png))!; + final String fileName = impellerEnabled ? 'impeller_2_dispose_op_restore_previous.apng.$i.png' : '2_dispose_op_restore_previous.apng.$i.png'; + final Uint8List goldenData = File( - path.join('flutter', 'lib', 'ui', 'fixtures', '2_dispose_op_restore_previous.apng.$i.png'), + path.join('flutter', 'lib', 'ui', 'fixtures', fileName), ).readAsBytesSync(); expect(imageData.buffer.asUint8List(), goldenData); diff --git a/testing/dart/color_filter_test.dart b/testing/dart/color_filter_test.dart index e5553604da9b2..66ec9548ca58f 100644 --- a/testing/dart/color_filter_test.dart +++ b/testing/dart/color_filter_test.dart @@ -7,6 +7,8 @@ import 'dart:ui'; import 'package:litetest/litetest.dart'; +import 'impeller_enabled.dart'; + const Color transparent = Color(0x00000000); const Color red = Color(0xFFAA0000); const Color green = Color(0xFF00AA00); @@ -56,6 +58,11 @@ void main() { Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenRedColorBlend); + // TODO(135699): enable this + if (impellerEnabled) { + return; + } + paint.invertColors = true; bytes = await getBytesForPaint(paint); expect(bytes[0], greenRedColorBlendInverted); @@ -87,6 +94,11 @@ void main() { Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenGreyscaled); + // TODO(135699): enable this + if (impellerEnabled) { + return; + } + paint.invertColors = true; bytes = await getBytesForPaint(paint); expect(bytes[0], greenInvertedGreyscaled); @@ -118,6 +130,10 @@ void main() { Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenLinearToSrgbGamma); + // TODO(135699): enable this + if (impellerEnabled) { + return; + } paint.invertColors = true; bytes = await getBytesForPaint(paint); expect(bytes[0], greenLinearToSrgbGammaInverted); @@ -131,6 +147,11 @@ void main() { Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenSrgbToLinearGamma); + // TODO(135699): enable this + if (impellerEnabled) { + return; + } + paint.invertColors = true; bytes = await getBytesForPaint(paint); expect(bytes[0], greenSrgbToLinearGammaInverted); diff --git a/testing/dart/compile_test.gni b/testing/dart/compile_test.gni deleted file mode 100644 index fbdf76ea849da..0000000000000 --- a/testing/dart/compile_test.gni +++ /dev/null @@ -1,91 +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. - -import("//build/compiled_action.gni") -import("//flutter/common/config.gni") -import("//third_party/dart/build/dart/dart_action.gni") -import("//third_party/dart/sdk_args.gni") - -import("//third_party/dart/build/dart/dart_action.gni") - -# Generates a Dart kernel snapshot using flutter_frontend_server. -# -# Arguments -# dart_main (required): The Main Dart file. -# -# dart_kernel (required): The path to the output kernel snapshot in the out -# directory. -# -# packages (required): The path to the .packages file. -template("compile_flutter_dart_test") { - assert(defined(invoker.dart_file), "The Dart test file must be specified.") - assert(defined(invoker.dart_kernel), - "The Dart Kernel file location must be specified.") - assert(defined(invoker.packages), - "The path to the .packages file must be specified.") - - common_deps = [ - "//flutter/flutter_frontend_server:frontend_server", - "//flutter/lib/snapshot:strong_platform", - ] - if (defined(invoker.deps)) { - common_deps += invoker.deps - } - - snapshot_depfile = - "$root_gen_dir/flutter/testing/snapshot_$target_name.depfile.d" - - common_vm_args = [ "--disable-dart-dev" ] - - flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") - - common_args = [ - "--sound-null-safety", - "--sdk-root", - flutter_patched_sdk, - "--target=flutter", - "--packages", - rebase_path(invoker.packages), - "--depfile", - rebase_path(snapshot_depfile), - "--output-dill", - rebase_path(invoker.dart_kernel, root_out_dir), - rebase_path(invoker.dart_file), - ] - - if (flutter_prebuilt_dart_sdk) { - action(target_name) { - testonly = true - deps = common_deps - pool = "//flutter/build/dart:dart_pool" - script = "//build/gn_run_binary.py" - inputs = [ invoker.dart_file ] - outputs = [ invoker.dart_kernel ] - depfile = snapshot_depfile - - ext = "" - if (is_win) { - ext = ".exe" - } - dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext", root_out_dir) - frontend_server = - rebase_path("$root_gen_dir/frontend_server.dart.snapshot") - - args = [ dart ] + common_vm_args + [ frontend_server ] + common_args - } - } else { - dart_action(target_name) { - testonly = true - deps = common_deps - pool = "//flutter/build/dart:dart_pool" - script = "$root_gen_dir/frontend_server.dart.snapshot" - packages = rebase_path(invoker.packages) - inputs = [ invoker.dart_file ] - outputs = [ invoker.dart_kernel ] - depfile = snapshot_depfile - vm_args = common_vm_args - args = common_args - } - } -} diff --git a/testing/dart/encoding_test.dart b/testing/dart/encoding_test.dart index 9ca46b9306d02..2aa426a108100 100644 --- a/testing/dart/encoding_test.dart +++ b/testing/dart/encoding_test.dart @@ -10,6 +10,8 @@ import 'dart:ui'; import 'package:litetest/litetest.dart'; import 'package:path/path.dart' as path; +import 'impeller_enabled.dart'; + const int _kWidth = 10; const int _kRadius = 2; @@ -18,6 +20,10 @@ const Color _kGreen = Color.fromRGBO(0, 255, 0, 1.0); void main() { test('decodeImageFromPixels float32', () async { + if (impellerEnabled) { + print('Disabled on Impeller - https://github.com/flutter/flutter/issues/135702'); + return; + } const int width = 2; const int height = 2; final Float32List pixels = Float32List(width * height * 4); @@ -91,6 +97,10 @@ void main() { }); test('Image.toByteData Unmodified format works with grayscale images', () async { + if (impellerEnabled) { + print('Disabled on Impeller - https://github.com/flutter/flutter/issues/135706'); + return; + } final Image image = await GrayscaleImage.load(); final ByteData data = (await image.toByteData(format: ImageByteFormat.rawUnmodified))!; final Uint8List bytes = data.buffer.asUint8List(); @@ -99,6 +109,10 @@ void main() { }); test('Image.toByteData PNG format works with simple image', () async { + if (impellerEnabled) { + print('Disabled on Impeller - https://github.com/flutter/flutter/issues/135706'); + return; + } final Image image = await Square4x4Image.image; final ByteData data = (await image.toByteData(format: ImageByteFormat.png))!; final List expected = await readFile('square.png'); diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index e97eb5128f6e5..da34695df7814 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -12,6 +12,7 @@ import 'dart:ui'; import 'package:litetest/litetest.dart'; import 'package:path/path.dart' as path; +import 'impeller_enabled.dart'; import 'shader_test_file_utils.dart'; void main() async { @@ -172,6 +173,10 @@ void main() async { }); test('FragmentShader simple shader renders correctly', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'functions.frag.iplr', ); @@ -182,6 +187,10 @@ void main() async { }); test('Reused FragmentShader simple shader renders correctly', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'functions.frag.iplr', ); @@ -196,6 +205,10 @@ void main() async { }); test('FragmentShader blue-green image renders green', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'blue_green_sampler.frag.iplr', ); @@ -208,6 +221,10 @@ void main() async { }); test('FragmentShader blue-green image renders green - GPU image', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'blue_green_sampler.frag.iplr', ); @@ -220,6 +237,10 @@ void main() async { }); test('FragmentShader with uniforms renders correctly', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'uniforms.frag.iplr', ); @@ -246,6 +267,10 @@ void main() async { }); test('FragmentShader shader with array uniforms renders correctly', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'uniform_arrays.frag.iplr', ); @@ -260,6 +285,10 @@ void main() async { }); test('FragmentShader The ink_sparkle shader is accepted', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'ink_sparkle.frag.iplr', ); @@ -273,6 +302,10 @@ void main() async { }); test('FragmentShader Uniforms are sorted correctly', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'uniforms_sorted.frag.iplr', ); @@ -314,6 +347,10 @@ void main() async { }); test('FragmentShader user defined functions do not redefine builtins', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'no_builtin_redefinition.frag.iplr', ); @@ -324,6 +361,10 @@ void main() async { }); test('FragmentShader fromAsset accepts a shader with no uniforms', () async { + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } final FragmentProgram program = await FragmentProgram.fromAsset( 'no_uniforms.frag.iplr', ); @@ -332,6 +373,11 @@ void main() async { shader.dispose(); }); + if (impellerEnabled) { + print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); + return; + } + // Test all supported GLSL ops. See lib/spirv/lib/src/constants.dart final Map iplrSupportedGLSLOpShaders = await _loadShaderAssets( path.join('supported_glsl_op_shaders', 'iplr'), diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index 0c96b6646dc0b..9d65c8ed4a66e 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -7,6 +7,8 @@ import 'package:litetest/litetest.dart'; import '../../lib/gpu/lib/gpu.dart' as gpu; +import 'impeller_enabled.dart'; + void main() { // TODO(131346): Remove this once we migrate the Dart GPU API into this space. test('smoketest', () async { @@ -25,9 +27,14 @@ void main() { test('gpu.context throws exception for incompatible embedders', () async { try { // ignore: unnecessary_statements - gpu.gpuContext; // Force the - fail('Exception not thrown'); + gpu.gpuContext; // Force the context to instantiate. + if (!impellerEnabled) { + fail('Exception not thrown, but no Impeller context available.'); + } } catch (e) { + if (impellerEnabled) { + fail('Exception thrown even though Impeller is enabled.'); + } expect( e.toString(), contains( diff --git a/testing/dart/image_filter_test.dart b/testing/dart/image_filter_test.dart index 0fa8651656300..0a79513d99430 100644 --- a/testing/dart/image_filter_test.dart +++ b/testing/dart/image_filter_test.dart @@ -7,6 +7,8 @@ import 'dart:ui'; import 'package:litetest/litetest.dart'; +import 'impeller_enabled.dart'; + const Color red = Color(0xFFAA0000); const Color green = Color(0xFF00AA00); @@ -174,6 +176,10 @@ void main() { } test('ImageFilter - blur', () async { + if (impellerEnabled) { + print('Disabled - see https://github.com/flutter/flutter/issues/135712'); + return; + } final Paint paint = Paint() ..color = green ..imageFilter = makeBlur(1.0, 1.0, TileMode.decal); @@ -201,6 +207,11 @@ void main() { }); test('ImageFilter - matrix', () async { + if (impellerEnabled) { + print('Disabled - see https://github.com/flutter/flutter/issues/135712'); + return; + } + final Paint paint = Paint() ..color = green ..imageFilter = makeScale(2.0, 2.0, 1.5, 1.5); @@ -227,6 +238,11 @@ void main() { }); test('ImageFilter - from color filters', () async { + if (impellerEnabled) { + print('Disabled - see https://github.com/flutter/flutter/issues/135712'); + return; + } + final Paint paint = Paint() ..color = green ..imageFilter = const ColorFilter.matrix(constValueColorMatrix); @@ -236,6 +252,11 @@ void main() { }); test('ImageFilter - color filter composition', () async { + if (impellerEnabled) { + print('Disabled - see https://github.com/flutter/flutter/issues/135712'); + return; + } + final ImageFilter compOrder1 = ImageFilter.compose( outer: const ColorFilter.matrix(halvesBrightnessColorMatrix), inner: const ColorFilter.matrix(constValueColorMatrix), diff --git a/testing/dart/impeller_enabled.dart b/testing/dart/impeller_enabled.dart new file mode 100644 index 0000000000000..18c457e3c059f --- /dev/null +++ b/testing/dart/impeller_enabled.dart @@ -0,0 +1,7 @@ +// 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. + +import 'dart:io'; + +bool get impellerEnabled => Platform.executableArguments.contains('--enable-impeller'); diff --git a/testing/dart/observatory/BUILD.gn b/testing/dart/observatory/BUILD.gn index a0e6afbe6be17..35c573a85a4db 100644 --- a/testing/dart/observatory/BUILD.gn +++ b/testing/dart/observatory/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/testing/dart/compile_test.gni") +import("//flutter/build/dart/rules.gni") tests = [ "skp_test.dart", @@ -12,10 +12,10 @@ tests = [ ] foreach(test, tests) { - compile_flutter_dart_test("compile_$test") { - dart_file = test - dart_kernel = "$root_gen_dir/$test.dill" - packages = "../.dart_tool/package_config.json" + flutter_frontend_server("compile_$test") { + main_dart = test + kernel_output = "$root_gen_dir/$test.dill" + package_config = "../.dart_tool/package_config.json" } } diff --git a/testing/dart/observatory/skp_test.dart b/testing/dart/observatory/skp_test.dart index 239e3b529f27a..6c2a1460f9f21 100644 --- a/testing/dart/observatory/skp_test.dart +++ b/testing/dart/observatory/skp_test.dart @@ -12,6 +12,8 @@ import 'package:litetest/litetest.dart'; import 'package:vm_service/vm_service.dart' as vms; import 'package:vm_service/vm_service_io.dart'; +import '../impeller_enabled.dart'; + void main() { test('Capture an SKP ', () async { final developer.ServiceProtocolInfo info = await developer.Service.getInfo(); @@ -42,13 +44,19 @@ void main() { PlatformDispatcher.instance.scheduleFrame(); await completer.future; - final vms.Response response = await vmService.callServiceExtension('_flutter.screenshotSkp'); + try { + final vms.Response response = await vmService.callServiceExtension('_flutter.screenshotSkp'); + expect(impellerEnabled, false); + final String base64data = response.json!['skp'] as String; + expect(base64data, isNotNull); + expect(base64data, isNotEmpty); + final Uint8List decoded = base64Decode(base64data); + expect(decoded.sublist(0, 8), 'skiapict'.codeUnits); + } on vms.RPCError catch (e) { + expect(impellerEnabled, true); + expect(e.toString(), contains('Cannot capture SKP screenshot with Impeller enabled.')); + } - final String base64data = response.json!['skp'] as String; - expect(base64data, isNotNull); - expect(base64data, isNotEmpty); - final Uint8List decoded = base64Decode(base64data); - expect(decoded.sublist(0, 8), 'skiapict'.codeUnits); await vmService.dispose(); }); diff --git a/testing/dart/observatory/tracing_test.dart b/testing/dart/observatory/tracing_test.dart index 1937c9b11df1e..6372a6a159763 100644 --- a/testing/dart/observatory/tracing_test.dart +++ b/testing/dart/observatory/tracing_test.dart @@ -12,6 +12,8 @@ import 'package:vm_service/vm_service.dart' as vms; import 'package:vm_service/vm_service_io.dart'; import 'package:vm_service_protos/vm_service_protos.dart'; +import '../impeller_enabled.dart'; + Future _testChromeFormatTrace(vms.VmService vmService) async { final vms.Timeline timeline = await vmService.getVMTimeline(); @@ -32,7 +34,7 @@ Future _testChromeFormatTrace(vms.VmService vmService) async { } } expect(saveLayerRecordCount, 3); - expect(saveLayerCount, 3); + expect(saveLayerCount, impellerEnabled ? 2 : 3); expect(flowEventCount, 5); } @@ -59,7 +61,7 @@ Future _testPerfettoFormatTrace(vms.VmService vmService) async { } } expect(saveLayerRecordCount, 3); - expect(saveLayerCount, 3); + expect(saveLayerCount, impellerEnabled ? 2 : 3); expect(flowIdCount, 5); } @@ -80,6 +82,7 @@ void main() { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawColor(const Color(0xff0000ff), BlendMode.srcOut); + // Will saveLayer implicitly for Skia, but not Impeller. canvas.drawPaint(Paint()..imageFilter = ImageFilter.blur(sigmaX: 2, sigmaY: 3)); canvas.saveLayer(null, Paint()); canvas.drawRect(const Rect.fromLTRB(10, 10, 20, 20), Paint()); diff --git a/testing/dart/observatory/vmservice_methods_test.dart b/testing/dart/observatory/vmservice_methods_test.dart index fa522b751ed09..14fd20d2afcb1 100644 --- a/testing/dart/observatory/vmservice_methods_test.dart +++ b/testing/dart/observatory/vmservice_methods_test.dart @@ -12,6 +12,8 @@ import 'package:litetest/litetest.dart'; import 'package:vm_service/vm_service.dart' as vms; import 'package:vm_service/vm_service_io.dart'; +import '../impeller_enabled.dart'; + void main() { test('Setting invalid directory returns an error', () async { vms.VmService? vmService; @@ -59,7 +61,7 @@ void main() { 'ext.ui.window.impellerEnabled', isolateId: isolateId, ); - expect(response.json!['enabled'], false); + expect(response.json!['enabled'], impellerEnabled); } finally { await vmService?.dispose(); } diff --git a/testing/dart/platform_view_test.dart b/testing/dart/platform_view_test.dart index 146c865899952..cd581a22d581d 100644 --- a/testing/dart/platform_view_test.dart +++ b/testing/dart/platform_view_test.dart @@ -10,10 +10,12 @@ void main() { test('PlatformView layers do not emit errors from tester', () async { final SceneBuilder builder = SceneBuilder(); builder.addPlatformView(1); - final Scene scene = builder.build(); - - PlatformDispatcher.instance.implicitView!.render(scene); - scene.dispose(); + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { + final Scene scene = builder.build(); + PlatformDispatcher.instance.implicitView!.render(scene); + scene.dispose(); + }; + PlatformDispatcher.instance.scheduleFrame(); // Test harness asserts that this does not emit an error from the shell logs. }); } diff --git a/testing/dart/pubspec.yaml b/testing/dart/pubspec.yaml index 2c2a9a3d6a835..771026fff3627 100644 --- a/testing/dart/pubspec.yaml +++ b/testing/dart/pubspec.yaml @@ -14,7 +14,7 @@ publish_to: none # relative to this directory into //third_party/dart environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: litetest: any diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 3ddeeac6cd92c..1fa3561ddd45e 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -260,7 +260,7 @@ std::ostream& operator<<(std::ostream& os, const DlFilterMode& mode) { } std::ostream& operator<<(std::ostream& os, const DlColor& color) { - return os << "DlColor(" << std::hex << color.argb << std::dec << ")"; + return os << "DlColor(" << std::hex << color.argb() << std::dec << ")"; } std::ostream& operator<<(std::ostream& os, DlImageSampling sampling) { @@ -287,6 +287,17 @@ static std::ostream& operator<<(std::ostream& os, const SkTextBlob* blob) { return os << "&SkTextBlob(ID: " << blob->uniqueID() << ", " << blob->bounds() << ")"; } +static std::ostream& operator<<(std::ostream& os, + const impeller::TextFrame* frame) { + if (frame == nullptr) { + return os << "no text"; + } + auto bounds = frame->GetBounds(); + return os << "&TextFrame(" + << bounds.GetLeft() << ", " << bounds.GetTop() << " => " + << bounds.GetRight() << ", " << bounds.GetBottom() << ")"; +} + std::ostream& operator<<(std::ostream& os, const DlVertexMode& mode) { switch (mode) { case DlVertexMode::kTriangles: return os << "VertexMode::kTriangles"; @@ -860,12 +871,13 @@ void DisplayListStreamDispatcher::drawTextBlob(const sk_sp blob, << x << ", " << y << ");" << std::endl; } -void DisplayListStreamDispatcher::drawTextFrame(const std::shared_ptr& text_frame, - SkScalar x, - SkScalar y) { - startl() << "drawTextFrame(" - << text_frame.get() << ", " - << x << ", " << y << ");" << std::endl; +void DisplayListStreamDispatcher::drawTextFrame( + const std::shared_ptr& text_frame, + SkScalar x, + SkScalar y) { + startl() << "drawTextFrame(" + << text_frame.get() << ", " + << x << ", " << y << ");" << std::endl; } void DisplayListStreamDispatcher::drawShadow(const SkPath& path, diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index a468deda878e2..8604906753deb 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -17,16 +17,16 @@ bool DisplayListsEQ_Verbose(const DisplayList* a, const DisplayList* b); bool inline DisplayListsEQ_Verbose(const DisplayList& a, const DisplayList& b) { return DisplayListsEQ_Verbose(&a, &b); } -bool inline DisplayListsEQ_Verbose(sk_sp a, - sk_sp b) { +bool inline DisplayListsEQ_Verbose(const sk_sp& a, + const sk_sp& b) { return DisplayListsEQ_Verbose(a.get(), b.get()); } bool DisplayListsNE_Verbose(const DisplayList* a, const DisplayList* b); bool inline DisplayListsNE_Verbose(const DisplayList& a, const DisplayList& b) { return DisplayListsNE_Verbose(&a, &b); } -bool inline DisplayListsNE_Verbose(sk_sp a, - sk_sp b) { +bool inline DisplayListsNE_Verbose(const sk_sp& a, + const sk_sp& b) { return DisplayListsNE_Verbose(a.get(), b.get()); } @@ -52,9 +52,9 @@ extern std::ostream& operator<<(std::ostream& os, const DlImage* image); class DisplayListStreamDispatcher final : public DlOpReceiver { public: - DisplayListStreamDispatcher(std::ostream& os, - int cur_indent = 2, - int indent = 2) + explicit DisplayListStreamDispatcher(std::ostream& os, + int cur_indent = 2, + int indent = 2) : os_(os), cur_indent_(cur_indent), indent_(indent) {} void setAntiAlias(bool aa) override; diff --git a/testing/litetest/pubspec.yaml b/testing/litetest/pubspec.yaml index 392ce6411fcd7..ee8f8fd4dda63 100644 --- a/testing/litetest/pubspec.yaml +++ b/testing/litetest/pubspec.yaml @@ -14,7 +14,7 @@ publish_to: none # relative to this directory into //third_party/dart environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: async_helper: any diff --git a/testing/run_tests.py b/testing/run_tests.py index a22579b6e0c17..7f196402c90e7 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -13,12 +13,17 @@ import argparse import errno import glob +import logging +import logging.handlers import multiprocessing import os import re import shutil import subprocess -import sys +# Explicitly import the parts of sys that are needed. This is to avoid using +# sys.stdout and sys.stderr directly. Instead, only the logger defined below +# should be used for output. +from sys import exit as sys_exit, platform as sys_platform import tempfile import time import typing @@ -36,15 +41,22 @@ ROBOTO_FONT_PATH = os.path.join(FONTS_DIR, 'Roboto-Regular.ttf') FONT_SUBSET_DIR = os.path.join(BUILDROOT_DIR, 'flutter', 'tools', 'font-subset') -FML_UNITTESTS_FILTER = '--gtest_filter=-*TimeSensitiveTest*' ENCODING = 'UTF-8' +logger = logging.getLogger(__name__) +logger_handler = logging.StreamHandler() + + +# Override print so that it uses the logger instead of stdout directly. +def print(*args, **kwargs): # pylint: disable=redefined-builtin + logger.info(*args, **kwargs) + def print_divider(char='='): - print('\n') + logger.info('\n') for _ in range(4): - print(''.join([char for _ in range(80)])) - print('\n') + logger.info(''.join([char for _ in range(80)])) + logger.info('\n') def is_asan(build_dir): @@ -71,7 +83,7 @@ def run_cmd( command_string = ' '.join(cmd) print_divider('>') - print(f'Running command "{command_string}"') + logger.info('Running command "%s"', command_string) start_time = time.time() @@ -87,17 +99,17 @@ def run_cmd( for line in iter(process.stdout.readline, ''): output += line - sys.stdout.write(line) + logger.info(line.rstrip()) - sys.stdout.flush() process.wait() end_time = time.time() if process.returncode != 0 and not expect_failure: print_divider('!') - print( - f'Failed Command:\n\n{command_string}\n\nExit Code: {process.returncode}\n' + logger.error( + 'Failed Command:\n\n%s\n\nExit Code: %s\n\nOutput:\n%s', command_string, + process.returncode, output ) print_divider('!') @@ -109,23 +121,26 @@ def run_cmd( if not allowed_failure: raise RuntimeError( - f'Command "{command_string}" exited with code {process.returncode}.' + 'Command "%s" exited with code %s.' % + (command_string, process.returncode) ) for forbidden_string in forbidden_output: if forbidden_string in output: raise RuntimeError( - f'command "{command_string}" contained forbidden string {forbidden_string}' + 'command "%s" contained forbidden string "%s"' % + (command_string, forbidden_string) ) print_divider('<') - print( - f'Command run successfully in {end_time - start_time:.2f} seconds: {command_string}' + logger.info( + 'Command run successfully in %.2f seconds: %s', end_time - start_time, + command_string ) def is_mac(): - return sys.platform == 'darwin' + return sys_platform == 'darwin' def is_aarm64(): @@ -139,11 +154,11 @@ def is_aarm64(): def is_linux(): - return sys.platform.startswith('linux') + return sys_platform.startswith('linux') def is_windows(): - return sys.platform.startswith(('cygwin', 'win')) + return sys_platform.startswith(('cygwin', 'win')) def executable_suffix(): @@ -217,7 +232,7 @@ def run_engine_executable( # pylint: disable=too-many-arguments gtest=False, ): if executable_filter is not None and executable_name not in executable_filter: - print('Skipping %s due to filter.' % executable_name) + logger.info('Skipping %s due to filter.', executable_name) return if flags is None: @@ -250,7 +265,7 @@ def run_engine_executable( # pylint: disable=too-many-arguments else: env['PATH'] = build_dir + ':' + env['PATH'] - print('Running %s in %s' % (executable_name, cwd)) + logger.info('Running %s in %s', executable_name, cwd) test_command = build_engine_executable_command( build_dir, @@ -283,9 +298,9 @@ def run_engine_executable( # pylint: disable=too-many-arguments if luci_test_outputs_path and os.path.exists(core_path) and os.path.exists( unstripped_exe): dump_path = os.path.join( - luci_test_outputs_path, '%s_%s.txt' % (executable_name, sys.platform) + luci_test_outputs_path, '%s_%s.txt' % (executable_name, sys_platform) ) - print('Writing core dump analysis to %s' % dump_path) + logger.error('Writing core dump analysis to %s', dump_path) subprocess.call([ os.path.join( BUILDROOT_DIR, 'flutter', 'testing', 'analyze_core_dump.sh' @@ -356,7 +371,7 @@ def __str__(self): def run_cc_tests(build_dir, executable_filter, coverage, capture_core_dump): - print('Running Engine Unit-tests.') + logger.info('Running Engine Unit-tests.') if capture_core_dump and is_linux(): import resource # pylint: disable=import-outside-toplevel @@ -386,7 +401,7 @@ def make_test(name, flags=None, extra_env=None): make_test('embedder_a11y_unittests'), make_test('embedder_proctable_unittests'), make_test('embedder_unittests'), - make_test('fml_unittests', flags=[FML_UNITTESTS_FILTER] + repeat_flags), + make_test('fml_unittests'), make_test('no_dart_plugin_registrant_unittests'), make_test('runtime_unittests'), make_test('testing_unittests'), @@ -500,17 +515,36 @@ def make_test(name, flags=None, extra_env=None): shuffle_flags + ['--enable_vulkan_validation'], coverage=coverage, extra_env=extra_env, - # TODO(117122): Remove this allowlist. - # https://github.com/flutter/flutter/issues/114872 + # TODO(https://github.com/flutter/flutter/issues/123733): Remove this allowlist. + # See also https://github.com/flutter/flutter/issues/114872. allowed_failure_output=[ '[MTLCompiler createVertexStageAndLinkPipelineWithFragment:', '[MTLCompiler pipelineStateWithVariant:', ] ) + # Run one interactive Vulkan test with validation enabled. + # + # TODO(matanlurey): https://github.com/flutter/flutter/issues/134852; enable + # more of the suite, and ideally we'd like to use Skia gold and take screen + # shots as well. + run_engine_executable( + build_dir, + 'impeller_unittests', + executable_filter, + shuffle_flags + [ + '--enable_vulkan_validation', + '--enable_playground', + '--playground_timeout_ms=4000', + '--gtest_filter="*ColorWheel/Vulkan"', + ], + coverage=coverage, + extra_env=extra_env, + ) + def run_engine_benchmarks(build_dir, executable_filter): - print('Running Engine Benchmarks.') + logger.info('Running Engine Benchmarks.') icu_flags = [ '--icu-data-file-path=%s' % os.path.join(build_dir, 'icudtl.dat') @@ -542,13 +576,37 @@ def run_engine_benchmarks(build_dir, executable_filter): ) -def gather_dart_test( - build_dir, - dart_file, - multithreaded, - enable_observatory=False, - expect_failure=False, -): +class FlutterTesterOptions(): + + def __init__( + self, + multithreaded=False, + enable_impeller=False, + enable_observatory=False, + expect_failure=False + ): + self.multithreaded = multithreaded + self.enable_impeller = enable_impeller + self.enable_observatory = enable_observatory + self.expect_failure = expect_failure + + def apply_args(self, command_args): + if not self.enable_observatory: + command_args.append('--disable-observatory') + + if self.enable_impeller: + command_args += ['--enable-impeller'] + + if self.multithreaded: + command_args.insert(0, '--force-multithreading') + + def threading_description(self): + if self.multithreaded: + return 'multithreaded' + return 'single-threaded' + + +def gather_dart_test(build_dir, dart_file, options): kernel_file_name = os.path.basename(dart_file) + '.dill' kernel_file_output = os.path.join(build_dir, 'gen', kernel_file_name) error_message = "%s doesn't exist. Please run the build that populates %s" % ( @@ -557,8 +615,8 @@ def gather_dart_test( assert os.path.isfile(kernel_file_output), error_message command_args = [] - if not enable_observatory: - command_args.append('--disable-observatory') + + options.apply_args(command_args) dart_file_contents = open(dart_file, 'r') custom_options = re.findall( @@ -576,18 +634,12 @@ def gather_dart_test( kernel_file_output, ] - if multithreaded: - threading = 'multithreaded' - command_args.insert(0, '--force-multithreading') - else: - threading = 'single-threaded' - tester_name = 'flutter_tester' - print( - "Running test '%s' using '%s' (%s)" % - (kernel_file_name, tester_name, threading) + logger.info( + "Running test '%s' using '%s' (%s)", kernel_file_name, tester_name, + options.threading_description() ) - forbidden_output = [] if 'unopt' in build_dir or expect_failure else [ + forbidden_output = [] if 'unopt' in build_dir or options.expect_failure else [ '[ERROR' ] return EngineExecutableTask( @@ -596,7 +648,7 @@ def gather_dart_test( None, command_args, forbidden_output=forbidden_output, - expect_failure=expect_failure, + expect_failure=options.expect_failure, ) @@ -810,101 +862,123 @@ def gather_dart_tests(build_dir, test_filter): for dart_test_file in dart_observatory_tests: if test_filter is not None and os.path.basename(dart_test_file ) not in test_filter: - print("Skipping '%s' due to filter." % dart_test_file) + logger.info("Skipping '%s' due to filter.", dart_test_file) else: - print( - "Gathering dart test '%s' with observatory enabled" % dart_test_file + logger.info( + "Gathering dart test '%s' with observatory enabled", dart_test_file ) - yield gather_dart_test(build_dir, dart_test_file, True, True) - yield gather_dart_test(build_dir, dart_test_file, False, True) + for multithreaded in [False, True]: + for enable_impeller in [False, True]: + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions( + multithreaded=multithreaded, + enable_impeller=enable_impeller, + enable_observatory=True + ) + ) for dart_test_file in dart_tests: if test_filter is not None and os.path.basename(dart_test_file ) not in test_filter: - print("Skipping '%s' due to filter." % dart_test_file) + logger.info("Skipping '%s' due to filter.", dart_test_file) else: - print("Gathering dart test '%s'" % dart_test_file) - yield gather_dart_test(build_dir, dart_test_file, True) - yield gather_dart_test(build_dir, dart_test_file, False) + logger.info("Gathering dart test '%s'", dart_test_file) + for multithreaded in [False, True]: + for enable_impeller in [False, True]: + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions( + multithreaded=multithreaded, enable_impeller=enable_impeller + ) + ) -def gather_dart_smoke_test(build_dir): +def gather_dart_smoke_test(build_dir, test_filter): smoke_test = os.path.join( - BUILDROOT_DIR, 'flutter', 'testing', 'smoke_test_failure', - 'fail_test.dart' + BUILDROOT_DIR, + 'flutter', + 'testing', + 'smoke_test_failure', + 'fail_test.dart', ) - yield gather_dart_test(build_dir, smoke_test, True, expect_failure=True) - yield gather_dart_test(build_dir, smoke_test, False, expect_failure=True) + if test_filter is not None and os.path.basename(smoke_test + ) not in test_filter: + logger.info("Skipping '%s' due to filter.", smoke_test) + else: + yield gather_dart_test( + build_dir, smoke_test, + FlutterTesterOptions(multithreaded=True, expect_failure=True) + ) + yield gather_dart_test( + build_dir, smoke_test, + FlutterTesterOptions(multithreaded=False, expect_failure=True) + ) -def gather_front_end_server_tests(build_dir): - test_dir = os.path.join(BUILDROOT_DIR, 'flutter', 'flutter_frontend_server') - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) +def gather_dart_package_tests(build_dir, package_path, extra_opts): + dart_tests = glob.glob('%s/test/*_test.dart' % package_path) + if not dart_tests: + raise Exception('No tests found for Dart package at %s' % package_path) for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', dart_test_file, build_dir, - os.path.join(build_dir, 'gen', 'frontend_server.dart.snapshot'), - os.path.join(build_dir, 'flutter_patched_sdk') - ] + opts = ['--disable-dart-dev', dart_test_file] + extra_opts yield EngineExecutableTask( build_dir, os.path.join('dart-sdk', 'bin', 'dart'), None, flags=opts, - cwd=test_dir + cwd=package_path ) -def gather_path_ops_tests(build_dir): - # TODO(dnfield): https://github.com/flutter/flutter/issues/107321 - if is_asan(build_dir): - return - - test_dir = os.path.join( - BUILDROOT_DIR, 'flutter', 'tools', 'path_ops', 'dart', 'test' - ) - opts = ['--disable-dart-dev', os.path.join(test_dir, 'path_ops_test.dart')] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_const_finder_tests(build_dir): - test_dir = os.path.join( - BUILDROOT_DIR, 'flutter', 'tools', 'const_finder', 'test' - ) - opts = [ - '--disable-dart-dev', - os.path.join(test_dir, 'const_finder_test.dart'), - os.path.join(build_dir, 'gen', 'frontend_server.dart.snapshot'), - os.path.join(build_dir, 'flutter_patched_sdk'), - os.path.join(build_dir, 'dart-sdk', 'lib', 'libraries.json') +# Returns a list of Dart packages to test. +# +# The first element of each tuple in the returned list is the path to the Dart +# package to test. It is assumed that the packages follow the convention that +# tests are named as '*_test.dart', and reside under a directory called 'test'. +# +# The second element of each tuple is a list of additional command line +# arguments to pass to each of the packages tests. +def build_dart_host_test_list(build_dir): + dart_host_tests = [ + ( + os.path.join('flutter', 'ci'), + [os.path.join(BUILDROOT_DIR, 'flutter')], + ), + ( + os.path.join('flutter', 'flutter_frontend_server'), + [ + build_dir, + os.path.join(build_dir, 'gen', 'frontend_server.dart.snapshot'), + os.path.join(build_dir, 'flutter_patched_sdk') + ], + ), + (os.path.join('flutter', 'testing', 'litetest'), []), + ( + os.path.join('flutter', 'tools', 'api_check'), + [os.path.join(BUILDROOT_DIR, 'flutter')], + ), + (os.path.join('flutter', 'tools', 'build_bucket_golden_scraper'), []), + (os.path.join('flutter', 'tools', 'clang_tidy'), []), + ( + os.path.join('flutter', 'tools', 'const_finder'), + [ + os.path.join(build_dir, 'gen', 'frontend_server.dart.snapshot'), + os.path.join(build_dir, 'flutter_patched_sdk'), + os.path.join(build_dir, 'dart-sdk', 'lib', 'libraries.json'), + ], + ), + (os.path.join('flutter', 'tools', 'githooks'), []), + (os.path.join('flutter', 'tools', 'pkg', 'engine_build_configs'), []), + (os.path.join('flutter', 'tools', 'pkg', 'engine_repo_tools'), []), + (os.path.join('flutter', 'tools', 'pkg', 'git_repo_tools'), []), ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - + if not is_asan(build_dir): + dart_host_tests += [ + (os.path.join('flutter', 'tools', 'path_ops', 'dart'), []), + ] -def gather_litetest_tests(build_dir): - test_dir = os.path.join(BUILDROOT_DIR, 'flutter', 'testing', 'litetest') - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = ['--disable-dart-dev', dart_test_file] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) + return dart_host_tests def run_benchmark_tests(build_dir): @@ -921,136 +995,11 @@ def run_benchmark_tests(build_dir): ) -def gather_githooks_tests(build_dir): - test_dir = os.path.join(BUILDROOT_DIR, 'flutter', 'tools', 'githooks') - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = ['--disable-dart-dev', dart_test_file] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_clang_tidy_tests(build_dir): - test_dir = os.path.join(BUILDROOT_DIR, 'flutter', 'tools', 'clang_tidy') - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', dart_test_file, - os.path.join(build_dir, 'compile_commands.json'), - os.path.join(BUILDROOT_DIR, 'flutter') - ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_build_bucket_golden_scraper_tests(build_dir): - test_dir = os.path.join( - BUILDROOT_DIR, 'flutter', 'tools', 'build_bucket_golden_scraper' - ) - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', - dart_test_file, - ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_engine_build_configs_tests(build_dir): - test_dir = os.path.join( - BUILDROOT_DIR, 'flutter', 'tools', 'pkg', 'engine_build_configs' - ) - dart_tests = glob.glob('%s/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', - dart_test_file, - ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_engine_repo_tools_tests(build_dir): - test_dir = os.path.join( - BUILDROOT_DIR, 'flutter', 'tools', 'pkg', 'engine_repo_tools' - ) - dart_tests = glob.glob('%s/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', - dart_test_file, - ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_api_consistency_tests(build_dir): - test_dir = os.path.join(BUILDROOT_DIR, 'flutter', 'tools', 'api_check') - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) - for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', dart_test_file, - os.path.join(BUILDROOT_DIR, 'flutter') - ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) - - -def gather_ci_tests(build_dir): - test_dir = os.path.join(BUILDROOT_DIR, 'flutter', 'ci') - dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) - - run_engine_executable( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=['pub', 'get', '--offline'], - cwd=test_dir, - ) - - for dart_test_file in dart_tests: - opts = [ - '--disable-dart-dev', dart_test_file, - os.path.join(BUILDROOT_DIR, 'flutter') - ] - yield EngineExecutableTask( - build_dir, - os.path.join('dart-sdk', 'bin', 'dart'), - None, - flags=opts, - cwd=test_dir - ) +def worker_init(queue, level): + queue_handler = logging.handlers.QueueHandler(queue) + log = logging.getLogger(__name__) + log.setLevel(level) + log.addHandler(queue_handler) def run_engine_tasks_in_parallel(tasks): @@ -1064,23 +1013,33 @@ def run_engine_tasks_in_parallel(tasks): # # See: https://bugs.python.org/issue26903 max_processes = multiprocessing.cpu_count() - if sys.platform.startswith(('cygwin', 'win')) and max_processes > 60: + if sys_platform.startswith(('cygwin', 'win')) and max_processes > 60: max_processes = 60 - pool = multiprocessing.Pool(processes=max_processes) - async_results = [(t, pool.apply_async(t, ())) for t in tasks] + queue = multiprocessing.Queue() + queue_listener = logging.handlers.QueueListener(queue, logger_handler) + queue_listener.start() + failures = [] - for task, async_result in async_results: - try: - async_result.get() - except Exception as exn: # pylint: disable=broad-except - failures += [(task, exn)] + try: + with multiprocessing.Pool(max_processes, worker_init, + [queue, logger.getEffectiveLevel()]) as pool: + async_results = [(t, pool.apply_async(t, ())) for t in tasks] + for task, async_result in async_results: + try: + async_result.get() + except Exception as exn: # pylint: disable=broad-except + failures += [(task, exn)] + finally: + queue_listener.stop() if len(failures) > 0: - print('The following commands failed:') + logger.error('The following commands failed:') for task, exn in failures: - print('%s\n%s\n' % (str(task), str(exn))) - raise Exception() + logger.error('%s\n', str(task)) + return False + + return True class DirectoryChange(): @@ -1133,6 +1092,7 @@ def main(): all_types = [ 'engine', 'dart', + 'dart-host', 'benchmarks', 'java', 'android', @@ -1165,7 +1125,13 @@ def main(): '--dart-filter', type=str, default='', - help='A list of Dart test scripts to run.' + help='A list of Dart test scripts to run in flutter_tester.' + ) + parser.add_argument( + '--dart-host-filter', + type=str, + default='', + help='A list of Dart test scripts to run with the Dart CLI.' ) parser.add_argument( '--java-filter', @@ -1230,9 +1196,20 @@ def main(): default=None, help='Provide the path of adb used for android tests. By default it looks on $PATH.' ) + parser.add_argument( + '--quiet', + dest='quiet', + action='store_true', + default=False, + help='Only emit output when there is an error.' + ) args = parser.parse_args() + logger.addHandler(logger_handler) + if not args.quiet: + logger.setLevel(logging.INFO) + if args.type == 'all': types = all_types else: @@ -1259,6 +1236,8 @@ def main(): os.environ[key] = value process.communicate() # Avoid pipe deadlock while waiting for termination. + success = True + engine_filter = args.engine_filter.split(',') if args.engine_filter else None if 'engine' in types: run_cc_tests( @@ -1283,27 +1262,34 @@ def main(): if 'dart' in types: dart_filter = args.dart_filter.split(',') if args.dart_filter else None - tasks = list(gather_dart_smoke_test(build_dir)) - tasks += list(gather_litetest_tests(build_dir)) - tasks += list(gather_githooks_tests(build_dir)) - tasks += list(gather_clang_tidy_tests(build_dir)) - tasks += list(gather_build_bucket_golden_scraper_tests(build_dir)) - tasks += list(gather_engine_build_configs_tests(build_dir)) - tasks += list(gather_engine_repo_tools_tests(build_dir)) - tasks += list(gather_api_consistency_tests(build_dir)) - tasks += list(gather_path_ops_tests(build_dir)) - tasks += list(gather_const_finder_tests(build_dir)) - tasks += list(gather_front_end_server_tests(build_dir)) - tasks += list(gather_ci_tests(build_dir)) + tasks = list(gather_dart_smoke_test(build_dir, dart_filter)) tasks += list(gather_dart_tests(build_dir, dart_filter)) - run_engine_tasks_in_parallel(tasks) + success = success and run_engine_tasks_in_parallel(tasks) + + if 'dart-host' in types: + dart_filter = args.dart_host_filter.split( + ',' + ) if args.dart_host_filter else None + dart_host_packages = build_dart_host_test_list(build_dir) + tasks = [] + for dart_host_package, extra_opts in dart_host_packages: + if dart_filter is None or dart_host_package in dart_filter: + tasks += list( + gather_dart_package_tests( + build_dir, + os.path.join(BUILDROOT_DIR, dart_host_package), + extra_opts, + ) + ) + + success = success and run_engine_tasks_in_parallel(tasks) if 'java' in types: assert not is_windows( ), "Android engine files can't be compiled on Windows." java_filter = args.java_filter if ',' in java_filter or '*' in java_filter: - print( + logger.wraning( 'Can only filter JUnit4 tests by single entire class name, ' 'eg "io.flutter.SmokeTest". Ignoring filter=' + java_filter ) @@ -1332,6 +1318,8 @@ def main(): if 'impeller-golden' in types: run_impeller_golden_tests(build_dir) + return 0 if success else 1 + if __name__ == '__main__': - sys.exit(main()) + sys_exit(main()) diff --git a/testing/scenario_app/android/app/gradle.lockfile b/testing/scenario_app/android/app/gradle.lockfile index 630350a3efe1b..e083af965d3fb 100644 --- a/testing/scenario_app/android/app/gradle.lockfile +++ b/testing/scenario_app/android/app/gradle.lockfile @@ -125,7 +125,7 @@ jline:jline:2.14.6=lintClassPath junit:junit:4.12=androidTestImplementationDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAndroidTestRuntimeClasspath junit:junit:4.13.1=lintClassPath net.java.dev.jna:jna-platform:5.6.0=lintClassPath -net.java.dev.jna:jna:5.6.0=lintClassPath +net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,lintClassPath net.sf.jopt-simple:jopt-simple:4.9=lintClassPath net.sf.kxml:kxml2:2.3.0=androidTestImplementationDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAndroidTestRuntimeClasspath,lintClassPath org.apache.ant:ant-antlr:1.10.9=lintClassPath @@ -168,29 +168,27 @@ org.glassfish.jaxb:txw2:2.3.2=lintClassPath org.hamcrest:hamcrest-core:1.3=androidTestImplementationDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAndroidTestRuntimeClasspath,lintClassPath org.hamcrest:hamcrest-integration:1.3=androidTestImplementationDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAndroidTestRuntimeClasspath org.hamcrest:hamcrest-library:1.3=androidTestImplementationDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAndroidTestRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.3=androidJacocoAnt -org.jacoco:org.jacoco.ant:0.8.3=androidJacocoAnt -org.jacoco:org.jacoco.core:0.8.3=androidJacocoAnt -org.jacoco:org.jacoco.report:0.8.3=androidJacocoAnt +org.jacoco:org.jacoco.agent:0.8.8=androidJacocoAnt +org.jacoco:org.jacoco.ant:0.8.8=androidJacocoAnt +org.jacoco:org.jacoco.core:0.8.8=androidJacocoAnt +org.jacoco:org.jacoco.report:0.8.8=androidJacocoAnt org.jetbrains.intellij.deps:trove4j:1.0.20181211=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,lintClassPath -org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.30=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-daemon-embeddable:1.5.30=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.5.30=kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.6.10=kotlinKlibCommonizerClasspath org.jetbrains.kotlin:kotlin-reflect:1.4.32=lintClassPath -org.jetbrains.kotlin:kotlin-reflect:1.5.30=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-script-runtime:1.5.30=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-script-runtime:1.6.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21=androidTestImplementationDependenciesMetadata,debugAndroidTestImplementationDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32=lintClassPath -org.jetbrains.kotlin:kotlin-stdlib-common:1.5.30=apiDependenciesMetadata,debugApiDependenciesMetadata,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseApiDependenciesMetadata -org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10=apiDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugApiDependenciesMetadata,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseApiDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32=lintClassPath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30=apiDependenciesMetadata,debugAndroidTestCompileClasspath,debugApiDependenciesMetadata,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseApiDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10=apiDependenciesMetadata,debugAndroidTestCompileClasspath,debugApiDependenciesMetadata,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseApiDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32=lintClassPath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30=apiDependenciesMetadata,debugAndroidTestCompileClasspath,debugApiDependenciesMetadata,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseApiDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10=apiDependenciesMetadata,debugAndroidTestCompileClasspath,debugApiDependenciesMetadata,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseApiDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jetbrains.kotlin:kotlin-stdlib:1.4.21=androidTestImplementationDependenciesMetadata,debugAndroidTestImplementationDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib:1.4.32=lintClassPath -org.jetbrains.kotlin:kotlin-stdlib:1.5.30=apiDependenciesMetadata,debugApiDependenciesMetadata,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseApiDependenciesMetadata -org.jetbrains.kotlin:kotlin-stdlib:1.5.31=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:1.6.10=apiDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugApiDependenciesMetadata,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseApiDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath @@ -203,12 +201,16 @@ org.junit.platform:junit-platform-launcher:1.7.0=lintClassPath org.junit:junit-bom:5.7.0=lintClassPath org.jvnet.staxex:stax-ex:1.8.1=lintClassPath org.opentest4j:opentest4j:1.2.0=lintClassPath -org.ow2.asm:asm-analysis:7.0=androidJacocoAnt,lintClassPath -org.ow2.asm:asm-commons:7.0=androidJacocoAnt,lintClassPath -org.ow2.asm:asm-tree:7.0=androidJacocoAnt,lintClassPath +org.ow2.asm:asm-analysis:7.0=lintClassPath +org.ow2.asm:asm-analysis:9.2=androidJacocoAnt +org.ow2.asm:asm-commons:7.0=lintClassPath +org.ow2.asm:asm-commons:9.2=androidJacocoAnt +org.ow2.asm:asm-tree:7.0=lintClassPath +org.ow2.asm:asm-tree:9.2=androidJacocoAnt org.ow2.asm:asm-util:7.0=lintClassPath -org.ow2.asm:asm:7.0=androidJacocoAnt,lintClassPath +org.ow2.asm:asm:7.0=lintClassPath +org.ow2.asm:asm:9.2=androidJacocoAnt org.testng:testng:7.3.0=lintClassPath xerces:xercesImpl:2.12.0=lintClassPath xml-apis:xml-apis:1.4.01=lintClassPath -empty=androidApis,androidJdkImage,androidTestApiDependenciesMetadata,androidTestCompileOnlyDependenciesMetadata,androidTestDebugApiDependenciesMetadata,androidTestDebugCompileOnlyDependenciesMetadata,androidTestDebugImplementationDependenciesMetadata,androidTestDebugIntransitiveDependenciesMetadata,androidTestDebugRuntimeOnlyDependenciesMetadata,androidTestIntransitiveDependenciesMetadata,androidTestReleaseApiDependenciesMetadata,androidTestReleaseCompileOnlyDependenciesMetadata,androidTestReleaseImplementationDependenciesMetadata,androidTestReleaseIntransitiveDependenciesMetadata,androidTestReleaseRuntimeOnlyDependenciesMetadata,androidTestRuntimeOnlyDependenciesMetadata,androidTestUtil,compileOnlyDependenciesMetadata,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAndroidTestApiDependenciesMetadata,debugAndroidTestCompileOnlyDependenciesMetadata,debugAndroidTestIntransitiveDependenciesMetadata,debugAndroidTestRuntimeOnlyDependenciesMetadata,debugAnnotationProcessorClasspath,debugCompileOnly,debugCompileOnlyDependenciesMetadata,debugIntransitiveDependenciesMetadata,debugReverseMetadataValues,debugRuntimeOnlyDependenciesMetadata,debugUnitTestAnnotationProcessorClasspath,debugUnitTestApiDependenciesMetadata,debugUnitTestCompileOnlyDependenciesMetadata,debugUnitTestImplementationDependenciesMetadata,debugUnitTestIntransitiveDependenciesMetadata,debugUnitTestRuntimeOnlyDependenciesMetadata,debugWearBundling,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseUnitTest,kotlinNativeCompilerPluginClasspath,lintChecks,lintPublish,releaseAnnotationProcessorClasspath,releaseCompileOnly,releaseCompileOnlyDependenciesMetadata,releaseIntransitiveDependenciesMetadata,releaseReverseMetadataValues,releaseRuntimeOnlyDependenciesMetadata,releaseUnitTestAnnotationProcessorClasspath,releaseUnitTestApiDependenciesMetadata,releaseUnitTestCompileOnlyDependenciesMetadata,releaseUnitTestImplementationDependenciesMetadata,releaseUnitTestIntransitiveDependenciesMetadata,releaseUnitTestRuntimeOnlyDependenciesMetadata,releaseWearBundling,runtimeOnlyDependenciesMetadata,testApiDependenciesMetadata,testCompileOnlyDependenciesMetadata,testDebugApiDependenciesMetadata,testDebugCompileOnlyDependenciesMetadata,testDebugImplementationDependenciesMetadata,testDebugIntransitiveDependenciesMetadata,testDebugRuntimeOnlyDependenciesMetadata,testImplementationDependenciesMetadata,testIntransitiveDependenciesMetadata,testReleaseApiDependenciesMetadata,testReleaseCompileOnlyDependenciesMetadata,testReleaseImplementationDependenciesMetadata,testReleaseIntransitiveDependenciesMetadata,testReleaseRuntimeOnlyDependenciesMetadata,testRuntimeOnlyDependenciesMetadata +empty=androidApis,androidJdkImage,androidTestApiDependenciesMetadata,androidTestCompileOnlyDependenciesMetadata,androidTestDebugApiDependenciesMetadata,androidTestDebugCompileOnlyDependenciesMetadata,androidTestDebugImplementationDependenciesMetadata,androidTestDebugIntransitiveDependenciesMetadata,androidTestDebugRuntimeOnlyDependenciesMetadata,androidTestIntransitiveDependenciesMetadata,androidTestReleaseApiDependenciesMetadata,androidTestReleaseCompileOnlyDependenciesMetadata,androidTestReleaseImplementationDependenciesMetadata,androidTestReleaseIntransitiveDependenciesMetadata,androidTestReleaseRuntimeOnlyDependenciesMetadata,androidTestRuntimeOnlyDependenciesMetadata,androidTestUtil,compileOnlyDependenciesMetadata,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAndroidTestApiDependenciesMetadata,debugAndroidTestCompileOnlyDependenciesMetadata,debugAndroidTestIntransitiveDependenciesMetadata,debugAndroidTestRuntimeOnlyDependenciesMetadata,debugAnnotationProcessorClasspath,debugCompileOnly,debugCompileOnlyDependenciesMetadata,debugIntransitiveDependenciesMetadata,debugReverseMetadataValues,debugRuntimeOnlyDependenciesMetadata,debugUnitTestAnnotationProcessorClasspath,debugUnitTestApiDependenciesMetadata,debugUnitTestCompileOnlyDependenciesMetadata,debugUnitTestImplementationDependenciesMetadata,debugUnitTestIntransitiveDependenciesMetadata,debugUnitTestRuntimeOnlyDependenciesMetadata,debugWearBundling,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseUnitTest,kotlinNativeCompilerPluginClasspath,lintChecks,lintPublish,releaseAnnotationProcessorClasspath,releaseCompileOnly,releaseCompileOnlyDependenciesMetadata,releaseIntransitiveDependenciesMetadata,releaseReverseMetadataValues,releaseRuntimeOnlyDependenciesMetadata,releaseUnitTestAnnotationProcessorClasspath,releaseUnitTestApiDependenciesMetadata,releaseUnitTestCompileOnlyDependenciesMetadata,releaseUnitTestImplementationDependenciesMetadata,releaseUnitTestIntransitiveDependenciesMetadata,releaseUnitTestRuntimeOnlyDependenciesMetadata,releaseWearBundling,runtimeOnlyDependenciesMetadata,testApiDependenciesMetadata,testCompileOnlyDependenciesMetadata,testDebugApiDependenciesMetadata,testDebugCompileOnlyDependenciesMetadata,testDebugImplementationDependenciesMetadata,testDebugIntransitiveDependenciesMetadata,testDebugRuntimeOnlyDependenciesMetadata,testFixturesApiDependenciesMetadata,testFixturesCompileOnlyDependenciesMetadata,testFixturesDebugApiDependenciesMetadata,testFixturesDebugCompileOnlyDependenciesMetadata,testFixturesDebugImplementationDependenciesMetadata,testFixturesDebugIntransitiveDependenciesMetadata,testFixturesDebugRuntimeOnlyDependenciesMetadata,testFixturesImplementationDependenciesMetadata,testFixturesIntransitiveDependenciesMetadata,testFixturesReleaseApiDependenciesMetadata,testFixturesReleaseCompileOnlyDependenciesMetadata,testFixturesReleaseImplementationDependenciesMetadata,testFixturesReleaseIntransitiveDependenciesMetadata,testFixturesReleaseRuntimeOnlyDependenciesMetadata,testFixturesRuntimeOnlyDependenciesMetadata,testImplementationDependenciesMetadata,testIntransitiveDependenciesMetadata,testReleaseApiDependenciesMetadata,testReleaseCompileOnlyDependenciesMetadata,testReleaseImplementationDependenciesMetadata,testReleaseIntransitiveDependenciesMetadata,testReleaseRuntimeOnlyDependenciesMetadata,testRuntimeOnlyDependenciesMetadata diff --git a/testing/scenario_app/android/app/src/main/AndroidManifest.xml b/testing/scenario_app/android/app/src/main/AndroidManifest.xml index 96ec901014b28..e125978054d8d 100644 --- a/testing/scenario_app/android/app/src/main/AndroidManifest.xml +++ b/testing/scenario_app/android/app/src/main/AndroidManifest.xml @@ -1,13 +1,16 @@ + android:theme="@style/AppTheme" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/extraction_config_11_and_below" + tools:ignore="UnusedAttribute"> { + AssetFileDescriptor afd = null; try { - final FileDescriptor fd = - getContentResolver().openAssetFileDescriptor(logFile, "w").getFileDescriptor(); + afd = getContentResolver().openAssetFileDescriptor(logFile, "w"); + final FileDescriptor fd = afd.getFileDescriptor(); final FileOutputStream outputStream = new FileOutputStream(fd); outputStream.write(reply.array()); outputStream.close(); } catch (IOException ex) { Log.e(TAG, "Could not write timeline file", ex); + } finally { + try { + if (afd != null) { + afd.close(); + } + } catch (IOException e) { + Log.w(TAG, "Could not close", e); + } } finish(); }); diff --git a/testing/scenario_app/android/app/src/main/res/xml/data_extraction_rules.xml b/testing/scenario_app/android/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000000000..9ec01e6da1e70 --- /dev/null +++ b/testing/scenario_app/android/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/testing/scenario_app/android/app/src/main/res/xml/extraction_config_11_and_below.xml b/testing/scenario_app/android/app/src/main/res/xml/extraction_config_11_and_below.xml new file mode 100644 index 0000000000000..ff293d0205569 --- /dev/null +++ b/testing/scenario_app/android/app/src/main/res/xml/extraction_config_11_and_below.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/testing/scenario_app/android/build.gradle b/testing/scenario_app/android/build.gradle index 007f63430020b..165461c763928 100644 --- a/testing/scenario_app/android/build.gradle +++ b/testing/scenario_app/android/build.gradle @@ -6,11 +6,16 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' classpath 'com.facebook.testing.screenshot:plugin:0.12.0' - // leakcanary uses Kotlin. This app does not, but the plugin is - // needed to specify language version options. - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30' + // leakcanary uses Kotlin. This app does not, but the plugin is + // needed to specify language version options. + // Gradle 8.0 requires minimum kotlin 1.6.10 + // https://docs.gradle.org/current/userguide/upgrading_version_7.html#legacy_incrementaltaskinputs_api + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10' + // Requries Minimum AGP 7.3. + // https://docs.gradle.org/current/userguide/upgrading_version_7.html#legacy_incrementaltaskinputs_api + classpath 'com.android.tools.build:gradle:7.4.2' + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/testing/scenario_app/android/buildscript-gradle.lockfile b/testing/scenario_app/android/buildscript-gradle.lockfile index 8a0282f655ecd..dc10f2efd340e 100644 --- a/testing/scenario_app/android/buildscript-gradle.lockfile +++ b/testing/scenario_app/android/buildscript-gradle.lockfile @@ -1,146 +1,149 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -androidx.databinding:databinding-common:7.0.0=classpath -androidx.databinding:databinding-compiler-common:7.0.0=classpath -com.android.databinding:baseLibrary:7.0.0=classpath -com.android.tools.analytics-library:crash:30.0.0=classpath -com.android.tools.analytics-library:protos:30.0.0=classpath -com.android.tools.analytics-library:shared:30.0.0=classpath -com.android.tools.analytics-library:tracker:30.0.0=classpath -com.android.tools.build.jetifier:jetifier-core:1.0.0-beta09=classpath -com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta09=classpath -com.android.tools.build:aapt2-proto:7.0.0-7396180=classpath -com.android.tools.build:aaptcompiler:7.0.0=classpath -com.android.tools.build:apksig:7.0.0=classpath -com.android.tools.build:apkzlib:7.0.0=classpath -com.android.tools.build:builder-model:7.0.0=classpath -com.android.tools.build:builder-test-api:7.0.0=classpath -com.android.tools.build:builder:7.0.0=classpath -com.android.tools.build:bundletool:1.6.0=classpath -com.android.tools.build:gradle-api:7.0.0=classpath -com.android.tools.build:gradle:7.0.0=classpath -com.android.tools.build:manifest-merger:30.0.0=classpath +androidx.databinding:databinding-common:7.4.2=classpath +androidx.databinding:databinding-compiler-common:7.4.2=classpath +com.android.databinding:baseLibrary:7.4.2=classpath +com.android.tools.analytics-library:crash:30.4.2=classpath +com.android.tools.analytics-library:protos:30.4.2=classpath +com.android.tools.analytics-library:shared:30.4.2=classpath +com.android.tools.analytics-library:tracker:30.4.2=classpath +com.android.tools.build.jetifier:jetifier-core:1.0.0-beta10=classpath +com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta10=classpath +com.android.tools.build:aapt2-proto:7.4.2-8841542=classpath +com.android.tools.build:aaptcompiler:7.4.2=classpath +com.android.tools.build:apksig:7.4.2=classpath +com.android.tools.build:apkzlib:7.4.2=classpath +com.android.tools.build:builder-model:7.4.2=classpath +com.android.tools.build:builder-test-api:7.4.2=classpath +com.android.tools.build:builder:7.4.2=classpath +com.android.tools.build:bundletool:1.11.4=classpath +com.android.tools.build:gradle-api:7.4.2=classpath +com.android.tools.build:gradle-settings-api:7.4.2=classpath +com.android.tools.build:gradle:7.4.2=classpath +com.android.tools.build:manifest-merger:30.4.2=classpath com.android.tools.build:transform-api:2.0.0-deprecated-use-gradle-api=classpath -com.android.tools.ddms:ddmlib:30.0.0=classpath -com.android.tools.layoutlib:layoutlib-api:30.0.0=classpath -com.android.tools.lint:lint-model:30.0.0=classpath -com.android.tools.utp:android-device-provider-gradle-proto:30.0.0=classpath -com.android.tools.utp:android-test-plugin-host-retention-proto:30.0.0=classpath -com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:30.0.0=classpath -com.android.tools:annotations:30.0.0=classpath -com.android.tools:common:30.0.0=classpath -com.android.tools:dvlib:30.0.0=classpath -com.android.tools:repository:30.0.0=classpath -com.android.tools:sdk-common:30.0.0=classpath -com.android.tools:sdklib:30.0.0=classpath -com.android:signflinger:7.0.0=classpath -com.android:zipflinger:7.0.0=classpath +com.android.tools.ddms:ddmlib:30.4.2=classpath +com.android.tools.layoutlib:layoutlib-api:30.4.2=classpath +com.android.tools.lint:lint-model:30.4.2=classpath +com.android.tools.lint:lint-typedef-remover:30.4.2=classpath +com.android.tools.utp:android-device-provider-ddmlib-proto:30.4.2=classpath +com.android.tools.utp:android-device-provider-gradle-proto:30.4.2=classpath +com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:30.4.2=classpath +com.android.tools.utp:android-test-plugin-host-coverage-proto:30.4.2=classpath +com.android.tools.utp:android-test-plugin-host-retention-proto:30.4.2=classpath +com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:30.4.2=classpath +com.android.tools:annotations:30.4.2=classpath +com.android.tools:common:30.4.2=classpath +com.android.tools:dvlib:30.4.2=classpath +com.android.tools:repository:30.4.2=classpath +com.android.tools:sdk-common:30.4.2=classpath +com.android.tools:sdklib:30.4.2=classpath +com.android:signflinger:7.4.2=classpath +com.android:zipflinger:7.4.2=classpath com.facebook.testing.screenshot:plugin:0.12.0=classpath com.github.gundy:semver4j:0.16.4=classpath com.google.android:annotations:4.1.1.4=classpath -com.google.api.grpc:proto-google-common-protos:1.12.0=classpath +com.google.api.grpc:proto-google-common-protos:2.0.1=classpath com.google.auto.value:auto-value-annotations:1.6.2=classpath com.google.code.findbugs:jsr305:3.0.2=classpath -com.google.code.gson:gson:2.8.6=classpath +com.google.code.gson:gson:2.8.9=classpath com.google.crypto.tink:tink:1.3.0-rc2=classpath com.google.dagger:dagger:2.28.3=classpath -com.google.errorprone:error_prone_annotations:2.3.4=classpath +com.google.errorprone:error_prone_annotations:2.4.0=classpath com.google.flatbuffers:flatbuffers-java:1.12.0=classpath com.google.guava:failureaccess:1.0.1=classpath com.google.guava:guava:30.1-jre=classpath com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=classpath com.google.j2objc:j2objc-annotations:1.3=classpath com.google.jimfs:jimfs:1.1=classpath -com.google.protobuf:protobuf-java-util:3.10.0=classpath -com.google.protobuf:protobuf-java:3.10.0=classpath -com.google.testing.platform:core-proto:0.0.8-alpha04=classpath -com.googlecode.json-simple:json-simple:1.1=classpath +com.google.protobuf:protobuf-java-util:3.17.2=classpath +com.google.protobuf:protobuf-java:3.17.2=classpath +com.google.testing.platform:core-proto:0.0.8-alpha08=classpath com.googlecode.juniversalchardet:juniversalchardet:1.0.3=classpath com.squareup:javapoet:1.10.0=classpath com.squareup:javawriter:2.5.0=classpath com.sun.activation:javax.activation:1.2.0=classpath com.sun.istack:istack-commons-runtime:3.0.8=classpath com.sun.xml.fastinfoset:FastInfoset:1.2.16=classpath -commons-codec:commons-codec:1.10=classpath +commons-codec:commons-codec:1.11=classpath commons-io:commons-io:2.4=classpath commons-logging:commons-logging:1.2=classpath de.undercouch:gradle-download-task:4.1.1=classpath -io.grpc:grpc-api:1.21.1=classpath -io.grpc:grpc-context:1.21.1=classpath -io.grpc:grpc-core:1.21.1=classpath -io.grpc:grpc-netty:1.21.1=classpath -io.grpc:grpc-protobuf-lite:1.21.1=classpath -io.grpc:grpc-protobuf:1.21.1=classpath -io.grpc:grpc-stub:1.21.1=classpath -io.netty:netty-buffer:4.1.34.Final=classpath -io.netty:netty-codec-http2:4.1.34.Final=classpath -io.netty:netty-codec-http:4.1.34.Final=classpath -io.netty:netty-codec-socks:4.1.34.Final=classpath -io.netty:netty-codec:4.1.34.Final=classpath -io.netty:netty-common:4.1.34.Final=classpath -io.netty:netty-handler-proxy:4.1.34.Final=classpath -io.netty:netty-handler:4.1.34.Final=classpath -io.netty:netty-resolver:4.1.34.Final=classpath -io.netty:netty-transport:4.1.34.Final=classpath -io.opencensus:opencensus-api:0.21.0=classpath -io.opencensus:opencensus-contrib-grpc-metrics:0.21.0=classpath -it.unimi.dsi:fastutil:8.4.0=classpath +io.grpc:grpc-api:1.39.0=classpath +io.grpc:grpc-context:1.39.0=classpath +io.grpc:grpc-core:1.39.0=classpath +io.grpc:grpc-netty:1.39.0=classpath +io.grpc:grpc-protobuf-lite:1.39.0=classpath +io.grpc:grpc-protobuf:1.39.0=classpath +io.grpc:grpc-stub:1.39.0=classpath +io.netty:netty-buffer:4.1.52.Final=classpath +io.netty:netty-codec-http2:4.1.52.Final=classpath +io.netty:netty-codec-http:4.1.52.Final=classpath +io.netty:netty-codec-socks:4.1.52.Final=classpath +io.netty:netty-codec:4.1.52.Final=classpath +io.netty:netty-common:4.1.52.Final=classpath +io.netty:netty-handler-proxy:4.1.52.Final=classpath +io.netty:netty-handler:4.1.52.Final=classpath +io.netty:netty-resolver:4.1.52.Final=classpath +io.netty:netty-transport:4.1.52.Final=classpath +io.perfmark:perfmark-api:0.23.0=classpath jakarta.activation:jakarta.activation-api:1.2.1=classpath jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=classpath +javax.annotation:javax.annotation-api:1.3.2=classpath javax.inject:javax.inject:1=classpath net.java.dev.jna:jna-platform:5.6.0=classpath net.java.dev.jna:jna:5.6.0=classpath net.sf.jopt-simple:jopt-simple:4.9=classpath net.sf.kxml:kxml2:2.3.0=classpath -org.antlr:antlr4:4.5.3=classpath org.apache.commons:commons-compress:1.20=classpath -org.apache.httpcomponents:httpclient:4.5.6=classpath -org.apache.httpcomponents:httpcore:4.4.10=classpath +org.apache.httpcomponents:httpclient:4.5.13=classpath +org.apache.httpcomponents:httpcore:4.4.13=classpath org.apache.httpcomponents:httpmime:4.5.6=classpath -org.bouncycastle:bcpkix-jdk15on:1.56=classpath -org.bouncycastle:bcprov-jdk15on:1.56=classpath +org.bitbucket.b_c:jose4j:0.7.0=classpath +org.bouncycastle:bcpkix-jdk15on:1.67=classpath +org.bouncycastle:bcprov-jdk15on:1.67=classpath org.checkerframework:checker-qual:3.5.0=classpath -org.codehaus.mojo:animal-sniffer-annotations:1.17=classpath +org.codehaus.mojo:animal-sniffer-annotations:1.19=classpath org.glassfish.jaxb:jaxb-runtime:2.3.2=classpath org.glassfish.jaxb:txw2:2.3.2=classpath org.jdom:jdom2:2.0.6=classpath -org.jetbrains.intellij.deps:trove4j:1.0.20181211=classpath -org.jetbrains.kotlin:kotlin-android-extensions:1.5.30=classpath -org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.5.30=classpath -org.jetbrains.kotlin:kotlin-build-common:1.5.30=classpath -org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.30=classpath -org.jetbrains.kotlin:kotlin-compiler-runner:1.5.30=classpath -org.jetbrains.kotlin:kotlin-daemon-client:1.5.30=classpath -org.jetbrains.kotlin:kotlin-daemon-embeddable:1.5.30=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.5.30=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.5.30=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30=classpath -org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.5.30=classpath -org.jetbrains.kotlin:kotlin-native-utils:1.5.30=classpath -org.jetbrains.kotlin:kotlin-project-model:1.5.30=classpath -org.jetbrains.kotlin:kotlin-reflect:1.4.32=classpath -org.jetbrains.kotlin:kotlin-scripting-common:1.5.30=classpath -org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.5.30=classpath -org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.5.30=classpath -org.jetbrains.kotlin:kotlin-scripting-jvm:1.5.30=classpath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32=classpath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32=classpath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32=classpath -org.jetbrains.kotlin:kotlin-stdlib:1.4.32=classpath -org.jetbrains.kotlin:kotlin-tooling-metadata:1.5.30=classpath -org.jetbrains.kotlin:kotlin-util-io:1.5.30=classpath -org.jetbrains.kotlin:kotlin-util-klib:1.5.30=classpath +org.jetbrains.intellij.deps:trove4j:1.0.20200330=classpath +org.jetbrains.kotlin:kotlin-android-extensions:1.6.10=classpath +org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.6.10=classpath +org.jetbrains.kotlin:kotlin-build-common:1.6.10=classpath +org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.10=classpath +org.jetbrains.kotlin:kotlin-compiler-runner:1.6.10=classpath +org.jetbrains.kotlin:kotlin-daemon-client:1.6.10=classpath +org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.6.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10=classpath +org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.6.10=classpath +org.jetbrains.kotlin:kotlin-native-utils:1.6.10=classpath +org.jetbrains.kotlin:kotlin-project-model:1.6.10=classpath +org.jetbrains.kotlin:kotlin-reflect:1.7.10=classpath +org.jetbrains.kotlin:kotlin-scripting-common:1.6.10=classpath +org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.6.10=classpath +org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.6.10=classpath +org.jetbrains.kotlin:kotlin-scripting-jvm:1.6.10=classpath +org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=classpath +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10=classpath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=classpath +org.jetbrains.kotlin:kotlin-stdlib:1.7.10=classpath +org.jetbrains.kotlin:kotlin-tooling-metadata:1.6.10=classpath +org.jetbrains.kotlin:kotlin-util-io:1.6.10=classpath +org.jetbrains.kotlin:kotlin-util-klib:1.6.10=classpath org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=classpath -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0=classpath org.jetbrains:annotations:13.0=classpath org.json:json:20180813=classpath org.jvnet.staxex:stax-ex:1.8.1=classpath -org.ow2.asm:asm-analysis:7.0=classpath -org.ow2.asm:asm-commons:7.0=classpath -org.ow2.asm:asm-tree:7.0=classpath -org.ow2.asm:asm-util:7.0=classpath -org.ow2.asm:asm:7.0=classpath +org.ow2.asm:asm-analysis:9.2=classpath +org.ow2.asm:asm-commons:9.2=classpath +org.ow2.asm:asm-tree:9.2=classpath +org.ow2.asm:asm-util:9.2=classpath +org.ow2.asm:asm:9.2=classpath +org.slf4j:slf4j-api:1.7.30=classpath org.tensorflow:tensorflow-lite-metadata:0.1.0-rc2=classpath xerces:xercesImpl:2.12.0=classpath xml-apis:xml-apis:1.4.01=classpath diff --git a/testing/scenario_app/bin/utils/logs.dart b/testing/scenario_app/bin/utils/logs.dart index 41800342d193f..0c7c8a6873bf3 100644 --- a/testing/scenario_app/bin/utils/logs.dart +++ b/testing/scenario_app/bin/utils/logs.dart @@ -10,7 +10,7 @@ String _red = _supportsAnsi ? '\u001b[31m' : ''; String _gray = _supportsAnsi ? '\u001b[90m' : ''; String _reset = _supportsAnsi? '\u001B[0m' : ''; -Future step(String msg, Function() fn) async { +Future step(String msg, Future Function() fn) async { stdout.writeln('-> $_green$msg$_reset'); try { await fn(); diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.pbxproj b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000..b67c72aabae64 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.pbxproj @@ -0,0 +1,372 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 686382C62ABE173000E27AAD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 686382C52ABE173000E27AAD /* AppDelegate.m */; }; + 686382C92ABE173000E27AAD /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 686382C82ABE173000E27AAD /* SceneDelegate.m */; }; + 686382CC2ABE173000E27AAD /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 686382CB2ABE173000E27AAD /* ViewController.m */; }; + 686382CF2ABE173000E27AAD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 686382CD2ABE173000E27AAD /* Main.storyboard */; }; + 686382D12ABE173000E27AAD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 686382D02ABE173000E27AAD /* Assets.xcassets */; }; + 686382D42ABE173000E27AAD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 686382D22ABE173000E27AAD /* LaunchScreen.storyboard */; }; + 686382D72ABE173000E27AAD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 686382D62ABE173000E27AAD /* main.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 686382C12ABE172F00E27AAD /* FlutterAppExtensionTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlutterAppExtensionTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 686382C42ABE173000E27AAD /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 686382C52ABE173000E27AAD /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 686382C72ABE173000E27AAD /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 686382C82ABE173000E27AAD /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + 686382CA2ABE173000E27AAD /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 686382CB2ABE173000E27AAD /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 686382CE2ABE173000E27AAD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 686382D02ABE173000E27AAD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 686382D32ABE173000E27AAD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 686382D52ABE173000E27AAD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 686382D62ABE173000E27AAD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 686382BE2ABE172F00E27AAD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 686382B82ABE172F00E27AAD = { + isa = PBXGroup; + children = ( + 686382C32ABE173000E27AAD /* FlutterAppExtensionTestHost */, + 686382C22ABE172F00E27AAD /* Products */, + ); + sourceTree = ""; + }; + 686382C22ABE172F00E27AAD /* Products */ = { + isa = PBXGroup; + children = ( + 686382C12ABE172F00E27AAD /* FlutterAppExtensionTestHost.app */, + ); + name = Products; + sourceTree = ""; + }; + 686382C32ABE173000E27AAD /* FlutterAppExtensionTestHost */ = { + isa = PBXGroup; + children = ( + 686382C42ABE173000E27AAD /* AppDelegate.h */, + 686382C52ABE173000E27AAD /* AppDelegate.m */, + 686382C72ABE173000E27AAD /* SceneDelegate.h */, + 686382C82ABE173000E27AAD /* SceneDelegate.m */, + 686382CA2ABE173000E27AAD /* ViewController.h */, + 686382CB2ABE173000E27AAD /* ViewController.m */, + 686382CD2ABE173000E27AAD /* Main.storyboard */, + 686382D02ABE173000E27AAD /* Assets.xcassets */, + 686382D22ABE173000E27AAD /* LaunchScreen.storyboard */, + 686382D52ABE173000E27AAD /* Info.plist */, + 686382D62ABE173000E27AAD /* main.m */, + ); + path = FlutterAppExtensionTestHost; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 686382C02ABE172F00E27AAD /* FlutterAppExtensionTestHost */ = { + isa = PBXNativeTarget; + buildConfigurationList = 686382DA2ABE173000E27AAD /* Build configuration list for PBXNativeTarget "FlutterAppExtensionTestHost" */; + buildPhases = ( + 686382BD2ABE172F00E27AAD /* Sources */, + 686382BE2ABE172F00E27AAD /* Frameworks */, + 686382BF2ABE172F00E27AAD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FlutterAppExtensionTestHost; + productName = FlutterAppExtensionTestHost; + productReference = 686382C12ABE172F00E27AAD /* FlutterAppExtensionTestHost.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 686382B92ABE172F00E27AAD /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1500; + TargetAttributes = { + 686382C02ABE172F00E27AAD = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = 686382BC2ABE172F00E27AAD /* Build configuration list for PBXProject "FlutterAppExtensionTestHost" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 686382B82ABE172F00E27AAD; + productRefGroup = 686382C22ABE172F00E27AAD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 686382C02ABE172F00E27AAD /* FlutterAppExtensionTestHost */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 686382BF2ABE172F00E27AAD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 686382D42ABE173000E27AAD /* LaunchScreen.storyboard in Resources */, + 686382D12ABE173000E27AAD /* Assets.xcassets in Resources */, + 686382CF2ABE173000E27AAD /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 686382BD2ABE172F00E27AAD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 686382CC2ABE173000E27AAD /* ViewController.m in Sources */, + 686382C62ABE173000E27AAD /* AppDelegate.m in Sources */, + 686382D72ABE173000E27AAD /* main.m in Sources */, + 686382C92ABE173000E27AAD /* SceneDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 686382CD2ABE173000E27AAD /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 686382CE2ABE173000E27AAD /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 686382D22ABE173000E27AAD /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 686382D32ABE173000E27AAD /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 686382D82ABE173000E27AAD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 686382D92ABE173000E27AAD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 686382DB2ABE173000E27AAD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FlutterAppExtensionTestHost/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.FlutterAppExtensionTestHost; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 686382DC2ABE173000E27AAD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FlutterAppExtensionTestHost/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.FlutterAppExtensionTestHost; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 686382BC2ABE172F00E27AAD /* Build configuration list for PBXProject "FlutterAppExtensionTestHost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 686382D82ABE173000E27AAD /* Debug */, + 686382D92ABE173000E27AAD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 686382DA2ABE173000E27AAD /* Build configuration list for PBXNativeTarget "FlutterAppExtensionTestHost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 686382DB2ABE173000E27AAD /* Debug */, + 686382DC2ABE173000E27AAD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 686382B92ABE172F00E27AAD /* Project object */; +} diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000..919434a6254f0 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000..18d981003d68d --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/AppDelegate.h b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/AppDelegate.h new file mode 100644 index 0000000000000..99c576ec95059 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/AppDelegate.h @@ -0,0 +1,9 @@ +// 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. + +#import + +@interface AppDelegate : UIResponder + +@end diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/AppDelegate.m b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/AppDelegate.m new file mode 100644 index 0000000000000..1beee74b03e03 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/AppDelegate.m @@ -0,0 +1,38 @@ +// 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. + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + // Override point for customization after application launch. + return YES; +} + +#pragma mark - UISceneSession lifecycle + +- (UISceneConfiguration*)application:(UIApplication*)application + configurationForConnectingSceneSession:(UISceneSession*)connectingSceneSession + options:(UISceneConnectionOptions*)options { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" + sessionRole:connectingSceneSession.role]; +} + +- (void)application:(UIApplication*)application + didDiscardSceneSessions:(NSSet*)sceneSessions { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called + // shortly after application:didFinishLaunchingWithOptions. Use this method to release any + // resources that were specific to the discarded scenes, as they will not return. +} + +@end diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/AccentColor.colorset/Contents.json b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000..eb87897008164 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000..13613e3ee1a93 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/Contents.json b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Base.lproj/LaunchScreen.storyboard b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000000..865e9329f3767 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Base.lproj/Main.storyboard b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Base.lproj/Main.storyboard new file mode 100644 index 0000000000000..808a21ce779ba --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Info.plist b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Info.plist new file mode 100644 index 0000000000000..81ed29b76cfbe --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/Info.plist @@ -0,0 +1,25 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/SceneDelegate.h b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/SceneDelegate.h new file mode 100644 index 0000000000000..6adc5d3a2cd7f --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/SceneDelegate.h @@ -0,0 +1,11 @@ +// 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. + +#import + +@interface SceneDelegate : UIResponder + +@property(strong, nonatomic) UIWindow* window; + +@end diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/SceneDelegate.m b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/SceneDelegate.m new file mode 100644 index 0000000000000..1271efb7faa5f --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/SceneDelegate.m @@ -0,0 +1,52 @@ +// 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. + +#import "SceneDelegate.h" + +@interface SceneDelegate () + +@end + +@implementation SceneDelegate + +- (void)scene:(UIScene*)scene + willConnectToSession:(UISceneSession*)session + options:(UISceneConnectionOptions*)connectionOptions { + // Use this method to optionally configure and attach the UIWindow `window` to the provided + // UIWindowScene `scene`. If using a storyboard, the `window` property will automatically be + // initialized and attached to the scene. This delegate does not imply the connecting scene or + // session are new (see `application:configurationForConnectingSceneSession` instead). +} + +- (void)sceneDidDisconnect:(UIScene*)scene { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene + // connects. The scene may re-connect later, as its session was not necessarily discarded (see + // `application:didDiscardSceneSessions` instead). +} + +- (void)sceneDidBecomeActive:(UIScene*)scene { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was + // inactive. +} + +- (void)sceneWillResignActive:(UIScene*)scene { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). +} + +- (void)sceneWillEnterForeground:(UIScene*)scene { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. +} + +- (void)sceneDidEnterBackground:(UIScene*)scene { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state + // information to restore the scene back to its current state. +} + +@end diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/ViewController.h b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/ViewController.h new file mode 100644 index 0000000000000..4f235259bfecc --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/ViewController.h @@ -0,0 +1,9 @@ +// 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. + +#import + +@interface ViewController : UIViewController + +@end diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/ViewController.m b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/ViewController.m new file mode 100644 index 0000000000000..b725cc412618f --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/ViewController.m @@ -0,0 +1,33 @@ +// 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. + +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + UIButton* openShare = + [UIButton systemButtonWithPrimaryAction:[UIAction actionWithHandler:^( + __kindof UIAction* _Nonnull action) { + UIActivityViewController* activityVC = + [[UIActivityViewController alloc] initWithActivityItems:@[ @"text to share" ] + applicationActivities:nil]; + activityVC.excludedActivityTypes = @[ + UIActivityTypePrint, UIActivityTypeCopyToPasteboard, + UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll + ]; // Exclude whichever aren't relevant + [self presentViewController:activityVC animated:YES completion:nil]; + }]]; + openShare.backgroundColor = [UIColor systemPinkColor]; + [openShare setTitle:@"Open Share" forState:UIControlStateNormal]; + [self.view addSubview:openShare]; + openShare.frame = CGRectMake(0, 0, 200, 200); +} + +@end diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/main.m b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/main.m new file mode 100644 index 0000000000000..f98f92491e8a0 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/FlutterAppExtensionTestHost/main.m @@ -0,0 +1,15 @@ +// 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. + +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + NSString* appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/testing/scenario_app/ios/FlutterAppExtensionTestHost/Scenarios/Info_Impeller.plist b/testing/scenario_app/ios/FlutterAppExtensionTestHost/Scenarios/Info_Impeller.plist new file mode 100644 index 0000000000000..2ad0401d1c878 --- /dev/null +++ b/testing/scenario_app/ios/FlutterAppExtensionTestHost/Scenarios/Info_Impeller.plist @@ -0,0 +1,29 @@ + + + + + + + FLTEnableImpeller + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/testing/scenario_app/ios/Scenarios.xcworkspace/contents.xcworkspacedata b/testing/scenario_app/ios/Scenarios.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000..00ca3de9d76c0 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/testing/scenario_app/ios/Scenarios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/testing/scenario_app/ios/Scenarios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000..18d981003d68d --- /dev/null +++ b/testing/scenario_app/ios/Scenarios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/testing/scenario_app/ios/Scenarios.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/testing/scenario_app/ios/Scenarios.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000000..0c67376ebacb4 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index ec69054a447da..4c0f0713c2ddf 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -62,7 +62,15 @@ 6860CE252A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6860CE222A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png */; }; 6860CE262A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6860CE232A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png */; }; 6860CE272A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6860CE242A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png */; }; + 686382EC2AC1F9F300E27AAD /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 686382EB2AC1F9F300E27AAD /* ShareViewController.m */; }; + 686382EF2AC1F9F300E27AAD /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 686382ED2AC1F9F300E27AAD /* MainInterface.storyboard */; }; + 686382F32AC1F9F300E27AAD /* ScenariosShare.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 686382E82AC1F9F300E27AAD /* ScenariosShare.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 686383132AC202B700E27AAD /* AppExtensionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686383122AC202B700E27AAD /* AppExtensionTests.m */; }; + 686383152AC2175100E27AAD /* ../../Flutter.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* ../../Flutter.xcframework */; }; + 686383162AC2175100E27AAD /* ../../Flutter.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* ../../Flutter.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; }; + 68C9D8012AD9B0EF00DF9D79 /* DarwinSystemFontTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68C9D8002AD9B0EF00DF9D79 /* DarwinSystemFontTests.m */; }; + 68C9D8092AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 68C9D8082AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png */; }; 68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; }; 68D93AEE2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 68D93AED2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */; }; F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */; }; @@ -83,6 +91,27 @@ remoteGlobalIDString = 248D76C622E388370012F0C1; remoteInfo = Scenarios; }; + 686382F12AC1F9F300E27AAD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 248D76BF22E388370012F0C1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 686382E72AC1F9F300E27AAD; + remoteInfo = ScenariosShare; + }; + 6863830A2AC2024200E27AAD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 686383052AC2024200E27AAD /* FlutterAppExtensionTestHost.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 686382C12ABE172F00E27AAD; + remoteInfo = FlutterAppExtensionTestHost; + }; + 686383102AC2027100E27AAD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 686383052AC2024200E27AAD /* FlutterAppExtensionTestHost.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 686382C02ABE172F00E27AAD; + remoteInfo = FlutterAppExtensionTestHost; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -120,6 +149,28 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 686382F42AC1F9F300E27AAD /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 686382F32AC1F9F300E27AAD /* ScenariosShare.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 686383172AC2175100E27AAD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 686383162AC2175100E27AAD /* ../../Flutter.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -185,7 +236,16 @@ 6860CE222A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 6860CE232A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 6860CE242A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; + 686382E82AC1F9F300E27AAD /* ScenariosShare.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ScenariosShare.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 686382EA2AC1F9F300E27AAD /* ShareViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShareViewController.h; sourceTree = ""; }; + 686382EB2AC1F9F300E27AAD /* ShareViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShareViewController.m; sourceTree = ""; }; + 686382EE2AC1F9F300E27AAD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 686382F02AC1F9F300E27AAD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 686383052AC2024200E27AAD /* FlutterAppExtensionTestHost.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FlutterAppExtensionTestHost.xcodeproj; path = ../FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj; sourceTree = ""; }; + 686383122AC202B700E27AAD /* AppExtensionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppExtensionTests.m; sourceTree = ""; }; 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = ""; }; + 68C9D8002AD9B0EF00DF9D79 /* DarwinSystemFontTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DarwinSystemFontTests.m; sourceTree = ""; }; + 68C9D8082AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = ""; }; 68D4017C2564859300ECD91A /* ContinuousTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContinuousTexture.m; sourceTree = ""; }; 68D93AED2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; @@ -219,15 +279,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 686382E52AC1F9F300E27AAD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 686383152AC2175100E27AAD /* ../../Flutter.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 248D76BE22E388370012F0C1 = { isa = PBXGroup; children = ( + 686383052AC2024200E27AAD /* FlutterAppExtensionTestHost.xcodeproj */, 248D76C922E388370012F0C1 /* Scenarios */, 248D76E222E388380012F0C1 /* ScenariosTests */, 248D76ED22E388380012F0C1 /* ScenariosUITests */, + 686382E92AC1F9F300E27AAD /* ScenariosShare */, 248D76C822E388370012F0C1 /* Products */, 248D76FC22E388900012F0C1 /* Frameworks */, ); @@ -239,6 +309,7 @@ 248D76C722E388370012F0C1 /* Scenarios.app */, 248D76DF22E388380012F0C1 /* ScenariosTests.xctest */, 248D76EA22E388380012F0C1 /* ScenariosUITests.xctest */, + 686382E82AC1F9F300E27AAD /* ScenariosShare.appex */, ); name = Products; sourceTree = ""; @@ -298,6 +369,8 @@ 246A6610252E693A00EAB0F3 /* RenderingSelectionTest.m */, 0DDEBC88258830B40065D0E8 /* SpawnEngineTest.m */, F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */, + 686383122AC202B700E27AAD /* AppExtensionTests.m */, + 68C9D8002AD9B0EF00DF9D79 /* DarwinSystemFontTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -311,9 +384,29 @@ name = Frameworks; sourceTree = ""; }; + 686382E92AC1F9F300E27AAD /* ScenariosShare */ = { + isa = PBXGroup; + children = ( + 686382EA2AC1F9F300E27AAD /* ShareViewController.h */, + 686382EB2AC1F9F300E27AAD /* ShareViewController.m */, + 686382ED2AC1F9F300E27AAD /* MainInterface.storyboard */, + 686382F02AC1F9F300E27AAD /* Info.plist */, + ); + path = ScenariosShare; + sourceTree = ""; + }; + 686383062AC2024200E27AAD /* Products */ = { + isa = PBXGroup; + children = ( + 6863830B2AC2024200E27AAD /* FlutterAppExtensionTestHost.app */, + ); + name = Products; + sourceTree = ""; + }; F7B464DC2759D02B00079189 /* Goldens */ = { isa = PBXGroup; children = ( + 68C9D8082AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png */, 3BFD971E2A990CF40094F51B /* golden_bogus_font_text_impeller_iPhone SE (3rd generation)_16.2_simulator.png */, 3BFD971F2A990CF40094F51B /* golden_spawn_engine_works_impeller_iPhone SE (3rd generation)_16.2_simulator.png */, 68D93AED2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */, @@ -355,10 +448,12 @@ 248D76C422E388370012F0C1 /* Frameworks */, 248D76C522E388370012F0C1 /* Resources */, 246B4E4422E3B5F700073EBF /* Embed Frameworks */, + 686382F42AC1F9F300E27AAD /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 686382F22AC1F9F300E27AAD /* PBXTargetDependency */, ); name = Scenarios; productName = Scenarios; @@ -396,6 +491,7 @@ buildRules = ( ); dependencies = ( + 686383112AC2027100E27AAD /* PBXTargetDependency */, 248D76EC22E388380012F0C1 /* PBXTargetDependency */, ); name = ScenariosUITests; @@ -403,6 +499,24 @@ productReference = 248D76EA22E388380012F0C1 /* ScenariosUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + 686382E72AC1F9F300E27AAD /* ScenariosShare */ = { + isa = PBXNativeTarget; + buildConfigurationList = 686382F72AC1F9F300E27AAD /* Build configuration list for PBXNativeTarget "ScenariosShare" */; + buildPhases = ( + 686382E42AC1F9F300E27AAD /* Sources */, + 686382E52AC1F9F300E27AAD /* Frameworks */, + 686382E62AC1F9F300E27AAD /* Resources */, + 686383172AC2175100E27AAD /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ScenariosShare; + productName = ScenariosShare; + productReference = 686382E82AC1F9F300E27AAD /* ScenariosShare.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -426,6 +540,9 @@ LastSwiftMigration = 1030; TestTargetID = 248D76C622E388370012F0C1; }; + 686382E72AC1F9F300E27AAD = { + CreatedOnToolsVersion = 15.0; + }; }; }; buildConfigurationList = 248D76C222E388370012F0C1 /* Build configuration list for PBXProject "Scenarios" */; @@ -439,15 +556,32 @@ mainGroup = 248D76BE22E388370012F0C1; productRefGroup = 248D76C822E388370012F0C1 /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 686383062AC2024200E27AAD /* Products */; + ProjectRef = 686383052AC2024200E27AAD /* FlutterAppExtensionTestHost.xcodeproj */; + }, + ); projectRoot = ""; targets = ( 248D76C622E388370012F0C1 /* Scenarios */, 248D76DE22E388380012F0C1 /* ScenariosTests */, 248D76E922E388380012F0C1 /* ScenariosUITests */, + 686382E72AC1F9F300E27AAD /* ScenariosShare */, ); }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + 6863830B2AC2024200E27AAD /* FlutterAppExtensionTestHost.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = FlutterAppExtensionTestHost.app; + remoteRef = 6863830A2AC2024200E27AAD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ 248D76C522E388370012F0C1 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -489,6 +623,7 @@ 684FFF8729F9C10700281002 /* golden_platform_view_multiple_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 68D93AEE2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 3BFD97202A990CF50094F51B /* golden_bogus_font_text_impeller_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, + 68C9D8092AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8D29F9C10700281002 /* golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8329F9C10700281002 /* golden_platform_view_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8B29F9C10700281002 /* golden_platform_view_clippath_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, @@ -497,6 +632,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 686382E62AC1F9F300E27AAD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 686382EF2AC1F9F300E27AAD /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -530,6 +673,7 @@ files = ( 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, + 68C9D8012AD9B0EF00DF9D79 /* DarwinSystemFontTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 0A02E8F724EFAD27002D54E5 /* BogusFontTextTest.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, @@ -539,10 +683,19 @@ F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */, 246A6611252E693A00EAB0F3 /* RenderingSelectionTest.m in Sources */, 4F06F1B32473296E000AF246 /* LocalizationInitializationTest.m in Sources */, + 686383132AC202B700E27AAD /* AppExtensionTests.m in Sources */, 0DDEBC89258830B40065D0E8 /* SpawnEngineTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + 686382E42AC1F9F300E27AAD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 686382EC2AC1F9F300E27AAD /* ShareViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -556,8 +709,29 @@ target = 248D76C622E388370012F0C1 /* Scenarios */; targetProxy = 248D76EB22E388380012F0C1 /* PBXContainerItemProxy */; }; + 686382F22AC1F9F300E27AAD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 686382E72AC1F9F300E27AAD /* ScenariosShare */; + targetProxy = 686382F12AC1F9F300E27AAD /* PBXContainerItemProxy */; + }; + 686383112AC2027100E27AAD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FlutterAppExtensionTestHost; + targetProxy = 686383102AC2027100E27AAD /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 686382ED2AC1F9F300E27AAD /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 686382EE2AC1F9F300E27AAD /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 248D76F122E388380012F0C1 /* Debug */ = { isa = XCBuildConfiguration; @@ -678,7 +852,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -701,7 +875,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -723,7 +897,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -750,7 +924,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -776,7 +950,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -802,7 +976,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -824,6 +998,70 @@ }; name = Release; }; + 686382F52AC1F9F300E27AAD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ScenariosShare/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ScenariosShare; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 flutter. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.Scenarios.ScenariosShare; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 686382F62AC1F9F300E27AAD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ScenariosShare/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ScenariosShare; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 flutter. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.Scenarios.ScenariosShare; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -863,6 +1101,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 686382F72AC1F9F300E27AAD /* Build configuration list for PBXNativeTarget "ScenariosShare" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 686382F52AC1F9F300E27AAD /* Debug */, + 686382F62AC1F9F300E27AAD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 248D76BF22E388370012F0C1 /* Project object */; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index d938a895373d0..042fb8c6c49ad 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -9,6 +9,20 @@ #import "ScreenBeforeFlutter.h" #import "TextPlatformView.h" +// A UIViewController that sets YES for its preferedStatusBarHidden property. +// StatusBar includes current time, which is non-deterministic. This ViewController +// removes the StatusBar to make the screenshot deterministic. +@interface NoStatusBarViewController : UIViewController + +@end + +@implementation NoStatusBarViewController +- (BOOL)prefersStatusBarHidden { + return YES; +} +@end + +// The FlutterViewController version of NoStatusBarViewController @interface NoStatusBarFlutterViewController : FlutterViewController @end @@ -76,6 +90,7 @@ - (BOOL)application:(UIApplication*)application @"--two-platform-view-clip-rect" : @"two_platform_view_clip_rect", @"--two-platform-view-clip-rrect" : @"two_platform_view_clip_rrect", @"--two-platform-view-clip-path" : @"two_platform_view_clip_path", + @"--darwin-system-font" : @"darwin_system_font", }; __block NSString* flutterViewControllerTestName = nil; [launchArgsMap @@ -166,7 +181,7 @@ - (void)setupFlutterViewControllerTest:(NSString*)scenarioIdentifier { UIViewController* rootViewController = flutterViewController; // Make Flutter View's origin x/y not 0. if ([scenarioIdentifier isEqualToString:@"non_full_screen_flutter_view_platform_view"]) { - rootViewController = [UIViewController new]; + rootViewController = [[NoStatusBarViewController alloc] init]; [rootViewController.view addSubview:flutterViewController.view]; flutterViewController.view.frame = CGRectMake(150, 150, 500, 500); } diff --git a/testing/scenario_app/ios/Scenarios/ScenariosShare/Base.lproj/MainInterface.storyboard b/testing/scenario_app/ios/Scenarios/ScenariosShare/Base.lproj/MainInterface.storyboard new file mode 100644 index 0000000000000..589bdd9e70de9 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosShare/Base.lproj/MainInterface.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/scenario_app/ios/Scenarios/ScenariosShare/Info.plist b/testing/scenario_app/ios/Scenarios/ScenariosShare/Info.plist new file mode 100644 index 0000000000000..ad33037771e88 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosShare/Info.plist @@ -0,0 +1,18 @@ + + + + + NSExtension + + NSExtensionAttributes + + NSExtensionActivationRule + TRUEPREDICATE + + NSExtensionPrincipalClass + ShareViewController + NSExtensionPointIdentifier + com.apple.share-services + + + diff --git a/testing/scenario_app/ios/Scenarios/ScenariosShare/ShareViewController.h b/testing/scenario_app/ios/Scenarios/ScenariosShare/ShareViewController.h new file mode 100644 index 0000000000000..004a75bd6ad35 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosShare/ShareViewController.h @@ -0,0 +1,10 @@ +// 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. + +#import +#import + +@interface ShareViewController : FlutterViewController + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosShare/ShareViewController.m b/testing/scenario_app/ios/Scenarios/ScenariosShare/ShareViewController.m new file mode 100644 index 0000000000000..cea875a76271a --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosShare/ShareViewController.m @@ -0,0 +1,31 @@ +// 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. + +#import "ShareViewController.h" + +@interface ShareViewController () + +@end + +@implementation ShareViewController + +- (instancetype)init { + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"FlutterControllerTest" project:nil]; + [engine run]; + self = [self initWithEngine:engine nibName:nil bundle:nil]; + self.view.accessibilityIdentifier = @"flutter_view"; + + [engine.binaryMessenger + setMessageHandlerOnChannel:@"waiting_for_status" + binaryMessageHandler:^(NSData* _Nullable message, FlutterBinaryReply _Nonnull reply) { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"driver" + binaryMessenger:engine.binaryMessenger + codec:[FlutterJSONMethodCodec sharedInstance]]; + [channel invokeMethod:@"set_scenario" arguments:@{@"name" : @"app_extension"}]; + }]; + return self; +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/AppExtensionTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/AppExtensionTests.m new file mode 100644 index 0000000000000..70d4e0a190a3b --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/AppExtensionTests.m @@ -0,0 +1,58 @@ +// Copyright 2020 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. + +#import +#import "GoldenTestManager.h" + +@interface AppExtensionTests : XCTestCase +@property(nonatomic, strong) XCUIApplication* hostApplication; +@end + +@implementation AppExtensionTests + +- (void)setUp { + [super setUp]; + self.continueAfterFailure = NO; + self.hostApplication = + [[XCUIApplication alloc] initWithBundleIdentifier:@"dev.flutter.FlutterAppExtensionTestHost"]; +} + +- (void)testAppExtensionLaunching { + [self.hostApplication launch]; + XCUIElement* button = self.hostApplication.buttons[@"Open Share"]; + if (![button waitForExistenceWithTimeout:10]) { + NSLog(@"%@", self.hostApplication.debugDescription); + XCTFail(@"Failed due to not able to find any button with %@ seconds", @(10)); + } + [button tap]; + BOOL launchedExtensionInFlutter = NO; + // Custom share extension button (like the one in this test) does not have a unique + // identity. They are all identified as `XCElementSnapshotPrivilegedValuePlaceholder`. + // Loop through all the `XCElementSnapshotPrivilegedValuePlaceholder` and find the Flutter one. + for (int i = 0; i < self.hostApplication.collectionViews.cells.count; i++) { + XCUIElement* shareSheetCell = + [self.hostApplication.collectionViews.cells elementBoundByIndex:i]; + if (![shareSheetCell.label isEqualToString:@"XCElementSnapshotPrivilegedValuePlaceholder"]) { + continue; + } + [shareSheetCell tap]; + + XCUIElement* flutterView = self.hostApplication.otherElements[@"flutter_view"]; + if ([flutterView waitForExistenceWithTimeout:10]) { + launchedExtensionInFlutter = YES; + break; + } + + // All the built-in share extensions have a Cancel button. + // Tap the Cancel button to close the built-in extension. + XCUIElement* cancel = self.hostApplication.buttons[@"Cancel"]; + if ([cancel waitForExistenceWithTimeout:10]) { + [cancel tap]; + } + } + // App extension successfully launched flutter view. + XCTAssertTrue(launchedExtensionInFlutter); +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/BogusFontTextTest.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/BogusFontTextTest.m index 14c9e0bb93365..dc554ae2e5986 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/BogusFontTextTest.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/BogusFontTextTest.m @@ -25,7 +25,7 @@ - (void)testFontRenderingWhenSuppliedWithBogusFont { XCTAssertTrue([addTextField waitForExistenceWithTimeout:30]); GoldenTestManager* manager = [[GoldenTestManager alloc] initWithLaunchArg:@"--bogus-font-text"]; - [manager checkGoldenForTest:self]; + [manager checkGoldenForTest:self rmesThreshold:kDefaultRmseThreshold]; } @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/DarwinSystemFontTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/DarwinSystemFontTests.m new file mode 100644 index 0000000000000..29fec251557ed --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/DarwinSystemFontTests.m @@ -0,0 +1,30 @@ +// 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. + +#import +#import +#import "GoldenTestManager.h" + +@interface DarwinSystemFontTests : XCTestCase + +@end + +@implementation DarwinSystemFontTests + +- (void)testFontRendering { + self.continueAfterFailure = NO; + + XCUIApplication* application = [[XCUIApplication alloc] init]; + application.launchArguments = @[ @"--darwin-system-font" ]; + [application launch]; + + XCUIElement* addTextField = application.textFields[@"ready"]; + XCTAssertTrue([addTextField waitForExistenceWithTimeout:30]); + + GoldenTestManager* manager = + [[GoldenTestManager alloc] initWithLaunchArg:@"--darwin-system-font"]; + [manager checkGoldenForTest:self rmesThreshold:kDefaultRmseThreshold]; +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.h b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.h index dead0c530e126..4c0b9306fe14b 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.h +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.h @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN // Compare this GoldenImage to `image`. // // Return YES if the `image` of this GoldenImage have the same pixels of provided `image`. -- (BOOL)compareGoldenToImage:(UIImage*)image; +- (BOOL)compareGoldenToImage:(UIImage*)image rmesThreshold:(double)rmesThreshold; @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m index 698207aa681d4..37ae7b70fbb1d 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m @@ -8,8 +8,6 @@ #import #include -static const double kRmseThreshold = 0.5; - @interface GoldenImage () @end @@ -28,7 +26,7 @@ - (instancetype)initWithGoldenNamePrefix:(NSString*)prefix { return self; } -- (BOOL)compareGoldenToImage:(UIImage*)image { +- (BOOL)compareGoldenToImage:(UIImage*)image rmesThreshold:(double)rmesThreshold { if (!self.image || !image) { os_log_error(OS_LOG_DEFAULT, "GOLDEN DIFF FAILED: image does not exists."); return NO; @@ -91,11 +89,11 @@ - (BOOL)compareGoldenToImage:(UIImage*)image { } } double rmse = sqrt(sum / size); - if (rmse > kRmseThreshold) { + if (rmse > rmesThreshold) { os_log_error( OS_LOG_DEFAULT, "GOLDEN DIFF FAILED: image diff greater than threshold. Current diff: %@, threshold: %@", - @(rmse), @(kRmseThreshold)); + @(rmse), @(rmesThreshold)); return NO; } return YES; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.h b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.h index fd1b05ef0b009..625fbf8b31e3c 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.h +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.h @@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN @interface GoldenPlatformViewTests : XCTestCase @property(nonatomic, strong) XCUIApplication* application; +@property(nonatomic, assign) double rmseThreadhold; // Initialize with a `GoldenTestManager`. - (instancetype)initWithManager:(GoldenTestManager*)manager invocation:(NSInvocation*)invocation; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m index 7e4bb368fc6eb..7199fa2600e86 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m @@ -22,6 +22,7 @@ @implementation GoldenPlatformViewTests - (instancetype)initWithManager:(GoldenTestManager*)manager invocation:(NSInvocation*)invocation { self = [super initWithInvocation:invocation]; _manager = manager; + _rmseThreadhold = kDefaultRmseThreshold; return self; } @@ -45,6 +46,6 @@ - (void)checkPlatformViewGolden { @(kSecondsToWaitForPlatformView)); } - [self.manager checkGoldenForTest:self]; + [self.manager checkGoldenForTest:self rmesThreshold:self.rmseThreadhold]; } @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.h b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.h index 4bb0b275a77e9..7026672af7c46 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.h +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.h @@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN extern NSDictionary* launchArgsMap; +const extern double kDefaultRmseThreshold; // Manages a `GoldenPlatformViewTests`. // @@ -27,7 +28,7 @@ extern NSDictionary* launchArgsMap; // Take a sceenshot of the test app and check it has the same pixels with // goldenImage inside the `GoldenTestManager`. -- (void)checkGoldenForTest:(XCTestCase*)test; +- (void)checkGoldenForTest:(XCTestCase*)test rmesThreshold:(double)rmesThreshold; @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m index 76c95a06501fe..ac55da74642bb 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m @@ -14,6 +14,7 @@ @interface GoldenTestManager () @implementation GoldenTestManager NSDictionary* launchArgsMap; +const double kDefaultRmseThreshold = 0.5; - (instancetype)initWithLaunchArg:(NSString*)launchArg { self = [super init]; @@ -52,6 +53,8 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { @"--two-platform-view-clip-rect" : @"two_platform_view_clip_rect", @"--two-platform-view-clip-rrect" : @"two_platform_view_clip_rrect", @"--two-platform-view-clip-path" : @"two_platform_view_clip_path", + @"--app-extension" : @"app_extension", + @"--darwin-system-font" : @"darwin_system_font", }; }); _identifier = launchArgsMap[launchArg]; @@ -73,7 +76,7 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { return self; } -- (void)checkGoldenForTest:(XCTestCase*)test { +- (void)checkGoldenForTest:(XCTestCase*)test rmesThreshold:(double)rmesThreshold { XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot]; if (!_goldenImage.image) { XCTAttachment* attachment = [XCTAttachment attachmentWithScreenshot:screenshot]; @@ -88,7 +91,7 @@ - (void)checkGoldenForTest:(XCTestCase*)test { _goldenImage.goldenName); } - if (![_goldenImage compareGoldenToImage:screenshot.image]) { + if (![_goldenImage compareGoldenToImage:screenshot.image rmesThreshold:rmesThreshold]) { XCTAttachment* screenshotAttachment = [XCTAttachment attachmentWithImage:screenshot.image]; screenshotAttachment.name = [_goldenImage.goldenName stringByAppendingString:@"_actual.png"]; screenshotAttachment.lifetime = XCTAttachmentLifetimeKeepAlways; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index 03984f27fda42..7415a51717ddb 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -375,6 +375,11 @@ - (instancetype)initWithInvocation:(NSInvocation*)invocation { } - (void)testPlatformView { + // (TODO)cyanglaz: remove the threshold adjustment after all the ci migrates to macOS13. + // https://github.com/flutter/flutter/issues/133207 + if ([NSProcessInfo processInfo].operatingSystemVersion.majorVersion >= 13) { + self.rmseThreadhold = 0.7; + } [self checkPlatformViewGolden]; } diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/SpawnEngineTest.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/SpawnEngineTest.m index f00a8c988bdfd..cc5810eeac608 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/SpawnEngineTest.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/SpawnEngineTest.m @@ -21,7 +21,7 @@ - (void)testSpawnEngineWorks { GoldenTestManager* manager = [[GoldenTestManager alloc] initWithLaunchArg:@"--spawn-engine-works"]; - [manager checkGoldenForTest:self]; + [manager checkGoldenForTest:self rmesThreshold:kDefaultRmseThreshold]; } @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png new file mode 100644 index 0000000000000..eba78ac2a07e9 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png differ diff --git a/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart b/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart new file mode 100644 index 0000000000000..354810a5d8d23 --- /dev/null +++ b/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart @@ -0,0 +1,44 @@ +// 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. + +import 'dart:ui'; + +import 'scenario.dart'; + +/// Shows a text that is shown in app extension. +class DarwinAppExtensionScenario extends Scenario { + /// Creates the DarwinAppExtensionScenario scenario. + DarwinAppExtensionScenario(super.view); + + // Semi-arbitrary. + final double _screenWidth = 700; + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + + final ParagraphBuilder paragraphBuilder = + ParagraphBuilder(ParagraphStyle()) + ..pushStyle(TextStyle(fontSize: 80)) + ..addText('flutter Scenarios app extension.') + ..pop(); + final Paragraph paragraph = paragraphBuilder.build(); + + paragraph.layout(ParagraphConstraints(width: _screenWidth)); + + canvas.drawParagraph(paragraph, const Offset(50, 80)); + final Picture picture = recorder.endRecording(); + + builder.addPicture( + Offset.zero, + picture, + willChangeHint: true, + ); + final Scene scene = builder.build(); + view.render(scene); + scene.dispose(); + } +} diff --git a/testing/scenario_app/lib/src/darwin_system_font.dart b/testing/scenario_app/lib/src/darwin_system_font.dart new file mode 100644 index 0000000000000..92bc1357625b7 --- /dev/null +++ b/testing/scenario_app/lib/src/darwin_system_font.dart @@ -0,0 +1,62 @@ +// 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. + +import 'dart:ui'; + +import 'channel_util.dart'; +import 'scenario.dart'; + +/// Tries to draw darwin system font: CupertinoSystemDisplay, CupertinoSystemText +class DarwinSystemFont extends Scenario { + /// Creates the DarwinSystemFont scenario. + DarwinSystemFont(super.view); + + // Semi-arbitrary. + final double _screenWidth = 700; + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + + final ParagraphBuilder paragraphBuilderDisplay = + ParagraphBuilder(ParagraphStyle(fontFamily: 'CupertinoSystemDisplay')) + ..pushStyle(TextStyle(fontSize: 50)) + ..addText('Cupertino System Display\n') + ..pop(); + final ParagraphBuilder paragraphBuilderText = + ParagraphBuilder(ParagraphStyle(fontFamily: 'CupertinoSystemText')) + ..pushStyle(TextStyle(fontSize: 50)) + ..addText('Cupertino System Text\n') + ..pop(); + + final Paragraph paragraphPro = paragraphBuilderDisplay.build(); + paragraphPro.layout(ParagraphConstraints(width: _screenWidth)); + canvas.drawParagraph(paragraphPro, const Offset(50, 80)); + + final Paragraph paragraphText = paragraphBuilderText.build(); + paragraphText.layout(ParagraphConstraints(width: _screenWidth)); + canvas.drawParagraph(paragraphText, const Offset(50, 200)); + + final Picture picture = recorder.endRecording(); + + builder.addPicture( + Offset.zero, + picture, + willChangeHint: true, + ); + final Scene scene = builder.build(); + view.render(scene); + scene.dispose(); + + sendJsonMessage( + dispatcher: view.platformDispatcher, + channel: 'display_data', + json: { + 'data': 'ready', + }, + ); + } +} diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index d9c586023bed2..87f1f6cbba952 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -6,6 +6,8 @@ import 'dart:ui'; import 'animated_color_square.dart'; import 'bogus_font_text.dart'; +import 'darwin_app_extension_scenario.dart'; +import 'darwin_system_font.dart'; import 'get_bitmap_scenario.dart'; import 'initial_route_reply.dart'; import 'locale_initialization.dart'; @@ -66,6 +68,8 @@ Map _scenarios = { 'pointer_events': (FlutterView view) => TouchesScenario(view), 'display_texture': (FlutterView view) => DisplayTexture(view), 'get_bitmap': (FlutterView view) => GetBitmapScenario(view), + 'app_extension': (FlutterView view) => DarwinAppExtensionScenario(view), + 'darwin_system_font': (FlutterView view) => DarwinSystemFont(view), }; Map _currentScenarioParams = {}; diff --git a/testing/scenario_app/pubspec.yaml b/testing/scenario_app/pubspec.yaml index c90a33d7f3204..c2b5d8425f9a3 100644 --- a/testing/scenario_app/pubspec.yaml +++ b/testing/scenario_app/pubspec.yaml @@ -5,7 +5,7 @@ name: scenario_app publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/dart/pkg, //third_party/dart/third_party/pkg, or diff --git a/testing/scenario_app/run_ios_tests.sh b/testing/scenario_app/run_ios_tests.sh index 3f730deaf3bba..772ea3b96294d 100755 --- a/testing/scenario_app/run_ios_tests.sh +++ b/testing/scenario_app/run_ios_tests.sh @@ -109,7 +109,9 @@ if set -o pipefail && xcodebuild -sdk iphonesimulator \ -skip-testing ScenariosUITests/TwoPlatformViewClipRRectTests/testPlatformView \ -skip-testing ScenariosUITests/TwoPlatformViewsWithOtherBackDropFilterTests/testPlatformView \ -skip-testing ScenariosUITests/UnobstructedPlatformViewTests/testMultiplePlatformViewsWithOverlays \ - INFOPLIST_FILE="Scenarios/Info_Impeller.plist"; then # Plist with FLTEnableImpeller=YES + # Plist with FLTEnableImpeller=YES, all projects in the workspace requires this file. + # For example, FlutterAppExtensionTestHost has a dummy file under the below directory. + INFOPLIST_FILE="Scenarios/Info_Impeller.plist"; then echo "test success." else echo "test failed." diff --git a/testing/skia_gold_client/pubspec.yaml b/testing/skia_gold_client/pubspec.yaml index 9f39ae9b2806e..53add9bf3093d 100644 --- a/testing/skia_gold_client/pubspec.yaml +++ b/testing/skia_gold_client/pubspec.yaml @@ -5,7 +5,7 @@ name: skia_gold_client publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/dart/pkg, //third_party/dart/third_party/pkg, or diff --git a/testing/smoke_test_failure/BUILD.gn b/testing/smoke_test_failure/BUILD.gn index 8110ea199fa16..1ce144ecc523a 100644 --- a/testing/smoke_test_failure/BUILD.gn +++ b/testing/smoke_test_failure/BUILD.gn @@ -2,15 +2,15 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/testing/dart/compile_test.gni") +import("//flutter/build/dart/rules.gni") tests = [ "fail_test.dart" ] foreach(test, tests) { - compile_flutter_dart_test("compile_$test") { - dart_file = test - dart_kernel = "$root_gen_dir/$test.dill" - packages = ".dart_tool/package_config.json" + flutter_frontend_server("compile_$test") { + main_dart = test + kernel_output = "$root_gen_dir/$test.dill" + package_config = ".dart_tool/package_config.json" } } diff --git a/testing/smoke_test_failure/pubspec.yaml b/testing/smoke_test_failure/pubspec.yaml index df2f11055e778..41670c0136865 100644 --- a/testing/smoke_test_failure/pubspec.yaml +++ b/testing/smoke_test_failure/pubspec.yaml @@ -5,7 +5,7 @@ name: smoke_test_failure publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/dart/pkg or //third_party/dart/third_party/pkg. diff --git a/testing/symbols/pubspec.yaml b/testing/symbols/pubspec.yaml index ab9d4b49a8d67..fb7d37a812907 100644 --- a/testing/symbols/pubspec.yaml +++ b/testing/symbols/pubspec.yaml @@ -5,7 +5,7 @@ name: verify_exported publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 22e9736aff9cb..a4b982171d16e 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -135,106 +135,8 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { }; final Map expectedSymbols = { 'JNI_OnLoad': 'T', - '_binary_icudtl_dat_size': 'A', - '_binary_icudtl_dat_start': 'D', - // TODO(dnfield): Remove these once Clang lld does not expose them. - // arm https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=47943 - '__adddf3': 'T', - '__addsf3': 'T', - '__aeabi_cdcmpeq': 'T', - '__aeabi_cdcmple': 'T', - '__aeabi_cdrcmple': 'T', - '__aeabi_cfcmpeq': 'T', - '__aeabi_cfcmple': 'T', - '__aeabi_cfrcmple': 'T', - '__aeabi_d2lz': 'T', - '__aeabi_d2uiz': 'T', - '__aeabi_d2ulz': 'T', - '__aeabi_dadd': 'T', - '__aeabi_dcmpeq': 'T', - '__aeabi_dcmpge': 'T', - '__aeabi_dcmpgt': 'T', - '__aeabi_dcmple': 'T', - '__aeabi_dcmplt': 'T', - '__aeabi_ddiv': 'T', - '__aeabi_dmul': 'T', - '__aeabi_drsub': 'T', - '__aeabi_dsub': 'T', - '__aeabi_f2d': 'T', - '__aeabi_f2lz': 'T', - '__aeabi_f2ulz': 'T', - '__aeabi_fadd': 'T', - '__aeabi_fcmpeq': 'T', - '__aeabi_fcmpge': 'T', - '__aeabi_fcmpgt': 'T', - '__aeabi_fcmple': 'T', - '__aeabi_fcmplt': 'T', - '__aeabi_frsub': 'T', - '__aeabi_fsub': 'T', - '__aeabi_i2d': 'T', - '__aeabi_i2f': 'T', - '__aeabi_l2d': 'T', - '__aeabi_l2f': 'T', - '__aeabi_lasr': 'T', - '__aeabi_ldivmod': 'T', - '__aeabi_llsl': 'T', - '__aeabi_llsr': 'T', - '__aeabi_ui2d': 'T', - '__aeabi_ui2f': 'T', - '__aeabi_uidiv': 'T', - '__aeabi_uidivmod': 'T', - '__aeabi_ul2d': 'T', - '__aeabi_ul2f': 'T', - '__aeabi_uldivmod': 'T', - '__ashldi3': 'T', - '__ashrdi3': 'T', - '__cmpdf2': 'T', - '__cmpsf2': 'T', - '__divdf3': 'T', - '__divdi3': 'T', - '__eqdf2': 'T', - '__eqsf2': 'T', - '__extendsfdf2': 'T', - '__fixdfdi': 'T', - '__fixsfdi': 'T', - '__fixunsdfdi': 'T', - '__fixunsdfsi': 'T', - '__fixunssfdi': 'T', - '__floatdidf': 'T', - '__floatdisf': 'T', - '__floatsidf': 'T', - '__floatsisf': 'T', - '__floatundidf': 'T', - '__floatundisf': 'T', - '__floatunsidf': 'T', - '__floatunsisf': 'T', - '__gedf2': 'T', - '__gesf2': 'T', - '__gnu_ldivmod_helper': 'T', - '__gnu_uldivmod_helper': 'T', - '__gtdf2': 'T', - '__gtsf2': 'T', - '__ledf2': 'T', - '__lesf2': 'T', - '__lshrdi3': 'T', - '__ltdf2': 'T', - '__ltsf2': 'T', - '__muldf3': 'T', - '__nedf2': 'T', - '__nesf2': 'T', - '__subdf3': 'T', - '__subsf3': 'T', - '__udivdi3': 'T', - '__udivsi3': 'T', - // arm64 - '__clz_tab': 'R', - '__udivti3': 'T', - // arm64 && x64 - '__emutls_get_address': 'T', - '__emutls_register_common': 'T', - // jit x86 - '__moddi3': 'T', - '__umoddi3': 'T', + '_binary_icudtl_dat_size': 'R', + '_binary_icudtl_dat_start': 'R', }; final Map badSymbols = {}; for (final String key in entryMap.keys) { diff --git a/testing/test_gl_surface.cc b/testing/test_gl_surface.cc index 8c98bacde9fdc..c70364696277a 100644 --- a/testing/test_gl_surface.cc +++ b/testing/test_gl_surface.cc @@ -20,6 +20,7 @@ #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h" #include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" @@ -336,7 +337,7 @@ sk_sp TestGLSurface::CreateGrContext() { return nullptr; } - context_ = GrDirectContext::MakeGL(interface); + context_ = GrDirectContexts::MakeGL(interface); return context_; } diff --git a/testing/test_metal_context.h b/testing/test_metal_context.h index c470e501257d4..734aed61f991a 100644 --- a/testing/test_metal_context.h +++ b/testing/test_metal_context.h @@ -41,7 +41,7 @@ class TestMetalContext { void* device_; void* command_queue_; sk_sp skia_context_; - std::mutex textures_mutex; + std::mutex textures_mutex_; int64_t texture_id_ctr_ = 1; // guarded by textures_mutex std::map> textures_; // guarded by textures_mutex }; diff --git a/testing/test_metal_context.mm b/testing/test_metal_context.mm index 6ac70a2ae25f3..dca0a34f7df28 100644 --- a/testing/test_metal_context.mm +++ b/testing/test_metal_context.mm @@ -41,7 +41,7 @@ } TestMetalContext::~TestMetalContext() { - std::scoped_lock lock(textures_mutex); + std::scoped_lock lock(textures_mutex_); textures_.clear(); if (device_) { [(__bridge id)device_ release]; @@ -64,7 +64,7 @@ } TestMetalContext::TextureInfo TestMetalContext::CreateMetalTexture(const SkISize& size) { - std::scoped_lock lock(textures_mutex); + std::scoped_lock lock(textures_mutex_); auto texture_descriptor = fml::scoped_nsobject{ [[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:size.width() @@ -100,7 +100,7 @@ // Don't remove the texture from the map here. bool TestMetalContext::Present(int64_t texture_id) { - std::scoped_lock lock(textures_mutex); + std::scoped_lock lock(textures_mutex_); if (textures_.find(texture_id) == textures_.end()) { return false; } else { @@ -109,7 +109,7 @@ } TestMetalContext::TextureInfo TestMetalContext::GetTextureInfo(int64_t texture_id) { - std::scoped_lock lock(textures_mutex); + std::scoped_lock lock(textures_mutex_); if (textures_.find(texture_id) == textures_.end()) { FML_CHECK(false) << "Invalid texture id: " << texture_id; return {.texture_id = -1, .texture = nullptr}; diff --git a/testing/test_metal_surface_impl.mm b/testing/test_metal_surface_impl.mm index 34878a9613d0d..cf2c6931f8621 100644 --- a/testing/test_metal_surface_impl.mm +++ b/testing/test_metal_surface_impl.mm @@ -15,6 +15,7 @@ #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GpuTypes.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" @@ -26,8 +27,8 @@ GrMtlTextureInfo skia_texture_info; skia_texture_info.fTexture.reset([texture retain]); - GrBackendTexture backend_texture(surface_size.width(), surface_size.height(), GrMipmapped::kNo, - skia_texture_info); + GrBackendTexture backend_texture(surface_size.width(), surface_size.height(), + skgpu::Mipmapped::kNo, skia_texture_info); sk_sp surface = SkSurfaces::WrapBackendTexture( test_metal_context_.GetSkiaContext().get(), backend_texture, kTopLeft_GrSurfaceOrigin, 1, diff --git a/testing/test_vulkan_context.cc b/testing/test_vulkan_context.cc index ea36581add8e2..d7472303f111a 100644 --- a/testing/test_vulkan_context.cc +++ b/testing/test_vulkan_context.cc @@ -14,19 +14,13 @@ #include "flutter/fml/memory/ref_ptr.h" #include "flutter/fml/native_library.h" +#include "flutter/vulkan/swiftshader_path.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" #include "third_party/skia/include/gpu/vk/GrVkExtensions.h" #include "vulkan/vulkan_core.h" -#ifdef FML_OS_MACOSX -#define VULKAN_SO_PATH "libvk_swiftshader.dylib" -#elif FML_OS_WIN -#define VULKAN_SO_PATH "vk_swiftshader.dll" -#else -#define VULKAN_SO_PATH "libvk_swiftshader.so" -#endif - namespace flutter { namespace testing { @@ -113,7 +107,7 @@ TestVulkanContext::TestVulkanContext() { GrContextOptions options = MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kVulkan); options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; - context_ = GrDirectContext::MakeVulkan(backend_context, options); + context_ = GrDirectContexts::MakeVulkan(backend_context, options); } TestVulkanContext::~TestVulkanContext() { diff --git a/testing/testing.gni b/testing/testing.gni index 7059bc15f017b..cf59753e51dc7 100644 --- a/testing/testing.gni +++ b/testing/testing.gni @@ -3,9 +3,8 @@ # found in the LICENSE file. import("//build/compiled_action.gni") +import("//flutter/build/dart/rules.gni") import("//flutter/common/config.gni") -import("//third_party/dart/build/dart/dart_action.gni") -import("//third_party/dart/sdk_args.gni") is_aot_test = flutter_runtime_mode == "profile" || flutter_runtime_mode == "release" @@ -51,48 +50,6 @@ template("fixtures_location") { } } -# Invokes the frontend server using the built Dart SDK or the prebuilt Dart SDK -# as appropriate. -# -# Parameters: -# The parameters testonly, deps, inputs, outputs, depfile, and args are -# forwarded from the invoker either to an 'action' target or a 'dart_action' -# target depending on whether a prebuilt Dart SDK is used or not, -# respectively. -template("_frontend_server") { - if (flutter_prebuilt_dart_sdk) { - action(target_name) { - testonly = invoker.testonly - deps = invoker.deps - script = "//build/gn_run_binary.py" - inputs = invoker.inputs - outputs = invoker.outputs - depfile = invoker.depfile - pool = "//flutter/build/dart:dart_pool" - - ext = "" - if (is_win) { - ext = ".exe" - } - dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext", root_out_dir) - frontend_server = rebase_path( - "$host_prebuilt_dart_sdk/bin/snapshots/frontend_server.dart.snapshot") - - args = [ - dart, - frontend_server, - ] + invoker.args - } - } else { - dart_action(target_name) { - forward_variables_from(invoker, "*") - deps += [ "//third_party/dart/utils/kernel-service:frontend_server" ] - script = "$root_out_dir/frontend_server.dart.snapshot" - pool = "//flutter/build/dart:dart_pool" - } - } -} - # Generates the Dart kernel snapshot. # # Arguments @@ -107,45 +64,26 @@ template("dart_snapshot_kernel") { assert(defined(invoker.dart_kernel), "The Dart Kernel file location must be specified") - _frontend_server(target_name) { - testonly = true - - deps = [ "//flutter/lib/snapshot:strong_platform" ] - - inputs = [ invoker.dart_main ] - - outputs = [ invoker.dart_kernel ] + args = [] + if (flutter_runtime_mode == "release" || + flutter_runtime_mode == "jit_release") { + args += [ "-Ddart.vm.product=true" ] + } - snapshot_depfile = "$target_gen_dir/snapshot_$target_name.depfile.d" - depfile = snapshot_depfile + if (is_aot_test) { + args += [ + "--aot", - args = [ - "--sdk-root", - rebase_path("$root_out_dir/flutter_patched_sdk"), - "--target", - "flutter", - "--sound-null-safety", - "--output-dill", - rebase_path(invoker.dart_kernel, root_out_dir), - "--depfile", - rebase_path(snapshot_depfile), + # type flow analysis + "--tfa", ] + } - if (flutter_runtime_mode == "release" || - flutter_runtime_mode == "jit_release") { - args += [ "-Ddart.vm.product=true" ] - } - - if (is_aot_test) { - args += [ - "--aot", - - # type flow analysis - "--tfa", - ] - } - - args += [ rebase_path(invoker.dart_main) ] + flutter_frontend_server(target_name) { + testonly = true + main_dart = invoker.dart_main + kernel_output = invoker.dart_kernel + extra_args = args } } diff --git a/third_party/.clang-tidy b/third_party/.clang-tidy new file mode 100644 index 0000000000000..ef7500e1a8539 --- /dev/null +++ b/third_party/.clang-tidy @@ -0,0 +1,4 @@ +# Disable all checks in this folder. +# +# We cannot ask third_party code to conform to our lints. +Checks: "-*" diff --git a/third_party/.gitignore b/third_party/.gitignore new file mode 100644 index 0000000000000..5bd42c1696fca --- /dev/null +++ b/third_party/.gitignore @@ -0,0 +1,22 @@ +# Ignore everything by default, as these come from gclient/DEPS. +# We'll explicitly include the folders we want to track. +/* + +# Include the .gitignore file itself and .clang-tidy. +!.gitignore +!.clang-tidy +!README.md + +# Allow custom README.flutter files in each folder. +**/README.flutter + +# Include folders that have hand-written code (not DEPS). +!accessibility/ +!canvaskit/ +!spring_animation/ +!test_shaders/ +!tonic/ +!txt/ +!web_locale_keymap/ +!web_test_fonts/ +!web_unicode/ diff --git a/third_party/README.md b/third_party/README.md new file mode 100644 index 0000000000000..ae435e36c8efb --- /dev/null +++ b/third_party/README.md @@ -0,0 +1,24 @@ +# `flutter/third_party` + +This directory contains third-party code that is a combination of: + +- Code that is vendored into the Flutter repository, from an external source. + For example, we might have `third_party/glfw`, which contains the GLFW + library, vendored from an external repository. + + > 💡 **TIP**: See [`DEPS`](../DEPS) for where these sources are declared. + +- Code that originates from another repository, but is copied (sometimes with + alterations) into the Flutter repository. For an example, see + [`third_party/spring_animation`](spring_animation/README.md). + +- Code that is licensed separately from the rest of the Flutter repository. + For example, see [`third_party/txt`](txt/). + +When adding a new _externally_ sourced third-party library, update `.gitignore`: + +```diff +# Ignores all third_party/ directories except for the ones we want to track. + ++ !{folder_name}/ +``` diff --git a/third_party/canvaskit/BUILD.gn b/third_party/canvaskit/BUILD.gn index 691e09ecbf660..125daf0998e80 100644 --- a/third_party/canvaskit/BUILD.gn +++ b/third_party/canvaskit/BUILD.gn @@ -27,6 +27,7 @@ copy("canvaskit_group") { sources = [ "$root_out_dir/canvaskit/canvaskit.js", + "$root_out_dir/canvaskit/canvaskit.js.symbols", "$root_out_dir/canvaskit/canvaskit.wasm", ] outputs = [ "$root_out_dir/flutter_web_sdk/canvaskit/{{source_file_part}}" ] @@ -61,6 +62,7 @@ copy("canvaskit_chromium_group") { sources = [ "$root_out_dir/canvaskit_chromium/canvaskit.js", + "$root_out_dir/canvaskit_chromium/canvaskit.js.symbols", "$root_out_dir/canvaskit_chromium/canvaskit.wasm", ] outputs = [ @@ -97,6 +99,7 @@ copy("skwasm_group") { sources = [ "$root_out_dir/skwasm/skwasm.js", + "$root_out_dir/skwasm/skwasm.js.symbols", "$root_out_dir/skwasm/skwasm.wasm", "$root_out_dir/skwasm/skwasm.worker.js", ] diff --git a/third_party/txt/BUILD.gn b/third_party/txt/BUILD.gn index a61438b72c5eb..da4d0615d7b9a 100644 --- a/third_party/txt/BUILD.gn +++ b/third_party/txt/BUILD.gn @@ -98,7 +98,10 @@ source_set("txt") { } if (is_mac || is_ios) { - sources += [ "src/txt/platform_mac.mm" ] + sources += [ + "src/txt/platform_mac.h", + "src/txt/platform_mac.mm", + ] deps += [ "//flutter/fml" ] } else if (is_android) { sources += [ "src/txt/platform_android.cc" ] @@ -148,6 +151,10 @@ if (enable_unittests) { "tests/txt_run_all_unittests.cc", ] + if (is_mac || is_ios) { + sources += [ "tests/platform_mac_tests.cc" ] + } + public_configs = [ ":txt_config" ] configs += [ ":allow_posix_names" ] diff --git a/third_party/txt/src/skia/paragraph_builder_skia.cc b/third_party/txt/src/skia/paragraph_builder_skia.cc index 2028e80353c50..443c2b9ca2b08 100644 --- a/third_party/txt/src/skia/paragraph_builder_skia.cc +++ b/third_party/txt/src/skia/paragraph_builder_skia.cc @@ -102,7 +102,7 @@ skt::ParagraphStyle ParagraphBuilderSkia::TxtToSkia(const ParagraphStyle& txt) { // Convert the default color of an SkParagraph text style into a DlPaint. flutter::DlPaint dl_paint; - dl_paint.setColor(text_style.getColor()); + dl_paint.setColor(flutter::DlColor(text_style.getColor())); text_style.setForegroundPaintID(CreatePaintID(dl_paint)); text_style.setFontStyle(MakeSkFontStyle(txt.font_weight, txt.font_style)); @@ -178,7 +178,7 @@ skt::TextStyle ParagraphBuilderSkia::TxtToSkia(const TextStyle& txt) { skia.setForegroundPaintID(CreatePaintID(txt.foreground.value())); } else { flutter::DlPaint dl_paint; - dl_paint.setColor(txt.color); + dl_paint.setColor(flutter::DlColor(txt.color)); skia.setForegroundPaintID(CreatePaintID(dl_paint)); } diff --git a/third_party/txt/src/skia/paragraph_skia.cc b/third_party/txt/src/skia/paragraph_skia.cc index a688720cfced9..67d67b1a04d2b 100644 --- a/third_party/txt/src/skia/paragraph_skia.cc +++ b/third_party/txt/src/skia/paragraph_skia.cc @@ -110,7 +110,7 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { return; } DlPaint paint; - paint.setColor(color); + paint.setColor(DlColor(color)); if (blur_sigma > 0.0) { DlBlurMaskFilter filter(DlBlurStyle::kNormal, blur_sigma, false); paint.setMaskFilter(&filter); @@ -213,7 +213,7 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { // filters rely on having the glyph coverage, whereas regular text is // drawn as rectangular texture samples. return ((paint.getColorSource() && !paint.getColorSource()->asColor()) || - paint.getDrawStyle() == DlDrawStyle::kStroke); + paint.getDrawStyle() != DlDrawStyle::kFill); } DlPaint toDlPaint(const DecorationStyle& decor_style, @@ -221,7 +221,7 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { DlPaint paint; paint.setDrawStyle(draw_style); paint.setAntiAlias(true); - paint.setColor(decor_style.getColor()); + paint.setColor(DlColor(decor_style.getColor())); paint.setStrokeWidth(decor_style.getStrokeWidth()); return paint; } diff --git a/third_party/txt/src/txt/asset_font_manager.h b/third_party/txt/src/txt/asset_font_manager.h index cc1c92711e793..4c1e34c17fdc1 100644 --- a/third_party/txt/src/txt/asset_font_manager.h +++ b/third_party/txt/src/txt/asset_font_manager.h @@ -89,7 +89,7 @@ class DynamicFontManager : public AssetFontManager { DynamicFontManager() : AssetFontManager(std::make_unique()) {} - TypefaceFontAssetProvider& font_provider() { + TypefaceFontAssetProvider& font_provider() const { return static_cast(*font_provider_); } }; diff --git a/third_party/txt/src/txt/platform_mac.h b/third_party/txt/src/txt/platform_mac.h new file mode 100644 index 0000000000000..7ff2e0d391ded --- /dev/null +++ b/third_party/txt/src/txt/platform_mac.h @@ -0,0 +1,17 @@ +// 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 TXT_PLATFORM_MAC_H_ +#define TXT_PLATFORM_MAC_H_ + +#include "txt/asset_font_manager.h" + +namespace txt { + +// Register additional system font for MacOS and iOS. +void RegisterSystemFonts(const DynamicFontManager& dynamic_font_manager); + +} // namespace txt + +#endif // TXT_PLATFORM_MAC_H_ diff --git a/third_party/txt/src/txt/platform_mac.mm b/third_party/txt/src/txt/platform_mac.mm index 4e116418c5974..3ab772ffb23e1 100644 --- a/third_party/txt/src/txt/platform_mac.mm +++ b/third_party/txt/src/txt/platform_mac.mm @@ -5,7 +5,9 @@ #include #include "flutter/fml/platform/darwin/platform_version.h" +#include "third_party/skia/include/ports/SkTypeface_mac.h" #include "txt/platform.h" +#include "txt/platform_mac.h" #if TARGET_OS_EMBEDDED || TARGET_OS_SIMULATOR #include @@ -15,6 +17,15 @@ #define FONT_CLASS NSFont #endif // TARGET_OS_EMBEDDED +// Apple system font larger than size 29 returns SFProDisplay typeface. +static const CGFloat kSFProDisplayBreakPoint = 29; +// Apple system font smaller than size 16 returns SFProText typeface. +static const CGFloat kSFProTextBreakPoint = 16; +// Font name represents the "SF Pro Display" system font on Apple platforms. +static const std::string kSFProDisplayName = "CupertinoSystemDisplay"; +// Font name represents the "SF Pro Text" system font on Apple platforms. +static const std::string kSFProTextName = "CupertinoSystemText"; + namespace txt { std::vector GetDefaultFontFamilies() { @@ -29,4 +40,34 @@ return SkFontMgr::RefDefault(); } +void RegisterSystemFonts(const DynamicFontManager& dynamic_font_manager) { + // iOS loads different system fonts when size is greater than 28 or lower + // than 17. The "familyName" property returned from CoreText stays the same + // despite the typeface is different. + // + // Below code manually loads and registers them as two different fonts + // so Flutter app can access them on macOS and iOS. + // + // Darwin system fonts from 17 to 28 also have dynamic spacing based on sizes. + // These two fonts do not match the spacings when sizes are from 17 to 28. + // The spacing should be handled by the app or the framework. + // + // See https://www.wwdcnotes.com/notes/wwdc20/10175/ for Apple's document on + // this topic. + sk_sp large_system_font = SkMakeTypefaceFromCTFont( + (CTFontRef)CFAutorelease(CTFontCreateUIFontForLanguage( + kCTFontUIFontSystem, kSFProDisplayBreakPoint, NULL))); + if (large_system_font) { + dynamic_font_manager.font_provider().RegisterTypeface(large_system_font, + kSFProDisplayName); + } + sk_sp regular_system_font = SkMakeTypefaceFromCTFont( + (CTFontRef)CFAutorelease(CTFontCreateUIFontForLanguage( + kCTFontUIFontSystem, kSFProTextBreakPoint, NULL))); + if (regular_system_font) { + dynamic_font_manager.font_provider().RegisterTypeface(regular_system_font, + kSFProTextName); + } +} + } // namespace txt diff --git a/third_party/txt/tests/platform_mac_tests.cc b/third_party/txt/tests/platform_mac_tests.cc new file mode 100644 index 0000000000000..54cea1c1324a1 --- /dev/null +++ b/third_party/txt/tests/platform_mac_tests.cc @@ -0,0 +1,34 @@ +// 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 "gtest/gtest.h" + +#include + +#include "txt/platform_mac.h" + +namespace txt { +namespace testing { + +class PlatformMacTests : public ::testing::Test { + public: + PlatformMacTests() {} + + void SetUp() override {} +}; + +TEST_F(PlatformMacTests, RegisterSystemFonts) { + DynamicFontManager dynamic_font_manager; + RegisterSystemFonts(dynamic_font_manager); + ASSERT_EQ(dynamic_font_manager.font_provider().GetFamilyCount(), 2ul); + ASSERT_NE(dynamic_font_manager.font_provider().MatchFamily( + "CupertinoSystemDisplay"), + nullptr); + ASSERT_NE( + dynamic_font_manager.font_provider().MatchFamily("CupertinoSystemText"), + nullptr); +} + +} // namespace testing +} // namespace txt diff --git a/third_party/web_locale_keymap/pubspec.yaml b/third_party/web_locale_keymap/pubspec.yaml index 2da80a46c5108..646fbb3b96997 100644 --- a/third_party/web_locale_keymap/pubspec.yaml +++ b/third_party/web_locale_keymap/pubspec.yaml @@ -3,7 +3,7 @@ name: web_locale_keymap publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dev_dependencies: test: ^1.21.7 diff --git a/third_party/web_test_fonts/pubspec.yaml b/third_party/web_test_fonts/pubspec.yaml index 6c2af49caa236..2042ad239ca21 100644 --- a/third_party/web_test_fonts/pubspec.yaml +++ b/third_party/web_test_fonts/pubspec.yaml @@ -3,7 +3,7 @@ name: web_test_fonts publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dev_dependencies: args: any diff --git a/third_party/web_unicode/pubspec.yaml b/third_party/web_unicode/pubspec.yaml index 99b6d98c56688..fd57811f90f19 100644 --- a/third_party/web_unicode/pubspec.yaml +++ b/third_party/web_unicode/pubspec.yaml @@ -3,7 +3,7 @@ name: web_unicode publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dev_dependencies: args: any diff --git a/tools/android_lint/pubspec.yaml b/tools/android_lint/pubspec.yaml index 4c62944ea4617..f60899e9ae2d8 100644 --- a/tools/android_lint/pubspec.yaml +++ b/tools/android_lint/pubspec.yaml @@ -4,7 +4,7 @@ name: android_lint environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party.pkg, //third_party/dart/pkg, or diff --git a/tools/api_check/pubspec.yaml b/tools/api_check/pubspec.yaml index ae99d7afd5322..1f33a38ad53f7 100644 --- a/tools/api_check/pubspec.yaml +++ b/tools/api_check/pubspec.yaml @@ -14,7 +14,7 @@ publish_to: none # relative to this directory into //third_party/dart environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/tools/build_bucket_golden_scraper/pubspec.yaml b/tools/build_bucket_golden_scraper/pubspec.yaml index 9d38d8327d97e..f9855c0958e4a 100644 --- a/tools/build_bucket_golden_scraper/pubspec.yaml +++ b/tools/build_bucket_golden_scraper/pubspec.yaml @@ -5,7 +5,7 @@ name: build_bucket_golden_scraper publish_to: none environment: - sdk: ^3.0.0 + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/tools/clang_tidy/README.md b/tools/clang_tidy/README.md index 460ecbbfb7959..2d82b4b105658 100644 --- a/tools/clang_tidy/README.md +++ b/tools/clang_tidy/README.md @@ -1,15 +1,103 @@ # clang_tidy -This is a Dart program/library that runs clang_tidy over modified files in the Flutter engine repo. +A wrapper library and program that runs `clang_tidy` on the Flutter engine repo. -By default the linter runs on the repo files changed contained in `src/out/host_debug/compile_commands.json` command. -To check files other than in `host_debug` use `--target-variant android_debug_unopt`, -`--target-variant ios_debug_sim_unopt`, etc. +```shell +# Assuming you are in the `flutter` root of the engine repo. +dart ./tools/clang_tidy/bin/main.dart +``` + +By default, the linter runs over _modified_[^1] files in the _latest_[^2] build +of the engine. + +A subset of checks can also be fixed automatically by passing `--fix`: + +```shell +dart ./tools/clang_tidy/bin/main.dart --fix +``` + +To configure what lints are enabled, see [`.clang-tidy`](../../.clang-tidy). + +> **💡 TIP**: If you're looking for the git pre-commit hook configuration, see +> [`githooks`](../githooks). + +## Advanced Usage + +Some common use cases are described below, or use `--help` to see all options. + +### Run with checks added or removed + +To run adding a check _not_ specified in `.clang-tidy`: + +```shell +dart ./tools/clang_tidy/bin/main.dart --checks="" +``` + +It's possible also to use wildcards to add multiple checks: + +```shell +dart ./tools/clang_tidy/bin/main.dart --checks="readability-*" +``` + +To remove a specific check: + +```shell +dart ./tools/clang_tidy/bin/main.dart --checks="-" +``` + +To remove multiple checks: -Alternatively, use `--compile-commands` to specify a path to a `compile_commands.json` file. +```shell +dart ./tools/clang_tidy/bin/main.dart --checks="-readability-*" +``` + +To remove _all_ checks (usually to add a specific check): + +```shell +dart ./tools/clang_tidy/bin/main.dart --checks="-*," +``` + +### Specify a specific build + +There are some rules that are only applicable to certain builds, or to check +a difference in behavior between two builds. + +Use `--target-variant` to specify a build: + +```shell +dart ./tools/clang_tidy/bin/main.dart --target-variant +``` + +For example, to check the `android_debug_unopt` build: +```shell +dart ./tools/clang_tidy/bin/main.dart --target-variant android_debug_unopt ``` -$ bin/main.dart --target-variant -$ bin/main.dart --compile-commands -$ bin/main.dart --help + +In rarer cases, for example comparing two different checkouts of the engine, +use `--src-dir=`. + +### Lint entire repository + +When adding a new lint rule, or when checking lint rules that impact files that +have not changed. + +Use `--lint-all` to lint all files in the repo: + +```shell +dart ./tools/clang_tidy/bin/main.dart --lint-all ``` + +Or, provide a regular expression to lint files that match: + +```shell +dart ./tools/clang_tidy/bin/main.dart --lint-regex=".*test.*\.cc" +``` + +> **⚠️ WARNING**: This may take a long time to run if a pattern is not provided +> or if the pattern matches a large number of files, i.e. on the order of +> thousands of files could take 30 minutes or more to run and lock your +> machine. + +[^1]: Modified files are determined by a `git diff` command compared to `HEAD`. +[^2]: Latest build is the last updated directory in `src/out/`. diff --git a/tools/clang_tidy/lib/clang_tidy.dart b/tools/clang_tidy/lib/clang_tidy.dart index f54d3c78a15e2..e3757b9f40d83 100644 --- a/tools/clang_tidy/lib/clang_tidy.dart +++ b/tools/clang_tidy/lib/clang_tidy.dart @@ -5,13 +5,15 @@ import 'dart:convert' show LineSplitter, jsonDecode; import 'dart:io' as io show File, stderr, stdout; +import 'package:engine_repo_tools/engine_repo_tools.dart'; +import 'package:git_repo_tools/git_repo_tools.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import 'package:process/process.dart'; import 'package:process_runner/process_runner.dart'; import 'src/command.dart'; -import 'src/git_repo.dart'; +import 'src/lint_target.dart'; import 'src/options.dart'; const String _linterOutputHeader = ''' @@ -47,14 +49,17 @@ class ClangTidy { /// Builds an instance of [ClangTidy] using a repo's [buildCommandPath]. /// /// ## Required + /// /// - [buildCommandsPath] is the path to the build_commands.json file. /// /// ## Optional + /// /// - [checksArg] are specific checks for clang-tidy to do. /// /// If omitted, checks will be determined by the `.clang-tidy` file in the /// repo. - /// - [lintAll] when true indicates that all files should be linted. + /// + /// - [lintTarget] is what files to lint. /// /// ## Optional (Test Overrides) /// @@ -75,8 +80,7 @@ class ClangTidy { ClangTidy({ required io.File buildCommandsPath, String checksArg = '', - bool lintAll = false, - bool lintHead = false, + LintTarget lintTarget = const LintChanged(), bool fix = false, StringSink? outSink, StringSink? errSink, @@ -85,32 +89,35 @@ class ClangTidy { options = Options( buildCommandsPath: buildCommandsPath, checksArg: checksArg, - lintAll: lintAll, - lintHead: lintHead, + lintTarget: lintTarget, fix: fix, errSink: errSink, ), _outSink = outSink ?? io.stdout, _errSink = errSink ?? io.stderr, - _processManager = processManager; + _processManager = processManager, + _engine = null; /// Builds an instance of [ClangTidy] from a command line. ClangTidy.fromCommandLine( List args, { + Engine? engine, StringSink? outSink, StringSink? errSink, ProcessManager processManager = const LocalProcessManager(), }) : - options = Options.fromCommandLine(args, errSink: errSink), + options = Options.fromCommandLine(args, errSink: errSink, engine: engine), _outSink = outSink ?? io.stdout, _errSink = errSink ?? io.stderr, - _processManager = processManager; + _processManager = processManager, + _engine = engine; /// The [Options] that specify how this [ClangTidy] operates. final Options options; final StringSink _outSink; final StringSink _errSink; final ProcessManager _processManager; + final Engine? _engine; late final DateTime _startTime; @@ -119,12 +126,12 @@ class ClangTidy { _startTime = DateTime.now(); if (options.help) { - options.printUsage(); + options.printUsage(engine: _engine); return 0; } if (options.errorMessage != null) { - options.printUsage(message: options.errorMessage); + options.printUsage(message: options.errorMessage, engine: _engine); return 1; } @@ -138,12 +145,23 @@ class ClangTidy { _outSink.writeln('Checking for specific checks: ${options.checks}.'); } final int changedFilesCount = filesOfInterest.length; - if (options.lintAll) { - _outSink.writeln('Checking all $changedFilesCount files the repo dir.'); - } else { - _outSink.writeln( - 'Dectected $changedFilesCount files that have changed', - ); + switch (options.lintTarget) { + case LintAll(): + _outSink.writeln('Checking all $changedFilesCount files in the repo.'); + case LintChanged(): + _outSink.writeln( + 'Checking $changedFilesCount files that have changed since the ' + 'last commit.', + ); + case LintHead(): + _outSink.writeln( + 'Checking $changedFilesCount files that have changed compared to ' + 'HEAD.', + ); + case LintRegex(:final String regex): + _outSink.writeln( + 'Checking $changedFilesCount files that match the regex "$regex".', + ); } } @@ -195,26 +213,39 @@ class ClangTidy { return computeResult + runResult > 0 ? 1 : 0; } - /// The files with local modifications or all the files if `lintAll` was - /// specified. + /// The files with local modifications or all/a subset of all files. + /// + /// See [LintTarget] for more information. @visibleForTesting Future> computeFilesOfInterest() async { - if (options.lintAll) { - return options.repoPath - .listSync(recursive: true) - .whereType() - .toList(); - } - - final GitRepo repo = GitRepo( - options.repoPath, - processManager: _processManager, - verbose: options.verbose, - ); - if (options.lintHead) { - return repo.changedFilesAtHead; + switch (options.lintTarget) { + case LintAll(): + return options.repoPath + .listSync(recursive: true) + .whereType() + .toList(); + case LintRegex(:final String regex): + final RegExp pattern = RegExp(regex); + return options.repoPath + .listSync(recursive: true) + .whereType() + .where((io.File file) => pattern.hasMatch(file.path)) + .toList(); + case LintChanged(): + final GitRepo repo = GitRepo.fromRoot( + options.repoPath, + processManager: _processManager, + verbose: options.verbose, + ); + return repo.changedFiles; + case LintHead(): + final GitRepo repo = GitRepo.fromRoot( + options.repoPath, + processManager: _processManager, + verbose: options.verbose, + ); + return repo.changedFilesAtHead; } - return repo.changedFiles; } /// Returns f(n) = value(n * [shardCount] + [id]). diff --git a/tools/clang_tidy/lib/src/lint_target.dart b/tools/clang_tidy/lib/src/lint_target.dart new file mode 100644 index 0000000000000..8f45ae9ccce95 --- /dev/null +++ b/tools/clang_tidy/lib/src/lint_target.dart @@ -0,0 +1,40 @@ +// 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. + +/// Describes what should be linted by the Clang Tidy tool. +sealed class LintTarget { + /// Creates a new [LintTarget]. + const LintTarget(); +} + +/// Lints all files in the project. +final class LintAll extends LintTarget { + /// Defines a lint target that lints all files in the project. + const LintAll(); +} + +/// Lint all files that have changed since the last commit. +/// +/// This considers only the last commit, not all commits in the current branch. +final class LintChanged extends LintTarget { + /// Defines a lint target of files that have changed since the last commit. + const LintChanged(); +} + +/// Lint all files that have changed compared to HEAD. +/// +/// This considers _all_ commits in the current branch, not just the last one. +final class LintHead extends LintTarget { + /// Defines a lint target of files that have changed compared to HEAD. + const LintHead(); +} + +/// Lint all files whose paths match the given regex. +final class LintRegex extends LintTarget { + /// Creates a new [LintRegex] with the given [regex]. + const LintRegex(this.regex); + + /// The regular expression to match against file paths. + final String regex; +} diff --git a/tools/clang_tidy/lib/src/options.dart b/tools/clang_tidy/lib/src/options.dart index 401d34b8e81bc..f9d5320fb18a4 100644 --- a/tools/clang_tidy/lib/src/options.dart +++ b/tools/clang_tidy/lib/src/options.dart @@ -5,11 +5,12 @@ import 'dart:io' as io show Directory, File, Platform, stderr; import 'package:args/args.dart'; +import 'package:engine_repo_tools/engine_repo_tools.dart'; import 'package:path/path.dart' as path; -// Path to root of the flutter/engine repository containing this script. -final String _engineRoot = path.dirname(path.dirname(path.dirname(path.dirname(path.fromUri(io.Platform.script))))); +import 'lint_target.dart'; +final Engine _engineRoot = Engine.findWithin(path.dirname(path.fromUri(io.Platform.script))); /// Adds warnings as errors for only specific runs. This is helpful if migrating one platform at a time. String? _platformSpecificWarningsAsErrors(ArgResults options) { @@ -29,8 +30,7 @@ class Options { this.help = false, this.verbose = false, this.checksArg = '', - this.lintAll = false, - this.lintHead = false, + this.lintTarget = const LintChanged(), this.fix = false, this.errorMessage, this.warningsAsErrors, @@ -70,14 +70,22 @@ class Options { required List shardCommandsPaths, int? shardId, }) { + final LintTarget lintTarget; + if (options.wasParsed('lint-all') || io.Platform.environment['FLUTTER_LINT_ALL'] != null) { + lintTarget = const LintAll(); + } else if (options.wasParsed('lint-regex')) { + lintTarget = LintRegex(options['lint-regex'] as String? ?? ''); + } else if (options.wasParsed('lint-head')) { + lintTarget = const LintHead(); + } else { + lintTarget = const LintChanged(); + } return Options( help: options['help'] as bool, verbose: options['verbose'] as bool, buildCommandsPath: buildCommandsPath, checksArg: options.wasParsed('checks') ? options['checks'] as String : '', - lintAll: io.Platform.environment['FLUTTER_LINT_ALL'] != null || - options['lint-all'] as bool, - lintHead: options['lint-head'] as bool, + lintTarget: lintTarget, fix: options['fix'] as bool, errSink: errSink, warningsAsErrors: _platformSpecificWarningsAsErrors(options), @@ -91,8 +99,13 @@ class Options { factory Options.fromCommandLine( List arguments, { StringSink? errSink, + Engine? engine, }) { - final ArgResults argResults = _argParser.parse(arguments); + // TODO(matanlurey): Refactor this further, ideally moving all of the engine + // resolution logic (i.e. --src-dir, --target-variant, --compile-commands) + // into a separate method, and perhaps also adding `engine.output(name)` + // to engine_repo_tools instead of path manipulation inlined below. + final ArgResults argResults = _argParser(defaultEngine: engine).parse(arguments); String? buildCommandsPath = argResults['compile-commands'] as String?; @@ -134,74 +147,92 @@ class Options { ); } - static final ArgParser _argParser = ArgParser() - ..addFlag( - 'help', - abbr: 'h', - help: 'Print help.', - negatable: false, - ) - ..addFlag( - 'lint-all', - help: 'Lint all of the sources, regardless of FLUTTER_NOLINT.', - ) - ..addFlag( - 'lint-head', - help: 'Lint files changed in the tip-of-tree commit.', - ) - ..addFlag( - 'fix', - help: 'Apply suggested fixes.', - ) - ..addFlag( - 'verbose', - help: 'Print verbose output.', - ) - ..addOption( - 'shard-id', - help: 'When used with the shard-commands option this identifies which shard will execute.', - valueHelp: 'A number less than 1 + the number of shard-commands arguments.', - ) - ..addOption( - 'shard-variants', - help: 'Comma separated list of other targets, this invocation ' - 'will only execute a subset of the intersection and the difference of the ' - 'compile commands. Use with `shard-id`.' - ) - ..addOption( - 'compile-commands', - help: 'Use the given path as the source of compile_commands.json. This ' - 'file is created by running "tools/gn". Cannot be used with --target-variant ' - 'or --src-dir.', - ) - ..addOption( - 'target-variant', - aliases: ['variant'], - help: 'The engine variant directory containing compile_commands.json ' - 'created by running "tools/gn". Cannot be used with --compile-commands.', - valueHelp: 'host_debug|android_debug_unopt|ios_debug|ios_debug_sim_unopt', - defaultsTo: 'host_debug', - ) - ..addOption('mac-host-warnings-as-errors', + static ArgParser _argParser({required Engine? defaultEngine}) { + defaultEngine ??= _engineRoot; + final io.Directory? latestBuild = defaultEngine.latestOutput()?.path; + return ArgParser() + ..addFlag( + 'help', + abbr: 'h', + help: 'Print help.', + negatable: false, + ) + ..addOption( + 'lint-regex', + help: 'Lint all files, regardless of FLUTTER_NOLINT. Provide a regex ' + 'to filter files. For example, `--lint-regex=".*impeller.*"` will ' + 'lint all files within a path that contains "impeller".', + valueHelp: 'regex', + ) + ..addFlag( + 'lint-all', + help: 'Lint all files, regardless of FLUTTER_NOLINT.', + ) + ..addFlag( + 'lint-head', + help: 'Lint files changed in the tip-of-tree commit.', + ) + ..addFlag( + 'fix', + help: 'Apply suggested fixes.', + ) + ..addFlag( + 'verbose', + help: 'Print verbose output.', + ) + ..addOption( + 'shard-id', + help: 'When used with the shard-commands option this identifies which shard will execute.', + valueHelp: 'A number less than 1 + the number of shard-commands arguments.', + ) + ..addOption( + 'shard-variants', + help: 'Comma separated list of other targets, this invocation ' + 'will only execute a subset of the intersection and the difference of the ' + 'compile commands. Use with `shard-id`.' + ) + ..addOption( + 'compile-commands', + help: 'Use the given path as the source of compile_commands.json. This ' + 'file is created by running "tools/gn". Cannot be used with --target-variant ' + 'or --src-dir.', + ) + ..addOption( + 'target-variant', + aliases: ['variant'], + help: 'The engine variant directory name containing compile_commands.json ' + 'created by running "tools/gn".\n\nIf not provided, the default is ' + 'the latest build in the engine defined by --src-dir (or the ' + 'default path, see --src-dir for details).\n\n' + 'Cannot be used with --compile-commands.', + valueHelp: 'host_debug|android_debug_unopt|ios_debug|ios_debug_sim_unopt', + defaultsTo: latestBuild == null ? 'host_debug' : path.basename(latestBuild.path), + ) + ..addOption('mac-host-warnings-as-errors', + help: + 'checks that will be treated as errors when running debug_host on mac.') + ..addOption( + 'src-dir', help: - 'checks that will be treated as errors when running debug_host on mac.') - ..addOption( - 'src-dir', - help: 'Path to the engine src directory. Cannot be used with --compile-commands.', - valueHelp: 'path/to/engine/src', - defaultsTo: path.dirname(_engineRoot), - ) - ..addOption( - 'checks', - help: 'Perform the given checks on the code. Defaults to the empty ' - 'string, indicating all checks should be performed.', - defaultsTo: '', - ) - ..addFlag( - 'enable-check-profile', - help: 'Enable per-check timing profiles and print a report to stderr.', - negatable: false, - ); + 'Path to the engine src directory.\n\n' + 'If not provided, the default is the engine root directory that ' + 'contains the `clang_tidy` tool.\n\n' + 'Cannot be used with --compile-commands.', + valueHelp: 'path/to/engine/src', + defaultsTo: _engineRoot.srcDir.path, + ) + ..addOption( + 'checks', + help: 'Perform the given checks on the code. Defaults to the empty ' + 'string, indicating all checks should be performed.', + defaultsTo: '', + ) + ..addFlag( + 'enable-check-profile', + help: 'Enable per-check timing profiles and print a report to stderr.', + negatable: false, + ); + } /// Whether to print a help message and exit. final bool help; @@ -219,7 +250,7 @@ class Options { final int? shardId; /// The root of the flutter/engine repository. - final io.Directory repoPath = io.Directory(_engineRoot); + final io.Directory repoPath = _engineRoot.flutterDir; /// Argument sent as `warnings-as-errors` to clang-tidy. final String? warningsAsErrors; @@ -230,11 +261,8 @@ class Options { /// Check argument to be supplied to the clang-tidy subprocess. final String? checks; - /// Whether all files should be linted. - final bool lintAll; - - /// Whether to lint only files changed in the tip-of-tree commit. - final bool lintHead; + /// What files to lint. + final LintTarget lintTarget; /// Whether checks should apply available fix-ups to the working copy. final bool fix; @@ -249,7 +277,7 @@ class Options { final StringSink _errSink; /// Print command usage with an additional message. - void printUsage({String? message}) { + void printUsage({String? message, required Engine? engine}) { if (message != null) { _errSink.writeln(message); } @@ -257,7 +285,7 @@ class Options { 'Usage: bin/main.dart [--help] [--lint-all] [--lint-head] [--fix] [--verbose] ' '[--diff-branch] [--target-variant variant] [--src-dir path/to/engine/src]', ); - _errSink.writeln(_argParser.usage); + _errSink.writeln(_argParser(defaultEngine: engine).usage); } /// Command line argument validation. @@ -275,8 +303,8 @@ class Options { return 'ERROR: --compile-commands option cannot be used with --src-dir.'; } - if (argResults.wasParsed('lint-all') && argResults.wasParsed('lint-head')) { - return 'ERROR: At most one of --lint-all and --lint-head can be passed.'; + if (const ['lint-all', 'lint-head', 'lint-regex'].where(argResults.wasParsed).length > 1) { + return 'ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.'; } if (!buildCommandsPath.existsSync()) { diff --git a/tools/clang_tidy/pubspec.yaml b/tools/clang_tidy/pubspec.yaml index 5e33c8c0db0e5..3dbe9e04ac86a 100644 --- a/tools/clang_tidy/pubspec.yaml +++ b/tools/clang_tidy/pubspec.yaml @@ -5,7 +5,7 @@ name: clang_tidy publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or @@ -18,6 +18,8 @@ environment: dependencies: args: any + git_repo_tools: any + engine_repo_tools: any meta: any path: any process: any @@ -27,6 +29,7 @@ dev_dependencies: async_helper: any expect: any litetest: any + process_fakes: any smith: any dependency_overrides: @@ -38,10 +41,14 @@ dependency_overrides: path: ../../../third_party/dart/pkg/async_helper collection: path: ../../../third_party/dart/third_party/pkg/collection + engine_repo_tools: + path: ../pkg/engine_repo_tools expect: path: ../../../third_party/dart/pkg/expect file: path: ../../../third_party/pkg/file/packages/file + git_repo_tools: + path: ../pkg/git_repo_tools litetest: path: ../../testing/litetest meta: @@ -52,6 +59,8 @@ dependency_overrides: path: ../../../third_party/pkg/platform process: path: ../../../third_party/pkg/process + process_fakes: + path: ../pkg/process_fakes process_runner: path: ../../../third_party/pkg/process_runner smith: diff --git a/tools/clang_tidy/test/clang_tidy_test.dart b/tools/clang_tidy/test/clang_tidy_test.dart index 5fcdd984a73e0..da3bbf921d171 100644 --- a/tools/clang_tidy/test/clang_tidy_test.dart +++ b/tools/clang_tidy/test/clang_tidy_test.dart @@ -6,19 +6,22 @@ import 'dart:io' as io show Directory, File, Platform, stderr; import 'package:clang_tidy/clang_tidy.dart'; import 'package:clang_tidy/src/command.dart'; +import 'package:clang_tidy/src/lint_target.dart'; import 'package:clang_tidy/src/options.dart'; +import 'package:engine_repo_tools/engine_repo_tools.dart'; import 'package:litetest/litetest.dart'; import 'package:path/path.dart' as path; import 'package:process/process.dart'; +import 'package:process_fakes/process_fakes.dart'; import 'package:process_runner/process_runner.dart'; -import 'process_fakes.dart'; /// A test fixture for the `clang-tidy` tool. final class Fixture { /// Simulates running the tool with the given [args]. factory Fixture.fromCommandLine(List args, { ProcessManager? processManager, + Engine? engine, }) { processManager ??= FakeProcessManager(); final StringBuffer outBuffer = StringBuffer(); @@ -28,6 +31,7 @@ final class Fixture { outSink: outBuffer, errSink: errBuffer, processManager: processManager, + engine: engine, ), errBuffer, outBuffer); } @@ -40,8 +44,7 @@ final class Fixture { final StringBuffer errBuffer = StringBuffer(); return Fixture._(ClangTidy( buildCommandsPath: options.buildCommandsPath, - lintAll: options.lintAll, - lintHead: options.lintHead, + lintTarget: options.lintTarget, fix: options.fix, outSink: outBuffer, errSink: errBuffer, @@ -106,21 +109,58 @@ void _withTempFile(String prefix, void Function(String path) func) { } Future main(List args) async { - if (args.isEmpty) { + final String? buildCommands = + args.firstOrNull ?? + Engine.findWithin().latestOutput()?.compileCommandsJson.path; + + if (buildCommands == null || args.length > 1) { io.stderr.writeln( 'Usage: clang_tidy_test.dart [path/to/compile_commands.json]', ); return 1; } - final String buildCommands = args[0]; - test('--help gives help', () async { - final Fixture fixture = Fixture.fromCommandLine(['--help']); - final int result = await fixture.tool.run(); + test('--help gives help, and uses host_debug by default outside of an engine root', () async { + final io.Directory rootDir = io.Directory.systemTemp.createTempSync('clang_tidy_test'); + try { + final Fixture fixture = Fixture.fromCommandLine( + ['--help'], + engine: TestEngine.createTemp(rootDir: rootDir) + ); + final int result = await fixture.tool.run(); - expect(fixture.tool.options.help, isTrue); - expect(result, equals(0)); - expect(fixture.errBuffer.toString(), contains('Usage: ')); + expect(fixture.tool.options.help, isTrue); + expect(result, equals(0)); + + final String errors = fixture.errBuffer.toString(); + expect(errors, contains('Usage: ')); + expect(errors, contains('defaults to "host_debug"')); + } finally { + rootDir.deleteSync(recursive: true); + } + }); + + test('--help gives help, and uses the latest build by default outside in an engine root', () async { + final io.Directory rootDir = io.Directory.systemTemp.createTempSync('clang_tidy_test'); + final io.Directory buildDir = io.Directory(path.join(rootDir.path, 'out', 'host_debug_unopt_arm64'))..createSync(recursive: true); + try { + final Fixture fixture = Fixture.fromCommandLine( + ['--help'], + engine: TestEngine.createTemp(rootDir: rootDir, outputs: [ + TestOutput(buildDir), + ]) + ); + final int result = await fixture.tool.run(); + + expect(fixture.tool.options.help, isTrue); + expect(result, equals(0)); + + final String errors = fixture.errBuffer.toString(); + expect(errors, contains('Usage: ')); + expect(errors, contains('defaults to "host_debug_unopt_arm64"')); + } finally { + rootDir.deleteSync(recursive: true); + } }); test('trimmed clang-tidy output', () { @@ -235,7 +275,24 @@ Future main(List args) async { expect(result, equals(1)); expect(fixture.errBuffer.toString(), contains( - 'ERROR: At most one of --lint-all and --lint-head can be passed.', + 'ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.', + )); + }); + + test('Error when --lint-all and --lint-regex are used together', () async { + final Fixture fixture = Fixture.fromCommandLine( + [ + '--compile-commands', + '/unused', + '--lint-all', + '--lint-regex=".*"', + ], + ); + final int result = await fixture.tool.run(); + + expect(result, equals(1)); + expect(fixture.errBuffer.toString(), contains( + 'ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.', )); }); @@ -243,7 +300,7 @@ Future main(List args) async { final Fixture fixture = Fixture.fromOptions( Options( buildCommandsPath: io.File(buildCommands), - lintAll: true, + lintTarget: const LintAll(), ), ); final List fileList = await fixture.tool.computeFilesOfInterest(); @@ -256,7 +313,7 @@ Future main(List args) async { buildCommandsPath: io.File(buildCommands), // Intentional: // ignore: avoid_redundant_argument_values - lintAll: false, + lintTarget: const LintChanged(), ), processManager: FakeProcessManager( onStart: (List command) { @@ -272,11 +329,31 @@ Future main(List args) async { expect(fileList.length, lessThan(300)); }); + test('lintAll=pattern checks based on a RegEx', () async { + final Fixture fixture = Fixture.fromOptions( + Options( + buildCommandsPath: io.File(buildCommands), + lintTarget: const LintRegex(r'.*test.*\.cc$'), + ), + processManager: FakeProcessManager( + onStart: (List command) { + if (command.first == 'git') { + // This just allows git to not actually be called. + return FakeProcess(); + } + return FakeProcessManager.unhandledStart(command); + }, + ), + ); + final List fileList = await fixture.tool.computeFilesOfInterest(); + expect(fileList.length, lessThan(1000)); + }); + test('Sharding', () async { final Fixture fixture = Fixture.fromOptions( Options( buildCommandsPath: io.File(buildCommands), - lintAll: true, + lintTarget: const LintAll(), ), processManager: FakeProcessManager( onStart: (List command) { @@ -352,7 +429,7 @@ Future main(List args) async { final Fixture fixture = Fixture.fromOptions( Options( buildCommandsPath: io.File(buildCommands), - lintAll: true, + lintTarget: const LintAll(), ), ); @@ -378,7 +455,7 @@ Future main(List args) async { final Fixture fixture = Fixture.fromOptions( Options( buildCommandsPath: io.File(buildCommands), - lintAll: true, + lintTarget: const LintAll(), ), ); diff --git a/tools/const_finder/BUILD.gn b/tools/const_finder/BUILD.gn index c20766d7d8643..d628749c26fca 100644 --- a/tools/const_finder/BUILD.gn +++ b/tools/const_finder/BUILD.gn @@ -13,7 +13,6 @@ application_snapshot("const_finder") { inputs = [ "bin/main.dart", - "lib/const_finder.dart", ".dart_tool/package_config.json", ] diff --git a/tools/const_finder/bin/main.dart b/tools/const_finder/bin/main.dart index d20423687ed4d..0a64dc452f8b6 100644 --- a/tools/const_finder/bin/main.dart +++ b/tools/const_finder/bin/main.dart @@ -6,7 +6,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:args/args.dart'; -import 'package:const_finder/const_finder.dart'; +import 'package:kernel/const_finder.dart'; void main(List args) { final ArgParser parser = ArgParser(); diff --git a/tools/const_finder/lib/const_finder.dart b/tools/const_finder/lib/const_finder.dart deleted file mode 100644 index 84f9c809b2f1e..0000000000000 --- a/tools/const_finder/lib/const_finder.dart +++ /dev/null @@ -1,207 +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. - -import 'dart:collection'; - -import 'package:kernel/kernel.dart'; - -class _ConstVisitor extends RecursiveVisitor { - _ConstVisitor( - this.kernelFilePath, - this.classLibraryUri, - this.className, - this.annotationClassLibraryUri, - this.annotationClassName, - ) : _visitedInstances = {}, - constantInstances = >[], - nonConstantLocations = >[]; - - /// The path to the file to open. - final String kernelFilePath; - - /// The library URI for the class to find. - final String classLibraryUri; - - /// The name of the class to find. - final String className; - - final Set _visitedInstances; - final List> constantInstances; - final List> nonConstantLocations; - - bool inIgnoredClass = false; - - /// Whether or not we are currently within the declaration of the target class. - /// - /// We use this to determine when to skip tracking non-constant - /// [ConstructorInvocation]s. This is because, in web builds, a static - /// method is always created called _#new#tearOff() which returns the result - /// of a non-constant invocation of the unnamed constructor. - /// - /// For the following Dart class "FooBar": - /// - /// class FooBar { - /// const FooBar(); - /// } - /// - /// The following kernel structure is generated: - /// - /// class FooBar extends core::Object /*hasConstConstructor*/ { - /// const constructor •() → min::FooBar - /// : super core::Object::•() - /// ; - /// static method _#new#tearOff() → min::FooBar - /// return new min::FooBar::•(); /* this is a non-const constructor invocation */ - /// method noOp() → void {} - /// } - bool inTargetClass = false; - - bool inTargetTearOff = false; - - /// The name of the name of the class of the annotation marking classes - /// whose constant references should be ignored. - final String? annotationClassName; - - /// The library URI of the class of the annotation marking classes whose - /// constant references should be ignored. - final String? annotationClassLibraryUri; - - // A cache of previously evaluated classes. - static final Map _classHeirarchyCache = {}; - bool _matches(Class node) { - final bool? result = _classHeirarchyCache[node]; - if (result != null) { - return result; - } - final bool exactMatch = node.name == className - && node.enclosingLibrary.importUri.toString() == classLibraryUri; - final bool match = exactMatch - || node.supers.any((Supertype supertype) => _matches(supertype.classNode)); - _classHeirarchyCache[node] = match; - return match; - } - - // Avoid visiting the same constant more than once. - final Set _cache = LinkedHashSet.identity(); - - @override - void visitProcedure(Procedure node) { - final bool isTearOff = node.isStatic && - node.kind == ProcedureKind.Method && - node.name.text == '_#new#tearOff'; - if (inTargetClass && isTearOff) { - inTargetTearOff = true; - } - super.visitProcedure(node); - inTargetTearOff = false; - } - - @override - void defaultConstant(Constant node) { - if (_cache.add(node)) { - super.defaultConstant(node); - } - } - - @override - void defaultConstantReference(Constant node) { - defaultConstant(node); - } - - @override - void visitConstructorInvocation(ConstructorInvocation node) { - final Class parentClass = node.target.parent! as Class; - if (!inTargetTearOff && _matches(parentClass)) { - final Location location = node.location!; - nonConstantLocations.add({ - 'file': location.file.toString(), - 'line': location.line, - 'column': location.column, - }); - } - super.visitConstructorInvocation(node); - } - - @override - void visitClass(Class node) { - inTargetClass = _matches(node); - // check if this is a class that we should ignore - inIgnoredClass = _classShouldBeIgnored(node); - super.visitClass(node); - inTargetClass = false; - inIgnoredClass = false; - } - - // If any annotations on the class match annotationClassName AND - // annotationClassLibraryUri. - bool _classShouldBeIgnored(Class node) { - if (annotationClassName == null || annotationClassLibraryUri == null) { - return false; - } - return node.annotations.any((Expression expression) { - if (expression is! ConstantExpression) { - return false; - } - - final Constant constant = expression.constant; - return constant is InstanceConstant - && constant.classNode.name == annotationClassName - && constant.classNode.enclosingLibrary.importUri.toString() == annotationClassLibraryUri; - }); - } - - @override - void visitInstanceConstantReference(InstanceConstant node) { - super.visitInstanceConstantReference(node); - if (!_matches(node.classNode) || inIgnoredClass) { - return; - } - - final Map instance = {}; - for (final MapEntry kvp in node.fieldValues.entries) { - if (kvp.value is! PrimitiveConstant) { - continue; - } - final PrimitiveConstant value = kvp.value as PrimitiveConstant; - instance[kvp.key.asField.name.text] = value.value; - } - if (_visitedInstances.add(instance.toString())) { - constantInstances.add(instance); - } - } -} - -/// A kernel AST visitor that finds const references. -class ConstFinder { - /// Creates a new ConstFinder class. - /// - /// The `kernelFilePath` is the path to a dill (kernel) file to process. - ConstFinder({ - required String kernelFilePath, - required String classLibraryUri, - required String className, - String? annotationClassLibraryUri, - String? annotationClassName, - }) : _visitor = _ConstVisitor( - kernelFilePath, - classLibraryUri, - className, - annotationClassLibraryUri, - annotationClassName, - ); - - final _ConstVisitor _visitor; - - /// Finds all instances - Map findInstances() { - _visitor._visitedInstances.clear(); - for (final Library library in loadComponentFromBinary(_visitor.kernelFilePath).libraries) { - library.visitChildren(_visitor); - } - return { - 'constantInstances': _visitor.constantInstances, - 'nonConstantLocations': _visitor.nonConstantLocations, - }; - } -} diff --git a/tools/const_finder/pubspec.yaml b/tools/const_finder/pubspec.yaml index b174754fff0df..c7d2b9f0f82bc 100644 --- a/tools/const_finder/pubspec.yaml +++ b/tools/const_finder/pubspec.yaml @@ -6,7 +6,7 @@ name: const_finder publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/dart/pkg or //third_party/dart/third_party/pkg. diff --git a/tools/const_finder/test/const_finder_test.dart b/tools/const_finder/test/const_finder_test.dart index 5df4791094465..552c6e7bca691 100644 --- a/tools/const_finder/test/const_finder_test.dart +++ b/tools/const_finder/test/const_finder_test.dart @@ -8,7 +8,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:collection/collection.dart'; -import 'package:const_finder/const_finder.dart'; +import 'package:kernel/const_finder.dart'; import 'package:path/path.dart' as path; void expect(T value, T expected) { diff --git a/tools/fuchsia/build_fuchsia_artifacts.py b/tools/fuchsia/build_fuchsia_artifacts.py index 4acf13259fac0..32af340e9a644 100755 --- a/tools/fuchsia/build_fuchsia_artifacts.py +++ b/tools/fuchsia/build_fuchsia_artifacts.py @@ -235,7 +235,7 @@ def CopyBuildToBucket(runtime_mode, arch, optimized, product): bucket_root = os.path.join(_bucket_directory, 'flutter') licenses_root = os.path.join(_src_root_dir, 'flutter/ci/licenses_golden') license_files = [ - 'licenses_flutter', 'licenses_fuchsia', 'licenses_gpu', 'licenses_skia', + 'licenses_flutter', 'licenses_fuchsia', 'licenses_skia', 'licenses_third_party' ] for license in license_files: @@ -495,9 +495,17 @@ def main(): if args.copy_unoptimized_debug_artifacts and runtime_mode == 'debug' and optimized: CopyBuildToBucket(runtime_mode, arch, not optimized, product) + # Set revision to HEAD if empty and remove upload. This is to support + # presubmit workflows. + should_upload = args.upload + engine_version = args.engine_version + if not engine_version: + engine_version = 'HEAD' + should_upload = False + # Create and optionally upload CIPD package if args.cipd_dry_run or args.upload: - ProcessCIPDPackage(args.upload, args.engine_version) + ProcessCIPDPackage(should_upload, engine_version) return 0 diff --git a/tools/fuchsia/merge_and_upload_debug_symbols.py b/tools/fuchsia/merge_and_upload_debug_symbols.py index e27fd0137bdff..cec5768d07224 100755 --- a/tools/fuchsia/merge_and_upload_debug_symbols.py +++ b/tools/fuchsia/merge_and_upload_debug_symbols.py @@ -214,7 +214,17 @@ def main(): arch = args.target_arch cipd_def = WriteCIPDDefinition(arch, out_dir, internal_symbol_dirs) - ProcessCIPDPackage(args.upload, cipd_def, args.engine_version, out_dir, arch) + + # Set revision to HEAD if empty and remove upload. This is to support + # presubmit workflows. An empty engine_version means this script is running + # on presubmit. + should_upload = args.upload + engine_version = args.engine_version + if not engine_version: + engine_version = 'HEAD' + should_upload = False + + ProcessCIPDPackage(should_upload, cipd_def, engine_version, out_dir, arch) return 0 diff --git a/tools/gen_web_locale_keymap/pubspec.yaml b/tools/gen_web_locale_keymap/pubspec.yaml index 888b50029207b..5fee8ff44e90f 100644 --- a/tools/gen_web_locale_keymap/pubspec.yaml +++ b/tools/gen_web_locale_keymap/pubspec.yaml @@ -2,7 +2,7 @@ name: gen_web_keyboard_keymap description: Generates keyboard layouts for Web from external sources. environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: args: any diff --git a/tools/githooks/README.md b/tools/githooks/README.md index 1ac57c8ca3326..ce0ab06a24216 100644 --- a/tools/githooks/README.md +++ b/tools/githooks/README.md @@ -21,8 +21,8 @@ scripts have the names that `git` will look for. This hooks runs when pushing commits to a remote branch, for example to create or update a pull request: `git push origin my-local-branch`. -The `pre-push` hook runs `ci/lint.sh` and `ci/format.sh`. `ci/analyze.sh` and -`ci/licenses.sh` are more expensive and are not run. +The `pre-push` hook runs `ci/clang_tidy.sh`, `ci/pylint.sh` and `ci/format.sh`. +`ci/analyze.sh` and `ci/licenses.sh` are more expensive and are not run. ### Adding new pre-push checks diff --git a/tools/githooks/pubspec.yaml b/tools/githooks/pubspec.yaml index 6b5bb5c2340a7..66755904f2fd5 100644 --- a/tools/githooks/pubspec.yaml +++ b/tools/githooks/pubspec.yaml @@ -5,7 +5,7 @@ name: githooks publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party.pkg, //third_party/dart/pkg, or @@ -18,9 +18,9 @@ environment: dependencies: args: any + clang_tidy: any meta: any path: any - clang_tidy: any dev_dependencies: async_helper: any @@ -39,10 +39,14 @@ dependency_overrides: path: ../clang_tidy collection: path: ../../../third_party/dart/third_party/pkg/collection + engine_repo_tools: + path: ../pkg/engine_repo_tools expect: path: ../../../third_party/dart/pkg/expect file: path: ../../../third_party/pkg/file/packages/file + git_repo_tools: + path: ../pkg/git_repo_tools litetest: path: ../../testing/litetest meta: diff --git a/tools/gn b/tools/gn index 5b481d6f33129..4eed7044d185f 100755 --- a/tools/gn +++ b/tools/gn @@ -106,16 +106,12 @@ def is_host_build(args): # Determines whether a prebuilt Dart SDK can be used instead of building one. -# We can use a prebuilt Dart SDK when: -# 1. It is a host build, a build targeting Fuchsia, or a build targeting desktop. -# 2. The prebuilt SDK exists under //flutter/prebuilts/$OS-$ARCH. def can_use_prebuilt_dart(args): prebuilt = None - # In a Fuchsia build, we can use a prebuilt Dart SDK for the host to build - # platform agnostic artifacts (e.g. kernel snapshots), and a Dart SDK - # targeting Fuchsia is not needed. So, it is safe to say that the prebuilt - # Dart SDK in a Fuchsia build is the host prebuilt Dart SDK. - if args.target_os is None or args.target_os == 'fuchsia': + # When doing a 'host' build (args.target_os is None), or a build when the + # target OS and host OS are different, the prebuilt Dart SDK is the Dart SDK + # for the host system's OS and archetecture. + if args.target_os is None or args.target_os in ['android', 'ios', 'fuchsia']: if sys.platform.startswith(('cygwin', 'win')): prebuilt = 'windows-x64' elif sys.platform == 'darwin': @@ -672,6 +668,9 @@ def to_gn_args(args): if args.enable_impeller_3d: gn_args['impeller_enable_3d'] = True + if args.enable_impeller_trace_canvas: + gn_args['impeller_trace_canvas'] = True + if args.enable_impeller_vulkan: gn_args['impeller_enable_vulkan'] = True @@ -1223,6 +1222,12 @@ def parse_args(args): help='Enables experimental 3d support.' ) + parser.add_argument( + '--enable-impeller-trace-canvas', + default=False, + action='store_true', + help='Enables tracing calls to Canvas.' + ) parser.add_argument( '--malioc-path', type=str, help='The path to the malioc tool.' ) diff --git a/tools/licenses/lib/paths.dart b/tools/licenses/lib/paths.dart index 4118cecee57eb..a35d1a2ac6116 100644 --- a/tools/licenses/lib/paths.dart +++ b/tools/licenses/lib/paths.dart @@ -29,7 +29,10 @@ final Set skippedPaths = { r'flutter/lib/web_ui/dev', // these are build tools; they do not end up in Engine artifacts r'flutter/prebuilts', r'flutter/sky/packages/sky_engine/LICENSE', + r'flutter/third_party/glfw/deps', // Only used by examples and tests; not linked in build. + r'flutter/third_party/glfw/docs', r'flutter/third_party/gn', + r'flutter/third_party/imgui', r'flutter/third_party/ninja', // build system r'flutter/third_party/test_shaders', // for tests only r'flutter/third_party/txt/third_party/fonts', @@ -98,8 +101,6 @@ final Set skippedPaths = { r'third_party/fontconfig', // not used in standard configurations r'third_party/freetype2/builds', r'third_party/freetype2/src/tools', - r'third_party/glfw/deps', // Only used by examples and tests; not linked in build. - r'third_party/glfw/docs', r'third_party/gradle', r'third_party/harfbuzz/docs', r'third_party/harfbuzz/util', // utils are command line tools that do not end up in the binary @@ -112,7 +113,6 @@ final Set skippedPaths = { r'third_party/icu/source/data/brkitr/dictionaries/cjdict.txt', // explicitly handled by ICU license r'third_party/icu/source/data/brkitr/dictionaries/laodict.txt', // explicitly handled by ICU license r'third_party/icu/source/data/dtd', - r'third_party/imgui', r'third_party/inja/doc', // documentation r'third_party/inja/third_party/amalgamate', // only used at build time r'third_party/inja/third_party/include/doctest', // seems to be a unit test library diff --git a/tools/licenses/pubspec.yaml b/tools/licenses/pubspec.yaml index 85f69a2bad58b..67a905e9c02c2 100644 --- a/tools/licenses/pubspec.yaml +++ b/tools/licenses/pubspec.yaml @@ -5,7 +5,7 @@ name: licenses publish_to: none environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided # in //third_party.pkg, //third_party/dart/pkg, or diff --git a/tools/path_ops/dart/pubspec.yaml b/tools/path_ops/dart/pubspec.yaml index db4d16542f5f1..d53bbbfe14ab5 100644 --- a/tools/path_ops/dart/pubspec.yaml +++ b/tools/path_ops/dart/pubspec.yaml @@ -8,7 +8,7 @@ publish_to: none homepage: https://github.com/flutter/engine/tree/main/tools/path_ops environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dev_dependencies: litetest: any diff --git a/tools/pkg/engine_build_configs/pubspec.yaml b/tools/pkg/engine_build_configs/pubspec.yaml index b093547b0ed49..8aeeda6181890 100644 --- a/tools/pkg/engine_build_configs/pubspec.yaml +++ b/tools/pkg/engine_build_configs/pubspec.yaml @@ -5,7 +5,7 @@ name: engine_build_configs publish_to: none environment: - sdk: '>=3.1.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/tools/pkg/engine_repo_tools/lib/engine_repo_tools.dart b/tools/pkg/engine_repo_tools/lib/engine_repo_tools.dart index 820f1bf29045b..f52cf8a6c4329 100644 --- a/tools/pkg/engine_repo_tools/lib/engine_repo_tools.dart +++ b/tools/pkg/engine_repo_tools/lib/engine_repo_tools.dart @@ -35,6 +35,12 @@ import 'package:path/path.dart' as p; /// If you have a path to a directory within the `$ENGINE/src` directory, or /// want to use the current working directory, use [Engine.findWithin]. final class Engine { + const Engine._({ + required this.srcDir, + required this.flutterDir, + required this.outDir, + }); + /// Creates an [Engine] from a path such as `/Users/.../flutter/engine/src`. /// /// ```dart @@ -66,25 +72,44 @@ final class Engine { // don't want to fail if it doesn't exist. final io.Directory outDir = io.Directory(p.join(srcPath, 'out')); - return Engine._(srcDir, flutterDir, outDir); + return Engine._( + srcDir: srcDir, + flutterDir: flutterDir, + outDir: outDir, + ); } /// Creates an [Engine] by looking for a `src/` directory in the given path. /// + /// Similar to [tryFindWithin], but throws a [StateError] if the path is not + /// within a valid engine. This is useful for tools that require an engine + /// and do not have a reasonable fallback or recovery path. + factory Engine.findWithin([String? path]) { + final Engine? engine = tryFindWithin(path); + if (engine == null) { + throw StateError('The path "$path" is not within a valid engine.'); + } + return engine; + } + + /// Creates an [Engine] by looking for a `src/` directory in the given [path]. + /// /// ```dart /// // Use the current working directory. /// final Engine engine = Engine.findWithin(); /// print(engine.srcDir.path); // /Users/.../engine/src /// /// // Use a specific directory. - /// final Engine engine = Engine.findWithin('/Users/.../engine/src/foo/bar/baz'); + /// final Engine engine = Engine.findWithin('/Users/.../engine/src/foo/bar'); /// print(engine.srcDir.path); // /Users/.../engine/src /// ``` /// - /// If a path is not provided, the current working directory is used. + /// If a [path] is not provided, the current working directory is used. /// - /// Throws a [StateError] if the path is not within a valid engine. - factory Engine.findWithin([String? path]) { + /// If path does not exist, or is not a directory, an error is thrown. + /// + /// Returns `null` if the path is not within a valid engine. + static Engine? tryFindWithin([String? path]) { path ??= p.current; // Search parent directories for a `src` directory. @@ -105,17 +130,9 @@ final class Engine { maybeSrcDir = maybeSrcDir.parent; } while (maybeSrcDir.parent.path != maybeSrcDir.path /* at root */); - throw StateError( - 'The path "$path" is not within a Flutter engine source directory.' - ); + return null; } - const Engine._( - this.srcDir, - this.flutterDir, - this.outDir, - ); - /// The path to the `$ENGINE/src` directory. final io.Directory srcDir; @@ -145,12 +162,88 @@ final class Engine { return null; } outputs.sort((Output a, Output b) { - return b.dir.statSync().modified.compareTo(a.dir.statSync().modified); + return b.path.statSync().modified.compareTo(a.path.statSync().modified); }); return outputs.first; } } +/// An implementation of [Engine] that has pre-defined outputs for testing. +final class TestEngine extends Engine { + /// Creates a [TestEngine] with pre-defined paths. + /// + /// The [srcDir] and [flutterDir] must exist, but the [outDir] is optional. + /// + /// Optionally, provide a list of [outputs] to use, otherwise it is empty. + TestEngine.withPaths({ + required super.srcDir, + required super.flutterDir, + required super.outDir, + List outputs = const [], + }) : _outputs = outputs, super._() { + if (!srcDir.existsSync()) { + throw ArgumentError.value(srcDir, 'srcDir', 'does not exist'); + } + if (!flutterDir.existsSync()) { + throw ArgumentError.value(flutterDir, 'flutterDir', 'does not exist'); + } + } + + /// Creates a [TestEngine] within a temporary directory. + /// + /// The [rootDir] is the temporary directory that will contain the engine. + /// + /// Optionally, provide a list of [outputs] to use, otherwise it is empty. + factory TestEngine.createTemp({ + required io.Directory rootDir, + List outputs = const [], + }) { + final io.Directory srcDir = io.Directory(p.join(rootDir.path, 'src')); + final io.Directory flutterDir = io.Directory(p.join(srcDir.path, 'flutter')); + final io.Directory outDir = io.Directory(p.join(srcDir.path, 'out')); + srcDir.createSync(recursive: true); + flutterDir.createSync(recursive: true); + outDir.createSync(recursive: true); + return TestEngine.withPaths( + srcDir: srcDir, + flutterDir: flutterDir, + outDir: outDir, + outputs: outputs, + ); + } + + final List _outputs; + + @override + List outputs() => List.unmodifiable(_outputs); + + @override + Output? latestOutput() { + if (_outputs.isEmpty) { + return null; + } + _outputs.sort((TestOutput a, TestOutput b) { + return b.lastModified.compareTo(a.lastModified); + }); + return _outputs.first; + } +} + +/// An implementation of [Output] that has a pre-defined path for testing. +final class TestOutput extends Output { + /// Creates a [TestOutput] with a pre-defined path. + /// + /// Optionally, provide a [lastModified] date. + TestOutput(super.path, {DateTime? lastModified}) + : lastModified = lastModified ?? _defaultLastModified, + super._(); + + static final DateTime _defaultLastModified = DateTime.now(); + + /// The last modified date of the output target. + final DateTime lastModified; +} + /// Thrown when an [Engine] could not be created from a path. sealed class InvalidEngineException implements Exception { /// Thrown when an [Engine] was created from a path not ending in `src`. @@ -210,19 +303,15 @@ final class InvalidEngineMissingFlutterDirectoryException implements InvalidEngi /// Represents a single output target in the `$ENGINE/src/out` directory. final class Output { - const Output._(this.dir); + const Output._(this.path); /// The directory containing the output target. - final io.Directory dir; + final io.Directory path; - /// The `compile_commands.json` file for this output target. + /// The `compile_commands.json` file that should exist for this output target. /// - /// Returns `null` if the file does not exist. - io.File? get compileCommandsJson { - final io.File file = io.File(p.join(dir.path, 'compile_commands.json')); - if (!file.existsSync()) { - return null; - } - return file; + /// The file may not exist. + io.File get compileCommandsJson { + return io.File(p.join(path.path, 'compile_commands.json')); } } diff --git a/tools/pkg/engine_repo_tools/pubspec.yaml b/tools/pkg/engine_repo_tools/pubspec.yaml index 455ee7cca5ea6..08f4a18186979 100644 --- a/tools/pkg/engine_repo_tools/pubspec.yaml +++ b/tools/pkg/engine_repo_tools/pubspec.yaml @@ -5,7 +5,7 @@ name: engine_repo_tools publish_to: none environment: - sdk: ^3.0.0 + sdk: '>=3.2.0-0 <4.0.0' # Do not add any dependencies that require more than what is provided in # //third_party/pkg, //third_party/dart/pkg, or diff --git a/tools/pkg/engine_repo_tools/test/engine_repo_tools_test.dart b/tools/pkg/engine_repo_tools/test/engine_repo_tools_test.dart index 48c18210b304e..4b6a05becbb1c 100644 --- a/tools/pkg/engine_repo_tools/test/engine_repo_tools_test.dart +++ b/tools/pkg/engine_repo_tools/test/engine_repo_tools_test.dart @@ -187,7 +187,7 @@ void main() { io.Directory(p.join(emptyDir.path, 'src', 'out', 'host_debug_unopt_arm64')).createSync(recursive: true); final Engine engine = Engine.fromSrcPath(p.join(emptyDir.path, 'src')); - final List outputs = engine.outputs().map((Output o) => p.basename(o.dir.path)).toList()..sort(); + final List outputs = engine.outputs().map((Output o) => p.basename(o.path.path)).toList()..sort(); expect(outputs, [ 'host_debug', 'host_debug_unopt_arm64', @@ -202,23 +202,39 @@ void main() { try { // Create a valid engine. - io.Directory(p.join(emptyDir.path, 'src', 'flutter')).createSync(recursive: true); - io.Directory(p.join(emptyDir.path, 'src', 'out')).createSync(recursive: true); + final io.Directory srcDir = io.Directory(p.join(emptyDir.path, 'src')) + ..createSync(recursive: true); + final io.Directory flutterDir = io.Directory(p.join(srcDir.path, 'flutter')) + ..createSync(recursive: true); + final io.Directory outDir = io.Directory(p.join(srcDir.path, 'out')) + ..createSync(recursive: true); // Create two targets in out: host_debug and host_debug_unopt_arm64. - io.Directory(p.join(emptyDir.path, 'src', 'out', 'host_debug')).createSync(recursive: true); - io.Directory(p.join(emptyDir.path, 'src', 'out', 'host_debug_unopt_arm64')).createSync(recursive: true); + final io.Directory hostDebug = io.Directory(p.join(outDir.path, 'host_debug')) + ..createSync(recursive: true); + final io.Directory hostDebugUnoptArm64 = io.Directory( + p.join(outDir.path, 'host_debug_unopt_arm64'), + )..createSync(recursive: true); + + final Engine engine = TestEngine.withPaths( + srcDir: srcDir, + flutterDir: flutterDir, + outDir: outDir, + outputs: [ + TestOutput( + hostDebug, + lastModified: DateTime.utc(2023, 9, 23, 21, 16), + ), + TestOutput( + hostDebugUnoptArm64, + lastModified: DateTime.utc(2023, 9, 23, 22, 16), + ), + ], + ); - // Intentionnally make host_debug a day old to ensure it is not picked. - final io.File oldJson = io.File(p.join(emptyDir.path, 'src', 'out', 'host_debug', 'compile_commands.json'))..createSync(); - oldJson.setLastModifiedSync(oldJson.lastModifiedSync().subtract(const Duration(days: 1))); - - io.File(p.join(emptyDir.path, 'src', 'out', 'host_debug_unopt_arm64', 'compile_commands.json')).createSync(); - - final Engine engine = Engine.fromSrcPath(p.join(emptyDir.path, 'src')); final Output? latestOutput = engine.latestOutput(); expect(latestOutput, isNotNull); - expect(p.basename(latestOutput!.dir.path), 'host_debug_unopt_arm64'); + expect(p.basename(latestOutput!.path.path), 'host_debug_unopt_arm64'); expect(latestOutput.compileCommandsJson, isNotNull); } finally { tearDown(); diff --git a/tools/pkg/git_repo_tools/README.md b/tools/pkg/git_repo_tools/README.md new file mode 100644 index 0000000000000..e308c8104b911 --- /dev/null +++ b/tools/pkg/git_repo_tools/README.md @@ -0,0 +1,23 @@ +# `git_repo_tools` + +This is a repo-internal library for `flutter/engine`, that contains shared code +for writing tools that want to interact with the `git` repository. For example, +finding all changed files in the current branch: + +```dart +import 'dart:io' as io show File, Platform; + +import 'package:engine_repo_tools/engine_repo_tools.dart'; +import 'package:git_repo_tools/git_repo_tools.dart'; +import 'package:path/path.dart' as path; + +void main() async { + // Finds the root of the engine repository from the current script. + final Engine engine = Engine.findWithin(path.dirname(path.fromUri(io.Platform.script))); + final GitRepo gitRepo = GitRepo(engine.flutterDir); + + for (final io.File file in gitRepo.changedFiles) { + print('Changed file: ${file.path}'); + } +} +``` diff --git a/tools/clang_tidy/lib/src/git_repo.dart b/tools/pkg/git_repo_tools/lib/git_repo_tools.dart similarity index 67% rename from tools/clang_tidy/lib/src/git_repo.dart rename to tools/pkg/git_repo_tools/lib/git_repo_tools.dart index 746359bb51198..40fd07747f06b 100644 --- a/tools/clang_tidy/lib/src/git_repo.dart +++ b/tools/pkg/git_repo_tools/lib/git_repo_tools.dart @@ -2,58 +2,66 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io' as io show Directory, File; +import 'dart:io' as io show Directory, File, stdout; import 'package:path/path.dart' as path; import 'package:process/process.dart'; import 'package:process_runner/process_runner.dart'; -/// Utility methods for working with a git repo. -class GitRepo { +/// Utility methods for working with a git repository. +final class GitRepo { /// The git repository rooted at `root`. - GitRepo(this.root, { + GitRepo.fromRoot(this.root, { this.verbose = false, + StringSink? logSink, ProcessManager processManager = const LocalProcessManager(), - }) : _processManager = processManager; + }) : + _processManager = processManager, + logSink = logSink ?? io.stdout; /// Whether to produce verbose log output. + /// + /// If true, output of git commands will be printed to [logSink]. final bool verbose; + /// Where to send verbose log output. + /// + /// Defaults to [io.stdout]. + final StringSink logSink; + /// The root of the git repo. final io.Directory root; /// The delegate to use for running processes. final ProcessManager _processManager; - List? _changedFiles; - /// Returns a list of all non-deleted files which differ from the nearest /// merge-base with `main`. If it can't find a fork point, uses the default /// merge-base. /// /// This is only computed once and cached. Subsequent invocations of the /// getter will return the same result. - Future> get changedFiles async => - _changedFiles ??= await _getChangedFiles(); - - List? _changedFilesAtHead; - - /// Returns a list of non-deleted files which differ between the HEAD - /// commit and its parent. - Future> get changedFilesAtHead async => - _changedFilesAtHead ??= await _getChangedFilesAtHead(); + late final Future> changedFiles = _changedFiles(); - Future> _getChangedFiles() async { + Future> _changedFiles() async { final ProcessRunner processRunner = ProcessRunner( defaultWorkingDirectory: root, processManager: _processManager, ); await _fetch(processRunner); + // Find the merge base between the current branch and the branch that was + // checked out at the time of the last fetch. The merge base is the common + // ancestor of the two branches, and the output is the hash of the merge + // base. ProcessRunnerResult mergeBaseResult = await processRunner.runProcess( ['git', 'merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD'], failOk: true, ); if (mergeBaseResult.exitCode != 0) { + if (verbose) { + logSink.writeln('git merge-base --fork-point failed, using default merge-base'); + logSink.writeln('Output:\n${mergeBaseResult.stdout}'); + } mergeBaseResult = await processRunner.runProcess([ 'git', 'merge-base', @@ -62,8 +70,7 @@ class GitRepo { ]); } final String mergeBase = mergeBaseResult.stdout.trim(); - final ProcessRunnerResult masterResult = await processRunner - .runProcess([ + final ProcessRunnerResult masterResult = await processRunner.runProcess([ 'git', 'diff', '--name-only', @@ -73,9 +80,17 @@ class GitRepo { return _gitOutputToList(masterResult); } - Future> _getChangedFilesAtHead() async { + /// Returns a list of non-deleted files which differ between the HEAD + /// commit and its parent. + /// + /// This is only computed once and cached. Subsequent invocations of the + /// getter will return the same result. + late final Future> changedFilesAtHead = _changedFilesAtHead(); + + Future> _changedFilesAtHead() async { final ProcessRunner processRunner = ProcessRunner( defaultWorkingDirectory: root, + processManager: _processManager, ); await _fetch(processRunner); final ProcessRunnerResult diffTreeResult = await processRunner.runProcess( @@ -98,6 +113,10 @@ class GitRepo { failOk: true, ); if (fetchResult.exitCode != 0) { + if (verbose) { + logSink.writeln('git fetch upstream main failed, using origin main'); + logSink.writeln('Output:\n${fetchResult.stdout}'); + } await processRunner.runProcess([ 'git', 'fetch', @@ -110,7 +129,7 @@ class GitRepo { List _gitOutputToList(ProcessRunnerResult result) { final String diffOutput = result.stdout.trim(); if (verbose) { - print('git diff output:\n$diffOutput'); + logSink.writeln('git diff output:\n$diffOutput'); } final Set resultMap = {}; resultMap.addAll(diffOutput.split('\n').where( diff --git a/tools/pkg/git_repo_tools/pubspec.yaml b/tools/pkg/git_repo_tools/pubspec.yaml new file mode 100644 index 0000000000000..1cf937a42d22d --- /dev/null +++ b/tools/pkg/git_repo_tools/pubspec.yaml @@ -0,0 +1,60 @@ +# 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: git_repo_tools +publish_to: none +environment: + sdk: '>=3.2.0-0 <4.0.0' + +# Do not add any dependencies that require more than what is provided in +# //third_party/pkg, //third_party/dart/pkg, or +# //third_party/dart/third_party/pkg. In particular, package:test is not usable +# here. + +# If you do add packages here, make sure you can run `pub get --offline`, and +# check the .packages and .package_config to make sure all the paths are +# relative to this directory into //third_party/dart + +dependencies: + meta: any + path: any + process: any + process_runner: any + +dev_dependencies: + async_helper: any + expect: any + litetest: any + process_fakes: any + smith: any + +dependency_overrides: + args: + path: ../../../../third_party/dart/third_party/pkg/args + async: + path: ../../../../third_party/dart/third_party/pkg/async + async_helper: + path: ../../../../third_party/dart/pkg/async_helper + collection: + path: ../../../../third_party/dart/third_party/pkg/collection + expect: + path: ../../../../third_party/dart/pkg/expect + file: + path: ../../../../third_party/pkg/file/packages/file + litetest: + path: ../../../testing/litetest + meta: + path: ../../../../third_party/dart/pkg/meta + path: + path: ../../../../third_party/dart/third_party/pkg/path + platform: + path: ../../../../third_party/pkg/platform + process: + path: ../../../../third_party/pkg/process + process_fakes: + path: ../process_fakes + process_runner: + path: ../../../../third_party/pkg/process_runner + smith: + path: ../../../../third_party/dart/pkg/smith diff --git a/tools/pkg/git_repo_tools/test/git_repo_tools_test.dart b/tools/pkg/git_repo_tools/test/git_repo_tools_test.dart new file mode 100644 index 0000000000000..bca022f353a5d --- /dev/null +++ b/tools/pkg/git_repo_tools/test/git_repo_tools_test.dart @@ -0,0 +1,215 @@ +// 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. + +import 'dart:io' as io; + +import 'package:git_repo_tools/git_repo_tools.dart'; +import 'package:litetest/litetest.dart'; +import 'package:process_fakes/process_fakes.dart'; + +void main() { + const String fakeShaHash = 'fake-sha-hash'; + + + test('returns non-deleted files which differ from merge-base with main', () async { + final Fixture fixture = Fixture( + processManager: FakeProcessManager( + onStart: (List command) { + // Succeed calling "git merge-base --fork-point FETCH_HEAD HEAD". + if (command.join(' ').startsWith('git merge-base --fork-point')) { + return FakeProcess(stdout: fakeShaHash); + } + + // Succeed calling "git fetch upstream main". + if (command.join(' ') == 'git fetch upstream main') { + return FakeProcess(); + } + + // Succeed calling "git diff --name-only --diff-filter=ACMRT fake-sha-hash". + if (command.join(' ') == 'git diff --name-only --diff-filter=ACMRT $fakeShaHash') { + return FakeProcess(stdout: 'file1\nfile2'); + } + + // Otherwise, fail. + return FakeProcessManager.unhandledStart(command); + }, + ), + ); + + try { + final List changedFiles = await fixture.gitRepo.changedFiles; + expect(changedFiles, hasLength(2)); + expect(changedFiles[0].path, endsWith('file1')); + expect(changedFiles[1].path, endsWith('file2')); + } finally { + fixture.gitRepo.root.deleteSync(recursive: true); + } + }); + + test('returns non-deleted files which differ from default merge-base', () async { + final Fixture fixture = Fixture( + processManager: FakeProcessManager( + onStart: (List command) { + if (command.join(' ').startsWith('git merge-base --fork-point')) { + return FakeProcess(exitCode: 1); + } + + if (command.join(' ').startsWith('git merge-base')) { + return FakeProcess(stdout: fakeShaHash); + } + + if (command.join(' ') == 'git fetch upstream main') { + return FakeProcess(); + } + + if (command.join(' ') == 'git diff --name-only --diff-filter=ACMRT $fakeShaHash') { + return FakeProcess(stdout: 'file1\nfile2'); + } + + // Otherwise, fail. + return FakeProcessManager.unhandledStart(command); + }, + ), + ); + + try { + final List changedFiles = await fixture.gitRepo.changedFiles; + expect(changedFiles, hasLength(2)); + expect(changedFiles[0].path, endsWith('file1')); + expect(changedFiles[1].path, endsWith('file2')); + } finally { + fixture.gitRepo.root.deleteSync(recursive: true); + } + }); + + test('returns non-deleted files which differ from HEAD', () async { + final Fixture fixture = Fixture( + processManager: FakeProcessManager( + onStart: (List command) { + if (command.join(' ') == 'git fetch upstream main') { + return FakeProcess(); + } + + if (command.join(' ') == 'git diff-tree --no-commit-id --name-only --diff-filter=ACMRT -r HEAD') { + return FakeProcess(stdout: 'file1\nfile2'); + } + + // Otherwise, fail. + return FakeProcessManager.unhandledStart(command); + }, + ), + ); + + try { + final List changedFiles = await fixture.gitRepo.changedFilesAtHead; + expect(changedFiles, hasLength(2)); + expect(changedFiles[0].path, endsWith('file1')); + expect(changedFiles[1].path, endsWith('file2')); + } finally { + fixture.gitRepo.root.deleteSync(recursive: true); + } + }); + + test('returns non-deleted files which differ from HEAD when merge-base fails', () async { + final Fixture fixture = Fixture( + processManager: FakeProcessManager( + onStart: (List command) { + if (command.join(' ') == 'git fetch upstream main') { + return FakeProcess(); + } + + if (command.join(' ') == 'git diff-tree --no-commit-id --name-only --diff-filter=ACMRT -r HEAD') { + return FakeProcess(stdout: 'file1\nfile2'); + } + + if (command.join(' ').startsWith('git merge-base --fork-point')) { + return FakeProcess(exitCode: 1); + } + + if (command.join(' ').startsWith('git merge-base')) { + return FakeProcess(stdout: fakeShaHash); + } + + // Otherwise, fail. + return FakeProcessManager.unhandledStart(command); + }, + ), + ); + + try { + final List changedFiles = await fixture.gitRepo.changedFilesAtHead; + expect(changedFiles, hasLength(2)); + expect(changedFiles[0].path, endsWith('file1')); + expect(changedFiles[1].path, endsWith('file2')); + } finally { + fixture.gitRepo.root.deleteSync(recursive: true); + } + }); + + test('verbose output is captured', () async { + final Fixture fixture = Fixture( + processManager: FakeProcessManager( + onStart: (List command) { + if (command.join(' ').startsWith('git merge-base --fork-point')) { + return FakeProcess(exitCode: 1); + } + + if (command.join(' ').startsWith('git merge-base')) { + return FakeProcess(stdout: fakeShaHash); + } + + if (command.join(' ') == 'git fetch upstream main') { + return FakeProcess(); + } + + if (command.join(' ') == 'git diff --name-only --diff-filter=ACMRT $fakeShaHash') { + return FakeProcess(stdout: 'file1\nfile2'); + } + + // Otherwise, fail. + return FakeProcessManager.unhandledStart(command); + }, + ), + verbose: true, + ); + + try { + await fixture.gitRepo.changedFiles; + expect(fixture.logSink.toString(), contains('git merge-base --fork-point failed, using default merge-base')); + expect(fixture.logSink.toString(), contains('git diff output:\nfile1\nfile2')); + } finally { + fixture.gitRepo.root.deleteSync(recursive: true); + } + }); +} + +final class Fixture { + factory Fixture({ + FakeProcessManager? processManager, + bool verbose = false, + }) { + final io.Directory root = io.Directory.systemTemp.createTempSync('git_repo_tools.test'); + final StringBuffer logSink = StringBuffer(); + processManager ??= FakeProcessManager(); + return Fixture._( + gitRepo: GitRepo.fromRoot(root, + logSink: logSink, + processManager: processManager, + verbose: verbose, + ), + logSink: logSink, + processManager: processManager, + ); + } + + const Fixture._({ + required this.gitRepo, + required this.logSink, + required this.processManager, + }); + + final GitRepo gitRepo; + final StringBuffer logSink; + final FakeProcessManager processManager; +} diff --git a/tools/pkg/process_fakes/README.md b/tools/pkg/process_fakes/README.md new file mode 100644 index 0000000000000..33da210aea5b0 --- /dev/null +++ b/tools/pkg/process_fakes/README.md @@ -0,0 +1,7 @@ +# `process_fakes` + +Fake implementations of `Process` and `ProcessManager` for testing. + +This is not a great package, and is the bare minimum needed for fairly basic +tooling that uses `ProcessManager`. If we ever need a more complete solution +we should look at upstreaming [`flutter_tools/.../fake_proecss_manager.dart`](https://github.com/flutter/flutter/blob/a9183f696c8e12617d05a26b0b5e80035e515f2a/packages/flutter_tools/test/src/fake_process_manager.dart#L223) diff --git a/tools/clang_tidy/test/process_fakes.dart b/tools/pkg/process_fakes/lib/process_fakes.dart similarity index 91% rename from tools/clang_tidy/test/process_fakes.dart rename to tools/pkg/process_fakes/lib/process_fakes.dart index 7bc5fe5778e1f..356f1d391f33d 100644 --- a/tools/clang_tidy/test/process_fakes.dart +++ b/tools/pkg/process_fakes/lib/process_fakes.dart @@ -9,15 +9,20 @@ import 'package:process/process.dart'; /// A fake implementation of [ProcessManager] that allows control for testing. final class FakeProcessManager implements ProcessManager { + /// Creates a fake process manager delegates to [onRun] and [onStart]. + /// + /// If either is not provided, it throws an [UnsupportedError] when called. FakeProcessManager({ io.ProcessResult Function(List command) onRun = unhandledRun, io.Process Function(List command) onStart = unhandledStart, }) : _onRun = onRun, _onStart = onStart; + /// A default implementation of [onRun] that throws an [UnsupportedError]. static io.ProcessResult unhandledRun(List command) { throw UnsupportedError('Unhandled run: ${command.join(' ')}'); } + /// A default implementation of [onStart] that throws an [UnsupportedError]. static io.Process unhandledStart(List command) { throw UnsupportedError('Unhandled start: ${command.join(' ')}'); } diff --git a/tools/pkg/process_fakes/pubspec.yaml b/tools/pkg/process_fakes/pubspec.yaml new file mode 100644 index 0000000000000..8ac6e85aa88fe --- /dev/null +++ b/tools/pkg/process_fakes/pubspec.yaml @@ -0,0 +1,36 @@ +# 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: process_fakes +publish_to: none +environment: + sdk: '>=3.2.0-0 <4.0.0' + +# Do not add any dependencies that require more than what is provided in +# //third_party/pkg, //third_party/dart/pkg, or +# //third_party/dart/third_party/pkg. In particular, package:test is not usable +# here. + +# If you do add packages here, make sure you can run `pub get --offline`, and +# check the .packages and .package_config to make sure all the paths are +# relative to this directory into //third_party/dart + +dependencies: + file: any + meta: any + path: any + platform: any + process: any + +dependency_overrides: + file: + path: ../../../../third_party/pkg/file/packages/file + meta: + path: ../../../../third_party/dart/pkg/meta + path: + path: ../../../../third_party/dart/third_party/pkg/path + platform: + path: ../../../../third_party/pkg/platform + process: + path: ../../../../third_party/pkg/process diff --git a/tools/pub_get_offline.py b/tools/pub_get_offline.py index 17f529c61c805..931db7319b8f9 100644 --- a/tools/pub_get_offline.py +++ b/tools/pub_get_offline.py @@ -44,6 +44,8 @@ os.path.join(ENGINE_DIR, 'tools', 'path_ops', 'dart'), os.path.join(ENGINE_DIR, 'tools', 'pkg', 'engine_build_configs'), os.path.join(ENGINE_DIR, 'tools', 'pkg', 'engine_repo_tools'), + os.path.join(ENGINE_DIR, 'tools', 'pkg', 'git_repo_tools'), + os.path.join(ENGINE_DIR, 'tools', 'pkg', 'process_fakes'), ] diff --git a/vulkan/procs/vulkan_interface.h b/vulkan/procs/vulkan_interface.h index 2d01ed72b7143..c0518bc256920 100644 --- a/vulkan/procs/vulkan_interface.h +++ b/vulkan/procs/vulkan_interface.h @@ -26,15 +26,19 @@ #include -#define VK_CALL_LOG_ERROR(expression) \ - ({ \ - __typeof__(expression) _rc = (expression); \ - if (_rc != VK_SUCCESS) { \ - FML_LOG(INFO) << "Vulkan call '" << #expression \ - << "' failed with error " \ - << vulkan::VulkanResultToString(_rc); \ - } \ - _rc; \ +#define VK_CALL_LOG_ERROR(expression) VK_CALL_LOG(expression, ERROR) + +#define VK_CALL_LOG_FATAL(expression) VK_CALL_LOG(expression, FATAL) + +#define VK_CALL_LOG(expression, severity) \ + ({ \ + __typeof__(expression) _rc = (expression); \ + if (_rc != VK_SUCCESS) { \ + FML_LOG(severity) << "Vulkan call '" << #expression \ + << "' failed with error " \ + << vulkan::VulkanResultToString(_rc); \ + } \ + _rc; \ }) namespace vulkan { diff --git a/vulkan/swiftshader_path.h b/vulkan/swiftshader_path.h new file mode 100644 index 0000000000000..424116a7efec7 --- /dev/null +++ b/vulkan/swiftshader_path.h @@ -0,0 +1,18 @@ +// 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_VULKAN_SWIFTSHADER_PATH_H_ +#define FLUTTER_VULKAN_SWIFTSHADER_PATH_H_ + +#ifndef VULKAN_SO_PATH +#if FML_OS_MACOSX +#define VULKAN_SO_PATH "libvk_swiftshader.dylib" +#elif FML_OS_WIN +#define VULKAN_SO_PATH "vk_swiftshader.dll" +#else +#define VULKAN_SO_PATH "libvk_swiftshader.so" +#endif // !FML_OS_MACOSX && !FML_OS_WIN +#endif // VULKAN_SO_PATH + +#endif // FLUTTER_VULKAN_SWIFTSHADER_PATH_H_ diff --git a/vulkan/vulkan_window.cc b/vulkan/vulkan_window.cc index f7f51dce9e738..95bd227dde462 100644 --- a/vulkan/vulkan_window.cc +++ b/vulkan/vulkan_window.cc @@ -19,6 +19,7 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" namespace vulkan { @@ -118,6 +119,7 @@ GrDirectContext* VulkanWindow::GetSkiaGrContext() { } bool VulkanWindow::CreateSkiaGrContext() { +#ifdef SK_VUKLAN GrVkBackendContext backend_context; if (!CreateSkiaBackendContext(&backend_context)) { @@ -127,7 +129,7 @@ bool VulkanWindow::CreateSkiaGrContext() { GrContextOptions options; options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; sk_sp context = - GrDirectContext::MakeVulkan(backend_context, options); + GrDirectContexts::MakeVulkan(backend_context, options); if (context == nullptr) { return false; @@ -138,6 +140,9 @@ bool VulkanWindow::CreateSkiaGrContext() { skia_gr_context_ = context; return true; +#else + return false; +#endif // SK_VULKAN } bool VulkanWindow::CreateSkiaBackendContext(GrVkBackendContext* context) { diff --git a/web_sdk/BUILD.gn b/web_sdk/BUILD.gn index 83d965219100d..1f3d136d4540b 100644 --- a/web_sdk/BUILD.gn +++ b/web_sdk/BUILD.gn @@ -229,6 +229,11 @@ template("_compile_platform") { if (defined(invoker.null_environment) && invoker.null_environment) { args += [ "--null-environment" ] } + skwasm_library = "dart:_skwasm_stub" + if (invoker.kernel_target == "dart2wasm") { + skwasm_library = "dart:_skwasm_impl" + } + args += [ "--target", "${invoker.kernel_target}", @@ -253,7 +258,7 @@ template("_compile_platform") { "--source", "dart:_engine", "--source", - "dart:_skwasm_stub", + skwasm_library, "--source", "dart:_web_unicode", "--source", @@ -306,6 +311,14 @@ _compile_platform("flutter_dart2js_kernel_sdk_full_sound") { null_environment = true } +_compile_platform("flutter_dart2wasm_kernel_sdk_full_sound") { + sound_null_safety = true + kernel_target = "dart2wasm" + summary_only = false + output_dill = "$root_out_dir/flutter_web_sdk/kernel/dart2wasm_platform.dill" + null_environment = true +} + # TODO(jacksongardner): remove these once they are no longer used by the flutter tool copy("flutter_dartdevc_kernel_sdk_outline_unsound_legacy") { sources = get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_unsound") @@ -328,6 +341,7 @@ group("flutter_platform_dills") { public_deps = [ ":flutter_dart2js_kernel_sdk_full_sound", ":flutter_dart2js_kernel_sdk_full_unsound", + ":flutter_dart2wasm_kernel_sdk_full_sound", ":flutter_dartdevc_kernel_sdk_outline_sound", ":flutter_dartdevc_kernel_sdk_outline_unsound", @@ -528,13 +542,17 @@ if (!is_fuchsia) { sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_sound") sources += get_target_outputs(":flutter_dart2js_kernel_sdk_full_unsound") sources += get_target_outputs(":flutter_dart2js_kernel_sdk_full_sound") + sources += get_target_outputs(":flutter_dart2wasm_kernel_sdk_full_sound") if (is_wasm) { sources += [ "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.js", + "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.js.symbols", "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.wasm", "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.js", + "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.js.symbols", "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.wasm", "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js", + "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js.symbols", "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.wasm", "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.worker.js", ] diff --git a/web_sdk/pubspec.yaml b/web_sdk/pubspec.yaml index 9d65bb86930d1..9d413a1cc5992 100644 --- a/web_sdk/pubspec.yaml +++ b/web_sdk/pubspec.yaml @@ -2,7 +2,7 @@ name: web_sdk_tests # Keep the SDK version range in sync with lib/web_ui/pubspec.yaml environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: args: 2.3.1 @@ -13,7 +13,7 @@ dev_dependencies: # up-to-date instead of a version from pub, which may not have the latest # language features enabled. analyzer: any - test: 1.22.1 + test: 1.24.8 dependency_overrides: # Must include all transitive dependencies from the "any" packages above. _fe_analyzer_shared: diff --git a/web_sdk/sdk_rewriter.dart b/web_sdk/sdk_rewriter.dart index 96b37598a6fcc..da1aa102a14fa 100644 --- a/web_sdk/sdk_rewriter.dart +++ b/web_sdk/sdk_rewriter.dart @@ -178,6 +178,9 @@ List getExtraImportsForLibrary(String libraryName) { extraImports.add(entry.value); } } + if (libraryName == 'skwasm_impl') { + extraImports.add("import 'dart:_wasm';"); + } return extraImports; } diff --git a/web_sdk/test/sdk_rewriter_test.dart b/web_sdk/test/sdk_rewriter_test.dart index 2eb2c0795dcf6..95cfe8256f9cd 100644 --- a/web_sdk/test/sdk_rewriter_test.dart +++ b/web_sdk/test/sdk_rewriter_test.dart @@ -184,6 +184,7 @@ void printSomething() { "import 'dart:_web_unicode';", "import 'dart:_web_test_fonts';", "import 'dart:_web_locale_keymap' as locale_keymap;", + "import 'dart:_wasm';", ]); // Other libraries (should not have extra imports). diff --git a/web_sdk/web_engine_tester/lib/static/host.dart b/web_sdk/web_engine_tester/lib/static/host.dart index bbd59c215fae9..9c8f0d8106b40 100644 --- a/web_sdk/web_engine_tester/lib/static/host.dart +++ b/web_sdk/web_engine_tester/lib/static/host.dart @@ -207,14 +207,8 @@ StreamChannel _connectToIframe(String url, int id) { ..height = '1000'; domDocument.body!.appendChild(iframe); - // Use this to communicate securely with the iframe. - final DomMessageChannel channel = createDomMessageChannel(); final StreamChannelController controller = StreamChannelController(sync: true); - // Use this to avoid sending a message to the iframe before it's sent a - // message to us. This ensures that no messages get dropped on the floor. - final Completer readyCompleter = Completer(); - final List domSubscriptions = []; final List> streamSubscriptions = >[]; _domSubscriptions[id] = domSubscriptions; @@ -229,20 +223,35 @@ StreamChannel _connectToIframe(String url, int id) { if (message.origin != domWindow.location.origin) { return; } - - if (message.data['href'] != iframe.src) { + if (message.source.location?.href != iframe.src) { return; } message.stopPropagation(); - if (message.data['ready'] == true) { + if (message.data == 'port') { + final DomMessagePort port = message.ports.first; + domSubscriptions.add( + DomSubscription(port, 'message',(DomEvent event) { + controller.local.sink.add((event as DomMessageEvent).data); + })); + port.start(); + streamSubscriptions.add(controller.local.stream.listen(port.postMessage)); + } else if (message.data['ready'] == true) { // This message indicates that the iframe is actively listening for // events, so the message channel's second port can now be transferred. + final DomMessageChannel channel = createDomMessageChannel(); + channel.port1.start(); channel.port2.start(); - iframe.contentWindow.postMessage('port', domWindow.location.origin, - [channel.port2]); - readyCompleter.complete(); + iframe.contentWindow.postMessage( + 'port', domWindow.location.origin, [channel.port2]); + domSubscriptions + .add(DomSubscription(channel.port1, 'message', (DomEvent message) { + controller.local.sink.add((message as DomMessageEvent).data['data']); + })); + + streamSubscriptions + .add(controller.local.stream.listen(channel.port1.postMessage)); } else if (message.data['exception'] == true) { // This message from `dart.js` indicates that an exception occurred // loading the test. @@ -250,16 +259,6 @@ StreamChannel _connectToIframe(String url, int id) { } })); - channel.port1.start(); - domSubscriptions.add(DomSubscription(channel.port1, 'message', - (DomEvent message) { - controller.local.sink.add((message as DomMessageEvent).data['data']); - })); - - streamSubscriptions.add(controller.local.stream.listen((dynamic message) async { - await readyCompleter.future; - channel.port1.postMessage(message); - })); return controller.foreign; } diff --git a/web_sdk/web_engine_tester/pubspec.yaml b/web_sdk/web_engine_tester/pubspec.yaml index 06ab52b319740..dadc9948addb8 100644 --- a/web_sdk/web_engine_tester/pubspec.yaml +++ b/web_sdk/web_engine_tester/pubspec.yaml @@ -2,12 +2,12 @@ name: web_engine_tester # Keep the SDK version range in sync with lib/web_ui/pubspec.yaml environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: js: 0.6.4 stream_channel: 2.1.1 - test: 1.22.1 + test: 1.24.8 webkit_inspection_protocol: 1.0.0 stack_trace: 1.10.0 ui: diff --git a/web_sdk/web_test_utils/pubspec.yaml b/web_sdk/web_test_utils/pubspec.yaml index 2879b821cc479..ca7efd336eec7 100644 --- a/web_sdk/web_test_utils/pubspec.yaml +++ b/web_sdk/web_test_utils/pubspec.yaml @@ -2,7 +2,7 @@ name: web_test_utils # Keep the SDK version range in sync with lib/web_ui/pubspec.yaml environment: - sdk: '>=3.0.0-0 <4.0.0' + sdk: '>=3.2.0-0 <4.0.0' dependencies: collection: 1.17.0