diff --git a/.ci.yaml b/.ci.yaml index c6c8442e1b72c..e5ee03b4374a8 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -360,7 +360,7 @@ targets: {"download_emsdk": true} dependencies: >- [ - {"dependency": "chrome_and_driver", "version": "version:117.0"}, + {"dependency": "chrome_and_driver", "version": "version:118.0.5993.70"}, {"dependency": "curl", "version": "version:7.64.0"} ] framework: "true" diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 95e9ba1fa6846..f74e8d5c75f29 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -26,7 +26,7 @@ jobs: 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 6a94995ebafd8..bd5c36e995806 100644 --- a/.github/workflows/third_party_scan.yml +++ b/.github/workflows/third_party_scan.yml @@ -29,7 +29,7 @@ jobs: 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..a19d20d383802 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,6 @@ pubspec.lock docs/doxygen/ xcuserdata -third_party/gn/ -third_party/ninja/ninja* - # Miscellaneous *.class *.lock diff --git a/DEPS b/DEPS index f9814f12b8a9b..a7f5fd584befc 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': '33502f9b0c7d845adceb4907d1c7dc1f22b3fd4c', + 'skia_revision': 'ff3c2e3d45bdc793cddc65a3c9fce937b09d1dfd', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. @@ -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': '077d768accfa3419b8b57cf779cca8228b417faa', + 'dart_revision': 'db2465f106adedb581a7c3531d679443cbf2e724', # 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': 'a51bd6df919a5b79574f0996a760cc20cb05697e', 'dart_boringssl_gen_rev': 'a468ba9fec3f59edf46a7db98caaca893e1e4d96', 'dart_boringssl_rev': '74646566e93de7551bfdfc5f49de7462f13d1d05', - 'dart_browser_launcher_rev': '1f69393d63a2f8d36d00b86cdd20df70c347af82', - 'dart_clock_rev': '1e75f08d3428bcd6f4b7cf70e788f24fc9b661e1', - 'dart_collection_rev': '91afde43f488eef618454b896301c6ff59af72e0', + '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_protobuf_rev': 'c559fe52734ef6e2389e26ec3901eaf23fd76543', 'dart_pub_rev': 'fca927ae2662204805e1646c0c0687369001a41a', 'dart_root_certificates_rev': '692f6d6488af68e0121317a9c2c9eb393eb0ee50', - 'dart_tools_rev': '3c248df45b9bf40738579e8616cd7d85f34ba5b1', - 'dart_watcher_rev': '1aed03e2a8005f45083fdb38cbd4b684cd23082f', - 'dart_webdev_rev': '3078f48fb56a1036bc0f2a08ac51567291d8a837', + 'dart_tools_rev': '92c5c15e3eb713b39779f4545bfa207ccdfeb1af', + 'dart_watcher_rev': '3998cdd37ecacd3a1715cdc76110b025bffbd1f6', + 'dart_webdev_rev': '7c2c2d70e05a5012b52e95b209aedce7acb62f94', 'dart_webkit_inspection_protocol_rev': '82f0c1c46dfdba5edf7c5fa84456233121dd69e1', - 'dart_yaml_edit_rev': '4a9734dda12f63ef9eee4121f87ff4401e25a607', + 'dart_yaml_edit_rev': 'a7e7fbad5ee263cc681681c1a6eb9e6df5336ad6', 'dart_zlib_rev': '14dd4c4455602c9b71a1a89b5cafd1f4030d2e3f', 'ocmock_rev': 'c4ec0e3a7a9f56cfdbd0aa01f4f97bb4b75c5ef8', # v3.7.1 @@ -262,7 +262,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '9780f253156165c515962f5f0c56235d34617689', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '1d2965f9b0f784abea8ddbeb54827afa9ed196b9', # Fuchsia compatibility # @@ -282,7 +282,7 @@ 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': @@ -340,22 +340,22 @@ deps = { {'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'), @@ -364,79 +364,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@1a2def95a3c04dafd27b85d17e6e828bd4afa1a3', 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@a3cfdc40a94d169bd7b559dc6f83ceb7f79de4e2', + Var('dart_git') + '/dartdoc.git@5156398c17ff7ab8defd87c7a5b9de1ee7e4564a', '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', '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@12516197c28a0763b1f90c26b87660810bf58cc8', + Var('dart_git') + '/http.git@88ec75eb603ce3d66911ac0df1b48e6582965131', '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@097e5635a6c1859e03e9c606c2ab4cfa74618bcc', + Var('dart_git') + '/mockito.git@47a5588788d37d7a94da9ceb9b3f3ef86c86f27e', 'src/third_party/dart/third_party/pkg/native': - Var('dart_git') + '/native.git@be4aaf7b849a64d67756c95b6270b9bfe47f3c7d', + Var('dart_git') + '/native.git@22f4481573c201542daa1451fc3c58b1e50bf75a', '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'), @@ -445,58 +445,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@485197819b93a9f9342c389f1715fb8e17a7ac0f', + 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@e190744aab3260887e99f94078858ac6ea0e9bc5', '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@3c68cba8e51c569428222b9185469249206172c6', + 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@8191a355cefe5e6073e597d139bfb46e4b00c493', + 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'), @@ -505,7 +505,7 @@ 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'), @@ -918,7 +918,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '4WW3KRrAbuY7VeGT0pBFAQktetsyx-3C0mKMNxCd0uYC' + 'version': 'Qjz4zE4Oe0AO_7T3f-M9BFLMSvDVKjNM4m4C1xpSplsC' } ], 'condition': 'host_os == "mac" and not download_fuchsia_sdk', @@ -928,7 +928,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'l2RxJKPfYn7QzGOoLPUPk0FyRZxbYTRv1JiQJgUbm9sC' + 'version': 'hMA99PoacaRZVXHgric86nJsfgQsM7O8Wn7ZDLc6Kp4C' } ], 'condition': 'host_os == "linux" and not download_fuchsia_sdk', @@ -936,7 +936,7 @@ deps = { }, 'src/third_party/impeller-cmake-example': { - 'url': Var('github_git') + '/bdero/impeller-cmake-example.git' + '@' + 'c2286827ae68e3c2694410e0047cab42dac3312f', + 'url': Var('github_git') + '/bdero/impeller-cmake-example.git' + '@' + 'ad78e80741349dda91c8a17fd16919655406e7d8', 'condition': 'download_impeller_cmake_example', }, @@ -956,7 +956,7 @@ deps = { 'packages': [ { 'package': 'flutter/flutter_font_fallbacks', - 'version': 'ba9a3d16939f9576afa67273198d779270cd768ae2867209ff3d72bab9acd3f6' + 'version': '14d38cffa1a2c9d4380094957aa63aee811b613b0dc53fd909557f61f6a9068c' } ], 'dep_type': 'cipd', 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/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_web_engine.json b/ci/builders/linux_web_engine.json index 83ceb59f3462e..d9cd85f3836fc 100644 --- a/ci/builders/linux_web_engine.json +++ b/ci/builders/linux_web_engine.json @@ -351,11 +351,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -387,11 +387,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -423,11 +423,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -459,11 +459,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -495,11 +495,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -531,11 +531,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -567,11 +567,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -603,11 +603,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "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:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -855,11 +855,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -891,11 +891,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -927,11 +927,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -963,11 +963,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -999,11 +999,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1035,11 +1035,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1071,11 +1071,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1095,7 +1095,7 @@ "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", - "os=Mac-12" + "os=Mac" ], "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-12" + "os=Mac" ], "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-12" + "os=Mac" ], "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-12" + "os=Mac" ], "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-12" + "os=Mac" ], "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:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1303,11 +1303,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1339,11 +1339,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1375,11 +1375,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1411,11 +1411,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1447,11 +1447,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1483,11 +1483,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1519,11 +1519,11 @@ "test_dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" }, { "dependency": "chrome_and_driver", - "version": "version:117.0" + "version": "118.0.5993.70" } ], "tasks": [ @@ -1539,4 +1539,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/ci/builders/mac_host_engine.json b/ci/builders/mac_host_engine.json index aca28e9280962..658074a91eb7e 100644 --- a/ci/builders/mac_host_engine.json +++ b/ci/builders/mac_host_engine.json @@ -146,7 +146,7 @@ "dependencies": [ { "dependency": "goldctl", - "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603" + "version": "git_revision:dddc0623e63150cbbafdcb273d4048f329e1dd09" } ], "gclient_variables": { diff --git a/ci/builders/mac_ios_engine.json b/ci/builders/mac_ios_engine.json index 600f495929b0c..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", 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 feed60b197fdf..7bfa23f637b9c 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -97,7 +97,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 @@ -122,8 +121,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 @@ -152,6 +153,7 @@ ../../../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 @@ -229,6 +231,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 @@ -387,6 +390,9 @@ ../../../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 @@ -424,6 +430,21 @@ ../../../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/ninja ../../../flutter/third_party/spring_animation/README.md @@ -470,6 +491,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 @@ -1659,6 +1684,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 @@ -2010,21 +2036,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 @@ -2639,10 +2650,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 @@ -2852,6 +2860,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 f463b1c0dbf13..1ff7c432414a1 100644 --- a/ci/licenses_golden/licenses_dart +++ b/ci/licenses_golden/licenses_dart @@ -1,4 +1,4 @@ -Signature: fdaa9367efe1c4a57d9abc0eb91db374 +Signature: 5a10fa747cefdae79b8be0d5e79aed14 ==================================================================================================== LIBRARY: dart @@ -4278,14 +4278,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 +4354,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 diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 53a9299268fe4..589e36106dd78 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 @@ -1027,6 +1661,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 @@ -1043,6 +1679,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 @@ -1413,8 +2051,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 @@ -2242,6 +2880,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 @@ -2367,10 +3007,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 @@ -2697,6 +3337,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 @@ -2718,6 +3359,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 @@ -3400,6 +4043,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 @@ -3777,6 +4421,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 @@ -3793,6 +4439,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 @@ -4163,8 +4811,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 @@ -4997,6 +5645,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 @@ -5122,12 +5772,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 @@ -5465,6 +6115,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 @@ -5486,6 +6137,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 @@ -6176,6 +6829,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 @@ -6831,4 +7485,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 9922af8743dce..b63a0dac0b071 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 02ffea6fdf329c5632a665b4146b152e +Signature: 1cfca3b0c6bb9916b4c76fbce199090c ==================================================================================================== LIBRARY: fuchsia_sdk @@ -2447,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 @@ -2745,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 @@ -3065,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 @@ -3239,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 @@ -3752,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 @@ -4008,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 @@ -4347,6 +4361,7 @@ 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 @@ -4354,6 +4369,8 @@ ORIGIN: ../../../fuchsia/sdk/linux/pkg/inspect/offer.shard.cml + ../../../fuchsi 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 @@ -4412,6 +4429,7 @@ 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 @@ -4419,6 +4437,8 @@ 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. diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 64b87cc1a193f..f3a9b5411cbbf 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: eca53560b3fc7923df8e91b89c6a742d +Signature: ff4e2b4b203f52640a7f654a921bb4bb ==================================================================================================== 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,16 +386,14 @@ 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/crop-imagefilter.md -FILE: ../../../third_party/skia/relnotes/directcontext_gl.md -FILE: ../../../third_party/skia/relnotes/directcontext_submit.md -FILE: ../../../third_party/skia/relnotes/mesh-child-effect.md -FILE: ../../../third_party/skia/relnotes/mesh-child-params.md -FILE: ../../../third_party/skia/relnotes/minify-mesh.md -FILE: ../../../third_party/skia/relnotes/patheffects.md -FILE: ../../../third_party/skia/relnotes/shadowflags.md -FILE: ../../../third_party/skia/relnotes/skpicture_png.md +FILE: ../../../third_party/skia/relnotes/base64.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/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 @@ -648,10 +647,11 @@ 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/SkDeque.cpp + ../../../third_party/skia/LICENSE @@ -744,7 +744,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 @@ -794,10 +793,11 @@ 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/SkDeque.cpp @@ -890,7 +890,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 @@ -4966,43 +4965,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 @@ -5194,6 +5156,8 @@ ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkSamplerYcbcrConversion.h ORIGIN: ../../../third_party/skia/src/gpu/ganesh/vk/GrVkTypesPriv.cpp + ../../../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 @@ -5406,6 +5370,8 @@ 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/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 @@ -6427,8 +6393,6 @@ 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 @@ -6560,8 +6524,6 @@ 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 @@ -7351,7 +7313,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 @@ -7388,7 +7349,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 @@ -7568,7 +7528,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 @@ -7605,7 +7564,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 @@ -8650,8 +8608,6 @@ 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/SkGainmapInfo.h + ../../../third_party/skia/LICENSE @@ -8692,8 +8648,6 @@ 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/SkGainmapInfo.h @@ -8770,19 +8724,12 @@ ORIGIN: ../../../third_party/skia/fuzz/FuzzQuadRoots.cpp + ../../../third_party/ 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 @@ -8811,6 +8758,7 @@ ORIGIN: ../../../third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h + . 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,11 +8773,13 @@ ORIGIN: ../../../third_party/skia/include/private/chromium/SkImageChromium.h + . 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/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 @@ -8937,6 +8887,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 @@ -8950,6 +8901,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 @@ -9036,19 +8989,12 @@ FILE: ../../../third_party/skia/fuzz/FuzzQuadRoots.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 @@ -9077,6 +9023,7 @@ 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 @@ -9091,11 +9038,13 @@ 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/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 @@ -9203,6 +9152,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 @@ -9216,6 +9166,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 @@ -9364,6 +9316,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 468830592f309..2f295074189c8 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 0e74d276e12eb0bcc1bfbdbbfabc2574 +Signature: 22b6a63b18dd5d72500a4009598eba44 ==================================================================================================== 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 @@ -61899,4 +61265,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..12e514691e32b 100644 --- a/ci/licenses_golden/tool_signature +++ b/ci/licenses_golden/tool_signature @@ -1,2 +1,2 @@ -Signature: 575f2a41ff152394b7e539742fa248e6 +Signature: 2da12666eedd83b571506f27f8445d99 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/settings.h b/common/settings.h index eb07585a33fb0..af002c93006c4 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; 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/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 bb3ff367243eb..1704db6488361 100644 --- a/display_list/testing/dl_rendering_unittests.cc +++ b/display_list/testing/dl_rendering_unittests.cc @@ -16,13 +16,18 @@ #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 +161,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 +333,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 +382,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 +403,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 +436,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 +458,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 +496,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 +518,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 +538,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_; }; @@ -463,29 +595,30 @@ class RenderEnvironment { return RenderEnvironment(provider, PixelFormat::kN32PremulPixelFormat); } - void init_ref(SkRenderer& sk_renderer, - DlRenderer& dl_renderer, - DlColor bg = DlColor::kTransparent()) { - init_ref(kEmptySkSetup, sk_renderer, kEmptyDlSetup, dl_renderer, bg); - } - 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, @@ -504,7 +637,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 +646,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 +672,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 +707,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) @@ -637,9 +816,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 +864,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 +909,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 +1036,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 +1087,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 +1102,54 @@ class TestParameters { class CanvasCompareTester { public: - static std::vector> kTestProviders; + static std::vector kTestBackends; + static std::string kTempDirectory; + + 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::kTestBackends.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 : kTestBackends) { + 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(); + impeller_result->write( + to_png_filename(test_name + " (Impeller reference)")); + } + } 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 +1167,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(ToSk(alpha_layer_color)); - cv->saveLayer(nullptr, &save_p); + 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(ToSk(alpha_layer_color)); - cv->saveLayer(nullptr, &save_p); + 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(ToSk(alpha_layer_color)); - cv->saveLayer(layer_bounds, &save_p); + 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 +1286,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 +1313,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 +1376,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 +1394,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 +1427,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 +1445,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 +1474,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 +1535,25 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "Blend == SrcIn", - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kSrcIn); - p.setColor(ToSk(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(ToSk(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 +1563,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 +1583,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 +1599,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 +1615,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 +1647,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 +1666,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 +1698,31 @@ class CanvasCompareTester { RenderWith(testP, env, tolerance, CaseParameters( "ColorFilter == RotateRGB", - [=](SkCanvas*, SkPaint& p) { - p.setColor(ToSk(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(ToSk(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 +1735,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 +1772,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 +1850,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 @@ -1544,152 +1868,153 @@ class CanvasCompareTester { 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 +2026,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 +2062,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 +2087,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 +2123,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 +2140,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 +2166,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 +2200,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 +2234,56 @@ 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()); + } + + static std::string to_png_filename(const std::string& desc) { + if (kTempDirectory.length() == 0) { + kTempDirectory = fml::CreateTemporaryDirectory(); + } + + std::string ret = kTempDirectory + "/"; + for (const char& ch : desc) { + ret += (ch == ':' || ch == ' ') ? '_' : ch; + } + return ret + ".png"; } 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 +2291,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 +2320,56 @@ 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 (!success) { + FML_LOG(ERROR) << "Impeller issue encountered for: " + << *imp_job.MakeDisplayList(base_info); + std::string filename = to_png_filename(info + " (Impeller Output)"); + imp_result->write(filename); + FML_LOG(ERROR) << "output saved in: " << filename; + std::string src_filename = to_png_filename(info + " (Impeller Input)"); + env.ref_impeller_result()->write(src_filename); + FML_LOG(ERROR) << "compare to reference without attributes: " + << src_filename; + std::string sk_filename = to_png_filename(info + " (Skia Output)"); + sk_result->write(sk_filename); + FML_LOG(ERROR) << "and to Skia reference with attributes: " + << sk_filename; + std::string sk_src_filename = to_png_filename(info + " (Skia Input)"); + env.ref_sk_result()->write(sk_src_filename); + FML_LOG(ERROR) << "operating on Skia source image: " << sk_src_filename; + } } quickCompareToReference(sk_result.get(), dl_result.get(), true, @@ -2156,14 +2554,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 +2575,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 +2603,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 +2625,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 +2746,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 +2755,20 @@ class CanvasCompareTester { } }; -std::vector> - CanvasCompareTester::kTestProviders; +std::vector CanvasCompareTester::kTestBackends; +std::string CanvasCompareTester::kTempDirectory = ""; 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 +2782,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::kN32PremulPixelFormat); - CanvasCompareTester::kTestProviders.push_back(std::move(provider)); - } - static void SetUpTestSuite() { bool do_software = true; bool do_opengl = false; @@ -2446,84 +2803,77 @@ class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags { } } if (do_software) { - AddProvider(BackendType::kSoftwareBackend, "Software"); + CanvasCompareTester::AddProvider(BackendType::kSoftwareBackend); } if (do_opengl) { - AddProvider(BackendType::kOpenGlBackend, "OpenGL"); + CanvasCompareTester::AddProvider(BackendType::kOpenGlBackend); } if (do_metal) { - AddProvider(BackendType::kMetalBackend, "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::kTestBackends) { + 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(); - } - 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 +2887,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 +3013,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 +3047,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 +3082,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 +3127,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 +3179,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 +3212,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 +3264,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 +3310,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 +3507,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 +3553,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 +3599,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)); } @@ -3287,21 +3635,21 @@ sk_sp makeTestDisplayList() { 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 +3658,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); }, - [=](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); + [=](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); }, +#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 +3696,7 @@ TEST_F(DisplayListCanvas, DrawTextBlob) { #endif // OS_FUCHSIA } -TEST_F(DisplayListCanvas, DrawShadow) { +TEST_F(DisplayListRendering, DrawShadow) { SkPath path; path.addRoundRect( { @@ -3350,18 +3711,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 +3737,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 +3763,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 +3783,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::kTestBackends) { + 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, @@ -3498,12 +3862,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::kN32PremulPixelFormat); - environments.push_back(std::move(env)); - } auto render_content = [](DisplayListBuilder& builder) { builder.DrawRect( @@ -3563,12 +3921,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::kTestBackends) { + 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 +4002,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::kN32PremulPixelFormat); - 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, @@ -3685,7 +4039,10 @@ TEST_F(DisplayListCanvas, MatrixColorFilterModifyTransparencyCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::kTestBackends) { + 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 +4072,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::kN32PremulPixelFormat); - 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, @@ -3760,7 +4110,10 @@ TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::kTestBackends) { + 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 +4185,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::kN32PremulPixelFormat); - 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 << "]"; @@ -3869,7 +4215,10 @@ TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::kTestBackends) { + 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 +4243,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::kN32PremulPixelFormat); - 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 << "]"; @@ -3937,7 +4279,10 @@ TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) { builder2.Restore(); auto display_list2 = builder2.Build(); - for (auto& env : environments) { + for (auto& back_end : CanvasCompareTester::kTestBackends) { + 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 +4309,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 @@ -4086,7 +4431,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, @@ -4193,7 +4538,8 @@ class DisplayListNopTest : public DisplayListCanvas { SkPaint sk_paint; sk_paint.setBlendMode(sk_mode); sk_paint.setColor(ToSk(color)); - for (auto& provider : CanvasCompareTester::kTestProviders) { + for (auto& back_end : CanvasCompareTester::kTestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); auto result_surface = provider->MakeOffscreenSurface( test_image->width(), test_image->height(), DlSurfaceProvider::kN32PremulPixelFormat); @@ -4207,8 +4553,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); @@ -4255,7 +4601,8 @@ class DisplayListNopTest : public DisplayListCanvas { 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::kTestBackends) { + auto provider = CanvasCompareTester::GetProvider(back_end); auto result_surface = provider->MakeOffscreenSurface( w, h, DlSurfaceProvider::kN32PremulPixelFormat); SkCanvas* result_canvas = result_surface->sk_surface()->getCanvas(); @@ -4269,8 +4616,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_surface_metal.cc b/display_list/testing/dl_test_surface_metal.cc index 01048e783cc1a..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" @@ -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 619d4de7dbb87..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() {} @@ -30,10 +33,25 @@ class DlMetalSurfaceProvider : public DlSurfaceProvider { bool supports(PixelFormat format) const override { 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 f3b611c62e8ee..ab7d75c0901e7 100644 --- a/display_list/testing/dl_test_surface_provider.cc +++ b/display_list/testing/dl_test_surface_provider.cc @@ -23,6 +23,17 @@ 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) { diff --git a/display_list/testing/dl_test_surface_provider.h b/display_list/testing/dl_test_surface_provider.h index 57055b93d3c19..1ad4af789cb43 100644 --- a/display_list/testing/dl_test_surface_provider.h +++ b/display_list/testing/dl_test_surface_provider.h @@ -7,6 +7,8 @@ #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" @@ -15,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; @@ -53,12 +65,14 @@ class DlSurfaceProvider { 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, @@ -70,6 +84,16 @@ class DlSurfaceProvider { 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/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..339374d77b837 100644 --- a/flow/frame_timings.cc +++ b/flow/frame_timings.cc @@ -254,4 +254,8 @@ const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const { return frame_number_trace_arg_val_.c_str(); } +void FrameTimingsRecorder::AssertInState(State state) const { + FML_DCHECK(state_ == state); +} + } // namespace flutter diff --git a/flow/frame_timings.h b/flow/frame_timings.h index e477a80b8f955..c81bcc3362298 100644 --- a/flow/frame_timings.h +++ b/flow/frame_timings.h @@ -116,6 +116,13 @@ 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. + 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/layer_tree.h b/flow/layers/layer_tree.h index 5f573da2099a0..af162d58e0c4f 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -94,6 +94,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/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index e1bbb29893268..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) 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 887d7637b7bb7..0b4338435c375 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -337,7 +337,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/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_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/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_unittests.cc b/impeller/aiks/aiks_unittests.cc index 4950aae8496fc..1a50dbe3e9406 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" @@ -123,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, @@ -1580,6 +1608,26 @@ TEST_P(AiksTest, DrawPaintWithAdvancedBlendOverFilter) { 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 { @@ -3562,6 +3610,56 @@ TEST_P(AiksTest, ClearBlend) { 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())); } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index ae4c9d512037f..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,7 +62,7 @@ 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.rendering_mode = Entity::RenderingMode::kSubpass; auto subpass = std::make_unique(); @@ -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); } @@ -173,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))); @@ -183,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)); @@ -217,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))); @@ -238,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)))); @@ -253,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)))); @@ -274,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); } @@ -318,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 && @@ -371,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; } @@ -410,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); } @@ -425,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, @@ -444,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; } @@ -503,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()); @@ -525,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(); @@ -550,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(); @@ -601,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 @@ -674,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 a7a5652229b03..22c154f3cb3e0 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -34,7 +34,7 @@ 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; + 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 87f7022e1cd1f..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 ******************************************************************************/ @@ -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 1c5bf32dd9f8a..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. @@ -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/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 66a3f7280f70a..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())) { @@ -59,7 +72,6 @@ std::shared_ptr Paint::CreateContentsForGeometry( std::shared_ptr Paint::WithFilters( std::shared_ptr input) const { input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes); - input = WithInvertFilter(input); auto image_filter = WithImageFilter(input, Matrix(), Entity::RenderingMode::kDirect); if (image_filter) { @@ -111,6 +123,7 @@ std::shared_ptr Paint::WithColorFilter( return input; } + auto color_filter = GetColorFilter(); if (!color_filter) { return input; } @@ -121,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 { @@ -208,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 c2e907a1cc5b0..bf96842b783c9 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -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 @@ -108,9 +110,6 @@ struct Paint { std::shared_ptr input, ColorFilterContents::AbsorbOpacity absorb_opacity = ColorFilterContents::AbsorbOpacity::kNo) const; - - std::shared_ptr WithInvertFilter( - std::shared_ptr input) const; }; } // namespace impeller 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..e3f87c8eea3dd --- /dev/null +++ b/impeller/aiks/trace_serializer.cc @@ -0,0 +1,226 @@ +// 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 Paint& paint) { + os << "{" << std::endl; + os << " color: [" << paint.color << "]" << std::endl; + os << " color_source:" + << "[ColorSource]" << 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/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/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/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 0b19cefb5b79b..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" @@ -483,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); } 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 896989c34ef3d..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" @@ -1681,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/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 1461e1b9cd9cf..560c9d804800e 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -164,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); @@ -187,142 +188,109 @@ ContentContext::ContentContext( 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()) { @@ -358,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; } @@ -382,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 4c4c17c43b194..a5f0aab9bd6a8 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -721,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 @@ -824,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( @@ -843,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 a181e22302b21..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( @@ -133,8 +133,8 @@ bool Contents::ApplyColorFilter( } 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; } @@ -145,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 b9dec5db2d12e..6a25261ac0c4e 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -36,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; @@ -66,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; @@ -89,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)`. /// @@ -113,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. diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 7e50634e4d185..225f915f9aa5e 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -114,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()) { @@ -122,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(); } @@ -155,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); @@ -181,6 +180,8 @@ 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); @@ -242,7 +243,7 @@ static std::optional AdvancedBlend( ? 1.0f : dst_snapshot->opacity) * alpha.value_or(1.0)}, - entity.GetBlendMode(), entity.GetStencilDepth()); + entity.GetBlendMode(), entity.GetClipDepth()); } std::optional BlendFilterContents::CreateForegroundAdvancedBlend( @@ -281,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); @@ -294,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: @@ -355,6 +354,8 @@ 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); @@ -391,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; } @@ -416,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; } @@ -429,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, @@ -454,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); @@ -467,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; @@ -479,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); @@ -519,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; } @@ -565,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()) { @@ -584,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); @@ -672,7 +672,7 @@ static std::optional PipelineBlend( ? 1.0f : dst_snapshot->opacity) * alpha.value_or(1.0)}, - entity.GetBlendMode(), entity.GetStencilDepth()); + entity.GetBlendMode(), entity.GetClipDepth()); } #define BLEND_CASE(mode) \ 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_matrix_filter_contents.cc b/impeller/entity/contents/filters/color_matrix_filter_contents.cc index 98024afa53207..d9878896cd903 100644 --- a/impeller/entity/contents/filters/color_matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/color_matrix_filter_contents.cc @@ -57,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(); @@ -68,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); @@ -117,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/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 8e3c9569305bf..72f56f443b79e 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -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/linear_to_srgb_filter_contents.cc b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc index df6f5e0c0e19c..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(); @@ -98,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 612e2e7b43eda..c16bcdb88d698 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/matrix_filter_contents.cc @@ -45,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! // @@ -65,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( 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 253089a85a26f..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(); @@ -98,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 3ced8b52d9d76..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,19 +131,16 @@ 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); frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * - entity.GetTransformation() * - Matrix::MakeTranslation(src_coverage.origin); + src_snapshot->transform; frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale(); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); 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/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 ffb190cf7a271..665cb599bc551 100644 --- a/impeller/entity/contents/solid_rrect_blur_contents.cc +++ b/impeller/entity/contents/solid_rrect_blur_contents.cc @@ -80,8 +80,6 @@ bool SolidRRectBlurContents::Render(const ContentContext& renderer, {Point(left, top)}, {Point(right, top)}, {Point(left, bottom)}, - {Point(left, bottom)}, - {Point(right, top)}, {Point(right, bottom)}, }); } @@ -89,14 +87,14 @@ bool SolidRRectBlurContents::Render(const ContentContext& renderer, Command cmd; DEBUG_COMMAND_INFO(cmd, "RRect Shadow"); ContentContextOptions opts = OptionsFromPassAndEntity(pass, entity); - opts.primitive_type = PrimitiveType::kTriangle; + 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())); 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 8cbcfaa9440b7..26fa0b3974e14 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -65,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); @@ -116,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 0d91112369ae7..bb065b959e146 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -198,12 +198,10 @@ std::optional EntityPass::GetSubpassCoverage( std::shared_ptr image_filter = subpass.delegate_->WithImageFilter(Rect(), subpass.xformation_); - // If the filter graph transforms the basis of the subpass, then its space - // has deviated too much from the parent pass to safely intersect with the - // pass coverage limit. - coverage_limit = - (image_filter && image_filter->IsTranslationOnly() ? std::nullopt - : coverage_limit); + // 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. @@ -352,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() && @@ -378,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. @@ -490,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( @@ -501,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; //-------------------------------------------------------------------------- @@ -546,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 )) { @@ -581,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(); } @@ -599,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(); @@ -641,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( @@ -652,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 @@ -686,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 - @@ -706,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 { @@ -728,8 +734,8 @@ bool EntityPass::OnRender( 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); @@ -762,78 +768,77 @@ 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. 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 stencil coverage hint with the + // blend), then we need to intersect the clip coverage hint with the // existing coverage hint. element_entity.GetContents()->SetCoverageHint( - current_stencil_coverage->Intersection( + current_clip_coverage->Intersection( element_entity.GetContents()->GetCoverageHint().value())); } else { - element_entity.GetContents()->SetCoverageHint(current_stencil_coverage); + 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.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_depth = element_entity.GetClipDepth(); + FML_DCHECK(restoration_depth < 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_depth + 1 < clip_coverage_stack.size()) + ? clip_coverage_stack[restoration_depth + 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_depth + 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; } @@ -856,8 +861,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; @@ -879,7 +884,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); } @@ -900,15 +905,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: @@ -1126,12 +1131,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) { diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index d09649abfd9d0..261d3272b14aa 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -43,12 +43,12 @@ class EntityPass { const Matrix& effect_transform, 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); @@ -142,7 +142,21 @@ class EntityPass { void SetEnableOffscreenCheckerboard(bool enabled); //---------------------------------------------------------------------------- - /// @brief Get the coverage of an unfiltered subpass. + /// @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, @@ -182,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 @@ -217,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 @@ -249,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; @@ -261,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_unittests.cc b/impeller/entity/entity_unittests.cc index c76ece221dbd8..0eb86e8b9fc27 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -1631,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()); } @@ -1647,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()); } @@ -1657,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()); } @@ -1669,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. @@ -1683,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); } } @@ -2417,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())); 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/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/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 30253989801de..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,10 +24,12 @@ class MetalScreenshot { size_t GetWidth() const; + size_t GetBytesPerRow() const; + bool WriteToPNG(const std::string& path) const; private: - friend class MetalScreenshoter; + friend class MetalScreenshotter; explicit MetalScreenshot(CGImageRef cgImage); FML_DISALLOW_COPY_AND_ASSIGN(MetalScreenshot); CGImageRef cg_image_; diff --git a/impeller/golden_tests/metal_screenshot.mm b/impeller/golden_tests/metal_screenshot.mm index 9a26bf08d3e5e..767ee93cb4132 100644 --- a/impeller/golden_tests/metal_screenshot.mm +++ b/impeller/golden_tests/metal_screenshot.mm @@ -29,6 +29,10 @@ return CGImageGetWidth(cg_image_); } +size_t MetalScreenshot::GetBytesPerRow() const { + return CGImageGetBytesPerRow(cg_image_); +} + bool MetalScreenshot::WriteToPNG(const std::string& path) const { bool result = false; NSURL* output_url = 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/playground/BUILD.gn b/impeller/playground/BUILD.gn index 08e47647dc7e3..25408c569b7ac 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -47,7 +47,7 @@ impeller_component("playground") { "../scene/shaders", "imgui:imgui_impeller_backend", "//flutter/fml", - "//third_party/glfw", + "//flutter/third_party/glfw", "//third_party/imgui:imgui_glfw", ] 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..1d783c0f8c841 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,21 @@ 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/mock_gles.cc", + "test/mock_gles.h", + "test/mock_gles_unittests.cc", + ] + deps = [ + ":gles", + "//flutter/testing:testing_lib", + ] +} + impeller_component("gles") { public_configs = [] 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..6896d6609777e 100644 --- a/impeller/renderer/backend/gles/context_gles.cc +++ b/impeller/renderer/backend/gles/context_gles.cc @@ -6,6 +6,7 @@ #include "impeller/base/config.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/command_buffer_gles.h" namespace impeller { @@ -52,32 +53,13 @@ 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())); } is_valid_ = true; diff --git a/impeller/renderer/backend/gles/context_gles.h b/impeller/renderer/backend/gles/context_gles.h index 92054cb413bee..56da6c55a8092 100644 --- a/impeller/renderer/backend/gles/context_gles.h +++ b/impeller/renderer/backend/gles/context_gles.h @@ -7,7 +7,7 @@ #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/pipeline_library_gles.h" #include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/backend/gles/sampler_library_gles.h" @@ -44,6 +44,9 @@ class ContextGLES final : public Context, std::shared_ptr pipeline_library_; std::shared_ptr sampler_library_; std::shared_ptr resource_allocator_; + // 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; 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 9bacfc87db4b3..e14116ef52b50 100644 --- a/impeller/renderer/backend/gles/gles.h +++ b/impeller/renderer/backend/gles/gles.h @@ -6,6 +6,7 @@ // IWYU pragma: begin_exports #include "GLES3/gl3.h" +#define GL_CLAMP_TO_BORDER 0x812D #define GL_GLEXT_PROTOTYPES #include "GLES2/gl2ext.h" // IWYU pragma: end_exports 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 eac2b77e59bb1..22945e6283850 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -17,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) @@ -28,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; + } } } }; @@ -39,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. /// @@ -63,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 @@ -75,7 +96,6 @@ struct GLProc { constexpr bool IsAvailable() const { return function != nullptr; } void Reset() { - name = nullptr; function = nullptr; error_fn = nullptr; } @@ -169,6 +189,7 @@ struct GLProc { #define FOR_EACH_IMPELLER_GLES3_PROC(PROC) PROC(BlitFramebuffer); #define FOR_EACH_IMPELLER_EXT_PROC(PROC) \ + PROC(DebugMessageControlKHR); \ PROC(DiscardFramebufferEXT); \ PROC(FramebufferTexture2DMultisampleEXT) \ PROC(PushDebugGroupKHR); \ @@ -189,6 +210,7 @@ class ProcTableGLES { public: using Resolver = std::function; explicit ProcTableGLES(Resolver resolver); + ProcTableGLES(ProcTableGLES&& other) = default; ~ProcTableGLES(); @@ -207,7 +229,7 @@ class ProcTableGLES { const DescriptionGLES* GetDescription() const; - const CapabilitiesGLES* GetCapabilities() const; + const std::shared_ptr& GetCapabilities() const; std::string DescribeCurrentFramebuffer() const; @@ -226,7 +248,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..dfd26f3dde09d 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 { @@ -233,6 +234,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 +255,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..801542b4fb27a 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" @@ -30,7 +29,7 @@ class ReactorGLES { using Ref = std::shared_ptr; - ReactorGLES(std::unique_ptr gl); + explicit ReactorGLES(std::unique_ptr gl); ~ReactorGLES(); @@ -63,8 +62,7 @@ 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(); } }; @@ -100,6 +98,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 735f8a85495f5..4816a2973b0da 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/gles/render_pass_gles.h" #include "flutter/fml/trace_event.h" +#include "fml/closure.h" #include "impeller/base/validation.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/formats_gles.h" @@ -192,8 +193,10 @@ struct RenderPassData { } } - 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; } } @@ -204,7 +207,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); @@ -300,7 +308,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 } //-------------------------------------------------------------------------- diff --git a/impeller/renderer/backend/gles/sampler_gles.cc b/impeller/renderer/backend/gles/sampler_gles.cc index f1a54debcbc19..19d4e0ff8f623 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,10 @@ static GLint ToAddressMode(SamplerAddressMode mode) { case SamplerAddressMode::kMirror: return GL_MIRRORED_REPEAT; case SamplerAddressMode::kDecal: - break; // Unsupported. + if (supports_decal_sampler_address_mode) { + return GL_CLAMP_TO_BORDER; + } + break; } FML_UNREACHABLE(); } @@ -96,10 +100,14 @@ bool SamplerGLES::ConfigureBoundTexture(const TextureGLES& texture, 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.value(), GL_TEXTURE_WRAP_S, + ToAddressMode(desc.width_address_mode, + gl.GetCapabilities()->SupportsDecalSamplerAddressMode())); + gl.TexParameteri( + target.value(), GL_TEXTURE_WRAP_T, + ToAddressMode(desc.height_address_mode, + gl.GetCapabilities()->SupportsDecalSamplerAddressMode())); 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/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/mock_gles.cc b/impeller/renderer/backend/gles/test/mock_gles.cc new file mode 100644 index 0000000000000..1abf7e8721d46 --- /dev/null +++ b/impeller/renderer/backend/gles/test/mock_gles.cc @@ -0,0 +1,151 @@ +// 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); + +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 { + 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/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 9b911c66cef17..260c9e4a45861 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc @@ -16,9 +16,11 @@ TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) { 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()); @@ -32,6 +34,7 @@ TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) { 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({ @@ -48,6 +51,7 @@ TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) { 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() @@ -66,6 +70,7 @@ TEST(BlitCommandVkTest, BlitGenerateMipmapCommandVK) { 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/surface_context_vk.cc b/impeller/renderer/backend/vulkan/surface_context_vk.cc index 2e20d2af5cbe6..afc1407f6f3f5 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -63,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; } diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc index 0af88001b8e95..dcd5d1c50b593 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc @@ -142,6 +142,7 @@ SwapchainImplVK::SwapchainImplVK( vk::SwapchainKHR old_swapchain, vk::SurfaceTransformFlagBitsKHR last_transform) { if (!context) { + VALIDATION_LOG << "Cannot create a swapchain without a context."; 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/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/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/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/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/painting.dart b/lib/ui/painting.dart index 638a257301d3b..cfb5e2348f06f 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1091,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: @@ -1479,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')) { @@ -1562,9 +1545,6 @@ class Paint { if (invertColors) { result.write('${semicolon}invert: $invertColors'); } - if (_dither) { - result.write('${semicolon}dither: $_dither'); - } result.write(')'); return result.toString(); } 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 3a957ede46156..7a82c46c9b855 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: 1181205 - Mac: 1181205 - Mac_Arm: 1181212 - Win: 1181217 - version: '117.0' # CIPD tag for the above Build IDs. Normally "ChromeMajorVersion.UploadAttempt". ;) + version: '118.0.5993.70' 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/generate_builder_json.dart b/lib/web_ui/dev/generate_builder_json.dart index e92820ee8d72a..6bbf9e94fe83e 100644 --- a/lib/web_ui/dev/generate_builder_json.dart +++ b/lib/web_ui/dev/generate_builder_json.dart @@ -149,12 +149,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:117.0', + 'version': '118.0.5993.70', }, 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/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/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/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/surface.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart index 4827c78b00f77..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/pubspec.yaml b/lib/web_ui/pubspec.yaml index fc25009154222..42d44d2894061 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -45,7 +45,7 @@ dev_dependencies: uuid: 4.1.0 watcher: 1.1.0 web_socket_channel: any - webdriver: 3.0.2 + webdriver: 3.0.3 webkit_inspection_protocol: any yaml: 3.0.0 web_test_utils: 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/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index a036d4723cb34..bc3de031f4411 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -23,7 +23,7 @@ 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, float device_pixel_ratio) = 0; 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 b0d8f62a22033..3dd925cee6213 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 @@ -84,7 +85,7 @@ void Animator::BeginFrame( } frame_scheduled_ = false; - regenerate_layer_tree_ = false; + regenerate_layer_trees_ = false; pending_frame_semaphore_.Signal(); if (!producer_continuation_) { @@ -143,7 +144,6 @@ void Animator::BeginFrame( void Animator::Render(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. @@ -161,12 +161,16 @@ void Animator::Render(std::unique_ptr layer_tree, 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); + // TODO(dkwingsmt): Currently only supports a single window. + // See https://github.com/flutter/flutter/issues/135530, item 2. + int64_t view_id = kFlutterImplicitViewId; + std::vector> layer_trees_tasks; + layer_trees_tasks.push_back(std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio)); // Commit the pending continuation. PipelineProduceResult result = - producer_continuation_.Complete(std::move(layer_tree_item)); + producer_continuation_.Complete(std::make_unique( + std::move(layer_trees_tasks), std::move(frame_timings_recorder_))); if (!result.success) { FML_DLOG(INFO) << "No pending continuation to commit"; @@ -188,15 +192,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 @@ -206,18 +210,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()) { @@ -248,8 +252,8 @@ 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)); } diff --git a/shell/common/animator.h b/shell/common/animator.h index 4e6295957bdcc..8a92705ede9e6 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,7 +51,7 @@ 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, float device_pixel_ratio); @@ -85,9 +84,9 @@ class Animator final { private: void BeginFrame(std::unique_ptr frame_timings_recorder); - bool CanReuseLastLayerTree(); + bool CanReuseLastLayerTrees(); - void DrawLastLayerTree( + void DrawLastLayerTrees( std::unique_ptr frame_timings_recorder); void AwaitVSync(); @@ -102,12 +101,11 @@ class Animator final { std::unique_ptr frame_timings_recorder_; 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..9190659fc6f55 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -41,10 +41,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; 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..ea0cb43d9be18 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -455,8 +455,8 @@ 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, diff --git a/shell/common/engine.h b/shell/common/engine.h index f7d888de425f2..a60d8b81f8ed4 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. 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 9a7e6e16552b0..0fc4d884c97bf 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" @@ -32,7 +33,6 @@ #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 { @@ -42,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), @@ -108,6 +109,7 @@ void Rasterizer::TeardownExternalViewEmbedder() { } void Rasterizer::Teardown() { + is_torn_down_ = true; if (surface_) { auto context_switch = surface_->MakeRenderContextCurrent(); if (context_switch->GetResult()) { @@ -119,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()) { @@ -129,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(); @@ -161,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() { @@ -176,74 +188,79 @@ 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) { 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; } @@ -270,12 +287,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 { @@ -393,42 +427,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() && @@ -497,73 +520,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 @@ -604,7 +716,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, @@ -618,25 +730,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(); @@ -655,22 +759,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( @@ -760,7 +865,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 {}; @@ -801,9 +910,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..1e21d0d8f1ecb 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(); @@ -214,13 +225,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); @@ -281,12 +293,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); @@ -353,11 +366,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); @@ -406,7 +421,7 @@ TEST(RasterizerTest, /*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 +442,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 +458,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 +493,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 +525,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 +554,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 +610,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(); @@ -635,12 +660,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); @@ -693,18 +719,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(); @@ -751,18 +778,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(); @@ -808,18 +836,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 +893,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(); @@ -942,13 +974,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 +1146,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 +1230,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 82db9dea4ff64..a6ba487b581f7 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 { @@ -1220,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); } @@ -1243,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_); @@ -1251,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)); } }); @@ -1695,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) { @@ -1840,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()), @@ -1971,7 +1975,8 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( // 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); @@ -1986,7 +1991,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; @@ -2002,7 +2007,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 d3bb7f0bd620c..133fecc4d0f43 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -650,10 +650,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| diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 38170118e2072..d19e5cb2bcaff 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -17,12 +17,8 @@ #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 { diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2371fa9113aa7..f899d5c5ede97 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -310,30 +310,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) { diff --git a/shell/common/switches.cc b/shell/common/switches.cc index f9cbb548cc0f5..655742109ae0b 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -455,6 +455,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), diff --git a/shell/common/switches.h b/shell/common/switches.h index 7dd04fc7e12e4..c6048705f3121 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -280,6 +280,9 @@ DEF_SWITCH(LeakVM, "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_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/BUILD.gn b/shell/platform/android/BUILD.gn index 06e4c9de02985..c455533c6457f 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -96,12 +96,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", diff --git a/shell/platform/android/android_context_gl_impeller.cc b/shell/platform/android/android_context_gl_impeller.cc index a2a555000ae09..83c1bb4fedd91 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 @@ -60,6 +61,9 @@ 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_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), 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 0b5d54a20785a..c8a7129e22fc1 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,71 +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(ERROR) << "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) { - jni_facade_->HardwareBufferClose(hardware_buffer_java); - jni_facade_->ImageClose(image_java); - 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..2bfa379f39c1e 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -367,6 +367,8 @@ public FlutterEngine( this.renderer = new FlutterRenderer(flutterJNI); this.platformViewsController = platformViewsController; + this.platformViewsController.setDisableImageReaderPlatformViews( + flutterJNI.getDisableImageReaderPlatformViews()); this.platformViewsController.onAttachedToJNI(); this.pluginRegistry = 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..da114ae3978d7 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,8 @@ 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 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,6 +333,9 @@ 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"); 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 6a0f3686ec5c9..e71e36488169d 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,9 @@ 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 +224,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); } @@ -367,6 +371,9 @@ public void release() { @Override @TargetApi(19) public void pushImage(Image image) { + if (released) { + return; + } Image toClose; synchronized (this) { toClose = this.image; @@ -374,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) { @@ -383,12 +391,26 @@ public void pushImage(Image image) { } @Override + @TargetApi(33) public Image acquireLatestImage() { Image r; synchronized (this) { r = this.image; this.image = null; } + if (r != null) { + try { + SyncFence fence = r.getFence(); + if (fence.getSignalTime() == SyncFence.SIGNAL_TIME_PENDING) { + boolean signaled = fence.awaitForever(); + if (!signaled) { + Log.e(TAG, "acquireLatestImage image's fence was never signalled."); + } + } + } catch (IOException e) { + // Drop. + } + } return r; } @@ -479,9 +501,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/plugin/platform/ImageReaderPlatformViewRenderTarget.java b/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java index 4ba58fce13b5f..79690179a8189 100644 --- a/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java +++ b/shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java @@ -12,14 +12,14 @@ import io.flutter.Log; import io.flutter.view.TextureRegistry.ImageTextureEntry; -@TargetApi(29) +@TargetApi(33) public class ImageReaderPlatformViewRenderTarget implements PlatformViewRenderTarget { private ImageTextureEntry textureEntry; private ImageReader reader; private int bufferWidth = 0; private int bufferHeight = 0; private static final String TAG = "ImageReaderPlatformViewRenderTarget"; - private static final int MAX_IMAGES = 3; + private static final int MAX_IMAGES = 4; private void closeReader() { if (this.reader != null) { @@ -40,7 +40,7 @@ public void onImageAvailable(ImageReader reader) { try { image = reader.acquireLatestImage(); } catch (IllegalStateException e) { - Log.e(TAG, "New image available but it could not be acquired: " + e.toString()); + Log.e(TAG, "onImageAvailable acquireLatestImage failed: " + e.toString()); } if (image == null) { return; @@ -72,33 +72,18 @@ protected ImageReader createImageReader33() { return reader; } - @TargetApi(29) - protected ImageReader createImageReader29() { - final ImageReader reader = - ImageReader.newInstance( - bufferWidth, - bufferHeight, - ImageFormat.PRIVATE, - MAX_IMAGES, - HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); - reader.setOnImageAvailableListener(this.onImageAvailableListener, onImageAvailableHandler); - return reader; - } - protected ImageReader createImageReader() { if (Build.VERSION.SDK_INT >= 33) { return createImageReader33(); - } else if (Build.VERSION.SDK_INT >= 29) { - return createImageReader29(); } throw new UnsupportedOperationException( - "ImageReaderPlatformViewRenderTarget requires API version 29+"); + "ImageReaderPlatformViewRenderTarget requires API version 33+"); } public ImageReaderPlatformViewRenderTarget(ImageTextureEntry textureEntry) { - if (Build.VERSION.SDK_INT < 29) { + if (Build.VERSION.SDK_INT < 33) { throw new UnsupportedOperationException( - "ImageReaderPlatformViewRenderTarget requires API version 29+"); + "ImageReaderPlatformViewRenderTarget requires API version 33+"); } this.textureEntry = textureEntry; } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 0e7ad70aee29d..f8e238c137052 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -147,7 +147,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega // Whether software rendering is used. private boolean usesSoftwareRendering = false; - private static boolean enableHardwareBufferRenderingTarget = true; + private static boolean enableHardwareBufferRenderingTarget = false; private final PlatformViewsChannel.PlatformViewsHandler channelHandler = new PlatformViewsChannel.PlatformViewsHandler() { @@ -181,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); } @@ -195,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 @@ -773,6 +776,10 @@ public void setSoftwareRendering(boolean useSoftwareRendering) { usesSoftwareRendering = useSoftwareRendering; } + public void setDisableImageReaderPlatformViews(boolean disableImageReaderPlatformViews) { + enableHardwareBufferRenderingTarget = !disableImageReaderPlatformViews; + } + /** * Detaches this platform views controller. * @@ -970,11 +977,13 @@ private void unlockInputConnection(@NonNull VirtualDisplayController controller) private static PlatformViewRenderTarget makePlatformViewRenderTarget( TextureRegistry textureRegistry) { - if (enableHardwareBufferRenderingTarget && 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); } @@ -1092,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/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index ac6111c343ada..83f1f635cf639 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" @@ -324,18 +324,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_)); 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/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/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/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/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/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index 19c779ec2a992..6a9c948ab5c08 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -384,10 +384,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]; } } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index 4a6afa2e3fccf..165a4b5bb110e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -2049,6 +2049,41 @@ - (bool)testSelectorsAreForwardedToFramework { ASSERT_FALSE(window.firstResponder == viewController.textInputPlugin); } +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)); 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 15d36a777fda0..3ad17b76cbea4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -169,7 +169,7 @@ - (void)setBackgroundColor:(NSColor*)color; /** * Private interface declaration for FlutterViewController. */ -@interface FlutterViewController () +@interface FlutterViewController () /** * The tracking area used to generate hover events, if enabled. @@ -831,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]; } @@ -851,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/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/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/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/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..9c0059b612160 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -29,6 +29,46 @@ #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/skia/include/core/SkSurface.h" +#if IMPELLER_SUPPORTS_RENDERING +#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 { + fml::RefPtr vulkan_proc_table; + std::shared_ptr context; + std::shared_ptr surface_context; +}; +#else +struct ImpellerVulkanContextHolder {}; +#endif // IMPELLER_SUPPORTS_RENDERING + #if defined(FML_OS_WIN) #include #endif // defined(FML_OS_WIN) @@ -81,11 +121,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 IMPELLER_SUPPORTS_RENDERING + if (impeller_context_holder_.context) { + impeller_context_holder_.context->Shutdown(); + } +#endif + } + + // |PlatformView| + std::shared_ptr GetImpellerContext() const override { +#if IMPELLER_SUPPORTS_RENDERING + return std::static_pointer_cast( + impeller_context_holder_.context); +#else + return nullptr; +#endif // IMPELLER_SUPPORTS_RENDERING + } // |PlatformView| std::unique_ptr CreateRenderingSurface() override { +#if IMPELLER_SUPPORTS_RENDERING + 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 // IMPELLER_SUPPORTS_RENDERING auto surface = std::make_unique( this, true /* render to surface */); FML_DCHECK(surface->IsValid()); @@ -126,6 +196,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,10 +306,63 @@ int RunTester(const flutter::Settings& settings, io_task_runner // io ); + ImpellerVulkanContextHolder impeller_context_holder; + +#if IMPELLER_SUPPORTS_RENDERING + if (settings.enable_impeller) { + impeller_context_holder.vulkan_proc_table = + fml::MakeRefCounted(VULKAN_SO_PATH); + if (!impeller_context_holder.vulkan_proc_table + ->NativeGetInstanceProcAddr()) { + FML_LOG(ERROR) << "Could not load Swiftshader library."; + return EXIT_FAILURE; + } + impeller::ContextVK::Settings context_settings; + context_settings.proc_address_callback = + impeller_context_holder.vulkan_proc_table->NativeGetInstanceProcAddr(); + context_settings.shader_libraries_data = ShaderLibraryMappings(); + context_settings.cache_directory = fml::paths::GetCachesDirectory(); + context_settings.enable_validation = settings.enable_vulkan_validation; + + impeller_context_holder.context = + impeller::ContextVK::Create(std::move(context_settings)); + if (!impeller_context_holder.context || + !impeller_context_holder.context->IsValid()) { + VALIDATION_LOG << "Could not create Vulkan context."; + return EXIT_FAILURE; + } + + impeller::vk::SurfaceKHR vk_surface; + impeller::vk::HeadlessSurfaceCreateInfoEXT surface_create_info; + auto res = + impeller_context_holder.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 EXIT_FAILURE; + } + + impeller::vk::UniqueSurfaceKHR surface{ + vk_surface, impeller_context_holder.context->GetInstance()}; + impeller_context_holder.surface_context = + impeller_context_holder.context->CreateSurfaceContext(); + if (!impeller_context_holder.surface_context->SetWindowSurface( + std::move(surface))) { + VALIDATION_LOG << "Could not set up surface for context."; + return EXIT_FAILURE; + } + } +#endif // IMPELLER_SUPPORTS_RENDERING + Shell::CreateCallback on_create_platform_view = - [](Shell& shell) { - return std::make_unique(shell, - shell.GetTaskRunners()); + [impeller_context_holder = + std::move(impeller_context_holder)](Shell& shell) { + return std::make_unique( + shell, shell.GetTaskRunners(), impeller_context_holder); }; Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { 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/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/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/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/display_list_testing.cc b/testing/display_list_testing.cc index c18d618209280..1fa3561ddd45e 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -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/run_tests.py b/testing/run_tests.py index 9a0e24f4f1022..500dbba643d03 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -41,7 +41,6 @@ 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__) @@ -402,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'), @@ -577,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" % ( @@ -592,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( @@ -611,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' logger.info( "Running test '%s' using '%s' (%s)", kernel_file_name, tester_name, - threading + 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( @@ -631,7 +648,7 @@ def gather_dart_test( None, command_args, forbidden_output=forbidden_output, - expect_failure=expect_failure, + expect_failure=options.expect_failure, ) @@ -850,8 +867,38 @@ def gather_dart_tests(build_dir, test_filter): 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) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions( + multithreaded=True, + enable_impeller=False, + enable_observatory=True + ) + ) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions( + multithreaded=True, + enable_impeller=True, + enable_observatory=True + ) + ) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions( + multithreaded=False, + enable_impeller=False, + enable_observatory=True + ) + ) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions( + multithreaded=False, + enable_impeller=True, + enable_observatory=True + ) + ) for dart_test_file in dart_tests: if test_filter is not None and os.path.basename(dart_test_file @@ -859,8 +906,22 @@ def gather_dart_tests(build_dir, test_filter): logger.info("Skipping '%s' due to filter.", dart_test_file) else: logger.info("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) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions(multithreaded=True, enable_impeller=False) + ) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions(multithreaded=True, enable_impeller=True) + ) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions(multithreaded=False, enable_impeller=False) + ) + yield gather_dart_test( + build_dir, dart_test_file, + FlutterTesterOptions(multithreaded=False, enable_impeller=True) + ) def gather_dart_smoke_test(build_dir, test_filter): @@ -875,8 +936,14 @@ def gather_dart_smoke_test(build_dir, test_filter): ) not in test_filter: logger.info("Skipping '%s' due to filter.", smoke_test) else: - yield gather_dart_test(build_dir, smoke_test, True, expect_failure=True) - yield gather_dart_test(build_dir, smoke_test, False, expect_failure=True) + 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_dart_package_tests(build_dir, package_path, extra_opts): diff --git a/testing/test_vulkan_context.cc b/testing/test_vulkan_context.cc index ea36581add8e2..5652431a800ad 100644 --- a/testing/test_vulkan_context.cc +++ b/testing/test_vulkan_context.cc @@ -14,19 +14,12 @@ #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/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 { 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..45ae89eb3d05d --- /dev/null +++ b/third_party/.gitignore @@ -0,0 +1,23 @@ +# 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/ +!ninja/ +!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/txt/src/skia/paragraph_skia.cc b/third_party/txt/src/skia/paragraph_skia.cc index d1dc525909a25..67d67b1a04d2b 100644 --- a/third_party/txt/src/skia/paragraph_skia.cc +++ b/third_party/txt/src/skia/paragraph_skia.cc @@ -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, diff --git a/tools/gn b/tools/gn index 5b481d6f33129..196219d35d611 100755 --- a/tools/gn +++ b/tools/gn @@ -672,6 +672,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 +1226,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..1e79b9e0e5b67 100644 --- a/tools/licenses/lib/paths.dart +++ b/tools/licenses/lib/paths.dart @@ -29,6 +29,8 @@ 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/ninja', // build system r'flutter/third_party/test_shaders', // for tests only @@ -98,8 +100,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 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..345919b83d8c1 100644 --- a/vulkan/vulkan_window.cc +++ b/vulkan/vulkan_window.cc @@ -118,6 +118,7 @@ GrDirectContext* VulkanWindow::GetSkiaGrContext() { } bool VulkanWindow::CreateSkiaGrContext() { +#ifdef SK_VUKLAN GrVkBackendContext backend_context; if (!CreateSkiaBackendContext(&backend_context)) { @@ -138,6 +139,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/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).