rasterizer.cc 10.5 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// 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 9 10 11
#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"
12
#include "third_party/skia/include/core/SkSerialProcs.h"
13
#include "third_party/skia/include/core/SkSurface.h"
14
#include "third_party/skia/include/core/SkSurfaceCharacterization.h"
15
#include "third_party/skia/include/utils/SkBase64.h"
16

17 18
namespace shell {

19
Rasterizer::Rasterizer(blink::TaskRunners task_runners)
20 21 22 23 24 25 26 27 28
    : Rasterizer(std::move(task_runners),
                 std::make_unique<flow::CompositorContext>()) {}

Rasterizer::Rasterizer(
    blink::TaskRunners task_runners,
    std::unique_ptr<flow::CompositorContext> compositor_context)
    : task_runners_(std::move(task_runners)),
      compositor_context_(std::move(compositor_context)),
      weak_factory_(this) {
29
  FML_DCHECK(compositor_context_);
30
}
31

32
Rasterizer::~Rasterizer() = default;
33

34
fml::WeakPtr<Rasterizer> Rasterizer::GetWeakPtr() const {
35
  return weak_factory_.GetWeakPtr();
36 37
}

38 39 40 41
fml::WeakPtr<blink::SnapshotDelegate> Rasterizer::GetSnapshotDelegate() const {
  return weak_factory_.GetWeakPtr();
}

42 43
void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
  surface_ = std::move(surface);
44
  compositor_context_->OnGrContextCreated();
45 46 47
}

void Rasterizer::Teardown() {
48
  compositor_context_->OnGrContextDestroyed();
49 50 51 52 53
  surface_.reset();
  last_layer_tree_.reset();
}

flow::TextureRegistry* Rasterizer::GetTextureRegistry() {
54
  return &compositor_context_->texture_registry();
55 56 57 58 59 60 61 62 63 64 65 66 67 68
}

flow::LayerTree* Rasterizer::GetLastLayerTree() {
  return last_layer_tree_.get();
}

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

void Rasterizer::Draw(
69
    fml::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
  TRACE_EVENT0("flutter", "GPURasterizer::Draw");

  flutter::Pipeline<flow::LayerTree>::Consumer consumer =
      std::bind(&Rasterizer::DoDraw, this, std::placeholders::_1);

  // Consume as many pipeline items as possible. But yield the event loop
  // between successive tries.
  switch (pipeline->Consume(consumer)) {
    case flutter::PipelineConsumeResult::MoreAvailable: {
      task_runners_.GetGPUTaskRunner()->PostTask(
          [weak_this = weak_factory_.GetWeakPtr(), pipeline]() {
            if (weak_this) {
              weak_this->Draw(pipeline);
            }
          });
      break;
    }
    default:
      break;
  }
}

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
sk_sp<SkImage> Rasterizer::MakeRasterSnapshot(sk_sp<SkPicture> picture,
                                              SkISize picture_size) {
  TRACE_EVENT0("flutter", __FUNCTION__);

  sk_sp<SkSurface> surface;
  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(SkImageInfo::MakeN32Premul(picture_size));
  } else {
    // 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
        SkImageInfo::MakeN32Premul(picture_size)  // image info
    );
  }

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

  surface->getCanvas()->drawPicture(picture.get());

  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;
}

139 140 141 142 143 144 145 146 147 148 149
void Rasterizer::DoDraw(std::unique_ptr<flow::LayerTree> layer_tree) {
  if (!layer_tree || !surface_) {
    return;
  }

  if (DrawToSurface(*layer_tree)) {
    last_layer_tree_ = std::move(layer_tree);
  }
}

bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) {
150
  FML_DCHECK(surface_);
151 152 153 154 155 156 157 158 159 160

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

  if (frame == nullptr) {
    return false;
  }

  // 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.
161
  compositor_context_->engine_time().SetLapTime(layer_tree.construction_time());
162

163 164
  auto canvas = frame->SkiaCanvas();

165
  auto external_view_embedder = surface_->GetExternalViewEmbedder();
166

167
  auto compositor_frame = compositor_context_->AcquireFrame(
168
      surface_->GetContext(), canvas, external_view_embedder,
169
      surface_->GetRootTransformation(), true);
170 171

  if (canvas) {
172
    canvas->clear(SK_ColorTRANSPARENT);
173
  }
174 175 176 177 178 179 180 181 182 183

  if (compositor_frame && compositor_frame->Raster(layer_tree, false)) {
    frame->Submit();
    FireNextFrameCallbackIfPresent();
    return true;
  }

  return false;
}

184 185 186 187 188
static sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
  return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
}

static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
189 190
    flow::LayerTree* tree,
    flow::CompositorContext& compositor_context) {
191
  FML_DCHECK(tree != nullptr);
192 193 194 195
  SkPictureRecorder recorder;
  recorder.beginRecording(
      SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));

196 197 198
  SkMatrix root_surface_transformation;
  root_surface_transformation.reset();

199 200 201 202 203
  // 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,
      root_surface_transformation, false);
204 205 206

  frame->Raster(*tree, true);

207 208 209 210
  SkSerialProcs procs = {0};
  procs.fTypefaceProc = SerializeTypeface;

  return recorder.finishRecordingAsPicture()->serialize(&procs);
211 212
}

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
static sk_sp<SkSurface> CreateSnapshotSurface(GrContext* surface_context,
                                              const SkISize& size) {
  const auto image_info = SkImageInfo::MakeN32Premul(size);
  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);
}

230 231 232 233 234
static sk_sp<SkData> ScreenshotLayerTreeAsImage(
    flow::LayerTree* tree,
    flow::CompositorContext& compositor_context,
    GrContext* surface_context,
    bool compressed) {
235 236 237 238 239
  // 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) {
240
    FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
241 242
    return nullptr;
  }
243 244 245

  // Draw the current layer tree into the snapshot surface.
  auto canvas = snapshot_surface->getCanvas();
246 247 248 249 250 251 252

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

  auto frame = compositor_context.AcquireFrame(
253
      surface_context, canvas, nullptr, root_surface_transformation, false);
254
  canvas->clear(SK_ColorTRANSPARENT);
255
  frame->Raster(*tree, true);
256
  canvas->flush();
257 258 259 260

  // Prepare an image from the surface, this image may potentially be on th GPU.
  auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot();
  if (!potentially_gpu_snapshot) {
261
    FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
262 263 264 265 266 267
    return nullptr;
  }

  // Copy the GPU image snapshot into CPU memory.
  auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
  if (!cpu_snapshot) {
268
    FML_LOG(ERROR) << "Screenshot: unable to make raster image";
269 270 271
    return nullptr;
  }

272
  // If the caller want the pixels to be compressed, there is a Skia utility to
273
  // compress to PNG. Use that.
274
  if (compressed) {
275 276 277 278 279 280
    return cpu_snapshot->encodeToData();
  }

  // Copy it into a bitmap and return the same.
  SkPixmap pixmap;
  if (!cpu_snapshot->peekPixels(&pixmap)) {
281
    FML_LOG(ERROR) << "Screenshot: unable to obtain bitmap pixels";
282
    return nullptr;
283
  }
284 285

  return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
286 287 288 289 290 291 292
}

Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
    Rasterizer::ScreenshotType type,
    bool base64_encode) {
  auto layer_tree = GetLastLayerTree();
  if (layer_tree == nullptr) {
293
    FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
294 295 296 297 298
    return {};
  }

  sk_sp<SkData> data = nullptr;

299 300
  GrContext* surface_context = surface_ ? surface_->GetContext() : nullptr;

301 302
  switch (type) {
    case ScreenshotType::SkiaPicture:
303
      data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
304 305
      break;
    case ScreenshotType::UncompressedImage:
306 307
      data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
                                        surface_context, false);
308 309
      break;
    case ScreenshotType::CompressedImage:
310 311
      data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
                                        surface_context, true);
312 313 314 315
      break;
  }

  if (data == nullptr) {
316
    FML_LOG(ERROR) << "Screenshot data was null.";
317 318 319 320 321 322 323 324 325 326 327 328 329
    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()};
}

330
void Rasterizer::SetNextFrameCallback(fml::closure callback) {
331 332 333 334 335 336 337 338 339 340 341 342 343
  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();
}

344
}  // namespace shell