blob: 20f74cecb8591c8dc443285d133313293ed83e19 [file] [log] [blame]
// 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.
#include "flutter/shell/common/rasterizer.h"
#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"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceCharacterization.h"
#include "third_party/skia/src/utils/SkBase64.h"
namespace shell {
Rasterizer::Rasterizer(blink::TaskRunners task_runners)
: task_runners_(std::move(task_runners)), weak_factory_(this) {
weak_prototype_ = weak_factory_.GetWeakPtr();
}
Rasterizer::~Rasterizer() = default;
fml::WeakPtr<Rasterizer> Rasterizer::GetWeakPtr() const {
return weak_prototype_;
}
void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
surface_ = std::move(surface);
}
void Rasterizer::Teardown() {
surface_.reset();
last_layer_tree_.reset();
}
flow::TextureRegistry* Rasterizer::GetTextureRegistry() {
if (!surface_) {
return nullptr;
}
return &(surface_->GetCompositorContext().texture_registry());
}
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(
fxl::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
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;
}
}
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) {
FXL_DCHECK(surface_);
auto frame = surface_->AcquireFrame(layer_tree.frame_size());
if (frame == nullptr) {
return false;
}
auto& compositor_context = surface_->GetCompositorContext();
// 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.
compositor_context.engine_time().SetLapTime(layer_tree.construction_time());
auto compositor_frame = compositor_context.AcquireFrame(
surface_->GetContext(), frame->SkiaCanvas(), true);
if (compositor_frame && compositor_frame->Raster(layer_tree, false)) {
frame->Submit();
FireNextFrameCallbackIfPresent();
return true;
}
return false;
}
static sk_sp<SkPicture> ScreenshotLayerTreeAsPicture(flow::LayerTree* tree) {
FXL_DCHECK(tree != nullptr);
SkPictureRecorder recorder;
recorder.beginRecording(
SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));
flow::CompositorContext compositor_context;
auto frame = compositor_context.AcquireFrame(
nullptr, recorder.getRecordingCanvas(), false);
frame->Raster(*tree, true);
return recorder.finishRecordingAsPicture();
}
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);
}
static sk_sp<SkData> ScreenshotLayerTreeAsImage(flow::LayerTree* tree,
GrContext* surface_context,
bool compressed) {
// 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) {
return nullptr;
}
// Draw the current layer tree into the snapshot surface.
flow::CompositorContext compositor_context;
auto canvas = snapshot_surface->getCanvas();
auto frame = compositor_context.AcquireFrame(surface_context, canvas, false);
canvas->clear(SK_ColorBLACK);
frame->Raster(*tree, true);
canvas->flush();
// 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) {
return nullptr;
}
// Copy the GPU image snapshot into CPU memory.
auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
if (!cpu_snapshot) {
return nullptr;
}
// If the caller want the pixels to be compressed, there is a Skia utilitiy to
// compress to PNG. Use that.
if (compressed) {
return cpu_snapshot->encodeToData();
}
// Copy it into a bitmap and return the same.
SkPixmap pixmap;
if (!cpu_snapshot->peekPixels(&pixmap)) {
return nullptr;
}
return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
}
Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
Rasterizer::ScreenshotType type,
bool base64_encode) {
auto layer_tree = GetLastLayerTree();
if (layer_tree == nullptr) {
FXL_DLOG(INFO) << "Last layer tree was null when screenshotting.";
return {};
}
sk_sp<SkData> data = nullptr;
GrContext* surface_context = surface_ ? surface_->GetContext() : nullptr;
switch (type) {
case ScreenshotType::SkiaPicture:
data = ScreenshotLayerTreeAsPicture(layer_tree)->serialize();
break;
case ScreenshotType::UncompressedImage:
data = ScreenshotLayerTreeAsImage(layer_tree, surface_context, false);
break;
case ScreenshotType::CompressedImage:
data = ScreenshotLayerTreeAsImage(layer_tree, surface_context, true);
break;
}
if (data == nullptr) {
FXL_DLOG(INFO) << "Sceenshot data was null.";
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()};
}
void Rasterizer::SetNextFrameCallback(fxl::Closure callback) {
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();
}
} // namespace shell