Flutter Impeller
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 
5 #include "impeller/scene/node.h"
6 
7 #include <inttypes.h>
8 #include <atomic>
9 #include <memory>
10 #include <vector>
11 
12 #include "flutter/fml/logging.h"
13 #include "impeller/base/strings.h"
14 #include "impeller/base/thread.h"
19 #include "impeller/scene/importer/scene_flatbuffers.h"
20 #include "impeller/scene/mesh.h"
21 #include "impeller/scene/node.h"
23 
24 namespace impeller {
25 namespace scene {
26 
27 static std::atomic_uint64_t kNextNodeID = 0;
28 
29 void Node::MutationLog::Append(const Entry& entry) {
30  WriterLock lock(write_mutex_);
31  dirty_ = true;
32  entries_.push_back(entry);
33 }
34 
35 std::optional<std::vector<Node::MutationLog::Entry>>
36 Node::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 
47 std::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 
61 static 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 
126 std::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 
181 void 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 
224 Node::Node() : name_(SPrintF("__node%" PRIu64, kNextNodeID++)){};
225 
226 Node::~Node() = default;
227 
228 Mesh::Mesh(Mesh&& mesh) = default;
229 
230 Mesh& Mesh::operator=(Mesh&& mesh) = default;
231 
232 const std::string& Node::GetName() const {
233  return name_;
234 }
235 
236 void Node::SetName(const std::string& new_name) {
237  name_ = new_name;
238 }
239 
241  return parent_;
242 }
243 
244 std::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 
261 std::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 
271 AnimationClip* 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 
300 bool 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 
325 std::vector<std::shared_ptr<Node>>& Node::GetChildren() {
326  return children_;
327 }
328 
329 void Node::SetMesh(Mesh mesh) {
330  mesh_ = std::move(mesh);
331 }
332 
334  return mesh_;
335 }
336 
337 void Node::SetIsJoint(bool is_joint) {
338  is_joint_ = is_joint;
339 }
340 
341 bool 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)) {
355  AnimationClip* clip =
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)) {
376  AnimationClip* clip =
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
impeller::scene::Node::Node
Node()
Definition: node.cc:224
impeller::scene::Node::GetLocalTransform
Matrix GetLocalTransform() const
Definition: node.cc:282
impeller::scene::Node::GetParent
Node * GetParent() const
Definition: node.cc:240
impeller::scene::Mesh
Definition: mesh.h:21
impeller::scene::Node::FindChildByName
std::shared_ptr< Node > FindChildByName(const std::string &name, bool exclude_animation_players=false) const
Definition: node.cc:244
impeller::scene::Node::~Node
~Node()
impeller::scene::AnimationClip::SetPlaybackTimeScale
void SetPlaybackTimeScale(Scalar playback_speed)
Sets the animation playback speed. Negative values make the clip play in reverse.
Definition: animation_clip.cc:61
impeller::scene::Mesh::Mesh
Mesh()
impeller::scene::Node::SetIsJoint
void SetIsJoint(bool is_joint)
Definition: node.cc:337
impeller::WriterLock
Definition: thread.h:116
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
animation_player.h
impeller::scene::AnimationClip::SetLoop
void SetLoop(bool looping)
Definition: animation_clip.cc:53
impeller::scene::Skin::MakeFromFlatbuffer
static std::unique_ptr< Skin > MakeFromFlatbuffer(const fb::Skin &skin, const std::vector< std::shared_ptr< Node >> &scene_nodes)
Definition: skin.cc:19
impeller::scene::Node::SetMesh
void SetMesh(Mesh mesh)
Definition: node.cc:329
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::Allocator::CreateTexture
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Definition: allocator.cc:49
validation.h
impeller::scene::AnimationClip
Definition: animation_clip.h:22
conversions.h
impeller::scene::Animation::MakeFromFlatbuffer
static std::shared_ptr< Animation > MakeFromFlatbuffer(const fb::Animation &animation, const std::vector< std::shared_ptr< Node >> &scene_nodes)
Definition: animation.cc:19
impeller::scene::Node::GetName
const std::string & GetName() const
Definition: node.cc:232
scene_encoder.h
impeller::scene::Node::GetChildren
std::vector< std::shared_ptr< Node > > & GetChildren()
Definition: node.cc:325
impeller::scene::Node::SetName
void SetName(const std::string &new_name)
Definition: node.cc:236
impeller::scene::Mesh::Render
bool Render(SceneEncoder &encoder, const Matrix &transform, const std::shared_ptr< Texture > &joints) const
Definition: mesh.cc:36
matrix.h
impeller::SecondsF
std::chrono::duration< float > SecondsF
Definition: timing.h:13
impeller::scene::Node::FindAnimationByName
std::shared_ptr< Animation > FindAnimationByName(const std::string &name) const
Definition: node.cc:261
node.h
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::scene::UnpackTextureFromFlatbuffer
static std::shared_ptr< Texture > UnpackTextureFromFlatbuffer(const fb::Texture *iptexture, Allocator &allocator)
Definition: node.cc:61
impeller::scene::Mesh::AddPrimitive
void AddPrimitive(Primitive mesh_)
Definition: mesh.cc:21
impeller::scene::kNextNodeID
static std::atomic_uint64_t kNextNodeID
Definition: node.cc:27
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:22
impeller::scene::Node::GetMesh
Mesh & GetMesh()
Definition: node.cc:333
impeller::scene::AnimationClip::SetWeight
void SetWeight(Scalar weight)
Definition: animation_clip.cc:69
impeller::scene::Geometry::MakeFromFlatbuffer
static std::shared_ptr< Geometry > MakeFromFlatbuffer(const fb::MeshPrimitive &mesh, Allocator &allocator)
Definition: geometry.cc:50
mesh.h
impeller::scene::AnimationPlayer
Definition: animation_player.h:26
strings.h
impeller::scene::Material::MakeFromFlatbuffer
static std::unique_ptr< Material > MakeFromFlatbuffer(const fb::Material &material, const std::vector< std::shared_ptr< Texture >> &textures)
Definition: material.cc:27
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::scene::Node::SetGlobalTransform
void SetGlobalTransform(Matrix transform)
Definition: node.cc:286
impeller::scene::Node::AddChild
bool AddChild(std::shared_ptr< Node > child)
Definition: node.cc:300
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::scene::Node::Render
bool Render(SceneEncoder &encoder, Allocator &allocator, const Matrix &parent_transform)
Definition: node.cc:345
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::scene::Node::MutationLog::Entry
std::variant< SetTransformEntry, SetAnimationStateEntry, SeekAnimationEntry > Entry
Definition: node.h:52
textures
std::vector< std::shared_ptr< FakeTexture > > textures
Definition: content_context_unittests.cc:92
impeller::scene::Node::IsJoint
bool IsJoint() const
Definition: node.cc:341
impeller::scene::Node::AddAnimation
AnimationClip * AddAnimation(const std::shared_ptr< Animation > &animation)
Definition: node.cc:271
impeller::scene::Node
Definition: node.h:30
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:38
impeller::scene::Node::AddMutation
void AddMutation(const MutationLog::Entry &entry)
Definition: node.cc:412
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:37
impeller::scene::SceneEncoder
Definition: scene_encoder.h:30
impeller::scene::Node::SetLocalTransform
void SetLocalTransform(Matrix transform)
Definition: node.cc:278
impeller::scene::AnimationClip::Seek
void Seek(SecondsF time)
Move the animation to the specified time. The given time is clamped to the animation's playback range...
Definition: animation_clip.cc:77
impeller::scene::Node::MakeFromFlatbuffer
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:47
impeller::scene::AnimationClip::SetPlaying
void SetPlaying(bool playing)
Definition: animation_clip.cc:32
thread.h
impeller
Definition: aiks_blur_unittests.cc:20
impeller::scene::Node::MutationLog::Append
void Append(const Entry &entry)
Definition: node.cc:29
impeller::scene::Mesh::operator=
Mesh & operator=(Mesh &&mesh)
impeller::scene::Material
Definition: material.h:27
impeller::scene::Node::GetGlobalTransform
Matrix GetGlobalTransform() const
Definition: node.cc:293
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::scene::importer::ToMatrix
Matrix ToMatrix(const std::vector< double > &m)
Definition: conversions.cc:15