From 880280a14b65963ea4b80c84fbd3ce455cd9688b Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 7 May 2024 14:00:54 -0700 Subject: [PATCH 01/22] Move setting the logging level into the `Logger` constructor, refactor. (#52624) Based on @zanderso's feedback here: https://github.com/flutter/engine/pull/52619#discussion_r1592868979. I think this is the most succinct way to setup logging, it also doesn't seem to make sense to allow the level to be configured at runtime, so boom. --- tools/engine_tool/lib/main.dart | 8 +++++++- .../lib/src/commands/command_runner.dart | 4 ---- tools/engine_tool/lib/src/logger.dart | 17 ++++++++--------- tools/engine_tool/test/logger_test.dart | 12 ++++-------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/tools/engine_tool/lib/main.dart b/tools/engine_tool/lib/main.dart index 9af1bc47b88c5..e524e07ad0ed5 100644 --- a/tools/engine_tool/lib/main.dart +++ b/tools/engine_tool/lib/main.dart @@ -60,12 +60,18 @@ void main(List args) async { io.exitCode = 1; } + final Logger logger; + if (verbose) { + logger = Logger(level: Logger.infoLevel); + } else { + logger = Logger(); + } final Environment environment = Environment( abi: ffi.Abi.current(), engine: engine, platform: const LocalPlatform(), processRunner: ProcessRunner(), - logger: Logger(), + logger: logger, verbose: verbose, ); diff --git a/tools/engine_tool/lib/src/commands/command_runner.dart b/tools/engine_tool/lib/src/commands/command_runner.dart index 4909dd8ca2d7e..3be4ad84fc8a1 100644 --- a/tools/engine_tool/lib/src/commands/command_runner.dart +++ b/tools/engine_tool/lib/src/commands/command_runner.dart @@ -6,7 +6,6 @@ import 'package:args/command_runner.dart'; import 'package:engine_build_configs/engine_build_configs.dart'; import '../environment.dart'; -import '../logger.dart'; import 'build_command.dart'; import 'fetch_command.dart'; import 'flags.dart'; @@ -94,9 +93,6 @@ final class ToolCommandRunner extends CommandRunner { @override Future run(Iterable args) async { - if (environment.verbose) { - environment.logger.level = Logger.infoLevel; - } try { return await runCommand(parse(args)) ?? 0; } on FormatException catch (e) { diff --git a/tools/engine_tool/lib/src/logger.dart b/tools/engine_tool/lib/src/logger.dart index 96c5fc0d5765e..1e14491856c99 100644 --- a/tools/engine_tool/lib/src/logger.dart +++ b/tools/engine_tool/lib/src/logger.dart @@ -26,10 +26,12 @@ import 'package:meta/meta.dart'; /// which can be inspected by unit tetss. class Logger { /// Constructs a logger for use in the tool. - Logger() + Logger({ + log.Level level = statusLevel, + }) : _logger = log.Logger.detached('et'), _test = false { - _logger.level = statusLevel; + _logger.level = level; _logger.onRecord.listen(_handler); _setupIoSink(io.stderr); _setupIoSink(io.stdout); @@ -37,10 +39,12 @@ class Logger { /// A logger for tests. @visibleForTesting - Logger.test() + Logger.test({ + log.Level level = statusLevel, + }) : _logger = log.Logger.detached('et'), _test = true { - _logger.level = statusLevel; + _logger.level = level; _logger.onRecord.listen((log.LogRecord r) => _testLogs.add(r)); } @@ -105,11 +109,6 @@ class Logger { /// Get the current logging level. log.Level get level => _logger.level; - /// Set the current logging level. - set level(log.Level l) { - _logger.level = l; - } - /// Record a log message level [Logger.error] and throw a FatalError. /// This should only be called when the program has entered an impossible /// to recover from state or when something isn't implemented yet. diff --git a/tools/engine_tool/test/logger_test.dart b/tools/engine_tool/test/logger_test.dart index d28ad14aa20a3..bf7fd715e070b 100644 --- a/tools/engine_tool/test/logger_test.dart +++ b/tools/engine_tool/test/logger_test.dart @@ -12,8 +12,7 @@ void main() { } test('Setting the level works', () { - final Logger logger = Logger.test(); - logger.level = Logger.infoLevel; + final Logger logger = Logger.test(level: Logger.infoLevel); expect(logger.level, equals(Logger.infoLevel)); }); @@ -42,8 +41,7 @@ void main() { }); test('info messages are recorded at the infoLevel log level', () { - final Logger logger = Logger.test(); - logger.level = Logger.infoLevel; + final Logger logger = Logger.test(level: Logger.infoLevel); logger.info('info'); expect(stringsFromLogs(logger.testLogs), equals(['info\n'])); }); @@ -73,15 +71,13 @@ void main() { }); test('newlines in info() can be disabled', () { - final Logger logger = Logger.test(); - logger.level = Logger.infoLevel; + final Logger logger = Logger.test(level: Logger.infoLevel); logger.info('info', newline: false); expect(stringsFromLogs(logger.testLogs), equals(['info'])); }); test('fatal throws exception', () { - final Logger logger = Logger.test(); - logger.level = Logger.infoLevel; + final Logger logger = Logger.test(level: Logger.infoLevel); bool caught = false; try { logger.fatal('test', newline: false); From d8dd731f91ef5fc2fb97e0b47acb8f4ebdad4fdc Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Tue, 7 May 2024 15:05:05 -0700 Subject: [PATCH 02/22] Fix links in rbe.md (#52635) --- docs/rbe/rbe.md | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/docs/rbe/rbe.md b/docs/rbe/rbe.md index 842ffbaf0085d..8ad8161e21902 100644 --- a/docs/rbe/rbe.md +++ b/docs/rbe/rbe.md @@ -55,8 +55,7 @@ The `gcloud` tool in this SDK must be on your path. The tool lives under `//flutter/buildtools/mac-arm64/gcloud/bin`, which is the path to add to your `PATH` environment variable. Alternatively, you can get the gcloud SDK on your path by installing it on your system by following the instructions at -[https://cloud.google.com/sdk/docs/install] -(https://cloud.google.com/sdk/docs/install). +[https://cloud.google.com/sdk/docs/install](https://cloud.google.com/sdk/docs/install). On macOS, before running the `gcloud` command ensure that `python3` is on your path, and does not come from e.g. homebrew. The command `which python3` should @@ -190,25 +189,23 @@ This can be debugged by doing a local build with RBE turned off. ## References -* Code for RBE (i.e. reproxy, rewrapper, bootstrap, etc.) lives in [this GitHub - repository](https://github.com/bazelbuild/reclient). The tools are not +* Code for RBE (i.e. reproxy, rewrapper, bootstrap, etc.) lives in + [this GitHub repository](https://github.com/bazelbuild/reclient). The tools are not well-documented, so the source code is the source of truth for the command line flags that they accept, for example. * Internal-facing RBE migration guide is [here](go/reclient-migration-guide). (Mostly focused on Chrome and Android, so not all parts are relevant to Flutter.) -* The version of RBE for local development is set in the DEPS file [here] - (https://github.com/flutter/engine/blob/8578edf9c9393471ca9eab18e9154f0e6066dcb6/DEPS#L53). +* The version of RBE for local development is set in the DEPS file + [here](https://github.com/flutter/engine/blob/8578edf9c9393471ca9eab18e9154f0e6066dcb6/DEPS#L53). It needs to be manually rolled occasionally. -* The version of RBE used by CI is set in a LUCI recipe [here] - (https://flutter.googlesource.com/recipes/+/be12675150183af68223f5fbc6e0f888a1139e79/recipe_modules/rbe/api.py#16). +* The version of RBE used by CI is set in a LUCI recipe + [here](https://flutter.googlesource.com/recipes/+/be12675150183af68223f5fbc6e0f888a1139e79/recipe_modules/rbe/api.py#16). It also needs to be manually rolled occasionally. -* Googler-only RBE configuration files live in the CIPD bucket [here] - (https://chrome-infra-packages.appspot.com/p/flutter_internal/rbe/reclient_cfgs). +* Googler-only RBE configuration files live in the CIPD bucket + [here](https://chrome-infra-packages.appspot.com/p/flutter_internal/rbe/reclient_cfgs). They need to be updated when we roll clang to a new version as described - [here] - (https://github.com/flutter/engine/pull/52062#issuecomment-2050902282). -* Flutter’s RBE worker pool is defined [here] - (https://source.corp.google.com/piper///depot/google3/configs/cloud/gong/services/flutter_rbe/modules/infra/prod/main.tf). -* Using RBE for Engine clang-tidy is blocked on [b/326591374] - (http://b/326591374). + [here](https://github.com/flutter/engine/pull/52062#issuecomment-2050902282). +* Flutter’s RBE worker pool is defined + [here](https://source.corp.google.com/piper///depot/google3/configs/cloud/gong/services/flutter_rbe/modules/infra/prod/main.tf). +* Using RBE for Engine clang-tidy is blocked on [b/326591374](http://b/326591374). From b8fe65ca974df824d0f7d7eaee6902690e84a78a Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 7 May 2024 15:56:17 -0700 Subject: [PATCH 03/22] Move `Logger.test` to an injected log output versus leaky abstraction. (#52639) Previously, `Logger.test` was a leaky abstraction that stored a `@visibleForTesting` variable that was sometimes unused. It saved a few lines of code for tests, but doesn't seem necessary, so I swapped it out for a callback. --- tools/engine_tool/lib/src/logger.dart | 16 +++-- .../engine_tool/test/fetch_command_test.dart | 4 +- .../engine_tool/test/format_command_test.dart | 31 +++++----- tools/engine_tool/test/lint_command_test.dart | 2 +- tools/engine_tool/test/logger_test.dart | 59 +++++++++++-------- tools/engine_tool/test/proc_utils_test.dart | 4 +- .../engine_tool/test/query_command_test.dart | 10 ++-- tools/engine_tool/test/run_command_test.dart | 10 ++-- tools/engine_tool/test/utils.dart | 6 +- tools/engine_tool/test/worker_pool_test.dart | 4 +- 10 files changed, 81 insertions(+), 65 deletions(-) diff --git a/tools/engine_tool/lib/src/logger.dart b/tools/engine_tool/lib/src/logger.dart index 1e14491856c99..28bd847885a67 100644 --- a/tools/engine_tool/lib/src/logger.dart +++ b/tools/engine_tool/lib/src/logger.dart @@ -8,6 +8,9 @@ import 'dart:io' as io show IOSink, stderr, stdout; import 'package:logging/logging.dart' as log; import 'package:meta/meta.dart'; +// Part of the public API, so it's nicer to provide the symbols directly. +export 'package:logging/logging.dart' show LogRecord; + // This is where a flutter_tool style progress spinner, color output, // ascii art, terminal control for clearing lines or the whole screen, etc. // can go. We can just add more methods to Logger using the flutter_tool's @@ -37,15 +40,16 @@ class Logger { _setupIoSink(io.stdout); } - /// A logger for tests. + /// Constructs a logger that invokes a [callback] for each log message. @visibleForTesting - Logger.test({ + Logger.test( + void Function(log.LogRecord) onLog, { log.Level level = statusLevel, }) : _logger = log.Logger.detached('et'), _test = true { _logger.level = level; - _logger.onRecord.listen((log.LogRecord r) => _testLogs.add(r)); + _logger.onRecord.listen(onLog); } /// The logging level for error messages. These go to stderr. @@ -101,7 +105,6 @@ class Logger { } final log.Logger _logger; - final List _testLogs = []; final bool _test; Spinner? _status; @@ -259,11 +262,6 @@ class Logger { s = s.replaceRange(leftEnd, rightStart, '...'); return s + maybeNewline; } - - /// In a [Logger] constructed by [Logger.test], this list will contain all of - /// the [LogRecord]s emitted by the test. - @visibleForTesting - List get testLogs => _testLogs; } /// A base class for progress spinners, and a no-op implementation that prints diff --git a/tools/engine_tool/test/fetch_command_test.dart b/tools/engine_tool/test/fetch_command_test.dart index 41abe4d4f8827..2488f448a9d05 100644 --- a/tools/engine_tool/test/fetch_command_test.dart +++ b/tools/engine_tool/test/fetch_command_test.dart @@ -51,7 +51,7 @@ void main() { } test('fetch command invokes gclient sync -D', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, List> runHistory) = linuxEnv(logger); final ToolCommandRunner runner = ToolCommandRunner( environment: env, @@ -67,7 +67,7 @@ void main() { }); test('fetch command has sync alias', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, List> runHistory) = linuxEnv(logger); final ToolCommandRunner runner = ToolCommandRunner( environment: env, diff --git a/tools/engine_tool/test/format_command_test.dart b/tools/engine_tool/test/format_command_test.dart index ebce685cd6fe7..1b4e7c80e24c5 100644 --- a/tools/engine_tool/test/format_command_test.dart +++ b/tools/engine_tool/test/format_command_test.dart @@ -48,7 +48,7 @@ void main() { } test('--fix is passed to ci/bin/format.dart by default', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_){ }); final FakeProcessManager manager = _formatProcessManager( expectedFlags: ['--fix'], ); @@ -64,7 +64,7 @@ void main() { }); test('--fix is not passed to ci/bin/format.dart with --dry-run', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final FakeProcessManager manager = _formatProcessManager( expectedFlags: [], ); @@ -82,7 +82,7 @@ void main() { test('exit code is non-zero when ci/bin/format.dart exit code was non zero', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final FakeProcessManager manager = _formatProcessManager( expectedFlags: ['--fix'], exitCode: 1, @@ -99,7 +99,7 @@ void main() { }); test('--all-files is passed to ci/bin/format.dart correctly', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_){}); final FakeProcessManager manager = _formatProcessManager( expectedFlags: ['--fix', '--all-files'], ); @@ -116,7 +116,7 @@ void main() { }); test('--verbose is passed to ci/bin/format.dart correctly', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_){}); final FakeProcessManager manager = _formatProcessManager( expectedFlags: ['--fix', '--verbose'], ); @@ -133,7 +133,8 @@ void main() { }); test('--quiet suppresses non-error output', () async { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); final FakeProcessManager manager = _formatProcessManager( expectedFlags: ['--fix'], stdout: ['many', 'lines', 'of', 'output'].join('\n'), @@ -149,11 +150,12 @@ void main() { '--$quietFlag', ]); expect(result, equals(0)); - expect(stringsFromLogs(logger.testLogs), equals(['error\n'])); + expect(stringsFromLogs(testLogs), equals(['error\n'])); }); test('Diffs are suppressed by default', () async { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); final FakeProcessManager manager = _formatProcessManager( expectedFlags: ['--fix'], stdout: [ @@ -174,11 +176,12 @@ void main() { 'format', ]); expect(result, equals(0)); - expect(stringsFromLogs(logger.testLogs), isEmpty); + expect(stringsFromLogs(testLogs), isEmpty); }); test('--dry-run disables --fix and prints diffs', () async { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); final FakeProcessManager manager = _formatProcessManager( expectedFlags: [], stdout: [ @@ -201,7 +204,7 @@ void main() { ]); expect(result, equals(0)); expect( - stringsFromLogs(logger.testLogs), + stringsFromLogs(testLogs), equals([ 'To fix, run `et format` or:\n', 'many\n', @@ -213,7 +216,8 @@ void main() { }); test('progress lines are followed by a carriage return', () async { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); const String progressLine = 'diff Jobs: 46% done, 1528/3301 completed, ' '7 in progress, 1753 pending, 13 failed.'; final FakeProcessManager manager = _formatProcessManager( @@ -229,8 +233,7 @@ void main() { 'format', ]); expect(result, equals(0)); - expect( - stringsFromLogs(logger.testLogs), equals(['$progressLine\r'])); + expect(stringsFromLogs(testLogs), equals(['$progressLine\r'])); }); } diff --git a/tools/engine_tool/test/lint_command_test.dart b/tools/engine_tool/test/lint_command_test.dart index 46fe19e19d365..9f1a7835f5720 100644 --- a/tools/engine_tool/test/lint_command_test.dart +++ b/tools/engine_tool/test/lint_command_test.dart @@ -78,7 +78,7 @@ void main() { } test('invoked linters', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, List> runHistory) = macEnv(logger); final ToolCommandRunner runner = ToolCommandRunner( environment: env, diff --git a/tools/engine_tool/test/logger_test.dart b/tools/engine_tool/test/logger_test.dart index bf7fd715e070b..1deea42fec0e7 100644 --- a/tools/engine_tool/test/logger_test.dart +++ b/tools/engine_tool/test/logger_test.dart @@ -12,72 +12,83 @@ void main() { } test('Setting the level works', () { - final Logger logger = Logger.test(level: Logger.infoLevel); + final Logger logger = Logger.test((_) {}, level: Logger.infoLevel); expect(logger.level, equals(Logger.infoLevel)); }); test('error messages are recorded at the default log level', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.error('Error'); - expect(stringsFromLogs(logger.testLogs), equals(['Error\n'])); + expect(stringsFromLogs(testLogs), equals(['Error\n'])); }); test('warning messages are recorded at the default log level', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.warning('Warning'); - expect(stringsFromLogs(logger.testLogs), equals(['Warning\n'])); + expect(stringsFromLogs(testLogs), equals(['Warning\n'])); }); test('status messages are recorded at the default log level', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.status('Status'); - expect(stringsFromLogs(logger.testLogs), equals(['Status\n'])); + expect(stringsFromLogs(testLogs), equals(['Status\n'])); }); test('info messages are not recorded at the default log level', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.info('info'); - expect(stringsFromLogs(logger.testLogs), equals([])); + expect(stringsFromLogs(testLogs), equals([])); }); test('info messages are recorded at the infoLevel log level', () { - final Logger logger = Logger.test(level: Logger.infoLevel); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add, level: Logger.infoLevel); logger.info('info'); - expect(stringsFromLogs(logger.testLogs), equals(['info\n'])); + expect(stringsFromLogs(testLogs), equals(['info\n'])); }); test('indent indents the message', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.status('Status', indent: 1); - expect(stringsFromLogs(logger.testLogs), equals([' Status\n'])); + expect(stringsFromLogs(testLogs), equals([' Status\n'])); }); test('newlines in error() can be disabled', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.error('Error', newline: false); - expect(stringsFromLogs(logger.testLogs), equals(['Error'])); + expect(stringsFromLogs(testLogs), equals(['Error'])); }); test('newlines in warning() can be disabled', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.warning('Warning', newline: false); - expect(stringsFromLogs(logger.testLogs), equals(['Warning'])); + expect(stringsFromLogs(testLogs), equals(['Warning'])); }); test('newlines in status() can be disabled', () { - final Logger logger = Logger.test(); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add); logger.status('Status', newline: false); - expect(stringsFromLogs(logger.testLogs), equals(['Status'])); + expect(stringsFromLogs(testLogs), equals(['Status'])); }); test('newlines in info() can be disabled', () { - final Logger logger = Logger.test(level: Logger.infoLevel); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add, level: Logger.infoLevel); logger.info('info', newline: false); - expect(stringsFromLogs(logger.testLogs), equals(['info'])); + expect(stringsFromLogs(testLogs), equals(['info'])); }); test('fatal throws exception', () { - final Logger logger = Logger.test(level: Logger.infoLevel); + final List testLogs = []; + final Logger logger = Logger.test(testLogs.add, level: Logger.infoLevel); bool caught = false; try { logger.fatal('test', newline: false); @@ -85,7 +96,7 @@ void main() { caught = true; } expect(caught, equals(true)); - expect(stringsFromLogs(logger.testLogs), equals(['test'])); + expect(stringsFromLogs(testLogs), equals(['test'])); }); test('fitToWidth', () { @@ -123,7 +134,7 @@ void main() { }); test('Spinner calls onFinish callback', () { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); bool called = false; final Spinner spinner = logger.startSpinner( onFinish: () { diff --git a/tools/engine_tool/test/proc_utils_test.dart b/tools/engine_tool/test/proc_utils_test.dart index e1027b053e06b..8f32dacf290e8 100644 --- a/tools/engine_tool/test/proc_utils_test.dart +++ b/tools/engine_tool/test/proc_utils_test.dart @@ -58,7 +58,7 @@ void main() { } test('process queue success', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = macEnv(logger); final WorkerPool wp = WorkerPool(env, NoopWorkerPoolProgressReporter()); final ProcessTask task = @@ -72,7 +72,7 @@ void main() { }); test('process queue failure', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = macEnv(logger); final WorkerPool wp = WorkerPool(env, NoopWorkerPoolProgressReporter()); final ProcessTask task = diff --git a/tools/engine_tool/test/query_command_test.dart b/tools/engine_tool/test/query_command_test.dart index f9da60c6c8b0c..0cf59cd1c6a62 100644 --- a/tools/engine_tool/test/query_command_test.dart +++ b/tools/engine_tool/test/query_command_test.dart @@ -65,7 +65,7 @@ void main() { ]); expect(result, equals(0)); expect( - stringsFromLogs(env.logger.testLogs), + stringsFromLogs(testEnvironment.testLogs), equals([ 'Add --verbose to see detailed information about each builder\n', '\n', @@ -105,7 +105,7 @@ void main() { ]); expect(result, equals(0)); expect( - stringsFromLogs(env.logger.testLogs), + stringsFromLogs(testEnvironment.testLogs), equals([ 'Add --verbose to see detailed information about each builder\n', '\n', @@ -137,7 +137,7 @@ void main() { ]); expect(result, equals(0)); expect( - env.logger.testLogs.length, + testEnvironment.testLogs.length, equals(30), ); } finally { @@ -161,10 +161,10 @@ void main() { ]); expect(result, equals(0)); expect( - env.logger.testLogs.length, + testEnvironment.testLogs.length, equals(4), ); - expect(env.logger.testLogs[1].message, + expect(testEnvironment.testLogs[1].message, startsWith('//flutter/display_list:display_list_unittests')); } finally { testEnvironment.cleanup(); diff --git a/tools/engine_tool/test/run_command_test.dart b/tools/engine_tool/test/run_command_test.dart index 0e6de3dfa8354..95ceee41f236e 100644 --- a/tools/engine_tool/test/run_command_test.dart +++ b/tools/engine_tool/test/run_command_test.dart @@ -86,7 +86,7 @@ void main() { } test('run command invokes flutter run', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, List> runHistory) = linuxEnv(logger); final ToolCommandRunner runner = ToolCommandRunner( environment: env, @@ -101,7 +101,7 @@ void main() { }); test('parse devices list', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = linuxEnv(logger); final List targets = parseDevices(env, fixtures.attachedDevices()); @@ -112,7 +112,7 @@ void main() { }); test('default device', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = linuxEnv(logger); final List targets = parseDevices(env, fixtures.attachedDevices()); @@ -125,7 +125,7 @@ void main() { }); test('device select', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = linuxEnv(logger); RunTarget target = selectRunTarget(env, fixtures.attachedDevices())!; expect(target.name, contains('gphone64')); @@ -134,7 +134,7 @@ void main() { }); test('flutter run device select', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, List> runHistory) = linuxEnv(logger); final ToolCommandRunner runner = ToolCommandRunner( environment: env, diff --git a/tools/engine_tool/test/utils.dart b/tools/engine_tool/test/utils.dart index 6e2e53226e202..b00e009d546a0 100644 --- a/tools/engine_tool/test/utils.dart +++ b/tools/engine_tool/test/utils.dart @@ -59,7 +59,8 @@ class TestEnvironment { bool verbose = false, this.cannedProcesses = const [], }) { - logger ??= Logger.test(); + testLogs = []; + logger ??= Logger.test(testLogs.add); environment = Environment( abi: abi, engine: engine, @@ -137,6 +138,9 @@ class TestEnvironment { /// A history of all executed processes. final List processHistory = []; + + /// Test log output. + late final List testLogs; } String _operatingSystemForAbi(ffi.Abi abi) { diff --git a/tools/engine_tool/test/worker_pool_test.dart b/tools/engine_tool/test/worker_pool_test.dart index 3b6b9aa568bf6..19752836fb976 100644 --- a/tools/engine_tool/test/worker_pool_test.dart +++ b/tools/engine_tool/test/worker_pool_test.dart @@ -100,7 +100,7 @@ void main() { } test('worker pool success', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = macEnv(logger); final TestWorkerPoolProgressReporter reporter = TestWorkerPoolProgressReporter(); @@ -114,7 +114,7 @@ void main() { }); test('worker pool failure', () async { - final Logger logger = Logger.test(); + final Logger logger = Logger.test((_) {}); final (Environment env, _) = macEnv(logger); final TestWorkerPoolProgressReporter reporter = TestWorkerPoolProgressReporter(); From 1c52274e1118d2dd6bb60648b625d77242c68da5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 7 May 2024 16:15:04 -0700 Subject: [PATCH 04/22] [Impeller] Disable aging in the AHB swapchains and use FILO. (#52637) The aging was causing elevated 9x percentile times after a long duration where no frames were rendered. The FILO queue behavior gives the fences the most time possible to signal and cause fewer GPU stalls. There are already no CPU stalls. --- .../swapchain/ahb/ahb_texture_pool_vk.cc | 27 +++++++++---------- .../swapchain/ahb/ahb_texture_pool_vk.h | 14 +++------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc index 24199c275fee3..31011109dffd0 100644 --- a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc @@ -10,12 +10,8 @@ namespace impeller { AHBTexturePoolVK::AHBTexturePoolVK(std::weak_ptr context, android::HardwareBufferDescriptor desc, - size_t max_entries, - std::chrono::milliseconds max_extry_age) - : context_(std::move(context)), - desc_(desc), - max_entries_(max_entries), - max_extry_age_(max_extry_age) { + size_t max_entries) + : context_(std::move(context)), desc_(desc), max_entries_(max_entries) { if (!desc_.IsAllocatable()) { VALIDATION_LOG << "Swapchain image is not allocatable."; return; @@ -29,8 +25,10 @@ AHBTexturePoolVK::PoolEntry AHBTexturePoolVK::Pop() { { Lock lock(pool_mutex_); if (!pool_.empty()) { - auto entry = pool_.back(); - pool_.pop_back(); + // Buffers are pushed to the back of the queue. To give the ready fences + // the most time to signal, pick a buffer from the front of the queue. + auto entry = pool_.front(); + pool_.pop_front(); return entry; } } @@ -80,13 +78,12 @@ void AHBTexturePoolVK::PerformGC() { } void AHBTexturePoolVK::PerformGCLocked() { - // Push-Pop operations happen at the back of the deque so the front ages as - // much as possible. So that's where we collect entries. - auto now = Clock::now(); - while (!pool_.empty() && - (pool_.size() > max_entries_ || - now - pool_.front().last_access_time > max_extry_age_)) { - pool_.pop_front(); + while (!pool_.empty() && (pool_.size() > max_entries_)) { + // Buffers are pushed to the back of the queue and popped from the front. + // The ones at the back should be given the most time for their fences to + // signal. If we are going to get rid of textures, they might as well be the + // newest ones since their fences will take the longest to signal. + pool_.pop_back(); } } diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h index 9cc3bebc91f40..242aaeb9f8c92 100644 --- a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h @@ -19,8 +19,7 @@ namespace impeller { /// sources that can be used as swapchain images. /// /// The number of cached entries in the texture pool is capped to a -/// caller specified value. Within this cap, no entry may be older -/// than the caller specified duration. +/// caller specified value. /// /// If a previously cached entry cannot be obtained from the pool, a /// new entry is created. The only case where a valid texture source @@ -55,14 +54,10 @@ class AHBTexturePoolVK { /// the texture sources. /// @param[in] max_entries The maximum entries that will remain cached /// in the pool. - /// @param[in] max_extry_age The maximum duration an entry will remain - /// cached in the pool. /// - explicit AHBTexturePoolVK( - std::weak_ptr context, - android::HardwareBufferDescriptor desc, - size_t max_entries = 2u, - std::chrono::milliseconds max_extry_age = std::chrono::seconds{1}); + explicit AHBTexturePoolVK(std::weak_ptr context, + android::HardwareBufferDescriptor desc, + size_t max_entries = 2u); ~AHBTexturePoolVK(); @@ -117,7 +112,6 @@ class AHBTexturePoolVK { const std::weak_ptr context_; const android::HardwareBufferDescriptor desc_; const size_t max_entries_; - const std::chrono::milliseconds max_extry_age_; bool is_valid_ = false; Mutex pool_mutex_; std::deque pool_ IPLR_GUARDED_BY(pool_mutex_); From 8b0ab9366715728877a1797f0696523b147cd68f Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 7 May 2024 17:02:22 -0700 Subject: [PATCH 05/22] [Impeller] Support copy from buffer to texture for GLES blitpass, use blit pass to set contents in glyph atlas. (#52510) part of https://github.com/flutter/flutter/issues/138798 Works around https://github.com/flutter/flutter/issues/144498 for the glyph atlas. Adds BlitPass::AddCopy implementation for GLES, which is mostly a copy of Texture::SetContents. Updates the glyph atlas to use a blit pass and device private textures instead of set contents (which is unsafely on Metal). This also removes DeviceBuffer::AsTexture, which isn't actually used anywhere - and creates a linear texture on iOS (and fails with an unsupported API on simulators). Note that in general, we don't actually have support for hostVisible textures on GLES _or_ Vulkan. Instead, VMA is falling back to device private textures. We may want to, separately, remove the concept of host visible from textures - and similarly remove the concept of transient from buffers. --- ci/licenses_golden/excluded_files | 1 - impeller/aiks/aiks_unittests.cc | 10 +- impeller/core/device_buffer.cc | 15 - impeller/core/device_buffer.h | 6 - impeller/core/texture.h | 6 + impeller/entity/BUILD.gn | 1 - impeller/entity/contents/content_context.cc | 12 +- .../contents/content_context_unittests.cc | 356 ------------------ .../contents/filters/blend_filter_contents.cc | 40 +- .../entity/contents/gradient_generator.cc | 22 +- impeller/entity/entity_unittests.cc | 21 +- impeller/playground/playground.cc | 55 +-- .../backend/gles/blit_command_gles.cc | 164 ++++++++ .../renderer/backend/gles/blit_command_gles.h | 10 + .../renderer/backend/gles/blit_pass_gles.cc | 25 +- .../renderer/backend/gles/blit_pass_gles.h | 7 +- .../backend/gles/capabilities_gles.cc | 4 - .../renderer/backend/gles/capabilities_gles.h | 3 - .../gles/test/capabilities_unittests.cc | 1 - .../renderer/backend/gles/texture_gles.cc | 153 ++++---- impeller/renderer/backend/gles/texture_gles.h | 4 +- .../backend/metal/blit_command_mtl.mm | 2 +- .../renderer/backend/metal/blit_pass_mtl.h | 3 +- .../renderer/backend/metal/blit_pass_mtl.mm | 7 +- .../renderer/backend/metal/context_mtl.mm | 1 - .../backend/metal/device_buffer_mtl.h | 5 - .../backend/metal/device_buffer_mtl.mm | 24 -- .../backend/vulkan/blit_command_vk.cc | 16 + .../renderer/backend/vulkan/blit_pass_vk.cc | 4 +- .../renderer/backend/vulkan/blit_pass_vk.h | 3 +- .../backend/vulkan/capabilities_vk.cc | 5 - .../renderer/backend/vulkan/capabilities_vk.h | 3 - impeller/renderer/blit_command.h | 1 + impeller/renderer/blit_pass.cc | 6 +- impeller/renderer/blit_pass.h | 8 +- impeller/renderer/capabilities.cc | 15 - impeller/renderer/capabilities.h | 8 - impeller/renderer/capabilities_unittests.cc | 1 - impeller/renderer/renderer_unittests.cc | 37 -- impeller/renderer/testing/mocks.h | 4 +- .../backends/skia/typographer_context_skia.cc | 74 +++- .../backends/stb/typographer_context_stb.cc | 48 ++- impeller/typographer/typographer_unittests.cc | 5 +- lib/ui/painting/image_decoder_impeller.cc | 16 +- .../painting/image_decoder_no_gl_unittests.h | 6 - 45 files changed, 528 insertions(+), 690 deletions(-) delete mode 100644 impeller/entity/contents/content_context_unittests.cc diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index f4e63b1bd063d..d2318510b9c05 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -144,7 +144,6 @@ ../../../flutter/impeller/display_list/skia_conversions_unittests.cc ../../../flutter/impeller/docs ../../../flutter/impeller/entity/contents/clip_contents_unittests.cc -../../../flutter/impeller/entity/contents/content_context_unittests.cc ../../../flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc ../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc ../../../flutter/impeller/entity/contents/host_buffer_unittests.cc diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index b4cd00fa9d716..bdc8533b04f8f 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -4,7 +4,6 @@ #include "flutter/impeller/aiks/aiks_unittests.h" -#include #include #include #include @@ -22,7 +21,6 @@ #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/aiks/testing/context_spy.h" #include "impeller/entity/contents/solid_color_contents.h" -#include "impeller/entity/render_target_cache.h" #include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" #include "impeller/geometry/geometry_asserts.h" @@ -35,7 +33,6 @@ #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/snapshot.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" -#include "impeller/typographer/backends/skia/typographer_context_skia.h" #include "impeller/typographer/backends/stb/text_frame_stb.h" #include "impeller/typographer/backends/stb/typeface_stb.h" #include "impeller/typographer/backends/stb/typographer_context_stb.h" @@ -3061,12 +3058,13 @@ TEST_P(AiksTest, MipmapGenerationWorksCorrectly) { auto texture = GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor); - ASSERT_TRUE(!!texture); - ASSERT_TRUE(texture->SetContents(mapping)); - + auto device_buffer = + GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping); auto command_buffer = GetContext()->CreateCommandBuffer(); auto blit_pass = command_buffer->CreateBlitPass(); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)), + texture); blit_pass->GenerateMipmap(texture); EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator())); EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok()); diff --git a/impeller/core/device_buffer.cc b/impeller/core/device_buffer.cc index 3eae7e0a5f5b0..1df3c03b36446 100644 --- a/impeller/core/device_buffer.cc +++ b/impeller/core/device_buffer.cc @@ -22,21 +22,6 @@ BufferView DeviceBuffer::AsBufferView(std::shared_ptr buffer) { return view; } -std::shared_ptr DeviceBuffer::AsTexture( - Allocator& allocator, - const TextureDescriptor& descriptor, - uint16_t row_bytes) const { - auto texture = allocator.CreateTexture(descriptor); - if (!texture) { - return nullptr; - } - if (!texture->SetContents(std::make_shared( - OnGetContents(), desc_.size))) { - return nullptr; - } - return texture; -} - const DeviceBufferDescriptor& DeviceBuffer::GetDeviceBufferDescriptor() const { return desc_; } diff --git a/impeller/core/device_buffer.h b/impeller/core/device_buffer.h index 0306fad46e595..07dba77ccfe6e 100644 --- a/impeller/core/device_buffer.h +++ b/impeller/core/device_buffer.h @@ -12,7 +12,6 @@ #include "impeller/core/buffer_view.h" #include "impeller/core/device_buffer_descriptor.h" #include "impeller/core/range.h" -#include "impeller/core/texture.h" namespace impeller { @@ -31,11 +30,6 @@ class DeviceBuffer { /// @brief Create a buffer view of this entire buffer. static BufferView AsBufferView(std::shared_ptr buffer); - virtual std::shared_ptr AsTexture( - Allocator& allocator, - const TextureDescriptor& descriptor, - uint16_t row_bytes) const; - const DeviceBufferDescriptor& GetDeviceBufferDescriptor() const; virtual uint8_t* OnGetContents() const = 0; diff --git a/impeller/core/texture.h b/impeller/core/texture.h index af14a9e9331c9..3be29ae71ca14 100644 --- a/impeller/core/texture.h +++ b/impeller/core/texture.h @@ -20,11 +20,13 @@ class Texture { virtual void SetLabel(std::string_view label) = 0; + // Deprecated: use BlitPass::AddCopy instead. [[nodiscard]] bool SetContents(const uint8_t* contents, size_t length, size_t slice = 0, bool is_opaque = false); + // Deprecated: use BlitPass::AddCopy instead. [[nodiscard]] bool SetContents(std::shared_ptr mapping, size_t slice = 0, bool is_opaque = false); @@ -39,6 +41,10 @@ class Texture { const TextureDescriptor& GetTextureDescriptor() const; + /// Update the coordinate system used by the texture. + /// + /// The setting is used to conditionally invert the coordinates to + /// account for the different origin of GLES textures. void SetCoordinateSystem(TextureCoordinateSystem coordinate_system); TextureCoordinateSystem GetCoordinateSystem() const; diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 9bd3fb78dfd9b..c529d46684393 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -239,7 +239,6 @@ impeller_component("entity_unittests") { sources = [ "contents/clip_contents_unittests.cc", - "contents/content_context_unittests.cc", "contents/filters/gaussian_blur_filter_contents_unittests.cc", "contents/filters/inputs/filter_input_unittests.cc", "contents/host_buffer_unittests.cc", diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 428ef5bd04af8..fe3338fdaa791 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -263,7 +263,17 @@ ContentContext::ContentContext( desc.size = ISize{1, 1}; empty_texture_ = GetContext()->GetResourceAllocator()->CreateTexture(desc); auto data = Color::BlackTransparent().ToR8G8B8A8(); - if (!empty_texture_->SetContents(data.data(), 4)) { + auto cmd_buffer = GetContext()->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + auto& host_buffer = GetTransientsBuffer(); + auto buffer_view = host_buffer.Emplace(data); + blit_pass->AddCopy(buffer_view, empty_texture_); + + if (!blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) || + !GetContext() + ->GetCommandQueue() + ->Submit({std::move(cmd_buffer)}) + .ok()) { VALIDATION_LOG << "Failed to create empty texture."; } } diff --git a/impeller/entity/contents/content_context_unittests.cc b/impeller/entity/contents/content_context_unittests.cc deleted file mode 100644 index 1063b069bbcd4..0000000000000 --- a/impeller/entity/contents/content_context_unittests.cc +++ /dev/null @@ -1,356 +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 -#include -#include -#include -#include - -#include "fml/logging.h" -#include "gtest/gtest.h" - -#include "impeller/base/backend_cast.h" -#include "impeller/base/comparable.h" -#include "impeller/core/allocator.h" -#include "impeller/core/device_buffer_descriptor.h" -#include "impeller/core/formats.h" -#include "impeller/core/texture_descriptor.h" -#include "impeller/entity/contents/content_context.h" -#include "impeller/entity/contents/test/recording_render_pass.h" -#include "impeller/geometry/color.h" -#include "impeller/renderer/capabilities.h" -#include "impeller/renderer/command_buffer.h" -#include "impeller/renderer/command_queue.h" -#include "impeller/renderer/pipeline.h" -#include "impeller/renderer/pipeline_descriptor.h" -#include "impeller/renderer/pipeline_library.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/renderer/shader_function.h" -#include "impeller/renderer/shader_library.h" - -namespace impeller { -namespace testing { - -namespace { -class FakeTexture : public Texture { - public: - explicit FakeTexture(const TextureDescriptor& desc) : Texture(desc) {} - - ~FakeTexture() override {} - - void SetLabel(std::string_view label) {} - - bool IsValid() const override { return true; } - - ISize GetSize() const override { return {1, 1}; } - - Scalar GetYCoordScale() const override { return 1.0; } - - bool OnSetContents(const uint8_t* contents, - size_t length, - size_t slice) override { - if (GetTextureDescriptor().GetByteSizeOfBaseMipLevel() != length) { - return false; - } - did_set_contents = true; - return true; - } - - bool OnSetContents(std::shared_ptr mapping, - size_t slice) override { - did_set_contents = true; - return true; - } - - bool did_set_contents = false; -}; - -class FakeAllocator : public Allocator, - public BackendCast { - public: - FakeAllocator() : Allocator() {} - - uint16_t MinimumBytesPerRow(PixelFormat format) const override { return 0; } - ISize GetMaxTextureSizeSupported() const override { return ISize(1, 1); } - - std::shared_ptr OnCreateBuffer( - const DeviceBufferDescriptor& desc) override { - return nullptr; - } - std::shared_ptr OnCreateTexture( - const TextureDescriptor& desc) override { - if (desc.size == ISize{1, 1}) { - auto result = std::make_shared(desc); - textures.push_back(result); - return result; - } - return nullptr; - } - - std::vector> textures = {}; -}; - -class FakePipeline : public Pipeline { - public: - FakePipeline(std::weak_ptr library, - const PipelineDescriptor& desc) - : Pipeline(std::move(library), desc) {} - - ~FakePipeline() override {} - - bool IsValid() const override { return true; } -}; - -class FakeComputePipeline : public Pipeline { - public: - FakeComputePipeline(std::weak_ptr library, - const ComputePipelineDescriptor& desc) - : Pipeline(std::move(library), desc) {} - - ~FakeComputePipeline() override {} - - bool IsValid() const override { return true; } -}; - -class FakePipelineLibrary : public PipelineLibrary { - public: - FakePipelineLibrary() {} - - ~FakePipelineLibrary() override {} - - bool IsValid() const override { return true; } - - PipelineFuture GetPipeline(PipelineDescriptor descriptor, - bool async) override { - auto pipeline = - std::make_shared(weak_from_this(), descriptor); - std::promise>> promise; - promise.set_value(std::move(pipeline)); - return PipelineFuture{ - .descriptor = descriptor, - .future = - std::shared_future>>( - promise.get_future())}; - } - - PipelineFuture GetPipeline( - ComputePipelineDescriptor descriptor, - bool async) override { - auto pipeline = - std::make_shared(weak_from_this(), descriptor); - std::promise>> promise; - promise.set_value(std::move(pipeline)); - return PipelineFuture{ - .descriptor = descriptor, - .future = std::shared_future< - std::shared_ptr>>( - promise.get_future())}; - } - - void RemovePipelinesWithEntryPoint( - std::shared_ptr function) {} -}; - -class FakeShaderFunction : public ShaderFunction { - public: - FakeShaderFunction(UniqueID parent_library_id, - std::string name, - ShaderStage stage) - : ShaderFunction(parent_library_id, std::move(name), stage){}; - - ~FakeShaderFunction() override {} -}; - -class FakeShaderLibrary : public ShaderLibrary { - public: - ~FakeShaderLibrary() override {} - - bool IsValid() const override { return true; } - - std::shared_ptr GetFunction(std::string_view name, - ShaderStage stage) { - return std::make_shared(UniqueID{}, std::string(name), - stage); - } - - void RegisterFunction(std::string name, - ShaderStage stage, - std::shared_ptr code, - RegistrationCallback callback) override {} - - void UnregisterFunction(std::string name, ShaderStage stage) override {} -}; - -class FakeCommandBuffer : public CommandBuffer { - public: - explicit FakeCommandBuffer(std::weak_ptr context) - : CommandBuffer(std::move(context)) {} - - ~FakeCommandBuffer() {} - - bool IsValid() const override { return true; } - - void SetLabel(const std::string& label) const override {} - - std::shared_ptr OnCreateRenderPass( - RenderTarget render_target) override { - return std::make_shared(nullptr, context_.lock(), - render_target); - } - - std::shared_ptr OnCreateBlitPass() override { FML_UNREACHABLE() } - - virtual bool OnSubmitCommands(CompletionCallback callback) { return true; } - - void OnWaitUntilScheduled() {} - - std::shared_ptr OnCreateComputePass() override { - FML_UNREACHABLE(); - } -}; - -class FakeContext : public Context, - public std::enable_shared_from_this { - public: - explicit FakeContext( - const std::string& gpu_model = "", - PixelFormat default_color_format = PixelFormat::kR8G8B8A8UNormInt) - : Context(), - allocator_(std::make_shared()), - capabilities_(std::shared_ptr( - CapabilitiesBuilder() - .SetDefaultColorFormat(default_color_format) - .Build())), - pipelines_(std::make_shared()), - queue_(std::make_shared()), - shader_library_(std::make_shared()), - gpu_model_(gpu_model) {} - - BackendType GetBackendType() const override { return BackendType::kVulkan; } - std::string DescribeGpuModel() const override { return gpu_model_; } - bool IsValid() const override { return true; } - const std::shared_ptr& GetCapabilities() const override { - return capabilities_; - } - std::shared_ptr GetResourceAllocator() const override { - return allocator_; - } - std::shared_ptr GetShaderLibrary() const { - return shader_library_; - } - std::shared_ptr GetSamplerLibrary() const { return nullptr; } - std::shared_ptr GetPipelineLibrary() const { - return pipelines_; - } - std::shared_ptr GetCommandQueue() const { return queue_; } - std::shared_ptr CreateCommandBuffer() const { - return std::make_shared(shared_from_this()); - } - void Shutdown() {} - - private: - std::shared_ptr allocator_; - std::shared_ptr capabilities_; - std::shared_ptr pipelines_; - std::shared_ptr queue_; - std::shared_ptr shader_library_; - std::string gpu_model_; -}; -} // namespace - -TEST(ContentContext, CachesPipelines) { - auto context = std::make_shared(); - - auto create_callback = [&]() { - return std::make_shared(context->GetPipelineLibrary(), - PipelineDescriptor{}); - }; - - ContentContext content_context(context, nullptr); - ContentContextOptions optionsA{.blend_mode = BlendMode::kSourceOver}; - ContentContextOptions optionsB{.blend_mode = BlendMode::kSource}; - - auto pipelineA = content_context.GetCachedRuntimeEffectPipeline( - "A", optionsA, create_callback); - - auto pipelineA2 = content_context.GetCachedRuntimeEffectPipeline( - "A", optionsA, create_callback); - - auto pipelineA3 = content_context.GetCachedRuntimeEffectPipeline( - "A", optionsB, create_callback); - - auto pipelineB = content_context.GetCachedRuntimeEffectPipeline( - "B", optionsB, create_callback); - - ASSERT_EQ(pipelineA.get(), pipelineA2.get()); - ASSERT_NE(pipelineA.get(), pipelineA3.get()); - ASSERT_NE(pipelineB.get(), pipelineA.get()); -} - -TEST(ContentContext, InvalidatesAllPipelinesWithSameUniqueNameOnClear) { - auto context = std::make_shared(); - ContentContext content_context(context, nullptr); - ContentContextOptions optionsA{.blend_mode = BlendMode::kSourceOver}; - ContentContextOptions optionsB{.blend_mode = BlendMode::kSource}; - - auto create_callback = [&]() { - return std::make_shared(context->GetPipelineLibrary(), - PipelineDescriptor{}); - }; - - auto pipelineA = content_context.GetCachedRuntimeEffectPipeline( - "A", optionsA, create_callback); - - auto pipelineA2 = content_context.GetCachedRuntimeEffectPipeline( - "A", optionsB, create_callback); - - auto pipelineB = content_context.GetCachedRuntimeEffectPipeline( - "B", optionsB, create_callback); - - ASSERT_TRUE(pipelineA); - ASSERT_TRUE(pipelineA2); - ASSERT_TRUE(pipelineB); - - ASSERT_EQ(pipelineA, content_context.GetCachedRuntimeEffectPipeline( - "A", optionsA, create_callback)); - ASSERT_EQ(pipelineA2, content_context.GetCachedRuntimeEffectPipeline( - "A", optionsB, create_callback)); - ASSERT_EQ(pipelineB, content_context.GetCachedRuntimeEffectPipeline( - "B", optionsB, create_callback)); - - content_context.ClearCachedRuntimeEffectPipeline("A"); - - ASSERT_NE(pipelineA, content_context.GetCachedRuntimeEffectPipeline( - "A", optionsA, create_callback)); - ASSERT_NE(pipelineA2, content_context.GetCachedRuntimeEffectPipeline( - "A", optionsB, create_callback)); - ASSERT_EQ(pipelineB, content_context.GetCachedRuntimeEffectPipeline( - "B", optionsB, create_callback)); - - content_context.ClearCachedRuntimeEffectPipeline("B"); - - ASSERT_NE(pipelineB, content_context.GetCachedRuntimeEffectPipeline( - "B", optionsB, create_callback)); -} - -TEST(ContentContext, InitializeCommonlyUsedShadersIfNeeded) { - ScopedValidationFatal fatal_validations; - // Set a pixel format that is larger than 32bpp. - auto context = std::make_shared("Mali G70", - PixelFormat::kR16G16B16A16Float); - ContentContext content_context(context, nullptr); - - FakeAllocator& fake_allocator = - FakeAllocator::Cast(*context->GetResourceAllocator()); - -#if IMPELLER_ENABLE_3D - EXPECT_EQ(fake_allocator.textures.size(), 2u); -#else - EXPECT_EQ(fake_allocator.textures.size(), 1u); -#endif // IMPELLER_ENABLE_3D -} - -} // namespace testing -} // namespace impeller diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 7f3ac8e322af1..cced93ac3b015 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -684,6 +684,8 @@ std::optional BlendFilterContents::CreateFramebufferAdvancedBlend( return std::nullopt; } + std::shared_ptr foreground_texture; + ContentContext::SubpassCallback subpass_callback = [&](const ContentContext& renderer, RenderPass& pass) { @@ -736,20 +738,7 @@ std::optional BlendFilterContents::CreateFramebufferAdvancedBlend( // texture for the foreground color. std::shared_ptr src_texture; if (foreground_color.has_value()) { - TextureDescriptor desc; - desc.size = {1, 1}; - desc.format = PixelFormat::kR8G8B8A8UNormInt; - desc.storage_mode = StorageMode::kHostVisible; - src_texture = - renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc); - if (!src_texture) { - return false; - } - - if (!src_texture->SetContents( - foreground_color->Premultiply().ToR8G8B8A8().data(), 4u)) { - return false; - } + src_texture = foreground_texture; } else { auto src_snapshot = inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity); @@ -864,6 +853,29 @@ std::optional BlendFilterContents::CreateFramebufferAdvancedBlend( std::shared_ptr cmd_buffer = renderer.GetContext()->CreateCommandBuffer(); + + // Generate a 1x1 texture to implement foreground color blending. + if (foreground_color.has_value()) { + TextureDescriptor desc; + desc.size = {1, 1}; + desc.format = PixelFormat::kR8G8B8A8UNormInt; + desc.storage_mode = StorageMode::kDevicePrivate; + foreground_texture = + renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc); + if (!foreground_texture) { + return std::nullopt; + } + auto blit_pass = cmd_buffer->CreateBlitPass(); + auto buffer_view = renderer.GetTransientsBuffer().Emplace( + foreground_color->Premultiply().ToR8G8B8A8()); + + blit_pass->AddCopy(std::move(buffer_view), foreground_texture); + if (!blit_pass->EncodeCommands( + renderer.GetContext()->GetResourceAllocator())) { + return std::nullopt; + } + } + auto render_target = renderer.MakeSubpass("FramebufferBlend", dst_snapshot->texture->GetSize(), cmd_buffer, subpass_callback); diff --git a/impeller/entity/contents/gradient_generator.cc b/impeller/entity/contents/gradient_generator.cc index 6672fb394118b..2d6431ee8b87c 100644 --- a/impeller/entity/contents/gradient_generator.cc +++ b/impeller/entity/contents/gradient_generator.cc @@ -2,15 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include - #include "impeller/entity/contents/gradient_generator.h" #include "flutter/fml/logging.h" +#include "impeller/base/strings.h" +#include "impeller/core/device_buffer.h" +#include "impeller/core/formats.h" #include "impeller/core/texture.h" -#include "impeller/entity/contents/content_context.h" #include "impeller/renderer/context.h" -#include "impeller/renderer/render_pass.h" namespace impeller { @@ -33,11 +32,20 @@ std::shared_ptr CreateGradientTexture( return nullptr; } - auto mapping = std::make_shared(gradient_data.color_bytes); - if (!texture->SetContents(mapping)) { - FML_DLOG(ERROR) << "Could not copy contents into Impeller texture."; + auto data_mapping = + std::make_shared(gradient_data.color_bytes); + auto buffer = + context->GetResourceAllocator()->CreateBufferWithCopy(*data_mapping); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(buffer)), texture); + + if (!blit_pass->EncodeCommands(context->GetResourceAllocator()) || + !context->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok()) { return nullptr; } + texture->SetLabel(impeller::SPrintF("Gradient(%p)", texture.get()).c_str()); return texture; } diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 14b7e2d493948..3d739d0373b23 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -12,6 +12,7 @@ #include "flutter/display_list/testing/dl_test_snippets.h" #include "fml/logging.h" #include "gtest/gtest.h" +#include "impeller/core/device_buffer.h" #include "impeller/core/formats.h" #include "impeller/core/texture_descriptor.h" #include "impeller/entity/contents/atlas_contents.h" @@ -2070,6 +2071,9 @@ static std::vector> CreateTestYUVTextures( uv_data.push_back(j % 2 == 0 ? u : v); } } + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + impeller::TextureDescriptor y_texture_descriptor; y_texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; y_texture_descriptor.format = PixelFormat::kR8UNormInt; @@ -2077,9 +2081,10 @@ static std::vector> CreateTestYUVTextures( auto y_texture = context->GetResourceAllocator()->CreateTexture(y_texture_descriptor); auto y_mapping = std::make_shared(y_data); - if (!y_texture->SetContents(y_mapping)) { - FML_DLOG(ERROR) << "Could not copy contents into Y texture."; - } + auto y_mapping_buffer = + context->GetResourceAllocator()->CreateBufferWithCopy(*y_mapping); + + blit_pass->AddCopy(DeviceBuffer::AsBufferView(y_mapping_buffer), y_texture); impeller::TextureDescriptor uv_texture_descriptor; uv_texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; @@ -2088,8 +2093,14 @@ static std::vector> CreateTestYUVTextures( auto uv_texture = context->GetResourceAllocator()->CreateTexture(uv_texture_descriptor); auto uv_mapping = std::make_shared(uv_data); - if (!uv_texture->SetContents(uv_mapping)) { - FML_DLOG(ERROR) << "Could not copy contents into UV texture."; + auto uv_mapping_buffer = + context->GetResourceAllocator()->CreateBufferWithCopy(*uv_mapping); + + blit_pass->AddCopy(DeviceBuffer::AsBufferView(uv_mapping_buffer), uv_texture); + + if (!blit_pass->EncodeCommands(context->GetResourceAllocator()) || + !context->GetCommandQueue()->Submit({cmd_buffer}).ok()) { + FML_DLOG(ERROR) << "Could not copy contents into Y/UV texture."; } return {y_texture, uv_texture}; diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index bf899cf2749ed..9a173c56ec020 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -414,27 +414,26 @@ static std::shared_ptr CreateTextureForDecompressedImage( return nullptr; } - auto uploaded = texture->SetContents(decompressed_image.GetAllocation()); - if (!uploaded) { - VALIDATION_LOG << "Could not upload texture to device memory for fixture."; + auto command_buffer = context->CreateCommandBuffer(); + if (!command_buffer) { + FML_DLOG(ERROR) << "Could not create command buffer for mipmap generation."; return nullptr; } + command_buffer->SetLabel("Mipmap Command Buffer"); + + auto blit_pass = command_buffer->CreateBlitPass(); + auto buffer_view = DeviceBuffer::AsBufferView( + context->GetResourceAllocator()->CreateBufferWithCopy( + *decompressed_image.GetAllocation())); + blit_pass->AddCopy(buffer_view, texture); if (enable_mipmapping) { - auto command_buffer = context->CreateCommandBuffer(); - if (!command_buffer) { - FML_DLOG(ERROR) - << "Could not create command buffer for mipmap generation."; - return nullptr; - } - command_buffer->SetLabel("Mipmap Command Buffer"); - auto blit_pass = command_buffer->CreateBlitPass(); blit_pass->SetLabel("Mipmap Blit Pass"); blit_pass->GenerateMipmap(texture); - blit_pass->EncodeCommands(context->GetResourceAllocator()); - if (!context->GetCommandQueue()->Submit({command_buffer}).ok()) { - FML_DLOG(ERROR) << "Failed to submit blit pass command buffer."; - return nullptr; - } + } + blit_pass->EncodeCommands(context->GetResourceAllocator()); + if (!context->GetCommandQueue()->Submit({command_buffer}).ok()) { + FML_DLOG(ERROR) << "Failed to submit blit pass command buffer."; + return nullptr; } return texture; } @@ -492,14 +491,24 @@ std::shared_ptr Playground::CreateTextureCubeForFixture( } texture->SetLabel("Texture cube"); + auto cmd_buffer = renderer_->GetContext()->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); for (size_t i = 0; i < fixture_names.size(); i++) { - auto uploaded = - texture->SetContents(images[i].GetAllocation()->GetMapping(), - images[i].GetAllocation()->GetSize(), i); - if (!uploaded) { - VALIDATION_LOG << "Could not upload texture to device memory."; - return nullptr; - } + auto device_buffer = + renderer_->GetContext()->GetResourceAllocator()->CreateBufferWithCopy( + *images[i].GetAllocation()); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), texture, {}, + "", /*slice=*/i); + } + + if (!blit_pass->EncodeCommands( + renderer_->GetContext()->GetResourceAllocator()) || + !renderer_->GetContext() + ->GetCommandQueue() + ->Submit({std::move(cmd_buffer)}) + .ok()) { + VALIDATION_LOG << "Could not upload texture to device memory."; + return nullptr; } return texture; diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc index f30ce5446f5d8..93753abbdb18a 100644 --- a/impeller/renderer/backend/gles/blit_command_gles.cc +++ b/impeller/renderer/backend/gles/blit_command_gles.cc @@ -5,7 +5,9 @@ #include "impeller/renderer/backend/gles/blit_command_gles.h" #include "flutter/fml/closure.h" +#include "fml/trace_event.h" #include "impeller/base/validation.h" +#include "impeller/geometry/point.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/texture_gles.h" @@ -117,6 +119,168 @@ bool BlitCopyTextureToTextureCommandGLES::Encode( return true; }; +namespace { +struct TexImage2DData { + GLint internal_format = 0; + GLenum external_format = GL_NONE; + GLenum type = GL_NONE; + BufferView buffer_view; + + explicit TexImage2DData(PixelFormat pixel_format) { + switch (pixel_format) { + case PixelFormat::kA8UNormInt: + internal_format = GL_ALPHA; + external_format = GL_ALPHA; + type = GL_UNSIGNED_BYTE; + break; + case PixelFormat::kR8UNormInt: + internal_format = GL_RED; + external_format = GL_RED; + type = GL_UNSIGNED_BYTE; + break; + case PixelFormat::kR8G8B8A8UNormInt: + case PixelFormat::kB8G8R8A8UNormInt: + case PixelFormat::kR8G8B8A8UNormIntSRGB: + case PixelFormat::kB8G8R8A8UNormIntSRGB: + internal_format = GL_RGBA; + external_format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + case PixelFormat::kR32G32B32A32Float: + internal_format = GL_RGBA; + external_format = GL_RGBA; + type = GL_FLOAT; + break; + case PixelFormat::kR16G16B16A16Float: + internal_format = GL_RGBA; + external_format = GL_RGBA; + type = GL_HALF_FLOAT; + break; + case PixelFormat::kS8UInt: + // Pure stencil textures are only available in OpenGL 4.4+, which is + // ~0% of mobile devices. Instead, we use a depth-stencil texture and + // only use the stencil component. + // + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml + case PixelFormat::kD24UnormS8Uint: + internal_format = GL_DEPTH_STENCIL; + external_format = GL_DEPTH_STENCIL; + type = GL_UNSIGNED_INT_24_8; + break; + case PixelFormat::kUnknown: + case PixelFormat::kD32FloatS8UInt: + case PixelFormat::kR8G8UNormInt: + case PixelFormat::kB10G10R10XRSRGB: + case PixelFormat::kB10G10R10XR: + case PixelFormat::kB10G10R10A10XR: + return; + } + is_valid_ = true; + } + + TexImage2DData(PixelFormat pixel_format, BufferView p_buffer_view) + : TexImage2DData(pixel_format) { + buffer_view = std::move(p_buffer_view); + } + + bool IsValid() const { return is_valid_; } + + private: + bool is_valid_ = false; +}; +} // namespace + +BlitCopyBufferToTextureCommandGLES::~BlitCopyBufferToTextureCommandGLES() = + default; + +std::string BlitCopyBufferToTextureCommandGLES::GetLabel() const { + return label; +} + +bool BlitCopyBufferToTextureCommandGLES::Encode( + const ReactorGLES& reactor) const { + TextureGLES& texture_gles = TextureGLES::Cast(*destination); + + if (texture_gles.GetType() != TextureGLES::Type::kTexture) { + VALIDATION_LOG << "Incorrect texture usage flags for setting contents on " + "this texture object."; + return false; + } + + if (texture_gles.IsWrapped()) { + VALIDATION_LOG << "Cannot set the contents of a wrapped texture."; + return false; + } + + const auto& tex_descriptor = texture_gles.GetTextureDescriptor(); + + if (tex_descriptor.size.IsEmpty()) { + return true; + } + + if (!tex_descriptor.IsValid() || + source.range.length < tex_descriptor.GetByteSizeOfBaseMipLevel()) { + return false; + } + + destination->SetCoordinateSystem(TextureCoordinateSystem::kUploadFromHost); + + GLenum texture_type; + GLenum texture_target; + switch (tex_descriptor.type) { + case TextureType::kTexture2D: + texture_type = GL_TEXTURE_2D; + texture_target = GL_TEXTURE_2D; + break; + case TextureType::kTexture2DMultisample: + VALIDATION_LOG << "Multisample texture uploading is not supported for " + "the OpenGLES backend."; + return false; + case TextureType::kTextureCube: + texture_type = GL_TEXTURE_CUBE_MAP; + texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice; + break; + case TextureType::kTextureExternalOES: + texture_type = GL_TEXTURE_EXTERNAL_OES; + texture_target = GL_TEXTURE_EXTERNAL_OES; + break; + } + + TexImage2DData data = TexImage2DData(tex_descriptor.format, source); + if (!data.IsValid()) { + VALIDATION_LOG << "Invalid texture format."; + return false; + } + + auto gl_handle = texture_gles.GetGLHandle(); + if (!gl_handle.has_value()) { + VALIDATION_LOG + << "Texture was collected before it could be uploaded to the GPU."; + return false; + } + const auto& gl = reactor.GetProcTable(); + gl.BindTexture(texture_type, gl_handle.value()); + const GLvoid* tex_data = + data.buffer_view.buffer->OnGetContents() + data.buffer_view.range.offset; + + { + TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes", + std::to_string(data.buffer_view.range.length).c_str()); + gl.TexImage2D(texture_target, // target + 0u, // LOD level + data.internal_format, // internal format + tex_descriptor.size.width, // width + tex_descriptor.size.height, // height + 0u, // border + data.external_format, // external format + data.type, // type + tex_data // data + ); + } + texture_gles.MarkContentsInitialized(); + return true; +} + BlitCopyTextureToBufferCommandGLES::~BlitCopyTextureToBufferCommandGLES() = default; diff --git a/impeller/renderer/backend/gles/blit_command_gles.h b/impeller/renderer/backend/gles/blit_command_gles.h index 5fa9c802f3a5b..f4cd901a3e848 100644 --- a/impeller/renderer/backend/gles/blit_command_gles.h +++ b/impeller/renderer/backend/gles/blit_command_gles.h @@ -20,6 +20,16 @@ struct BlitEncodeGLES : BackendCast { [[nodiscard]] virtual bool Encode(const ReactorGLES& reactor) const = 0; }; +struct BlitCopyBufferToTextureCommandGLES + : public BlitEncodeGLES, + public BlitCopyBufferToTextureCommand { + ~BlitCopyBufferToTextureCommandGLES() override; + + std::string GetLabel() const override; + + [[nodiscard]] bool Encode(const ReactorGLES& reactor) const override; +}; + struct BlitCopyTextureToTextureCommandGLES : public BlitEncodeGLES, public BlitCopyTextureToTextureCommand { diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc index 92364c4c7cf20..100259a490e7b 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.cc +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -104,7 +104,7 @@ bool BlitPassGLES::OnCopyTextureToTextureCommand( command->source_region = source_region; command->destination_origin = destination_origin; - commands_.emplace_back(std::move(command)); + commands_.push_back(std::move(command)); return true; } @@ -122,7 +122,26 @@ bool BlitPassGLES::OnCopyTextureToBufferCommand( command->source_region = source_region; command->destination_offset = destination_offset; - commands_.emplace_back(std::move(command)); + commands_.push_back(std::move(command)); + return true; +} + +// |BlitPass| +bool BlitPassGLES::OnCopyBufferToTextureCommand( + BufferView source, + std::shared_ptr destination, + IPoint destination_origin, + std::string label, + uint32_t slice) { + auto command = std::make_unique(); + command->label = label; + command->source = std::move(source); + command->destination = std::move(destination); + command->destination_origin = destination_origin; + command->label = label; + command->slice = slice; + + commands_.push_back(std::move(command)); return true; } @@ -133,7 +152,7 @@ bool BlitPassGLES::OnGenerateMipmapCommand(std::shared_ptr texture, command->label = label; command->texture = std::move(texture); - commands_.emplace_back(std::move(command)); + commands_.push_back(std::move(command)); return true; } diff --git a/impeller/renderer/backend/gles/blit_pass_gles.h b/impeller/renderer/backend/gles/blit_pass_gles.h index 170e3346f4fc8..58eaed6e8eb1d 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.h +++ b/impeller/renderer/backend/gles/blit_pass_gles.h @@ -58,10 +58,9 @@ class BlitPassGLES final : public BlitPass, bool OnCopyBufferToTextureCommand(BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) override { - IMPELLER_UNIMPLEMENTED; - return false; - } + std::string label, + uint32_t slice) override; + // |BlitPass| bool OnGenerateMipmapCommand(std::shared_ptr texture, std::string label) override; diff --git a/impeller/renderer/backend/gles/capabilities_gles.cc b/impeller/renderer/backend/gles/capabilities_gles.cc index a67486bd37047..b55d2fe54dc02 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.cc +++ b/impeller/renderer/backend/gles/capabilities_gles.cc @@ -153,10 +153,6 @@ bool CapabilitiesGLES::SupportsSSBO() const { return false; } -bool CapabilitiesGLES::SupportsBufferToTextureBlits() const { - return false; -} - bool CapabilitiesGLES::SupportsTextureToTextureBlits() const { return false; } diff --git a/impeller/renderer/backend/gles/capabilities_gles.h b/impeller/renderer/backend/gles/capabilities_gles.h index afcc71b418f73..d551acafac5d4 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.h +++ b/impeller/renderer/backend/gles/capabilities_gles.h @@ -86,9 +86,6 @@ class CapabilitiesGLES final // |Capabilities| bool SupportsSSBO() const override; - // |Capabilities| - bool SupportsBufferToTextureBlits() const override; - // |Capabilities| bool SupportsTextureToTextureBlits() const override; diff --git a/impeller/renderer/backend/gles/test/capabilities_unittests.cc b/impeller/renderer/backend/gles/test/capabilities_unittests.cc index 2ad20916a7233..9ae36118220fc 100644 --- a/impeller/renderer/backend/gles/test/capabilities_unittests.cc +++ b/impeller/renderer/backend/gles/test/capabilities_unittests.cc @@ -17,7 +17,6 @@ TEST(CapabilitiesGLES, CanInitializeWithDefaults) { EXPECT_FALSE(capabilities->SupportsOffscreenMSAA()); EXPECT_FALSE(capabilities->SupportsSSBO()); - EXPECT_FALSE(capabilities->SupportsBufferToTextureBlits()); EXPECT_FALSE(capabilities->SupportsTextureToTextureBlits()); EXPECT_FALSE(capabilities->SupportsFramebufferFetch()); EXPECT_FALSE(capabilities->SupportsCompute()); diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index c656b0d7d0ec2..af745e23fd565 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -18,6 +18,7 @@ namespace impeller { +namespace { static bool IsDepthStencilFormat(PixelFormat format) { switch (format) { case PixelFormat::kS8UInt: @@ -55,76 +56,6 @@ static TextureGLES::Type GetTextureTypeFromDescriptor( : TextureGLES::Type::kTexture; } -HandleType ToHandleType(TextureGLES::Type type) { - switch (type) { - case TextureGLES::Type::kTexture: - case TextureGLES::Type::kTextureMultisampled: - return HandleType::kTexture; - case TextureGLES::Type::kRenderBuffer: - case TextureGLES::Type::kRenderBufferMultisampled: - return HandleType::kRenderBuffer; - } - FML_UNREACHABLE(); -} - -TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc) - : TextureGLES(std::move(reactor), desc, false, std::nullopt) {} - -TextureGLES::TextureGLES(ReactorGLES::Ref reactor, - TextureDescriptor desc, - enum IsWrapped wrapped) - : TextureGLES(std::move(reactor), desc, true, std::nullopt) {} - -std::shared_ptr TextureGLES::WrapFBO(ReactorGLES::Ref reactor, - TextureDescriptor desc, - GLuint fbo) { - return std::shared_ptr( - new TextureGLES(std::move(reactor), desc, true, fbo)); -} - -TextureGLES::TextureGLES(std::shared_ptr reactor, - TextureDescriptor desc, - bool is_wrapped, - std::optional fbo) - : Texture(desc), - reactor_(std::move(reactor)), - type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())), - handle_(reactor_->CreateHandle(ToHandleType(type_))), - is_wrapped_(is_wrapped), - wrapped_fbo_(fbo) { - // Ensure the texture descriptor itself is valid. - if (!GetTextureDescriptor().IsValid()) { - VALIDATION_LOG << "Invalid texture descriptor."; - return; - } - // Ensure the texture doesn't exceed device capabilities. - const auto tex_size = GetTextureDescriptor().size; - const auto max_size = - reactor_->GetProcTable().GetCapabilities()->max_texture_size; - if (tex_size.Max(max_size) != max_size) { - VALIDATION_LOG << "Texture of size " << tex_size - << " would exceed max supported size of " << max_size << "."; - return; - } - - is_valid_ = true; -} - -// |Texture| -TextureGLES::~TextureGLES() { - reactor_->CollectHandle(handle_); -} - -// |Texture| -bool TextureGLES::IsValid() const { - return is_valid_; -} - -// |Texture| -void TextureGLES::SetLabel(std::string_view label) { - reactor_->SetDebugLabel(handle_, std::string{label.data(), label.size()}); -} - struct TexImage2DData { GLint internal_format = 0; GLenum external_format = GL_NONE; @@ -194,6 +125,77 @@ struct TexImage2DData { private: bool is_valid_ = false; }; +} // namespace + +HandleType ToHandleType(TextureGLES::Type type) { + switch (type) { + case TextureGLES::Type::kTexture: + case TextureGLES::Type::kTextureMultisampled: + return HandleType::kTexture; + case TextureGLES::Type::kRenderBuffer: + case TextureGLES::Type::kRenderBufferMultisampled: + return HandleType::kRenderBuffer; + } + FML_UNREACHABLE(); +} + +TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc) + : TextureGLES(std::move(reactor), desc, false, std::nullopt) {} + +TextureGLES::TextureGLES(ReactorGLES::Ref reactor, + TextureDescriptor desc, + enum IsWrapped wrapped) + : TextureGLES(std::move(reactor), desc, true, std::nullopt) {} + +std::shared_ptr TextureGLES::WrapFBO(ReactorGLES::Ref reactor, + TextureDescriptor desc, + GLuint fbo) { + return std::shared_ptr( + new TextureGLES(std::move(reactor), desc, true, fbo)); +} + +TextureGLES::TextureGLES(std::shared_ptr reactor, + TextureDescriptor desc, + bool is_wrapped, + std::optional fbo) + : Texture(desc), + reactor_(std::move(reactor)), + type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())), + handle_(reactor_->CreateHandle(ToHandleType(type_))), + is_wrapped_(is_wrapped), + wrapped_fbo_(fbo) { + // Ensure the texture descriptor itself is valid. + if (!GetTextureDescriptor().IsValid()) { + VALIDATION_LOG << "Invalid texture descriptor."; + return; + } + // Ensure the texture doesn't exceed device capabilities. + const auto tex_size = GetTextureDescriptor().size; + const auto max_size = + reactor_->GetProcTable().GetCapabilities()->max_texture_size; + if (tex_size.Max(max_size) != max_size) { + VALIDATION_LOG << "Texture of size " << tex_size + << " would exceed max supported size of " << max_size << "."; + return; + } + + is_valid_ = true; +} + +// |Texture| +TextureGLES::~TextureGLES() { + reactor_->CollectHandle(handle_); +} + +// |Texture| +bool TextureGLES::IsValid() const { + return is_valid_; +} + +// |Texture| +void TextureGLES::SetLabel(std::string_view label) { + reactor_->SetDebugLabel(handle_, std::string{label.data(), label.size()}); +} // |Texture| bool TextureGLES::OnSetContents(const uint8_t* contents, @@ -234,11 +236,8 @@ bool TextureGLES::OnSetContents(std::shared_ptr mapping, return true; } - if (!tex_descriptor.IsValid()) { - return false; - } - - if (mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) { + if (!tex_descriptor.IsValid() || + mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) { return false; } @@ -456,6 +455,10 @@ bool TextureGLES::Bind() const { return true; } +void TextureGLES::MarkContentsInitialized() const { + contents_initialized_ = true; +} + bool TextureGLES::GenerateMipmap() { if (!IsValid()) { return false; diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index 5a8c3a79b33b2..a1827876d8a8d 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -60,9 +60,9 @@ class TextureGLES final : public Texture, std::optional GetFBO() const { return wrapped_fbo_; } - private: - friend class AllocatorMTL; + void MarkContentsInitialized() const; + private: ReactorGLES::Ref reactor_; const Type type_; HandleGLES handle_; diff --git a/impeller/renderer/backend/metal/blit_command_mtl.mm b/impeller/renderer/backend/metal/blit_command_mtl.mm index 17c8b51ba7269..f07c8d09bd558 100644 --- a/impeller/renderer/backend/metal/blit_command_mtl.mm +++ b/impeller/renderer/backend/metal/blit_command_mtl.mm @@ -132,7 +132,7 @@ sourceBytesPerImage:destination_bytes_per_image sourceSize:source_size_mtl toTexture:destination_mtl - destinationSlice:0 + destinationSlice:slice destinationLevel:0 destinationOrigin:destination_origin_mtl]; diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.h b/impeller/renderer/backend/metal/blit_pass_mtl.h index 33a27f5799eb9..6ddfe3d55a2e0 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.h +++ b/impeller/renderer/backend/metal/blit_pass_mtl.h @@ -56,7 +56,8 @@ class BlitPassMTL final : public BlitPass { bool OnCopyBufferToTextureCommand(BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) override; + std::string label, + uint32_t slice) override; // |BlitPass| bool OnGenerateMipmapCommand(std::shared_ptr texture, diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm index b884d01c0c151..ce58225feb58e 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.mm +++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/metal/blit_pass_mtl.h" #include #include +#include #include #include "flutter/fml/closure.h" @@ -127,12 +128,14 @@ BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) { + std::string label, + uint32_t slice) { auto command = std::make_unique(); - command->label = label; + command->label = std::move(label); command->source = std::move(source); command->destination = std::move(destination); command->destination_origin = destination_origin; + command->slice = slice; commands_.emplace_back(std::move(command)); return true; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 6248368687451..22882ea0bd666 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -56,7 +56,6 @@ static bool DeviceSupportsComputeSubgroups(id device) { return CapabilitiesBuilder() .SetSupportsOffscreenMSAA(true) .SetSupportsSSBO(true) - .SetSupportsBufferToTextureBlits(true) .SetSupportsTextureToTextureBlits(true) .SetSupportsDecalSamplerAddressMode(true) .SetSupportsFramebufferFetch(DeviceSupportsFramebufferFetch(device)) diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h index f2a55b5bd6e95..69806690038fe 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.h +++ b/impeller/renderer/backend/metal/device_buffer_mtl.h @@ -36,11 +36,6 @@ class DeviceBufferMTL final // |DeviceBuffer| uint8_t* OnGetContents() const override; - // |DeviceBuffer| - std::shared_ptr AsTexture(Allocator& allocator, - const TextureDescriptor& descriptor, - uint16_t row_bytes) const override; - // |DeviceBuffer| bool OnCopyHostBuffer(const uint8_t* source, Range source_range, diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index c2ea8d89ef731..ff763f2643ebe 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -29,30 +29,6 @@ return reinterpret_cast(buffer_.contents); } -std::shared_ptr DeviceBufferMTL::AsTexture( - Allocator& allocator, - const TextureDescriptor& descriptor, - uint16_t row_bytes) const { - auto mtl_texture_desc = ToMTLTextureDescriptor(descriptor); - - if (!mtl_texture_desc) { - VALIDATION_LOG << "Texture descriptor was invalid."; - return nullptr; - } - - if (@available(iOS 13.0, macos 10.15, *)) { - mtl_texture_desc.resourceOptions = buffer_.resourceOptions; - } - - auto texture = [buffer_ newTextureWithDescriptor:mtl_texture_desc - offset:0 - bytesPerRow:row_bytes]; - if (!texture) { - return nullptr; - } - return TextureMTL::Create(descriptor, texture); -} - [[nodiscard]] bool DeviceBufferMTL::OnCopyHostBuffer(const uint8_t* source, Range source_range, size_t offset) { diff --git a/impeller/renderer/backend/vulkan/blit_command_vk.cc b/impeller/renderer/backend/vulkan/blit_command_vk.cc index b5c3d6a147eda..922c2d2848c31 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk.cc @@ -262,6 +262,22 @@ bool BlitCopyBufferToTextureCommandVK::Encode(CommandEncoderVK& encoder) const { image_copy // ); + // Transition to shader-read. + { + BarrierVK barrier; + barrier.cmd_buffer = cmd_buffer; + barrier.src_access = vk::AccessFlagBits::eTransferWrite; + barrier.src_stage = vk::PipelineStageFlagBits::eTransfer; + barrier.dst_access = vk::AccessFlagBits::eShaderRead; + barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader; + + barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal; + + if (!dst.SetLayout(barrier)) { + return false; + } + } + return true; } diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.cc b/impeller/renderer/backend/vulkan/blit_pass_vk.cc index 32f00964c6c99..1dfb570fd7e31 100644 --- a/impeller/renderer/backend/vulkan/blit_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.cc @@ -97,13 +97,15 @@ bool BlitPassVK::OnCopyBufferToTextureCommand( BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) { + std::string label, + uint32_t slice) { auto command = std::make_unique(); command->source = std::move(source); command->destination = std::move(destination); command->destination_origin = destination_origin; command->label = std::move(label); + command->slice = slice; commands_.push_back(std::move(command)); return true; diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.h b/impeller/renderer/backend/vulkan/blit_pass_vk.h index 2aaeda68daec7..f693023ff68dd 100644 --- a/impeller/renderer/backend/vulkan/blit_pass_vk.h +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.h @@ -56,7 +56,8 @@ class BlitPassVK final : public BlitPass { bool OnCopyBufferToTextureCommand(BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) override; + std::string label, + uint32_t slice) override; // |BlitPass| bool OnGenerateMipmapCommand(std::shared_ptr texture, std::string label) override; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index a052954a658ed..7b465b70408c7 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -536,11 +536,6 @@ bool CapabilitiesVK::SupportsSSBO() const { return true; } -// |Capabilities| -bool CapabilitiesVK::SupportsBufferToTextureBlits() const { - return true; -} - // |Capabilities| bool CapabilitiesVK::SupportsTextureToTextureBlits() const { return true; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index d1e327b687ebb..632a96a4bec01 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -186,9 +186,6 @@ class CapabilitiesVK final : public Capabilities, // |Capabilities| bool SupportsSSBO() const override; - // |Capabilities| - bool SupportsBufferToTextureBlits() const override; - // |Capabilities| bool SupportsTextureToTextureBlits() const override; diff --git a/impeller/renderer/blit_command.h b/impeller/renderer/blit_command.h index 7f456f659326b..07c951e470aad 100644 --- a/impeller/renderer/blit_command.h +++ b/impeller/renderer/blit_command.h @@ -33,6 +33,7 @@ struct BlitCopyBufferToTextureCommand : public BlitCommand { BufferView source; std::shared_ptr destination; IPoint destination_origin; + uint32_t slice = 0; }; struct BlitGenerateMipmapCommand : public BlitCommand { diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index 81c4e500b0494..56e919b7ac9a1 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -123,7 +123,8 @@ bool BlitPass::AddCopy(std::shared_ptr source, bool BlitPass::AddCopy(BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) { + std::string label, + uint32_t slice) { if (!destination) { VALIDATION_LOG << "Attempted to add a texture blit with no destination."; return false; @@ -141,7 +142,8 @@ bool BlitPass::AddCopy(BufferView source, } return OnCopyBufferToTextureCommand(std::move(source), std::move(destination), - destination_origin, std::move(label)); + destination_origin, std::move(label), + slice); } bool BlitPass::GenerateMipmap(std::shared_ptr texture, diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index 64ab734c1035b..3ea790c5a438a 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -93,13 +93,16 @@ class BlitPass { /// destination buffer. /// @param[in] label The optional debug label to give the /// command. + /// @param[in] slice For cubemap textures, the slice to write + /// data to. /// /// @return If the command was valid for subsequent commitment. /// bool AddCopy(BufferView source, std::shared_ptr destination, IPoint destination_origin = {}, - std::string label = ""); + std::string label = "", + uint32_t slice = 0); //---------------------------------------------------------------------------- /// @brief Record a command to generate all mip levels for a texture. @@ -146,7 +149,8 @@ class BlitPass { BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label) = 0; + std::string label, + uint32_t slice) = 0; virtual bool OnGenerateMipmapCommand(std::shared_ptr texture, std::string label) = 0; diff --git a/impeller/renderer/capabilities.cc b/impeller/renderer/capabilities.cc index 1ef1fe38a8062..395fecf1b81f1 100644 --- a/impeller/renderer/capabilities.cc +++ b/impeller/renderer/capabilities.cc @@ -27,11 +27,6 @@ class StandardCapabilities final : public Capabilities { // |Capabilities| bool SupportsSSBO() const override { return supports_ssbo_; } - // |Capabilities| - bool SupportsBufferToTextureBlits() const override { - return supports_buffer_to_texture_blits_; - } - // |Capabilities| bool SupportsTextureToTextureBlits() const override { return supports_texture_to_texture_blits_; @@ -88,7 +83,6 @@ class StandardCapabilities final : public Capabilities { private: StandardCapabilities(bool supports_offscreen_msaa, bool supports_ssbo, - bool supports_buffer_to_texture_blits, bool supports_texture_to_texture_blits, bool supports_framebuffer_fetch, bool supports_compute, @@ -102,7 +96,6 @@ class StandardCapabilities final : public Capabilities { PixelFormat default_glyph_atlas_format) : supports_offscreen_msaa_(supports_offscreen_msaa), supports_ssbo_(supports_ssbo), - supports_buffer_to_texture_blits_(supports_buffer_to_texture_blits), supports_texture_to_texture_blits_(supports_texture_to_texture_blits), supports_framebuffer_fetch_(supports_framebuffer_fetch), supports_compute_(supports_compute), @@ -120,7 +113,6 @@ class StandardCapabilities final : public Capabilities { bool supports_offscreen_msaa_ = false; bool supports_ssbo_ = false; - bool supports_buffer_to_texture_blits_ = false; bool supports_texture_to_texture_blits_ = false; bool supports_framebuffer_fetch_ = false; bool supports_compute_ = false; @@ -152,12 +144,6 @@ CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsSSBO(bool value) { return *this; } -CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsBufferToTextureBlits( - bool value) { - supports_buffer_to_texture_blits_ = value; - return *this; -} - CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsTextureToTextureBlits( bool value) { supports_texture_to_texture_blits_ = value; @@ -227,7 +213,6 @@ std::unique_ptr CapabilitiesBuilder::Build() { return std::unique_ptr(new StandardCapabilities( // supports_offscreen_msaa_, // supports_ssbo_, // - supports_buffer_to_texture_blits_, // supports_texture_to_texture_blits_, // supports_framebuffer_fetch_, // supports_compute_, // diff --git a/impeller/renderer/capabilities.h b/impeller/renderer/capabilities.h index cfdf98856576b..b86a90fa868de 100644 --- a/impeller/renderer/capabilities.h +++ b/impeller/renderer/capabilities.h @@ -28,11 +28,6 @@ class Capabilities { /// Objects (SSBOs) to pipelines. virtual bool SupportsSSBO() const = 0; - /// @brief Whether the context backend supports blitting from a given - /// `DeviceBuffer` view to a texture region (via the relevant - /// `BlitPass::AddCopy` overloads). - virtual bool SupportsBufferToTextureBlits() const = 0; - /// @brief Whether the context backend supports blitting from one texture /// region to another texture region (via the relevant /// `BlitPass::AddCopy` overloads). @@ -128,8 +123,6 @@ class CapabilitiesBuilder { CapabilitiesBuilder& SetSupportsSSBO(bool value); - CapabilitiesBuilder& SetSupportsBufferToTextureBlits(bool value); - CapabilitiesBuilder& SetSupportsTextureToTextureBlits(bool value); CapabilitiesBuilder& SetSupportsFramebufferFetch(bool value); @@ -157,7 +150,6 @@ class CapabilitiesBuilder { private: bool supports_offscreen_msaa_ = false; bool supports_ssbo_ = false; - bool supports_buffer_to_texture_blits_ = false; bool supports_texture_to_texture_blits_ = false; bool supports_framebuffer_fetch_ = false; bool supports_compute_ = false; diff --git a/impeller/renderer/capabilities_unittests.cc b/impeller/renderer/capabilities_unittests.cc index 2692d067ad0a3..e92d37341082e 100644 --- a/impeller/renderer/capabilities_unittests.cc +++ b/impeller/renderer/capabilities_unittests.cc @@ -20,7 +20,6 @@ namespace testing { CAPABILITY_TEST(SupportsOffscreenMSAA, false); CAPABILITY_TEST(SupportsSSBO, false); -CAPABILITY_TEST(SupportsBufferToTextureBlits, false); CAPABILITY_TEST(SupportsTextureToTextureBlits, false); CAPABILITY_TEST(SupportsFramebufferFetch, false); CAPABILITY_TEST(SupportsCompute, false); diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 545e4cd6c4866..a6fe1c23a2723 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -973,43 +973,6 @@ TEST_P(RendererTest, InactiveUniforms) { OpenPlaygroundHere(callback); } -TEST_P(RendererTest, CanCreateCPUBackedTexture) { - if (GetParam() == PlaygroundBackend::kOpenGLES) { - GTEST_SKIP_("CPU backed textures are not supported on OpenGLES."); - } - - auto context = GetContext(); - auto allocator = context->GetResourceAllocator(); - size_t dimension = 2; - - do { - ISize size(dimension, dimension); - TextureDescriptor texture_descriptor; - texture_descriptor.storage_mode = StorageMode::kHostVisible; - texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; - texture_descriptor.size = size; - auto row_bytes = - std::max(static_cast(size.width * 4), - allocator->MinimumBytesPerRow(texture_descriptor.format)); - auto buffer_size = size.height * row_bytes; - - DeviceBufferDescriptor buffer_descriptor; - buffer_descriptor.storage_mode = StorageMode::kHostVisible; - buffer_descriptor.size = buffer_size; - - auto buffer = allocator->CreateBuffer(buffer_descriptor); - - ASSERT_TRUE(buffer); - - auto texture = buffer->AsTexture(*allocator, texture_descriptor, row_bytes); - - ASSERT_TRUE(texture); - ASSERT_TRUE(texture->IsValid()); - - dimension *= 2; - } while (dimension <= 8192); -} - TEST_P(RendererTest, DefaultIndexSize) { using VS = BoxFadeVertexShader; diff --git a/impeller/renderer/testing/mocks.h b/impeller/renderer/testing/mocks.h index 45b519cc3c937..848a811393414 100644 --- a/impeller/renderer/testing/mocks.h +++ b/impeller/renderer/testing/mocks.h @@ -83,7 +83,8 @@ class MockBlitPass : public BlitPass { (BufferView source, std::shared_ptr destination, IPoint destination_origin, - std::string label), + std::string label, + uint32_t slice), (override)); MOCK_METHOD(bool, OnGenerateMipmapCommand, @@ -193,7 +194,6 @@ class MockCapabilities : public Capabilities { MOCK_METHOD(bool, SupportsOffscreenMSAA, (), (const, override)); MOCK_METHOD(bool, SupportsImplicitResolvingMSAA, (), (const, override)); MOCK_METHOD(bool, SupportsSSBO, (), (const, override)); - MOCK_METHOD(bool, SupportsBufferToTextureBlits, (), (const, override)); MOCK_METHOD(bool, SupportsTextureToTextureBlits, (), (const, override)); MOCK_METHOD(bool, SupportsFramebufferFetch, (), (const, override)); MOCK_METHOD(bool, SupportsCompute, (), (const, override)); diff --git a/impeller/typographer/backends/skia/typographer_context_skia.cc b/impeller/typographer/backends/skia/typographer_context_skia.cc index 11a612660a14b..36c6af3076939 100644 --- a/impeller/typographer/backends/skia/typographer_context_skia.cc +++ b/impeller/typographer/backends/skia/typographer_context_skia.cc @@ -9,10 +9,14 @@ #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "fml/closure.h" +#include "fml/mapping.h" #include "impeller/base/allocation.h" #include "impeller/core/allocator.h" +#include "impeller/renderer/command_buffer.h" #include "impeller/typographer/backends/skia/glyph_atlas_context_skia.h" #include "impeller/typographer/backends/skia/typeface_skia.h" +#include "impeller/typographer/glyph_atlas.h" #include "impeller/typographer/rectangle_packer.h" #include "impeller/typographer/typographer_context.h" #include "include/core/SkColor.h" @@ -257,8 +261,11 @@ static std::shared_ptr CreateAtlasBitmap(const GlyphAtlas& atlas, return bitmap; } -static bool UpdateGlyphTextureAtlas(std::shared_ptr bitmap, - const std::shared_ptr& texture) { +static bool UpdateGlyphTextureAtlas( + std::shared_ptr bitmap, + const std::shared_ptr& allocator, + const std::shared_ptr& texture, + const std::shared_ptr& blit_pass) { TRACE_EVENT0("impeller", __FUNCTION__); FML_DCHECK(bitmap != nullptr); @@ -270,12 +277,17 @@ static bool UpdateGlyphTextureAtlas(std::shared_ptr bitmap, [bitmap](auto, auto) mutable { bitmap.reset(); } // proc ); - return texture->SetContents(mapping); + std::shared_ptr device_buffer = + allocator->CreateBufferWithCopy(*mapping); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)), + texture); + return blit_pass->EncodeCommands(allocator); } static std::shared_ptr UploadGlyphTextureAtlas( const std::shared_ptr& allocator, std::shared_ptr bitmap, + const std::shared_ptr& blit_pass, const ISize& atlas_size, PixelFormat format) { TRACE_EVENT0("impeller", __FUNCTION__); @@ -287,7 +299,7 @@ static std::shared_ptr UploadGlyphTextureAtlas( const auto& pixmap = bitmap->pixmap(); TextureDescriptor texture_descriptor; - texture_descriptor.storage_mode = StorageMode::kHostVisible; + texture_descriptor.storage_mode = StorageMode::kDevicePrivate; texture_descriptor.format = format; texture_descriptor.size = atlas_size; @@ -296,21 +308,25 @@ static std::shared_ptr UploadGlyphTextureAtlas( return nullptr; } - auto texture = allocator->CreateTexture(texture_descriptor); + std::shared_ptr texture = + allocator->CreateTexture(texture_descriptor); if (!texture || !texture->IsValid()) { return nullptr; } texture->SetLabel("GlyphAtlas"); - auto mapping = std::make_shared( - reinterpret_cast(bitmap->getAddr(0, 0)), // data - texture_descriptor.GetByteSizeOfBaseMipLevel(), // size - [bitmap](auto, auto) mutable { bitmap.reset(); } // proc - ); + std::shared_ptr mapping = + std::make_shared( + reinterpret_cast(bitmap->getAddr(0, 0)), // data + texture_descriptor.GetByteSizeOfBaseMipLevel(), // size + [bitmap](auto, auto) mutable { bitmap.reset(); } // proc + ); + std::shared_ptr device_buffer = + allocator->CreateBufferWithCopy(*mapping); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)), + texture); + blit_pass->EncodeCommands(allocator); - if (!texture->SetContents(mapping)) { - return nullptr; - } return texture; } @@ -329,6 +345,12 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( if (font_glyph_map.empty()) { return last_atlas; } + std::shared_ptr cmd_buffer = context.CreateCommandBuffer(); + std::shared_ptr blit_pass = cmd_buffer->CreateBlitPass(); + + fml::ScopedCleanupClosure closure([&cmd_buffer, &context]() { + context.GetCommandQueue()->Submit({std::move(cmd_buffer)}); + }); // --------------------------------------------------------------------------- // Step 1: Determine if the atlas type and font glyph pairs are compatible @@ -387,7 +409,8 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( // --------------------------------------------------------------------------- // Step 5a: Update the existing texture with the updated bitmap. // --------------------------------------------------------------------------- - if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) { + if (!UpdateGlyphTextureAtlas(bitmap, context.GetResourceAllocator(), + last_atlas->GetTexture(), blit_pass)) { return nullptr; } return last_atlas; @@ -407,8 +430,8 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( font_glyph_pairs.push_back({scaled_font, glyph}); } } - auto glyph_atlas = std::make_shared(type); - auto atlas_size = OptimumAtlasSizeForFontGlyphPairs( + std::shared_ptr glyph_atlas = std::make_shared(type); + ISize atlas_size = OptimumAtlasSizeForFontGlyphPairs( font_glyph_pairs, // glyph_positions, // atlas_context, // @@ -450,6 +473,21 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( } atlas_context_skia.UpdateBitmap(bitmap); + // If the new atlas size is the same size as the previous texture, reuse the + // texture and treat this as an updated that replaces all glyphs. + if (last_atlas && last_atlas->GetTexture()) { + std::shared_ptr last_texture = last_atlas->GetTexture(); + if (atlas_size == last_texture->GetSize()) { + if (!UpdateGlyphTextureAtlas(bitmap, context.GetResourceAllocator(), + last_texture, blit_pass)) { + return nullptr; + } + + glyph_atlas->SetTexture(last_texture); + return glyph_atlas; + } + } + // --------------------------------------------------------------------------- // Step 7b: Upload the atlas as a texture. // --------------------------------------------------------------------------- @@ -462,8 +500,8 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( format = PixelFormat::kR8G8B8A8UNormInt; break; } - auto texture = UploadGlyphTextureAtlas(context.GetResourceAllocator(), bitmap, - atlas_size, format); + std::shared_ptr texture = UploadGlyphTextureAtlas( + context.GetResourceAllocator(), bitmap, blit_pass, atlas_size, format); if (!texture) { return nullptr; } diff --git a/impeller/typographer/backends/stb/typographer_context_stb.cc b/impeller/typographer/backends/stb/typographer_context_stb.cc index ee73d1ea4a15d..d7deda3f3aef4 100644 --- a/impeller/typographer/backends/stb/typographer_context_stb.cc +++ b/impeller/typographer/backends/stb/typographer_context_stb.cc @@ -9,8 +9,11 @@ #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "fml/closure.h" +#include "fml/mapping.h" #include "impeller/base/allocation.h" #include "impeller/core/allocator.h" +#include "impeller/core/device_buffer.h" #include "impeller/typographer/backends/stb/glyph_atlas_context_stb.h" #include "impeller/typographer/font_glyph_pair.h" #include "typeface_stb.h" @@ -309,6 +312,8 @@ static std::shared_ptr CreateAtlasBitmap(const GlyphAtlas& atlas, // static bool UpdateGlyphTextureAtlas(std::shared_ptr bitmap, static bool UpdateGlyphTextureAtlas(std::shared_ptr& bitmap, + const std::shared_ptr& allocator, + std::shared_ptr& blit_pass, const std::shared_ptr& texture) { TRACE_EVENT0("impeller", __FUNCTION__); @@ -323,11 +328,16 @@ static bool UpdateGlyphTextureAtlas(std::shared_ptr& bitmap, // specify a release proc. ); - return texture->SetContents(mapping); + std::shared_ptr device_buffer = + allocator->CreateBufferWithCopy(*mapping); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), texture); + + return blit_pass->EncodeCommands(allocator); } static std::shared_ptr UploadGlyphTextureAtlas( const std::shared_ptr& allocator, + std::shared_ptr& blit_pass, std::shared_ptr& bitmap, const ISize& atlas_size, PixelFormat format) { @@ -339,7 +349,7 @@ static std::shared_ptr UploadGlyphTextureAtlas( FML_DCHECK(bitmap != nullptr); TextureDescriptor texture_descriptor; - texture_descriptor.storage_mode = StorageMode::kHostVisible; + texture_descriptor.storage_mode = StorageMode::kDevicePrivate; texture_descriptor.format = format; texture_descriptor.size = atlas_size; @@ -354,16 +364,20 @@ static std::shared_ptr UploadGlyphTextureAtlas( } texture->SetLabel("GlyphAtlas"); - auto mapping = std::make_shared( - reinterpret_cast(bitmap->GetPixels()), // data - texture_descriptor.GetByteSizeOfBaseMipLevel() // size - // As the bitmap is static in this module I believe we don't need to - // specify a release proc. - ); - - if (!texture->SetContents(mapping)) { + std::shared_ptr mapping = + std::make_shared( + reinterpret_cast(bitmap->GetPixels()), // data + texture_descriptor.GetByteSizeOfBaseMipLevel() // size + // As the bitmap is static in this module I believe we don't need to + // specify a release proc. + ); + std::shared_ptr device_buffer = + allocator->CreateBufferWithCopy(*mapping); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), texture); + if (!blit_pass->EncodeCommands(allocator)) { return nullptr; } + return texture; } @@ -383,6 +397,13 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( return last_atlas; } + std::shared_ptr cmd_buffer = context.CreateCommandBuffer(); + std::shared_ptr blit_pass = cmd_buffer->CreateBlitPass(); + + fml::ScopedCleanupClosure closure([&cmd_buffer, &context]() { + context.GetCommandQueue()->Submit({std::move(cmd_buffer)}); + }); + // --------------------------------------------------------------------------- // Step 1: Determine if the atlas type and font glyph pairs are compatible // with the current atlas and reuse if possible. @@ -441,7 +462,8 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( // --------------------------------------------------------------------------- // Step 5a: Update the existing texture with the updated bitmap. // --------------------------------------------------------------------------- - if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) { + if (!UpdateGlyphTextureAtlas(bitmap, context.GetResourceAllocator(), + blit_pass, last_atlas->GetTexture())) { return nullptr; } return last_atlas; @@ -519,8 +541,8 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( : PixelFormat::kR8G8B8A8UNormInt; break; } - auto texture = UploadGlyphTextureAtlas(context.GetResourceAllocator(), bitmap, - atlas_size, format); + auto texture = UploadGlyphTextureAtlas(context.GetResourceAllocator(), + blit_pass, bitmap, atlas_size, format); if (!texture) { return nullptr; } diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index 51e05bda31f6c..4f7b1209999c1 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -338,8 +338,7 @@ TEST_P(TypographerTest, RectanglePackerAddsNonoverlapingRectangles) { ASSERT_EQ(packer->PercentFull(), 0); } -TEST_P(TypographerTest, - GlyphAtlasTextureIsRecycledWhenContentsAreNotRecreated) { +TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledWhenContentsAreRecreated) { auto context = TypographerContextSkia::Make(); auto atlas_context = context->CreateGlyphAtlasContext(); ASSERT_TRUE(context && context->IsValid()); @@ -371,7 +370,7 @@ TEST_P(TypographerTest, auto new_packer = atlas_context->GetRectPacker(); - ASSERT_NE(second_texture, first_texture); + ASSERT_EQ(second_texture, first_texture); ASSERT_NE(old_packer, new_packer); } diff --git a/lib/ui/painting/image_decoder_impeller.cc b/lib/ui/painting/image_decoder_impeller.cc index 7532399699d46..cd081699f7da4 100644 --- a/lib/ui/painting/image_decoder_impeller.cc +++ b/lib/ui/painting/image_decoder_impeller.cc @@ -494,18 +494,10 @@ void ImageDecoderImpeller::Decode(fml::RefPtr descriptor, gpu_disabled_switch]() { sk_sp image; std::string decode_error; - if (context->GetCapabilities()->SupportsBufferToTextureBlits()) { - std::tie(image, decode_error) = UploadTextureToPrivate( - context, bitmap_result.device_buffer, bitmap_result.image_info, - bitmap_result.sk_bitmap, gpu_disabled_switch); - result(image, decode_error); - } else { - std::tie(image, decode_error) = UploadTextureToStorage( - context, bitmap_result.sk_bitmap, gpu_disabled_switch, - impeller::StorageMode::kDevicePrivate, - /*create_mips=*/true); - result(image, decode_error); - } + std::tie(image, decode_error) = UploadTextureToPrivate( + context, bitmap_result.device_buffer, bitmap_result.image_info, + bitmap_result.sk_bitmap, gpu_disabled_switch); + result(image, decode_error); }; if (context->GetBackendType() == impeller::Context::BackendType::kOpenGLES) { diff --git a/lib/ui/painting/image_decoder_no_gl_unittests.h b/lib/ui/painting/image_decoder_no_gl_unittests.h index 0376f6a2cd72c..b1839f0768c47 100644 --- a/lib/ui/painting/image_decoder_no_gl_unittests.h +++ b/lib/ui/painting/image_decoder_no_gl_unittests.h @@ -44,12 +44,6 @@ class TestImpellerDeviceBuffer : public DeviceBuffer { ~TestImpellerDeviceBuffer() { free(bytes_); } private: - std::shared_ptr AsTexture(Allocator& allocator, - const TextureDescriptor& descriptor, - uint16_t row_bytes) const override { - return nullptr; - } - bool SetLabel(const std::string& label) override { return true; } bool SetLabel(const std::string& label, Range range) override { return true; } From 0a469aa59c4eca199accae2d64ecae2c11b501fe Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 7 May 2024 17:10:04 -0700 Subject: [PATCH 06/22] [Impeller] In the AHB swapchain, make the wait stages be non-temporaries. (#52644) The reference to these was garbage in the submit info. --- .../backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc index 9cac3d9e1622d..0212d0cd82d24 100644 --- a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc @@ -332,7 +332,7 @@ bool AHBSwapchainImplVK::SubmitWaitForRenderReady( vk::SubmitInfo submit_info; if (render_ready_semaphore) { - constexpr const auto kWaitStages = + static constexpr const auto kWaitStages = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eTransfer; From 78878241c8f32b598683d5ebe1501f4eed2b5d95 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 7 May 2024 20:04:17 -0700 Subject: [PATCH 07/22] [Impeller] bump image count to 3 (#52650) From testing on a Pixel 7 pro, we get constant destruction of buffers at 2, but not at 3, 4, or 5. I suspect the problem is that when we stop pumping frames, we need to be able to hold all buffers that were in use. Since we support 2 frames in flight while one is encoding, that gives us 3 maximum buffers. --- .../renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h index 242aaeb9f8c92..19429adedcd0c 100644 --- a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h @@ -57,7 +57,7 @@ class AHBTexturePoolVK { /// explicit AHBTexturePoolVK(std::weak_ptr context, android::HardwareBufferDescriptor desc, - size_t max_entries = 2u); + size_t max_entries = 3u); ~AHBTexturePoolVK(); From 071014f31c6b8c169f8e793444d600b3b40efd4f Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 7 May 2024 22:06:09 -0700 Subject: [PATCH 08/22] [Impeller] support requires readback from onscreen texture in experimental canvas. (#52640) This doesn't yet add support for backdrop filters or emulated advanced blends, but will allow them to work if `surface_frame.framebuffer_info().supports_readback` is true. part of https://github.com/flutter/flutter/issues/142054 --- impeller/aiks/experimental_canvas.cc | 107 ++++++++++++++++-- impeller/aiks/experimental_canvas.h | 19 ++-- impeller/display_list/dl_dispatcher.cc | 3 +- impeller/display_list/dl_dispatcher.h | 1 + shell/gpu/gpu_surface_metal_impeller.mm | 5 +- shell/gpu/gpu_surface_vulkan_impeller.cc | 6 +- .../embedder/embedder_external_view.cc | 4 +- 7 files changed, 121 insertions(+), 24 deletions(-) diff --git a/impeller/aiks/experimental_canvas.cc b/impeller/aiks/experimental_canvas.cc index 0451e5d36c919..7837b0ca9fb90 100644 --- a/impeller/aiks/experimental_canvas.cc +++ b/impeller/aiks/experimental_canvas.cc @@ -12,6 +12,7 @@ #include "impeller/entity/entity.h" #include "impeller/entity/entity_pass_clip_stack.h" #include "impeller/geometry/color.h" +#include "impeller/renderer/render_target.h" namespace impeller { @@ -107,10 +108,12 @@ static std::unique_ptr CreateRenderTarget( } ExperimentalCanvas::ExperimentalCanvas(ContentContext& renderer, - RenderTarget& render_target) + RenderTarget& render_target, + bool requires_readback) : Canvas(), renderer_(renderer), render_target_(render_target), + requires_readback_(requires_readback), clip_coverage_stack_(EntityPassClipStack( Rect::MakeSize(render_target.GetRenderTargetSize()))) { SetupRenderPass(); @@ -118,10 +121,12 @@ ExperimentalCanvas::ExperimentalCanvas(ContentContext& renderer, ExperimentalCanvas::ExperimentalCanvas(ContentContext& renderer, RenderTarget& render_target, + bool requires_readback, Rect cull_rect) : Canvas(cull_rect), renderer_(renderer), render_target_(render_target), + requires_readback_(requires_readback), clip_coverage_stack_(EntityPassClipStack( Rect::MakeSize(render_target.GetRenderTargetSize()))) { SetupRenderPass(); @@ -129,16 +134,19 @@ ExperimentalCanvas::ExperimentalCanvas(ContentContext& renderer, ExperimentalCanvas::ExperimentalCanvas(ContentContext& renderer, RenderTarget& render_target, + bool requires_readback, IRect cull_rect) : Canvas(cull_rect), renderer_(renderer), render_target_(render_target), + requires_readback_(requires_readback), clip_coverage_stack_(EntityPassClipStack( Rect::MakeSize(render_target.GetRenderTargetSize()))) { SetupRenderPass(); } void ExperimentalCanvas::SetupRenderPass() { + renderer_.GetRenderTargetCache()->Start(); auto color0 = render_target_.GetColorAttachments().find(0u)->second; auto& stencil_attachment = render_target_.GetStencilAttachment(); @@ -158,18 +166,27 @@ void ExperimentalCanvas::SetupRenderPass() { color0.clear_color = Color::BlackTransparent(); render_target_.SetColorAttachment(color0, 0); - entity_pass_targets_.push_back(std::make_unique( - render_target_, - renderer_.GetDeviceCapabilities().SupportsReadFromResolve(), - renderer_.GetDeviceCapabilities().SupportsImplicitResolvingMSAA())); + // If requires_readback is true, then there is a backdrop filter or emulated + // advanced blend in the first save layer. This requires a readback, which + // isn't supported by onscreen textures. To support this, we immediately begin + // a second save layer with the same dimensions as the onscreen. When + // rendering is completed, we must blit this saveLayer to the onscreen. + if (requires_readback_) { + entity_pass_targets_.push_back(CreateRenderTarget( + renderer_, color0.texture->GetSize(), /*mip_count=*/1, + /*clear_color=*/Color::BlackTransparent())); + } else { + entity_pass_targets_.push_back(std::make_unique( + render_target_, + renderer_.GetDeviceCapabilities().SupportsReadFromResolve(), + renderer_.GetDeviceCapabilities().SupportsImplicitResolvingMSAA())); + } auto inline_pass = std::make_unique( renderer_, *entity_pass_targets_.back(), 0); inline_pass_contexts_.emplace_back(std::move(inline_pass)); auto result = inline_pass_contexts_.back()->GetRenderPass(0u); render_passes_.push_back(result.pass); - - renderer_.GetRenderTargetCache()->Start(); } void ExperimentalCanvas::Save(uint32_t total_content_depth) { @@ -450,4 +467,80 @@ void ExperimentalCanvas::AddClipEntityToCurrentPass(Entity entity) { entity.Render(renderer_, *render_passes_.back()); } +bool ExperimentalCanvas::BlitToOnscreen() { + auto command_buffer = renderer_.GetContext()->CreateCommandBuffer(); + command_buffer->SetLabel("EntityPass Root Command Buffer"); + auto offscreen_target = entity_pass_targets_.back()->GetRenderTarget(); + + if (renderer_.GetContext() + ->GetCapabilities() + ->SupportsTextureToTextureBlits()) { + auto blit_pass = command_buffer->CreateBlitPass(); + blit_pass->AddCopy(offscreen_target.GetRenderTargetTexture(), + render_target_.GetRenderTargetTexture()); + if (!blit_pass->EncodeCommands( + renderer_.GetContext()->GetResourceAllocator())) { + VALIDATION_LOG << "Failed to encode root pass blit command."; + return false; + } + if (!renderer_.GetContext() + ->GetCommandQueue() + ->Submit({command_buffer}) + .ok()) { + return false; + } + } else { + auto render_pass = command_buffer->CreateRenderPass(render_target_); + render_pass->SetLabel("EntityPass Root Render Pass"); + + { + auto size_rect = Rect::MakeSize(offscreen_target.GetRenderTargetSize()); + auto contents = TextureContents::MakeRect(size_rect); + contents->SetTexture(offscreen_target.GetRenderTargetTexture()); + contents->SetSourceRect(size_rect); + contents->SetLabel("Root pass blit"); + + Entity entity; + entity.SetContents(contents); + entity.SetBlendMode(BlendMode::kSource); + + if (!entity.Render(renderer_, *render_pass)) { + VALIDATION_LOG << "Failed to render EntityPass root blit."; + return false; + } + } + + if (!render_pass->EncodeCommands()) { + VALIDATION_LOG << "Failed to encode root pass command buffer."; + return false; + } + if (!renderer_.GetContext() + ->GetCommandQueue() + ->Submit({command_buffer}) + .ok()) { + return false; + } + } + return true; +} + +void ExperimentalCanvas::EndReplay() { + FML_DCHECK(inline_pass_contexts_.size() == 1u); + inline_pass_contexts_.back()->EndPass(); + + // If requires_readback_ was true, then we rendered to an offscreen texture + // instead of to the onscreen provided in the render target. Now we need to + // draw or blit the offscreen back to the onscreen. + if (requires_readback_) { + BlitToOnscreen(); + } + + render_passes_.clear(); + inline_pass_contexts_.clear(); + renderer_.GetRenderTargetCache()->End(); + + Reset(); + Initialize(initial_cull_rect_); +} + } // namespace impeller diff --git a/impeller/aiks/experimental_canvas.h b/impeller/aiks/experimental_canvas.h index 6500ec90f09d5..fab017bee8c20 100644 --- a/impeller/aiks/experimental_canvas.h +++ b/impeller/aiks/experimental_canvas.h @@ -20,14 +20,18 @@ namespace impeller { class ExperimentalCanvas : public Canvas { public: - ExperimentalCanvas(ContentContext& renderer, RenderTarget& render_target); + ExperimentalCanvas(ContentContext& renderer, + RenderTarget& render_target, + bool requires_readback); ExperimentalCanvas(ContentContext& renderer, RenderTarget& render_target, + bool requires_readback, Rect cull_rect); ExperimentalCanvas(ContentContext& renderer, RenderTarget& render_target, + bool requires_readback, IRect cull_rect); ~ExperimentalCanvas() override = default; @@ -42,16 +46,7 @@ class ExperimentalCanvas : public Canvas { bool Restore() override; - void EndReplay() { - FML_DCHECK(inline_pass_contexts_.size() == 1u); - inline_pass_contexts_.back()->EndPass(); - render_passes_.clear(); - inline_pass_contexts_.clear(); - renderer_.GetRenderTargetCache()->End(); - - Reset(); - Initialize(initial_cull_rect_); - } + void EndReplay(); void DrawTextFrame(const std::shared_ptr& text_frame, Point position, @@ -73,6 +68,7 @@ class ExperimentalCanvas : public Canvas { ContentContext& renderer_; RenderTarget& render_target_; + const bool requires_readback_; EntityPassClipStack clip_coverage_stack_; std::vector> inline_pass_contexts_; std::vector> entity_pass_targets_; @@ -83,6 +79,7 @@ class ExperimentalCanvas : public Canvas { void AddRenderEntityToCurrentPass(Entity entity, bool reuse_depth) override; void AddClipEntityToCurrentPass(Entity entity) override; + bool BlitToOnscreen(); Point GetGlobalPassPosition() { if (save_layer_state_.empty()) { diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index e0c1073b3bb7e..f4f9052c1d3da 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -1168,8 +1168,9 @@ Canvas& DlDispatcher::GetCanvas() { ExperimentalDlDispatcher::ExperimentalDlDispatcher(ContentContext& renderer, RenderTarget& render_target, + bool requires_readback, IRect cull_rect) - : canvas_(renderer, render_target, cull_rect) {} + : canvas_(renderer, render_target, requires_readback, cull_rect) {} Canvas& ExperimentalDlDispatcher::GetCanvas() { return canvas_; diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index 40bf7994351e3..633d453ce2490 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -289,6 +289,7 @@ class ExperimentalDlDispatcher : public DlDispatcherBase { public: ExperimentalDlDispatcher(ContentContext& renderer, RenderTarget& render_target, + bool requires_readback, IRect cull_rect); ~ExperimentalDlDispatcher() = default; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 8d372d74c900a..01093fe37612f 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -164,6 +164,9 @@ impeller::IRect cull_rect = surface->coverage(); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); + [[maybe_unused]] auto supports_readback = + surface_frame.framebuffer_info().supports_readback; + #if ENABLE_EXPERIMENTAL_CANVAS impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), impeller::Matrix()); @@ -173,7 +176,7 @@ fml::MakeCopyable([aiks_context, &display_list, &cull_rect, &sk_cull_rect](impeller::RenderTarget& render_target) -> bool { impeller::ExperimentalDlDispatcher impeller_dispatcher( - aiks_context->GetContentContext(), render_target, cull_rect); + aiks_context->GetContentContext(), render_target, supports_readback, cull_rect); display_list->Dispatch(impeller_dispatcher, sk_cull_rect); impeller_dispatcher.FinishRecording(); aiks_context->GetContentContext().GetTransientsBuffer().Reset(); diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index 97f82d178300d..430dfb6489903 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -84,11 +84,12 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( auto cull_rect = surface->GetTargetRenderPassDescriptor().GetRenderTargetSize(); + [[maybe_unused]] auto supports_readback = + surface_frame.framebuffer_info().supports_readback; return renderer->Render( std::move(surface), - fml::MakeCopyable([aiks_context, cull_rect, display_list]( - impeller::RenderTarget& render_target) + fml::MakeCopyable([&](impeller::RenderTarget& render_target) -> bool { #if ENABLE_EXPERIMENTAL_CANVAS impeller::TextFrameDispatcher collector( @@ -98,6 +99,7 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( SkIRect::MakeWH(cull_rect.width, cull_rect.height)); impeller::ExperimentalDlDispatcher impeller_dispatcher( aiks_context->GetContentContext(), render_target, + supports_readback, impeller::IRect::RoundOut( impeller::Rect::MakeSize(cull_rect))); display_list->Dispatch( diff --git a/shell/platform/embedder/embedder_external_view.cc b/shell/platform/embedder/embedder_external_view.cc index 3ac5d72dd0e72..642216f680fec 100644 --- a/shell/platform/embedder/embedder_external_view.cc +++ b/shell/platform/embedder/embedder_external_view.cc @@ -114,10 +114,10 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target, impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), impeller::Matrix()); - display_list->Dispatch(collector, sk_cull_rect); impeller::ExperimentalDlDispatcher impeller_dispatcher( - aiks_context->GetContentContext(), *impeller_target, cull_rect); + aiks_context->GetContentContext(), *impeller_target, + /*supports_readback=*/false, cull_rect); display_list->Dispatch(impeller_dispatcher, sk_cull_rect); impeller_dispatcher.FinishRecording(); aiks_context->GetContentContext().GetTransientsBuffer().Reset(); From f3ce5a59c2072cd396c5424814154363fb4a9138 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 May 2024 10:55:33 -0400 Subject: [PATCH 09/22] Roll Skia from 2319f1ae8fe4 to c7794fee8063 (20 revisions) (#52668) https://skia.googlesource.com/skia.git/+log/2319f1ae8fe4..c7794fee8063 2024-05-08 briansalomon@gmail.com [graphite] handle drawPath with oval/rrect as special case 2024-05-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll SK Tool from 825a4029e916 to 25a2a685e52e 2024-05-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Skia Infra from be0e106b41c7 to 825a4029e916 (7 revisions) 2024-05-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn from 35243cd3490b to 8f1e9f35e615 (30 revisions) 2024-05-08 michaelludwig@google.com [graphite] Let Surface automatically generate mipmaps when rendering a picture image 2024-05-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll vulkan-deps from 5fe3cf4db2e6 to e70a70873c7b (1 revision) 2024-05-07 scroggo@google.com Change CtsEnforcement for SurfaceAsyncReadPixels 2024-05-07 nicolettep@google.com [graphite] Add immutable sampler attribute to DescriptorData 2024-05-07 jvanverth@google.com Revert "[graphite] Fix use of linearFilterInset in texture shader." 2024-05-07 nicolettep@google.com [graphite] Add YCbCr GM to graphite 2024-05-07 brianosman@google.com Revert "Update clang_win and win_toolchain assets" 2024-05-07 jamesgk@google.com [graphite] Disable analytic blur for rrects 2024-05-07 brianosman@google.com Update clang_win and win_toolchain assets 2024-05-07 egdaniel@google.com [Graphite] Fix build for labels. 2024-05-07 johnstiles@google.com Add parentheses to permit constant folding. 2024-05-07 jvanverth@google.com [graphite] Fix use of linearFilterInset in texture shader. 2024-05-07 egdaniel@google.com [Graphite] Set labels on proxies for specific usages. 2024-05-07 kjlubick@google.com Add missing tag for zlib.h #include 2024-05-07 michaelludwig@google.com [graphite] Avoid unnecessary copy from makeImageSnapshot() in picture-shader rendering 2024-05-07 johnstiles@google.com Fix PathKit test. If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC brianosman@google.com,egdaniel@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 017a1205d71b9..956f3a77e06fa 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': '2319f1ae8fe42525f8b6a1969a1cee67bdbee290', + 'skia_revision': 'c7794fee80633070f2738239fb4ce792bcad81fd', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 61cd418b17686..ef54532d33aac 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: a329094cc4d882853b50dadf3146d846 +Signature: ac73c5eeec4665c9449839594eb08f74 ==================================================================================================== LIBRARY: etc1 From 2aa69807b57e5c452a4e3acfaeac6170a468c8fb Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Wed, 8 May 2024 08:07:25 -0700 Subject: [PATCH 10/22] Roll reclient forward (#52632) For https://github.com/flutter/flutter/issues/147750. Depends on https://github.com/flutter/buildroot/pull/852. This PR rolls forward reclient, the buildroot, and our RBE configs. The new RBE configs do a few things. First they default the exec strategy to `racing`. Second, they add a local wrapper script that passes the correct value for `-fdebug-prefix-map` in both the local and remote cases. Finally, they remove the canonicalization of the build's working directory, so that the remote working directory will have the same name as the local working directory. This solves the issue where the build working directory contained a mysterious `set_by_reclient/a` component. --- DEPS | 6 +++--- .../engine_build_configs/lib/src/build_config_runner.dart | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 956f3a77e06fa..a9e03a34dabcf 100644 --- a/DEPS +++ b/DEPS @@ -44,7 +44,7 @@ vars = { # See https://github.com/flutter/flutter/wiki/Engine-pre‐submits-and-post‐submits#post-submit 'clang_version': 'git_revision:725656bdd885483c39f482a01ea25d67acf39c46', - 'reclient_version': 'git_revision:2c9285bdffcfd1b21afb028d57494ff78761af81', + 'reclient_version': 'git_revision:29a9d3cb597b6a7d67fa3e9aa8a7cab1c81232ee', 'gcloud_version': 'version:2@444.0.0.chromium.3', @@ -277,7 +277,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'c5f0206b6fd70a85a41718f0bc6481b7c5348026', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'b55755acbe3d771f2c0f9da708af4bc9477b91d7', 'src/flutter/third_party/depot_tools': Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '580b4ff3f5cd0dcaa2eacda28cefe0f45320e8f7', @@ -946,7 +946,7 @@ deps = { 'packages': [ { 'package': 'flutter_internal/rbe/reclient_cfgs', - 'version': 'ZuU_165luvP45DTYUP6WlEXb5EXyVxEuAm45ZB1c3zoC', + 'version': 'XIomtC8MFuQrF9qI5xYcFfcfKXZTbcY6nL6NgF-pSRIC', } ], 'condition': 'use_rbe', diff --git a/tools/pkg/engine_build_configs/lib/src/build_config_runner.dart b/tools/pkg/engine_build_configs/lib/src/build_config_runner.dart index 39b0574cdc433..765b1fc3422e2 100644 --- a/tools/pkg/engine_build_configs/lib/src/build_config_runner.dart +++ b/tools/pkg/engine_build_configs/lib/src/build_config_runner.dart @@ -356,7 +356,7 @@ final class BuildRunner extends Runner { final List bootstrapCommand = [ bootstrapPath, '--re_proxy=$reproxyPath', - '--automatic_auth=true', + '--use_application_default_credentials', if (shutdown) '--shutdown' else ...['--cfg=$reclientConfigPath'], ]; if (!processRunner.processManager.canRun(bootstrapPath)) { From 4bac4a6c5fd79b13acbef396ac8e36bff4d302d1 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 8 May 2024 08:23:48 -0700 Subject: [PATCH 11/22] DisplayListBuilder internal reorganization with better rendering op overlap detection (#52646) This commit makes some long-needed internal improvements to the way that the DisplayListBuilder manages its per-save/saveLayer data. The information for layer bounds and matrix/clips is now maintained in the layer info structure itself rather than shared across a number of stack structures which required careful alignment of the 3 different stacks and made it more difficult to compare and update adjacent layers during save and restore operations. The new code stores all information for a layer within a single structure and the save and restore operations can be more clear about which information they are getting or setting in the current and parent layers. In addition, the layer bounds accumulations were updated to have a more flexible algorithm for detecting overlap of rendering operations for the opacity peephole optimization. Previously, more than one rendering op on a layer would prevent opacity peephole optimizations, but now the condition will be recognized until the first rendering op that overlaps the bounds of the previous rendering operations. This will help for some potentially common cases of 2 non-overlapping ops or even a list of rendering operations laid out in a row. --- ci/licenses_golden/licenses_flutter | 10 +- display_list/BUILD.gn | 5 +- .../benchmarking/dl_complexity_unittests.cc | 2 +- display_list/display_list_unittests.cc | 99 +- display_list/dl_builder.cc | 959 +++++++++++------- display_list/dl_builder.h | 321 +++--- display_list/dl_vertices.cc | 4 +- display_list/geometry/dl_geometry_types.h | 72 ++ display_list/utils/dl_accumulation_rect.cc | 67 ++ display_list/utils/dl_accumulation_rect.h | 54 + display_list/utils/dl_bounds_accumulator.cc | 134 --- display_list/utils/dl_bounds_accumulator.h | 168 --- display_list/utils/dl_matrix_clip_tracker.cc | 33 +- display_list/utils/dl_matrix_clip_tracker.h | 47 +- impeller/geometry/rect.h | 4 +- impeller/geometry/size.h | 4 +- 16 files changed, 1074 insertions(+), 909 deletions(-) create mode 100644 display_list/geometry/dl_geometry_types.h create mode 100644 display_list/utils/dl_accumulation_rect.cc create mode 100644 display_list/utils/dl_accumulation_rect.h delete mode 100644 display_list/utils/dl_bounds_accumulator.cc delete mode 100644 display_list/utils/dl_bounds_accumulator.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ac492bd5658fe..4cea8511e761b 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -39927,6 +39927,7 @@ ORIGIN: ../../../flutter/display_list/effects/dl_path_effect.cc + ../../../flutt ORIGIN: ../../../flutter/display_list/effects/dl_path_effect.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/effects/dl_runtime_effect.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/effects/dl_runtime_effect.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/geometry/dl_geometry_types.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/geometry/dl_region.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/geometry/dl_region.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/geometry/dl_rtree.cc + ../../../flutter/LICENSE @@ -39944,8 +39945,8 @@ ORIGIN: ../../../flutter/display_list/skia/dl_sk_dispatcher.h + ../../../flutter ORIGIN: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/skia/dl_sk_types.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/utils/dl_bounds_accumulator.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/utils/dl_bounds_accumulator.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/utils/dl_accumulation_rect.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/utils/dl_accumulation_rect.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/utils/dl_comparable.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.h + ../../../flutter/LICENSE @@ -42804,6 +42805,7 @@ FILE: ../../../flutter/display_list/effects/dl_path_effect.cc FILE: ../../../flutter/display_list/effects/dl_path_effect.h FILE: ../../../flutter/display_list/effects/dl_runtime_effect.cc FILE: ../../../flutter/display_list/effects/dl_runtime_effect.h +FILE: ../../../flutter/display_list/geometry/dl_geometry_types.h FILE: ../../../flutter/display_list/geometry/dl_region.cc FILE: ../../../flutter/display_list/geometry/dl_region.h FILE: ../../../flutter/display_list/geometry/dl_rtree.cc @@ -42821,8 +42823,8 @@ FILE: ../../../flutter/display_list/skia/dl_sk_dispatcher.h FILE: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.cc FILE: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.h FILE: ../../../flutter/display_list/skia/dl_sk_types.h -FILE: ../../../flutter/display_list/utils/dl_bounds_accumulator.cc -FILE: ../../../flutter/display_list/utils/dl_bounds_accumulator.h +FILE: ../../../flutter/display_list/utils/dl_accumulation_rect.cc +FILE: ../../../flutter/display_list/utils/dl_accumulation_rect.h FILE: ../../../flutter/display_list/utils/dl_comparable.h FILE: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.cc FILE: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.h diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 2bbb1e545e4db..dd6aba8d3814b 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -60,6 +60,7 @@ source_set("display_list") { "effects/dl_path_effect.h", "effects/dl_runtime_effect.cc", "effects/dl_runtime_effect.h", + "geometry/dl_geometry_types.h", "geometry/dl_region.cc", "geometry/dl_region.h", "geometry/dl_rtree.cc", @@ -77,8 +78,8 @@ source_set("display_list") { "skia/dl_sk_paint_dispatcher.cc", "skia/dl_sk_paint_dispatcher.h", "skia/dl_sk_types.h", - "utils/dl_bounds_accumulator.cc", - "utils/dl_bounds_accumulator.h", + "utils/dl_accumulation_rect.cc", + "utils/dl_accumulation_rect.h", "utils/dl_matrix_clip_tracker.cc", "utils/dl_matrix_clip_tracker.h", "utils/dl_receiver_utils.cc", diff --git a/display_list/benchmarking/dl_complexity_unittests.cc b/display_list/benchmarking/dl_complexity_unittests.cc index 3e86622a76591..511ba8b1f7972 100644 --- a/display_list/benchmarking/dl_complexity_unittests.cc +++ b/display_list/benchmarking/dl_complexity_unittests.cc @@ -102,7 +102,7 @@ TEST(DisplayListComplexity, StrokeWidth) { auto display_list_stroke_0 = builder_stroke_0.Build(); DisplayListBuilder builder_stroke_1; - builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), + builder_stroke_1.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), DlPaint().setStrokeWidth(1.0f)); auto display_list_stroke_1 = builder_stroke_1.Build(); diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index a149e75acdd38..20814eca265f4 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -532,13 +532,13 @@ TEST_F(DisplayListTest, UnclippedSaveLayerContentAccountsForFilter) { builder.Restore(); auto display_list = builder.Build(); - ASSERT_EQ(display_list->op_count(), 6u); + EXPECT_EQ(display_list->op_count(), 6u); EXPECT_EQ(display_list->total_depth(), 2u); SkRect result_rect = draw_rect.makeOutset(30.0f, 30.0f); ASSERT_TRUE(result_rect.intersect(clip_rect)); ASSERT_EQ(result_rect, SkRect::MakeLTRB(100.0f, 110.0f, 131.0f, 190.0f)); - ASSERT_EQ(display_list->bounds(), result_rect); + EXPECT_EQ(display_list->bounds(), result_rect); } TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) { @@ -565,13 +565,72 @@ TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) { builder.Restore(); auto display_list = builder.Build(); - ASSERT_EQ(display_list->op_count(), 6u); + EXPECT_EQ(display_list->op_count(), 6u); EXPECT_EQ(display_list->total_depth(), 2u); SkRect result_rect = draw_rect.makeOutset(30.0f, 30.0f); ASSERT_TRUE(result_rect.intersect(clip_rect)); ASSERT_EQ(result_rect, SkRect::MakeLTRB(100.0f, 110.0f, 129.0f, 190.0f)); - ASSERT_EQ(display_list->bounds(), result_rect); + EXPECT_EQ(display_list->bounds(), result_rect); +} + +TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithBlurFilter) { + SkRect cull_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f); + SkRect draw_rect = SkRect::MakeLTRB(25.0f, 25.0f, 99.0f, 75.0f); + auto filter = DlBlurImageFilter::Make(10.0f, 10.0f, DlTileMode::kDecal); + DlPaint layer_paint = DlPaint().setImageFilter(filter); + + // We want a draw rect that is outside the layer bounds even though its + // filtered output might be inside. The drawn rect should be culled by + // the expectations of the layer bounds even though it is close enough + // to be visible due to filtering. + ASSERT_FALSE(cull_rect.intersects(draw_rect)); + SkRect mapped_rect; + ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect)); + ASSERT_TRUE(mapped_rect.intersects(cull_rect)); + + DisplayListBuilder builder; + builder.SaveLayer(&cull_rect, &layer_paint); + { // + builder.DrawRect(draw_rect, DlPaint()); + } + builder.Restore(); + auto display_list = builder.Build(); + + EXPECT_EQ(display_list->op_count(), 2u); + EXPECT_EQ(display_list->total_depth(), 1u); + + EXPECT_TRUE(display_list->bounds().isEmpty()) << display_list->bounds(); +} + +TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithMatrixFilter) { + SkRect cull_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f); + SkRect draw_rect = SkRect::MakeLTRB(25.0f, 125.0f, 75.0f, 175.0f); + auto filter = DlMatrixImageFilter::Make(SkMatrix::Translate(100.0f, 0.0f), + DlImageSampling::kLinear); + DlPaint layer_paint = DlPaint().setImageFilter(filter); + + // We want a draw rect that is outside the layer bounds even though its + // filtered output might be inside. The drawn rect should be culled by + // the expectations of the layer bounds even though it is close enough + // to be visible due to filtering. + ASSERT_FALSE(cull_rect.intersects(draw_rect)); + SkRect mapped_rect; + ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect)); + ASSERT_TRUE(mapped_rect.intersects(cull_rect)); + + DisplayListBuilder builder; + builder.SaveLayer(&cull_rect, &layer_paint); + { // + builder.DrawRect(draw_rect, DlPaint()); + } + builder.Restore(); + auto display_list = builder.Build(); + + EXPECT_EQ(display_list->op_count(), 2u); + EXPECT_EQ(display_list->total_depth(), 1u); + + EXPECT_TRUE(display_list->bounds().isEmpty()) << display_list->bounds(); } TEST_F(DisplayListTest, SingleOpSizes) { @@ -1144,14 +1203,33 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); for (int i = 0; i < 10; i++) { - receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); } auto display_list = builder.Build(); EXPECT_FALSE(display_list->can_apply_group_opacity()); } +TEST_F(DisplayListTest, LineOfNonOverlappingOpsSupportGroupOpacity) { + DisplayListBuilder builder; + for (int i = 0; i < 10; i++) { + builder.DrawRect(SkRect::MakeXYWH(i * 30, 0, 30, 30), DlPaint()); + } + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + +TEST_F(DisplayListTest, CrossOfNonOverlappingOpsSupportGroupOpacity) { + DisplayListBuilder builder; + builder.DrawRect(SkRect::MakeLTRB(200, 200, 300, 300), DlPaint()); // center + builder.DrawRect(SkRect::MakeLTRB(100, 200, 200, 300), DlPaint()); // left + builder.DrawRect(SkRect::MakeLTRB(200, 100, 300, 200), DlPaint()); // above + builder.DrawRect(SkRect::MakeLTRB(300, 200, 400, 300), DlPaint()); // right + builder.DrawRect(SkRect::MakeLTRB(200, 300, 300, 400), DlPaint()); // below + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) { DisplayListBuilder builder; DlOpReceiver& receiver = ToReceiver(builder); @@ -1248,7 +1326,8 @@ class SaveLayerOptionsExpector : public virtual DlOpReceiver, void saveLayer(const SkRect& bounds, const SaveLayerOptions options, const DlImageFilter* backdrop) override { - EXPECT_EQ(options, expected_[save_layer_count_]); + EXPECT_EQ(options, expected_[save_layer_count_]) + << "index " << save_layer_count_; save_layer_count_++; } @@ -3858,7 +3937,7 @@ TEST_F(DisplayListTest, SaveContentDepthTest) { builder.Save(); // covers depth 1->9 { - builder.Translate(5, 5); + builder.Translate(5, 5); // triggers deferred save at depth 1 builder.DrawRect({10, 10, 20, 20}, DlPaint()); // depth 2 builder.DrawDisplayList(child, 1.0f); // depth 3 (content) + 4 (self) @@ -3868,12 +3947,12 @@ TEST_F(DisplayListTest, SaveContentDepthTest) { builder.DrawRect({12, 12, 22, 22}, DlPaint()); // depth 5 builder.DrawRect({14, 14, 24, 24}, DlPaint()); // depth 6 } - builder.Restore(); // layer is restored with depth 7 + builder.Restore(); // layer is restored with depth 6 builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 8 builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 9 } - builder.Restore(); + builder.Restore(); // save is restored with depth 9 builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 10 builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 11 diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index f417fa1bcb65e..c0edc341a0a3d 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -9,7 +9,7 @@ #include "flutter/display_list/dl_op_flags.h" #include "flutter/display_list/dl_op_records.h" #include "flutter/display_list/effects/dl_color_source.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/display_list/utils/dl_accumulation_rect.h" #include "fml/logging.h" #include "third_party/skia/include/core/SkScalar.h" @@ -65,7 +65,7 @@ void* DisplayListBuilder::Push(size_t pod, Args&&... args) { } sk_sp DisplayListBuilder::Build() { - while (layer_stack_.size() > 1) { + while (save_stack_.size() > 1) { restore(); } @@ -74,42 +74,67 @@ sk_sp DisplayListBuilder::Build() { size_t nested_bytes = nested_bytes_; int nested_count = nested_op_count_; uint32_t total_depth = depth_; - bool compatible = current_layer_->is_group_opacity_compatible(); + bool compatible = current_info().is_group_opacity_compatible(); bool is_safe = is_ui_thread_safe_; - bool affects_transparency = current_layer_->affects_transparent_layer(); - - sk_sp rtree = this->rtree(); - SkRect bounds = rtree ? rtree->bounds() : this->bounds(); + bool affects_transparency = current_info().affects_transparent_layer; + + sk_sp rtree; + SkRect bounds; + if (rtree_data_.has_value()) { + auto& rects = rtree_data_->rects; + auto& indices = rtree_data_->indices; + rtree = sk_make_sp(rects.data(), rects.size(), indices.data(), + [](int id) { return id >= 0; }); + // RTree bounds may be tighter due to applying filter bounds + // adjustments to each op as we restore layers rather than to + // the entire layer bounds. + bounds = rtree->bounds(); + rtree_data_.reset(); + } else { + bounds = current_info().global_space_accumulator->bounds(); + } used_ = allocated_ = render_op_count_ = op_index_ = 0; nested_bytes_ = nested_op_count_ = 0; depth_ = 0; is_ui_thread_safe_ = true; - storage_.realloc(bytes); - layer_stack_.pop_back(); - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); - tracker_.reset(); - layer_tracker_.reset(); + current_opacity_compatibility_ = true; + render_op_depth_cost_ = 1u; current_ = DlPaint(); + save_stack_.pop_back(); + save_stack_.emplace_back(original_cull_rect_); + current_info().is_nop = original_cull_rect_.IsEmpty(); + if (rtree) { + rtree_data_.emplace(); + } else { + current_info().global_space_accumulator.reset(new AccumulationRect()); + } + + storage_.realloc(bytes); return sk_sp( new DisplayList(std::move(storage_), bytes, count, nested_bytes, nested_count, total_depth, bounds, compatible, is_safe, affects_transparency, std::move(rtree))); } +static constexpr DlRect kEmpty = DlRect(); + +static const DlRect& ProtectEmpty(const SkRect& rect) { + // isEmpty protects us against NaN while we normalize any empty cull rects + return rect.isEmpty() ? kEmpty : ToDlRect(rect); +} + DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, bool prepare_rtree) - : tracker_(cull_rect, SkMatrix::I()) { + : original_cull_rect_(ProtectEmpty(cull_rect)) { + save_stack_.emplace_back(original_cull_rect_); + current_info().is_nop = original_cull_rect_.IsEmpty(); if (prepare_rtree) { - accumulator_ = std::make_unique(); + rtree_data_.emplace(); } else { - accumulator_ = std::make_unique(); + current_info().global_space_accumulator.reset(new AccumulationRect()); } - - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); } DisplayListBuilder::~DisplayListBuilder() { @@ -120,7 +145,7 @@ DisplayListBuilder::~DisplayListBuilder() { } SkISize DisplayListBuilder::GetBaseLayerSize() const { - return tracker_.base_device_cull_rect().roundOut().size(); + return ToSkISize(DlIRect::RoundOut(original_cull_rect_).GetSize()); } SkImageInfo DisplayListBuilder::GetImageInfo() const { @@ -388,193 +413,24 @@ void DisplayListBuilder::SetAttributesFromPaint( } void DisplayListBuilder::checkForDeferredSave() { - if (current_layer_->has_deferred_save_op_) { - size_t save_offset_ = used_; + if (current_info().has_deferred_save_op) { + size_t save_offset = used_; Push(0); - current_layer_->save_offset_ = save_offset_; - current_layer_->start_depth_ = depth_; - current_layer_->has_deferred_save_op_ = false; + current_info().save_offset = save_offset; + current_info().save_depth = depth_; + current_info().has_deferred_save_op = false; } } void DisplayListBuilder::Save() { - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); - - FML_DCHECK(layer_stack_.size() >= 2u); - // Note we can't use the previous value of current_layer_ because - // the emplace_back() may have moved the storage locations, so we - // recompute the location of the penultimate layer info here. - auto parent_layer = &layer_stack_.end()[-2]; - - current_layer_->has_deferred_save_op_ = true; - current_layer_->is_nop_ = parent_layer->is_nop_; - - if (parent_layer->layer_accumulator_) { - FML_DCHECK(layer_tracker_); - // If the previous layer was using an accumulator, we need to keep - // filling it with content bounds. We reuse the previous accumulator - // for this layer, but clone the associated transform so that new - // transform mutations are restricted to this save/restore context. - current_layer_->layer_accumulator_ = parent_layer->layer_accumulator_; - layer_tracker_->save(); - } else { - FML_DCHECK(!layer_tracker_); - } + bool was_nop = current_info().is_nop; + save_stack_.emplace_back(¤t_info()); + current_info().is_nop = was_nop; - tracker_.save(); - accumulator()->save(); + FML_DCHECK(save_stack_.size() >= 2u); + FML_DCHECK(current_info().has_deferred_save_op); } -void DisplayListBuilder::Restore() { - if (layer_stack_.size() <= 1) { - return; - } - - SaveOpBase* op = reinterpret_cast(storage_.get() + - current_layer_->save_offset()); - - if (!current_layer_->has_deferred_save_op_) { - op->restore_index = op_index_; - op->total_content_depth = depth_ - current_layer_->start_depth_; - Push(0); - if (current_layer_->is_save_layer()) { - // A saveLayer will usually do a final copy to the main buffer in - // addition to its content, but that is accounted for outside of - // the total content depth computed above. - depth_ += render_op_depth_cost_; - } - } - - std::shared_ptr filter = current_layer_->filter(); - { - // We should not pop the stack until we are done synching up the current - // and parent layers. - auto parent_layer = &layer_stack_.end()[-2]; - - if (current_layer_->is_save_layer()) { - // Layers are never deferred for now, we need to update the - // following code if we ever do saveLayer culling... - FML_DCHECK(!current_layer_->has_deferred_save_op_); - FML_DCHECK(current_layer_->layer_accumulator_); - - SkRect content_bounds = current_layer_->layer_accumulator_->bounds(); - - switch (op->type) { - case DisplayListOpType::kSaveLayer: - case DisplayListOpType::kSaveLayerBackdrop: { - SaveLayerOpBase* layer_op = reinterpret_cast(op); - if (op->options.bounds_from_caller()) { - if (!content_bounds.isEmpty() && - !layer_op->rect.contains(content_bounds)) { - op->options = op->options.with_content_is_clipped(); - content_bounds.intersect(layer_op->rect); - } - } - layer_op->rect = content_bounds; - break; - } - default: - FML_UNREACHABLE(); - } - - if (layer_tracker_->getSaveCount() > 1) { - layer_tracker_->restore(); - } else { - // If this was the last layer in the tracker, then there should - // be no parent saveLayer. - FML_DCHECK(!parent_layer->layer_accumulator_); - layer_tracker_.reset(); - } - - if (parent_layer->layer_accumulator_) { - SkRect bounds_for_parent = content_bounds; - if (filter) { - if (!filter->map_local_bounds(bounds_for_parent, bounds_for_parent)) { - parent_layer->set_unbounded(); - } - } - // The content_bounds were accumulated in the base coordinate system - // of the current layer, and have been adjusted there according to - // its image filter. - // The content bounds accumulation of the parent layer is relative - // to the parent's base coordinate system, so we need to adjust - // bounds_for_parent to that coordinate space. - FML_DCHECK(layer_tracker_); - layer_tracker_->mapRect(&bounds_for_parent); - parent_layer->layer_accumulator_->accumulate(bounds_for_parent); - } - - if (current_layer_->is_group_opacity_compatible()) { - // We are now going to go back and modify the matching saveLayer - // call to add the option indicating it can distribute an opacity - // value to its children. - // - // Note that this operation cannot and does not change the size - // or structure of the SaveLayerOp record. It only sets an option - // flag on an existing field. - // - // Note that these kinds of modification operations on data already - // in the DisplayList are only allowed *during* the build phase. - // Once built, the DisplayList records must remain read only to - // ensure consistency of rendering and |Equals()| behavior. - op->options = op->options.with_can_distribute_opacity(); - } - } else { - if (layer_tracker_) { - FML_DCHECK(layer_tracker_->getSaveCount() > 1); - layer_tracker_->restore(); - } - // For regular save() ops there was no protecting layer so we have to - // accumulate the inheritance properties into the enclosing layer. - if (current_layer_->cannot_inherit_opacity()) { - parent_layer->mark_incompatible(); - } else if (current_layer_->has_compatible_op()) { - parent_layer->add_compatible_op(); - } - } - } - - // Remember whether the outgoing layer was unbounded so we can adjust - // for it below after we apply the outgoing layer's filter to the bounds. - bool popped_was_unbounded = current_layer_->is_unbounded(); - - // parent_layer is no longer in scope, time to pop the layer. - layer_stack_.pop_back(); - tracker_.restore(); - current_layer_ = &layer_stack_.back(); - - // As we pop the accumulator, use the filter that was applied to the - // outgoing layer (saved above, if any) to adjust the bounds that - // were accumulated while that layer was active. - if (filter) { - const SkRect clip = tracker_.device_cull_rect(); - if (!accumulator()->restore( - [filter = filter, matrix = GetTransform()](const SkRect& input, - SkRect& output) { - SkIRect output_bounds; - bool ret = filter->map_device_bounds(input.roundOut(), matrix, - output_bounds); - output.set(output_bounds); - return ret; - }, - &clip)) { - popped_was_unbounded = true; - } - } else { - accumulator()->restore(); - } - - if (popped_was_unbounded) { - AccumulateUnbounded(); - } -} -void DisplayListBuilder::RestoreToCount(int restore_count) { - FML_DCHECK(restore_count <= GetSaveCount()); - while (restore_count < GetSaveCount() && GetSaveCount() > 1) { - restore(); - } -} void DisplayListBuilder::saveLayer(const SkRect& bounds, const SaveLayerOptions in_options, const DlImageFilter* backdrop) { @@ -584,75 +440,122 @@ void DisplayListBuilder::saveLayer(const SkRect& bounds, : kSaveLayerFlags; OpResult result = PaintResult(current_, flags); if (result == OpResult::kNoEffect) { - save(); - current_layer_->is_nop_ = true; + // If we can't render, whether because we were already in a no-render + // state from the parent or because our own attributes make us a nop, + // we can just simplify this whole layer to a regular save that has + // nop state. We need to have a SaveInfo for the eventual restore(), + // but no rendering ops should be accepted between now and then so + // it doesn't need any of the data associated with a layer SaveInfo. + Save(); + current_info().is_nop = true; return; } - size_t save_layer_offset = used_; + // Snapshot these values before we do any work as we need the values + // from before the method was called, but some of the operations below + // might update them. + size_t save_offset = used_; + uint32_t save_depth = depth_; + + // A backdrop will affect up to the entire surface, bounded by the clip + bool will_be_unbounded = (backdrop != nullptr); + std::shared_ptr filter; if (options.renders_with_attributes()) { - // The actual flood of the outer layer clip will occur after the - // (eventual) corresponding restore is called, but rather than - // remember this information in the LayerInfo until the restore - // method is processed, we just mark the unbounded state up front. - // Another reason to accumulate the clip here rather than in - // restore is so that this savelayer will be tagged in the rtree - // with its full bounds and the right op_index so that it doesn't - // get culled during rendering. if (!paint_nops_on_transparency()) { // We will fill the clip of the outer layer when we restore. - // Accumulate should always return true here because if the - // clip was empty then that would have been caught up above - // when we tested the PaintResult. - [[maybe_unused]] bool unclipped = AccumulateUnbounded(); - FML_DCHECK(unclipped); + will_be_unbounded = true; } + filter = current_.getImageFilter(); CheckLayerOpacityCompatibility(true); - layer_stack_.emplace_back(save_layer_offset, depth_); - layer_stack_.back().filter_ = current_.getImageFilter(); } else { CheckLayerOpacityCompatibility(false); - layer_stack_.emplace_back(save_layer_offset, depth_); } - current_layer_ = &layer_stack_.back(); - current_layer_->is_save_layer_ = true; - tracker_.save(); - accumulator()->save(); + // The actual flood of the outer layer clip will occur after the + // (eventual) corresponding restore is called, but rather than + // remember this information in the LayerInfo until the restore + // method is processed, we just mark the unbounded state up front. + // Another reason to accumulate the clip here rather than in + // restore is so that this savelayer will be tagged in the rtree + // with its full bounds and the right op_index so that it doesn't + // get culled during rendering. + if (will_be_unbounded) { + // Accumulate should always return true here because if the + // clip was empty then that would have been caught up above + // when we tested the PaintResult. + [[maybe_unused]] bool unclipped = AccumulateUnbounded(); + FML_DCHECK(unclipped); + } - SkRect record_bounds; - if (in_options.bounds_from_caller()) { - options = options.with_bounds_from_caller(); - record_bounds = bounds; - } else { - FML_DCHECK(record_bounds.isEmpty()); + // Accumulate information for the SaveInfo we are about to push onto the + // stack. + { + size_t rtree_index = + rtree_data_.has_value() ? rtree_data_->rects.size() : 0u; + + save_stack_.emplace_back(¤t_info(), filter, rtree_index); + current_info().is_nop = false; + FML_DCHECK(!current_info().has_deferred_save_op); + current_info().save_offset = save_offset; + current_info().save_depth = save_depth; + + if (filter && !rtree_data_.has_value()) { + // By default the new SaveInfo shares the global accumulation rect with + // the parent layer and will only have one if the rtree_data is not + // being accumulated. + // + // But, if we have a filter and we are not accumulating rtree data, + // then we'll need to adjust all of the bounds accumulated via this + // new layer by the filter so we need to use a separate global + // accumulation rect for this layer and adjust it during RestoreLayer() + // before accumulating it into the parent layer. + current_info().global_space_accumulator.reset(new AccumulationRect()); + } + + // If we inherit some culling bounds and we have a filter then we need + // to adjust them so that we cull for the correct input space for the + // output of the filter. + if (filter) { + SkRect outer_cull_rect = current_info().global_state.local_cull_rect(); + + SkIRect output_bounds = outer_cull_rect.roundOut(); + SkIRect input_bounds; + if (filter->get_input_device_bounds(output_bounds, SkMatrix::I(), + input_bounds)) { + current_info().global_state.resetLocalCullRect( + SkRect::Make(input_bounds)); + } else { + // Filter could not make any promises about the bounds it needs to + // fill the output space, so we use a maximal rect to accumulate + // the layer bounds. + current_info().global_state.resetDeviceCullRect(kMaxCullRect); + } + } + + // We always want to cull based on user provided bounds, though, as + // that is legacy behavior even if it doesn't always work precisely + // in a rotated or skewed coordinate system. + if (in_options.bounds_from_caller()) { + current_info().global_state.clipRect(bounds, ClipOp::kIntersect, false); + } } - current_layer_->layer_accumulator_.reset(new RectBoundsAccumulator()); - if (layer_tracker_) { - layer_tracker_->save(); - layer_tracker_->setTransform(SkMatrix::I()); - } else { - SkRect cull_rect; + + // Accumulate options to store in the SaveLayer op record. + { + SkRect record_bounds; if (in_options.bounds_from_caller()) { - cull_rect = bounds; + options = options.with_bounds_from_caller(); + record_bounds = bounds; } else { - cull_rect = tracker_.local_cull_rect(); + FML_DCHECK(record_bounds.isEmpty()); } - layer_tracker_.reset( - new DisplayListMatrixClipTracker(cull_rect, SkMatrix::I())); - } - if (backdrop) { - // A backdrop will affect up to the entire surface, bounded by the clip - // Accumulate should always return true here because if the - // clip was empty then that would have been caught up above - // when we tested the PaintResult. - [[maybe_unused]] bool unclipped = AccumulateUnbounded(); - FML_DCHECK(unclipped); - Push(0, options, record_bounds, backdrop); - } else { - Push(0, options, record_bounds); + if (backdrop) { + Push(0, options, record_bounds, backdrop); + } else { + Push(0, options, record_bounds); + } } if (options.renders_with_attributes()) { @@ -660,32 +563,12 @@ void DisplayListBuilder::saveLayer(const SkRect& bounds, // account because an individual primitive with an ImageFilter can apply // opacity on top of it. But, if the layer is applying the ImageFilter // then it cannot pass the opacity on. - if (!current_opacity_compatibility_ || - current_.getImageFilter() != nullptr) { + if (!current_opacity_compatibility_ || filter) { UpdateLayerOpacityCompatibility(false); } } + // REMIND: NEEDED? UpdateLayerResult(result); - - if (options.renders_with_attributes() && current_.getImageFilter()) { - // We use |resetCullRect| here because we will be accumulating bounds of - // primitives before applying the filter to those bounds. We might - // encounter a primitive whose bounds are clipped, but whose filtered - // bounds will not be clipped. If the individual rendering ops bounds - // are clipped, it will not contribute to the overall bounds which - // could lead to inaccurate (subset) bounds of the DisplayList. - // We need to reset the cull rect here to avoid this premature clipping. - // The filtered bounds will be clipped to the existing clip rect when - // this layer is restored. - // If bounds is null then the original cull_rect will be used. - tracker_.resetLocalCullRect(in_options.bounds_from_caller() ? &bounds - : nullptr); - } else if (in_options.bounds_from_caller()) { - // Even though Skia claims that the bounds are only a hint, they actually - // use them as the temporary layer bounds during rendering the layer, so - // we set them as if a clip operation were performed. - tracker_.clipRect(bounds, ClipOp::kIntersect, false); - } } void DisplayListBuilder::SaveLayer(const SkRect* bounds, const DlPaint* paint, @@ -706,44 +589,300 @@ void DisplayListBuilder::SaveLayer(const SkRect* bounds, saveLayer(temp_bounds, options, backdrop); } +void DisplayListBuilder::Restore() { + if (save_stack_.size() <= 1) { + return; + } + + { + // The current_info will have a lifetime that does not extend past the + // pop_back() method below. + auto& current_info = this->current_info(); + + if (!current_info.has_deferred_save_op) { + SaveOpBase* op = reinterpret_cast(storage_.get() + + current_info.save_offset); + FML_DCHECK(op->type == DisplayListOpType::kSave || + op->type == DisplayListOpType::kSaveLayer || + op->type == DisplayListOpType::kSaveLayerBackdrop); + + op->restore_index = op_index_; + op->total_content_depth = depth_ - current_info.save_depth; + + Push(0); + + if (current_info.is_save_layer) { + RestoreLayer(current_info, parent_info(), op); + } else { + // We only propagate these values through a regular save() + if (current_info.opacity_incompatible_op_detected) { + parent_info().opacity_incompatible_op_detected = true; + } + } + } else { + FML_DCHECK(!current_info.is_save_layer); + } + } + + save_stack_.pop_back(); +} + +void DisplayListBuilder::RestoreLayer(const SaveInfo& current_info, + SaveInfo& parent_info, + void* base_op) { + FML_DCHECK(save_stack_.size() > 1); + FML_DCHECK(!current_info.has_deferred_save_op); + + // A saveLayer will usually do a final copy to the main buffer in + // addition to its content, but that is accounted for outside of + // the total content depth computed above in Restore. + depth_ += render_op_depth_cost_; + + SkRect content_bounds = current_info.layer_local_accumulator->bounds(); + + SaveLayerOpBase* layer_op = reinterpret_cast(base_op); + FML_DCHECK(layer_op->type == DisplayListOpType::kSaveLayer || + layer_op->type == DisplayListOpType::kSaveLayerBackdrop); + + switch (layer_op->type) { + case DisplayListOpType::kSaveLayer: + case DisplayListOpType::kSaveLayerBackdrop: { + if (layer_op->options.bounds_from_caller()) { + if (!content_bounds.isEmpty() && + !layer_op->rect.contains(content_bounds)) { + layer_op->options = layer_op->options.with_content_is_clipped(); + content_bounds.intersect(layer_op->rect); + } + } + layer_op->rect = content_bounds; + break; + } + default: + FML_UNREACHABLE(); + } + + if (current_info.is_group_opacity_compatible()) { + // We are now going to go back and modify the matching saveLayer + // call to add the option indicating it can distribute an opacity + // value to its children. + layer_op->options = layer_op->options.with_can_distribute_opacity(); + } + + TransferLayerBounds(current_info, parent_info, content_bounds); +} + +// There are a few different conditions and corresponding operations to +// consider when transferring bounds from one layer to another. The current +// layer will have accumulated its bounds into 2 potential places: +// +// - Its own private layer local bounds, which were potentially clipped by +// the supplied bounds and passed here as the content_bounds. +// +// - Either the rtree rect list, or the global space accumulator, one or +// the other. +// +// If there is no filter then the private layer bounds are complete and +// they simply need to be passed along to the parent into its layer local +// accumulator. Also, if there was no filter then the existing bounds +// recorded in either the rtree rects or the layer's global space accumulator +// (shared with its parent) need no updating so no global space transfer +// has to occur. +// +// If there is a filter then the global content bounds will need to be +// adjusted in one of two ways (rtree vs non-rtree): +// +// - If we are accumulating rtree rects then each of the rects accumulated +// during this current layer will need to be updated by the filter in the +// global coordinate space in which they were accumulated. In this mode +// we should never have a global space accumulator on the layer. +// +// - Otherwise we were accumulating global bounds into our own private +// global space accumulator which need to be adjusted in the global space +// coordinate system by the filter. +// +// Finally, we will have to adjust the layer's content bounds by the filter +// and accumulate those into the parent layer's local bounds. +void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, + SaveInfo& parent_info, + const SkRect& content_bounds) { + auto& filter = current_info.filter; + + if (!filter) { + // One or the other of the rtree data or the global space accumulator + // must be non-null, and the other must be null. + FML_DCHECK(rtree_data_.has_value() != + static_cast(current_info.global_space_accumulator)); + + // The current and parent global space accumulators either must both be + // null, or they must both point to the same accumulator. + FML_DCHECK(current_info.global_space_accumulator.get() == + parent_info.global_space_accumulator.get()); + + // If we have no filter then the global bounds were already accumulated + // into the parent's global accumulator, but we need to update the local + // bounds of the parent for the results of the saveLayer call. + parent_info.AccumulateBoundsLocal(content_bounds); + return; + } + + bool parent_is_flooded = false; + SkRect bounds_for_parent = content_bounds; + + // First, let's adjust or transfer the global bounds. + + if (rtree_data_.has_value()) { + // Neither current or parent layer should have a global space accumulator + FML_DCHECK(!current_info.global_space_accumulator); + FML_DCHECK(!parent_info.global_space_accumulator); + + // The rtree rects were accumulated without the bounds modification of + // the filter applied to the layer so they may fail to trigger on a + // culled dispatch if their filter "fringes" are in the dispatch scope + // but their base rendering bounds are not. (Also, they will not + // contribute fully when we compute the overall bounds of this DL.) + // + // To make sure they are rendered in the culled dispatch situation, we + // revisit all of the RTree rects accumulated during the current layer + // (indicated by rtree_rects_start_index) and expand them by the filter. + + // Matrix and Clip are the global values from just before our saveLayer + // and should still be the current values present in the parent layer. + const SkRect clip = parent_info.global_state.device_cull_rect(); + const SkMatrix matrix = parent_info.global_state.matrix_3x3(); + + // Starting rect index was snapshotted to this layer's data during + // saveLayer. + auto rect_start_index = current_info.rtree_rects_start_index; + + if (AdjustRTreeRects(rtree_data_.value(), *filter, matrix, clip, + rect_start_index)) { + parent_is_flooded = true; + } + } else { + // Both current or parent layer should have a global space accumulator + FML_DCHECK(current_info.global_space_accumulator); + FML_DCHECK(parent_info.global_space_accumulator); + + // And they should not be the same accumulator + FML_DCHECK(current_info.global_space_accumulator.get() != + parent_info.global_space_accumulator.get()); + + SkRect global_bounds = current_info.global_space_accumulator->bounds(); + if (!global_bounds.isEmpty()) { + SkIRect global_ibounds = global_bounds.roundOut(); + if (!filter->map_device_bounds(global_ibounds, + parent_info.global_state.matrix_3x3(), + global_ibounds)) { + parent_is_flooded = true; + } else { + global_bounds.set(global_ibounds); + const SkRect clip = parent_info.global_state.device_cull_rect(); + if (global_bounds.intersect(clip)) { + parent_info.global_space_accumulator->accumulate(global_bounds); + } + } + } + } + + // Now we visit the layer bounds which are in the layer's local coordinate + // system must be accumulated into the parent layer's bounds while + // adjusting them by the layer's local coordinate system (handled by the + // Accumulate() methods). + + // A filter will happily adjust empty bounds to be non-empty, so we + // specifically avoid that case here. Also, if we are already planning + // to flood the parent due to any of the cases above, we don't need to + // run the filter on the content bounds only to discover the same + // condition. + if (!parent_is_flooded && !bounds_for_parent.isEmpty()) { + if (!filter->map_local_bounds(bounds_for_parent, bounds_for_parent)) { + parent_is_flooded = true; + } + } + + if (parent_is_flooded) { + // All of the above computations deferred the flooded parent status + // to here. We need to mark the parent as flooded in both its layer + // and global accumulators. Note that even though the rtree rects + // were expanded to the size of the clip above, this method will still + // add one more rect to the rtree with the op index of the restore + // command to prevent the saveLayer itself from being elided in the + // rare case that there are no rendering ops in it, or somehow none + // of them were chosen by the rtree search (unlikely). The saveLayer + // must be processed for the parent flood to happen. + AccumulateUnbounded(parent_info); + } else { + parent_info.AccumulateBoundsLocal(bounds_for_parent); + } +} + +bool DisplayListBuilder::AdjustRTreeRects(RTreeData& data, + const DlImageFilter& filter, + const SkMatrix& matrix, + const SkRect& clip, + size_t rect_start_index) { + auto& rects = data.rects; + auto& indices = data.indices; + FML_DCHECK(rects.size() == indices.size()); + int ret = false; + auto rect_keep = rect_start_index; + for (size_t i = rect_start_index; i < rects.size(); i++) { + SkRect bounds = rects[i]; + SkIRect ibounds; + if (filter.map_device_bounds(bounds.roundOut(), matrix, ibounds)) { + bounds.set(ibounds); + } else { + bounds = clip; + ret = true; + } + if (bounds.intersect(clip)) { + indices[rect_keep] = indices[i]; + rects[rect_keep] = bounds; + rect_keep++; + } + } + indices.resize(rect_keep); + rects.resize(rect_keep); + return ret; +} + +void DisplayListBuilder::RestoreToCount(int restore_count) { + FML_DCHECK(restore_count <= GetSaveCount()); + while (restore_count < GetSaveCount() && GetSaveCount() > 1) { + restore(); + } +} + void DisplayListBuilder::Translate(SkScalar tx, SkScalar ty) { if (std::isfinite(tx) && std::isfinite(ty) && (tx != 0.0 || ty != 0.0)) { checkForDeferredSave(); Push(0, tx, ty); - tracker_.translate(tx, ty); - if (layer_tracker_) { - layer_tracker_->translate(tx, ty); - } + global_state().translate(tx, ty); + layer_local_state().translate(tx, ty); } } void DisplayListBuilder::Scale(SkScalar sx, SkScalar sy) { if (std::isfinite(sx) && std::isfinite(sy) && (sx != 1.0 || sy != 1.0)) { checkForDeferredSave(); Push(0, sx, sy); - tracker_.scale(sx, sy); - if (layer_tracker_) { - layer_tracker_->scale(sx, sy); - } + global_state().scale(sx, sy); + layer_local_state().scale(sx, sy); } } void DisplayListBuilder::Rotate(SkScalar degrees) { if (SkScalarMod(degrees, 360.0) != 0.0) { checkForDeferredSave(); Push(0, degrees); - tracker_.rotate(degrees); - if (layer_tracker_) { - layer_tracker_->rotate(degrees); - } + global_state().rotate(degrees); + layer_local_state().rotate(degrees); } } void DisplayListBuilder::Skew(SkScalar sx, SkScalar sy) { if (std::isfinite(sx) && std::isfinite(sy) && (sx != 0.0 || sy != 0.0)) { checkForDeferredSave(); Push(0, sx, sy); - tracker_.skew(sx, sy); - if (layer_tracker_) { - layer_tracker_->skew(sx, sy); - } + global_state().skew(sx, sy); + layer_local_state().skew(sx, sy); } } @@ -764,12 +903,10 @@ void DisplayListBuilder::Transform2DAffine( Push(0, mxx, mxy, mxt, myx, myy, myt); - tracker_.transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - if (layer_tracker_) { - layer_tracker_->transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - } + global_state().transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + layer_local_state().transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); } } } @@ -799,44 +936,42 @@ void DisplayListBuilder::TransformFullPerspective( myx, myy, myz, myt, mzx, mzy, mzz, mzt, mwx, mwy, mwz, mwt); - tracker_.transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - if (layer_tracker_) { - layer_tracker_->transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - } + global_state().transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + layer_local_state().transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); } } // clang-format on void DisplayListBuilder::TransformReset() { checkForDeferredSave(); Push(0); - if (layer_tracker_) { - // The matrices in layer_tracker_ and tracker_ are similar, but - // start at a different base transform. The tracker_ potentially - // has some number of transform operations on it that prefix the - // operations accumulated in layer_tracker_. So we can't set them both - // to identity in parallel as they would no longer maintain their - // relationship to each other. - // Instead we reinterpret this operation as transforming by the - // inverse of the current transform. Doing so to tracker_ sets it - // to identity so we can avoid the math there, but we must do the - // math the long way for layer_tracker_. This becomes: - // layer_tracker_.transform(tracker_.inverse()); - if (!layer_tracker_->inverseTransform(tracker_)) { - // If the inverse operation failed then that means that either - // the matrix above the current layer was singular, or the matrix - // became singular while we were accumulating the current layer. - // In either case, we should no longer be accumulating any - // contents so we set the layer tracking transform to a singular one. - layer_tracker_->setTransform(SkMatrix::Scale(0.0f, 0.0f)); - } - } - tracker_.setIdentity(); + + // The matrices in layer_tracker_ and tracker_ are similar, but + // start at a different base transform. The tracker_ potentially + // has some number of transform operations on it that prefix the + // operations accumulated in layer_tracker_. So we can't set them both + // to identity in parallel as they would no longer maintain their + // relationship to each other. + // Instead we reinterpret this operation as transforming by the + // inverse of the current transform. Doing so to tracker_ sets it + // to identity so we can avoid the math there, but we must do the + // math the long way for layer_tracker_. This becomes: + // layer_tracker_.transform(tracker_.inverse()); + if (!layer_local_state().inverseTransform(global_state())) { + // If the inverse operation failed then that means that either + // the matrix above the current layer was singular, or the matrix + // became singular while we were accumulating the current layer. + // In either case, we should no longer be accumulating any + // contents so we set the layer tracking transform to a singular one. + layer_local_state().setTransform(SkMatrix::Scale(0.0f, 0.0f)); + } + + global_state().setIdentity(); } void DisplayListBuilder::Transform(const SkMatrix* matrix) { if (matrix != nullptr) { @@ -859,11 +994,13 @@ void DisplayListBuilder::ClipRect(const SkRect& rect, if (!rect.isFinite()) { return; } - tracker_.clipRect(rect, clip_op, is_aa); - if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { - current_layer_->is_nop_ = true; + global_state().clipRect(rect, clip_op, is_aa); + if (current_info().is_nop || + current_info().global_state.is_cull_rect_empty()) { + current_info().is_nop = true; return; } + layer_local_state().clipRect(rect, clip_op, is_aa); checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: @@ -880,11 +1017,13 @@ void DisplayListBuilder::ClipRRect(const SkRRect& rrect, if (rrect.isRect()) { clipRect(rrect.rect(), clip_op, is_aa); } else { - tracker_.clipRRect(rrect, clip_op, is_aa); - if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { - current_layer_->is_nop_ = true; + global_state().clipRRect(rrect, clip_op, is_aa); + if (current_info().is_nop || + current_info().global_state.is_cull_rect_empty()) { + current_info().is_nop = true; return; } + layer_local_state().clipRRect(rrect, clip_op, is_aa); checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: @@ -916,11 +1055,13 @@ void DisplayListBuilder::ClipPath(const SkPath& path, return; } } - tracker_.clipPath(path, clip_op, is_aa); - if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { - current_layer_->is_nop_ = true; + global_state().clipPath(path, clip_op, is_aa); + if (current_info().is_nop || + current_info().global_state.is_cull_rect_empty()) { + current_info().is_nop = true; return; } + layer_local_state().clipPath(path, clip_op, is_aa); checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: @@ -933,7 +1074,7 @@ void DisplayListBuilder::ClipPath(const SkPath& path, } bool DisplayListBuilder::QuickReject(const SkRect& bounds) const { - return tracker_.content_culled(bounds); + return global_state().content_culled(bounds); } void DisplayListBuilder::drawPaint() { @@ -1135,11 +1276,11 @@ void DisplayListBuilder::drawPoints(PointMode mode, FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); int bytes = count * sizeof(SkPoint); - RectBoundsAccumulator ptBounds; + AccumulationRect accumulator; for (size_t i = 0; i < count; i++) { - ptBounds.accumulate(pts[i]); + accumulator.accumulate(pts[i]); } - SkRect point_bounds = ptBounds.bounds(); + SkRect point_bounds = accumulator.bounds(); if (!AccumulateOpBounds(point_bounds, flags)) { return; } @@ -1165,7 +1306,12 @@ void DisplayListBuilder::drawPoints(PointMode mode, // distribution of group opacity without analyzing the mode and the // bounds of every sub-primitive. // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c - UpdateLayerOpacityCompatibility(false); + current_info().layer_local_accumulator->record_overlapping_bounds(); + // Even though we've eliminated the possibility of opacity peephole + // optimizations above, we still set the appropriate flags based on + // the rendering attributes in case we solve the overlapping points + // problem above. + CheckLayerOpacityCompatibility(); UpdateLayerResult(result); } void DisplayListBuilder::DrawPoints(PointMode mode, @@ -1189,6 +1335,15 @@ void DisplayListBuilder::drawVertices(const DlVertices* vertices, // cases. UpdateLayerOpacityCompatibility(false); UpdateLayerResult(result); + // Even though we already eliminated opacity peephole optimization + // due to the color issues identified above, drawVertices also fails + // based on the fact that the vertices are rendered independently + // so we cannot guarantee the non-overlapping condition. We record + // both conditions in case a solution is found to applying the + // colors above - both conditions must be analyzed sufficiently + // and implemented accordingly before drawVertices is compatible with + // opacity peephole optimizations. + current_info().layer_local_accumulator->record_overlapping_bounds(); } } void DisplayListBuilder::DrawVertices(const DlVertices* vertices, @@ -1312,18 +1467,31 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, return; } SkPoint quad[4]; - RectBoundsAccumulator atlasBounds; + AccumulationRect accumulator; for (int i = 0; i < count; i++) { const SkRect& src = tex[i]; xform[i].toQuad(src.width(), src.height(), quad); for (int j = 0; j < 4; j++) { - atlasBounds.accumulate(quad[j]); + accumulator.accumulate(quad[j]); } } - if (atlasBounds.is_empty() || - !AccumulateOpBounds(atlasBounds.bounds(), flags)) { + if (accumulator.is_empty() || + !AccumulateOpBounds(accumulator.bounds(), flags)) { return; } + // Accumulating the bounds might not trip the overlap condition if the + // whole atlas operation is separated from other rendering calls, but + // since each atlas op is treated as an independent operation, we have + // to pass along our locally computed overlap condition for the individual + // atlas operations to the layer accumulator. + // Note that the above accumulation may falsely trigger the overlapping + // state as it is done quad corner by quad corner and an entire quad may + // be non-overlapping with the layer bounds, but as we add each point + // independently it might expand the bounds on one corner and then flag + // the condition when the next corner is added. + if (accumulator.overlap_detected()) { + current_info().layer_local_accumulator->record_overlapping_bounds(); + } int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); void* data_ptr; @@ -1380,32 +1548,25 @@ void DisplayListBuilder::DrawDisplayList(const sk_sp display_list, SkScalar opacity) { if (!std::isfinite(opacity) || opacity <= SK_ScalarNearlyZero || display_list->op_count() == 0 || display_list->bounds().isEmpty() || - current_layer_->is_nop_) { + current_info().is_nop) { return; } const SkRect bounds = display_list->bounds(); bool accumulated; - switch (accumulator()->type()) { - case BoundsAccumulatorType::kRect: - accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); - break; - case BoundsAccumulatorType::kRTree: - auto rtree = display_list->rtree(); - if (rtree) { - std::list rects = - rtree->searchAndConsolidateRects(GetLocalClipBounds(), false); - accumulated = false; - for (const SkRect& rect : rects) { - // TODO (https://github.com/flutter/flutter/issues/114919): Attributes - // are not necessarily `kDrawDisplayListFlags`. - if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) { - accumulated = true; - } - } - } else { - accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); + sk_sp rtree; + if (!rtree_data_.has_value() || !(rtree = display_list->rtree())) { + accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); + } else { + std::list rects = + rtree->searchAndConsolidateRects(GetLocalClipBounds(), false); + accumulated = false; + for (const SkRect& rect : rects) { + // TODO (https://github.com/flutter/flutter/issues/114919): Attributes + // are not necessarily `kDrawDisplayListFlags`. + if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) { + accumulated = true; } - break; + } } if (!accumulated) { return; @@ -1608,17 +1769,21 @@ bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds, return true; } -bool DisplayListBuilder::AccumulateUnbounded() { - SkRect clip = tracker_.device_cull_rect(); +bool DisplayListBuilder::AccumulateUnbounded(SaveInfo& layer) { + SkRect clip = layer.global_state.device_cull_rect(); if (clip.isEmpty()) { return false; } - accumulator()->accumulate(clip, op_index_); - if (current_layer_->layer_accumulator_) { - FML_DCHECK(layer_tracker_); - current_layer_->layer_accumulator_->accumulate( - layer_tracker_->device_cull_rect()); + if (rtree_data_.has_value()) { + FML_DCHECK(!layer.global_space_accumulator); + rtree_data_->rects.push_back(clip); + rtree_data_->indices.push_back(op_index_); + } else { + FML_DCHECK(layer.global_space_accumulator); + layer.global_space_accumulator->accumulate(clip); } + clip = layer.layer_state.device_cull_rect(); + layer.layer_local_accumulator->accumulate(clip); return true; } @@ -1630,22 +1795,44 @@ bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds, return AccumulateUnbounded(); } } -bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) { - if (!bounds.isEmpty()) { - SkRect device_bounds; - tracker_.mapRect(bounds, &device_bounds); - if (device_bounds.intersect(tracker_.device_cull_rect())) { - accumulator()->accumulate(device_bounds, op_index_); - if (current_layer_->layer_accumulator_) { - FML_DCHECK(layer_tracker_); - SkRect layer_bounds; - layer_tracker_->mapRect(bounds, &layer_bounds); - current_layer_->layer_accumulator_->accumulate(layer_bounds); - } - return true; + +bool DisplayListBuilder::AccumulateBounds(const SkRect& bounds, + SaveInfo& layer, + int id) { + if (bounds.isEmpty()) { + return false; + } + SkRect device_bounds; + layer.global_state.mapRect(bounds, &device_bounds); + if (!device_bounds.intersect(layer.global_state.device_cull_rect())) { + return false; + } + if (rtree_data_.has_value()) { + FML_DCHECK(!layer.global_space_accumulator); + if (id >= 0) { + rtree_data_->rects.push_back(device_bounds); + rtree_data_->indices.push_back(id); } + } else { + FML_DCHECK(layer.global_space_accumulator); + layer.global_space_accumulator->accumulate(device_bounds); } - return false; + SkRect layer_bounds; + layer.layer_state.mapRect(bounds, &layer_bounds); + layer.layer_local_accumulator->accumulate(layer_bounds); + return true; +} + +bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const SkRect& bounds) { + if (bounds.isEmpty()) { + return false; + } + SkRect local_bounds; + layer_state.mapRect(bounds, &local_bounds); + if (local_bounds.intersect(layer_state.device_cull_rect())) { + layer_local_accumulator->accumulate(local_bounds); + } + return true; } bool DisplayListBuilder::paint_nops_on_transparency() { @@ -1769,7 +1956,7 @@ DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint, DisplayListBuilder::OpResult DisplayListBuilder::PaintResult( const DlPaint& paint, DisplayListAttributeFlags flags) { - if (current_layer_->is_nop_) { + if (current_info().is_nop) { return OpResult::kNoEffect; } if (flags.applies_blend()) { diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h index 64ccd65e216da..54fdf6b2c1e17 100644 --- a/display_list/dl_builder.h +++ b/display_list/dl_builder.h @@ -13,8 +13,9 @@ #include "flutter/display_list/dl_paint.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/effects/dl_path_effect.h" +#include "flutter/display_list/geometry/dl_geometry_types.h" #include "flutter/display_list/image/dl_image.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/display_list/utils/dl_accumulation_rect.h" #include "flutter/display_list/utils/dl_comparable.h" #include "flutter/display_list/utils/dl_matrix_clip_tracker.h" #include "flutter/fml/macros.h" @@ -55,7 +56,7 @@ class DisplayListBuilder final : public virtual DlCanvas, // |DlCanvas| void Restore() override; // |DlCanvas| - int GetSaveCount() const override { return layer_stack_.size(); } + int GetSaveCount() const override { return save_stack_.size(); } // |DlCanvas| void RestoreToCount(int restore_count) override; @@ -104,13 +105,13 @@ class DisplayListBuilder final : public virtual DlCanvas, /// save stack. // |DlCanvas| SkM44 GetTransformFullPerspective() const override { - return tracker_.matrix_4x4(); + return global_state().matrix_4x4(); } /// Returns the 3x3 partial perspective transform representing all transform /// operations executed so far in this DisplayList within the enclosing /// save stack. // |DlCanvas| - SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); } + SkMatrix GetTransform() const override { return global_state().matrix_3x3(); } // |DlCanvas| void ClipRect(const SkRect& rect, @@ -130,14 +131,14 @@ class DisplayListBuilder final : public virtual DlCanvas, /// be rendered. // |DlCanvas| SkRect GetDestinationClipBounds() const override { - return tracker_.device_cull_rect(); + return global_state().device_cull_rect(); } /// Conservative estimate of the bounds of all outstanding clip operations /// transformed into the local coordinate space in which currently /// recorded rendering operations are interpreted. // |DlCanvas| SkRect GetLocalClipBounds() const override { - return tracker_.local_cull_rect(); + return global_state().local_cull_rect(); } /// Return true iff the supplied bounds are easily shown to be outside @@ -504,114 +505,174 @@ class DisplayListBuilder final : public virtual DlCanvas, template void* Push(size_t extra, Args&&... args); - void intersect(const SkRect& rect); - - // kInvalidSigma is used to indicate that no MaskBlur is currently set. - static constexpr SkScalar kInvalidSigma = 0.0; - static bool mask_sigma_valid(SkScalar sigma) { - return std::isfinite(sigma) && sigma > 0.0; - } + struct RTreeData { + std::vector rects; + std::vector indices; + }; + // The SaveInfo class stores internal data for both Save and SaveLayer calls class SaveInfo { public: - explicit SaveInfo(size_t save_offset = 0, uint32_t start_depth = 0) - : save_offset_(save_offset), start_depth_(start_depth) {} - - // The offset into the memory buffer where the save DLOp record - // for this save() call is placed. This may be needed if the - // eventual restore() call has discovered important information about - // the records inside the saveLayer that may impact how the saveLayer - // is handled (e.g., |cannot_inherit_opacity| == false). - // This offset is only valid if |has_layer| is true. - size_t save_offset() const { return save_offset_; } - - bool is_save_layer() const { return is_save_layer_; } - bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; } - bool has_compatible_op() const { return has_compatible_op_; } - bool affects_transparent_layer() const { - return affects_transparent_layer_; - } + // For vector reallocation calls to copy vector data + SaveInfo(const SaveInfo& copy) = default; + SaveInfo(SaveInfo&& copy) = default; + + // For constructor (root layer) initialization + explicit SaveInfo(const DlRect& cull_rect) + : is_root_layer(true), + is_save_layer(true), + global_state(cull_rect), + layer_state(cull_rect), + layer_local_accumulator(new AccumulationRect()) {} + + // For regular save calls: + // Passing a pointer to the parent_info so as to distinguish this + // call from the copy constructor used above in vector reallocations + explicit SaveInfo(const SaveInfo* parent_info) + : is_root_layer(false), + is_save_layer(false), + has_deferred_save_op(true), + global_state(parent_info->global_state), + layer_state(parent_info->layer_state), + global_space_accumulator(parent_info->global_space_accumulator), + layer_local_accumulator(parent_info->layer_local_accumulator) {} + + // For saveLayer calls: + explicit SaveInfo(const SaveInfo* parent_info, + const std::shared_ptr& filter, + int rtree_rect_index) + : is_root_layer(false), + is_save_layer(true), + rtree_rects_start_index(rtree_rect_index), + global_state(parent_info->global_state), + layer_state(parent_info->global_state.local_cull_rect()), + global_space_accumulator(parent_info->global_space_accumulator), + layer_local_accumulator(new AccumulationRect()), + filter(filter) {} bool is_group_opacity_compatible() const { - return !cannot_inherit_opacity_; - } - - void mark_incompatible() { cannot_inherit_opacity_ = true; } - - // For now this only allows a single compatible op to mark the - // layer as being compatible with group opacity. If we start - // computing bounds of ops in the Builder methods then we - // can upgrade this to checking for overlapping ops. - // See https://github.com/flutter/flutter/issues/93899 - void add_compatible_op() { - if (!cannot_inherit_opacity_) { - if (has_compatible_op_) { - cannot_inherit_opacity_ = true; - } else { - has_compatible_op_ = true; - } - } + return !opacity_incompatible_op_detected && + !layer_local_accumulator->overlap_detected(); } - // Records that the current layer contains an op that produces visible - // output on a transparent surface. - void add_visible_op() { - affects_transparent_layer_ = true; - } - - // The filter to apply to the layer bounds when it is restored - std::shared_ptr filter() { return filter_; } - - // is_unbounded should be set to true if we ever encounter an operation - // on a layer that either is unrestricted (|drawColor| or |drawPaint|) - // or cannot compute its bounds (some effects and filters) and there - // was no outstanding clip op at the time. - // When the layer is restored, the outer layer may then process this - // unbounded state by accumulating its own clip or transferring the - // unbounded state to its own outer layer. - // Typically the DisplayList will have been constructed with a cull - // rect which will act as a default clip for the outermost layer and - // the unbounded state of all sub layers will eventually be caught by - // that cull rect so that the overall unbounded state of the entire - // DisplayList will never be true. - // - // For historical consistency it is worth noting that SkPicture used - // to treat these same conditions as a Nop (they accumulate the - // SkPicture cull rect, but if no cull rect was specified then it is - // an empty Rect and so has no effect on the bounds). + // Records the given bounds after transforming by the global and + // layer matrices. + bool AccumulateBoundsLocal(const SkRect& bounds); + + // Simply transfers the local bounds to the parent + void TransferBoundsToParent(const SaveInfo& parent); + + const bool is_root_layer; + const bool is_save_layer; + + bool has_deferred_save_op = false; + bool is_nop = false; + bool opacity_incompatible_op_detected = false; + bool is_unbounded = false; + bool affects_transparent_layer = false; + + // The offset into the buffer where the associated save op is recorded + // (which is not necessarily the same as when the Save() method is called) + size_t save_offset = 0; + // The depth when the save call is recorded, used to compute the total + // depth of its content when the associated restore is called. + uint32_t save_depth = 0; + + // The index of the rtree rects when the saveLayer was called, used + // only in the case that the saveLayer has a filter so that the + // accumulated rects can be updated in the corresponding restore call. + const size_t rtree_rects_start_index = 0; + + // The transform and clip accumulated since the root of the DisplayList + DisplayListMatrixClipState global_state; + + // The transform and clip accumulated since the most recent saveLayer, + // used to compute and update its bounds when the restore is called. + DisplayListMatrixClipState layer_state; + + // Not every layer needs its own accumulator(s). In particular, the + // global accumulator is only used if we are not construting an rtree. + // Regular save calls will share both accumulators with their parent. + // Additionally, a saveLayer will separate its global accumulator from + // its parent (if not constructing an rtree) when it has a filter which + // requires it to post-adjust the bounds accumulated while recording + // its content. Finally, every saveLayer has its own local accumulator. // - // Flutter is unlikely to ever run into this as the Dart mechanisms - // all supply a non-null cull rect for all Dart Picture objects, - // even if that cull rect is kGiantRect. - void set_unbounded() { is_unbounded_ = true; } - - // |is_unbounded| should be called after |getLayerBounds| in case - // a problem was found during the computation of those bounds, - // the layer will have one last chance to flag an unbounded state. - bool is_unbounded() const { return is_unbounded_; } - - private: - size_t save_offset_; - uint32_t start_depth_; - bool is_save_layer_ = false; - bool cannot_inherit_opacity_ = false; - bool has_compatible_op_ = false; - std::shared_ptr filter_; - bool is_unbounded_ = false; - bool has_deferred_save_op_ = false; - bool is_nop_ = false; - bool affects_transparent_layer_ = false; - std::shared_ptr layer_accumulator_; - - friend class DisplayListBuilder; + // All accumulations could occur in the local layer space, and then be + // transformed and accumulated into the parent as each layer is restored, + // but that technique would compound the bounds errors that happen when + // a list of transforms is performed serially on a rectangle (mainly + // when multiple rotation or skew transforms are involved). + + // The bounds accumulator for the entire DisplayList, relative to its root + std::shared_ptr global_space_accumulator; + + // The bounds accumulator to set/verify the bounds of the most recently + // invoked saveLayer call, relative to the root of that saveLayer + std::shared_ptr layer_local_accumulator; + + // The filter that will be applied to the contents of the saveLayer + // when it is restored into the parent layer. + const std::shared_ptr filter; }; - std::vector layer_stack_; - SaveInfo* current_layer_; - DisplayListMatrixClipTracker tracker_; - std::unique_ptr layer_tracker_; - std::unique_ptr accumulator_; - BoundsAccumulator* accumulator() { return accumulator_.get(); } + const DlRect original_cull_rect_; + std::vector save_stack_; + std::optional rtree_data_; + + DlPaint current_; + + // Returns a reference to the SaveInfo structure at the top of the current + // save_stack state. Note that the clip and matrix state can be accessed + // more directly through global_state() and layer_state(). + SaveInfo& current_info() { return save_stack_.back(); } + const SaveInfo& current_info() const { return save_stack_.back(); } + + // Returns a reference to the SaveInfo structure just below the top + // of the current save_stack state. + SaveInfo& parent_info() { return *std::prev(save_stack_.end(), 2); } + const SaveInfo& parent_info() const { + return *std::prev(save_stack_.end(), 2); + } + + // Returns a reference to the matrix and clip state for the entire + // DisplayList. The initial transform of this state is identity and + // the initial cull_rect is the root original_cull_rect supplied + // in the constructor. It is a summary of all transform and clip + // calls that have happened since the DisplayList was created + // (and have not yet been removed by a restore() call). + DisplayListMatrixClipState& global_state() { + return current_info().global_state; + } + const DisplayListMatrixClipState& global_state() const { + return current_info().global_state; + } + + // Returns a reference to the matrix and clip state relative to the + // current layer, whether that is defined by the most recent saveLayer + // call, or by the initial root state of the entire DisplayList for + // calls not surrounded by a saveLayer/restore pair. It is a summary + // of only those transform and clip calls that have happened since + // the creation of the DisplayList or since the most recent saveLayer + // (and have not yet been removed by a restore() call). + DisplayListMatrixClipState& layer_local_state() { + return current_info().layer_state; + } + const DisplayListMatrixClipState& layer_local_state() const { + return current_info().layer_state; + } + + void RestoreLayer(const SaveInfo& current_info, + SaveInfo& parent_info, + void* base_op); + void TransferLayerBounds(const SaveInfo& current_info, + SaveInfo& parent_info, + const SkRect& content_bounds); + bool AdjustRTreeRects(RTreeData& data, + const DlImageFilter& filter, + const SkMatrix& matrix, + const SkRect& clip, + size_t rect_index); // This flag indicates whether or not the current rendering attributes // are compatible with rendering ops applying an inherited opacity. @@ -637,10 +698,8 @@ class DisplayListBuilder final : public virtual DlCanvas, // Update the opacity compatibility flags of the current layer for an op // that has determined its compatibility as indicated by |compatible|. void UpdateLayerOpacityCompatibility(bool compatible) { - if (compatible) { - current_layer_->add_compatible_op(); - } else { - current_layer_->mark_incompatible(); + if (!compatible) { + current_info().opacity_incompatible_op_detected = true; } } @@ -683,38 +742,6 @@ class DisplayListBuilder final : public virtual DlCanvas, void onSetPathEffect(const DlPathEffect* effect); void onSetMaskFilter(const DlMaskFilter* filter); - // The DisplayList had an unbounded call with no cull rect or clip - // to contain it. Should only be called after the stream is fully - // built. - // Unbounded operations are calls like |drawColor| which are defined - // to flood the entire surface, or calls that relied on a rendering - // attribute which is unable to compute bounds (should be rare). - // In those cases the bounds will represent only the accumulation - // of the bounded calls and this flag will be set to indicate that - // condition. - bool is_unbounded() const { - FML_DCHECK(layer_stack_.size() == 1); - return layer_stack_.front().is_unbounded(); - } - - SkRect bounds() const { - FML_DCHECK(layer_stack_.size() == 1); - if (is_unbounded()) { - FML_LOG(INFO) << "returning partial bounds for unbounded DisplayList"; - } - - return accumulator_->bounds(); - } - - sk_sp rtree() { - FML_DCHECK(layer_stack_.size() == 1); - if (is_unbounded()) { - FML_LOG(INFO) << "returning partial rtree for unbounded DisplayList"; - } - - return accumulator_->rtree(); - } - static DisplayListAttributeFlags FlagsForPointMode(PointMode mode); enum class OpResult { @@ -733,7 +760,7 @@ class DisplayListBuilder final : public virtual DlCanvas, case OpResult::kPreservesTransparency: break; case OpResult::kAffectsAll: - current_layer_->add_visible_op(); + current_info().affects_transparent_layer = true; break; } } @@ -752,7 +779,10 @@ class DisplayListBuilder final : public virtual DlCanvas, // Records the fact that we encountered an op that either could not // estimate its bounds or that fills all of the destination space. - bool AccumulateUnbounded(); + bool AccumulateUnbounded(SaveInfo& layer); + bool AccumulateUnbounded() { + return AccumulateUnbounded(current_info()); + } // Records the bounds for an op after modifying them according to the // supplied attribute flags and transforming by the current matrix. @@ -769,9 +799,10 @@ class DisplayListBuilder final : public virtual DlCanvas, // Records the given bounds after transforming by the current matrix // and clipping against the current clip. - bool AccumulateBounds(SkRect& bounds); - - DlPaint current_; + bool AccumulateBounds(const SkRect& bounds, SaveInfo& layer, int id); + bool AccumulateBounds(const SkRect& bounds) { + return AccumulateBounds(bounds, current_info(), op_index_); + } }; } // namespace flutter diff --git a/display_list/dl_vertices.cc b/display_list/dl_vertices.cc index cbbe6e1ea4ab8..c338a02a97abc 100644 --- a/display_list/dl_vertices.cc +++ b/display_list/dl_vertices.cc @@ -4,7 +4,7 @@ #include "flutter/display_list/dl_vertices.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/display_list/utils/dl_accumulation_rect.h" #include "flutter/fml/logging.h" namespace flutter { @@ -86,7 +86,7 @@ size_t DlVertices::size() const { } static SkRect compute_bounds(const SkPoint* points, int count) { - RectBoundsAccumulator accumulator; + AccumulationRect accumulator; for (int i = 0; i < count; i++) { accumulator.accumulate(points[i]); } diff --git a/display_list/geometry/dl_geometry_types.h b/display_list/geometry/dl_geometry_types.h new file mode 100644 index 0000000000000..34b431cde4c62 --- /dev/null +++ b/display_list/geometry/dl_geometry_types.h @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_DISPLAY_LIST_GEOMETRY_DL_GEOMETRY_TYPES_H_ +#define FLUTTER_DISPLAY_LIST_GEOMETRY_DL_GEOMETRY_TYPES_H_ + +#include "flutter/impeller/geometry/matrix.h" +#include "flutter/impeller/geometry/rect.h" +#include "flutter/impeller/geometry/scalar.h" + +#include "flutter/third_party/skia/include/core/SkM44.h" +#include "flutter/third_party/skia/include/core/SkMatrix.h" +#include "flutter/third_party/skia/include/core/SkRect.h" +#include "flutter/third_party/skia/include/core/SkSize.h" + +namespace flutter { + +using DlScalar = impeller::Scalar; +using DlDegrees = impeller::Degrees; +using DlRadians = impeller::Radians; + +using DlISize = impeller::ISize32; +using DlSize = impeller::Size; +using DlRect = impeller::Rect; +using DlIRect = impeller::IRect32; +using DlMatrix = impeller::Matrix; + +static_assert(sizeof(SkRect) == sizeof(DlRect)); + +inline const DlRect& ToDlRect(const SkRect& rect) { + return *reinterpret_cast(&rect); +} + +inline constexpr DlMatrix ToDlMatrix(const SkMatrix& matrix) { + // clang-format off + return DlMatrix::MakeColumn( + matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY], 0.0f, matrix[SkMatrix::kMPersp0], + matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY], 0.0f, matrix[SkMatrix::kMPersp1], + 0.0f, 0.0f, 1.0f, 0.0f, + matrix[SkMatrix::kMTransX], matrix[SkMatrix::kMTransY], 0.0f, matrix[SkMatrix::kMPersp2] + ); + // clang-format on +} + +inline constexpr DlMatrix ToDlMatrix(const SkM44& matrix) { + DlMatrix dl_matrix; + matrix.getColMajor(dl_matrix.m); + return dl_matrix; +} + +inline const SkRect& ToSkRect(const DlRect& rect) { + return *reinterpret_cast(&rect); +} + +inline const SkISize& ToSkISize(const DlISize& size) { + return *reinterpret_cast(&size); +} + +inline constexpr SkMatrix ToSkMatrix(const DlMatrix& matrix) { + return SkMatrix::MakeAll(matrix.m[0], matrix.m[4], matrix.m[12], // + matrix.m[1], matrix.m[5], matrix.m[13], // + matrix.m[3], matrix.m[7], matrix.m[15]); +} + +inline constexpr SkM44 ToSkM44(const DlMatrix& matrix) { + return SkM44::ColMajor(matrix.m); +} + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_GEOMETRY_DL_GEOMETRY_TYPES_H_ diff --git a/display_list/utils/dl_accumulation_rect.cc b/display_list/utils/dl_accumulation_rect.cc new file mode 100644 index 0000000000000..cd882895d7fed --- /dev/null +++ b/display_list/utils/dl_accumulation_rect.cc @@ -0,0 +1,67 @@ +// 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/display_list/utils/dl_accumulation_rect.h" + +namespace flutter { + +void AccumulationRect::accumulate(SkScalar x, SkScalar y) { + if (!std::isfinite(x) || !std::isfinite(y)) { + return; + } + if (x >= min_x_ && x < max_x_ && y >= min_y_ && y < max_y_) { + record_overlapping_bounds(); + return; + } + if (min_x_ > x) { + min_x_ = x; + } + if (min_y_ > y) { + min_y_ = y; + } + if (max_x_ < x) { + max_x_ = x; + } + if (max_y_ < y) { + max_y_ = y; + } +} + +void AccumulationRect::accumulate(SkRect r) { + if (r.isEmpty()) { + return; + } + if (r.fLeft < max_x_ && r.fRight > min_x_ && // + r.fTop < max_y_ && r.fBottom > min_y_) { + record_overlapping_bounds(); + } + if (min_x_ > r.fLeft) { + min_x_ = r.fLeft; + } + if (min_y_ > r.fTop) { + min_y_ = r.fTop; + } + if (max_x_ < r.fRight) { + max_x_ = r.fRight; + } + if (max_y_ < r.fBottom) { + max_y_ = r.fBottom; + } +} + +SkRect AccumulationRect::bounds() const { + return (max_x_ >= min_x_ && max_y_ >= min_y_) + ? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_) + : SkRect::MakeEmpty(); +} + +void AccumulationRect::reset() { + min_x_ = std::numeric_limits::infinity(); + min_y_ = std::numeric_limits::infinity(); + max_x_ = -std::numeric_limits::infinity(); + max_y_ = -std::numeric_limits::infinity(); + overlap_detected_ = false; +} + +} // namespace flutter diff --git a/display_list/utils/dl_accumulation_rect.h b/display_list/utils/dl_accumulation_rect.h new file mode 100644 index 0000000000000..ca492c17e9d06 --- /dev/null +++ b/display_list/utils/dl_accumulation_rect.h @@ -0,0 +1,54 @@ +// 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_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_ +#define FLUTTER_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_ + +#include + +#include "flutter/display_list/geometry/dl_geometry_types.h" +#include "flutter/display_list/geometry/dl_rtree.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +// Utility class to collect bounds from a bunch of rectangles and points +// while also noting if there might be any overlap between any of the data +// point/rects. Note that the overlap protection is not sophisticated, +// simply noting if the new data intersects with the already accumulated +// bounds. This can successfully detect non-overlap of a linear sequence +// of non-overlapping objects, or even a cross of non-overlapping objects +// as long as they are built out from the center in the right order. True +// detection of non-overlapping objects would require much more time and/or +// space. +class AccumulationRect { + public: + AccumulationRect() { reset(); } + + void accumulate(SkScalar x, SkScalar y); + void accumulate(SkPoint p) { accumulate(p.fX, p.fY); } + void accumulate(SkRect r); + void accumulate(DlRect r) { accumulate(ToSkRect(r)); } + + bool is_empty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; } + bool is_not_empty() const { return min_x_ < max_x_ && min_y_ < max_y_; } + + SkRect bounds() const; + + void reset(); + + bool overlap_detected() const { return overlap_detected_; } + void record_overlapping_bounds() { overlap_detected_ = true; } + + private: + DlScalar min_x_; + DlScalar min_y_; + DlScalar max_x_; + DlScalar max_y_; + bool overlap_detected_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_ diff --git a/display_list/utils/dl_bounds_accumulator.cc b/display_list/utils/dl_bounds_accumulator.cc deleted file mode 100644 index 34750ed4bd529..0000000000000 --- a/display_list/utils/dl_bounds_accumulator.cc +++ /dev/null @@ -1,134 +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/display_list/utils/dl_bounds_accumulator.h" - -namespace flutter { - -void RectBoundsAccumulator::accumulate(const SkRect& r, int index) { - if (r.fLeft < r.fRight && r.fTop < r.fBottom) { - rect_.accumulate(r.fLeft, r.fTop); - rect_.accumulate(r.fRight, r.fBottom); - } -} - -void RectBoundsAccumulator::save() { - saved_rects_.emplace_back(rect_); - rect_ = AccumulationRect(); -} -void RectBoundsAccumulator::restore() { - if (!saved_rects_.empty()) { - SkRect layer_bounds = rect_.bounds(); - pop_and_accumulate(layer_bounds, nullptr); - } -} -bool RectBoundsAccumulator::restore( - std::function mapper, - const SkRect* clip) { - bool success = true; - if (!saved_rects_.empty()) { - SkRect layer_bounds = rect_.bounds(); - success = mapper(layer_bounds, layer_bounds); - pop_and_accumulate(layer_bounds, clip); - } - return success; -} -void RectBoundsAccumulator::pop_and_accumulate(SkRect& layer_bounds, - const SkRect* clip) { - FML_DCHECK(!saved_rects_.empty()); - - rect_ = saved_rects_.back(); - saved_rects_.pop_back(); - - if (clip == nullptr || layer_bounds.intersect(*clip)) { - accumulate(layer_bounds, -1); - } -} - -RectBoundsAccumulator::AccumulationRect::AccumulationRect() { - min_x_ = std::numeric_limits::infinity(); - min_y_ = std::numeric_limits::infinity(); - max_x_ = -std::numeric_limits::infinity(); - max_y_ = -std::numeric_limits::infinity(); -} -void RectBoundsAccumulator::AccumulationRect::accumulate(SkScalar x, - SkScalar y) { - if (min_x_ > x) { - min_x_ = x; - } - if (min_y_ > y) { - min_y_ = y; - } - if (max_x_ < x) { - max_x_ = x; - } - if (max_y_ < y) { - max_y_ = y; - } -} -SkRect RectBoundsAccumulator::AccumulationRect::bounds() const { - return (max_x_ >= min_x_ && max_y_ >= min_y_) - ? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_) - : SkRect::MakeEmpty(); -} - -void RTreeBoundsAccumulator::accumulate(const SkRect& r, int index) { - if (r.fLeft < r.fRight && r.fTop < r.fBottom) { - rects_.push_back(r); - rect_indices_.push_back(index); - } -} -void RTreeBoundsAccumulator::save() { - saved_offsets_.push_back(rects_.size()); -} -void RTreeBoundsAccumulator::restore() { - if (saved_offsets_.empty()) { - return; - } - - saved_offsets_.pop_back(); -} -bool RTreeBoundsAccumulator::restore( - std::function map, - const SkRect* clip) { - if (saved_offsets_.empty()) { - return true; - } - - size_t previous_size = saved_offsets_.back(); - saved_offsets_.pop_back(); - - bool success = true; - for (size_t i = previous_size; i < rects_.size(); i++) { - SkRect original = rects_[i]; - if (!map(original, original)) { - success = false; - } - if (clip == nullptr || original.intersect(*clip)) { - rect_indices_[previous_size] = rect_indices_[i]; - rects_[previous_size] = original; - previous_size++; - } - } - rects_.resize(previous_size); - rect_indices_.resize(previous_size); - return success; -} - -SkRect RTreeBoundsAccumulator::bounds() const { - FML_DCHECK(saved_offsets_.empty()); - RectBoundsAccumulator accumulator; - for (auto& rect : rects_) { - accumulator.accumulate(rect, 0); - } - return accumulator.bounds(); -} - -sk_sp RTreeBoundsAccumulator::rtree() const { - FML_DCHECK(saved_offsets_.empty()); - return sk_make_sp(rects_.data(), rects_.size(), rect_indices_.data(), - [](int id) { return id >= 0; }); -} - -} // namespace flutter diff --git a/display_list/utils/dl_bounds_accumulator.h b/display_list/utils/dl_bounds_accumulator.h deleted file mode 100644 index db4ceda2698b6..0000000000000 --- a/display_list/utils/dl_bounds_accumulator.h +++ /dev/null @@ -1,168 +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_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_ -#define FLUTTER_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_ - -#include - -#include "flutter/display_list/geometry/dl_rtree.h" -#include "flutter/fml/logging.h" - -// This file contains various utility classes to ease implementing -// a Flutter DisplayList DlOpReceiver, including: -// -// IgnoreAttributeDispatchHelper: -// IgnoreClipDispatchHelper: -// IgnoreTransformDispatchHelper -// Empty overrides of all of the associated methods of DlOpReceiver -// for receivers that only track some of the rendering operations - -namespace flutter { - -enum class BoundsAccumulatorType { - kRect, - kRTree, -}; - -class BoundsAccumulator { - public: - /// function definition for modifying the bounds of a rectangle - /// during a restore operation. The function is used primarily - /// to account for the bounds impact of an ImageFilter on a - /// saveLayer on a per-rect basis. The implementation may apply - /// this function at whatever granularity it can manage easily - /// (for example, a Rect accumulator might apply it to the entire - /// local bounds being restored, whereas an RTree accumulator might - /// apply it individually to each element in the local RTree). - /// - /// The function will do a best faith attempt at determining the - /// modified bounds and store the results in the supplied |dest| - /// rectangle and return true. If the function is unable to - /// accurately determine the modifed bounds, it will set the - /// |dest| rectangle to a copy of the input bounds (or a best - /// guess) and return false to indicate that the bounds should not - /// be trusted. - typedef bool BoundsModifier(const SkRect& original, SkRect* dest); - - virtual ~BoundsAccumulator() = default; - - virtual void accumulate(const SkRect& r, int index = 0) = 0; - - /// Save aside the rects/bounds currently being accumulated and start - /// accumulating a new set of rects/bounds. When restore is called, - /// some additional modifications may be applied to these new bounds - /// before they are accumulated back into the surrounding bounds. - virtual void save() = 0; - - /// Restore to the previous accumulation and incorporate the bounds of - /// the primitives that were recorded since the last save (if needed). - virtual void restore() = 0; - - /// Restore the previous set of accumulation rects/bounds and accumulate - /// the current rects/bounds that were accumulated since the most recent - /// call to |save| into them with modifications specified by the |map| - /// parameter and clipping to the clip parameter if it is not null. - /// - /// The indicated map function is applied to the various rects and bounds - /// that have been accumulated in this save/restore cycle before they - /// are then accumulated into the previous accumulations. The granularity - /// of the application of the map function to the rectangles that were - /// accumulated during the save period is left up to the implementation. - /// - /// This method will return true if the map function returned true on - /// every single invocation. A false return value means that the - /// bounds accumulated during this restore may not be trusted (as - /// determined by the map function). - /// - /// If there are no saved accumulations to restore to, this method will - /// NOP ignoring the map function and the optional clip entirely. - virtual bool restore( - std::function map, - const SkRect* clip = nullptr) = 0; - - virtual SkRect bounds() const = 0; - - virtual sk_sp rtree() const = 0; - - virtual BoundsAccumulatorType type() const = 0; -}; - -class RectBoundsAccumulator final : public virtual BoundsAccumulator { - public: - void accumulate(SkScalar x, SkScalar y) { rect_.accumulate(x, y); } - void accumulate(const SkPoint& p) { rect_.accumulate(p.fX, p.fY); } - void accumulate(const SkRect& r, int index) override; - - bool is_empty() const { return rect_.is_empty(); } - bool is_not_empty() const { return rect_.is_not_empty(); } - - void save() override; - void restore() override; - bool restore(std::function mapper, - const SkRect* clip) override; - - SkRect bounds() const override { - FML_DCHECK(saved_rects_.empty()); - return rect_.bounds(); - } - - BoundsAccumulatorType type() const override { - return BoundsAccumulatorType::kRect; - } - - sk_sp rtree() const override { return nullptr; } - - private: - class AccumulationRect { - public: - AccumulationRect(); - - void accumulate(SkScalar x, SkScalar y); - - bool is_empty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; } - bool is_not_empty() const { return min_x_ < max_x_ && min_y_ < max_y_; } - - SkRect bounds() const; - - private: - SkScalar min_x_; - SkScalar min_y_; - SkScalar max_x_; - SkScalar max_y_; - }; - - void pop_and_accumulate(SkRect& layer_bounds, const SkRect* clip); - - AccumulationRect rect_; - std::vector saved_rects_; -}; - -class RTreeBoundsAccumulator final : public virtual BoundsAccumulator { - public: - void accumulate(const SkRect& r, int index) override; - void save() override; - void restore() override; - - bool restore( - std::function map, - const SkRect* clip = nullptr) override; - - SkRect bounds() const override; - - sk_sp rtree() const override; - - BoundsAccumulatorType type() const override { - return BoundsAccumulatorType::kRTree; - } - - private: - std::vector rects_; - std::vector rect_indices_; - std::vector saved_offsets_; -}; - -} // namespace flutter - -#endif // FLUTTER_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_ diff --git a/display_list/utils/dl_matrix_clip_tracker.cc b/display_list/utils/dl_matrix_clip_tracker.cc index 6464889c0aafd..9ed7f5d7ececa 100644 --- a/display_list/utils/dl_matrix_clip_tracker.cc +++ b/display_list/utils/dl_matrix_clip_tracker.cc @@ -18,24 +18,37 @@ bool DisplayListMatrixClipTracker::is_3x3(const SkM44& m) { // clang-format on } +static constexpr DlRect kEmpty = DlRect(); + +static const DlRect& ProtectEmpty(const SkRect& rect) { + // isEmpty protects us against NaN while we normalize any empty cull rects + return rect.isEmpty() ? kEmpty : ToDlRect(rect); +} + +static const DlRect& ProtectEmpty(const DlRect& rect) { + // isEmpty protects us against NaN while we normalize any empty cull rects + return rect.IsEmpty() ? kEmpty : rect; +} + DisplayListMatrixClipState::DisplayListMatrixClipState(const DlRect& cull_rect, const DlMatrix& matrix) - : cull_rect_(cull_rect), matrix_(matrix) {} + : cull_rect_(ProtectEmpty(cull_rect)), matrix_(matrix) {} + +DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect) + : cull_rect_(ProtectEmpty(cull_rect)), matrix_(DlMatrix()) {} DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect, const SkMatrix& matrix) - : cull_rect_(ToDlRect(cull_rect)), matrix_(ToDlMatrix(matrix)) {} + : cull_rect_(ProtectEmpty(cull_rect)), matrix_(ToDlMatrix(matrix)) {} DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect, const SkM44& matrix) - : cull_rect_(ToDlRect(cull_rect)), matrix_(ToDlMatrix(matrix)) {} + : cull_rect_(ProtectEmpty(cull_rect)), matrix_(ToDlMatrix(matrix)) {} DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( const DlRect& cull_rect, const DlMatrix& matrix) { - // isEmpty protects us against NaN as we normalize any empty cull rects - DlRect cull = cull_rect.IsEmpty() ? DlRect() : cull_rect; - saved_.emplace_back(cull, matrix); + saved_.emplace_back(cull_rect, matrix); current_ = &saved_.back(); save(); // saved_[0] will always be the initial settings } @@ -43,9 +56,7 @@ DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( const SkRect& cull_rect, const SkMatrix& matrix) { - // isEmpty protects us against NaN as we normalize any empty cull rects - SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect; - saved_.emplace_back(cull, matrix); + saved_.emplace_back(cull_rect, matrix); current_ = &saved_.back(); save(); // saved_[0] will always be the initial settings } @@ -53,9 +64,7 @@ DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( const SkRect& cull_rect, const SkM44& m44) { - // isEmpty protects us against NaN as we normalize any empty cull rects - SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect; - saved_.emplace_back(cull, m44); + saved_.emplace_back(cull_rect, m44); current_ = &saved_.back(); save(); // saved_[0] will always be the initial settings } diff --git a/display_list/utils/dl_matrix_clip_tracker.h b/display_list/utils/dl_matrix_clip_tracker.h index 286b395fb33eb..1c8f33992f6a2 100644 --- a/display_list/utils/dl_matrix_clip_tracker.h +++ b/display_list/utils/dl_matrix_clip_tracker.h @@ -8,9 +8,8 @@ #include #include "flutter/display_list/dl_canvas.h" +#include "flutter/display_list/geometry/dl_geometry_types.h" #include "flutter/fml/logging.h" -#include "flutter/impeller/geometry/matrix.h" -#include "flutter/impeller/geometry/rect.h" #include "third_party/skia/include/core/SkM44.h" #include "third_party/skia/include/core/SkMatrix.h" @@ -24,49 +23,11 @@ namespace flutter { class DisplayListMatrixClipState { private: using ClipOp = DlCanvas::ClipOp; - using DlRect = impeller::Rect; - using DlMatrix = impeller::Matrix; - using DlDegrees = impeller::Degrees; - - static_assert(sizeof(SkRect) == sizeof(DlRect)); - - static const DlRect& ToDlRect(const SkRect& rect) { - return *reinterpret_cast(&rect); - } - - static constexpr DlMatrix ToDlMatrix(const SkMatrix& matrix) { - // clang-format off - return DlMatrix::MakeColumn( - matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY], 0.0f, matrix[SkMatrix::kMPersp0], - matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY], 0.0f, matrix[SkMatrix::kMPersp1], - 0.0f, 0.0f, 1.0f, 0.0f, - matrix[SkMatrix::kMTransX], matrix[SkMatrix::kMTransY], 0.0f, matrix[SkMatrix::kMPersp2] - ); - // clang-format on - } - - static constexpr DlMatrix ToDlMatrix(const SkM44& matrix) { - DlMatrix dl_matrix; - matrix.getColMajor(dl_matrix.m); - return dl_matrix; - } - - static const SkRect& ToSkRect(const DlRect& rect) { - return *reinterpret_cast(&rect); - } - - static constexpr SkMatrix ToSkMatrix(const DlMatrix& matrix) { - return SkMatrix::MakeAll(matrix.m[0], matrix.m[4], matrix.m[12], - matrix.m[1], matrix.m[5], matrix.m[13], - matrix.m[3], matrix.m[7], matrix.m[15]); - } - - static constexpr SkM44 ToSkM44(const DlMatrix& matrix) { - return SkM44::ColMajor(matrix.m); - } public: - DisplayListMatrixClipState(const DlRect& cull_rect, const DlMatrix& matrix); + explicit DisplayListMatrixClipState(const DlRect& cull_rect, + const DlMatrix& matrix = DlMatrix()); + explicit DisplayListMatrixClipState(const SkRect& cull_rect); DisplayListMatrixClipState(const SkRect& cull_rect, const SkMatrix& matrix); DisplayListMatrixClipState(const SkRect& cull_rect, const SkM44& matrix); DisplayListMatrixClipState(const DisplayListMatrixClipState& other) = default; diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 9757eb9b7bd27..919141c160ce1 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -744,7 +744,9 @@ struct TRect { }; using Rect = TRect; -using IRect = TRect; +using IRect32 = TRect; +using IRect64 = TRect; +using IRect = IRect64; #undef ONLY_ON_FLOAT #undef ONLY_ON_FLOAT_M diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 9841b63d32642..45554734a15f4 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -135,7 +135,9 @@ constexpr TSize operator/(U s, const TSize& p) { } using Size = TSize; -using ISize = TSize; +using ISize32 = TSize; +using ISize64 = TSize; +using ISize = ISize64; static_assert(sizeof(Size) == 2 * sizeof(Scalar)); From 20ea6331e17d68bb11d3ac9c1fddbd9ba4420a17 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 8 May 2024 08:46:06 -0700 Subject: [PATCH 12/22] [Impeller] Update BlitPass::AddCopy to use destination_region instead of origin for buffer to texture copies. (#52555) Based on https://github.com/flutter/engine/pull/52510 Work towards https://github.com/flutter/flutter/issues/138798 Change IPoint destination_origin to IRect destination_region, which allows us to specify an area smaller than the entire texture to replace. This will eventually allow us to only upload individual glyphs. This fixes the cubemap issue I previously hit: each face needs to track initialization separately. --- impeller/aiks/aiks_unittests.cc | 34 +++++++++++++++++++ .../backend/gles/blit_command_gles.cc | 28 ++++++++++++--- .../renderer/backend/gles/blit_pass_gles.cc | 4 +-- .../renderer/backend/gles/blit_pass_gles.h | 2 +- .../renderer/backend/gles/proc_table_gles.h | 1 + .../renderer/backend/gles/texture_gles.cc | 19 ++++++----- impeller/renderer/backend/gles/texture_gles.h | 11 ++++-- .../backend/metal/blit_command_mtl.mm | 15 ++++---- .../renderer/backend/metal/blit_pass_mtl.h | 2 +- .../renderer/backend/metal/blit_pass_mtl.mm | 4 +-- .../backend/vulkan/blit_command_vk.cc | 10 +++--- .../renderer/backend/vulkan/blit_pass_vk.cc | 4 +-- .../renderer/backend/vulkan/blit_pass_vk.h | 3 +- impeller/renderer/blit_command.h | 2 +- impeller/renderer/blit_pass.cc | 23 ++++++++++--- impeller/renderer/blit_pass.h | 17 +++++++--- impeller/renderer/blit_pass_unittests.cc | 29 ++++++++++++++++ impeller/renderer/testing/mocks.h | 2 +- testing/impeller_golden_tests_output.txt | 3 ++ 19 files changed, 165 insertions(+), 48 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index bdc8533b04f8f..b214c91d027a8 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -20,6 +20,7 @@ #include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/aiks/testing/context_spy.h" +#include "impeller/core/device_buffer.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" @@ -3151,6 +3152,39 @@ TEST_P(AiksTest, CanRenderTextWithLargePerspectiveTransform) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, SetContentsWithRegion) { + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + + // Replace part of the texture with a red rectangle. + std::vector bytes(100 * 100 * 4); + for (auto i = 0u; i < bytes.size(); i += 4) { + bytes[i] = 255; + bytes[i + 1] = 0; + bytes[i + 2] = 0; + bytes[i + 3] = 255; + } + auto mapping = + std::make_shared(bytes.data(), bytes.size()); + auto device_buffer = + GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping); + auto cmd_buffer = GetContext()->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge, + IRect::MakeLTRB(50, 50, 150, 150)); + + auto did_submit = + blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) && + GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok(); + ASSERT_TRUE(did_submit); + + auto image = std::make_shared(bridge); + + Canvas canvas; + canvas.DrawImage(image, {0, 0}, {}); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc index 93753abbdb18a..2d104449a0d25 100644 --- a/impeller/renderer/backend/gles/blit_command_gles.cc +++ b/impeller/renderer/backend/gles/blit_command_gles.cc @@ -219,7 +219,9 @@ bool BlitCopyBufferToTextureCommandGLES::Encode( } if (!tex_descriptor.IsValid() || - source.range.length < tex_descriptor.GetByteSizeOfBaseMipLevel()) { + source.range.length != + BytesPerPixelForPixelFormat(tex_descriptor.format) * + destination_region.Area()) { return false; } @@ -263,9 +265,9 @@ bool BlitCopyBufferToTextureCommandGLES::Encode( const GLvoid* tex_data = data.buffer_view.buffer->OnGetContents() + data.buffer_view.range.offset; - { - TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes", - std::to_string(data.buffer_view.range.length).c_str()); + // GL_INVALID_OPERATION if the texture array has not been + // defined by a previous glTexImage2D operation. + if (!texture_gles.IsSliceInitialized(slice)) { gl.TexImage2D(texture_target, // target 0u, // LOD level data.internal_format, // internal format @@ -276,8 +278,24 @@ bool BlitCopyBufferToTextureCommandGLES::Encode( data.type, // type tex_data // data ); + texture_gles.MarkSliceInitialized(slice); + } + + { + TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes", + std::to_string(data.buffer_view.range.length).c_str()); + gl.TexSubImage2D(texture_target, // target + 0u, // LOD level + destination_region.GetX(), // xoffset + destination_region.GetY(), // yoffset + destination_region.GetWidth(), // width + destination_region.GetHeight(), // height + data.external_format, // external format + data.type, // type + tex_data // data + + ); } - texture_gles.MarkContentsInitialized(); return true; } diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc index 100259a490e7b..363400df99c9d 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.cc +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -130,14 +130,14 @@ bool BlitPassGLES::OnCopyTextureToBufferCommand( bool BlitPassGLES::OnCopyBufferToTextureCommand( BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) { auto command = std::make_unique(); command->label = label; command->source = std::move(source); command->destination = std::move(destination); - command->destination_origin = destination_origin; + command->destination_region = destination_region; command->label = label; command->slice = slice; diff --git a/impeller/renderer/backend/gles/blit_pass_gles.h b/impeller/renderer/backend/gles/blit_pass_gles.h index 58eaed6e8eb1d..1b5385f874e35 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.h +++ b/impeller/renderer/backend/gles/blit_pass_gles.h @@ -57,7 +57,7 @@ class BlitPassGLES final : public BlitPass, // |BlitPass| bool OnCopyBufferToTextureCommand(BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) override; diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h index 88397c5d68278..0ea7ba2a12041 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -170,6 +170,7 @@ struct GLProc { PROC(StencilMaskSeparate); \ PROC(StencilOpSeparate); \ PROC(TexImage2D); \ + PROC(TexSubImage2D); \ PROC(TexParameteri); \ PROC(TexParameterfv); \ PROC(Uniform1fv); \ diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index af745e23fd565..96939b0676e3c 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -304,8 +304,8 @@ bool TextureGLES::OnSetContents(std::shared_ptr mapping, } }; - contents_initialized_ = reactor_->AddOperation(texture_upload); - return contents_initialized_; + slices_initialized_ = reactor_->AddOperation(texture_upload); + return slices_initialized_[0]; } // |Texture| @@ -343,13 +343,10 @@ static std::optional ToRenderBufferFormat(PixelFormat format) { } void TextureGLES::InitializeContentsIfNecessary() const { - if (!IsValid()) { - return; - } - if (contents_initialized_) { + if (!IsValid() || slices_initialized_[0]) { return; } - contents_initialized_ = true; + slices_initialized_[0] = true; if (is_wrapped_) { return; @@ -455,8 +452,12 @@ bool TextureGLES::Bind() const { return true; } -void TextureGLES::MarkContentsInitialized() const { - contents_initialized_ = true; +void TextureGLES::MarkSliceInitialized(size_t slice) const { + slices_initialized_[slice] = true; +} + +bool TextureGLES::IsSliceInitialized(size_t slice) const { + return slices_initialized_[slice]; } bool TextureGLES::GenerateMipmap() { diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index a1827876d8a8d..86c8b822352e7 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_TEXTURE_GLES_H_ #define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_TEXTURE_GLES_H_ +#include + #include "impeller/base/backend_cast.h" #include "impeller/core/texture.h" #include "impeller/renderer/backend/gles/handle_gles.h" @@ -60,13 +62,18 @@ class TextureGLES final : public Texture, std::optional GetFBO() const { return wrapped_fbo_; } - void MarkContentsInitialized() const; + // For non cubemap textures, 0 indicates uninitialized and 1 indicates + // initialized. For cubemap textures, each face is initialized separately with + // each bit tracking the initialization of the corresponding slice. + void MarkSliceInitialized(size_t slice) const; + + bool IsSliceInitialized(size_t slice) const; private: ReactorGLES::Ref reactor_; const Type type_; HandleGLES handle_; - mutable bool contents_initialized_ = false; + mutable std::bitset<6> slices_initialized_ = 0; const bool is_wrapped_; const std::optional wrapped_fbo_; bool is_valid_ = false; diff --git a/impeller/renderer/backend/metal/blit_command_mtl.mm b/impeller/renderer/backend/metal/blit_command_mtl.mm index f07c8d09bd558..ca73897cdbccf 100644 --- a/impeller/renderer/backend/metal/blit_command_mtl.mm +++ b/impeller/renderer/backend/metal/blit_command_mtl.mm @@ -114,22 +114,21 @@ } auto destination_origin_mtl = - MTLOriginMake(destination_origin.x, destination_origin.y, 0); - - auto image_size = destination->GetTextureDescriptor().size; - auto source_size_mtl = MTLSizeMake(image_size.width, image_size.height, 1); + MTLOriginMake(destination_region.GetX(), destination_region.GetY(), 0); + auto source_size_mtl = MTLSizeMake(destination_region.GetWidth(), + destination_region.GetHeight(), 1); auto destination_bytes_per_pixel = BytesPerPixelForPixelFormat(destination->GetTextureDescriptor().format); auto destination_bytes_per_row = - source_size_mtl.width * destination_bytes_per_pixel; - auto destination_bytes_per_image = - source_size_mtl.height * destination_bytes_per_row; + destination_region.GetWidth() * destination_bytes_per_pixel; [encoder copyFromBuffer:source_mtl sourceOffset:source.range.offset sourceBytesPerRow:destination_bytes_per_row - sourceBytesPerImage:destination_bytes_per_image + sourceBytesPerImage: + 0 // 0 for 2D textures according to + // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400752-copyfrombuffer sourceSize:source_size_mtl toTexture:destination_mtl destinationSlice:slice diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.h b/impeller/renderer/backend/metal/blit_pass_mtl.h index 6ddfe3d55a2e0..b21a04e20a107 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.h +++ b/impeller/renderer/backend/metal/blit_pass_mtl.h @@ -55,7 +55,7 @@ class BlitPassMTL final : public BlitPass { // |BlitPass| bool OnCopyBufferToTextureCommand(BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) override; diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm index ce58225feb58e..172a36212dbb0 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.mm +++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm @@ -127,14 +127,14 @@ bool BlitPassMTL::OnCopyBufferToTextureCommand( BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) { auto command = std::make_unique(); command->label = std::move(label); command->source = std::move(source); command->destination = std::move(destination); - command->destination_origin = destination_origin; + command->destination_region = destination_region; command->slice = slice; commands_.emplace_back(std::move(command)); diff --git a/impeller/renderer/backend/vulkan/blit_command_vk.cc b/impeller/renderer/backend/vulkan/blit_command_vk.cc index 922c2d2848c31..bc1c21947821c 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk.cc @@ -246,10 +246,12 @@ bool BlitCopyBufferToTextureCommandVK::Encode(CommandEncoderVK& encoder) const { image_copy.setBufferImageHeight(0); image_copy.setImageSubresource( vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1)); - image_copy.setImageOffset( - vk::Offset3D(destination_origin.x, destination_origin.y, 0)); - image_copy.setImageExtent(vk::Extent3D(destination->GetSize().width, - destination->GetSize().height, 1)); + image_copy.imageOffset.x = destination_region.GetX(); + image_copy.imageOffset.y = destination_region.GetY(); + image_copy.imageOffset.z = 0u; + image_copy.imageExtent.width = destination_region.GetWidth(); + image_copy.imageExtent.height = destination_region.GetHeight(); + image_copy.imageExtent.depth = 1u; if (!dst.SetLayout(dst_barrier)) { VALIDATION_LOG << "Could not encode layout transition."; diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.cc b/impeller/renderer/backend/vulkan/blit_pass_vk.cc index 1dfb570fd7e31..814ebd1efe39b 100644 --- a/impeller/renderer/backend/vulkan/blit_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.cc @@ -96,14 +96,14 @@ bool BlitPassVK::OnCopyTextureToBufferCommand( bool BlitPassVK::OnCopyBufferToTextureCommand( BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) { auto command = std::make_unique(); command->source = std::move(source); command->destination = std::move(destination); - command->destination_origin = destination_origin; + command->destination_region = destination_region; command->label = std::move(label); command->slice = slice; diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.h b/impeller/renderer/backend/vulkan/blit_pass_vk.h index f693023ff68dd..1d336c27a424d 100644 --- a/impeller/renderer/backend/vulkan/blit_pass_vk.h +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.h @@ -6,6 +6,7 @@ #define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_BLIT_PASS_VK_H_ #include "flutter/impeller/base/config.h" +#include "impeller/geometry/rect.h" #include "impeller/renderer/backend/vulkan/blit_command_vk.h" #include "impeller/renderer/blit_pass.h" @@ -55,7 +56,7 @@ class BlitPassVK final : public BlitPass { // |BlitPass| bool OnCopyBufferToTextureCommand(BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) override; // |BlitPass| diff --git a/impeller/renderer/blit_command.h b/impeller/renderer/blit_command.h index 07c951e470aad..0bae872ab0796 100644 --- a/impeller/renderer/blit_command.h +++ b/impeller/renderer/blit_command.h @@ -32,7 +32,7 @@ struct BlitCopyTextureToBufferCommand : public BlitCommand { struct BlitCopyBufferToTextureCommand : public BlitCommand { BufferView source; std::shared_ptr destination; - IPoint destination_origin; + IRect destination_region; uint32_t slice = 0; }; diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index 56e919b7ac9a1..cd9511d56abb6 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -122,28 +122,41 @@ bool BlitPass::AddCopy(std::shared_ptr source, bool BlitPass::AddCopy(BufferView source, std::shared_ptr destination, - IPoint destination_origin, + std::optional destination_region, std::string label, uint32_t slice) { if (!destination) { VALIDATION_LOG << "Attempted to add a texture blit with no destination."; return false; } + ISize destination_size = destination->GetSize(); + IRect destination_region_value = + destination_region.value_or(IRect::MakeSize(destination_size)); + if (destination_region_value.GetX() < 0 || + destination_region_value.GetY() < 0 || + destination_region_value.GetRight() > destination_size.width || + destination_region_value.GetBottom() > destination_size.height) { + VALIDATION_LOG << "Blit region cannot be larger than destination texture."; + return false; + } auto bytes_per_pixel = BytesPerPixelForPixelFormat(destination->GetTextureDescriptor().format); - auto bytes_per_image = - destination->GetTextureDescriptor().size.Area() * bytes_per_pixel; + auto bytes_per_image = destination_region_value.Area() * bytes_per_pixel; if (source.range.length != bytes_per_image) { VALIDATION_LOG << "Attempted to add a texture blit with out of bounds access."; return false; } + if (slice > 5) { + VALIDATION_LOG << "Invalid value for slice: " << slice; + return false; + } return OnCopyBufferToTextureCommand(std::move(source), std::move(destination), - destination_origin, std::move(label), - slice); + destination_region_value, + std::move(label), slice); } bool BlitPass::GenerateMipmap(std::shared_ptr texture, diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index 3ea790c5a438a..5b2f56b33bd29 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -89,8 +89,9 @@ class BlitPass { /// @param[in] source The buffer view to read for copying. /// @param[in] destination The texture to overwrite using the source /// contents. - /// @param[in] destination_offset The offset to start writing to in the - /// destination buffer. + /// @param[in] destination_region The offset to start writing to in the + /// destination texture. If not provided, this + /// defaults to the entire texture. /// @param[in] label The optional debug label to give the /// command. /// @param[in] slice For cubemap textures, the slice to write @@ -98,9 +99,17 @@ class BlitPass { /// /// @return If the command was valid for subsequent commitment. /// + /// If a region smaller than the texture size is provided, the + /// contents are treated as containing tightly packed pixel data of + /// that region. Only the portion of the texture in this region is + /// replaced and existing data is preserved. + /// + /// For example, to replace the top left 10 x 10 region of a larger + /// 100 x 100 texture, the region is {0, 0, 10, 10} and the expected + /// buffer size in bytes is 100 x bpp. bool AddCopy(BufferView source, std::shared_ptr destination, - IPoint destination_origin = {}, + std::optional destination_region = std::nullopt, std::string label = "", uint32_t slice = 0); @@ -148,7 +157,7 @@ class BlitPass { virtual bool OnCopyBufferToTextureCommand( BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_region, std::string label, uint32_t slice) = 0; diff --git a/impeller/renderer/blit_pass_unittests.cc b/impeller/renderer/blit_pass_unittests.cc index dbf534d1deda5..732af99108a1a 100644 --- a/impeller/renderer/blit_pass_unittests.cc +++ b/impeller/renderer/blit_pass_unittests.cc @@ -4,6 +4,7 @@ #include "gtest/gtest.h" #include "impeller/base/validation.h" +#include "impeller/core/device_buffer_descriptor.h" #include "impeller/core/formats.h" #include "impeller/core/texture_descriptor.h" #include "impeller/playground/playground_test.h" @@ -75,5 +76,33 @@ TEST_P(BlitPassTest, BlitPassesForMatchingFormats) { EXPECT_TRUE(blit_pass->AddCopy(src, dst)); } +TEST_P(BlitPassTest, ChecksInvalidSliceParameters) { + ScopedValidationDisable scope; // avoid noise in output. + auto context = GetContext(); + auto cmd_buffer = context->CreateCommandBuffer(); + auto blit_pass = cmd_buffer->CreateBlitPass(); + + TextureDescriptor dst_format; + dst_format.storage_mode = StorageMode::kDevicePrivate; + dst_format.format = PixelFormat::kR8G8B8A8UNormInt; + dst_format.size = {100, 100}; + auto dst = context->GetResourceAllocator()->CreateTexture(dst_format); + + DeviceBufferDescriptor src_format; + src_format.size = 40000; + src_format.storage_mode = StorageMode::kHostVisible; + auto src = context->GetResourceAllocator()->CreateBuffer(src_format); + + ASSERT_TRUE(dst); + ASSERT_TRUE(src); + + EXPECT_FALSE(blit_pass->AddCopy(DeviceBuffer::AsBufferView(src), dst, + std::nullopt, "", /*slice=*/25)); + EXPECT_FALSE(blit_pass->AddCopy(DeviceBuffer::AsBufferView(src), dst, + std::nullopt, "", /*slice=*/6)); + EXPECT_TRUE(blit_pass->AddCopy(DeviceBuffer::AsBufferView(src), dst, + std::nullopt, "", /*slice=*/0)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/testing/mocks.h b/impeller/renderer/testing/mocks.h index 848a811393414..c333ac9624a55 100644 --- a/impeller/renderer/testing/mocks.h +++ b/impeller/renderer/testing/mocks.h @@ -82,7 +82,7 @@ class MockBlitPass : public BlitPass { OnCopyBufferToTextureCommand, (BufferView source, std::shared_ptr destination, - IPoint destination_origin, + IRect destination_rect, std::string label, uint32_t slice), (override)); diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index 4c37c79c51c0c..f267f1d5759c8 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -702,6 +702,9 @@ impeller_Play_AiksTest_SaveLayerDrawsBehindSubsequentEntities_Vulkan.png impeller_Play_AiksTest_SaveLayerFiltersScaleWithTransform_Metal.png impeller_Play_AiksTest_SaveLayerFiltersScaleWithTransform_OpenGLES.png impeller_Play_AiksTest_SaveLayerFiltersScaleWithTransform_Vulkan.png +impeller_Play_AiksTest_SetContentsWithRegion_Metal.png +impeller_Play_AiksTest_SetContentsWithRegion_OpenGLES.png +impeller_Play_AiksTest_SetContentsWithRegion_Vulkan.png impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_Metal.png impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_OpenGLES.png impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_Vulkan.png From 0e23e60093d162e19108fcf28233f918fdafc136 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 May 2024 11:56:04 -0400 Subject: [PATCH 13/22] Manual roll Dart SDK from a9cf6a411c71 to b7cad2edae4b (7 revisions) (#52669) Manual roll requested by zra@google.com https://dart.googlesource.com/sdk.git/+log/a9cf6a411c71..b7cad2edae4b 2024-05-08 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-138.0.dev 2024-05-08 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-137.0.dev 2024-05-08 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-136.0.dev 2024-05-07 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-135.0.dev 2024-05-07 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-134.0.dev 2024-05-07 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-133.0.dev 2024-05-07 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-132.0.dev If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter Engine: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 106 +++++++++++++++---------------- ci/licenses_golden/licenses_dart | 4 +- sky/packages/sky_engine/LICENSE | 2 +- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/DEPS b/DEPS index a9e03a34dabcf..0fc578c594589 100644 --- a/DEPS +++ b/DEPS @@ -56,28 +56,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': 'a9cf6a411c717916c4a170fcf7f6836a93a8ea04', + 'dart_revision': 'b7cad2edae4bcbe05332c9956a04692d19798904', # 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': '9784f012848a7eb321c2037bdb363dfe0eab8bc9', + 'dart_binaryen_rev': '4b7c610f14329dfe045534ec1cde7f28330393f9', 'dart_boringssl_gen_rev': '9c7294fd58261a79794f5afaa26598cf1442ad20', 'dart_boringssl_rev': 'd24a38200fef19150eef00cad35b138936c08767', - 'dart_browser_launcher_rev': 'c4b2c81aa9debcce3651eda1b68a9bc5d5adf400', - 'dart_clock_rev': 'a732a09149708ae0b986bec9931576d22818d158', - 'dart_collection_rev': 'fc616ff8fd7b732c07b2b19e31b6601e59900ccf', - 'dart_devtools_rev': '486dae7785dc0bda5cc0c463ab704e469bb03f1a', + 'dart_browser_launcher_rev': '0dcf2246c11eaf6c4f2591332f1057734a847793', + 'dart_clock_rev': '80e70acf72cc3a876d3158911b097b581cd8fd1a', + 'dart_collection_rev': '471839875a3bbfb26a7d51eca82be5b788660982', + 'dart_devtools_rev': '73489f91e37c8f51161b3c63ffced83318e14424', 'dart_libprotobuf_rev': '24487dd1045c7f3d64a21f38a3f0c06cc4cf2edb', 'dart_perfetto_rev': '13ce0c9e13b0940d2476cd0cff2301708a9a2e2b', 'dart_protobuf_gn_rev': 'ca669f79945418f6229e4fef89b666b2a88cbb10', 'dart_protobuf_rev': 'ccf104dbc36929c0f8708285d5f3a8fae206343e', 'dart_pub_rev': '55cc6c138ad4777cf4fc0710c55dc37a33fdcd69', - 'dart_tools_rev': '3e21ae9cea75987365249f2c067eabd16fbaf2d7', - 'dart_watcher_rev': '1bd2f20d0d924c8422aa2b9afdb165bff4f053c0', - 'dart_web_rev': '9d8c802d13b785b1a5b201c4a43605d640841c98', - 'dart_webdev_rev': '50bf268794a4cb70b3a82d8177e55160ee4227df', - 'dart_webkit_inspection_protocol_rev': '153fea4fe5ac45bebf0c2e76bb3d76b0f1fcdaae', - 'dart_yaml_edit_rev': 'f5a92b3bc64b4e78ec1fb616ad1dff2174e799b1', + 'dart_tools_rev': 'd35d633e6df04f8a60a305a36437f67f9fefca5d', + 'dart_watcher_rev': '42308ea9e22bc2d1edab497ee58c0df0affbfed0', + 'dart_web_rev': 'ff7185c6c0360987116115039a08d9134c8d62aa', + 'dart_webdev_rev': 'c233e453d3172536d2e04575d752c1a7c0acae57', + 'dart_webkit_inspection_protocol_rev': '5740cc91eaeb13a02007b77b128fccf4b056db6e', + 'dart_yaml_edit_rev': 'c4629ad7eec13ddf449918ab8a796e0403330e3f', 'ocmock_rev': 'c4ec0e3a7a9f56cfdbd0aa01f4f97bb4b75c5ef8', # v3.7.1 @@ -344,28 +344,28 @@ deps = { # WARNING: Unused Dart dependencies in the list below till "WARNING:" marker are removed automatically - see create_updated_flutter_deps.py. 'src/flutter/third_party/dart/third_party/binaryen/src': - Var('chromium_git') + '/external/github.com/WebAssembly/binaryen.git@9784f012848a7eb321c2037bdb363dfe0eab8bc9', + Var('chromium_git') + '/external/github.com/WebAssembly/binaryen.git@4b7c610f14329dfe045534ec1cde7f28330393f9', 'src/flutter/third_party/dart/third_party/devtools': - {'dep_type': 'cipd', 'packages': [{'package': 'dart/third_party/flutter/devtools', 'version': 'git_revision:486dae7785dc0bda5cc0c463ab704e469bb03f1a'}]}, + {'dep_type': 'cipd', 'packages': [{'package': 'dart/third_party/flutter/devtools', 'version': 'git_revision:73489f91e37c8f51161b3c63ffced83318e14424'}]}, 'src/flutter/third_party/dart/third_party/pkg/args': Var('dart_git') + '/args.git@5c83bc9785d6c32ffe6824ba79fadcc51fbcd1c1', 'src/flutter/third_party/dart/third_party/pkg/async': - Var('dart_git') + '/async.git@47968047eb9888f74ca0691640821bd55b47e763', + Var('dart_git') + '/async.git@09cba7fb16aa66886951872a68bbd49ad8d09cf8', 'src/flutter/third_party/dart/third_party/pkg/bazel_worker': - Var('dart_git') + '/bazel_worker.git@79d2ad13c83d5e0883136503d86ddf60fe665900', + Var('dart_git') + '/bazel_worker.git@2fb4fbff6ee8d26fee1f3576fa0500057d897afd', 'src/flutter/third_party/dart/third_party/pkg/boolean_selector': - Var('dart_git') + '/boolean_selector.git@24635df68661bb44c1c13fb405562421e24298e5', + Var('dart_git') + '/boolean_selector.git@2cbd4a60b89fe51a404e9ee3625a8fb8019561dd', 'src/flutter/third_party/dart/third_party/pkg/browser_launcher': Var('dart_git') + '/browser_launcher.git' + '@' + Var('dart_browser_launcher_rev'), 'src/flutter/third_party/dart/third_party/pkg/cli_util': - Var('dart_git') + '/cli_util.git@e222c560d88d127bc5e991ca96be2b1e875f4f3e', + Var('dart_git') + '/cli_util.git@9fe3eeb8a2fad6da9a156055207337474436da12', 'src/flutter/third_party/dart/third_party/pkg/clock': Var('dart_git') + '/clock.git' + '@' + Var('dart_clock_rev'), @@ -374,61 +374,61 @@ deps = { Var('dart_git') + '/collection.git' + '@' + Var('dart_collection_rev'), 'src/flutter/third_party/dart/third_party/pkg/convert': - Var('dart_git') + '/convert.git@186ac2281f0436451b49e1bd8458ff791b42637d', + Var('dart_git') + '/convert.git@056626e0cddd56c4cc1184aac787ba06ecdaae3a', 'src/flutter/third_party/dart/third_party/pkg/crypto': - Var('dart_git') + '/crypto.git@1c7fbadd92a44322ee1e89eec9747a24dc6c6746', + Var('dart_git') + '/crypto.git@3f815aca8ad5020bb39be09ec9bfd75c36910809', 'src/flutter/third_party/dart/third_party/pkg/csslib': - Var('dart_git') + '/csslib.git@171ed4885f1db13e22b04b513a169e7a99d2c5d5', + Var('dart_git') + '/csslib.git@141dd6567651500bb8c17ccb65e3c9e117c64035', 'src/flutter/third_party/dart/third_party/pkg/dart_style': Var('dart_git') + '/dart_style.git@a6ad7693555a9add6f98ad6fd94de80d35c89415', 'src/flutter/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@d9e31ff950c92350b464b859eaac272a97e15070', + Var('dart_git') + '/dartdoc.git@6e9c1eae7ef0a1521e865dafbacc2c8a1d2fd9dd', 'src/flutter/third_party/dart/third_party/pkg/file': - Var('dart_git') + '/external/github.com/google/file.dart@f858c6fe9d1b0167b944aa62dd9b4321036b5238', + Var('dart_git') + '/external/github.com/google/file.dart@8ce0d13ffe9dac267bdbd6c65c145ba4f611af72', 'src/flutter/third_party/dart/third_party/pkg/fixnum': - Var('dart_git') + '/fixnum.git@dec16eb715f70f2fe0ed509da2e118354bea21d8', + Var('dart_git') + '/fixnum.git@2c9f25d5057ce4b450ea193a8501e9f79a1b1dc8', 'src/flutter/third_party/dart/third_party/pkg/glob': - Var('dart_git') + '/glob.git@25ee2c20a3c7b93f4096036939a59e40916a3890', + Var('dart_git') + '/glob.git@44e8c22f9271a62b03d2551d1e7b7ac18d589f16', 'src/flutter/third_party/dart/third_party/pkg/html': - Var('dart_git') + '/html.git@5b99b43f71da0c6b0f962244c7f5dfa8ca970fef', + Var('dart_git') + '/html.git@44613e897fc80f2fcd43a355c98506df12fd38e2', 'src/flutter/third_party/dart/third_party/pkg/http': - Var('dart_git') + '/http.git@e459e5c6f6c60d56a42755164526501f3daa66df', + Var('dart_git') + '/http.git@dd31e64685b03a9700cc99d7b7fc637dd60c4d3a', 'src/flutter/third_party/dart/third_party/pkg/http_multi_server': - Var('dart_git') + '/http_multi_server.git@6ce0a137aa20cc12b883a386bb7c0ae677cb3429', + Var('dart_git') + '/http_multi_server.git@e45a674bf4d2552438e3632cdc40f303f4b01e74', 'src/flutter/third_party/dart/third_party/pkg/http_parser': - Var('dart_git') + '/http_parser.git@8ffcaec6fedfac5274cfa57e133d47d499bdabc0', + Var('dart_git') + '/http_parser.git@ad0581078d45bfa3b2ded5b7d9a5f7797c8d109d', 'src/flutter/third_party/dart/third_party/pkg/intl': Var('dart_git') + '/intl.git@5d65e3808ce40e6282e40881492607df4e35669f', 'src/flutter/third_party/dart/third_party/pkg/json_rpc_2': - Var('dart_git') + '/json_rpc_2.git@1a4c47339a741c7554996e467cf2b8bf515ca5b9', + Var('dart_git') + '/json_rpc_2.git@7547bb16a90dbd436d50dad93f2e7a9dc0956423', 'src/flutter/third_party/dart/third_party/pkg/leak_tracker': Var('dart_git') + '/leak_tracker.git@f5620600a5ce1c44f65ddaa02001e200b096e14c', 'src/flutter/third_party/dart/third_party/pkg/logging': - Var('dart_git') + '/logging.git@dcaf2498e5ce34b17b48770b4e78c44fc9e63cd3', + Var('dart_git') + '/logging.git@49d89b1de6e847174bc93b709e858b99e61b2ae7', 'src/flutter/third_party/dart/third_party/pkg/markdown': - Var('dart_git') + '/markdown.git@782b1803a29aa964410d93b4437d5d1efa47f6b4', + Var('dart_git') + '/markdown.git@6aaa152f30255cc660d387b6e4e34543afbb277c', 'src/flutter/third_party/dart/third_party/pkg/matcher': - Var('dart_git') + '/matcher.git@54c2798d9b75c6db5cdcc7acbf5b9ae215316ed5', + Var('dart_git') + '/matcher.git@b42bf10b0da3e5b748bbc51bdc1dfaeacf824563', 'src/flutter/third_party/dart/third_party/pkg/mime': - Var('dart_git') + '/mime.git@0a32241c4fcd077a945949760b287677e4d7aff4', + Var('dart_git') + '/mime.git@b01c9a24e0991da479bd405138be3b3e403ff456', 'src/flutter/third_party/dart/third_party/pkg/mockito': Var('dart_git') + '/mockito.git@81ecb88b631a9c1e677852a33ad916238dee0ef2', @@ -437,13 +437,13 @@ deps = { Var('dart_git') + '/native.git@71ada4b5c8001e6b8207ed40331d158e0912cd94', 'src/flutter/third_party/dart/third_party/pkg/package_config': - Var('dart_git') + '/package_config.git@854dc194131af8e2c16ef46ad210af9b2ef45fa9', + Var('dart_git') + '/package_config.git@a36e496d61937a800c22f68035dc98bf8ead7fb2', 'src/flutter/third_party/dart/third_party/pkg/path': - Var('dart_git') + '/path.git@a7284b9917830c18618b26d396d0f0bffc92bb01', + Var('dart_git') + '/path.git@f411b96a2598d3911b485d606430fd775fd9e41a', 'src/flutter/third_party/dart/third_party/pkg/pool': - Var('dart_git') + '/pool.git@8055cbb290590e761e1149dfbf7c2283fdd88e4f', + Var('dart_git') + '/pool.git@e6df05a287a2193c4c566608f9cd12e8689d3264', 'src/flutter/third_party/dart/third_party/pkg/protobuf': Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_rev'), @@ -452,52 +452,52 @@ deps = { Var('dart_git') + '/pub.git' + '@' + Var('dart_pub_rev'), 'src/flutter/third_party/dart/third_party/pkg/pub_semver': - Var('dart_git') + '/pub_semver.git@7581029bf5346fd5a44b95f4686bba8de7598859', + Var('dart_git') + '/pub_semver.git@df834e180f86b6dac8ef0222a28987009ad75a75', 'src/flutter/third_party/dart/third_party/pkg/shelf': - Var('dart_git') + '/shelf.git@7352d1b7ac283df8eac36441d7569be8a36757f9', + Var('dart_git') + '/shelf.git@c8d8058e426bd6a874dd4ded29cd88216e248387', 'src/flutter/third_party/dart/third_party/pkg/source_map_stack_trace': - Var('dart_git') + '/source_map_stack_trace.git@9ed8875f0933bf0d8e09694211cb9765147b27bb', + Var('dart_git') + '/source_map_stack_trace.git@4671c20004a89a3eabd150f2fc65bcf4319bba49', 'src/flutter/third_party/dart/third_party/pkg/source_maps': - Var('dart_git') + '/source_maps.git@aedfc39235941a31070fe53149b5776ea29be44f', + Var('dart_git') + '/source_maps.git@c4dc7d082608405049f0cc0bc23ca693e4532815', 'src/flutter/third_party/dart/third_party/pkg/source_span': - Var('dart_git') + '/source_span.git@45e11a3a0e4b14116911fffc21e2cdc95824e28f', + Var('dart_git') + '/source_span.git@1695904bca53ae6d3d51f05d25ce6568b376aecd', 'src/flutter/third_party/dart/third_party/pkg/sse': - Var('dart_git') + '/sse.git@1ab266ad570134294468225d1c2ffe86338005be', + Var('dart_git') + '/sse.git@f5ed4d6339794db5ec98969f3f60d0ce8c44dd60', 'src/flutter/third_party/dart/third_party/pkg/stack_trace': - Var('dart_git') + '/stack_trace.git@c39ae6e955a76a2ff183288f051f4eee7a5e94d1', + Var('dart_git') + '/stack_trace.git@54c7b9da965bbdc4b49038ce0ab8ecdd0449e5fe', 'src/flutter/third_party/dart/third_party/pkg/stream_channel': - Var('dart_git') + '/stream_channel.git@5f72035464ea675c75982b80db967943a0cfddcb', + Var('dart_git') + '/stream_channel.git@ba351378fbddffd736e2faf6a412568f9aba0536', 'src/flutter/third_party/dart/third_party/pkg/string_scanner': - Var('dart_git') + '/string_scanner.git@8dbfddf9a7e8cdd4978ddd3cdab188cfabc543fd', + Var('dart_git') + '/string_scanner.git@6e893afd9a8f0ff746156acc103a2dd1646a85fb', 'src/flutter/third_party/dart/third_party/pkg/tar': Var('dart_git') + '/external/github.com/simolus3/tar.git@6150a0c88d9bd17e3e961593a9a7a9564866e8b4', 'src/flutter/third_party/dart/third_party/pkg/term_glyph': - Var('dart_git') + '/term_glyph.git@2ad48ce95b6e6d001251d55b15719ee113caf802', + Var('dart_git') + '/term_glyph.git@c3e323bcfe3f992c2b3059e141eb5ccbc1ba39c4', 'src/flutter/third_party/dart/third_party/pkg/test': - Var('dart_git') + '/test.git@c7a76b08c11afb27fdb61d65b5fc757e4274928c', + Var('dart_git') + '/test.git@0562baf0994ee76fcdcc55bf1d70e00ddeed44f3', 'src/flutter/third_party/dart/third_party/pkg/test_reflective_loader': - Var('dart_git') + '/test_reflective_loader.git@d7167a2375d8a0c02c12b960c059a115a777f238', + Var('dart_git') + '/test_reflective_loader.git@537ab5b24632c03918fb229531cfb41946eeae27', 'src/flutter/third_party/dart/third_party/pkg/tools': Var('dart_git') + '/tools.git' + '@' + Var('dart_tools_rev'), 'src/flutter/third_party/dart/third_party/pkg/typed_data': - Var('dart_git') + '/typed_data.git@8c7393cbbbba7a5d38c6772371f92d6b38e433fc', + Var('dart_git') + '/typed_data.git@5b3708eb633593b30868871301e8e15d344d7585', 'src/flutter/third_party/dart/third_party/pkg/usage': - Var('dart_git') + '/usage.git@2847cdb94e238a921c600e81b52c91baf4ea0b50', + Var('dart_git') + '/usage.git@bc4907bca87e2ef5e9ee80e1bef7ee57f5cbc6d2', 'src/flutter/third_party/dart/third_party/pkg/watcher': Var('dart_git') + '/watcher.git' + '@' + Var('dart_watcher_rev'), @@ -506,7 +506,7 @@ deps = { Var('dart_git') + '/web.git' + '@' + Var('dart_web_rev'), 'src/flutter/third_party/dart/third_party/pkg/web_socket_channel': - Var('dart_git') + '/web_socket_channel.git@b67222092b4153cd7d61b92744b4f0a620c55481', + Var('dart_git') + '/web_socket_channel.git@d86313dfb30fbc4c15c53424c3d637559ad978e1', 'src/flutter/third_party/dart/third_party/pkg/webdev': Var('dart_git') + '/webdev.git' + '@' + Var('dart_webdev_rev'), @@ -515,7 +515,7 @@ deps = { Var('dart_git') + '/external/github.com/google/webkit_inspection_protocol.dart.git' + '@' + Var('dart_webkit_inspection_protocol_rev'), 'src/flutter/third_party/dart/third_party/pkg/yaml': - Var('dart_git') + '/yaml.git@5a1c4be2437bc4122ccf08a3a0f06a7683e62f30', + Var('dart_git') + '/yaml.git@022ea6456d27ce6cc38f4d44833ba8865ac43b9f', 'src/flutter/third_party/dart/third_party/pkg/yaml_edit': Var('dart_git') + '/yaml_edit.git' + '@' + Var('dart_yaml_edit_rev'), diff --git a/ci/licenses_golden/licenses_dart b/ci/licenses_golden/licenses_dart index 56ada58ec70d2..a87cd5e39ac79 100644 --- a/ci/licenses_golden/licenses_dart +++ b/ci/licenses_golden/licenses_dart @@ -1,4 +1,4 @@ -Signature: b2a3037048bb465b665a641ba709d39f +Signature: bc915b1167a126718a286c70d54e2f3a ==================================================================================================== LIBRARY: dart @@ -4741,7 +4741,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/a9cf6a411c717916c4a170fcf7f6836a93a8ea04 +You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/b7cad2edae4bcbe05332c9956a04692d19798904 /third_party/fallback_root_certificates/ ==================================================================================================== diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index 456fcc59b7199..f47a16635657c 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -32293,7 +32293,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/a9cf6a411c717916c4a170fcf7f6836a93a8ea04 +You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/b7cad2edae4bcbe05332c9956a04692d19798904 /third_party/fallback_root_certificates/ -------------------------------------------------------------------------------- From 9cb60de37b6d582d886f46b86b90ccb9d6323fea Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 May 2024 12:08:23 -0400 Subject: [PATCH 14/22] Roll Skia from c7794fee8063 to d8427844b843 (2 revisions) (#52671) https://skia.googlesource.com/skia.git/+log/c7794fee8063..d8427844b843 2024-05-08 michaelludwig@google.com [graphite] Delete duplicate null check/warning 2024-05-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll vulkan-deps from e70a70873c7b to cb509807efbe (1 revision) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC brianosman@google.com,egdaniel@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 0fc578c594589..16dd48aab44b1 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': 'c7794fee80633070f2738239fb4ce792bcad81fd', + 'skia_revision': 'd8427844b843668ba0e81ae013aedde9aebcc2ea', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index ef54532d33aac..7341e4674392e 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ac73c5eeec4665c9449839594eb08f74 +Signature: de637806d74611b916ccf51d310ff740 ==================================================================================================== LIBRARY: etc1 From 86d2768e0f79a5df78885163b17717ec4000c4a7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 May 2024 13:57:57 -0400 Subject: [PATCH 15/22] Roll Dart SDK from b7cad2edae4b to 673b3aa8558a (1 revision) (#52677) https://dart.googlesource.com/sdk.git/+log/b7cad2edae4b..673b3aa8558a 2024-05-08 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-139.0.dev If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter Engine: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 16dd48aab44b1..7cef9ae3fe5b2 100644 --- a/DEPS +++ b/DEPS @@ -56,7 +56,7 @@ 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': 'b7cad2edae4bcbe05332c9956a04692d19798904', + 'dart_revision': '673b3aa8558a4af94a6c9f424b7c4569899bef9b', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -434,7 +434,7 @@ deps = { Var('dart_git') + '/mockito.git@81ecb88b631a9c1e677852a33ad916238dee0ef2', 'src/flutter/third_party/dart/third_party/pkg/native': - Var('dart_git') + '/native.git@71ada4b5c8001e6b8207ed40331d158e0912cd94', + Var('dart_git') + '/native.git@a152cfa21129510e9227962a01ec12918e192ecc', 'src/flutter/third_party/dart/third_party/pkg/package_config': Var('dart_git') + '/package_config.git@a36e496d61937a800c22f68035dc98bf8ead7fb2', From 0c417292c6bdac750ffede38998426fd6e825554 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 8 May 2024 10:59:55 -0700 Subject: [PATCH 16/22] [Impeller] require and use backpressure for AHB swapchain. (#52676) I think we might be buffer stuffing on the A02s specifically. Though I don't know if this will just "fix" it by turning off the swapchain for that device. https://github.com/flutter/flutter/issues/147721 --- impeller/toolkit/android/proc_table.h | 1 + impeller/toolkit/android/surface_control.cc | 4 +++- impeller/toolkit/android/surface_transaction.cc | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h index 9e639603b9c1b..3f34cfd65999a 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -60,6 +60,7 @@ namespace impeller::android { INVOKE(ASurfaceTransaction_setBuffer, 29) \ INVOKE(ASurfaceTransaction_setColor, 29) \ INVOKE(ASurfaceTransaction_setOnComplete, 29) \ + INVOKE(ASurfaceTransaction_setEnableBackPressure, 31) \ INVOKE(ASurfaceTransactionStats_getPreviousReleaseFenceFd, 29) \ INVOKE(ATrace_isEnabled, 23) \ INVOKE(eglGetNativeClientBufferANDROID, 0) diff --git a/impeller/toolkit/android/surface_control.cc b/impeller/toolkit/android/surface_control.cc index 71282e7ccc981..da6a48b572791 100644 --- a/impeller/toolkit/android/surface_control.cc +++ b/impeller/toolkit/android/surface_control.cc @@ -5,6 +5,7 @@ #include "impeller/toolkit/android/surface_control.h" #include "impeller/base/validation.h" +#include "impeller/toolkit/android/proc_table.h" #include "impeller/toolkit/android/surface_transaction.h" namespace impeller::android { @@ -49,7 +50,8 @@ bool SurfaceControl::RemoveFromParent() const { bool SurfaceControl::IsAvailableOnPlatform() { return GetProcTable().IsValid() && - GetProcTable().ASurfaceControl_createFromWindow.IsAvailable(); + GetProcTable().ASurfaceControl_createFromWindow.IsAvailable() && + GetProcTable().ASurfaceTransaction_setEnableBackPressure.IsAvailable(); } } // namespace impeller::android diff --git a/impeller/toolkit/android/surface_transaction.cc b/impeller/toolkit/android/surface_transaction.cc index 557dedb3cb261..454f62ddb203c 100644 --- a/impeller/toolkit/android/surface_transaction.cc +++ b/impeller/toolkit/android/surface_transaction.cc @@ -58,7 +58,13 @@ bool SurfaceTransaction::SetContents(const SurfaceControl* control, VALIDATION_LOG << "Invalid control or buffer."; return false; } - GetProcTable().ASurfaceTransaction_setBuffer( + + const auto& proc_table = GetProcTable(); + + proc_table.ASurfaceTransaction_setEnableBackPressure( + transaction_.get(), control->GetHandle(), true); + + proc_table.ASurfaceTransaction_setBuffer( transaction_.get(), // control->GetHandle(), // buffer->GetHandle(), // From cb7c1ed7c726ffd9548686b3547f612c429355f1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 May 2024 14:21:04 -0400 Subject: [PATCH 17/22] Roll Skia from d8427844b843 to d965fc1fd319 (4 revisions) (#52679) https://skia.googlesource.com/skia.git/+log/d8427844b843..d965fc1fd319 2024-05-08 michaelludwig@google.com [graphite] Add child DrawTasks to DrawContexts when drawn; track pending reads 2024-05-08 robertphillips@google.com [graphite] Make MatrixConvolutionImageFilter stably keyed 2024-05-08 michaelludwig@google.com [graphite] Don't instantiate budgeted Surfaces immediately 2024-05-08 kjlubick@google.com Update rules to enforce IWYU in the modular build If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC brianosman@google.com,egdaniel@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7cef9ae3fe5b2..a2ce3ca20ae9b 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': 'd8427844b843668ba0e81ae013aedde9aebcc2ea', + 'skia_revision': 'd965fc1fd31926d65514189b5f49512d53b3f777', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 7341e4674392e..7d2ee094a0b32 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: de637806d74611b916ccf51d310ff740 +Signature: c5c51f42f0f5f0d4128cd704127c8a5a ==================================================================================================== LIBRARY: etc1 @@ -9525,6 +9525,7 @@ ORIGIN: ../../../flutter/third_party/skia/modules/skunicode/src/SkBidiFactory_ic ORIGIN: ../../../flutter/third_party/skia/src/base/SkFixedArray.h + ../../../flutter/third_party/skia/LICENSE ORIGIN: ../../../flutter/third_party/skia/src/core/SkKnownRuntimeEffects.cpp + ../../../flutter/third_party/skia/LICENSE ORIGIN: ../../../flutter/third_party/skia/src/core/SkKnownRuntimeEffects.h + ../../../flutter/third_party/skia/LICENSE +ORIGIN: ../../../flutter/third_party/skia/src/effects/imagefilters/SkMatrixConvolutionImageFilter.h + ../../../flutter/third_party/skia/LICENSE ORIGIN: ../../../flutter/third_party/skia/src/gpu/DataUtils.cpp + ../../../flutter/third_party/skia/LICENSE ORIGIN: ../../../flutter/third_party/skia/src/gpu/DataUtils.h + ../../../flutter/third_party/skia/LICENSE ORIGIN: ../../../flutter/third_party/skia/src/gpu/SwizzlePriv.h + ../../../flutter/third_party/skia/LICENSE @@ -9583,6 +9584,7 @@ FILE: ../../../flutter/third_party/skia/modules/skunicode/src/SkBidiFactory_icu_ FILE: ../../../flutter/third_party/skia/src/base/SkFixedArray.h FILE: ../../../flutter/third_party/skia/src/core/SkKnownRuntimeEffects.cpp FILE: ../../../flutter/third_party/skia/src/core/SkKnownRuntimeEffects.h +FILE: ../../../flutter/third_party/skia/src/effects/imagefilters/SkMatrixConvolutionImageFilter.h FILE: ../../../flutter/third_party/skia/src/gpu/DataUtils.cpp FILE: ../../../flutter/third_party/skia/src/gpu/DataUtils.h FILE: ../../../flutter/third_party/skia/src/gpu/SwizzlePriv.h From 351390905b81930cad5d8af947f69126786b46f0 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Wed, 8 May 2024 11:27:32 -0700 Subject: [PATCH 18/22] Revert 484688634c71ec38c18218eaa13dc47fa7007b74 (#52680) reverts https://github.com/flutter/engine/pull/52526 fixes https://github.com/flutter/flutter/issues/147989 That PR has broken playgrounds. It's non essential so we can revert it an bdero can take a look whenever he gets another chance. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- ci/licenses_golden/licenses_flutter | 8 + impeller/BUILD.gn | 4 + impeller/aiks/BUILD.gn | 2 + impeller/aiks/aiks_playground.cc | 6 +- impeller/aiks/aiks_playground.h | 2 + impeller/aiks/aiks_playground_inspector.cc | 277 ++++++++++++++++ impeller/aiks/aiks_playground_inspector.h | 55 ++++ impeller/aiks/aiks_unittests.cc | 33 ++ impeller/core/BUILD.gn | 2 + impeller/core/capture.cc | 220 +++++++++++++ impeller/core/capture.h | 300 ++++++++++++++++++ impeller/entity/contents/content_context.h | 5 +- impeller/entity/contents/contents.cc | 3 +- .../entity/contents/solid_color_contents.cc | 3 +- impeller/entity/contents/texture_contents.cc | 21 +- impeller/entity/entity.cc | 8 + impeller/entity/entity.h | 6 + impeller/entity/entity_pass.cc | 36 ++- impeller/entity/entity_pass.h | 4 + impeller/entity/entity_pass_clip_stack.cc | 12 + impeller/renderer/context.cc | 4 +- impeller/renderer/context.h | 3 + impeller/tools/impeller.gni | 3 + shell/gpu/gpu_surface_gl_impeller.h | 1 - shell/gpu/gpu_surface_vulkan_impeller.h | 1 - testing/impeller_golden_tests_output.txt | 3 + 26 files changed, 1004 insertions(+), 18 deletions(-) create mode 100644 impeller/aiks/aiks_playground_inspector.cc create mode 100644 impeller/aiks/aiks_playground_inspector.h create mode 100644 impeller/core/capture.cc create mode 100644 impeller/core/capture.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4cea8511e761b..b8c15596edb2c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -40210,6 +40210,8 @@ ORIGIN: ../../../flutter/impeller/aiks/aiks_context.cc + ../../../flutter/LICENS ORIGIN: ../../../flutter/impeller/aiks/aiks_context.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc + ../../../flutter/LICENSE +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_benchmarks.cc + ../../../flutter/LICENSE @@ -40311,6 +40313,8 @@ ORIGIN: ../../../flutter/impeller/core/allocator.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/allocator.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/buffer_view.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/buffer_view.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/core/capture.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/core/capture.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/device_buffer.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/device_buffer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/device_buffer_descriptor.cc + ../../../flutter/LICENSE @@ -43088,6 +43092,8 @@ FILE: ../../../flutter/impeller/aiks/aiks_context.cc FILE: ../../../flutter/impeller/aiks/aiks_context.h FILE: ../../../flutter/impeller/aiks/aiks_playground.cc FILE: ../../../flutter/impeller/aiks/aiks_playground.h +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_benchmarks.cc @@ -43189,6 +43195,8 @@ FILE: ../../../flutter/impeller/core/allocator.cc FILE: ../../../flutter/impeller/core/allocator.h FILE: ../../../flutter/impeller/core/buffer_view.cc FILE: ../../../flutter/impeller/core/buffer_view.h +FILE: ../../../flutter/impeller/core/capture.cc +FILE: ../../../flutter/impeller/core/capture.h FILE: ../../../flutter/impeller/core/device_buffer.cc FILE: ../../../flutter/impeller/core/device_buffer.h FILE: ../../../flutter/impeller/core/device_buffer_descriptor.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 8e7aee9978027..86a638abea5f8 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -14,6 +14,10 @@ config("impeller_public_config") { defines += [ "IMPELLER_DEBUG=1" ] } + if (impeller_capture) { + defines += [ "IMPELLER_ENABLE_CAPTURE=1" ] + } + if (impeller_supports_rendering) { defines += [ "IMPELLER_SUPPORTS_RENDERING=1" ] } diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 591687172bf4a..8c6516821241c 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -57,6 +57,8 @@ impeller_component("aiks_playground") { sources = [ "aiks_playground.cc", "aiks_playground.h", + "aiks_playground_inspector.cc", + "aiks_playground_inspector.h", ] deps = [ ":aiks", diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index ddbbca1f4b298..db1545425f771 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -23,6 +23,7 @@ void AiksPlayground::SetTypographerContext( } void AiksPlayground::TearDown() { + inspector_.HackResetDueToTextureLeaks(); PlaygroundTest::TearDown(); } @@ -44,8 +45,9 @@ bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) { } return Playground::OpenPlaygroundHere( - [&renderer, &callback](RenderTarget& render_target) -> bool { - const std::optional& picture = callback(renderer); + [this, &renderer, &callback](RenderTarget& render_target) -> bool { + const std::optional& picture = inspector_.RenderInspector( + renderer, [&]() { return callback(renderer); }); if (!picture.has_value()) { return false; diff --git a/impeller/aiks/aiks_playground.h b/impeller/aiks/aiks_playground.h index ca3bc1b6de899..4d8a94693229e 100644 --- a/impeller/aiks/aiks_playground.h +++ b/impeller/aiks/aiks_playground.h @@ -6,6 +6,7 @@ #define FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_H_ #include "impeller/aiks/aiks_context.h" +#include "impeller/aiks/aiks_playground_inspector.h" #include "impeller/aiks/picture.h" #include "impeller/playground/playground_test.h" #include "impeller/typographer/typographer_context.h" @@ -37,6 +38,7 @@ class AiksPlayground : public PlaygroundTest { private: std::shared_ptr typographer_context_; + AiksInspector inspector_; AiksPlayground(const AiksPlayground&) = delete; diff --git a/impeller/aiks/aiks_playground_inspector.cc b/impeller/aiks/aiks_playground_inspector.cc new file mode 100644 index 0000000000000..89ace8c5d1283 --- /dev/null +++ b/impeller/aiks/aiks_playground_inspector.cc @@ -0,0 +1,277 @@ +// 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/aiks_playground_inspector.h" + +#include + +#include "impeller/core/capture.h" +#include "impeller/entity/entity_pass.h" +#include "impeller/renderer/context.h" +#include "third_party/imgui/imgui.h" +#include "third_party/imgui/imgui_internal.h" + +namespace impeller { + +static const char* kElementsWindowName = "Elements"; +static const char* kPropertiesWindowName = "Properties"; + +static const std::initializer_list kSupportedDocuments = { + EntityPass::kCaptureDocumentName}; + +AiksInspector::AiksInspector() = default; + +const std::optional& AiksInspector::RenderInspector( + AiksContext& aiks_context, + const std::function()>& picture_callback) { + //---------------------------------------------------------------------------- + /// Configure the next frame. + /// + + RenderCapture(aiks_context.GetContext()->capture); + + //---------------------------------------------------------------------------- + /// Configure the next frame. + /// + + if (ImGui::IsKeyPressed(ImGuiKey_Z)) { + wireframe_ = !wireframe_; + aiks_context.GetContentContext().SetWireframe(wireframe_); + } + + if (ImGui::IsKeyPressed(ImGuiKey_C)) { + capturing_ = !capturing_; + if (capturing_) { + aiks_context.GetContext()->capture = + CaptureContext::MakeAllowlist({kSupportedDocuments}); + } + } + if (!capturing_) { + hovered_element_ = nullptr; + selected_element_ = nullptr; + aiks_context.GetContext()->capture = CaptureContext::MakeInactive(); + std::optional new_picture = picture_callback(); + + // If the new picture doesn't have a pass, that means it was already moved + // into the inspector. Simply re-emit the last received valid picture. + if (!new_picture.has_value() || new_picture->pass) { + last_picture_ = std::move(new_picture); + } + } + + return last_picture_; +} + +void AiksInspector::HackResetDueToTextureLeaks() { + last_picture_.reset(); +} + +static const auto kPropertiesProcTable = CaptureProcTable{ + .boolean = + [](CaptureBooleanProperty& p) { + ImGui::Checkbox(p.label.c_str(), &p.value); + }, + .integer = + [](CaptureIntegerProperty& p) { + if (p.options.range.has_value()) { + ImGui::SliderInt(p.label.c_str(), &p.value, + static_cast(p.options.range->min), + static_cast(p.options.range->max)); + return; + } + ImGui::InputInt(p.label.c_str(), &p.value); + }, + .scalar = + [](CaptureScalarProperty& p) { + if (p.options.range.has_value()) { + ImGui::SliderFloat(p.label.c_str(), &p.value, p.options.range->min, + p.options.range->max); + return; + } + ImGui::DragFloat(p.label.c_str(), &p.value, 0.01); + }, + .point = + [](CapturePointProperty& p) { + if (p.options.range.has_value()) { + ImGui::SliderFloat2(p.label.c_str(), + reinterpret_cast(&p.value), + p.options.range->min, p.options.range->max); + return; + } + ImGui::DragFloat2(p.label.c_str(), reinterpret_cast(&p.value), + 0.01); + }, + .vector3 = + [](CaptureVector3Property& p) { + if (p.options.range.has_value()) { + ImGui::SliderFloat3(p.label.c_str(), + reinterpret_cast(&p.value), + p.options.range->min, p.options.range->max); + return; + } + ImGui::DragFloat3(p.label.c_str(), reinterpret_cast(&p.value), + 0.01); + }, + .rect = + [](CaptureRectProperty& p) { + ImGui::DragFloat4(p.label.c_str(), reinterpret_cast(&p.value), + 0.01); + }, + .color = + [](CaptureColorProperty& p) { + ImGui::ColorEdit4(p.label.c_str(), + reinterpret_cast(&p.value)); + }, + .matrix = + [](CaptureMatrixProperty& p) { + float* pointer = reinterpret_cast(&p.value); + ImGui::DragFloat4((p.label + " X basis").c_str(), pointer, 0.001); + ImGui::DragFloat4((p.label + " Y basis").c_str(), pointer + 4, 0.001); + ImGui::DragFloat4((p.label + " Z basis").c_str(), pointer + 8, 0.001); + ImGui::DragFloat4((p.label + " Translation").c_str(), pointer + 12, + 0.001); + }, + .string = + [](CaptureStringProperty& p) { + ImGui::InputTextEx(p.label.c_str(), "", + // Fine as long as it's read-only. + const_cast(p.value.c_str()), p.value.size(), + ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly); + }, +}; + +void AiksInspector::RenderCapture(CaptureContext& capture_context) { + if (!capturing_) { + return; + } + + auto document = capture_context.GetDocument(EntityPass::kCaptureDocumentName); + + //---------------------------------------------------------------------------- + /// Setup a shared dockspace to collect the capture windows. + /// + + ImGui::SetNextWindowBgAlpha(0.5); + ImGui::Begin("Capture"); + auto dockspace_id = ImGui::GetID("CaptureDockspace"); + if (!ImGui::DockBuilderGetNode(dockspace_id)) { + ImGui::SetWindowSize(ImVec2(370, 680)); + ImGui::SetWindowPos(ImVec2(640, 55)); + + ImGui::DockBuilderRemoveNode(dockspace_id); + ImGui::DockBuilderAddNode(dockspace_id); + + ImGuiID opposite_id; + ImGuiID up_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.6, + nullptr, &opposite_id); + ImGuiID down_id = ImGui::DockBuilderSplitNode(opposite_id, ImGuiDir_Down, + 0.0, nullptr, nullptr); + ImGui::DockBuilderDockWindow(kElementsWindowName, up_id); + ImGui::DockBuilderDockWindow(kPropertiesWindowName, down_id); + + ImGui::DockBuilderFinish(dockspace_id); + } + ImGui::DockSpace(dockspace_id); + ImGui::End(); // Capture window. + + //---------------------------------------------------------------------------- + /// Element hierarchy window. + /// + + ImGui::Begin(kElementsWindowName); + auto root_element = document.GetElement(); + hovered_element_ = nullptr; + if (root_element) { + RenderCaptureElement(*root_element); + } + ImGui::End(); // Hierarchy window. + + if (selected_element_) { + //---------------------------------------------------------------------------- + /// Properties window. + /// + + ImGui::Begin(kPropertiesWindowName); + { + selected_element_->properties.Iterate([&](CaptureProperty& property) { + property.Invoke(kPropertiesProcTable); + }); + } + ImGui::End(); // Inspector window. + + //---------------------------------------------------------------------------- + /// Selected coverage highlighting. + /// + + auto coverage_property = + selected_element_->properties.FindFirstByLabel("Coverage"); + if (coverage_property) { + auto coverage = coverage_property->AsRect(); + if (coverage.has_value()) { + Scalar scale = ImGui::GetWindowDpiScale(); + ImGui::GetBackgroundDrawList()->AddRect( + ImVec2(coverage->GetLeft() / scale, + coverage->GetTop() / scale), // p_min + ImVec2(coverage->GetRight() / scale, + coverage->GetBottom() / scale), // p_max + 0x992222FF, // col + 0.0, // rounding + ImDrawFlags_None, // flags + 8.0); // thickness + } + } + } + + //---------------------------------------------------------------------------- + /// Hover coverage highlight. + /// + + if (hovered_element_) { + auto coverage_property = + hovered_element_->properties.FindFirstByLabel("Coverage"); + if (coverage_property) { + auto coverage = coverage_property->AsRect(); + if (coverage.has_value()) { + Scalar scale = ImGui::GetWindowDpiScale(); + ImGui::GetBackgroundDrawList()->AddRect( + ImVec2(coverage->GetLeft() / scale, + coverage->GetTop() / scale), // p_min + ImVec2(coverage->GetRight() / scale, + coverage->GetBottom() / scale), // p_max + 0x66FF2222, // col + 0.0, // rounding + ImDrawFlags_None, // flags + 8.0); // thickness + } + } + } +} + +void AiksInspector::RenderCaptureElement(CaptureElement& element) { + ImGui::PushID(&element); + + bool is_selected = selected_element_ == &element; + bool has_children = element.children.Count() > 0; + + bool opened = ImGui::TreeNodeEx( + element.label.c_str(), (is_selected ? ImGuiTreeNodeFlags_Selected : 0) | + (has_children ? 0 : ImGuiTreeNodeFlags_Leaf) | + ImGuiTreeNodeFlags_SpanFullWidth | + ImGuiTreeNodeFlags_OpenOnArrow | + ImGuiTreeNodeFlags_DefaultOpen); + if (ImGui::IsItemClicked()) { + selected_element_ = &element; + } + if (ImGui::IsItemHovered()) { + hovered_element_ = &element; + } + if (opened) { + element.children.Iterate( + [&](CaptureElement& child) { RenderCaptureElement(child); }); + ImGui::TreePop(); + } + ImGui::PopID(); +} + +} // namespace impeller diff --git a/impeller/aiks/aiks_playground_inspector.h b/impeller/aiks/aiks_playground_inspector.h new file mode 100644 index 0000000000000..297fb16b64e69 --- /dev/null +++ b/impeller/aiks/aiks_playground_inspector.h @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_INSPECTOR_H_ +#define FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_INSPECTOR_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/aiks/aiks_context.h" +#include "impeller/aiks/picture.h" +#include "impeller/core/capture.h" +#include "impeller/renderer/context.h" + +namespace impeller { + +class AiksInspector { + public: + AiksInspector(); + + const std::optional& RenderInspector( + AiksContext& aiks_context, + const std::function()>& picture_callback); + + // Resets (releases) the underlying |Picture| object. + // + // Underlying issue: . + // + // The tear-down code is not running in the right order; we still have a + // reference to the |Picture| object when the |Context| is being destroyed, + // which causes the |Texture| objects to leak. + // + // TODO(matanlurey): https://github.com/flutter/flutter/issues/134748. + void HackResetDueToTextureLeaks(); + + private: + void RenderCapture(CaptureContext& capture_context); + void RenderCaptureElement(CaptureElement& element); + + bool capturing_ = false; + bool wireframe_ = false; + CaptureElement* hovered_element_ = nullptr; + CaptureElement* selected_element_ = nullptr; + std::optional last_picture_; + + AiksInspector(const AiksInspector&) = delete; + + AiksInspector& operator=(const AiksInspector&) = delete; +}; + +}; // namespace impeller + +#endif // FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_INSPECTOR_H_ diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index b214c91d027a8..12c5414ae1b70 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -20,6 +20,7 @@ #include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/aiks/testing/context_spy.h" +#include "impeller/core/capture.h" #include "impeller/core/device_buffer.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/geometry/color.h" @@ -2595,6 +2596,38 @@ TEST_P(AiksTest, PipelineBlendSingleParameter) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CaptureContext) { + auto capture_context = CaptureContext::MakeAllowlist({"TestDocument"}); + + auto callback = [&](AiksContext& renderer) -> std::optional { + Canvas canvas; + + capture_context.Rewind(); + auto document = capture_context.GetDocument("TestDocument"); + + auto color = document.AddColor("Background color", Color::CornflowerBlue()); + canvas.DrawPaint({.color = color}); + + if (AiksTest::ImGuiBegin("TestDocument", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + document.GetElement()->properties.Iterate([](CaptureProperty& property) { + property.Invoke({.color = [](CaptureColorProperty& p) { + ImGui::ColorEdit4(p.label.c_str(), + reinterpret_cast(&p.value)); + }}); + }); + ImGui::End(); + } + + return canvas.EndRecordingAsPicture(); + }; + OpenPlaygroundHere(callback); +} + +TEST_P(AiksTest, CaptureInactivatedByDefault) { + ASSERT_FALSE(GetContext()->capture.IsActive()); +} + // Regression test for https://github.com/flutter/flutter/issues/134678. TEST_P(AiksTest, ReleasesTextureOnTeardown) { auto context = MakeContext(); diff --git a/impeller/core/BUILD.gn b/impeller/core/BUILD.gn index e526c46115313..e4587bda12a83 100644 --- a/impeller/core/BUILD.gn +++ b/impeller/core/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("core") { "allocator.h", "buffer_view.cc", "buffer_view.h", + "capture.cc", + "capture.h", "device_buffer.cc", "device_buffer.h", "device_buffer_descriptor.cc", diff --git a/impeller/core/capture.cc b/impeller/core/capture.cc new file mode 100644 index 0000000000000..a4b4c462b829b --- /dev/null +++ b/impeller/core/capture.cc @@ -0,0 +1,220 @@ +// 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/core/capture.h" + +#include +#include + +namespace impeller { + +//----------------------------------------------------------------------------- +/// CaptureProperty +/// + +CaptureProperty::CaptureProperty(const std::string& label, Options options) + : CaptureCursorListElement(label), options(options) {} + +CaptureProperty::~CaptureProperty() = default; + +bool CaptureProperty::MatchesCloselyEnough(const CaptureProperty& other) const { + if (label != other.label) { + return false; + } + if (GetType() != other.GetType()) { + return false; + } + return true; +} + +#define _CAPTURE_PROPERTY_CAST_DEFINITION(type_name, pascal_name, lower_name) \ + std::optional CaptureProperty::As##pascal_name() const { \ + if (GetType() != Type::k##pascal_name) { \ + return std::nullopt; \ + } \ + return reinterpret_cast(this) \ + ->value; \ + } + +_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_CAST_DEFINITION); + +#define _CAPTURE_PROPERTY_DEFINITION(type_name, pascal_name, lower_name) \ + Capture##pascal_name##Property::Capture##pascal_name##Property( \ + const std::string& label, type_name value, Options options) \ + : CaptureProperty(label, options), value(std::move(value)) {} \ + \ + std::shared_ptr \ + Capture##pascal_name##Property::Make(const std::string& label, \ + type_name value, Options options) { \ + auto result = std::shared_ptr( \ + new Capture##pascal_name##Property(label, std::move(value), options)); \ + return result; \ + } \ + \ + CaptureProperty::Type Capture##pascal_name##Property::GetType() const { \ + return Type::k##pascal_name; \ + } \ + \ + void Capture##pascal_name##Property::Invoke( \ + const CaptureProcTable& proc_table) { \ + proc_table.lower_name(*this); \ + } + +_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_DEFINITION); + +//----------------------------------------------------------------------------- +/// CaptureElement +/// + +CaptureElement::CaptureElement(const std::string& label) + : CaptureCursorListElement(label) {} + +std::shared_ptr CaptureElement::Make(const std::string& label) { + return std::shared_ptr(new CaptureElement(label)); +} + +void CaptureElement::Rewind() { + properties.Rewind(); + children.Rewind(); +} + +bool CaptureElement::MatchesCloselyEnough(const CaptureElement& other) const { + return label == other.label; +} + +//----------------------------------------------------------------------------- +/// Capture +/// + +Capture::Capture() = default; + +#ifdef IMPELLER_ENABLE_CAPTURE +Capture::Capture(const std::string& label) + : element_(CaptureElement::Make(label)), active_(true) { + element_->label = label; +} +#else +Capture::Capture(const std::string& label) {} +#endif + +Capture Capture::MakeInactive() { + return Capture(); +} + +std::shared_ptr Capture::GetElement() const { +#ifdef IMPELLER_ENABLE_CAPTURE + return element_; +#else + return nullptr; +#endif +} + +void Capture::Rewind() { + return GetElement()->Rewind(); +} + +#ifdef IMPELLER_ENABLE_CAPTURE +#define _CAPTURE_PROPERTY_RECORDER_DEFINITION(type_name, pascal_name, \ + lower_name) \ + type_name Capture::Add##pascal_name(std::string_view label, type_name value, \ + CaptureProperty::Options options) { \ + if (!active_) { \ + return value; \ + } \ + FML_DCHECK(element_ != nullptr); \ + \ + std::string label_clone = std::string(label); \ + auto new_value = Capture##pascal_name##Property::Make( \ + label_clone, std::move(value), options); \ + \ + auto next = std::reinterpret_pointer_cast( \ + element_->properties.GetNext(std::move(new_value), options.readonly)); \ + \ + return next->value; \ + } + +_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_RECORDER_DEFINITION); +#endif + +//----------------------------------------------------------------------------- +/// CaptureContext +/// + +#ifdef IMPELLER_ENABLE_CAPTURE +CaptureContext::CaptureContext() : active_(true) {} +CaptureContext::CaptureContext(std::initializer_list allowlist) + : active_(true), allowlist_(allowlist) {} +#else +CaptureContext::CaptureContext() {} +CaptureContext::CaptureContext(std::initializer_list allowlist) {} +#endif + +CaptureContext::CaptureContext(CaptureContext::InactiveFlag) {} + +CaptureContext CaptureContext::MakeInactive() { + return CaptureContext(InactiveFlag{}); +} + +CaptureContext CaptureContext::MakeAllowlist( + std::initializer_list allowlist) { + return CaptureContext(allowlist); +} + +bool CaptureContext::IsActive() const { +#ifdef IMPELLER_ENABLE_CAPTURE + return active_; +#else + return false; +#endif +} + +void CaptureContext::Rewind() { +#ifdef IMPELLER_ENABLE_CAPTURE + for (auto& [name, capture] : documents_) { + capture.GetElement()->Rewind(); + } +#else + return; +#endif +} + +Capture CaptureContext::GetDocument(const std::string& label) { +#ifdef IMPELLER_ENABLE_CAPTURE + if (!active_) { + return Capture::MakeInactive(); + } + + if (allowlist_.has_value()) { + if (allowlist_->find(label) == allowlist_->end()) { + return Capture::MakeInactive(); + } + } + + auto found = documents_.find(label); + if (found != documents_.end()) { + // Always rewind when fetching an existing document. + found->second.Rewind(); + return found->second; + } + + auto new_document = Capture(label); + documents_.emplace(label, new_document); + return new_document; +#else + return Capture::MakeInactive(); +#endif +} + +bool CaptureContext::DoesDocumentExist(const std::string& label) const { +#ifdef IMPELLER_ENABLE_CAPTURE + if (!active_) { + return false; + } + return documents_.find(label) != documents_.end(); +#else + return false; +#endif +} + +} // namespace impeller diff --git a/impeller/core/capture.h b/impeller/core/capture.h new file mode 100644 index 0000000000000..9e5c0d9318417 --- /dev/null +++ b/impeller/core/capture.h @@ -0,0 +1,300 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_CORE_CAPTURE_H_ +#define FLUTTER_IMPELLER_CORE_CAPTURE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/vector.h" + +namespace impeller { + +struct CaptureProcTable; + +#define _FOR_EACH_CAPTURE_PROPERTY(PROPERTY_V) \ + PROPERTY_V(bool, Boolean, boolean) \ + PROPERTY_V(int, Integer, integer) \ + PROPERTY_V(Scalar, Scalar, scalar) \ + PROPERTY_V(Point, Point, point) \ + PROPERTY_V(Vector3, Vector3, vector3) \ + PROPERTY_V(Rect, Rect, rect) \ + PROPERTY_V(Color, Color, color) \ + PROPERTY_V(Matrix, Matrix, matrix) \ + PROPERTY_V(std::string, String, string) + +template +struct CaptureCursorListElement { + std::string label; + + explicit CaptureCursorListElement(const std::string& label) : label(label){}; + + virtual ~CaptureCursorListElement() = default; + + //---------------------------------------------------------------------------- + /// @brief Determines if previously captured data matches closely enough with + /// newly recorded data to safely emitted in its place. If this + /// returns `false`, then the remaining elements in the capture list + /// are discarded and re-recorded. + /// + /// This mechanism ensures that the UI of an interactive inspector can + /// never deviate from reality, even if the schema of the captured + /// data were to significantly deviate. + /// + virtual bool MatchesCloselyEnough(const Type& other) const = 0; +}; + +#define _CAPTURE_TYPE(type_name, pascal_name, lower_name) k##pascal_name, + +#define _CAPTURE_PROPERTY_CAST_DECLARATION(type_name, pascal_name, lower_name) \ + std::optional As##pascal_name() const; + +/// A capturable property type +struct CaptureProperty : public CaptureCursorListElement { + enum class Type { _FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_TYPE) }; + + struct Options { + struct Range { + Scalar min; + Scalar max; + }; + + /// Readonly properties are always re-recorded during capture. Any edits + /// made to readonly values in-between captures are overwritten during the + /// next capture. + bool readonly = false; + + /// An inspector hint that can be used for displaying sliders. Only used for + /// numeric types. Rounded down for integer types. + std::optional range; + }; + + Options options; + + CaptureProperty(const std::string& label, Options options); + + virtual ~CaptureProperty(); + + virtual Type GetType() const = 0; + + virtual void Invoke(const CaptureProcTable& proc_table) = 0; + + bool MatchesCloselyEnough(const CaptureProperty& other) const override; + + _FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_CAST_DECLARATION) +}; + +#define _CAPTURE_PROPERTY_DECLARATION(type_name, pascal_name, lower_name) \ + struct Capture##pascal_name##Property final : public CaptureProperty { \ + type_name value; \ + \ + static std::shared_ptr \ + Make(const std::string& label, type_name value, Options options); \ + \ + /* |CaptureProperty| */ \ + Type GetType() const override; \ + \ + /* |CaptureProperty| */ \ + void Invoke(const CaptureProcTable& proc_table) override; \ + \ + private: \ + Capture##pascal_name##Property(const std::string& label, \ + type_name value, \ + Options options); \ + \ + FML_DISALLOW_COPY_AND_ASSIGN(Capture##pascal_name##Property); \ + }; + +_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_DECLARATION); + +#define _CAPTURE_PROC(type_name, pascal_name, lower_name) \ + std::function lower_name = \ + [](Capture##pascal_name##Property& value) {}; + +struct CaptureProcTable { + _FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROC) +}; + +template +class CapturePlaybackList { + public: + CapturePlaybackList() = default; + + ~CapturePlaybackList() { + // Force the list element type to inherit the CRTP type. We can't enforce + // this as a template requirement directly because `CaptureElement` has a + // recursive `CaptureCursorList` property, and so the + // compiler fails the check due to the type being incomplete. + static_assert(std::is_base_of_v, Type>); + } + + void Rewind() { cursor_ = 0; } + + size_t Count() { return values_.size(); } + + std::shared_ptr GetNext(std::shared_ptr captured, + bool force_overwrite) { + if (cursor_ < values_.size()) { + std::shared_ptr& result = values_[cursor_]; + + if (result->MatchesCloselyEnough(*captured)) { + if (force_overwrite) { + values_[cursor_] = captured; + } + // Safe playback is possible. + ++cursor_; + return result; + } + // The data has changed too much from the last capture to safely continue + // playback. Discard this and all subsequent elements to re-record. + values_.resize(cursor_); + } + + ++cursor_; + values_.push_back(captured); + return captured; + } + + std::shared_ptr FindFirstByLabel(const std::string& label) { + for (std::shared_ptr& value : values_) { + if (value->label == label) { + return value; + } + } + return nullptr; + } + + void Iterate(std::function iterator) const { + for (auto& value : values_) { + iterator(*value); + } + } + + private: + size_t cursor_ = 0; + std::vector> values_; + + CapturePlaybackList(const CapturePlaybackList&) = delete; + + CapturePlaybackList& operator=(const CapturePlaybackList&) = delete; +}; + +/// A document of capture data, containing a list of properties and a list +/// of subdocuments. +struct CaptureElement final : public CaptureCursorListElement { + CapturePlaybackList properties; + CapturePlaybackList children; + + static std::shared_ptr Make(const std::string& label); + + void Rewind(); + + bool MatchesCloselyEnough(const CaptureElement& other) const override; + + private: + explicit CaptureElement(const std::string& label); + + CaptureElement(const CaptureElement&) = delete; + + CaptureElement& operator=(const CaptureElement&) = delete; +}; + +#ifdef IMPELLER_ENABLE_CAPTURE +#define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, \ + lower_name) \ + type_name Add##pascal_name(std::string_view label, type_name value, \ + CaptureProperty::Options options = {}); +#else +#define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, \ + lower_name) \ + inline type_name Add##pascal_name(std::string_view label, type_name value, \ + CaptureProperty::Options options = {}) { \ + return value; \ + } +#endif + +class Capture { + public: + explicit Capture(const std::string& label); + + Capture(); + + static Capture MakeInactive(); + + inline Capture CreateChild(std::string_view label) { +#ifdef IMPELLER_ENABLE_CAPTURE + if (!active_) { + return Capture(); + } + + std::string label_copy = std::string(label); + auto new_capture = Capture(label_copy); + new_capture.element_ = + element_->children.GetNext(new_capture.element_, false); + new_capture.element_->Rewind(); + return new_capture; +#else + return Capture(); +#endif + } + + std::shared_ptr GetElement() const; + + void Rewind(); + + _FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_RECORDER_DECLARATION) + + private: +#ifdef IMPELLER_ENABLE_CAPTURE + std::shared_ptr element_; + bool active_ = false; +#endif +}; + +class CaptureContext { + public: + CaptureContext(); + + static CaptureContext MakeInactive(); + + static CaptureContext MakeAllowlist( + std::initializer_list allowlist); + + bool IsActive() const; + + void Rewind(); + + Capture GetDocument(const std::string& label); + + bool DoesDocumentExist(const std::string& label) const; + + private: + struct InactiveFlag {}; + explicit CaptureContext(InactiveFlag); + CaptureContext(std::initializer_list allowlist); + +#ifdef IMPELLER_ENABLE_CAPTURE + bool active_ = false; + std::optional> allowlist_; + std::unordered_map documents_; +#endif +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_CORE_CAPTURE_H_ diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index aa173c3b04fc1..1888845a222f8 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -10,17 +10,18 @@ #include #include +#include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" #include "flutter/fml/status_or.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" #include "impeller/core/host_buffer.h" +#include "impeller/entity/entity.h" #include "impeller/renderer/capabilities.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/pipeline.h" #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/render_target.h" -#include "impeller/typographer/lazy_glyph_atlas.h" #include "impeller/typographer/typographer_context.h" #include "impeller/entity/border_mask_blur.frag.h" @@ -53,6 +54,8 @@ #include "impeller/entity/tiled_texture_fill.frag.h" #include "impeller/entity/yuv_to_rgb_filter.frag.h" +#include "impeller/typographer/glyph_atlas.h" + #include "impeller/entity/conical_gradient_ssbo_fill.frag.h" #include "impeller/entity/linear_gradient_ssbo_fill.frag.h" #include "impeller/entity/radial_gradient_ssbo_fill.frag.h" diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 421713761175e..2c24482a544eb 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -6,11 +6,12 @@ #include #include "fml/logging.h" +#include "impeller/base/strings.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" #include "impeller/entity/contents/anonymous_contents.h" #include "impeller/entity/contents/content_context.h" -#include "impeller/entity/entity.h" +#include "impeller/entity/contents/texture_contents.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 0c99d47990b7b..083ac7d61ec38 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -48,10 +48,11 @@ std::optional SolidColorContents::GetCoverage( bool SolidColorContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { + auto capture = entity.GetCapture().CreateChild("SolidColorContents"); using VS = SolidFillPipeline::VertexShader; VS::FrameInfo frame_info; - frame_info.color = GetColor().Premultiply(); + frame_info.color = capture.AddColor("Color", GetColor()).Premultiply(); PipelineBuilderCallback pipeline_callback = [&renderer](ContentContextOptions options) { diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index a2af33e96c486..72fce8d0ff211 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -109,6 +109,8 @@ std::optional TextureContents::RenderToSnapshot( bool TextureContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { + auto capture = entity.GetCapture().CreateChild("TextureContents"); + using VS = TextureFillVertexShader; using FS = TextureFillFragmentShader; using FSStrict = TextureFillStrictSrcFragmentShader; @@ -122,15 +124,18 @@ bool TextureContents::Render(const ContentContext& renderer, texture_->GetTextureDescriptor().type == TextureType::kTextureExternalOES; FML_DCHECK(!is_external_texture); + auto source_rect = capture.AddRect("Source rect", source_rect_); auto texture_coords = - Rect::MakeSize(texture_->GetSize()).Project(source_rect_); + Rect::MakeSize(texture_->GetSize()).Project(source_rect); VertexBufferBuilder vertex_builder; + auto destination_rect = + capture.AddRect("Destination rect", destination_rect_); vertex_builder.AddVertices({ - {destination_rect_.GetLeftTop(), texture_coords.GetLeftTop()}, - {destination_rect_.GetRightTop(), texture_coords.GetRightTop()}, - {destination_rect_.GetLeftBottom(), texture_coords.GetLeftBottom()}, - {destination_rect_.GetRightBottom(), texture_coords.GetRightBottom()}, + {destination_rect.GetLeftTop(), texture_coords.GetLeftTop()}, + {destination_rect.GetRightTop(), texture_coords.GetRightTop()}, + {destination_rect.GetLeftBottom(), texture_coords.GetLeftBottom()}, + {destination_rect.GetRightBottom(), texture_coords.GetRightBottom()}, }); auto& host_buffer = renderer.GetTransientsBuffer(); @@ -165,11 +170,11 @@ bool TextureContents::Render(const ContentContext& renderer, // texel to ensure that linear filtering does not sample anything outside // the source rect bounds. auto strict_texture_coords = - Rect::MakeSize(texture_->GetSize()).Project(source_rect_.Expand(-0.5)); + Rect::MakeSize(texture_->GetSize()).Project(source_rect.Expand(-0.5)); FSStrict::FragInfo frag_info; frag_info.source_rect = Vector4(strict_texture_coords.GetLTRB()); - frag_info.alpha = GetOpacity(); + frag_info.alpha = capture.AddScalar("Alpha", GetOpacity()); FSStrict::BindFragInfo(pass, host_buffer.EmplaceUniform((frag_info))); FSStrict::BindTextureSampler( pass, texture_, @@ -177,7 +182,7 @@ bool TextureContents::Render(const ContentContext& renderer, sampler_descriptor_)); } else { FS::FragInfo frag_info; - frag_info.alpha = GetOpacity(); + frag_info.alpha = capture.AddScalar("Alpha", GetOpacity()); FS::BindFragInfo(pass, host_buffer.EmplaceUniform((frag_info))); FS::BindTextureSampler( pass, texture_, diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index cb336892d3a61..9111a61d29318 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -188,8 +188,16 @@ Scalar Entity::DeriveTextScale() const { return GetTransform().GetMaxBasisLengthXY(); } +Capture& Entity::GetCapture() const { + return capture_; +} + Entity Entity::Clone() const { return Entity(*this); } +void Entity::SetCapture(Capture capture) const { + capture_ = std::move(capture); +} + } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 0161d7153eaac..d2d4eb9596a64 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -7,6 +7,7 @@ #include +#include "impeller/core/capture.h" #include "impeller/entity/contents/contents.h" #include "impeller/geometry/color.h" #include "impeller/geometry/matrix.h" @@ -122,6 +123,10 @@ class Entity { Scalar DeriveTextScale() const; + Capture& GetCapture() const; + + void SetCapture(Capture capture) const; + Entity Clone() const; private: @@ -131,6 +136,7 @@ class Entity { std::shared_ptr contents_; BlendMode blend_mode_ = BlendMode::kSourceOver; uint32_t clip_depth_ = 1u; + mutable Capture capture_; }; } // namespace impeller diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 4ed42ceeb0cd8..596f4412daf84 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -44,6 +44,8 @@ std::tuple, BlendMode> ElementAsBackgroundColor( } } // namespace +const std::string EntityPass::kCaptureDocumentName = "EntityPass"; + EntityPass::EntityPass() = default; EntityPass::~EntityPass() = default; @@ -352,6 +354,9 @@ bool EntityPass::DoesBackdropGetRead(ContentContext& renderer) const { bool EntityPass::Render(ContentContext& renderer, const RenderTarget& render_target) const { + auto capture = + renderer.GetContext()->capture.GetDocument(kCaptureDocumentName); + renderer.GetRenderTargetCache()->Start(); fml::ScopedCleanupClosure reset_state([&renderer]() { renderer.GetLazyGlyphAtlas()->ResetTextFrames(); @@ -372,6 +377,10 @@ bool EntityPass::Render(ContentContext& renderer, return false; } + capture.AddRect("Coverage", + Rect::MakeSize(root_render_target.GetRenderTargetSize()), + {.readonly = true}); + const auto& lazy_glyph_atlas = renderer.GetLazyGlyphAtlas(); IterateAllEntities([&lazy_glyph_atlas](const Entity& entity) { if (const auto& contents = entity.GetContents()) { @@ -393,6 +402,7 @@ bool EntityPass::Render(ContentContext& renderer, GetClearColorOrDefault(render_target.GetRenderTargetSize())); if (!OnRender(renderer, // renderer + capture, // capture offscreen_target.GetRenderTarget() .GetRenderTargetSize(), // root_pass_size offscreen_target, // pass_target @@ -499,6 +509,7 @@ bool EntityPass::Render(ContentContext& renderer, return OnRender( // renderer, // renderer + capture, // capture root_render_target.GetRenderTargetSize(), // root_pass_size pass_target, // pass_target Point(), // global_pass_position @@ -510,6 +521,7 @@ bool EntityPass::Render(ContentContext& renderer, EntityPass::EntityResult EntityPass::GetEntityForElement( const EntityPass::Element& element, ContentContext& renderer, + Capture& capture, InlinePassContext& pass_context, ISize root_pass_size, Point global_pass_position, @@ -521,6 +533,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( /// if (const auto& entity = std::get_if(&element)) { Entity element_entity = entity->Clone(); + element_entity.SetCapture(capture.CreateChild("Entity")); if (!global_pass_position.IsZero()) { // If the pass image is going to be rendered with a non-zero position, @@ -545,9 +558,11 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( if (!subpass->backdrop_filter_proc_ && subpass->delegate_->CanCollapseIntoParentPass(subpass)) { + auto subpass_capture = capture.CreateChild("EntityPass (Collapsed)"); // Directly render into the parent target and move on. if (!subpass->OnRender( renderer, // renderer + subpass_capture, // capture root_pass_size, // root_pass_size pass_context.GetPassTarget(), // pass_target global_pass_position, // global_pass_position @@ -591,10 +606,12 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( if (!clip_coverage_stack.HasCoverage()) { // 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 clip_coverage_back = clip_coverage_stack.CurrentClipCoverage(); if (!clip_coverage_back.has_value()) { + capture.CreateChild("Subpass Entity (Skipped: Empty clip B)"); return EntityPass::EntityResult::Skip(); } @@ -606,12 +623,14 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( .GetRenderTargetSize())) .Intersection(clip_coverage_back.value()); if (!coverage_limit.has_value()) { + capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit A)"); return EntityPass::EntityResult::Skip(); } coverage_limit = coverage_limit->Intersection(Rect::MakeSize(root_pass_size)); if (!coverage_limit.has_value()) { + capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit B)"); return EntityPass::EntityResult::Skip(); } @@ -620,11 +639,13 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( ? coverage_limit : GetSubpassCoverage(*subpass, coverage_limit); if (!subpass_coverage.has_value()) { + capture.CreateChild("Subpass Entity (Skipped: Empty subpass coverage A)"); return EntityPass::EntityResult::Skip(); } auto subpass_size = ISize(subpass_coverage->GetSize()); if (subpass_size.IsEmpty()) { + capture.CreateChild("Subpass Entity (Skipped: Empty subpass coverage B)"); return EntityPass::EntityResult::Skip(); } @@ -639,6 +660,9 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Failure(); } + 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, @@ -650,6 +674,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // time they are transient). if (!subpass->OnRender( renderer, // renderer + subpass_capture, // capture root_pass_size, // root_pass_size subpass_target, // pass_target subpass_coverage->GetOrigin(), // global_pass_position @@ -689,11 +714,16 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Failure(); } Entity element_entity; + Capture subpass_texture_capture = + capture.CreateChild("Entity (Subpass texture)"); element_entity.SetClipDepth(subpass->clip_depth_); + element_entity.SetCapture(subpass_texture_capture); element_entity.SetContents(std::move(offscreen_texture_contents)); element_entity.SetBlendMode(subpass->blend_mode_); - element_entity.SetTransform(Matrix::MakeTranslation( - Vector3(subpass_coverage->GetOrigin() - global_pass_position))); + element_entity.SetTransform(subpass_texture_capture.AddMatrix( + "Transform", + Matrix::MakeTranslation( + Vector3(subpass_coverage->GetOrigin() - global_pass_position)))); return EntityPass::EntityResult::Success(std::move(element_entity)); } @@ -816,6 +846,7 @@ bool EntityPass::RenderElement(Entity& element_entity, bool EntityPass::OnRender( ContentContext& renderer, + Capture& capture, ISize root_pass_size, EntityPassTarget& pass_target, Point global_pass_position, @@ -888,6 +919,7 @@ 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 diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 87731602d6925..4fcf7f6cf3b05 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -53,6 +53,8 @@ class EntityPass { /// `GetEntityForElement()`. using Element = std::variant>; + static const std::string kCaptureDocumentName; + using BackdropFilterProc = std::function( FilterInput::Ref, const Matrix& effect_transform, @@ -236,6 +238,7 @@ class EntityPass { EntityResult GetEntityForElement(const EntityPass::Element& element, ContentContext& renderer, + Capture& capture, InlinePassContext& pass_context, ISize root_pass_size, Point global_pass_position, @@ -301,6 +304,7 @@ class EntityPass { /// parent pass. /// bool OnRender(ContentContext& renderer, + Capture& capture, ISize root_pass_size, EntityPassTarget& pass_target, Point global_pass_position, diff --git a/impeller/entity/entity_pass_clip_stack.cc b/impeller/entity/entity_pass_clip_stack.cc index 54a30aceef209..68dcf2271eca5 100644 --- a/impeller/entity/entity_pass_clip_stack.cc +++ b/impeller/entity/entity_pass_clip_stack.cc @@ -124,6 +124,18 @@ EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState( } break; } +#ifdef IMPELLER_ENABLE_CAPTURE + { + auto element_entity_coverage = entity.GetCoverage(); + if (element_entity_coverage.has_value()) { + element_entity_coverage = + element_entity_coverage->Shift(global_pass_position); + entity.GetCapture().AddRect("Coverage", *element_entity_coverage, + {.readonly = true}); + } + } +#endif + RecordEntity(entity, global_clip_coverage.type, subpass_state.clip_coverage.back().coverage); diff --git a/impeller/renderer/context.cc b/impeller/renderer/context.cc index b6d5a624cb97a..fb42d5744a10b 100644 --- a/impeller/renderer/context.cc +++ b/impeller/renderer/context.cc @@ -4,11 +4,13 @@ #include "impeller/renderer/context.h" +#include "impeller/core/capture.h" + namespace impeller { Context::~Context() = default; -Context::Context() = default; +Context::Context() : capture(CaptureContext::MakeInactive()) {} bool Context::UpdateOffscreenLayerPixelFormat(PixelFormat format) { return false; diff --git a/impeller/renderer/context.h b/impeller/renderer/context.h index 4a51560978bb2..59ac871aad483 100644 --- a/impeller/renderer/context.h +++ b/impeller/renderer/context.h @@ -9,6 +9,7 @@ #include #include "impeller/core/allocator.h" +#include "impeller/core/capture.h" #include "impeller/core/formats.h" #include "impeller/renderer/capabilities.h" #include "impeller/renderer/command_queue.h" @@ -170,6 +171,8 @@ class Context { /// virtual void Shutdown() = 0; + CaptureContext capture; + /// Stores a task on the `ContextMTL` that is awaiting access for the GPU. /// /// The task will be executed in the event that the GPU access has changed to diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index bfc3932fb05b4..55453fe574f32 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -11,6 +11,9 @@ declare_args() { impeller_debug = flutter_runtime_mode == "debug" || flutter_runtime_mode == "profile" + # Whether the runtime capture/playback system is enabled. + impeller_capture = flutter_runtime_mode == "debug" + # Whether the Metal backend is enabled. impeller_enable_metal = (is_mac || is_ios) && target_os != "fuchsia" diff --git a/shell/gpu/gpu_surface_gl_impeller.h b/shell/gpu/gpu_surface_gl_impeller.h index d35dd14e962f5..5b6fbd9c1f3b5 100644 --- a/shell/gpu/gpu_surface_gl_impeller.h +++ b/shell/gpu/gpu_surface_gl_impeller.h @@ -12,7 +12,6 @@ #include "flutter/impeller/aiks/aiks_context.h" #include "flutter/impeller/renderer/context.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" -#include "impeller/renderer/renderer.h" namespace flutter { diff --git a/shell/gpu/gpu_surface_vulkan_impeller.h b/shell/gpu/gpu_surface_vulkan_impeller.h index fb761329ee95a..3ddb106fa7c49 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.h +++ b/shell/gpu/gpu_surface_vulkan_impeller.h @@ -12,7 +12,6 @@ #include "flutter/impeller/aiks/aiks_context.h" #include "flutter/impeller/renderer/context.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" -#include "impeller/renderer/renderer.h" namespace flutter { diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index f267f1d5759c8..67fc0eb14d438 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -478,6 +478,9 @@ impeller_Play_AiksTest_CanRenderWithContiguousClipRestores_Vulkan.png impeller_Play_AiksTest_CanSaveLayerStandalone_Metal.png impeller_Play_AiksTest_CanSaveLayerStandalone_OpenGLES.png impeller_Play_AiksTest_CanSaveLayerStandalone_Vulkan.png +impeller_Play_AiksTest_CaptureContext_Metal.png +impeller_Play_AiksTest_CaptureContext_OpenGLES.png +impeller_Play_AiksTest_CaptureContext_Vulkan.png impeller_Play_AiksTest_ClearBlendWithBlur_Metal.png impeller_Play_AiksTest_ClearBlendWithBlur_OpenGLES.png impeller_Play_AiksTest_ClearBlendWithBlur_Vulkan.png From d47b038a696f6e36f9d39444342d9c4576f6aa1b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 May 2024 15:33:22 -0400 Subject: [PATCH 19/22] Roll Skia from d965fc1fd319 to 4019d8957cff (2 revisions) (#52682) https://skia.googlesource.com/skia.git/+log/d965fc1fd319..4019d8957cff 2024-05-08 brianosman@google.com Reland "Update clang_win and win_toolchain assets" 2024-05-08 kjlubick@google.com Fix experimental use of generate_cpp_files_for_headers If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC brianosman@google.com,egdaniel@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- sky/packages/sky_engine/LICENSE | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index a2ce3ca20ae9b..00bab5ff7f68b 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': 'd965fc1fd31926d65514189b5f49512d53b3f777', + 'skia_revision': '4019d8957cffe178af3bb5e7834e6a419d0088ba', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 7d2ee094a0b32..254c375431317 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c5c51f42f0f5f0d4128cd704127c8a5a +Signature: 12581bbedc783b541d655b33cbe83347 ==================================================================================================== LIBRARY: etc1 diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index f47a16635657c..7e3d929bbc5dd 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -32293,7 +32293,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/b7cad2edae4bcbe05332c9956a04692d19798904 +You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/673b3aa8558a4af94a6c9f424b7c4569899bef9b /third_party/fallback_root_certificates/ -------------------------------------------------------------------------------- From 93c504058426074b465fc83fb16d87168287a78b Mon Sep 17 00:00:00 2001 From: "auto-submit[bot]" <98614782+auto-submit[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 19:46:46 +0000 Subject: [PATCH 20/22] Reverts "Roll Dart SDK from b7cad2edae4b to 673b3aa8558a (1 revision) (#52677)" (#52683) Reverts: flutter/engine#52677 Initiated by: zanderso Reason for reverting: Failed roll to framework in https://github.com/flutter/flutter/pull/147999 Original PR Author: skia-flutter-autoroll Reviewed By: {fluttergithubbot} This change reverts the following previous change: https://dart.googlesource.com/sdk.git/+log/b7cad2edae4b..673b3aa8558a 2024-05-08 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.5.0-139.0.dev If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter Engine: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 00bab5ff7f68b..7cd2f1380a377 100644 --- a/DEPS +++ b/DEPS @@ -56,7 +56,7 @@ 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': '673b3aa8558a4af94a6c9f424b7c4569899bef9b', + 'dart_revision': 'b7cad2edae4bcbe05332c9956a04692d19798904', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -434,7 +434,7 @@ deps = { Var('dart_git') + '/mockito.git@81ecb88b631a9c1e677852a33ad916238dee0ef2', 'src/flutter/third_party/dart/third_party/pkg/native': - Var('dart_git') + '/native.git@a152cfa21129510e9227962a01ec12918e192ecc', + Var('dart_git') + '/native.git@71ada4b5c8001e6b8207ed40331d158e0912cd94', 'src/flutter/third_party/dart/third_party/pkg/package_config': Var('dart_git') + '/package_config.git@a36e496d61937a800c22f68035dc98bf8ead7fb2', From bd9ef6a3868b784be7ce5847aaf535f81eaa63ed Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 May 2024 13:16:38 -0700 Subject: [PATCH 21/22] [Impeller] Start a glossary. (#52641) Fixes https://github.com/flutter/flutter/issues/107609 --- impeller/README.md | 1 + impeller/docs/glossary.md | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 impeller/docs/glossary.md diff --git a/impeller/README.md b/impeller/README.md index 68be9987f9898..115bae40ae48d 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -242,3 +242,4 @@ To your `Info.plist` file, add under the top-level `` tag: * [Threading in the Vulkan Backend](docs/vulkan_threading.md) * [Android Rendering Backend Selection](docs/android.md) * [Using Impeller as a Standalone Rendering Library (with OpenGL ES)](docs/standalone_gles.md) +* [Glossary](docs/glossary.md) diff --git a/impeller/docs/glossary.md b/impeller/docs/glossary.md new file mode 100644 index 0000000000000..3a00878b7b7b8 --- /dev/null +++ b/impeller/docs/glossary.md @@ -0,0 +1,41 @@ +# Glossary + +### Device & Host + +In the context of graphics and Impeller, the device is the GPU and the host, the CPU. + +### Client Rendering API + +The API Impeller uses to talk to devices. Examples of these are OpenGL, Metal, Vulkan, Direct X. + +### Window System Integration (WSI) + +Impeller can render using one of the client rendering APIs into a render target. However, that render target needs to be presented in the platform's window system. A Window System Integration API does this and is usually extremely platform specific. For instance, OpenGL may be available on macOS and Linux, but the WSI API for macOS is EAGL and usually (but not always) EGL for Linux. + +### Varying + +In the context of shaders, a varying is a value that is interpolated between two vertices (specified by the vertex shader) and provided to the fragment shader. + +### OpenGL + +[OpenGL and OpenGL ES (Embedded Systems)](https://www.opengl.org/) are [client rendering APIs](#client-rendering-api). Impeller uses these on older versions of Android today. + +### Vulkan + +[Vulkan](https://www.vulkan.org/) is a modern [client rendering API](#client-rendering-api) used by Impeller on Android. It is also available natively on the major non-Apple platforms. On the Apple platforms, Vulkan is implemented on top of [Metal](#metal) via a translation layer called [MoltenVK](https://github.com/KhronosGroup/MoltenVK). + +Impeller supports a baseline of Vulkan 1.1 and uses extensions where available. + +### Metal + +[Metal](https://developer.apple.com/metal/) is a modern [client rendering API](#client-rendering-api) used by Impeller on macOS and iOS. It is not available on non-Apple platforms. + +### EGL + +[EGL](https://www.khronos.org/egl) provides [WSI](#window-system-integration-wsi) for OpenGL ES. + +### Android Hardware Buffers (AHB) + +Available only on Android and used by Impeller on API levels at or above 29, [AHBs](https://developer.android.com/ndk/reference/group/a-hardware-buffer) are resources that can be treated as textures by both [OpenGL](#opengl) and [Vulkan](#vulkan) and shared with the system compositor for [WSI](#window-system-integration-wsi). + +In the Impeller codebase, classes that deal with AHBs have the `ahb_` prefix. From 7f84442c5cf87862f9788d256707d37573364f81 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 7 May 2024 14:04:57 -0700 Subject: [PATCH 22/22] "Reland "Add host_profile_arm64 and host_release_arm64 local engine configurations." Adds --no-lto. This reverts commit 1fe835b85f5c23f500268b82dfd043c9f0eb8ca1. --- ci/builders/local_engine.json | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/ci/builders/local_engine.json b/ci/builders/local_engine.json index 573e4ef89a700..4ca26c6bcab6b 100644 --- a/ci/builders/local_engine.json +++ b/ci/builders/local_engine.json @@ -544,6 +544,70 @@ } } }, + { + "cas_archive": false, + "drone_dimensions": [ + "os=Mac-13", + "device_type=none" + ], + "gclient_variables": { + "download_android_deps": false, + "use_rbe": true + }, + "gn": [ + "--runtime-mode", + "profile", + "--no-lto", + "--mac-cpu", + "arm64", + "--xcode-symlinks", + "--rbe", + "--no-goma" + ], + "name": "macos/host_profile_arm64", + "description": "Builds a profile mode engine for a macOS host on arm64.", + "ninja": { + "config": "host_profile_arm64", + "targets": [] + }, + "properties": { + "$flutter/osx_sdk": { + "sdk_version": "15a240d" + } + } + }, + { + "cas_archive": false, + "drone_dimensions": [ + "os=Mac-13", + "device_type=none" + ], + "gclient_variables": { + "download_android_deps": false, + "use_rbe": true + }, + "gn": [ + "--runtime-mode", + "release", + "--no-lto", + "--mac-cpu", + "arm64", + "--xcode-symlinks", + "--rbe", + "--no-goma" + ], + "name": "macos/host_release_arm64", + "description": "Builds a release mode engine for a macOS host on arm64.", + "ninja": { + "config": "host_release_arm64", + "targets": [] + }, + "properties": { + "$flutter/osx_sdk": { + "sdk_version": "15a240d" + } + } + }, { "cas_archive": false, "drone_dimensions": [