rasterizer.cc 21.6 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
#include <utility>

11 12
#include "flutter/fml/time/time_delta.h"
#include "flutter/fml/time/time_point.h"
13 14 15
#include "third_party/skia/include/core/SkEncodedImageFormat.h"
#include "third_party/skia/include/core/SkImageEncoder.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
16
#include "third_party/skia/include/core/SkSerialProcs.h"
17
#include "third_party/skia/include/core/SkSurface.h"
18
#include "third_party/skia/include/core/SkSurfaceCharacterization.h"
19
#include "third_party/skia/include/utils/SkBase64.h"
20

21
namespace flutter {
22

23 24 25 26
// 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 已提交
27 28 29 30 31 32 33 34 35
// 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)) {}

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

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

54
Rasterizer::~Rasterizer() = default;
55

56 57
fml::TaskRunnerAffineWeakPtr<Rasterizer> Rasterizer::GetWeakPtr() const {
  return weak_factory_.GetTaskRunnerAffineWeakPtr();
58 59
}

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

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

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

86 87 88 89 90 91 92 93 94 95 96 97 98
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();
}

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

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

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

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

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
  RasterStatus raster_status = RasterStatus::kFailed;
  Pipeline<flutter::LayerTree>::Consumer consumer =
      [&](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) {
    auto front_continuation = pipeline->ProduceIfEmpty();
    bool result =
        front_continuation.Complete(std::move(resubmitted_layer_tree_));
    if (result) {
      consume_result = PipelineConsumeResult::MoreAvailable;
    }
  } else if (raster_status == RasterStatus::kEnqueuePipeline) {
    consume_result = PipelineConsumeResult::MoreAvailable;
  }
142

143 144 145 146
  // Merging the thread as we know the next `Draw` should be run on the platform
  // thread.
  if (raster_status == RasterStatus::kResubmit) {
    auto* external_view_embedder = surface_->GetExternalViewEmbedder();
147 148 149
    // We know only the `external_view_embedder` can
    // causes|RasterStatus::kResubmit|. Check to make sure.
    FML_DCHECK(external_view_embedder != nullptr);
150 151 152
    external_view_embedder->EndFrame(raster_thread_merger_);
  }

153
  // Consume as many pipeline items as possible. But yield the event loop
154
  // between successive tries.
155 156 157 158 159 160 161 162 163 164 165 166
  switch (consume_result) {
    case PipelineConsumeResult::MoreAvailable: {
      task_runners_.GetRasterTaskRunner()->PostTask(
          [weak_this = weak_factory_.GetTaskRunnerAffineWeakPtr(), pipeline]() {
            if (weak_this) {
              weak_this->Draw(pipeline);
            }
          });
      break;
    }
    default:
      break;
167 168 169
  }
}

170 171 172
sk_sp<SkImage> Rasterizer::DoMakeRasterSnapshot(
    SkISize size,
    std::function<void(SkCanvas*)> draw_callback) {
173 174 175 176
  TRACE_EVENT0("flutter", __FUNCTION__);

  sk_sp<SkSurface> surface;
  SkImageInfo image_info = SkImageInfo::MakeN32Premul(
177
      size.width(), size.height(), SkColorSpace::MakeSRGB());
178 179 180 181 182
  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 {
183
    if (!surface_->MakeRenderContextCurrent()) {
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
      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;
  }

199
  draw_callback(surface->getCanvas());
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
  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;
}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
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);
                              });
}

250 251
RasterStatus Rasterizer::DoDraw(
    std::unique_ptr<flutter::LayerTree> layer_tree) {
252
  FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
253

254
  if (!layer_tree || !surface_) {
255
    return RasterStatus::kFailed;
256 257
  }

258
  FrameTiming timing;
259
  const fml::TimePoint frame_target_time = layer_tree->target_time();
260 261 262 263
  timing.Set(FrameTiming::kBuildStart, layer_tree->build_start());
  timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish());
  timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now());

264 265 266
  PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
  persistent_cache->ResetStoredNewShaders();

267 268
  RasterStatus raster_status = DrawToSurface(*layer_tree);
  if (raster_status == RasterStatus::kSuccess) {
269
    last_layer_tree_ = std::move(layer_tree);
270 271 272
  } else if (raster_status == RasterStatus::kResubmit) {
    resubmitted_layer_tree_ = std::move(layer_tree);
    return raster_status;
273
  }
274 275 276 277 278 279 280

  if (persistent_cache->IsDumpingSkp() &&
      persistent_cache->StoredNewShaders()) {
    auto screenshot =
        ScreenshotLastLayerTree(ScreenshotType::SkiaPicture, false);
    persistent_cache->DumpSkp(*screenshot.data);
  }
281 282 283 284

  // 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.
285 286
  const auto raster_finish_time = fml::TimePoint::Now();
  timing.Set(FrameTiming::kRasterFinish, raster_finish_time);
287
  delegate_.OnFrameRasterized(timing);
288

289 290 291 292 293 294 295 296 297 298 299
  if (raster_finish_time > frame_target_time) {
    fml::TimePoint latest_frame_target_time =
        delegate_.GetLatestFrameTargetTime();
    const auto frame_budget_millis = delegate_.GetFrameBudget().count();
    if (latest_frame_target_time < raster_finish_time) {
      latest_frame_target_time =
          latest_frame_target_time +
          fml::TimeDelta::FromMillisecondsF(frame_budget_millis);
    }
    const auto frame_lag =
        (latest_frame_target_time - frame_target_time).ToMillisecondsF();
300 301 302 303 304 305 306 307 308 309 310 311 312
    const int vsync_transitions_missed = round(frame_lag / frame_budget_millis);
    fml::tracing::TraceEventAsyncComplete(
        "flutter",                    // category
        "SceneDisplayLag",            // name
        raster_finish_time,           // begin_time
        latest_frame_target_time,     // end_time
        "frame_target_time",          // arg_key_1
        frame_target_time,            // arg_val_1
        "current_frame_target_time",  // arg_key_2
        latest_frame_target_time,     // arg_val_2
        "vsync_transitions_missed",   // arg_key_3
        vsync_transitions_missed      // arg_val_3
    );
313 314
  }

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
  // 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.
331 332 333
  if (raster_thread_merger_) {
    if (raster_thread_merger_->DecrementLease() ==
        fml::RasterThreadStatus::kUnmergedNow) {
334 335 336 337 338
      return RasterStatus::kEnqueuePipeline;
    }
  }

  return raster_status;
339 340
}

341
RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
342
  TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface");
343
  FML_DCHECK(surface_);
344

345
  auto frame = surface_->AcquireFrame(layer_tree.frame_size());
346 347

  if (frame == nullptr) {
348
    return RasterStatus::kFailed;
349 350 351 352 353
  }

  // 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.
354
  compositor_context_->ui_time().SetLapTime(layer_tree.build_time());
355

356
  auto* external_view_embedder = surface_->GetExternalViewEmbedder();
357

358
  SkCanvas* embedder_root_canvas = nullptr;
359
  if (external_view_embedder != nullptr) {
360
    external_view_embedder->BeginFrame(layer_tree.frame_size(),
361 362
                                       surface_->GetContext(),
                                       layer_tree.device_pixel_ratio());
363
    embedder_root_canvas = external_view_embedder->GetRootCanvas();
364
  }
365

366 367 368 369
  // 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 =
370
      embedder_root_canvas ? SkMatrix{} : surface_->GetRootTransformation();
371

372 373
  auto root_surface_canvas =
      embedder_root_canvas ? embedder_root_canvas : frame->SkiaCanvas();
374

375
  auto compositor_frame = compositor_context_->AcquireFrame(
376 377 378 379 380
      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
381
      frame->supports_readback(),   // surface supports pixel reads
382
      raster_thread_merger_         // thread merger
383
  );
384

385 386 387 388 389
  if (compositor_frame) {
    RasterStatus raster_status = compositor_frame->Raster(layer_tree, false);
    if (raster_status == RasterStatus::kFailed) {
      return raster_status;
    }
390
    if (external_view_embedder != nullptr) {
391 392 393 394 395 396 397 398 399 400
      external_view_embedder->SubmitFrame(surface_->GetContext(),
                                          root_surface_canvas);
      // The external view embedder may mutate the root surface canvas while
      // submitting the frame.
      // Therefore, submit the final frame after asking the external view
      // embedder to submit the frame.
      frame->Submit();
      external_view_embedder->FinishFrame();
    } else {
      frame->Submit();
401
    }
402

403
    FireNextFrameCallbackIfPresent();
404

405
    if (surface_->GetContext()) {
406
      TRACE_EVENT0("flutter", "PerformDeferredSkiaCleanup");
407
      surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
408
    }
409

410
    return raster_status;
411 412
  }

413
  return RasterStatus::kFailed;
414 415
}

416 417 418 419 420
static sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
  return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
}

static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
421 422
    flutter::LayerTree* tree,
    flutter::CompositorContext& compositor_context) {
423
  FML_DCHECK(tree != nullptr);
424 425 426 427
  SkPictureRecorder recorder;
  recorder.beginRecording(
      SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));

428 429 430
  SkMatrix root_surface_transformation;
  root_surface_transformation.reset();

431 432 433 434
  // 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,
435
      root_surface_transformation, false, true, nullptr);
436 437 438

  frame->Raster(*tree, true);

439 440 441 442
  SkSerialProcs procs = {0};
  procs.fTypefaceProc = SerializeTypeface;

  return recorder.finishRecordingAsPicture()->serialize(&procs);
443 444
}

445 446
static sk_sp<SkSurface> CreateSnapshotSurface(GrContext* surface_context,
                                              const SkISize& size) {
447 448
  const auto image_info = SkImageInfo::MakeN32Premul(
      size.width(), size.height(), SkColorSpace::MakeSRGB());
449 450 451 452 453 454 455 456 457 458 459 460 461 462
  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);
}

463
static sk_sp<SkData> ScreenshotLayerTreeAsImage(
464 465
    flutter::LayerTree* tree,
    flutter::CompositorContext& compositor_context,
466 467
    GrContext* surface_context,
    bool compressed) {
468 469 470 471 472
  // 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) {
473
    FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
474 475
    return nullptr;
  }
476 477

  // Draw the current layer tree into the snapshot surface.
478
  auto* canvas = snapshot_surface->getCanvas();
479 480 481 482 483 484

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

485 486 487 488 489 490 491
  // 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,
492
      true, nullptr);
493
  canvas->clear(SK_ColorTRANSPARENT);
494
  frame->Raster(*tree, true);
495
  canvas->flush();
496 497

  // Prepare an image from the surface, this image may potentially be on th GPU.
498
  auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot();
499
  if (!potentially_gpu_snapshot) {
500
    FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
501 502 503 504
    return nullptr;
  }

  // Copy the GPU image snapshot into CPU memory.
505
  auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
506
  if (!cpu_snapshot) {
507
    FML_LOG(ERROR) << "Screenshot: unable to make raster image";
508 509 510
    return nullptr;
  }

511
  // If the caller want the pixels to be compressed, there is a Skia utility to
512
  // compress to PNG. Use that.
513
  if (compressed) {
514 515 516 517 518 519
    return cpu_snapshot->encodeToData();
  }

  // Copy it into a bitmap and return the same.
  SkPixmap pixmap;
  if (!cpu_snapshot->peekPixels(&pixmap)) {
520
    FML_LOG(ERROR) << "Screenshot: unable to obtain bitmap pixels";
521
    return nullptr;
522
  }
523 524

  return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
525 526 527 528 529
}

Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
    Rasterizer::ScreenshotType type,
    bool base64_encode) {
530
  auto* layer_tree = GetLastLayerTree();
531
  if (layer_tree == nullptr) {
532
    FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
533 534 535 536 537
    return {};
  }

  sk_sp<SkData> data = nullptr;

538 539
  GrContext* surface_context = surface_ ? surface_->GetContext() : nullptr;

540 541
  switch (type) {
    case ScreenshotType::SkiaPicture:
542
      data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
543 544
      break;
    case ScreenshotType::UncompressedImage:
545 546
      data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
                                        surface_context, false);
547 548
      break;
    case ScreenshotType::CompressedImage:
549 550
      data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
                                        surface_context, true);
551 552 553 554
      break;
  }

  if (data == nullptr) {
555
    FML_LOG(ERROR) << "Screenshot data was null.";
556 557 558 559 560 561 562 563 564 565 566 567 568
    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()};
}

569
void Rasterizer::SetNextFrameCallback(const fml::closure& callback) {
570 571 572 573 574 575 576 577 578 579 580 581 582
  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();
}

583 584 585 586 587 588 589 590 591 592 593 594 595 596
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;
  }

597 598 599 600 601 602 603 604
  GrContext* context = surface_->GetContext();
  if (context) {
    int max_resources;
    context->getResourceCacheLimits(&max_resources, nullptr);
    context->setResourceCacheLimits(max_resources, max_bytes);
  }
}

605 606 607 608 609 610 611 612 613 614 615 616 617
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;
}

618 619 620 621 622 623 624 625 626
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;

627
}  // namespace flutter