diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 02e8ce98e055c..d705ac98a1d99 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1703,7 +1703,8 @@ ORIGIN: ../../../flutter/lib/ui/dart_runtime_hooks.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/dart_ui.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/dart_ui.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/dart_wrapper.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/ui/experiments/compositing_3d.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/experiments/scene.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/experiments/setup_hooks.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/experiments/ui.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/geometry.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/hash_codes.dart + ../../../flutter/LICENSE @@ -1783,6 +1784,10 @@ ORIGIN: ../../../flutter/lib/ui/painting/picture_recorder.cc + ../../../flutter/ ORIGIN: ../../../flutter/lib/ui/painting/picture_recorder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/painting/rrect.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/painting/rrect.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/painting/scene/scene_node.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/painting/scene/scene_node.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/painting/scene/scene_shader.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/painting/scene/scene_shader.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/painting/shader.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/painting/shader.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/painting/single_frame_codec.cc + ../../../flutter/LICENSE @@ -1805,6 +1810,7 @@ ORIGIN: ../../../flutter/lib/ui/semantics/semantics_update_builder.cc + ../../.. ORIGIN: ../../../flutter/lib/ui/semantics/semantics_update_builder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/semantics/string_attribute.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/semantics/string_attribute.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/setup_hooks.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/snapshot_delegate.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/text.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/text/asset_manager_font_provider.cc + ../../../flutter/LICENSE @@ -4175,7 +4181,8 @@ FILE: ../../../flutter/lib/ui/dart_runtime_hooks.h FILE: ../../../flutter/lib/ui/dart_ui.cc FILE: ../../../flutter/lib/ui/dart_ui.h FILE: ../../../flutter/lib/ui/dart_wrapper.h -FILE: ../../../flutter/lib/ui/experiments/compositing_3d.dart +FILE: ../../../flutter/lib/ui/experiments/scene.dart +FILE: ../../../flutter/lib/ui/experiments/setup_hooks.dart FILE: ../../../flutter/lib/ui/experiments/ui.dart FILE: ../../../flutter/lib/ui/geometry.dart FILE: ../../../flutter/lib/ui/hash_codes.dart @@ -4255,6 +4262,10 @@ FILE: ../../../flutter/lib/ui/painting/picture_recorder.cc FILE: ../../../flutter/lib/ui/painting/picture_recorder.h FILE: ../../../flutter/lib/ui/painting/rrect.cc FILE: ../../../flutter/lib/ui/painting/rrect.h +FILE: ../../../flutter/lib/ui/painting/scene/scene_node.cc +FILE: ../../../flutter/lib/ui/painting/scene/scene_node.h +FILE: ../../../flutter/lib/ui/painting/scene/scene_shader.cc +FILE: ../../../flutter/lib/ui/painting/scene/scene_shader.h FILE: ../../../flutter/lib/ui/painting/shader.cc FILE: ../../../flutter/lib/ui/painting/shader.h FILE: ../../../flutter/lib/ui/painting/single_frame_codec.cc @@ -4277,6 +4288,7 @@ FILE: ../../../flutter/lib/ui/semantics/semantics_update_builder.cc FILE: ../../../flutter/lib/ui/semantics/semantics_update_builder.h FILE: ../../../flutter/lib/ui/semantics/string_attribute.cc FILE: ../../../flutter/lib/ui/semantics/string_attribute.h +FILE: ../../../flutter/lib/ui/setup_hooks.dart FILE: ../../../flutter/lib/ui/snapshot_delegate.h FILE: ../../../flutter/lib/ui/text.dart FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.cc diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 2c9863a6cf907..32756ec2597df 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -71,6 +71,14 @@ source_set("display_list") { "//flutter/impeller/runtime_stage", "//third_party/skia", ] + + if (!defined(defines)) { + defines = [] + } + if (impeller_enable_3d) { + defines += [ "IMPELLER_ENABLE_3D" ] + public_deps += [ "//flutter/impeller/scene" ] + } } if (enable_unittests) { diff --git a/display_list/display_list.cc b/display_list/display_list.cc index e343ed365e604..751e16158ae58 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -184,6 +184,9 @@ void DisplayList::Dispatch(Dispatcher& dispatcher, break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) +#ifdef IMPELLER_ENABLE_3D + DL_OP_DISPATCH(SetSceneColorSource) +#endif // IMPELLER_ENABLE_3D #undef DL_OP_DISPATCH @@ -209,6 +212,9 @@ void DisplayList::DisposeOps(uint8_t* ptr, uint8_t* end) { break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) +#ifdef IMPELLER_ENABLE_3D + DL_OP_DISPOSE(SetSceneColorSource) +#endif // IMPELLER_ENABLE_3D #undef DL_OP_DISPOSE @@ -247,6 +253,9 @@ static bool CompareOps(uint8_t* ptrA, break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) +#ifdef IMPELLER_ENABLE_3D + DL_OP_EQUALS(SetSceneColorSource) +#endif // IMPELLER_ENABLE_3D #undef DL_OP_EQUALS diff --git a/display_list/display_list.h b/display_list/display_list.h index 0fa5636f372cd..1ee8645960737 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -159,7 +159,12 @@ namespace flutter { V(DrawShadowTransparentOccluder) #define DL_OP_TO_ENUM_VALUE(name) k##name, -enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; +enum class DisplayListOpType { + FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) +#ifdef IMPELLER_ENABLE_3D + DL_OP_TO_ENUM_VALUE(SetSceneColorSource) +#endif // IMPELLER_ENABLE_3D +}; #undef DL_OP_TO_ENUM_VALUE class Dispatcher; diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index b5b1c0e2b9e0e..b63d2e355bbec 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -9,6 +9,7 @@ #include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/display_list/display_list_color_source.h" #include "flutter/display_list/display_list_ops.h" +#include "fml/logging.h" namespace flutter { @@ -202,6 +203,14 @@ void DisplayListBuilder::onSetColorSource(const DlColorSource* source) { Push(0, 0, effect); break; } +#ifdef IMPELLER_ENABLE_3D + case DlColorSourceType::kScene: { + const DlSceneColorSource* scene = source->asScene(); + FML_DCHECK(scene); + Push(0, 0, scene); + break; + } +#endif // IMPELLER_ENABLE_3D case DlColorSourceType::kUnknown: Push(0, 0, source->skia_object()); break; diff --git a/display_list/display_list_color_source.h b/display_list/display_list_color_source.h index 1e5f545554a72..361e7b2817b01 100644 --- a/display_list/display_list_color_source.h +++ b/display_list/display_list_color_source.h @@ -22,6 +22,14 @@ #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkRuntimeEffect.h" +#ifdef IMPELLER_ENABLE_3D +#include "impeller/geometry/matrix.h" // nogncheck +#include "impeller/scene/node.h" // nogncheck +namespace flutter { +class DlSceneColorSource; +} +#endif // IMPELLER_ENABLE_3D + namespace flutter { class DlColorColorSource; @@ -51,6 +59,9 @@ enum class DlColorSourceType { kConicalGradient, kSweepGradient, kRuntimeEffect, +#ifdef IMPELLER_ENABLE_3D + kScene, +#endif // IMPELLER_ENABLE_3D kUnknown }; @@ -160,6 +171,10 @@ class DlColorSource return nullptr; } +#ifdef IMPELLER_ENABLE_3D + virtual const DlSceneColorSource* asScene() const { return nullptr; } +#endif // IMPELLER_ENABLE_3D + // If this filter contains images, specifies the owning context for those // images. // Images with a DlImage::OwningContext::kRaster must only call skia_object @@ -754,6 +769,48 @@ class DlRuntimeEffectColorSource final : public DlColorSource { FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlRuntimeEffectColorSource); }; +#ifdef IMPELLER_ENABLE_3D +class DlSceneColorSource final : public DlColorSource { + public: + DlSceneColorSource(std::shared_ptr node, + impeller::Matrix camera_matrix) + : node_(std::move(node)), camera_matrix_(camera_matrix) {} + + const DlSceneColorSource* asScene() const override { return this; } + + std::shared_ptr shared() const override { + return std::make_shared(node_, camera_matrix_); + } + + DlColorSourceType type() const override { return DlColorSourceType::kScene; } + size_t size() const override { return sizeof(*this); } + + bool is_opaque() const override { return false; } + + std::shared_ptr scene_node() const { return node_; } + + impeller::Matrix camera_matrix() const { return camera_matrix_; } + + sk_sp skia_object() const override { return nullptr; } + + protected: + bool equals_(DlColorSource const& other) const override { + FML_DCHECK(other.type() == DlColorSourceType::kScene); + auto that = static_cast(&other); + if (node_ != that->node_) { + return false; + } + return true; + } + + private: + std::shared_ptr node_; + impeller::Matrix camera_matrix_; // the view-projection matrix of the scene. + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlSceneColorSource); +}; +#endif // IMPELLER_ENABLE_3D + class DlUnknownColorSource final : public DlColorSource { public: DlUnknownColorSource(sk_sp shader) diff --git a/display_list/display_list_ops.h b/display_list/display_list_ops.h index 40775cdae7066..d4f42f4906b5d 100644 --- a/display_list/display_list_ops.h +++ b/display_list/display_list_ops.h @@ -312,6 +312,26 @@ struct SetRuntimeEffectColorSourceOp : DLOp { } }; +#ifdef IMPELLER_ENABLE_3D +struct SetSceneColorSourceOp : DLOp { + static const auto kType = DisplayListOpType::kSetSceneColorSource; + + SetSceneColorSourceOp(const DlSceneColorSource* source) + : source(source->scene_node(), source->camera_matrix()) {} + + const DlSceneColorSource source; + + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setColorSource(&source); + } + + DisplayListCompare equals(const SetSceneColorSourceOp* other) const { + return (source == other->source) ? DisplayListCompare::kEqual + : DisplayListCompare::kNotEqual; + } +}; +#endif // IMPELLER_ENABLE_3D + // 4 byte header + 16 byte payload uses 24 total bytes (4 bytes unused) struct SetSharedImageFilterOp : DLOp { static const auto kType = DisplayListOpType::kSetSharedImageFilter; diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index 48cf0f1619f38..7b647a80929a0 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -22,6 +22,13 @@ impeller_component("display_list") { "//flutter/fml", "//third_party/skia", ] + + if (!defined(defines)) { + defines = [] + } + if (impeller_enable_3d) { + defines += [ "IMPELLER_ENABLE_3D" ] + } } impeller_component("display_list_unittests") { @@ -37,4 +44,11 @@ impeller_component("display_list_unittests") { ":display_list", "../playground:playground_test", ] + + if (!defined(defines)) { + defines = [] + } + if (impeller_enable_3d) { + defines += [ "IMPELLER_ENABLE_3D" ] + } } diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 538eff84f1b80..0f9b0de500f37 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -337,6 +337,10 @@ static std::optional ToColorSourceType( return Paint::ColorSourceType::kSweepGradient; case flutter::DlColorSourceType::kRuntimeEffect: return Paint::ColorSourceType::kRuntimeEffect; +#ifdef IMPELLER_ENABLE_3D + case flutter::DlColorSourceType::kScene: + return Paint::ColorSourceType::kScene; +#endif // IMPELLER_ENABLE_3D case flutter::DlColorSourceType::kUnknown: return std::nullopt; } @@ -502,19 +506,24 @@ void DisplayListDispatcher::setColorSource( return; } case Paint::ColorSourceType::kScene: { - // const flutter::DlSceneColorSource* scene_color_source = - // source->asScene(); std::shared_ptr scene_node = - // scene_color_source->node(); Matrix camera_transform = - // scene_color_node->camera_transform(); +#ifdef IMPELLER_ENABLE_3D + const flutter::DlSceneColorSource* scene_color_source = source->asScene(); + std::shared_ptr scene_node = + scene_color_source->scene_node(); + Matrix camera_transform = scene_color_source->camera_matrix(); - paint_.color_source = [/*scene_node, camera_transform*/]() { + paint_.color_source = [scene_node, camera_transform]() { auto contents = std::make_shared(); - // contents->SetNode(scene_node); - // contents->SetCameraTransform(camera_transform); + contents->SetNode(scene_node); + contents->SetCameraTransform(camera_transform); return contents; }; - } +#else // IMPELLER_ENABLE_3D + FML_LOG(ERROR) << "ColorSourceType::kScene can only be used if Impeller " + "Scene is enabled."; +#endif // IMPELLER_ENABLE_3D return; + } case Paint::ColorSourceType::kConicalGradient: UNIMPLEMENTED; break; diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index f2b5ac2bb5889..3ce405483022a 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -23,6 +23,7 @@ #include "impeller/geometry/constants.h" #include "impeller/geometry/point.h" #include "impeller/playground/widgets.h" +#include "impeller/scene/node.h" #include "third_party/imgui/imgui.h" #include "third_party/skia/include/core/SkBlurTypes.h" #include "third_party/skia/include/core/SkClipOp.h" @@ -1158,5 +1159,32 @@ TEST_P(DisplayListTest, DrawShapes) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +#ifdef IMPELLER_ENABLE_3D +TEST_P(DisplayListTest, SceneColorSource) { + // Load up the scene. + auto mapping = + flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene"); + ASSERT_NE(mapping, nullptr); + + std::shared_ptr gltf_scene = + impeller::scene::Node::MakeFromFlatbuffer( + *mapping, *GetContext()->GetResourceAllocator()); + ASSERT_NE(gltf_scene, nullptr); + + flutter::DisplayListBuilder builder; + + auto color_source = std::make_shared( + gltf_scene, + Matrix::MakePerspective(Degrees(45), GetWindowSize(), 0.1, 1000) * + Matrix::MakeLookAt({3, 2, -5}, {0, 0, 0}, {0, 1, 0})); + + flutter::DlPaint paint = flutter::DlPaint().setColorSource(color_source); + + builder.drawPaint(paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} +#endif + } // namespace testing } // namespace impeller diff --git a/impeller/entity/contents/scene_contents.cc b/impeller/entity/contents/scene_contents.cc index 3a1c0d9264fba..3cc574faf2973 100644 --- a/impeller/entity/contents/scene_contents.cc +++ b/impeller/entity/contents/scene_contents.cc @@ -71,6 +71,8 @@ bool SceneContents::Render(const ContentContext& renderer, TiledTextureContents contents; contents.SetGeometry(GetGeometry()); contents.SetTexture(subpass_target.GetRenderTargetTexture()); + contents.SetMatrix( + Matrix::MakeScale(1 / entity.GetTransformation().GetScale())); return contents.Render(renderer, entity, pass); } diff --git a/impeller/scene/node.cc b/impeller/scene/node.cc index dcc9b2c213278..7343adb8fa98e 100644 --- a/impeller/scene/node.cc +++ b/impeller/scene/node.cc @@ -287,12 +287,19 @@ Matrix Node::GetGlobalTransform() const { } bool Node::AddChild(std::shared_ptr node) { - // This ensures that cycles are impossible. - if (node->parent_ != nullptr) { - VALIDATION_LOG - << "Cannot add a node as a child which already has a parent."; - return false; - } + // TODO(bdero): Figure out a better paradigm/rules for nodes with multiple + // parents. We should probably disallow this, make deep + // copying of nodes cheap and easy, add mesh instancing, etc. + // Today, the parent link is only used for skin posing, and so + // it's reasonable to not have a check and allow multi-parenting. + // Even still, there should still be some kind of cycle + // prevention/detection, ideally at the protocol level. + // + // if (node->parent_ != nullptr) { + // VALIDATION_LOG + // << "Cannot add a node as a child which already has a parent."; + // return false; + // } node->parent_ = this; children_.push_back(std::move(node)); diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index d1fb6eb3ee7b9..e2c35817d64df 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -36,7 +36,7 @@ declare_args() { # Call glGetError after each OpenGL call and log failures. impeller_error_check_all_gl_calls = is_debug - # Eperimentally enable 3d code paths. + # Enable experimental 3D scene rendering. impeller_enable_3d = false } diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 9048897a15ff1..adaec644f017b 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -191,6 +191,14 @@ source_set("ui") { } if (impeller_enable_3d) { defines += [ "IMPELLER_ENABLE_3D" ] + sources += [ + "painting/scene/scene_node.cc", + "painting/scene/scene_node.h", + "painting/scene/scene_shader.cc", + "painting/scene/scene_shader.h", + ] + + deps += [ "//flutter/impeller/scene" ] } } diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index deba7695f4fb7..6f1c718fa1b5f 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -262,22 +262,6 @@ void SceneBuilder::addTexture(double dx, AddLayer(std::move(layer)); } -#ifdef IMPELLER_ENABLE_3D -// static -void SceneBuilder::addModelLayer(Dart_Handle wrapper, - double dx, - double dy, - double width, - double height, - int64_t viewId) { - auto* scene_builder = tonic::DartConverter::FromDart(wrapper); - SkMatrix sk_matrix = SkMatrix::Translate(dx, dy); - auto layer = std::make_shared(sk_matrix); - scene_builder->AddLayer(std::move(layer)); -} - -#endif // IMPELLER_ENABLE_3D - void SceneBuilder::addPlatformView(double dx, double dy, double width, diff --git a/lib/ui/compositing/scene_builder.h b/lib/ui/compositing/scene_builder.h index 04d350236dabb..f2de29c4802d2 100644 --- a/lib/ui/compositing/scene_builder.h +++ b/lib/ui/compositing/scene_builder.h @@ -34,15 +34,6 @@ class SceneBuilder : public RefCountedDartWrappable { res->AssociateWithDartWrapper(wrapper); } -#ifdef IMPELLER_ENABLE_3D - static void addModelLayer(Dart_Handle wrapper, - double dx, - double dy, - double width, - double height, - int64_t viewId); -#endif // IMPELLER_ENABLE_3D - ~SceneBuilder() override; void pushTransformHandle(Dart_Handle layer_handle, diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 4d1a6bca3ac0d..24af045cec348 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -41,6 +41,11 @@ #include "third_party/tonic/dart_args.h" #include "third_party/tonic/logging/dart_error.h" +#ifdef IMPELLER_ENABLE_3D +#include "flutter/lib/ui/painting/scene/scene_node.h" +#include "flutter/lib/ui/painting/scene/scene_shader.h" +#endif // IMPELLER_ENABLE_3D + using tonic::ToDart; namespace flutter { @@ -295,7 +300,20 @@ typedef CanvasPath Path; V(Vertices, dispose, 1) #ifdef IMPELLER_ENABLE_3D -#define FFI_METHOD_LIST_3D(V) V(SceneBuilder::addModelLayer, 7) + +#define FFI_FUNCTION_LIST_3D(V) \ + V(SceneNode::Create, 1) V(SceneShader::Create, 2) + +#define FFI_METHOD_LIST_3D(V) \ + V(SceneNode, initFromAsset, 3) \ + V(SceneNode, initFromTransform, 2) \ + V(SceneNode, AddChild, 2) \ + V(SceneNode, SetTransform, 2) \ + V(SceneNode, SetAnimationState, 5) \ + V(SceneNode, SeekAnimation, 3) \ + V(SceneShader, SetCameraTransform, 2) \ + V(SceneShader, Dispose, 1) + #endif // IMPELLER_ENABLE_3D #define FFI_FUNCTION_INSERT(FUNCTION, ARGS) \ @@ -325,7 +343,8 @@ void InitDispatcherMap() { FFI_FUNCTION_LIST(FFI_FUNCTION_INSERT) FFI_METHOD_LIST(FFI_METHOD_INSERT) #ifdef IMPELLER_ENABLE_3D - FFI_METHOD_LIST_3D(FFI_FUNCTION_INSERT) + FFI_FUNCTION_LIST_3D(FFI_FUNCTION_INSERT) + FFI_METHOD_LIST_3D(FFI_METHOD_INSERT) #endif // IMPELLER_ENABLE_3D } diff --git a/lib/ui/experiments/compositing_3d.dart b/lib/ui/experiments/compositing_3d.dart deleted file mode 100644 index 3fce6a31e7844..0000000000000 --- a/lib/ui/experiments/compositing_3d.dart +++ /dev/null @@ -1,18 +0,0 @@ -// 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. -part of dart.ui; - -void addModelLayer( - SceneBuilder builder, - int viewId, { - Offset offset = Offset.zero, - double width = 0.0, - double height = 0.0, -}) { - assert(offset != null, 'Offset argument was null'); - _addModelLayer(builder, offset.dx, offset.dy, width, height, viewId); -} - -@FfiNative('SceneBuilder::addModelLayer') -external void _addModelLayer(Object object, double dx, double dy, double width, double height, int viewId); diff --git a/lib/ui/experiments/scene.dart b/lib/ui/experiments/scene.dart new file mode 100644 index 0000000000000..2b2e477a4c41e --- /dev/null +++ b/lib/ui/experiments/scene.dart @@ -0,0 +1,164 @@ +// 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. +part of dart.ui; + +/// A composable [SceneNode]. +class SceneNode extends NativeFieldWrapperClass1 { + @pragma('vm:entry-point') + SceneNode._create() { + _constructor(); + } + + String? _debugName; + + /// Creates a scene node from the asset with key [assetKey]. + /// + /// The asset must be a file produced as the output of the `scenec` importer. + /// The constructed object should then be reused via the [shader] + /// method to create [Shader] objects that can be used by [Shader.paint]. + static Future fromAsset(String assetKey) { + // The flutter tool converts all asset keys with spaces into URI + // encoded paths (replacing ' ' with '%20', for example). We perform + // the same encoding here so that users can load assets with the same + // key they have written in the pubspec. + final String encodedKey = Uri(path: Uri.encodeFull(assetKey)).path; + { + final SceneNode? sceneNode = _ipsceneRegistry[encodedKey]?.target; + if (sceneNode != null) { + return Future.value(sceneNode); + } + } + + final SceneNode sceneNode = SceneNode._create(); + return _futurize((_Callback callback) { + final String error = sceneNode._initFromAsset(assetKey, callback); + if (error.isNotEmpty) { + return error; + } + assert(() { + sceneNode._debugName = assetKey; + return true; + }()); + + _ipsceneRegistry[encodedKey] = WeakReference(sceneNode); + + return null; + }).then((_) => sceneNode); + } + + static SceneNode fromTransform(Float64List matrix4) { + final SceneNode sceneNode = SceneNode._create(); + sceneNode._initFromTransform(matrix4); + return sceneNode; + } + + void addChild(SceneNode sceneNode) { + _addChild(sceneNode); + } + + void setTransform(Float64List matrix4) { + _setTransform(matrix4); + } + + void setAnimationState(String animationName, bool playing, double weight, double timeScale) { + _setAnimationState(animationName, playing, weight, timeScale); + } + + void seekAnimation(String animationName, double time) { + _seekAnimation(animationName, time); + } + + // This is a cache of ipscene-backed scene nodes that have been loaded via + // SceneNode.fromAsset. It holds weak references to the SceneNodes so that the + // case where an in-use ipscene is requested again can be fast, but scenes + // that are no longer referenced are not retained because of the cache. + static final Map> _ipsceneRegistry = + >{}; + + static Future _reinitializeScene(String assetKey) async { + final WeakReference? sceneRef = _ipsceneRegistry == null + ? null + : _ipsceneRegistry[assetKey]; + + // If a scene for the asset isn't already registered, then there's no + // need to reinitialize it. + if (sceneRef == null) { + return; + } + + final SceneNode? sceneNode = sceneRef.target; + if (sceneNode == null) { + return; + } + + await _futurize((_Callback callback) { + final String error = sceneNode._initFromAsset(assetKey, callback); + if (error.isNotEmpty) { + return error; + } + return null; + }); + } + + @FfiNative('SceneNode::Create') + external void _constructor(); + + @FfiNative, Handle, Handle)>('SceneNode::initFromAsset') + external String _initFromAsset(String assetKey, _Callback completionCallback); + + @FfiNative, Handle)>('SceneNode::initFromTransform') + external void _initFromTransform(Float64List matrix4); + + @FfiNative, Handle)>('SceneNode::AddChild') + external void _addChild(SceneNode sceneNode); + + @FfiNative, Handle)>('SceneNode::SetTransform') + external void _setTransform(Float64List matrix4); + + @FfiNative, Handle, Handle, Handle, Handle)>('SceneScene::SetAnimationState') + external void _setAnimationState(String animationName, bool playing, double weight, double timeScale); + + @FfiNative, Handle, Handle)>('SceneNode::SeekAnimation') + external void _seekAnimation(String animationName, double time); + + /// Returns a fresh instance of [SceneShader]. + SceneShader sceneShader() => SceneShader._(this, debugName: _debugName); +} + +/// A [Shader] generated from a [SceneNode]. +/// +/// Instances of this class can be obtained from the +/// [SceneNode.sceneShader] method. +class SceneShader extends Shader { + SceneShader._(SceneNode node, { String? debugName }) : _debugName = debugName, super._() { + _constructor(node); + } + + // ignore: unused_field + final String? _debugName; + + void setCameraTransform(Float64List matrix4) { + _setCameraTransform(matrix4); + } + + /// Releases the native resources held by the [SceneShader]. + /// + /// After this method is called, calling methods on the shader, or attaching + /// it to a [Paint] object will fail with an exception. Calling [dispose] + /// twice will also result in an exception being thrown. + @override + void dispose() { + super.dispose(); + _dispose(); + } + + @FfiNative('SceneShader::Create') + external void _constructor(SceneNode node); + + @FfiNative, Handle)>('SceneShader::SetCameraTransform') + external void _setCameraTransform(Float64List matrix4); + + @FfiNative)>('SceneShader::Dispose') + external void _dispose(); +} diff --git a/lib/ui/experiments/setup_hooks.dart b/lib/ui/experiments/setup_hooks.dart new file mode 100644 index 0000000000000..8d5dfdd966da8 --- /dev/null +++ b/lib/ui/experiments/setup_hooks.dart @@ -0,0 +1,51 @@ +// 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. + +part of dart.ui; + +Future _reinitializeScene( + String method, + Map parameters, +) async { + final String? assetKey = parameters['assetKey']; + if (assetKey != null) { + await SceneNode._reinitializeScene(assetKey); + } + + // Always succeed. + return developer.ServiceExtensionResponse.result(json.encode({ + 'type': 'Success', + })); +} + +// This is a copy of ui/setup_hooks.dart, but with reinitializeScene added for hot reloading 3D scenes. + +@pragma('vm:entry-point') +void _setupHooks() { + assert(() { + // In debug mode, register the schedule frame extension. + developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); + + // In debug mode, allow shaders to be reinitialized. + developer.registerExtension( + 'ext.ui.window.reinitializeShader', + _reinitializeShader, + ); + + // In debug mode, allow 3D scenes to be reinitialized. + developer.registerExtension( + 'ext.ui.window.reinitializeScene', + _reinitializeScene, + ); + return true; + }()); + + // In debug and profile mode, allow tools to display the current rendering backend. + if (!_kReleaseMode) { + developer.registerExtension( + 'ext.ui.window.impellerEnabled', + _getImpellerEnabled, + ); + } +} diff --git a/lib/ui/experiments/ui.dart b/lib/ui/experiments/ui.dart index e6ee89e1097d2..41d3fcfece6c4 100644 --- a/lib/ui/experiments/ui.dart +++ b/lib/ui/experiments/ui.dart @@ -25,7 +25,6 @@ import 'dart:typed_data'; part '../annotations.dart'; part '../channel_buffers.dart'; part '../compositing.dart'; -part 'compositing_3d.dart'; part '../geometry.dart'; part '../hash_codes.dart'; part '../hooks.dart'; @@ -38,6 +37,8 @@ part '../painting.dart'; part '../platform_dispatcher.dart'; part '../plugins.dart'; part '../pointer.dart'; +part 'scene.dart'; part '../semantics.dart'; +part 'setup_hooks.dart'; part '../text.dart'; part '../window.dart'; diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index 49a567e5b5db6..4fb41bfb41137 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -83,29 +83,6 @@ Future _getImpellerEnabled( })); } -@pragma('vm:entry-point') -void _setupHooks() { - assert(() { - // In debug mode, register the schedule frame extension. - developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); - - // In debug mode, allow shaders to be reinitialized. - developer.registerExtension( - 'ext.ui.window.reinitializeShader', - _reinitializeShader, - ); - return true; - }()); - - // In debug and profile mode, allow tools to display the current rendering backend. - if (!_kReleaseMode) { - developer.registerExtension( - 'ext.ui.window.impellerEnabled', - _getImpellerEnabled, - ); - } -} - const bool _kReleaseMode = bool.fromEnvironment('dart.vm.product'); /// Returns runtime Dart compilation trace as a UTF-8 encoded memory buffer. diff --git a/lib/ui/painting/fragment_program.h b/lib/ui/painting/fragment_program.h index 50da9cbf9dba0..484549104395e 100644 --- a/lib/ui/painting/fragment_program.h +++ b/lib/ui/painting/fragment_program.h @@ -7,7 +7,6 @@ #include "flutter/display_list/display_list_runtime_effect.h" #include "flutter/lib/ui/dart_wrapper.h" -#include "flutter/lib/ui/painting/fragment_shader.h" #include "flutter/lib/ui/painting/shader.h" #include "third_party/tonic/dart_library_natives.h" diff --git a/lib/ui/painting/scene/scene_node.cc b/lib/ui/painting/scene/scene_node.cc new file mode 100644 index 0000000000000..cc0b2bae1f04b --- /dev/null +++ b/lib/ui/painting/scene/scene_node.cc @@ -0,0 +1,142 @@ +// 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/lib/ui/painting/scene/scene_node.h" + +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/trace_event.h" +#include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/scene/scene_shader.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/platform_configuration.h" +#include "impeller/geometry/matrix.h" +#include "impeller/scene/animation/property_resolver.h" + +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_args.h" +#include "third_party/tonic/dart_binding_macros.h" +#include "third_party/tonic/dart_library_natives.h" + +namespace flutter { + +IMPLEMENT_WRAPPERTYPEINFO(ui, SceneNode); + +void SceneNode::Create(Dart_Handle wrapper) { + auto res = fml::MakeRefCounted(); + res->AssociateWithDartWrapper(wrapper); +} + +std::string SceneNode::initFromAsset(const std::string& asset_name, + Dart_Handle completion_callback_handle) { + FML_TRACE_EVENT("flutter", "SceneNode::initFromAsset", "asset", asset_name); + + if (!Dart_IsClosure(completion_callback_handle)) { + return "Completion callback must be a function."; + } + + auto dart_state = UIDartState::Current(); + if (!dart_state->IsImpellerEnabled()) { + return "3D scenes require the Impeller rendering backend to be enabled."; + } + + std::shared_ptr asset_manager = + dart_state->platform_configuration()->client()->GetAssetManager(); + std::unique_ptr data = asset_manager->GetAsMapping(asset_name); + if (data == nullptr) { + return std::string("Asset '") + asset_name + std::string("' not found."); + } + + auto& task_runners = dart_state->GetTaskRunners(); + + std::promise> context_promise; + auto impeller_context_promise = context_promise.get_future(); + task_runners.GetIOTaskRunner()->PostTask( + fml::MakeCopyable([promise = std::move(context_promise), + io_manager = dart_state->GetIOManager()]() mutable { + promise.set_value(io_manager ? io_manager->GetImpellerContext() + : nullptr); + })); + + auto persistent_completion_callback = + std::make_unique(dart_state, + completion_callback_handle); + + auto ui_task = fml::MakeCopyable( + [this, callback = std::move(persistent_completion_callback)]( + std::shared_ptr node) mutable { + auto dart_state = callback->dart_state().lock(); + if (!dart_state) { + // The root isolate could have died in the meantime. + return; + } + tonic::DartState::Scope scope(dart_state); + + node_ = std::move(node); + tonic::DartInvoke(callback->Get(), {Dart_TypeVoid()}); + + // callback is associated with the Dart isolate and must be + // deleted on the UI thread. + callback.reset(); + }); + + task_runners.GetRasterTaskRunner()->PostTask( + fml::MakeCopyable([ui_task = std::move(ui_task), task_runners, + impeller_context = impeller_context_promise.get(), + data = std::move(data)]() { + auto node = impeller::scene::Node::MakeFromFlatbuffer( + *data, *impeller_context->GetResourceAllocator()); + + task_runners.GetUITaskRunner()->PostTask( + [ui_task, node = std::move(node)]() { ui_task(node); }); + })); + + return ""; +} + +void SceneNode::initFromTransform(const tonic::Float64List& matrix4) { + node_ = std::make_shared(); + node_->SetLocalTransform( + impeller::Matrix(matrix4[0], matrix4[1], matrix4[2], matrix4[3], // + matrix4[4], matrix4[5], matrix4[6], matrix4[7], // + matrix4[8], matrix4[9], matrix4[10], matrix4[11], // + matrix4[12], matrix4[13], matrix4[14], matrix4[15])); +} + +void SceneNode::AddChild(Dart_Handle scene_node_handle) { + if (!node_) { + return; + } + auto* scene_node = + tonic::DartConverter::FromDart(scene_node_handle); + if (!scene_node) { + return; + } + node_->AddChild(scene_node->node_); + children_.push_back(fml::Ref(scene_node)); +} + +void SceneNode::SetTransform(const tonic::Float64List& matrix4) { + // TODO(bdero): Implement mutation log. +} + +void SceneNode::SetAnimationState(const std::string& animation_name, + bool playing, + double weight, + double time_scale) { + // TODO(bdero): Implement mutation log. +} + +void SceneNode::SeekAnimation(const std::string& animation_name, double time) { + // TODO(bdero): Implement mutation log. +} + +SceneNode::SceneNode() = default; + +SceneNode::~SceneNode() = default; + +} // namespace flutter diff --git a/lib/ui/painting/scene/scene_node.h b/lib/ui/painting/scene/scene_node.h new file mode 100644 index 0000000000000..563b0f9232c55 --- /dev/null +++ b/lib/ui/painting/scene/scene_node.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef FLUTTER_LIB_UI_PAINTING_SCENE_NODE_H_ +#define FLUTTER_LIB_UI_PAINTING_SCENE_NODE_H_ + +#include +#include +#include + +#include "flutter/fml/memory/ref_ptr.h" +#include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/shader.h" + +#include "third_party/tonic/dart_library_natives.h" + +namespace flutter { + +class SceneShader; + +/// @brief A scene node, which may be a deserialized ipscene asset. This node +/// can be safely added as a child to multiple scene nodes, whether +/// they're in the same scene or a different scene. The deserialized +/// node itself is treated as immutable on the IO thread. +/// +/// Internally, nodes may have an animation player, which is controlled +/// via the mutation log in the `DlSceneColorSource`, which is built by +/// `SceneShader`. +class SceneNode : public RefCountedDartWrappable { + DEFINE_WRAPPERTYPEINFO(); + FML_FRIEND_MAKE_REF_COUNTED(SceneShader); + + public: + SceneNode(); + ~SceneNode() override; + + static void Create(Dart_Handle wrapper); + + std::string initFromAsset(const std::string& asset_name, + Dart_Handle completion_callback_handle); + + void initFromTransform(const tonic::Float64List& matrix4); + + void AddChild(Dart_Handle scene_node_handle); + + void SetTransform(const tonic::Float64List& matrix4); + + void SetAnimationState(const std::string& animation_name, + bool playing, + double weight, + double time_scale); + + void SeekAnimation(const std::string& animation_name, double time); + + fml::RefPtr node(Dart_Handle shader); + + private: + std::shared_ptr node_; + std::vector> children_; + + friend SceneShader; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_PAINTING_SCENE_NODE_H_ diff --git a/lib/ui/painting/scene/scene_shader.cc b/lib/ui/painting/scene/scene_shader.cc new file mode 100644 index 0000000000000..55e89a266a26c --- /dev/null +++ b/lib/ui/painting/scene/scene_shader.cc @@ -0,0 +1,82 @@ +// 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/lib/ui/painting/scene/scene_shader.h" + +#include +#include + +#include "flutter/display_list/display_list_color_source.h" +#include "flutter/display_list/display_list_tile_mode.h" +#include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/scene/scene_node.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/size.h" + +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_args.h" +#include "third_party/tonic/dart_binding_macros.h" +#include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/typed_data/typed_list.h" + +namespace flutter { + +IMPLEMENT_WRAPPERTYPEINFO(ui, SceneShader); + +SceneShader::SceneShader(fml::RefPtr scene_node) + : scene_node_(std::move(scene_node)) {} + +void SceneShader::Create(Dart_Handle wrapper, Dart_Handle scene_node_handle) { + auto* scene_node = + tonic::DartConverter::FromDart(scene_node_handle); + if (!scene_node) { + return; + } + + auto res = fml::MakeRefCounted(fml::Ref(scene_node)); + res->AssociateWithDartWrapper(wrapper); +} + +void SceneShader::SetCameraTransform(const tonic::Float64List& matrix4) { + camera_transform_ = + impeller::Matrix(matrix4[0], matrix4[1], matrix4[2], matrix4[3], // + matrix4[4], matrix4[5], matrix4[6], matrix4[7], // + matrix4[8], matrix4[9], matrix4[10], matrix4[11], // + matrix4[12], matrix4[13], matrix4[14], matrix4[15]); +} + +static impeller::Matrix DefaultCameraTransform() { + // TODO(bdero): There's no way to know what the draw area will be yet, so + // make the DlSceneColorSource camera transform optional and + // defer this default (or parameterize the camera instead). + return impeller::Matrix::MakePerspective( + impeller::Degrees(45), impeller::ISize{800, 600}, 0.1, 1000) * + impeller::Matrix::MakeLookAt({0, 0, -5}, {0, 0, 0}, {0, 1, 0}); +} + +std::shared_ptr SceneShader::shader(DlImageSampling sampling) { + FML_CHECK(scene_node_); + + if (!scene_node_->node_) { + return nullptr; + } + + // TODO(bdero): Gather the mutation log and include it in the scene color + // source. + + return std::make_shared(scene_node_->node_, + camera_transform_.IsIdentity() + ? DefaultCameraTransform() + : camera_transform_); +} + +void SceneShader::Dispose() { + scene_node_ = nullptr; + ClearDartWrapper(); +} + +SceneShader::~SceneShader() = default; + +} // namespace flutter diff --git a/lib/ui/painting/scene/scene_shader.h b/lib/ui/painting/scene/scene_shader.h new file mode 100644 index 0000000000000..c3d4be37447d1 --- /dev/null +++ b/lib/ui/painting/scene/scene_shader.h @@ -0,0 +1,45 @@ +// 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. + +#ifndef FLUTTER_LIB_UI_PAINTING_SCENE_SHADER_H_ +#define FLUTTER_LIB_UI_PAINTING_SCENE_SHADER_H_ + +#include +#include + +#include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/scene/scene_node.h" +#include "flutter/lib/ui/painting/shader.h" +#include "impeller/geometry/matrix.h" + +#include "third_party/tonic/dart_library_natives.h" + +namespace flutter { + +class SceneShader : public Shader { + DEFINE_WRAPPERTYPEINFO(); + FML_FRIEND_MAKE_REF_COUNTED(SceneShader); + + public: + ~SceneShader() override; + + static void Create(Dart_Handle wrapper, Dart_Handle scene_node_handle); + + void SetCameraTransform(const tonic::Float64List& matrix4); + + void Dispose(); + + // |Shader| + std::shared_ptr shader(DlImageSampling) override; + + private: + explicit SceneShader(fml::RefPtr scene_node); + + impeller::Matrix camera_transform_; + fml::RefPtr scene_node_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_PAINTING_SCENE_SHADER_H_ diff --git a/lib/ui/setup_hooks.dart b/lib/ui/setup_hooks.dart new file mode 100644 index 0000000000000..cb79a052c25bf --- /dev/null +++ b/lib/ui/setup_hooks.dart @@ -0,0 +1,29 @@ +// 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. + +part of dart.ui; + +@pragma('vm:entry-point') +void _setupHooks() { + assert(() { + // In debug mode, register the schedule frame extension. + developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); + + // In debug mode, allow shaders to be reinitialized. + developer.registerExtension( + 'ext.ui.window.reinitializeShader', + _reinitializeShader, + ); + + return true; + }()); + + // In debug and profile mode, allow tools to display the current rendering backend. + if (!_kReleaseMode) { + developer.registerExtension( + 'ext.ui.window.impellerEnabled', + _getImpellerEnabled, + ); + } +} diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index 36818d3c74480..e7f9558a359aa 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -38,5 +38,6 @@ part 'platform_dispatcher.dart'; part 'plugins.dart'; part 'pointer.dart'; part 'semantics.dart'; +part 'setup_hooks.dart'; part 'text.dart'; part 'window.dart';