- 
                Notifications
    You must be signed in to change notification settings 
- Fork 6k
Trace calls to Canvas::saveLayer #29444
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -135,7 +135,7 @@ RasterStatus CompositorContext::ScopedFrame::Raster( | |
| } | ||
|  | ||
| if (needs_save_layer) { | ||
| FML_LOG(INFO) << "Using SaveLayer to protect non-readback surface"; | ||
| TRACE_EVENT0("flutter", "Canvas::saveLayer"); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cost of the save layer is not just isolated to this scope. There will be significant time taken in the flush waiting on the GPU to service this workload. So, perhaps a more actionable metric is to have a counter that tracks the number of save layers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Counters won't show up in systracing. My idea here is that devtools could manually count these, and developers would be able to more easily see what callstacks were resulting in them. Then we'd want something like debugging the SKP that resulted and you could visually see what parts of the draw used a savelayer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (so yes, these trace events will be very small, but devtools will be able to scan for them and know they result in more expensive operations later) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not just that the bulk of the work happens in a different phase of the pipeline (i.e. flush vs LayerTree.Paint), it's that even the saveLayer call itself doesn't really do the work. It remembers a few items you told it, then later the restore applies that work. So there are many reasons why this timed event isn't capturing any real work. Why doesn't devtools scan for the counter events? They are just another type of event in the Chrome Tracing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 Discussing this in person, it looks like devtools should be able to detect counter events. systrace is an issue though and I am investigating if counters are not supported by systrace or whether this is a limitation with our trace logger to systrace. In any case, it looks like our use case ought to be address by just tracing to the timeline. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be great to switch this to a counter as these events are adding noise to the trace right now without clear user value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We didn't use counters because it wouldn't be seen by users. The action a user should take is to turn on debugTracePaintEnabled and remove or rewrite the render object adding this call. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 I don't remember the full context of this discussion---if these are trace events we explicitly added for the benefit of end-users, then we should certainly try to make them more useful---but in general, it's been our practice to add trace events as needed to assist Engine developers in zeroing in on issues, be they performance issues or otherwise. So (in general, maybe not for this specific case) if a trace event is too noisy, the corrective action would be to filter it before an end-user sees it. | ||
| SkRect bounds = SkRect::Make(layer_tree.frame_size()); | ||
| SkPaint paint; | ||
| paint.setBlendMode(SkBlendMode::kSrc); | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -105,6 +105,7 @@ void Canvas::saveLayerWithoutBounds(const Paint& paint, | |
| if (!canvas_) { | ||
| return; | ||
| } | ||
| TRACE_EVENT0("flutter", "Canvas::saveLayer"); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is incorrect. This line is only recording a saveLayer and has nothing to do with the execution of saveLayers. The recorded picture can be reused on every single frame from here on and it won't be recorded (there's not much that can be done for that with SkP, but DL could record them as they execute). The recorded picture could be a mistake and never used even once. This trace will appear on the build thread, not the raster thread. There is everything wrong with this event. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know that this record won't be enough, but in most cases it will provide signal about where in developer code someone actually triggered a saveLayer. My more recent concern is that it might not be enough, since things like  How would you suggest achieving this? | ||
| canvas_->saveLayer(nullptr, paint.paint()); | ||
| } | ||
|  | ||
|  | @@ -117,6 +118,7 @@ void Canvas::saveLayer(double left, | |
| if (!canvas_) { | ||
| return; | ||
| } | ||
| TRACE_EVENT0("flutter", "Canvas::saveLayer"); | ||
| SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); | ||
| canvas_->saveLayer(&bounds, paint.paint()); | ||
| } | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|  | ||
| import 'dart:async'; | ||
| import 'dart:developer' as developer; | ||
| import 'dart:ui'; | ||
|  | ||
| import 'package:litetest/litetest.dart'; | ||
| import 'package:vm_service/vm_service.dart' as vms; | ||
| import 'package:vm_service/vm_service_io.dart'; | ||
|  | ||
| void main() { | ||
| test('Canvas.saveLayer emits tracing', () async { | ||
| final developer.ServiceProtocolInfo info = await developer.Service.getInfo(); | ||
|  | ||
| if (info.serverUri == null) { | ||
| fail('This test must not be run with --disable-observatory.'); | ||
| } | ||
|  | ||
| final vms.VmService vmService = await vmServiceConnectUri( | ||
| 'ws://localhost:${info.serverUri!.port}${info.serverUri!.path}ws', | ||
| ); | ||
|  | ||
| final Completer<void> completer = Completer<void>(); | ||
| window.onBeginFrame = (Duration timeStamp) { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
| canvas.saveLayer(null, Paint()); | ||
| canvas.saveLayer(const Rect.fromLTWH(0, 0, 100, 100), Paint()); | ||
| canvas.drawRect(const Rect.fromLTRB(10, 10, 20, 20), Paint()); | ||
| canvas.restore(); | ||
| canvas.restore(); | ||
| final Picture picture = recorder.endRecording(); | ||
|  | ||
| final SceneBuilder builder = SceneBuilder(); | ||
| builder.addPicture(Offset.zero, picture); | ||
| final Scene scene = builder.build(); | ||
|  | ||
| window.render(scene); | ||
| scene.dispose(); | ||
| completer.complete(); | ||
| }; | ||
| window.scheduleFrame(); | ||
| await completer.future; | ||
|  | ||
| final vms.Timeline timeline = await vmService.getVMTimeline(); | ||
| await vmService.dispose(); | ||
|  | ||
| int saveLayerCount = 0; | ||
| for (final vms.TimelineEvent event in timeline.traceEvents!) { | ||
| final Map<String, dynamic> json = event.json!; | ||
| if (json['name'] == 'Canvas::saveLayer' && json['ph'] == 'B') { | ||
| saveLayerCount += 1; | ||
| } | ||
| } | ||
| expect(saveLayerCount, 2); | ||
| }); | ||
| } | 
Uh oh!
There was an error while loading. Please reload this page.