[Impeller Scene] Command encoding (#37977)

diff --git a/impeller/scene/geometry.cc b/impeller/scene/geometry.cc
index 47ed75f..8a7bf01 100644
--- a/impeller/scene/geometry.cc
+++ b/impeller/scene/geometry.cc
@@ -6,6 +6,12 @@
 
 #include <memory>
 
+#include "impeller/geometry/point.h"
+#include "impeller/geometry/vector.h"
+#include "impeller/renderer/formats.h"
+#include "impeller/renderer/vertex_buffer_builder.h"
+#include "impeller/scene/shaders/geometry.vert.h"
+
 namespace impeller {
 namespace scene {
 
@@ -27,9 +33,19 @@
   size_ = size;
 }
 
-VertexBuffer CuboidGeometry::GetVertexBuffer(
-    std::shared_ptr<Allocator>& allocator) const {
-  return {};
+VertexBuffer CuboidGeometry::GetVertexBuffer(Allocator& allocator) const {
+  VertexBufferBuilder<GeometryVertexShader::PerVertexData, uint16_t> builder;
+  // Layout: position, normal, tangent, uv
+  builder.AddVertices({
+      // Front.
+      {Vector3(0, 0, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(0, 0)},
+      {Vector3(1, 0, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(1, 0)},
+      {Vector3(1, 1, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(1, 1)},
+      {Vector3(1, 1, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(1, 1)},
+      {Vector3(0, 1, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(0, 1)},
+      {Vector3(0, 0, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(0, 0)},
+  });
+  return builder.CreateVertexBuffer(allocator);
 }
 
 }  // namespace scene
diff --git a/impeller/scene/geometry.h b/impeller/scene/geometry.h
index f0e7e47..92c90d1 100644
--- a/impeller/scene/geometry.h
+++ b/impeller/scene/geometry.h
@@ -19,19 +19,16 @@
  public:
   static std::shared_ptr<CuboidGeometry> MakeCuboid(Vector3 size);
 
- private:
-  virtual VertexBuffer GetVertexBuffer(
-      std::shared_ptr<Allocator>& allocator) const = 0;
+  virtual VertexBuffer GetVertexBuffer(Allocator& allocator) const = 0;
 };
 
 class CuboidGeometry final : public Geometry {
  public:
   void SetSize(Vector3 size);
 
- private:
-  VertexBuffer GetVertexBuffer(
-      std::shared_ptr<Allocator>& allocator) const override;
+  VertexBuffer GetVertexBuffer(Allocator& allocator) const override;
 
+ private:
   Vector3 size_;
 };
 
diff --git a/impeller/scene/material.cc b/impeller/scene/material.cc
index a38a14a..a37b2bf 100644
--- a/impeller/scene/material.cc
+++ b/impeller/scene/material.cc
@@ -3,6 +3,11 @@
 // found in the LICENSE file.
 
 #include "impeller/scene/material.h"
+#include "impeller/renderer/formats.h"
+#include "impeller/renderer/sampler_descriptor.h"
+#include "impeller/renderer/sampler_library.h"
+#include "impeller/scene/scene_context.h"
+#include "impeller/scene/shaders/unlit.frag.h"
 
 #include <memory>
 
@@ -33,6 +38,11 @@
   is_translucent_ = is_translucent;
 }
 
+SceneContextOptions Material::GetContextOptions(const RenderPass& pass) const {
+  // TODO(bdero): Pipeline blend and stencil config.
+  return {.sample_count = pass.GetRenderTarget().GetSampleCount()};
+}
+
 //------------------------------------------------------------------------------
 /// UnlitMaterial
 ///
@@ -41,6 +51,40 @@
   color_ = color;
 }
 
+void UnlitMaterial::SetColorTexture(std::shared_ptr<Texture> color_texture) {
+  color_texture_ = std::move(color_texture);
+}
+
+// |Material|
+std::shared_ptr<Pipeline<PipelineDescriptor>> UnlitMaterial::GetPipeline(
+    const SceneContext& scene_context,
+    const RenderPass& pass) const {
+  return scene_context.GetUnlitPipeline(GetContextOptions(pass));
+}
+
+// |Material|
+void UnlitMaterial::BindToCommand(const SceneContext& scene_context,
+                                  HostBuffer& buffer,
+                                  Command& command) const {
+  // Uniform buffer.
+  UnlitPipeline::FragmentShader::FragInfo info;
+  info.color = color_;
+  UnlitPipeline::FragmentShader::BindFragInfo(command,
+                                              buffer.EmplaceUniform(info));
+
+  // Textures.
+  SamplerDescriptor sampler_descriptor;
+  sampler_descriptor.label = "Trilinear";
+  sampler_descriptor.min_filter = MinMagFilter::kLinear;
+  sampler_descriptor.mag_filter = MinMagFilter::kLinear;
+  sampler_descriptor.mip_filter = MipFilter::kLinear;
+  UnlitPipeline::FragmentShader::BindBaseColorTexture(
+      command,
+      color_texture_ ? color_texture_ : scene_context.GetPlaceholderTexture(),
+      scene_context.GetContext()->GetSamplerLibrary()->GetSampler(
+          sampler_descriptor));
+}
+
 //------------------------------------------------------------------------------
 /// StandardMaterial
 ///
@@ -78,5 +122,17 @@
   environment_map_ = std::move(environment_map);
 }
 
+// |Material|
+std::shared_ptr<Pipeline<PipelineDescriptor>> StandardMaterial::GetPipeline(
+    const SceneContext& scene_context,
+    const RenderPass& pass) const {
+  return nullptr;
+}
+
+// |Material|
+void StandardMaterial::BindToCommand(const SceneContext& scene_context,
+                                     HostBuffer& buffer,
+                                     Command& command) const {}
+
 }  // namespace scene
 }  // namespace impeller
diff --git a/impeller/scene/material.h b/impeller/scene/material.h
index a9e0be7..a579467 100644
--- a/impeller/scene/material.h
+++ b/impeller/scene/material.h
@@ -8,11 +8,16 @@
 
 #include "impeller/geometry/scalar.h"
 #include "impeller/renderer/formats.h"
+#include "impeller/renderer/render_pass.h"
 #include "impeller/renderer/texture.h"
 
 namespace impeller {
 namespace scene {
 
+class SceneContext;
+struct SceneContextOptions;
+class Geometry;
+
 class UnlitMaterial;
 class StandardMaterial;
 
@@ -40,7 +45,16 @@
 
   void SetTranslucent(bool is_translucent);
 
+  virtual std::shared_ptr<Pipeline<PipelineDescriptor>> GetPipeline(
+      const SceneContext& scene_context,
+      const RenderPass& pass) const = 0;
+  virtual void BindToCommand(const SceneContext& scene_context,
+                             HostBuffer& buffer,
+                             Command& command) const = 0;
+
  protected:
+  SceneContextOptions GetContextOptions(const RenderPass& pass) const;
+
   BlendConfig blend_config_;
   StencilConfig stencil_config_;
   bool is_translucent_ = false;
@@ -50,8 +64,21 @@
  public:
   void SetColor(Color color);
 
+  void SetColorTexture(std::shared_ptr<Texture> color_texture);
+
+  // |Material|
+  std::shared_ptr<Pipeline<PipelineDescriptor>> GetPipeline(
+      const SceneContext& scene_context,
+      const RenderPass& pass) const override;
+
+  // |Material|
+  void BindToCommand(const SceneContext& scene_context,
+                     HostBuffer& buffer,
+                     Command& command) const override;
+
  private:
-  Color color_;
+  Color color_ = Color::White();
+  std::shared_ptr<Texture> color_texture_;
 };
 
 class StandardMaterial final : public Material {
@@ -67,8 +94,18 @@
 
   void SetEnvironmentMap(std::shared_ptr<Texture> environment_map);
 
+  // |Material|
+  std::shared_ptr<Pipeline<PipelineDescriptor>> GetPipeline(
+      const SceneContext& scene_context,
+      const RenderPass& pass) const override;
+
+  // |Material|
+  void BindToCommand(const SceneContext& scene_context,
+                     HostBuffer& buffer,
+                     Command& command) const override;
+
  private:
-  Color albedo_ = Color::CornflowerBlue();
+  Color albedo_ = Color::White();
   Scalar roughness_ = 0.5;
   Scalar metallic_ = 0.5;
 
diff --git a/impeller/scene/scene.cc b/impeller/scene/scene.cc
index 6fbd0e6..ee4fa8c 100644
--- a/impeller/scene/scene.cc
+++ b/impeller/scene/scene.cc
@@ -32,9 +32,9 @@
   }
 
   // Encode the commands.
+
   std::shared_ptr<CommandBuffer> command_buffer =
-      encoder.BuildSceneCommandBuffer(*scene_context_->GetContext(),
-                                      render_target);
+      encoder.BuildSceneCommandBuffer(*scene_context_, render_target);
 
   // TODO(bdero): Do post processing.
 
diff --git a/impeller/scene/scene_context.cc b/impeller/scene/scene_context.cc
index 006b13c..dcf1ce8 100644
--- a/impeller/scene/scene_context.cc
+++ b/impeller/scene/scene_context.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "impeller/scene/scene_context.h"
+#include "impeller/renderer/formats.h"
 
 namespace impeller {
 namespace scene {
@@ -33,6 +34,27 @@
 
   unlit_pipeline_[{}] = CreateDefaultPipeline<UnlitPipeline>(*context_);
 
+  {
+    impeller::TextureDescriptor texture_descriptor;
+    texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
+    texture_descriptor.format = PixelFormat::kDefaultColor;
+    texture_descriptor.size = {1, 1};
+    texture_descriptor.mip_count = 1u;
+
+    placeholder_texture_ =
+        context_->GetResourceAllocator()->CreateTexture(texture_descriptor);
+    if (!placeholder_texture_) {
+      FML_DLOG(ERROR) << "Could not create placeholder texture.";
+      return;
+    }
+
+    uint8_t pixel[] = {0xFF, 0xFF, 0xFF, 0xFF};
+    if (!placeholder_texture_->SetContents(pixel, 4, 0)) {
+      FML_DLOG(ERROR) << "Could not set contents of placeholder texture.";
+      return;
+    }
+  }
+
   is_valid_ = true;
 }
 
@@ -46,5 +68,9 @@
   return context_;
 }
 
+std::shared_ptr<Texture> SceneContext::GetPlaceholderTexture() const {
+  return placeholder_texture_;
+}
+
 }  // namespace scene
 }  // namespace impeller
diff --git a/impeller/scene/scene_context.h b/impeller/scene/scene_context.h
index 0b13e48..1e1a2a6 100644
--- a/impeller/scene/scene_context.h
+++ b/impeller/scene/scene_context.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <memory>
+
 #include "impeller/renderer/context.h"
 #include "impeller/renderer/pipeline_descriptor.h"
 #include "impeller/scene/shaders/geometry.vert.h"
@@ -46,22 +48,19 @@
 
   std::shared_ptr<Context> GetContext() const;
 
+  std::shared_ptr<Texture> GetPlaceholderTexture() const;
+
   std::shared_ptr<Pipeline<PipelineDescriptor>> GetUnlitPipeline(
       SceneContextOptions opts) const {
     return GetPipeline(unlit_pipeline_, opts);
   }
 
  private:
-  std::shared_ptr<Context> context_;
-
   template <class T>
   using Variants = std::unordered_map<SceneContextOptions,
                                       std::unique_ptr<T>,
                                       SceneContextOptions::Hash,
                                       SceneContextOptions::Equal>;
-
-  mutable Variants<UnlitPipeline> unlit_pipeline_;
-
   template <class TypedPipeline>
   std::shared_ptr<Pipeline<PipelineDescriptor>> GetPipeline(
       Variants<TypedPipeline>& container,
@@ -91,7 +90,13 @@
     return variant_pipeline;
   }
 
+  std::shared_ptr<Context> context_;
+  mutable Variants<UnlitPipeline> unlit_pipeline_;
+
   bool is_valid_ = false;
+  // A 1x1 opaque white texture that can be used as a placeholder binding.
+  // Available for the lifetime of the scene context
+  std::shared_ptr<Texture> placeholder_texture_;
 
   FML_DISALLOW_COPY_AND_ASSIGN(SceneContext);
 };
diff --git a/impeller/scene/scene_encoder.cc b/impeller/scene/scene_encoder.cc
index 456a86d..fbf7e49 100644
--- a/impeller/scene/scene_encoder.cc
+++ b/impeller/scene/scene_encoder.cc
@@ -4,24 +4,71 @@
 
 #include "flutter/fml/macros.h"
 
-#include "fml/logging.h"
+#include "flutter/fml/logging.h"
+#include "impeller/renderer/command.h"
 #include "impeller/renderer/render_target.h"
+#include "impeller/scene/scene_context.h"
 #include "impeller/scene/scene_encoder.h"
+#include "impeller/scene/shaders/geometry.vert.h"
 
 namespace impeller {
 namespace scene {
 
 SceneEncoder::SceneEncoder() = default;
 
+void SceneEncoder::Add(const SceneCommand& command) {
+  // TODO(bdero): Manage multi-pass translucency ordering.
+  commands_.push_back(command);
+}
+
+static void EncodeCommand(const SceneContext& scene_context,
+                          RenderPass& render_pass,
+                          const SceneCommand& scene_command) {
+  auto& host_buffer = render_pass.GetTransientsBuffer();
+
+  Command cmd;
+  cmd.label = scene_command.label;
+  cmd.stencil_reference =
+      0;  // TODO(bdero): Configurable stencil ref per-command.
+
+  cmd.BindVertices(scene_command.geometry->GetVertexBuffer(
+      *scene_context.GetContext()->GetResourceAllocator()));
+
+  cmd.pipeline =
+      scene_command.material->GetPipeline(scene_context, render_pass);
+  scene_command.material->BindToCommand(scene_context, host_buffer, cmd);
+
+  GeometryVertexShader::VertInfo info;
+  info.mvp = scene_command.transform;
+  GeometryVertexShader::BindVertInfo(cmd, host_buffer.EmplaceUniform(info));
+
+  render_pass.AddCommand(std::move(cmd));
+}
+
 std::shared_ptr<CommandBuffer> SceneEncoder::BuildSceneCommandBuffer(
-    Context& context,
+    const SceneContext& scene_context,
     const RenderTarget& render_target) const {
-  auto command_buffer = context.CreateCommandBuffer();
+  auto command_buffer = scene_context.GetContext()->CreateCommandBuffer();
   if (!command_buffer) {
     FML_LOG(ERROR) << "Failed to create command buffer.";
     return nullptr;
   }
 
+  auto render_pass = command_buffer->CreateRenderPass(render_target);
+  if (!render_pass) {
+    FML_LOG(ERROR) << "Failed to create render pass.";
+    return nullptr;
+  }
+
+  for (auto& command : commands_) {
+    EncodeCommand(scene_context, *render_pass, command);
+  }
+
+  if (!render_pass->EncodeCommands()) {
+    FML_LOG(ERROR) << "Failed to encode render pass commands.";
+    return nullptr;
+  }
+
   return command_buffer;
 }
 
diff --git a/impeller/scene/scene_encoder.h b/impeller/scene/scene_encoder.h
index 2b8f15c..e09be57 100644
--- a/impeller/scene/scene_encoder.h
+++ b/impeller/scene/scene_encoder.h
@@ -5,24 +5,39 @@
 #pragma once
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "flutter/fml/macros.h"
-
 #include "impeller/renderer/command_buffer.h"
+#include "impeller/scene/geometry.h"
+#include "impeller/scene/material.h"
 
 namespace impeller {
 namespace scene {
 
 class Scene;
 
+struct SceneCommand {
+  std::string label;
+  Matrix transform;
+  Geometry* geometry;
+  Material* material;
+};
+
 class SceneEncoder {
+ public:
+  void Add(const SceneCommand& command);
+
  private:
   SceneEncoder();
 
   std::shared_ptr<CommandBuffer> BuildSceneCommandBuffer(
-      Context& context,
+      const SceneContext& scene_context,
       const RenderTarget& render_target) const;
 
+  std::vector<SceneCommand> commands_;
+
   friend Scene;
 
   FML_DISALLOW_COPY_AND_ASSIGN(SceneEncoder);
diff --git a/impeller/scene/static_mesh_entity.cc b/impeller/scene/static_mesh_entity.cc
index 493d342..e234e95 100644
--- a/impeller/scene/static_mesh_entity.cc
+++ b/impeller/scene/static_mesh_entity.cc
@@ -3,8 +3,11 @@
 // found in the LICENSE file.
 
 #include "impeller/scene/static_mesh_entity.h"
+
 #include <memory>
+
 #include "impeller/scene/material.h"
+#include "impeller/scene/scene_encoder.h"
 
 namespace impeller {
 namespace scene {
@@ -23,6 +26,13 @@
 // |SceneEntity|
 bool StaticMeshEntity::OnRender(SceneEncoder& encoder,
                                 const Camera& camera) const {
+  SceneCommand command = {
+      .label = "Static Mesh",
+      .transform = GetGlobalTransform(),
+      .geometry = geometry_.get(),
+      .material = material_.get(),
+  };
+  encoder.Add(command);
   return true;
 }