Flutter Engine
The Flutter Engine
node.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <inttypes.h>
8#include <atomic>
9#include <memory>
10#include <vector>
11
12#include "flutter/fml/logging.h"
19#include "impeller/scene/importer/scene_flatbuffers.h"
20#include "impeller/scene/mesh.h"
21#include "impeller/scene/node.h"
23
24namespace impeller {
25namespace scene {
26
27static std::atomic_uint64_t kNextNodeID = 0;
28
29void Node::MutationLog::Append(const Entry& entry) {
30 WriterLock lock(write_mutex_);
31 dirty_ = true;
32 entries_.push_back(entry);
33}
34
35std::optional<std::vector<Node::MutationLog::Entry>>
36Node::MutationLog::Flush() {
37 WriterLock lock(write_mutex_);
38 if (!dirty_) {
39 return std::nullopt;
40 }
41 dirty_ = false;
42 auto result = entries_;
43 entries_ = {};
44 return result;
45}
46
47std::shared_ptr<Node> Node::MakeFromFlatbuffer(
48 const fml::Mapping& ipscene_mapping,
49 Allocator& allocator) {
50 flatbuffers::Verifier verifier(ipscene_mapping.GetMapping(),
51 ipscene_mapping.GetSize());
52 if (!fb::VerifySceneBuffer(verifier)) {
53 VALIDATION_LOG << "Failed to unpack scene: Scene flatbuffer is invalid.";
54 return nullptr;
55 }
56
57 return Node::MakeFromFlatbuffer(*fb::GetScene(ipscene_mapping.GetMapping()),
58 allocator);
59}
60
61static std::shared_ptr<Texture> UnpackTextureFromFlatbuffer(
62 const fb::Texture* iptexture,
63 Allocator& allocator) {
64 if (iptexture == nullptr || iptexture->embedded_image() == nullptr ||
65 iptexture->embedded_image()->bytes() == nullptr) {
66 return nullptr;
67 }
68
69 auto embedded = iptexture->embedded_image();
70
71 uint8_t bytes_per_component = 0;
72 switch (embedded->component_type()) {
73 case fb::ComponentType::k8Bit:
74 bytes_per_component = 1;
75 break;
76 case fb::ComponentType::k16Bit:
77 // bytes_per_component = 2;
78 FML_LOG(WARNING) << "16 bit textures not yet supported.";
79 return nullptr;
80 }
81
82 switch (embedded->component_count()) {
83 case 4:
84 // RGBA.
85 break;
86 case 1:
87 case 3:
88 default:
89 FML_LOG(WARNING) << "Textures with " << embedded->component_count()
90 << " components are not supported." << std::endl;
91 return nullptr;
92 }
93 if (embedded->bytes()->size() != bytes_per_component *
94 embedded->component_count() *
95 embedded->width() * embedded->height()) {
96 FML_LOG(WARNING) << "Embedded texture has an unexpected size. Skipping."
97 << std::endl;
98 return nullptr;
99 }
100
101 auto image_mapping = std::make_shared<fml::NonOwnedMapping>(
102 embedded->bytes()->Data(), embedded->bytes()->size());
103
104 auto texture_descriptor = TextureDescriptor{};
105 texture_descriptor.storage_mode = StorageMode::kHostVisible;
106 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
107 texture_descriptor.size = ISize(embedded->width(), embedded->height());
108 // TODO(bdero): Generate mipmaps for embedded textures.
109 texture_descriptor.mip_count = 1u;
110
111 auto texture = allocator.CreateTexture(texture_descriptor);
112 if (!texture) {
113 FML_LOG(ERROR) << "Could not allocate texture.";
114 return nullptr;
115 }
116
117 auto uploaded = texture->SetContents(image_mapping);
118 if (!uploaded) {
119 FML_LOG(ERROR) << "Could not upload texture to device memory.";
120 return nullptr;
121 }
122
123 return texture;
124}
125
126std::shared_ptr<Node> Node::MakeFromFlatbuffer(const fb::Scene& scene,
127 Allocator& allocator) {
128 // Unpack textures.
129 std::vector<std::shared_ptr<Texture>> textures;
130 if (scene.textures()) {
131 for (const auto iptexture : *scene.textures()) {
132 // The elements of the unpacked texture array must correspond exactly with
133 // the ipscene texture array. So if a texture is empty or invalid, a
134 // nullptr is inserted as a placeholder.
135 textures.push_back(UnpackTextureFromFlatbuffer(iptexture, allocator));
136 }
137 }
138
139 auto result = std::make_shared<Node>();
140 result->SetLocalTransform(importer::ToMatrix(*scene.transform()));
141
142 if (!scene.nodes() || !scene.children()) {
143 return result; // The scene is empty.
144 }
145
146 // Initialize nodes for unpacking the entire scene.
147 std::vector<std::shared_ptr<Node>> scene_nodes;
148 scene_nodes.reserve(scene.nodes()->size());
149 for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) {
150 scene_nodes.push_back(std::make_shared<Node>());
151 }
152
153 // Connect children to the root node.
154 for (int child : *scene.children()) {
155 if (child < 0 || static_cast<size_t>(child) >= scene_nodes.size()) {
156 VALIDATION_LOG << "Scene child index out of range.";
157 continue;
158 }
159 result->AddChild(scene_nodes[child]);
160 }
161
162 // Unpack each node.
163 for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) {
164 scene_nodes[node_i]->UnpackFromFlatbuffer(*scene.nodes()->Get(node_i),
165 scene_nodes, textures, allocator);
166 }
167
168 // Unpack animations.
169 if (scene.animations()) {
170 for (const auto animation : *scene.animations()) {
171 if (auto out_animation =
172 Animation::MakeFromFlatbuffer(*animation, scene_nodes)) {
173 result->animations_.push_back(out_animation);
174 }
175 }
176 }
177
178 return result;
179}
180
181void Node::UnpackFromFlatbuffer(
182 const fb::Node& source_node,
183 const std::vector<std::shared_ptr<Node>>& scene_nodes,
184 const std::vector<std::shared_ptr<Texture>>& textures,
185 Allocator& allocator) {
186 name_ = source_node.name()->str();
187 SetLocalTransform(importer::ToMatrix(*source_node.transform()));
188
189 /// Meshes.
190
191 if (source_node.mesh_primitives()) {
192 Mesh mesh;
193 for (const auto* primitives : *source_node.mesh_primitives()) {
194 auto geometry = Geometry::MakeFromFlatbuffer(*primitives, allocator);
195 auto material =
196 primitives->material()
197 ? Material::MakeFromFlatbuffer(*primitives->material(), textures)
198 : Material::MakeUnlit();
199 mesh.AddPrimitive({std::move(geometry), std::move(material)});
200 }
201 SetMesh(std::move(mesh));
202 }
203
204 /// Child nodes.
205
206 if (source_node.children()) {
207 // Wire up graph connections.
208 for (int child : *source_node.children()) {
209 if (child < 0 || static_cast<size_t>(child) >= scene_nodes.size()) {
210 VALIDATION_LOG << "Node child index out of range.";
211 continue;
212 }
213 AddChild(scene_nodes[child]);
214 }
215 }
216
217 /// Skin.
218
219 if (source_node.skin()) {
220 skin_ = Skin::MakeFromFlatbuffer(*source_node.skin(), scene_nodes);
221 }
222}
223
224Node::Node() : name_(SPrintF("__node%" PRIu64, kNextNodeID++)){};
225
226Node::~Node() = default;
227
228Mesh::Mesh(Mesh&& mesh) = default;
229
230Mesh& Mesh::operator=(Mesh&& mesh) = default;
231
232const std::string& Node::GetName() const {
233 return name_;
234}
235
236void Node::SetName(const std::string& new_name) {
237 name_ = new_name;
238}
239
241 return parent_;
242}
243
244std::shared_ptr<Node> Node::FindChildByName(
245 const std::string& name,
246 bool exclude_animation_players) const {
247 for (auto& child : children_) {
248 if (exclude_animation_players && child->animation_player_.has_value()) {
249 continue;
250 }
251 if (child->GetName() == name) {
252 return child;
253 }
254 if (auto found = child->FindChildByName(name)) {
255 return found;
256 }
257 }
258 return nullptr;
259}
260
261std::shared_ptr<Animation> Node::FindAnimationByName(
262 const std::string& name) const {
263 for (const auto& animation : animations_) {
264 if (animation->GetName() == name) {
265 return animation;
266 }
267 }
268 return nullptr;
269}
270
271AnimationClip* Node::AddAnimation(const std::shared_ptr<Animation>& animation) {
272 if (!animation_player_.has_value()) {
273 animation_player_ = AnimationPlayer();
274 }
275 return animation_player_->AddAnimation(animation, this);
276}
277
279 local_transform_ = transform;
280}
281
283 return local_transform_;
284}
285
287 Matrix inverse_global_transform =
288 parent_ ? parent_->GetGlobalTransform().Invert() : Matrix();
289
290 local_transform_ = inverse_global_transform * transform;
291}
292
294 if (parent_) {
295 return parent_->GetGlobalTransform() * local_transform_;
296 }
297 return local_transform_;
298}
299
300bool Node::AddChild(std::shared_ptr<Node> node) {
301 if (!node) {
302 VALIDATION_LOG << "Cannot add null child to node.";
303 return false;
304 }
305
306 // TODO(bdero): Figure out a better paradigm/rules for nodes with multiple
307 // parents. We should probably disallow this, make deep
308 // copying of nodes cheap and easy, add mesh instancing, etc.
309 // Today, the parent link is only used for skin posing, and so
310 // it's reasonable to not have a check and allow multi-parenting.
311 // Even still, there should still be some kind of cycle
312 // prevention/detection, ideally at the protocol level.
313 //
314 // if (node->parent_ != nullptr) {
315 // VALIDATION_LOG
316 // << "Cannot add a node as a child which already has a parent.";
317 // return false;
318 // }
319 node->parent_ = this;
320 children_.push_back(std::move(node));
321
322 return true;
323}
324
325std::vector<std::shared_ptr<Node>>& Node::GetChildren() {
326 return children_;
327}
328
330 mesh_ = std::move(mesh);
331}
332
334 return mesh_;
335}
336
337void Node::SetIsJoint(bool is_joint) {
338 is_joint_ = is_joint;
339}
340
341bool Node::IsJoint() const {
342 return is_joint_;
343}
344
346 Allocator& allocator,
347 const Matrix& parent_transform) {
348 std::optional<std::vector<MutationLog::Entry>> log = mutation_log_.Flush();
349 if (log.has_value()) {
350 for (const auto& entry : log.value()) {
351 if (auto e = std::get_if<MutationLog::SetTransformEntry>(&entry)) {
352 local_transform_ = e->transform;
353 } else if (auto e =
354 std::get_if<MutationLog::SetAnimationStateEntry>(&entry)) {
356 animation_player_.has_value()
357 ? animation_player_->GetClip(e->animation_name)
358 : nullptr;
359 if (!clip) {
360 auto animation = FindAnimationByName(e->animation_name);
361 if (!animation) {
362 continue;
363 }
364 clip = AddAnimation(animation);
365 if (!clip) {
366 continue;
367 }
368 }
369
370 clip->SetPlaying(e->playing);
371 clip->SetLoop(e->loop);
372 clip->SetWeight(e->weight);
373 clip->SetPlaybackTimeScale(e->time_scale);
374 } else if (auto e =
375 std::get_if<MutationLog::SeekAnimationEntry>(&entry)) {
377 animation_player_.has_value()
378 ? animation_player_->GetClip(e->animation_name)
379 : nullptr;
380 if (!clip) {
381 auto animation = FindAnimationByName(e->animation_name);
382 if (!animation) {
383 continue;
384 }
385 clip = AddAnimation(animation);
386 if (!clip) {
387 continue;
388 }
389 }
390
391 clip->Seek(SecondsF(e->time));
392 }
393 }
394 }
395
396 if (animation_player_.has_value()) {
397 animation_player_->Update();
398 }
399
400 Matrix transform = parent_transform * local_transform_;
401 mesh_.Render(encoder, transform,
402 skin_ ? skin_->GetJointsTexture(allocator) : nullptr);
403
404 for (auto& child : children_) {
405 if (!child->Render(encoder, allocator, transform)) {
406 return false;
407 }
408 }
409 return true;
410}
411
413 mutation_log_.Append(entry);
414}
415
416} // namespace scene
417} // namespace impeller
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
virtual const uint8_t * GetMapping() const =0
virtual size_t GetSize() const =0
An object that allocates device memory.
Definition: allocator.h:22
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Definition: allocator.cc:49
static std::shared_ptr< Animation > MakeFromFlatbuffer(const fb::Animation &animation, const std::vector< std::shared_ptr< Node > > &scene_nodes)
Definition: animation.cc:19
static std::shared_ptr< Geometry > MakeFromFlatbuffer(const fb::MeshPrimitive &mesh, Allocator &allocator)
Definition: geometry.cc:50
static std::unique_ptr< Material > MakeFromFlatbuffer(const fb::Material &material, const std::vector< std::shared_ptr< Texture > > &textures)
Definition: material.cc:27
bool Render(SceneEncoder &encoder, const Matrix &transform, const std::shared_ptr< Texture > &joints) const
Definition: mesh.cc:36
Mesh & operator=(Mesh &&mesh)
std::variant< SetTransformEntry, SetAnimationStateEntry, SeekAnimationEntry > Entry
Definition: node.h:48
void Append(const Entry &entry)
Definition: node.cc:29
void SetIsJoint(bool is_joint)
Definition: node.cc:337
bool AddChild(std::shared_ptr< Node > child)
Definition: node.cc:300
void SetLocalTransform(Matrix transform)
Definition: node.cc:278
void SetGlobalTransform(Matrix transform)
Definition: node.cc:286
Node * GetParent() const
Definition: node.cc:240
std::shared_ptr< Animation > FindAnimationByName(const std::string &name) const
Definition: node.cc:261
Matrix GetGlobalTransform() const
Definition: node.cc:293
AnimationClip * AddAnimation(const std::shared_ptr< Animation > &animation)
Definition: node.cc:271
void AddMutation(const MutationLog::Entry &entry)
Definition: node.cc:412
bool Render(SceneEncoder &encoder, Allocator &allocator, const Matrix &parent_transform)
Definition: node.cc:345
const std::string & GetName() const
Definition: node.cc:232
void SetName(const std::string &new_name)
Definition: node.cc:236
std::shared_ptr< Node > FindChildByName(const std::string &name, bool exclude_animation_players=false) const
Definition: node.cc:244
Mesh & GetMesh()
Definition: node.cc:333
std::vector< std::shared_ptr< Node > > & GetChildren()
Definition: node.cc:325
void SetMesh(Mesh mesh)
Definition: node.cc:329
bool IsJoint() const
Definition: node.cc:341
Matrix GetLocalTransform() const
Definition: node.cc:282
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:47
static std::unique_ptr< Skin > MakeFromFlatbuffer(const fb::Skin &skin, const std::vector< std::shared_ptr< Node > > &scene_nodes)
Definition: skin.cc:19
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
FlTexture * texture
SkMesh mesh
Definition: SkRecords.h:345
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
Matrix ToMatrix(const std::vector< double > &m)
Definition: conversions.cc:15
static std::shared_ptr< Texture > UnpackTextureFromFlatbuffer(const fb::Texture *iptexture, Allocator &allocator)
Definition: node.cc:61
static std::atomic_uint64_t kNextNodeID
Definition: node.cc:27
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
ISize64 ISize
Definition: size.h:140
std::chrono::duration< float > SecondsF
Definition: timing.h:13
SK_API sk_sp< PrecompileColorFilter > Matrix()
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
Matrix Invert() const
Definition: matrix.cc:97
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define ERROR(message)
Definition: elf_loader.cc:260
#define VALIDATION_LOG
Definition: validation.h:73