Merge pull request #2258 from abarth/flow

Move //sky/compositor to //flow
This commit is contained in:
Adam Barth 2016-01-12 22:42:02 -08:00
commit 4742f4ef6a
34 changed files with 1493 additions and 0 deletions

View File

@ -0,0 +1,45 @@
# 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.
source_set("flow") {
sources = [
"checkerboard.cc",
"checkerboard.h",
"clip_path_layer.cc",
"clip_path_layer.h",
"clip_rect_layer.cc",
"clip_rect_layer.h",
"clip_rrect_layer.cc",
"clip_rrect_layer.h",
"color_filter_layer.cc",
"color_filter_layer.h",
"container_layer.cc",
"container_layer.h",
"instrumentation.cc",
"instrumentation.h",
"layer.cc",
"layer.h",
"layer_tree.cc",
"layer_tree.h",
"opacity_layer.cc",
"opacity_layer.h",
"paint_context.cc",
"paint_context.h",
"picture_layer.cc",
"picture_layer.h",
"raster_cache.cc",
"raster_cache.h",
"performance_overlay_layer.cc",
"performance_overlay_layer.h",
"shader_mask_layer.cc",
"shader_mask_layer.h",
"transform_layer.cc",
"transform_layer.h",
]
deps = [
"//base",
"//skia",
]
}

View File

@ -0,0 +1,6 @@
Flow
====
Flow is a simple compositor based on Skia that the Flutter engine uses to cache
recoded paint commands and pixels generated from those recordings. Flow runs on
the GPU thread and uploads information to the GPU.

View File

@ -0,0 +1,45 @@
// 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 "flow/checkerboard.h"
#include "third_party/skia/include/core/SkShader.h"
namespace flow {
static SkShader* CreateCheckerboardShader(SkColor c1, SkColor c2, int size) {
SkBitmap bm;
bm.allocN32Pixels(2 * size, 2 * size);
bm.eraseColor(c1);
bm.eraseArea(SkIRect::MakeLTRB(0, 0, size, size), c2);
bm.eraseArea(SkIRect::MakeLTRB(size, size, 2 * size, 2 * size), c2);
return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode);
}
static void DrawCheckerboard(SkCanvas* canvas,
SkColor c1,
SkColor c2,
int size) {
SkPaint paint;
paint.setShader(CreateCheckerboardShader(c1, c2, size))->unref();
canvas->drawPaint(paint);
}
void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect) {
// Draw a checkerboard
canvas->save();
canvas->clipRect(rect);
DrawCheckerboard(canvas, 0x4400FF00, 0x00000000, 12);
canvas->restore();
// Stroke the drawn area
SkPaint debugPaint;
debugPaint.setStrokeWidth(3);
debugPaint.setColor(SK_ColorRED);
debugPaint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(rect, debugPaint);
}
} // namespace flow

View File

@ -0,0 +1,16 @@
// 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.
#ifndef FLOW_CHECKERBOARD_H_
#define FLOW_CHECKERBOARD_H_
#include "third_party/skia/include/core/SkCanvas.h"
namespace flow {
void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect);
} // namespace flow
#endif // FLOW_CHECKERBOARD_H_

View File

@ -0,0 +1,23 @@
// 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 "flow/clip_path_layer.h"
namespace flow {
ClipPathLayer::ClipPathLayer() {
}
ClipPathLayer::~ClipPathLayer() {
}
void ClipPathLayer::Paint(PaintContext::ScopedFrame& frame) {
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, false);
canvas.saveLayer(&clip_path_.getBounds(), nullptr);
canvas.clipPath(clip_path_);
PaintChildren(frame);
}
} // namespace flow

View File

@ -0,0 +1,30 @@
// 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.
#ifndef FLOW_CLIP_PATH_LAYER_H_
#define FLOW_CLIP_PATH_LAYER_H_
#include "flow/container_layer.h"
namespace flow {
class ClipPathLayer : public ContainerLayer {
public:
ClipPathLayer();
~ClipPathLayer() override;
void set_clip_path(const SkPath& clip_path) { clip_path_ = clip_path; }
protected:
void Paint(PaintContext::ScopedFrame& frame) override;
private:
SkPath clip_path_;
DISALLOW_COPY_AND_ASSIGN(ClipPathLayer);
};
} // namespace flow
#endif // FLOW_CLIP_PATH_LAYER_H_

View File

@ -0,0 +1,22 @@
// 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 "flow/clip_rect_layer.h"
namespace flow {
ClipRectLayer::ClipRectLayer() {
}
ClipRectLayer::~ClipRectLayer() {
}
void ClipRectLayer::Paint(PaintContext::ScopedFrame& frame) {
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, true);
canvas.clipRect(clip_rect_);
PaintChildren(frame);
}
} // namespace flow

View File

@ -0,0 +1,30 @@
// 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.
#ifndef FLOW_CLIP_RECT_LAYER_H_
#define FLOW_CLIP_RECT_LAYER_H_
#include "flow/container_layer.h"
namespace flow {
class ClipRectLayer : public ContainerLayer {
public:
ClipRectLayer();
~ClipRectLayer() override;
void set_clip_rect(const SkRect& clip_rect) { clip_rect_ = clip_rect; }
protected:
void Paint(PaintContext::ScopedFrame& frame) override;
private:
SkRect clip_rect_;
DISALLOW_COPY_AND_ASSIGN(ClipRectLayer);
};
} // namespace flow
#endif // FLOW_CLIP_RECT_LAYER_H_

View File

@ -0,0 +1,23 @@
// 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 "flow/clip_rrect_layer.h"
namespace flow {
ClipRRectLayer::ClipRRectLayer() {
}
ClipRRectLayer::~ClipRRectLayer() {
}
void ClipRRectLayer::Paint(PaintContext::ScopedFrame& frame) {
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, false);
canvas.saveLayer(&clip_rrect_.getBounds(), nullptr);
canvas.clipRRect(clip_rrect_);
PaintChildren(frame);
}
} // namespace flow

View File

@ -0,0 +1,29 @@
// 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.
#ifndef FLOW_CLIP_RRECT_LAYER_H_
#define FLOW_CLIP_RRECT_LAYER_H_
#include "flow/container_layer.h"
namespace flow {
class ClipRRectLayer : public ContainerLayer {
public:
ClipRRectLayer();
~ClipRRectLayer() override;
void set_clip_rrect(const SkRRect& clip_rrect) { clip_rrect_ = clip_rrect; }
void Paint(PaintContext::ScopedFrame& frame) override;
private:
SkRRect clip_rrect_;
DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer);
};
} // namespace flow
#endif // FLOW_CLIP_RRECT_LAYER_H_

View File

@ -0,0 +1,32 @@
// 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 "flow/color_filter_layer.h"
namespace flow {
ColorFilterLayer::ColorFilterLayer() {
}
ColorFilterLayer::~ColorFilterLayer() {
}
void ColorFilterLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
ContainerLayer::Preroll(context, matrix);
set_paint_bounds(context->child_paint_bounds);
}
void ColorFilterLayer::Paint(PaintContext::ScopedFrame& frame) {
skia::RefPtr<SkColorFilter> color_filter =
skia::AdoptRef(SkColorFilter::CreateModeFilter(color_, transfer_mode_));
SkPaint paint;
paint.setColorFilter(color_filter.get());
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, false);
canvas.saveLayer(&paint_bounds(), &paint);
PaintChildren(frame);
}
} // namespace flow

View File

@ -0,0 +1,36 @@
// 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.
#ifndef FLOW_COLOR_FILTER_LAYER_H_
#define FLOW_COLOR_FILTER_LAYER_H_
#include "flow/container_layer.h"
namespace flow {
class ColorFilterLayer : public ContainerLayer {
public:
ColorFilterLayer();
~ColorFilterLayer() override;
void set_color(SkColor color) { color_ = color; }
void set_transfer_mode(SkXfermode::Mode transfer_mode) {
transfer_mode_ = transfer_mode;
}
protected:
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
void Paint(PaintContext::ScopedFrame& frame) override;
private:
SkColor color_;
SkXfermode::Mode transfer_mode_;
DISALLOW_COPY_AND_ASSIGN(ColorFilterLayer);
};
} // namespace flow
#endif // FLOW_COLOR_FILTER_LAYER_H_

View File

@ -0,0 +1,40 @@
// 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 "flow/container_layer.h"
namespace flow {
ContainerLayer::ContainerLayer() {
}
ContainerLayer::~ContainerLayer() {
}
void ContainerLayer::Add(std::unique_ptr<Layer> layer) {
layer->set_parent(this);
layers_.push_back(std::move(layer));
}
void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
PrerollChildren(context, matrix);
}
void ContainerLayer::PrerollChildren(PrerollContext* context,
const SkMatrix& matrix) {
SkRect child_paint_bounds;
for (auto& layer : layers_) {
PrerollContext child_context = *context;
layer->Preroll(&child_context, matrix);
child_paint_bounds.join(child_context.child_paint_bounds);
}
context->child_paint_bounds = child_paint_bounds;
}
void ContainerLayer::PaintChildren(PaintContext::ScopedFrame& frame) const {
for (auto& layer : layers_)
layer->Paint(frame);
}
} // namespace flow

View File

@ -0,0 +1,35 @@
// 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.
#ifndef FLOW_CONTAINER_LAYER_H_
#define FLOW_CONTAINER_LAYER_H_
#include <vector>
#include "flow/layer.h"
namespace flow {
class ContainerLayer : public Layer {
public:
ContainerLayer();
~ContainerLayer() override;
void Add(std::unique_ptr<Layer> layer);
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
void PrerollChildren(PrerollContext* context, const SkMatrix& matrix);
void PaintChildren(PaintContext::ScopedFrame& frame) const;
const std::vector<std::unique_ptr<Layer>>& layers() const { return layers_; }
private:
std::vector<std::unique_ptr<Layer>> layers_;
DISALLOW_COPY_AND_ASSIGN(ContainerLayer);
};
} // namespace flow
#endif // FLOW_CONTAINER_LAYER_H_

View File

@ -0,0 +1,140 @@
// 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 "flow/instrumentation.h"
#include <algorithm>
#include "third_party/skia/include/core/SkPath.h"
namespace flow {
namespace instrumentation {
static const size_t kMaxSamples = 120;
static const size_t kMaxFrameMarkers = 8;
static const double kOneFrameMS = 1e3 / 60.0;
Stopwatch::Stopwatch() : _start(base::TimeTicks::Now()), _current_sample(0) {
const base::TimeDelta delta;
_laps.resize(kMaxSamples, delta);
}
void Stopwatch::start() {
_start = base::TimeTicks::Now();
_current_sample = (_current_sample + 1) % kMaxSamples;
}
void Stopwatch::stop() {
_laps[_current_sample] = base::TimeTicks::Now() - _start;
}
void Stopwatch::setLapTime(const base::TimeDelta& delta) {
_current_sample = (_current_sample + 1) % kMaxSamples;
_laps[_current_sample] = delta;
}
const base::TimeDelta& Stopwatch::lastLap() const {
return _laps[(_current_sample - 1) % kMaxSamples];
}
static inline constexpr double UnitFrameInterval(double frameTimeMS) {
return frameTimeMS * 60.0 * 1e-3;
}
base::TimeDelta Stopwatch::maxDelta() const {
base::TimeDelta maxDelta;
for (size_t i = 0; i < kMaxSamples; i++) {
if (_laps[i] > maxDelta) {
maxDelta = _laps[i];
}
}
return maxDelta;
}
void Stopwatch::visualize(SkCanvas& canvas, const SkRect& rect) const {
SkPaint paint;
// Paint the background
paint.setColor(0xAAFFFFFF);
canvas.drawRect(rect, paint);
// Paint the graph
SkPath path;
const SkScalar width = rect.width();
const SkScalar height = rect.height();
// Find the max delta. We use this to scale the graph
double maxInterval = maxDelta().InMillisecondsF();
if (maxInterval < kOneFrameMS) {
maxInterval = kOneFrameMS;
} else {
maxInterval =
kOneFrameMS * (static_cast<size_t>(maxInterval / kOneFrameMS) + 1);
}
const double maxUnitInterval = UnitFrameInterval(maxInterval);
// Draw the path
double unitHeight =
UnitFrameInterval(_laps[0].InMillisecondsF()) / maxUnitInterval;
path.moveTo(0, height);
path.lineTo(0, height * (1.0 - unitHeight));
for (size_t i = 0; i < kMaxSamples; i++) {
double unitWidth = (static_cast<double>(i + 1) / kMaxSamples);
unitHeight =
UnitFrameInterval(_laps[i].InMillisecondsF()) / maxUnitInterval;
path.lineTo(width * unitWidth, height * (1.0 - unitHeight));
}
path.lineTo(width, height);
path.close();
paint.setColor(0xAA0000FF);
canvas.drawPath(path, paint);
paint.setStrokeWidth(1);
paint.setStyle(SkPaint::Style::kStroke_Style);
paint.setColor(0xAAFFFFFF);
if (maxInterval > kOneFrameMS) {
// Paint the horizontal markers
size_t frameMarkerCount = static_cast<size_t>(maxInterval / kOneFrameMS);
// Limit the number of markers displayed. After a certain point, the graph
// becomes crowded
if (frameMarkerCount > kMaxFrameMarkers) {
frameMarkerCount = 1;
}
for (size_t frameIndex = 0; frameIndex < frameMarkerCount; frameIndex++) {
const double frameHeight =
height * (1.0 - (UnitFrameInterval((frameIndex + 1) * kOneFrameMS) /
maxUnitInterval));
canvas.drawLine(0, frameHeight, width, frameHeight, paint);
}
}
// Paint the vertical marker
if (UnitFrameInterval(lastLap().InMillisecondsF()) > 1.0) {
// budget exceeded
paint.setColor(SK_ColorRED);
} else {
// within budget
paint.setColor(SK_ColorGREEN);
}
double sampleX = width * (static_cast<double>(_current_sample) / kMaxSamples);
paint.setStrokeWidth(3);
canvas.drawLine(sampleX, 0, sampleX, height, paint);
}
Stopwatch::~Stopwatch() = default;
} // namespace instrumentation
} // namespace flow

View File

@ -0,0 +1,76 @@
// 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.
#ifndef FLOW_INSTRUMENTATION_H_
#define FLOW_INSTRUMENTATION_H_
#include <vector>
#include "base/macros.h"
#include "base/time/time.h"
#include "third_party/skia/include/core/SkCanvas.h"
namespace flow {
namespace instrumentation {
class Stopwatch {
public:
class ScopedLap {
public:
explicit ScopedLap(Stopwatch& stopwatch) : _stopwatch(stopwatch) {
_stopwatch.start();
}
~ScopedLap() { _stopwatch.stop(); }
private:
Stopwatch& _stopwatch;
DISALLOW_COPY_AND_ASSIGN(ScopedLap);
};
explicit Stopwatch();
~Stopwatch();
const base::TimeDelta& lastLap() const;
base::TimeDelta currentLap() const { return base::TimeTicks::Now() - _start; }
void visualize(SkCanvas& canvas, const SkRect& rect) const;
void start();
void stop();
void setLapTime(const base::TimeDelta& delta);
private:
base::TimeTicks _start;
std::vector<base::TimeDelta> _laps;
size_t _current_sample;
base::TimeDelta maxDelta() const;
DISALLOW_COPY_AND_ASSIGN(Stopwatch);
};
class Counter {
public:
explicit Counter() : _count(0) {}
size_t count() const { return _count; }
void reset(size_t count = 0) { _count = count; }
void increment(size_t count = 1) { _count += count; }
private:
size_t _count;
DISALLOW_COPY_AND_ASSIGN(Counter);
};
} // namespace instrumentation
} // namespace flow
#endif // FLOW_INSTRUMENTATION_H_

View File

@ -0,0 +1,23 @@
// 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 "flow/layer.h"
#include "third_party/skia/include/core/SkColorFilter.h"
namespace flow {
Layer::Layer()
: parent_(nullptr)
, has_paint_bounds_(false)
, paint_bounds_() {
}
Layer::~Layer() {
}
void Layer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
}
} // namespace flow

View File

@ -0,0 +1,67 @@
// 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.
#ifndef FLOW_LAYER_H_
#define FLOW_LAYER_H_
#include <memory>
#include <vector>
#include "base/logging.h"
#include "base/macros.h"
#include "skia/ext/refptr.h"
#include "flow/paint_context.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkXfermode.h"
namespace flow {
class ContainerLayer;
class Layer {
public:
Layer();
virtual ~Layer();
struct PrerollContext {
PaintContext::ScopedFrame& frame;
SkRect child_paint_bounds;
};
virtual void Preroll(PrerollContext* context, const SkMatrix& matrix);
virtual void Paint(PaintContext::ScopedFrame& frame) = 0;
ContainerLayer* parent() const { return parent_; }
void set_parent(ContainerLayer* parent) { parent_ = parent; }
const bool has_paint_bounds() const { return has_paint_bounds_; }
const SkRect& paint_bounds() const {
DCHECK(has_paint_bounds_);
return paint_bounds_;
}
void set_paint_bounds(const SkRect& paint_bounds) {
has_paint_bounds_ = true;
paint_bounds_ = paint_bounds;
}
private:
ContainerLayer* parent_;
bool has_paint_bounds_; // if false, paint_bounds_ is not valid
SkRect paint_bounds_;
DISALLOW_COPY_AND_ASSIGN(Layer);
};
} // namespace flow
#endif // FLOW_LAYER_H_

View File

@ -0,0 +1,31 @@
// 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 "flow/layer_tree.h"
#include "base/trace_event/trace_event.h"
#include "flow/layer.h"
namespace flow {
LayerTree::LayerTree() : rasterizer_tracing_threashold_(0) {
}
LayerTree::~LayerTree() {
}
void LayerTree::Raster(PaintContext::ScopedFrame& frame) {
{
TRACE_EVENT0("flutter", "LayerTree::Preroll")
Layer::PrerollContext context = { frame, SkRect::MakeEmpty() };
root_layer_->Preroll(&context, SkMatrix());
}
{
TRACE_EVENT0("flutter", "LayerTree::Paint")
root_layer_->Paint(frame);
}
}
} // namespace flow

View File

@ -0,0 +1,66 @@
// 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.
#ifndef FLOW_LAYER_TREE_H_
#define FLOW_LAYER_TREE_H_
#include <stdint.h>
#include <memory>
#include "base/macros.h"
#include "base/time/time.h"
#include "flow/layer.h"
#include "third_party/skia/include/core/SkSize.h"
namespace flow {
class LayerTree {
public:
LayerTree();
~LayerTree();
void Raster(PaintContext::ScopedFrame& frame);
Layer* root_layer() const { return root_layer_.get(); }
void set_root_layer(std::unique_ptr<Layer> root_layer) {
root_layer_ = std::move(root_layer);
}
const SkISize& frame_size() const { return frame_size_; }
void set_frame_size(const SkISize& frame_size) { frame_size_ = frame_size; }
void set_construction_time(const base::TimeDelta& delta) {
construction_time_ = delta;
}
const base::TimeDelta& construction_time() const {
return construction_time_;
}
// The number of frame intervals missed after which the compositor must
// trace the rasterized picture to a trace file. Specify 0 to disable all
// tracing
void set_rasterizer_tracing_threshold(uint32_t interval) {
rasterizer_tracing_threashold_ = interval;
}
uint32_t rasterizer_tracing_threshold() const {
return rasterizer_tracing_threashold_;
}
private:
SkISize frame_size_; // Physical pixels.
std::unique_ptr<Layer> root_layer_;
base::TimeDelta construction_time_;
uint32_t rasterizer_tracing_threashold_;
DISALLOW_COPY_AND_ASSIGN(LayerTree);
};
} // namespace flow
#endif // FLOW_LAYER_TREE_H_

View File

@ -0,0 +1,31 @@
// 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 "flow/opacity_layer.h"
namespace flow {
OpacityLayer::OpacityLayer() {
}
OpacityLayer::~OpacityLayer() {
}
void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
ContainerLayer::Preroll(context, matrix);
set_paint_bounds(context->child_paint_bounds);
}
void OpacityLayer::Paint(PaintContext::ScopedFrame& frame) {
SkPaint paint;
paint.setColor(SkColorSetARGB(alpha_, 0, 0, 0));
paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, false);
canvas.saveLayer(&paint_bounds(), &paint);
PaintChildren(frame);
}
} // namespace flow

View File

@ -0,0 +1,31 @@
// 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.
#ifndef FLOW_OPACITY_LAYER_H_
#define FLOW_OPACITY_LAYER_H_
#include "flow/container_layer.h"
namespace flow {
class OpacityLayer : public ContainerLayer {
public:
OpacityLayer();
~OpacityLayer() override;
void set_alpha(int alpha) { alpha_ = alpha; }
protected:
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
void Paint(PaintContext::ScopedFrame& frame) override;
private:
int alpha_;
DISALLOW_COPY_AND_ASSIGN(OpacityLayer);
};
} // namespace flow
#endif // FLOW_OPACITY_LAYER_H_

View File

@ -0,0 +1,52 @@
// 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 "flow/paint_context.h"
#include "base/logging.h"
#include "third_party/skia/include/core/SkCanvas.h"
namespace flow {
PaintContext::PaintContext() {
}
void PaintContext::beginFrame(ScopedFrame& frame, bool enableInstrumentation) {
if (enableInstrumentation) {
frame_count_.increment();
frame_time_.start();
}
}
void PaintContext::endFrame(ScopedFrame& frame, bool enableInstrumentation) {
raster_cache_.SweepAfterFrame();
if (enableInstrumentation) {
frame_time_.stop();
}
}
PaintContext::ScopedFrame PaintContext::AcquireFrame(
GrContext* gr_context, SkCanvas& canvas, bool instrumentation_enabled) {
return ScopedFrame(*this, gr_context, canvas, instrumentation_enabled);
}
PaintContext::ScopedFrame::ScopedFrame(PaintContext& context,
GrContext* gr_context,
SkCanvas& canvas,
bool instrumentation_enabled)
: context_(context), gr_context_(gr_context), canvas_(&canvas),
instrumentation_enabled_(instrumentation_enabled) {
context_.beginFrame(*this, instrumentation_enabled_);
}
PaintContext::ScopedFrame::ScopedFrame(ScopedFrame&& frame) = default;
PaintContext::ScopedFrame::~ScopedFrame() {
context_.endFrame(*this, instrumentation_enabled_);
}
PaintContext::~PaintContext() {
}
} // namespace flow

View File

@ -0,0 +1,74 @@
// 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.
#ifndef FLOW_PAINT_CONTEXT_H_
#define FLOW_PAINT_CONTEXT_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/logging.h"
#include "flow/instrumentation.h"
#include "flow/raster_cache.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
namespace flow {
class PaintContext {
public:
class ScopedFrame {
public:
SkCanvas& canvas() { return *canvas_; }
PaintContext& context() const { return context_; }
GrContext* gr_context() const { return gr_context_; }
ScopedFrame(ScopedFrame&& frame);
~ScopedFrame();
private:
PaintContext& context_;
GrContext* gr_context_;
SkCanvas* canvas_;
const bool instrumentation_enabled_;
ScopedFrame(PaintContext& context,
GrContext* gr_context,
SkCanvas& canvas,
bool instrumentation_enabled);
friend class PaintContext;
DISALLOW_COPY_AND_ASSIGN(ScopedFrame);
};
PaintContext();
~PaintContext();
ScopedFrame AcquireFrame(GrContext* gr_context,
SkCanvas& canvas,
bool instrumentation_enabled = true);
RasterCache& raster_cache() { return raster_cache_; }
const instrumentation::Counter& frame_count() const { return frame_count_; }
const instrumentation::Stopwatch& frame_time() const { return frame_time_; }
instrumentation::Stopwatch& engine_time() { return engine_time_; };
private:
RasterCache raster_cache_;
instrumentation::Counter frame_count_;
instrumentation::Stopwatch frame_time_;
instrumentation::Stopwatch engine_time_;
void beginFrame(ScopedFrame& frame, bool enableInstrumentation);
void endFrame(ScopedFrame& frame, bool enableInstrumentation);
DISALLOW_COPY_AND_ASSIGN(PaintContext);
};
} // namespace flow
#endif // FLOW_PAINT_CONTEXT_H_

View File

@ -0,0 +1,79 @@
// 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 <string>
#include <iostream>
#include <iomanip>
#include "flow/performance_overlay_layer.h"
namespace flow {
PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t enabledOptions)
: options_(enabledOptions) {
}
static void DrawStatisticsText(SkCanvas& canvas,
const std::string& string,
int x,
int y) {
SkPaint paint;
paint.setTextSize(14);
paint.setLinearText(false);
paint.setColor(SK_ColorRED);
canvas.drawText(string.c_str(), string.size(), x, y, paint);
}
static void VisualizeStopWatch(SkCanvas& canvas,
const instrumentation::Stopwatch& stopwatch,
SkScalar width,
bool show_graph,
bool show_labels,
std::string label_prefix) {
const int x = 8;
const int y = 70;
const int height = 80;
if (show_graph) {
SkRect visualizationRect = SkRect::MakeWH(width, height);
stopwatch.visualize(canvas, visualizationRect);
}
if (show_labels) {
double msPerFrame = stopwatch.lastLap().InMillisecondsF();
double fps = 1e3 / msPerFrame;
std::stringstream stream;
stream.setf(std::ios::fixed | std::ios::showpoint);
stream << std::setprecision(2);
stream << label_prefix << " " << fps << " FPS | " << msPerFrame
<< "ms/frame";
DrawStatisticsText(canvas, stream.str(), x, y);
}
if (show_labels || show_graph) {
canvas.translate(0, height);
}
}
void PerformanceOverlayLayer::Paint(PaintContext::ScopedFrame& frame) {
if (!options_) {
return;
}
SkScalar width = has_paint_bounds() ? paint_bounds().width() : 0;
SkAutoCanvasRestore save(&frame.canvas(), true);
VisualizeStopWatch(frame.canvas(), frame.context().frame_time(), width,
options_ & kVisualizeRasterizerStatistics,
options_ & kDisplayRasterizerStatistics,
"Rasterizer");
VisualizeStopWatch(frame.canvas(), frame.context().engine_time(), width,
options_ & kVisualizeEngineStatistics,
options_ & kDisplayEngineStatistics,
"Engine");
}
} // namespace flow

View File

@ -0,0 +1,32 @@
// 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.
#ifndef FLOW_PERFORMANCE_OVERLAY_LAYER_H_
#define FLOW_PERFORMANCE_OVERLAY_LAYER_H_
#include "base/macros.h"
#include "flow/layer.h"
namespace flow {
const int kDisplayRasterizerStatistics = 0x01;
const int kVisualizeRasterizerStatistics = 0x02;
const int kDisplayEngineStatistics = 0x04;
const int kVisualizeEngineStatistics = 0x08;
class PerformanceOverlayLayer : public Layer {
public:
explicit PerformanceOverlayLayer(uint64_t enabledOptions);
void Paint(PaintContext::ScopedFrame& frame) override;
private:
int options_;
DISALLOW_COPY_AND_ASSIGN(PerformanceOverlayLayer);
};
} // namespace flow
#endif // FLOW_PERFORMANCE_OVERLAY_LAYER_H_

View File

@ -0,0 +1,45 @@
// 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 "flow/picture_layer.h"
#include "base/logging.h"
#include "flow/checkerboard.h"
#include "flow/raster_cache.h"
namespace flow {
// TODO(abarth): Make this configurable by developers.
const bool kDebugCheckerboardRasterizedLayers = false;
PictureLayer::PictureLayer() {
}
PictureLayer::~PictureLayer() {
}
void PictureLayer::Preroll(PrerollContext* context,
const SkMatrix& matrix) {
image_ = context->frame.context().raster_cache().GetPrerolledImage(
context->frame.gr_context(), picture_.get(), matrix);
context->child_paint_bounds = picture_->cullRect().makeOffset(offset_.x(), offset_.y());
}
void PictureLayer::Paint(PaintContext::ScopedFrame& frame) {
DCHECK(picture_);
SkCanvas& canvas = frame.canvas();
if (image_) {
SkRect rect = picture_->cullRect().makeOffset(offset_.x(), offset_.y());
canvas.drawImageRect(image_.get(), rect, nullptr, SkCanvas::kFast_SrcRectConstraint);
if (kDebugCheckerboardRasterizedLayers)
DrawCheckerboard(&canvas, rect);
} else {
SkAutoCanvasRestore save(&canvas, true);
canvas.translate(offset_.x(), offset_.y());
canvas.drawPicture(picture_.get());
}
}
} // namespace flow

View File

@ -0,0 +1,38 @@
// 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.
#ifndef FLOW_PICTURE_LAYER_H_
#define FLOW_PICTURE_LAYER_H_
#include "flow/layer.h"
namespace flow {
class PictureLayer : public Layer {
public:
PictureLayer();
~PictureLayer() override;
void set_offset(const SkPoint& offset) { offset_ = offset; }
void set_picture(SkPicture* picture) { picture_ = skia::SharePtr(picture); }
SkPicture* picture() const { return picture_.get(); }
void Preroll(PrerollContext* frame, const SkMatrix& matrix) override;
void Paint(PaintContext::ScopedFrame& frame) override;
private:
SkPoint offset_;
skia::RefPtr<SkPicture> picture_;
// If we rasterized the picture separately, image_ holds the pixels.
skia::RefPtr<SkImage> image_;
DISALLOW_COPY_AND_ASSIGN(PictureLayer);
};
} // namespace flow
#endif // FLOW_PICTURE_LAYER_H_

View File

@ -0,0 +1,114 @@
// Copyright 2016 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 "flow/raster_cache.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "flow/paint_context.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
#define ENABLE_RASTER_CACHE 1
namespace flow {
#if ENABLE_RASTER_CACHE
static const int kRasterThreshold = 3;
static bool isWorthRasterizing(SkPicture* picture) {
// TODO(abarth): We should find a better heuristic here that lets us avoid
// wasting memory on trivial layers that are easy to re-rasterize every frame.
return picture->approximateOpCount() > 10 || picture->hasText();
}
#endif
RasterCache::RasterCache() {
}
RasterCache::~RasterCache() {
}
RasterCache::Entry::Entry() {
physical_size.setEmpty();
}
RasterCache::Entry::~Entry() {
}
skia::RefPtr<SkImage> RasterCache::GetPrerolledImage(GrContext* context,
SkPicture* picture,
const SkMatrix& ctm) {
#if ENABLE_RASTER_CACHE
SkScalar scaleX = ctm.getScaleX();
SkScalar scaleY = ctm.getScaleY();
SkRect rect = picture->cullRect();
SkISize physical_size = SkISize::Make(rect.width() * scaleX,
rect.height() * scaleY);
if (physical_size.isEmpty())
return nullptr;
Entry& entry = cache_[picture->uniqueID()];
const bool size_matched = entry.physical_size == physical_size;
entry.used_this_frame = true;
entry.physical_size = physical_size;
if (!size_matched) {
entry.access_count = 1;
entry.image = nullptr;
return nullptr;
}
entry.access_count++;
if (entry.access_count >= kRasterThreshold) {
// Saturate at the threshhold.
entry.access_count = kRasterThreshold;
if (!entry.image && isWorthRasterizing(picture)) {
TRACE_EVENT2("flutter", "Rasterize picture layer",
"width", physical_size.width(),
"height", physical_size.height());
SkImageInfo info = SkImageInfo::MakeN32Premul(physical_size);
skia::RefPtr<SkSurface> surface = skia::AdoptRef(
SkSurface::NewRenderTarget(context, SkSurface::kYes_Budgeted, info));
if (surface) {
SkCanvas* canvas = surface->getCanvas();
canvas->clear(SK_ColorTRANSPARENT);
SkMatrix matrix = SkMatrix::MakeScale(scaleX, scaleY);
canvas->drawPicture(picture, &matrix, nullptr);
entry.image = skia::AdoptRef(surface->newImageSnapshot());
}
}
}
return entry.image;
#else
return nullptr;
#endif
}
void RasterCache::SweepAfterFrame() {
std::vector<Cache::iterator> dead;
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
Entry& entry = it->second;
if (!entry.used_this_frame)
dead.push_back(it);
entry.used_this_frame = false;
}
for (auto it : dead)
cache_.erase(it);
}
} // namespace flow

View File

@ -0,0 +1,47 @@
// Copyright 2016 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.
#ifndef FLOW_RASTER_CACHE_H_
#define FLOW_RASTER_CACHE_H_
#include <memory>
#include <unordered_map>
#include "base/macros.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkSize.h"
#include "third_party/skia/include/core/SkImage.h"
#include "flow/instrumentation.h"
namespace flow {
class RasterCache {
public:
RasterCache();
~RasterCache();
skia::RefPtr<SkImage> GetPrerolledImage(
GrContext* context, SkPicture* picture, const SkMatrix& ctm);
void SweepAfterFrame();
private:
struct Entry {
Entry();
~Entry();
bool used_this_frame = false;
int access_count = 0;
SkISize physical_size;
skia::RefPtr<SkImage> image;
};
using Cache = std::unordered_map<uint32_t, Entry>;
Cache cache_;
DISALLOW_COPY_AND_ASSIGN(RasterCache);
};
} // namespace flow
#endif // FLOW_RASTER_CACHE_H_

View File

@ -0,0 +1,33 @@
// Copyright 2016 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 "flow/shader_mask_layer.h"
namespace flow {
ShaderMaskLayer::ShaderMaskLayer() {
}
ShaderMaskLayer::~ShaderMaskLayer() {
}
void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
ContainerLayer::Preroll(context, matrix);
set_paint_bounds(context->child_paint_bounds);
}
void ShaderMaskLayer::Paint(PaintContext::ScopedFrame& frame) {
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, false);
canvas.saveLayer(&paint_bounds(), nullptr);
PaintChildren(frame);
SkPaint paint;
paint.setXfermodeMode(transfer_mode_);
paint.setShader(shader_.get());
canvas.translate(mask_rect_.left(), mask_rect_.top());
canvas.drawRect(SkRect::MakeWH(mask_rect_.width(), mask_rect_.height()), paint);
}
} // namespace flow

View File

@ -0,0 +1,43 @@
// Copyright 2016 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.
#ifndef FLOW_SHADER_MASK_LAYER_H_
#define FLOW_SHADER_MASK_LAYER_H_
#include "flow/container_layer.h"
#include "third_party/skia/include/core/SkShader.h"
namespace flow {
class ShaderMaskLayer : public ContainerLayer {
public:
ShaderMaskLayer();
~ShaderMaskLayer() override;
void set_shader(SkShader* shader) { shader_ = skia::SharePtr(shader); }
void set_mask_rect(const SkRect& mask_rect) {
mask_rect_ = mask_rect;
}
void set_transfer_mode(SkXfermode::Mode transfer_mode) {
transfer_mode_ = transfer_mode;
}
protected:
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
void Paint(PaintContext::ScopedFrame& frame) override;
private:
skia::RefPtr<SkShader> shader_;
SkRect mask_rect_;
SkXfermode::Mode transfer_mode_;
DISALLOW_COPY_AND_ASSIGN(ShaderMaskLayer);
};
} // namespace flow
#endif // FLOW_SHADER_MASK_LAYER_H_

View File

@ -0,0 +1,29 @@
// 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 "flow/transform_layer.h"
namespace flow {
TransformLayer::TransformLayer() {
}
TransformLayer::~TransformLayer() {
}
void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
SkMatrix childMatrix;
childMatrix.setConcat(matrix, transform_);
PrerollChildren(context, childMatrix);
transform_.mapRect(&context->child_paint_bounds);
}
void TransformLayer::Paint(PaintContext::ScopedFrame& frame) {
SkCanvas& canvas = frame.canvas();
SkAutoCanvasRestore save(&canvas, true);
canvas.concat(transform_);
PaintChildren(frame);
}
} // namespace flow

View File

@ -0,0 +1,30 @@
// 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.
#ifndef FLOW_TRANSFORM_LAYER_H_
#define FLOW_TRANSFORM_LAYER_H_
#include "flow/container_layer.h"
namespace flow {
class TransformLayer : public ContainerLayer {
public:
TransformLayer();
~TransformLayer() override;
void set_transform(const SkMatrix& transform) { transform_ = transform; }
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
void Paint(PaintContext::ScopedFrame& frame) override;
private:
SkMatrix transform_;
DISALLOW_COPY_AND_ASSIGN(TransformLayer);
};
} // namespace flow
#endif // FLOW_TRANSFORM_LAYER_H_