diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index feed60b197fdf..eb856b479e043 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -122,8 +122,10 @@ ../../../flutter/impeller/.gitignore ../../../flutter/impeller/README.md ../../../flutter/impeller/aiks/aiks_unittests.cc +../../../flutter/impeller/aiks/canvas_recorder_unittests.cc ../../../flutter/impeller/aiks/canvas_unittests.cc ../../../flutter/impeller/aiks/testing +../../../flutter/impeller/aiks/trace_serializer_unittests.cc ../../../flutter/impeller/archivist/archivist_unittests.cc ../../../flutter/impeller/base/README.md ../../../flutter/impeller/base/base_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 53a9299268fe4..84d3c9fd3f98a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1027,6 +1027,8 @@ ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc + ../../../f ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/canvas.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/canvas.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/canvas_recorder.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/canvas_type.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/color_filter.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/color_filter.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/color_source.cc + ../../../flutter/LICENSE @@ -1043,6 +1045,8 @@ ORIGIN: ../../../flutter/impeller/aiks/picture.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/picture.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/picture_recorder.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/picture_recorder.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/trace_serializer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/trace_serializer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/archivist/archivable.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/archivist/archivable.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/archivist/archive.cc + ../../../flutter/LICENSE @@ -3777,6 +3781,8 @@ FILE: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc FILE: ../../../flutter/impeller/aiks/aiks_playground_inspector.h FILE: ../../../flutter/impeller/aiks/canvas.cc FILE: ../../../flutter/impeller/aiks/canvas.h +FILE: ../../../flutter/impeller/aiks/canvas_recorder.h +FILE: ../../../flutter/impeller/aiks/canvas_type.h FILE: ../../../flutter/impeller/aiks/color_filter.cc FILE: ../../../flutter/impeller/aiks/color_filter.h FILE: ../../../flutter/impeller/aiks/color_source.cc @@ -3793,6 +3799,8 @@ FILE: ../../../flutter/impeller/aiks/picture.cc FILE: ../../../flutter/impeller/aiks/picture.h FILE: ../../../flutter/impeller/aiks/picture_recorder.cc FILE: ../../../flutter/impeller/aiks/picture_recorder.h +FILE: ../../../flutter/impeller/aiks/trace_serializer.cc +FILE: ../../../flutter/impeller/aiks/trace_serializer.h FILE: ../../../flutter/impeller/archivist/archivable.cc FILE: ../../../flutter/impeller/archivist/archivable.h FILE: ../../../flutter/impeller/archivist/archive.cc diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index ea963093336ba..f341021bdf5d3 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -4,12 +4,18 @@ import("../tools/impeller.gni") +config("impeller_canvas_recorder_config") { + defines = [ "IMPELLER_TRACE_CANVAS" ] +} + impeller_component("aiks") { sources = [ "aiks_context.cc", "aiks_context.h", "canvas.cc", "canvas.h", + "canvas_recorder.h", + "canvas_type.h", "color_filter.cc", "color_filter.h", "color_source.cc", @@ -26,6 +32,7 @@ impeller_component("aiks") { "picture.h", "picture_recorder.cc", "picture_recorder.h", + "trace_serializer.h", ] public_deps = [ @@ -35,6 +42,11 @@ impeller_component("aiks") { ] deps = [ "//flutter/fml" ] + + if (impeller_trace_canvas) { + sources += [ "trace_serializer.cc" ] + public_configs = [ ":impeller_canvas_recorder_config" ] + } } impeller_component("aiks_playground") { @@ -60,10 +72,12 @@ impeller_component("aiks_unittests") { sources = [ "aiks_unittests.cc", + "canvas_recorder_unittests.cc", "canvas_unittests.cc", "testing/context_mock.h", "testing/context_spy.cc", "testing/context_spy.h", + "trace_serializer_unittests.cc", ] deps = [ ":aiks", @@ -75,6 +89,11 @@ impeller_component("aiks_unittests") { "//flutter/impeller/typographer/backends/stb:typographer_stb_backend", "//flutter/testing:testing_lib", ] + + if (!impeller_trace_canvas) { + sources += [ "trace_serializer.cc" ] + public_configs = [ ":impeller_canvas_recorder_config" ] + } } impeller_component("aiks_unittests_golden") { diff --git a/impeller/aiks/canvas_recorder.h b/impeller/aiks/canvas_recorder.h new file mode 100644 index 0000000000000..56601207f86ad --- /dev/null +++ b/impeller/aiks/canvas_recorder.h @@ -0,0 +1,290 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "impeller/aiks/canvas.h" + +#define FLT_CANVAS_RECORDER_OP_ARG(name) CanvasRecorderOp::name, &Canvas::name + +namespace impeller { + +/// TODO(tbd): These are very similar to `flutter::DisplayListOpType`. When +/// golden tests can be written at a higher level, migrate these to +/// flutter::DisplayListOpType. +enum CanvasRecorderOp : uint16_t { + New, + Save, + SaveLayer, + Restore, + RestoreToCount, + ResetTransform, + Transform, + Concat, + PreConcat, + Translate, + Scale2, + Scale3, + Skew, + Rotate, + DrawPath, + DrawPaint, + DrawRect, + DrawRRect, + DrawCircle, + DrawPoints, + DrawImage, + DrawImageRect, + ClipPath, + ClipRect, + ClipRRect, + DrawPicture, + DrawTextFrame, + DrawVertices, + DrawAtlas, +}; + +/// Static polymorphic replacement for impeller::Canvas that records methods +/// called on an impeller::Canvas and forwards it to a real instance. +/// TODO(https://github.com/flutter/flutter/issues/135718): Move this recorder +/// to the DisplayList level when golden tests can be written at the ui.Canvas +/// layer. +template +class CanvasRecorder { + public: +#ifndef IMPELLER_TRACE_CANVAS + // Canvas recorder should only be used when IMPELLER_TRACE_CANVAS is defined + // (never in production code). + static_assert(false); +#endif + + CanvasRecorder() : canvas_() { serializer_.Write(CanvasRecorderOp::New); } + + explicit CanvasRecorder(Rect cull_rect) : canvas_(cull_rect) { + serializer_.Write(CanvasRecorderOp::New); + } + + explicit CanvasRecorder(IRect cull_rect) : canvas_(cull_rect) { + serializer_.Write(CanvasRecorderOp::New); + } + + ~CanvasRecorder() {} + + const Serializer& GetSerializer() const { return serializer_; } + + template + ReturnType ExecuteAndSerialize(CanvasRecorderOp op, + ReturnType (Canvas::*canvasMethod)()) { + serializer_.Write(op); + return (canvas_.*canvasMethod)(); + } + + template + auto ExecuteAndSerialize(CanvasRecorderOp op, + FuncType canvasMethod, + Args&&... args) + -> decltype((std::declval().* + canvasMethod)(std::forward(args)...)) { + // Serialize each argument + (serializer_.Write(std::forward(args)), ...); + serializer_.Write(op); + return (canvas_.*canvasMethod)(std::forward(args)...); + } + + ////////////////////////////////////////////////////////////////////////////// + // Canvas Static Polymorphism //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + void Save() { + return ExecuteAndSerialize(CanvasRecorderOp::Save, &Canvas::Save); + } + + void SaveLayer( + const Paint& paint, + std::optional bounds = std::nullopt, + const std::shared_ptr& backdrop_filter = nullptr) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(SaveLayer), paint, + bounds, backdrop_filter); + } + + bool Restore() { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Restore)); + } + + size_t GetSaveCount() const { return canvas_.GetSaveCount(); } + + void RestoreToCount(size_t count) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(RestoreToCount), + count); + } + + const Matrix& GetCurrentTransformation() const { + return canvas_.GetCurrentTransformation(); + } + + const std::optional GetCurrentLocalCullingBounds() const { + return canvas_.GetCurrentLocalCullingBounds(); + } + + void ResetTransform() { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ResetTransform)); + } + + void Transform(const Matrix& xformation) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Transform), + xformation); + } + + void Concat(const Matrix& xformation) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Concat), xformation); + } + + void PreConcat(const Matrix& xformation) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(PreConcat), + xformation); + } + + void Translate(const Vector3& offset) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Translate), offset); + } + + void Scale(const Vector2& scale) { + return ExecuteAndSerialize( + CanvasRecorderOp::Scale2, + static_cast(&Canvas::Scale), scale); + } + + void Scale(const Vector3& scale) { + return ExecuteAndSerialize( + CanvasRecorderOp::Scale3, + static_cast(&Canvas::Scale), scale); + } + + void Skew(Scalar sx, Scalar sy) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Skew), sx, sy); + } + + void Rotate(Radians radians) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(Rotate), radians); + } + + void DrawPath(const Path& path, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPath), path, + paint); + } + + void DrawPaint(const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPaint), paint); + } + + void DrawRect(Rect rect, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawRect), rect, + paint); + } + + void DrawRRect(Rect rect, Scalar corner_radius, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawRRect), rect, + corner_radius, paint); + } + + void DrawCircle(Point center, Scalar radius, const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawCircle), center, + radius, paint); + } + + void DrawPoints(std::vector points, + Scalar radius, + const Paint& paint, + PointStyle point_style) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPoints), points, + radius, paint, point_style); + } + + void DrawImage(const std::shared_ptr& image, + Point offset, + const Paint& paint, + SamplerDescriptor sampler = {}) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawImage), image, + offset, paint, sampler); + } + + void DrawImageRect(const std::shared_ptr& image, + Rect source, + Rect dest, + const Paint& paint, + SamplerDescriptor sampler = {}) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawImageRect), image, + source, dest, paint, sampler); + } + + void ClipPath( + const Path& path, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ClipPath), path, + clip_op); + } + + void ClipRect( + const Rect& rect, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ClipRect), rect, + clip_op); + } + + void ClipRRect( + const Rect& rect, + Scalar corner_radius, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(ClipRRect), rect, + corner_radius, clip_op); + } + + void DrawPicture(const Picture& picture) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawPicture), + picture); + } + + void DrawTextFrame(const std::shared_ptr& text_frame, + Point position, + const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawTextFrame), + text_frame, position, paint); + } + + void DrawVertices(const std::shared_ptr& vertices, + BlendMode blend_mode, + const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawVertices), + vertices, blend_mode, paint); + } + + void DrawAtlas(const std::shared_ptr& atlas, + std::vector transforms, + std::vector texture_coordinates, + std::vector colors, + BlendMode blend_mode, + SamplerDescriptor sampler, + std::optional cull_rect, + const Paint& paint) { + return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawAtlas), // + atlas, // + transforms, // + texture_coordinates, // + colors, // + blend_mode, // + sampler, // + cull_rect, // + paint); + } + + Picture EndRecordingAsPicture() { return canvas_.EndRecordingAsPicture(); } + + private: + Canvas canvas_; + Serializer serializer_; +}; + +} // namespace impeller diff --git a/impeller/aiks/canvas_recorder_unittests.cc b/impeller/aiks/canvas_recorder_unittests.cc new file mode 100644 index 0000000000000..041fc185dac23 --- /dev/null +++ b/impeller/aiks/canvas_recorder_unittests.cc @@ -0,0 +1,236 @@ +// Copyright 2013 The Flutter 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/testing/testing.h" +#include "impeller/aiks/canvas_recorder.h" +namespace impeller { +namespace testing { + +namespace { +class Serializer { + public: + void Write(CanvasRecorderOp op) { last_op_ = op; } + + void Write(const Paint& paint) {} + + void Write(const std::optional optional_rect) {} + + void Write(const std::shared_ptr& image_filter) {} + + void Write(size_t size) {} + + void Write(const Matrix& matrix) {} + + void Write(const Vector3& vec3) {} + + void Write(const Vector2& vec2) {} + + void Write(const Radians& vec2) {} + + void Write(const Path& path) {} + + void Write(const std::vector& points) {} + + void Write(const PointStyle& point_style) {} + + void Write(const std::shared_ptr& image) {} + + void Write(const SamplerDescriptor& sampler) {} + + void Write(const Entity::ClipOperation& clip_op) {} + + void Write(const Picture& clip_op) {} + + void Write(const std::shared_ptr& text_frame) {} + + void Write(const std::shared_ptr& vertices) {} + + void Write(const BlendMode& blend_mode) {} + + void Write(const std::vector& matrices) {} + + void Write(const std::vector& matrices) {} + + void Write(const std::vector& matrices) {} + + CanvasRecorderOp last_op_; +}; +} // namespace + +TEST(CanvasRecorder, Save) { + CanvasRecorder recorder; + recorder.Save(); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Save); +} + +TEST(CanvasRecorder, SaveLayer) { + CanvasRecorder recorder; + Paint paint; + recorder.SaveLayer(paint); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::SaveLayer); +} + +TEST(CanvasRecorder, Restore) { + CanvasRecorder recorder; + recorder.Restore(); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Restore); +} + +TEST(CanvasRecorder, RestoreToCount) { + CanvasRecorder recorder; + recorder.Save(); + recorder.RestoreToCount(0); + ASSERT_EQ(recorder.GetSerializer().last_op_, + CanvasRecorderOp::RestoreToCount); +} + +TEST(CanvasRecorder, ResetTransform) { + CanvasRecorder recorder; + recorder.ResetTransform(); + ASSERT_EQ(recorder.GetSerializer().last_op_, + CanvasRecorderOp::ResetTransform); +} + +TEST(CanvasRecorder, Transform) { + CanvasRecorder recorder; + recorder.Transform(Matrix()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Transform); +} + +TEST(CanvasRecorder, Concat) { + CanvasRecorder recorder; + recorder.Concat(Matrix()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Concat); +} + +TEST(CanvasRecorder, PreConcat) { + CanvasRecorder recorder; + recorder.PreConcat(Matrix()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::PreConcat); +} + +TEST(CanvasRecorder, Translate) { + CanvasRecorder recorder; + recorder.Translate(Vector3()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Translate); +} + +TEST(CanvasRecorder, Scale2) { + CanvasRecorder recorder; + recorder.Scale(Vector2()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Scale2); +} + +TEST(CanvasRecorder, Scale3) { + CanvasRecorder recorder; + recorder.Scale(Vector3()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Scale3); +} + +TEST(CanvasRecorder, Skew) { + CanvasRecorder recorder; + recorder.Skew(0, 0); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Skew); +} + +TEST(CanvasRecorder, Rotate) { + CanvasRecorder recorder; + recorder.Rotate(Radians(0)); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::Rotate); +} + +TEST(CanvasRecorder, DrawPath) { + CanvasRecorder recorder; + recorder.DrawPath(Path(), Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPath); +} + +TEST(CanvasRecorder, DrawPaint) { + CanvasRecorder recorder; + recorder.DrawPaint(Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPaint); +} + +TEST(CanvasRecorder, DrawRect) { + CanvasRecorder recorder; + recorder.DrawRect(Rect(), Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawRect); +} + +TEST(CanvasRecorder, DrawRRect) { + CanvasRecorder recorder; + recorder.DrawRRect(Rect(), 0, Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawRRect); +} + +TEST(CanvasRecorder, DrawCircle) { + CanvasRecorder recorder; + recorder.DrawCircle(Point(), 0, Paint()); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawCircle); +} + +TEST(CanvasRecorder, DrawPoints) { + CanvasRecorder recorder; + recorder.DrawPoints(std::vector{}, 0, Paint(), PointStyle::kRound); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPoints); +} + +TEST(CanvasRecorder, DrawImage) { + CanvasRecorder recorder; + recorder.DrawImage({}, {}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawImage); +} + +TEST(CanvasRecorder, DrawImageRect) { + CanvasRecorder recorder; + recorder.DrawImageRect({}, {}, {}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawImageRect); +} + +TEST(CanvasRecorder, ClipPath) { + CanvasRecorder recorder; + recorder.ClipPath({}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::ClipPath); +} + +TEST(CanvasRecorder, ClipRect) { + CanvasRecorder recorder; + recorder.ClipRect({}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::ClipRect); +} + +TEST(CanvasRecorder, ClipRRect) { + CanvasRecorder recorder; + recorder.ClipRRect({}, 0); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::ClipRRect); +} + +TEST(CanvasRecorder, DrawPicture) { + CanvasRecorder recorder; + recorder.DrawPicture({}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawPicture); +} + +TEST(CanvasRecorder, DrawTextFrame) { + CanvasRecorder recorder; + recorder.DrawTextFrame({}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawTextFrame); +} + +TEST(CanvasRecorder, DrawVertices) { + CanvasRecorder recorder; + auto geometry = std::shared_ptr( + new VerticesGeometry({}, {}, {}, {}, {}, {})); + recorder.DrawVertices(geometry, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawVertices); +} + +TEST(CanvasRecorder, DrawAtlas) { + CanvasRecorder recorder; + recorder.DrawAtlas({}, {}, {}, {}, {}, {}, {}, {}); + ASSERT_EQ(recorder.GetSerializer().last_op_, CanvasRecorderOp::DrawAtlas); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/aiks/canvas_type.h b/impeller/aiks/canvas_type.h new file mode 100644 index 0000000000000..a6992163a77ba --- /dev/null +++ b/impeller/aiks/canvas_type.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/aiks/canvas.h" +#include "impeller/aiks/canvas_recorder.h" +#include "impeller/aiks/trace_serializer.h" + +namespace impeller { + +/// CanvasType defines what is the concrete type of the Canvas to be used. When +/// the recorder is enabled it will be swapped out in place of the Canvas at +/// compile-time. +#ifdef IMPELLER_TRACE_CANVAS +using CanvasType = CanvasRecorder; +#else +using CanvasType = Canvas; +#endif + +} // namespace impeller diff --git a/impeller/aiks/image_filter.h b/impeller/aiks/image_filter.h index 0c5bdd3889717..e3e6908df515c 100644 --- a/impeller/aiks/image_filter.h +++ b/impeller/aiks/image_filter.h @@ -15,6 +15,25 @@ namespace impeller { struct Paint; +class LocalMatrixImageFilter; +class BlurImageFilter; +class DilateImageFilter; +class ErodeImageFilter; +class MatrixImageFilter; +class ComposeImageFilter; +class ColorImageFilter; + +class ImageFilterVisitor { + public: + virtual void Visit(const BlurImageFilter& filter) = 0; + virtual void Visit(const LocalMatrixImageFilter& filter) = 0; + virtual void Visit(const DilateImageFilter& filter) = 0; + virtual void Visit(const ErodeImageFilter& filter) = 0; + virtual void Visit(const MatrixImageFilter& filter) = 0; + virtual void Visit(const ComposeImageFilter& filter) = 0; + virtual void Visit(const ColorImageFilter& filter) = 0; +}; + /******************************************************************************* ******* ImageFilter ******************************************************************************/ @@ -65,6 +84,8 @@ class ImageFilter { const FilterInput::Ref& input) const = 0; virtual std::shared_ptr Clone() const = 0; + + virtual void Visit(ImageFilterVisitor& visitor) = 0; }; /******************************************************************************* @@ -87,6 +108,9 @@ class BlurImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Sigma sigma_x_; Sigma sigma_y_; @@ -111,6 +135,9 @@ class DilateImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Radius radius_x_; Radius radius_y_; @@ -133,6 +160,9 @@ class ErodeImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Radius radius_x_; Radius radius_y_; @@ -155,6 +185,11 @@ class MatrixImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + + const Matrix& GetMatrix() const { return matrix_; } + private: Matrix matrix_; SamplerDescriptor sampler_descriptor_; @@ -177,6 +212,9 @@ class ComposeImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: std::shared_ptr inner_; std::shared_ptr outer_; @@ -199,6 +237,9 @@ class ColorImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: std::shared_ptr color_filter_; }; @@ -221,6 +262,9 @@ class LocalMatrixImageFilter : public ImageFilter { // |ImageFilter| std::shared_ptr Clone() const override; + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + private: Matrix matrix_; std::shared_ptr internal_filter_; diff --git a/impeller/aiks/trace_serializer.cc b/impeller/aiks/trace_serializer.cc new file mode 100644 index 0000000000000..e3f87c8eea3dd --- /dev/null +++ b/impeller/aiks/trace_serializer.cc @@ -0,0 +1,226 @@ +// Copyright 2013 The Flutter 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 "impeller/aiks/trace_serializer.h" +#include "flutter/fml/logging.h" + +namespace impeller { + +namespace { + +class ImageFilterTraceVisitor : public ImageFilterVisitor { + public: + explicit ImageFilterTraceVisitor(std::ostream& os) : os_(os) {} + void Visit(const BlurImageFilter& filter) override { + os_ << "BlurImageFilter"; + } + void Visit(const LocalMatrixImageFilter& filter) override { + os_ << "LocalMatrixImageFilter"; + } + void Visit(const DilateImageFilter& filter) override { + os_ << "DilateImageFilter"; + } + void Visit(const ErodeImageFilter& filter) override { + os_ << "ErodeImageFilter"; + } + void Visit(const MatrixImageFilter& filter) override { + os_ << "{MatrixImageFilter matrix: " << filter.GetMatrix() << "}"; + } + void Visit(const ComposeImageFilter& filter) override { + os_ << "ComposeImageFilter"; + } + void Visit(const ColorImageFilter& filter) override { + os_ << "ColorImageFilter"; + } + + private: + std::ostream& os_; +}; + +std::ostream& operator<<(std::ostream& os, + const std::shared_ptr& image_filter) { + if (image_filter) { + os << "["; + ImageFilterTraceVisitor visitor(os); + image_filter->Visit(visitor); + os << "]"; + } else { + os << "[None]"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const Paint& paint) { + os << "{" << std::endl; + os << " color: [" << paint.color << "]" << std::endl; + os << " color_source:" + << "[ColorSource]" << std::endl; + os << " dither: [" << paint.dither << "]" << std::endl; + os << " stroke_width: [" << paint.stroke_width << "]" << std::endl; + os << " stroke_cap: " + << "[Paint::Cap]" << std::endl; + os << " stroke_join: " + << "[Paint::Join]" << std::endl; + os << " stroke_miter: [" << paint.stroke_miter << "]" << std::endl; + os << " style:" + << "[Paint::Style]" << std::endl; + os << " blend_mode: [" << BlendModeToString(paint.blend_mode) << "]" + << std::endl; + os << " invert_colors: [" << paint.invert_colors << "]" << std::endl; + os << " image_filter: " << paint.image_filter << std::endl; + os << " color_filter: " << paint.color_filter << std::endl; + os << " mask_blur_descriptor: " + << "[std::optional]" << std::endl; + os << "}"; + return os; +} +} // namespace + +#define FLT_CANVAS_RECORDER_OP_TO_STRING(name) \ + case CanvasRecorderOp::name: \ + return #name + +namespace { +std::string_view CanvasRecorderOpToString(CanvasRecorderOp op) { + switch (op) { + FLT_CANVAS_RECORDER_OP_TO_STRING(New); + FLT_CANVAS_RECORDER_OP_TO_STRING(Save); + FLT_CANVAS_RECORDER_OP_TO_STRING(SaveLayer); + FLT_CANVAS_RECORDER_OP_TO_STRING(Restore); + FLT_CANVAS_RECORDER_OP_TO_STRING(RestoreToCount); + FLT_CANVAS_RECORDER_OP_TO_STRING(ResetTransform); + FLT_CANVAS_RECORDER_OP_TO_STRING(Transform); + FLT_CANVAS_RECORDER_OP_TO_STRING(Concat); + FLT_CANVAS_RECORDER_OP_TO_STRING(PreConcat); + FLT_CANVAS_RECORDER_OP_TO_STRING(Translate); + FLT_CANVAS_RECORDER_OP_TO_STRING(Scale2); + FLT_CANVAS_RECORDER_OP_TO_STRING(Scale3); + FLT_CANVAS_RECORDER_OP_TO_STRING(Skew); + FLT_CANVAS_RECORDER_OP_TO_STRING(Rotate); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPath); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPaint); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawRRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawCircle); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPoints); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawImage); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawImageRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(ClipPath); + FLT_CANVAS_RECORDER_OP_TO_STRING(ClipRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(ClipRRect); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawPicture); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawTextFrame); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawVertices); + FLT_CANVAS_RECORDER_OP_TO_STRING(DrawAtlas); + } +} +} // namespace + +TraceSerializer::TraceSerializer() {} + +void TraceSerializer::Write(CanvasRecorderOp op) { + if (op == CanvasRecorderOp::New) { + FML_LOG(ERROR) << "######################################################"; + } else { + FML_LOG(ERROR) << CanvasRecorderOpToString(op) << ":" << buffer_.str(); + buffer_.str(""); + buffer_.clear(); + } +} + +void TraceSerializer::Write(const Paint& paint) { + buffer_ << "[" << paint << "] "; +} + +void TraceSerializer::Write(const std::optional optional_rect) { + if (optional_rect.has_value()) { + buffer_ << "[" << optional_rect.value() << "] "; + } else { + buffer_ << "[None] "; + } +} + +void TraceSerializer::Write(const std::shared_ptr& image_filter) { + buffer_ << image_filter << " "; +} + +void TraceSerializer::Write(size_t size) { + buffer_ << "[" << size << "] "; +} + +void TraceSerializer::Write(const Matrix& matrix) { + buffer_ << "[" << matrix << "] "; +} + +void TraceSerializer::Write(const Vector3& vec3) { + buffer_ << "[" << vec3 << "] "; +} + +void TraceSerializer::Write(const Vector2& vec2) { + buffer_ << "[" << vec2 << "] "; +} + +void TraceSerializer::Write(const Radians& radians) { + buffer_ << "[" << radians.radians << "] "; +} + +void TraceSerializer::Write(const Path& path) { + buffer_ << "[Path] "; +} + +void TraceSerializer::Write(const std::vector& points) { + buffer_ << "[std::vector] "; +} + +void TraceSerializer::Write(const PointStyle& point_style) { + buffer_ << "[PointStyle] "; +} + +void TraceSerializer::Write(const std::shared_ptr& image) { + buffer_ << "[std::shared_ptr] "; +} + +void TraceSerializer::Write(const SamplerDescriptor& sampler) { + buffer_ << "[SamplerDescriptor] "; +} + +void TraceSerializer::Write(const Entity::ClipOperation& clip_op) { + switch (clip_op) { + case Entity::ClipOperation::kDifference: + buffer_ << "[kDifference] "; + break; + case Entity::ClipOperation::kIntersect: + buffer_ << "[kIntersect] "; + break; + } +} + +void TraceSerializer::Write(const Picture& clip_op) { + buffer_ << "[Picture] "; +} + +void TraceSerializer::Write(const std::shared_ptr& text_frame) { + buffer_ << "[std::shared_ptr] "; +} + +void TraceSerializer::Write(const std::shared_ptr& vertices) { + buffer_ << "[std::shared_ptr] "; +} + +void TraceSerializer::Write(const BlendMode& blend_mode) { + buffer_ << "[" << BlendModeToString(blend_mode) << "] "; +} + +void TraceSerializer::Write(const std::vector& matrices) { + buffer_ << "[std::vector] "; +} + +void TraceSerializer::Write(const std::vector& matrices) { + buffer_ << "[std::vector] "; +} + +void TraceSerializer::Write(const std::vector& matrices) { + buffer_ << "[std::vector] "; +} +} // namespace impeller diff --git a/impeller/aiks/trace_serializer.h b/impeller/aiks/trace_serializer.h new file mode 100644 index 0000000000000..9232de504a375 --- /dev/null +++ b/impeller/aiks/trace_serializer.h @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include "impeller/aiks/canvas_recorder.h" + +namespace impeller { + +class TraceSerializer { + public: + TraceSerializer(); + + void Write(CanvasRecorderOp op); + + void Write(const Paint& paint); + + void Write(const std::optional optional_rect); + + void Write(const std::shared_ptr& image_filter); + + void Write(size_t size); + + void Write(const Matrix& matrix); + + void Write(const Vector3& vec3); + + void Write(const Vector2& vec2); + + void Write(const Radians& vec2); + + void Write(const Path& path); + + void Write(const std::vector& points); + + void Write(const PointStyle& point_style); + + void Write(const std::shared_ptr& image); + + void Write(const SamplerDescriptor& sampler); + + void Write(const Entity::ClipOperation& clip_op); + + void Write(const Picture& clip_op); + + void Write(const std::shared_ptr& text_frame); + + void Write(const std::shared_ptr& vertices); + + void Write(const BlendMode& blend_mode); + + void Write(const std::vector& matrices); + + void Write(const std::vector& matrices); + + void Write(const std::vector& matrices); + + private: + std::stringstream buffer_; +}; + +} // namespace impeller diff --git a/impeller/aiks/trace_serializer_unittests.cc b/impeller/aiks/trace_serializer_unittests.cc new file mode 100644 index 0000000000000..8bfaa47629bec --- /dev/null +++ b/impeller/aiks/trace_serializer_unittests.cc @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter 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 +#include "flutter/testing/testing.h" +#include "impeller/aiks/trace_serializer.h" +namespace impeller { +namespace testing { + +TEST(TraceSerializer, Save) { + CanvasRecorder recorder; + std::ostringstream ss; + fml::LogMessage::CaptureNextLog(&ss); + recorder.Save(); + ASSERT_TRUE(ss.str().size() > 0); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index 0dc3b7ce72461..8f0136a020917 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -6,7 +6,7 @@ #include "flutter/display_list/dl_op_receiver.h" #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas.h" +#include "impeller/aiks/canvas_type.h" #include "impeller/aiks/paint.h" namespace impeller { @@ -226,7 +226,7 @@ class DlDispatcher final : public flutter::DlOpReceiver { private: Paint paint_; - Canvas canvas_; + CanvasType canvas_; Matrix initial_matrix_; FML_DISALLOW_COPY_AND_ASSIGN(DlDispatcher); diff --git a/impeller/display_list/nine_patch_converter.cc b/impeller/display_list/nine_patch_converter.cc index 4a4536999ac6e..21f3097937541 100644 --- a/impeller/display_list/nine_patch_converter.cc +++ b/impeller/display_list/nine_patch_converter.cc @@ -61,7 +61,7 @@ void NinePatchConverter::DrawNinePatch(const std::shared_ptr& image, Rect center, Rect dst, const SamplerDescriptor& sampler, - Canvas* canvas, + CanvasType* canvas, Paint* paint) { if (dst.IsEmpty()) { return; diff --git a/impeller/display_list/nine_patch_converter.h b/impeller/display_list/nine_patch_converter.h index 422a8943a0bd6..45caf719df677 100644 --- a/impeller/display_list/nine_patch_converter.h +++ b/impeller/display_list/nine_patch_converter.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas.h" +#include "impeller/aiks/canvas_type.h" #include "impeller/aiks/image.h" #include "impeller/aiks/paint.h" #include "impeller/core/sampler_descriptor.h" @@ -26,7 +26,7 @@ class NinePatchConverter { Rect center, Rect dst, const SamplerDescriptor& sampler, - Canvas* canvas, + CanvasType* canvas, Paint* paint); private: diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index f2d33b0c28e4a..65b1f56254e28 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -44,6 +44,9 @@ declare_args() { # Enable experimental 3D scene rendering. impeller_enable_3d = false + + # Enable to get trace statements for canvas usage. + impeller_trace_canvas = false } declare_args() { diff --git a/tools/gn b/tools/gn index 5b481d6f33129..196219d35d611 100755 --- a/tools/gn +++ b/tools/gn @@ -672,6 +672,9 @@ def to_gn_args(args): if args.enable_impeller_3d: gn_args['impeller_enable_3d'] = True + if args.enable_impeller_trace_canvas: + gn_args['impeller_trace_canvas'] = True + if args.enable_impeller_vulkan: gn_args['impeller_enable_vulkan'] = True @@ -1223,6 +1226,12 @@ def parse_args(args): help='Enables experimental 3d support.' ) + parser.add_argument( + '--enable-impeller-trace-canvas', + default=False, + action='store_true', + help='Enables tracing calls to Canvas.' + ) parser.add_argument( '--malioc-path', type=str, help='The path to the malioc tool.' )