From 3e30e6fbfab523ddf248a45a20bcb6ca5bbd0b4f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 12 Dec 2022 23:57:24 -0800 Subject: [PATCH] [Impeller Scene] Node deserialization (#38190) * [Impeller Scene] Node deserialization * Address comments * Always use RGBA for the placeholder texture * Fix node child update * Temporarily disable test for GLES * Remove GLES test skip --- impeller/scene/geometry.cc | 2 +- impeller/scene/geometry.h | 2 +- impeller/scene/material.cc | 6 +++ impeller/scene/material.h | 6 +++ impeller/scene/mesh.cc | 16 +++--- impeller/scene/mesh.h | 12 +++-- impeller/scene/node.cc | 83 +++++++++++++++++++++++++++++-- impeller/scene/node.h | 16 +++++- impeller/scene/scene_context.cc | 2 +- impeller/scene/scene_unittests.cc | 36 ++++++-------- 10 files changed, 140 insertions(+), 41 deletions(-) diff --git a/impeller/scene/geometry.cc b/impeller/scene/geometry.cc index 2a5f74c244602..426b7a1a95933 100644 --- a/impeller/scene/geometry.cc +++ b/impeller/scene/geometry.cc @@ -40,7 +40,7 @@ std::shared_ptr Geometry::MakeVertexBuffer( return result; } -std::shared_ptr Geometry::MakeFromFBMeshPrimitive( +std::shared_ptr Geometry::MakeFromFlatbuffer( const fb::MeshPrimitive& mesh, Allocator& allocator) { IndexType index_type; diff --git a/impeller/scene/geometry.h b/impeller/scene/geometry.h index bd41589f8988b..641762ab72b88 100644 --- a/impeller/scene/geometry.h +++ b/impeller/scene/geometry.h @@ -28,7 +28,7 @@ class Geometry { static std::shared_ptr MakeVertexBuffer( VertexBuffer vertex_buffer); - static std::shared_ptr MakeFromFBMeshPrimitive( + static std::shared_ptr MakeFromFlatbuffer( const fb::MeshPrimitive& mesh, Allocator& allocator); diff --git a/impeller/scene/material.cc b/impeller/scene/material.cc index b7bcbe882698f..7b4c59a684f70 100644 --- a/impeller/scene/material.cc +++ b/impeller/scene/material.cc @@ -18,6 +18,8 @@ namespace scene { /// Material /// +Material::~Material() = default; + std::unique_ptr Material::MakeUnlit() { return std::make_unique(); } @@ -47,6 +49,8 @@ SceneContextOptions Material::GetContextOptions(const RenderPass& pass) const { /// UnlitMaterial /// +UnlitMaterial::~UnlitMaterial() = default; + void UnlitMaterial::SetColor(Color color) { color_ = color; } @@ -94,6 +98,8 @@ void UnlitMaterial::BindToCommand(const SceneContext& scene_context, /// StandardMaterial /// +StandardMaterial::~StandardMaterial() = default; + void StandardMaterial::SetAlbedo(Color albedo) { albedo_ = albedo; } diff --git a/impeller/scene/material.h b/impeller/scene/material.h index 7c5e1a2caf26b..5814986b67a26 100644 --- a/impeller/scene/material.h +++ b/impeller/scene/material.h @@ -23,6 +23,8 @@ class StandardMaterial; class Material { public: + virtual ~Material(); + struct BlendConfig { BlendOperation color_op = BlendOperation::kAdd; BlendFactor source_color_factor = BlendFactor::kOne; @@ -62,6 +64,8 @@ class Material { class UnlitMaterial final : public Material { public: + ~UnlitMaterial(); + void SetColor(Color color); void SetColorTexture(std::shared_ptr color_texture); @@ -86,6 +90,8 @@ class UnlitMaterial final : public Material { class StandardMaterial final : public Material { public: + ~StandardMaterial(); + void SetAlbedo(Color albedo); void SetRoughness(Scalar roughness); void SetMetallic(Scalar metallic); diff --git a/impeller/scene/mesh.cc b/impeller/scene/mesh.cc index c0a6e1911b623..ff9558126285c 100644 --- a/impeller/scene/mesh.cc +++ b/impeller/scene/mesh.cc @@ -17,23 +17,27 @@ Mesh::Mesh() = default; Mesh::~Mesh() = default; void Mesh::AddPrimitive(Primitive mesh) { - if (mesh.geometry_ == nullptr) { + if (mesh.geometry == nullptr) { VALIDATION_LOG << "Mesh geometry cannot be null."; } - if (mesh.material_ == nullptr) { + if (mesh.material == nullptr) { VALIDATION_LOG << "Mesh material cannot be null."; } - meshes_.push_back(std::move(mesh)); + primitives_.push_back(std::move(mesh)); +} + +std::vector& Mesh::GetPrimitives() { + return primitives_; } bool Mesh::Render(SceneEncoder& encoder, const Matrix& transform) const { - for (const auto& mesh : meshes_) { + for (const auto& mesh : primitives_) { SceneCommand command = { .label = "Mesh Primitive", .transform = transform, - .geometry = mesh.geometry_.get(), - .material = mesh.material_.get(), + .geometry = mesh.geometry.get(), + .material = mesh.material.get(), }; encoder.Add(command); } diff --git a/impeller/scene/mesh.h b/impeller/scene/mesh.h index a454e9d2daa77..5684b4fa5d8dd 100644 --- a/impeller/scene/mesh.h +++ b/impeller/scene/mesh.h @@ -18,19 +18,25 @@ namespace scene { class Mesh final { public: struct Primitive { - std::shared_ptr geometry_; - std::shared_ptr material_; + std::shared_ptr geometry; + std::shared_ptr material; }; Mesh(); ~Mesh(); + Mesh(Mesh&& mesh); + Mesh& operator=(Mesh&& mesh); + void AddPrimitive(Primitive mesh_); + std::vector& GetPrimitives(); bool Render(SceneEncoder& encoder, const Matrix& transform) const; private: - std::vector meshes_; + std::vector primitives_; + + FML_DISALLOW_COPY_AND_ASSIGN(Mesh); }; } // namespace scene diff --git a/impeller/scene/node.cc b/impeller/scene/node.cc index 09838c5744508..ab7a5f7617463 100644 --- a/impeller/scene/node.cc +++ b/impeller/scene/node.cc @@ -8,6 +8,7 @@ #include "impeller/base/validation.h" #include "impeller/geometry/matrix.h" +#include "impeller/scene/importer/scene_flatbuffers.h" #include "impeller/scene/mesh.h" #include "impeller/scene/node.h" #include "impeller/scene/scene_encoder.h" @@ -15,10 +16,62 @@ namespace impeller { namespace scene { +std::optional Node::MakeFromFlatbuffer(fml::Mapping& mapping, + Allocator& allocator) { + flatbuffers::Verifier verifier(mapping.GetMapping(), mapping.GetSize()); + if (!fb::VerifySceneBuffer(verifier)) { + return std::nullopt; + } + + return Node::MakeFromFlatbuffer(*fb::GetScene(mapping.GetMapping()), + allocator); +} + +Node Node::MakeFromFlatbuffer(const fb::Scene& scene, Allocator& allocator) { + Node result; + + if (!scene.children()) { + return result; + } + for (const auto* child : *scene.children()) { + result.AddChild(Node::MakeFromFlatbuffer(*child, allocator)); + } + + return result; +} + +Node Node::MakeFromFlatbuffer(const fb::Node& node, Allocator& allocator) { + Node result; + + Mesh mesh; + for (const auto* primitives : *node.mesh_primitives()) { + auto geometry = Geometry::MakeFromFlatbuffer(*primitives, allocator); + mesh.AddPrimitive({geometry, Material::MakeUnlit()}); + } + result.SetMesh(std::move(mesh)); + + if (!node.children()) { + return result; + } + for (const auto* child : *node.children()) { + result.AddChild(Node::MakeFromFlatbuffer(*child, allocator)); + } + + return result; +} + Node::Node() = default; Node::~Node() = default; +Mesh::Mesh(Mesh&& mesh) = default; + +Mesh& Mesh::operator=(Mesh&& mesh) = default; + +Node::Node(Node&& node) = default; + +Node& Node::operator=(Node&& node) = default; + void Node::SetLocalTransform(Matrix transform) { local_transform_ = transform; } @@ -41,13 +94,33 @@ Matrix Node::GetGlobalTransform() const { return local_transform_; } -void Node::AddChild(Node child) { - children_.push_back(child); - child.parent_ = this; +bool Node::AddChild(Node node) { + 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)); + + Node& ref = children_.back(); + for (Node& child : ref.children_) { + child.parent_ = &ref; + } + + return true; +} + +std::vector& Node::GetChildren() { + return children_; +} + +void Node::SetMesh(Mesh mesh) { + mesh_ = std::move(mesh); } -void Node::SetMesh(const Mesh& mesh) { - mesh_ = mesh; +Mesh& Node::GetMesh() { + return mesh_; } bool Node::Render(SceneEncoder& encoder, const Matrix& parent_transform) const { diff --git a/impeller/scene/node.h b/impeller/scene/node.h index 7a95cc1930d4a..19633b3959241 100644 --- a/impeller/scene/node.h +++ b/impeller/scene/node.h @@ -20,18 +20,28 @@ namespace scene { class Node final { public: + static std::optional MakeFromFlatbuffer(fml::Mapping& mapping, + Allocator& allocator); + static Node MakeFromFlatbuffer(const fb::Scene& scene, Allocator& allocator); + static Node MakeFromFlatbuffer(const fb::Node& node, Allocator& allocator); + Node(); ~Node(); + Node(Node&& node); + Node& operator=(Node&& node); + void SetLocalTransform(Matrix transform); Matrix GetLocalTransform() const; void SetGlobalTransform(Matrix transform); Matrix GetGlobalTransform() const; - void AddChild(Node child); + bool AddChild(Node child); + std::vector& GetChildren(); - void SetMesh(const Mesh& mesh); + void SetMesh(Mesh mesh); + Mesh& GetMesh(); bool Render(SceneEncoder& encoder, const Matrix& parent_transform) const; @@ -42,6 +52,8 @@ class Node final { Node* parent_ = nullptr; std::vector children_; Mesh mesh_; + + FML_DISALLOW_COPY_AND_ASSIGN(Node); }; } // namespace scene diff --git a/impeller/scene/scene_context.cc b/impeller/scene/scene_context.cc index 6a270b9d1ed31..fbb6f02f677d7 100644 --- a/impeller/scene/scene_context.cc +++ b/impeller/scene/scene_context.cc @@ -49,7 +49,7 @@ SceneContext::SceneContext(std::shared_ptr context) { impeller::TextureDescriptor texture_descriptor; texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; - texture_descriptor.format = PixelFormat::kDefaultColor; + texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = {1, 1}; texture_descriptor.mip_count = 1u; diff --git a/impeller/scene/scene_unittests.cc b/impeller/scene/scene_unittests.cc index 746d6158f9500..1a1b207c7450c 100644 --- a/impeller/scene/scene_unittests.cc +++ b/impeller/scene/scene_unittests.cc @@ -47,7 +47,7 @@ TEST_P(SceneTest, CuboidUnlit) { Node& root = scene.GetRoot(); root.SetLocalTransform(Matrix::MakeTranslation(-size / 2)); - root.SetMesh(mesh); + root.SetMesh(std::move(mesh)); } // Face towards the +Z direction (+X right, +Y up). @@ -70,34 +70,26 @@ TEST_P(SceneTest, GLTFScene) { auto mapping = flutter::testing::OpenFixtureAsMapping("flutter_logo.glb.ipscene"); + ASSERT_NE(mapping, nullptr); - flatbuffers::Verifier verifier(mapping->GetMapping(), mapping->GetSize()); - ASSERT_TRUE(fb::VerifySceneBuffer(verifier)); - - // TODO(bdero): Add full scene deserialization utilities. - const auto* fb_scene = fb::GetScene(mapping->GetMapping()); - const auto fb_nodes = fb_scene->children(); - ASSERT_EQ(fb_nodes->size(), 1u); - const auto fb_meshes = fb_nodes->begin()->mesh_primitives(); - ASSERT_EQ(fb_meshes->size(), 1u); - const auto* fb_mesh = fb_meshes->Get(0); - auto geometry = Geometry::MakeFromFBMeshPrimitive(*fb_mesh, *allocator); - ASSERT_NE(geometry, nullptr); + std::optional gltf_scene = + Node::MakeFromFlatbuffer(*mapping, *allocator); + ASSERT_TRUE(gltf_scene.has_value()); std::shared_ptr material = Material::MakeUnlit(); - auto bridge = CreateTextureForFixture("flutter_logo_baked.png"); - material->SetColorTexture(bridge); + auto color_baked = CreateTextureForFixture("flutter_logo_baked.png"); + material->SetColorTexture(color_baked); material->SetVertexColorWeight(0); - Renderer::RenderCallback callback = [&](RenderTarget& render_target) { - auto scene = Scene(GetContext()); + ASSERT_EQ(gltf_scene->GetChildren().size(), 1u); + ASSERT_EQ(gltf_scene->GetChildren()[0].GetMesh().GetPrimitives().size(), 1u); + gltf_scene->GetChildren()[0].GetMesh().GetPrimitives()[0].material = material; - Mesh mesh; - mesh.AddPrimitive({geometry, material}); - - scene.GetRoot().SetLocalTransform(Matrix::MakeScale({3, 3, 3})); - scene.GetRoot().SetMesh(mesh); + auto scene = Scene(GetContext()); + scene.GetRoot().AddChild(std::move(gltf_scene.value())); + scene.GetRoot().SetLocalTransform(Matrix::MakeScale({3, 3, 3})); + Renderer::RenderCallback callback = [&](RenderTarget& render_target) { Quaternion rotation({0, 1, 0}, -GetSecondsElapsed() * 0.5); Vector3 start_position(-1, -1.5, -5);