rasterizer.cc 19.3 KB
Newer Older
M
Michael Goderbauer 已提交
1
// Copyright 2013 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "flutter/shell/common/rasterizer.h"
6

7 8
#include "flutter/shell/common/persistent_cache.h"

9 10 11 12 13
#include <utility>

#include "third_party/skia/include/core/SkEncodedImageFormat.h"
#include "third_party/skia/include/core/SkImageEncoder.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
14
#include "third_party/skia/include/core/SkSerialProcs.h"
15
#include "third_party/skia/include/core/SkSurface.h"
16
#include "third_party/skia/include/core/SkSurfaceCharacterization.h"
17
#include "third_party/skia/include/utils/SkBase64.h"
18

19
namespace flutter {
20

21 22 23 24
// The rasterizer will tell Skia to purge cached resources that have not been
// used within this interval.
static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000);

D
Dan Field 已提交
25 26 27 28 29 30 31 32 33
// TODO(dnfield): Remove this once internal embedders have caught up.
static Rasterizer::DummyDelegate dummy_delegate_;
Rasterizer::Rasterizer(
    TaskRunners task_runners,
    std::unique_ptr<flutter::CompositorContext> compositor_context)
    : Rasterizer(dummy_delegate_,
                 std::move(task_runners),
                 std::move(compositor_context)) {}

34 35 36
Rasterizer::Rasterizer(Delegate& delegate, TaskRunners task_runners)
    : Rasterizer(delegate,
                 std::move(task_runners),
37 38
                 std::make_unique<flutter::CompositorContext>(
                     delegate.GetFrameBudget())) {}
39 40

Rasterizer::Rasterizer(
41
    Delegate& delegate,
42
    TaskRunners task_runners,
43
    std::unique_ptr<flutter::CompositorContext> compositor_context)
44 45
    : delegate_(delegate),
      task_runners_(std::move(task_runners)),
46
      compositor_context_(std::move(compositor_context)),
47
      user_override_resource_cache_bytes_(false),
48
      weak_factory_(this) {
49
  FML_DCHECK(compositor_context_);
50
}
51

52
Rasterizer::~Rasterizer() = default;
53

54
fml::WeakPtr<Rasterizer> Rasterizer::GetWeakPtr() const {
55
  return weak_factory_.GetWeakPtr();
56 57
}

58 59 60 61
fml::WeakPtr<SnapshotDelegate> Rasterizer::GetSnapshotDelegate() const {
  return weak_factory_.GetWeakPtr();
}

62 63
void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
  surface_ = std::move(surface);
64 65 66 67
  if (max_cache_bytes_.has_value()) {
    SetResourceCacheMaxBytes(max_cache_bytes_.value(),
                             user_override_resource_cache_bytes_);
  }
68
  compositor_context_->OnGrContextCreated();
69 70 71 72 73 74 75
  if (surface_->GetExternalViewEmbedder()) {
    const auto platform_id =
        task_runners_.GetPlatformTaskRunner()->GetTaskQueueId();
    const auto gpu_id = task_runners_.GetGPUTaskRunner()->GetTaskQueueId();
    gpu_thread_merger_ =
        fml::MakeRefCounted<fml::GpuThreadMerger>(platform_id, gpu_id);
  }
76 77 78
}

void Rasterizer::Teardown() {
79
  compositor_context_->OnGrContextDestroyed();
80 81 82 83
  surface_.reset();
  last_layer_tree_.reset();
}

84 85 86 87 88 89 90 91 92 93 94 95 96
void Rasterizer::NotifyLowMemoryWarning() const {
  if (!surface_) {
    FML_DLOG(INFO) << "Rasterizer::PurgeCaches called with no surface.";
    return;
  }
  auto context = surface_->GetContext();
  if (!context) {
    FML_DLOG(INFO) << "Rasterizer::PurgeCaches called with no GrContext.";
    return;
  }
  context->freeGpuResources();
}

97
flutter::TextureRegistry* Rasterizer::GetTextureRegistry() {
98
  return &compositor_context_->texture_registry();
99 100
}

101
flutter::LayerTree* Rasterizer::GetLastLayerTree() {
102 103 104 105 106 107 108 109 110 111
  return last_layer_tree_.get();
}

void Rasterizer::DrawLastLayerTree() {
  if (!last_layer_tree_ || !surface_) {
    return;
  }
  DrawToSurface(*last_layer_tree_);
}

112
void Rasterizer::Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) {
113
  TRACE_EVENT0("flutter", "GPURasterizer::Draw");
114 115 116 117 118
  if (gpu_thread_merger_ && !gpu_thread_merger_->IsOnRasterizingThread()) {
    // we yield and let this frame be serviced on the right thread.
    return;
  }
  FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread());
119

120
  RasterStatus raster_status = RasterStatus::kFailed;
121
  Pipeline<flutter::LayerTree>::Consumer consumer =
122 123 124 125 126 127 128 129
      [&](std::unique_ptr<LayerTree> layer_tree) {
        raster_status = DoDraw(std::move(layer_tree));
      };

  PipelineConsumeResult consume_result = pipeline->Consume(consumer);
  // if the raster status is to resubmit the frame, we push the frame to the
  // front of the queue and also change the consume status to more available.
  if (raster_status == RasterStatus::kResubmit) {
130
    auto front_continuation = pipeline->ProduceIfEmpty();
131 132 133 134
    front_continuation.Complete(std::move(resubmitted_layer_tree_));
  } else if (raster_status == RasterStatus::kEnqueuePipeline) {
    consume_result = PipelineConsumeResult::MoreAvailable;
  }
135 136 137

  // Consume as many pipeline items as possible. But yield the event loop
  // between successive tries.
138
  switch (consume_result) {
139
    case PipelineConsumeResult::MoreAvailable: {
140 141 142 143 144 145 146 147 148 149 150 151 152
      task_runners_.GetGPUTaskRunner()->PostTask(
          [weak_this = weak_factory_.GetWeakPtr(), pipeline]() {
            if (weak_this) {
              weak_this->Draw(pipeline);
            }
          });
      break;
    }
    default:
      break;
  }
}

153 154 155
sk_sp<SkImage> Rasterizer::DoMakeRasterSnapshot(
    SkISize size,
    std::function<void(SkCanvas*)> draw_callback) {
156 157 158 159
  TRACE_EVENT0("flutter", __FUNCTION__);

  sk_sp<SkSurface> surface;
  SkImageInfo image_info = SkImageInfo::MakeN32Premul(
160
      size.width(), size.height(), SkColorSpace::MakeSRGB());
161 162 163 164 165
  if (surface_ == nullptr || surface_->GetContext() == nullptr) {
    // Raster surface is fine if there is no on screen surface. This might
    // happen in case of software rendering.
    surface = SkSurface::MakeRaster(image_info);
  } else {
166
    if (!surface_->MakeRenderContextCurrent()) {
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
      return nullptr;
    }

    // When there is an on screen surface, we need a render target SkSurface
    // because we want to access texture backed images.
    surface = SkSurface::MakeRenderTarget(surface_->GetContext(),  // context
                                          SkBudgeted::kNo,         // budgeted
                                          image_info               // image info
    );
  }

  if (surface == nullptr || surface->getCanvas() == nullptr) {
    return nullptr;
  }

182
  draw_callback(surface->getCanvas());
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
  surface->getCanvas()->flush();

  sk_sp<SkImage> device_snapshot;
  {
    TRACE_EVENT0("flutter", "MakeDeviceSnpashot");
    device_snapshot = surface->makeImageSnapshot();
  }

  if (device_snapshot == nullptr) {
    return nullptr;
  }

  {
    TRACE_EVENT0("flutter", "DeviceHostTransfer");
    if (auto raster_image = device_snapshot->makeRasterImage()) {
      return raster_image;
    }
  }

  return nullptr;
}

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
sk_sp<SkImage> Rasterizer::MakeRasterSnapshot(sk_sp<SkPicture> picture,
                                              SkISize picture_size) {
  return DoMakeRasterSnapshot(picture_size,
                              [picture = std::move(picture)](SkCanvas* canvas) {
                                canvas->drawPicture(picture);
                              });
}

sk_sp<SkImage> Rasterizer::ConvertToRasterImage(sk_sp<SkImage> image) {
  TRACE_EVENT0("flutter", __FUNCTION__);

  // If the rasterizer does not have a surface with a GrContext, then it will
  // be unable to render a cross-context SkImage.  The caller will need to
  // create the raster image on the IO thread.
  if (surface_ == nullptr || surface_->GetContext() == nullptr) {
    return nullptr;
  }

  if (image == nullptr) {
    return nullptr;
  }

  return DoMakeRasterSnapshot(image->dimensions(),
                              [image = std::move(image)](SkCanvas* canvas) {
                                canvas->drawImage(image, 0, 0);
                              });
}

233 234
RasterStatus Rasterizer::DoDraw(
    std::unique_ptr<flutter::LayerTree> layer_tree) {
235 236
  FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread());

237
  if (!layer_tree || !surface_) {
238
    return RasterStatus::kFailed;
239 240
  }

241 242 243 244 245
  FrameTiming timing;
  timing.Set(FrameTiming::kBuildStart, layer_tree->build_start());
  timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish());
  timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now());

246 247 248
  PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
  persistent_cache->ResetStoredNewShaders();

249 250
  RasterStatus raster_status = DrawToSurface(*layer_tree);
  if (raster_status == RasterStatus::kSuccess) {
251
    last_layer_tree_ = std::move(layer_tree);
252 253 254
  } else if (raster_status == RasterStatus::kResubmit) {
    resubmitted_layer_tree_ = std::move(layer_tree);
    return raster_status;
255
  }
256 257 258 259 260 261 262

  if (persistent_cache->IsDumpingSkp() &&
      persistent_cache->StoredNewShaders()) {
    auto screenshot =
        ScreenshotLastLayerTree(ScreenshotType::SkiaPicture, false);
    persistent_cache->DumpSkp(*screenshot.data);
  }
263 264 265 266 267 268

  // TODO(liyuqian): in Fuchsia, the rasterization doesn't finish when
  // Rasterizer::DoDraw finishes. Future work is needed to adapt the timestamp
  // for Fuchsia to capture SceneUpdateContext::ExecutePaintTasks.
  timing.Set(FrameTiming::kRasterFinish, fml::TimePoint::Now());
  delegate_.OnFrameRasterized(timing);
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

  // Pipeline pressure is applied from a couple of places:
  // rasterizer: When there are more items as of the time of Consume.
  // animator (via shell): Frame gets produces every vsync.
  // Enqueing here is to account for the following scenario:
  // T = 1
  //  - one item (A) in the pipeline
  //  - rasterizer starts (and merges the threads)
  //  - pipeline consume result says no items to process
  // T = 2
  //  - animator produces (B) to the pipeline
  //  - applies pipeline pressure via platform thread.
  // T = 3
  //   - rasterizes finished (and un-merges the threads)
  //   - |Draw| for B yields as its on the wrong thread.
  // This enqueue ensures that we attempt to consume from the right
  // thread one more time after un-merge.
  if (gpu_thread_merger_) {
    if (gpu_thread_merger_->DecrementLease() ==
        fml::GpuThreadStatus::kUnmergedNow) {
      return RasterStatus::kEnqueuePipeline;
    }
  }

  return raster_status;
294 295
}

296
RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
297
  TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface");
298
  FML_DCHECK(surface_);
299

300
  auto frame = surface_->AcquireFrame(layer_tree.frame_size());
301 302

  if (frame == nullptr) {
303
    return RasterStatus::kFailed;
304 305 306 307 308
  }

  // There is no way for the compositor to know how long the layer tree
  // construction took. Fortunately, the layer tree does. Grab that time
  // for instrumentation.
309
  compositor_context_->ui_time().SetLapTime(layer_tree.build_time());
310

311
  auto* external_view_embedder = surface_->GetExternalViewEmbedder();
312

313
  SkCanvas* embedder_root_canvas = nullptr;
314
  if (external_view_embedder != nullptr) {
315
    external_view_embedder->BeginFrame(layer_tree.frame_size(),
316 317
                                       surface_->GetContext(),
                                       layer_tree.device_pixel_ratio());
318
    embedder_root_canvas = external_view_embedder->GetRootCanvas();
319
  }
320

321 322 323 324
  // If the external view embedder has specified an optional root surface, the
  // root surface transformation is set by the embedder instead of
  // having to apply it here.
  SkMatrix root_surface_transformation =
325
      embedder_root_canvas ? SkMatrix{} : surface_->GetRootTransformation();
326

327 328
  auto root_surface_canvas =
      embedder_root_canvas ? embedder_root_canvas : frame->SkiaCanvas();
329

330
  auto compositor_frame = compositor_context_->AcquireFrame(
331 332 333 334 335
      surface_->GetContext(),       // skia GrContext
      root_surface_canvas,          // root surface canvas
      external_view_embedder,       // external view embedder
      root_surface_transformation,  // root surface transformation
      true,                         // instrumentation enabled
336
      frame->supports_readback(),   // surface supports pixel reads
337 338
      gpu_thread_merger_            // thread merger
  );
339

340 341 342 343 344
  if (compositor_frame) {
    RasterStatus raster_status = compositor_frame->Raster(layer_tree, false);
    if (raster_status == RasterStatus::kFailed) {
      return raster_status;
    }
345
    frame->Submit();
346
    if (external_view_embedder != nullptr) {
347
      external_view_embedder->SubmitFrame(surface_->GetContext());
348
    }
349

350
    FireNextFrameCallbackIfPresent();
351

352
    if (surface_->GetContext()) {
353
      TRACE_EVENT0("flutter", "PerformDeferredSkiaCleanup");
354
      surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
355
    }
356

357
    return raster_status;
358 359
  }

360
  return RasterStatus::kFailed;
361 362
}

363 364 365 366 367
static sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
  return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
}

static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
368 369
    flutter::LayerTree* tree,
    flutter::CompositorContext& compositor_context) {
370
  FML_DCHECK(tree != nullptr);
371 372 373 374
  SkPictureRecorder recorder;
  recorder.beginRecording(
      SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));

375 376 377
  SkMatrix root_surface_transformation;
  root_surface_transformation.reset();

378 379 380 381
  // TODO(amirh): figure out how to take a screenshot with embedded UIView.
  // https://github.com/flutter/flutter/issues/23435
  auto frame = compositor_context.AcquireFrame(
      nullptr, recorder.getRecordingCanvas(), nullptr,
382
      root_surface_transformation, false, true, nullptr);
383 384 385

  frame->Raster(*tree, true);

386 387 388 389
  SkSerialProcs procs = {0};
  procs.fTypefaceProc = SerializeTypeface;

  return recorder.finishRecordingAsPicture()->serialize(&procs);
390 391
}

392 393
static sk_sp<SkSurface> CreateSnapshotSurface(GrContext* surface_context,
                                              const SkISize& size) {
394 395
  const auto image_info = SkImageInfo::MakeN32Premul(
      size.width(), size.height(), SkColorSpace::MakeSRGB());
396 397 398 399 400 401 402 403 404 405 406 407 408 409
  if (surface_context) {
    // There is a rendering surface that may contain textures that are going to
    // be referenced in the layer tree about to be drawn.
    return SkSurface::MakeRenderTarget(surface_context,  //
                                       SkBudgeted::kNo,  //
                                       image_info        //
    );
  }

  // There is no rendering surface, assume no GPU textures are present and
  // create a raster surface.
  return SkSurface::MakeRaster(image_info);
}

410
static sk_sp<SkData> ScreenshotLayerTreeAsImage(
411 412
    flutter::LayerTree* tree,
    flutter::CompositorContext& compositor_context,
413 414
    GrContext* surface_context,
    bool compressed) {
415 416 417 418 419
  // Attempt to create a snapshot surface depending on whether we have access to
  // a valid GPU rendering context.
  auto snapshot_surface =
      CreateSnapshotSurface(surface_context, tree->frame_size());
  if (snapshot_surface == nullptr) {
420
    FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
421 422
    return nullptr;
  }
423 424

  // Draw the current layer tree into the snapshot surface.
425
  auto* canvas = snapshot_surface->getCanvas();
426 427 428 429 430 431

  // There is no root surface transformation for the screenshot layer. Reset the
  // matrix to identity.
  SkMatrix root_surface_transformation;
  root_surface_transformation.reset();

432 433 434 435 436 437 438
  // We want to ensure we call the base method for
  // CompositorContext::AcquireFrame instead of the platform-specific method.
  // Specifically, Fuchsia's CompositorContext handles the rendering surface
  // itself which means that we will still continue to render to the onscreen
  // surface if we don't call the base method.
  auto frame = compositor_context.flutter::CompositorContext::AcquireFrame(
      surface_context, canvas, nullptr, root_surface_transformation, false,
439
      true, nullptr);
440
  canvas->clear(SK_ColorTRANSPARENT);
441
  frame->Raster(*tree, true);
442
  canvas->flush();
443 444

  // Prepare an image from the surface, this image may potentially be on th GPU.
445
  auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot();
446
  if (!potentially_gpu_snapshot) {
447
    FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
448 449 450 451
    return nullptr;
  }

  // Copy the GPU image snapshot into CPU memory.
452
  auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
453
  if (!cpu_snapshot) {
454
    FML_LOG(ERROR) << "Screenshot: unable to make raster image";
455 456 457
    return nullptr;
  }

458
  // If the caller want the pixels to be compressed, there is a Skia utility to
459
  // compress to PNG. Use that.
460
  if (compressed) {
461 462 463 464 465 466
    return cpu_snapshot->encodeToData();
  }

  // Copy it into a bitmap and return the same.
  SkPixmap pixmap;
  if (!cpu_snapshot->peekPixels(&pixmap)) {
467
    FML_LOG(ERROR) << "Screenshot: unable to obtain bitmap pixels";
468
    return nullptr;
469
  }
470 471

  return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
472 473 474 475 476
}

Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
    Rasterizer::ScreenshotType type,
    bool base64_encode) {
477
  auto* layer_tree = GetLastLayerTree();
478
  if (layer_tree == nullptr) {
479
    FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
480 481 482 483 484
    return {};
  }

  sk_sp<SkData> data = nullptr;

485 486
  GrContext* surface_context = surface_ ? surface_->GetContext() : nullptr;

487 488
  switch (type) {
    case ScreenshotType::SkiaPicture:
489
      data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
490 491
      break;
    case ScreenshotType::UncompressedImage:
492 493
      data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
                                        surface_context, false);
494 495
      break;
    case ScreenshotType::CompressedImage:
496 497
      data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
                                        surface_context, true);
498 499 500 501
      break;
  }

  if (data == nullptr) {
502
    FML_LOG(ERROR) << "Screenshot data was null.";
503 504 505 506 507 508 509 510 511 512 513 514 515
    return {};
  }

  if (base64_encode) {
    size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr);
    auto b64_data = SkData::MakeUninitialized(b64_size);
    SkBase64::Encode(data->data(), data->size(), b64_data->writable_data());
    return Rasterizer::Screenshot{b64_data, layer_tree->frame_size()};
  }

  return Rasterizer::Screenshot{data, layer_tree->frame_size()};
}

516
void Rasterizer::SetNextFrameCallback(const fml::closure& callback) {
517 518 519 520 521 522 523 524 525 526 527 528 529
  next_frame_callback_ = callback;
}

void Rasterizer::FireNextFrameCallbackIfPresent() {
  if (!next_frame_callback_) {
    return;
  }
  // It is safe for the callback to set a new callback.
  auto callback = next_frame_callback_;
  next_frame_callback_ = nullptr;
  callback();
}

530 531 532 533 534 535 536 537 538 539 540 541 542 543
void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) {
  user_override_resource_cache_bytes_ |= from_user;

  if (!from_user && user_override_resource_cache_bytes_) {
    // We should not update the setting here if a user has explicitly set a
    // value for this over the flutter/skia channel.
    return;
  }

  max_cache_bytes_ = max_bytes;
  if (!surface_) {
    return;
  }

544 545 546 547 548 549 550 551
  GrContext* context = surface_->GetContext();
  if (context) {
    int max_resources;
    context->getResourceCacheLimits(&max_resources, nullptr);
    context->setResourceCacheLimits(max_resources, max_bytes);
  }
}

552 553 554 555 556 557 558 559 560 561 562 563 564
std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
  if (!surface_) {
    return std::nullopt;
  }
  GrContext* context = surface_->GetContext();
  if (context) {
    size_t max_bytes;
    context->getResourceCacheLimits(nullptr, &max_bytes);
    return max_bytes;
  }
  return std::nullopt;
}

565 566 567 568 569 570 571 572 573
Rasterizer::Screenshot::Screenshot() {}

Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data, SkISize p_size)
    : data(std::move(p_data)), frame_size(p_size) {}

Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default;

Rasterizer::Screenshot::~Screenshot() = default;

574
}  // namespace flutter