[Impeller] dont use dynamic shader metadata path for precompiled shaders. (#56827)

Should fix https://github.com/flutter/flutter/issues/159520 ~~but I still need to check locally.~~ Seems to do the trick.

All cmd bindings were copying the shader metadata, which meant allocating/de-allocating a lot of strings per draw.
diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h
index 6c6b90e..15246a2 100644
--- a/impeller/compiler/code_gen_template.h
+++ b/impeller/compiler/code_gen_template.h
@@ -159,7 +159,7 @@
 {% endfor %}) {
     return {{ proto.args.0.argument_name }}.BindResource({% for arg in proto.args %}
   {% if loop.is_first %}
-{{to_shader_stage(shader_stage)}}, {{ proto.descriptor_type }}, kResource{{ proto.name }}, kMetadata{{ proto.name }}, {% else %}
+{{to_shader_stage(shader_stage)}}, {{ proto.descriptor_type }}, kResource{{ proto.name }}, &kMetadata{{ proto.name }}, {% else %}
 std::move({{ arg.argument_name }}){% if not loop.is_last %}, {% endif %}
   {% endif %}
   {% endfor %});
diff --git a/impeller/core/resource_binder.h b/impeller/core/resource_binder.h
index 8f25ff0..120d8c9 100644
--- a/impeller/core/resource_binder.h
+++ b/impeller/core/resource_binder.h
@@ -26,13 +26,13 @@
   virtual bool BindResource(ShaderStage stage,
                             DescriptorType type,
                             const ShaderUniformSlot& slot,
-                            const ShaderMetadata& metadata,
+                            const ShaderMetadata* metadata,
                             BufferView view) = 0;
 
   virtual bool BindResource(ShaderStage stage,
                             DescriptorType type,
                             const SampledImageSlot& slot,
-                            const ShaderMetadata& metadata,
+                            const ShaderMetadata* metadata,
                             std::shared_ptr<const Texture> texture,
                             const std::unique_ptr<const Sampler>& sampler) = 0;
 };
diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc
index e099df6..dc2c2fe 100644
--- a/impeller/entity/contents/runtime_effect_contents.cc
+++ b/impeller/entity/contents/runtime_effect_contents.cc
@@ -50,9 +50,9 @@
   }
 }
 
-static std::shared_ptr<ShaderMetadata> MakeShaderMetadata(
+static std::unique_ptr<ShaderMetadata> MakeShaderMetadata(
     const RuntimeUniformDescription& uniform) {
-  auto metadata = std::make_shared<ShaderMetadata>();
+  std::unique_ptr<ShaderMetadata> metadata = std::make_unique<ShaderMetadata>();
   metadata->name = uniform.name;
   metadata->members.emplace_back(ShaderStructMemberMetadata{
       .type = GetShaderType(uniform.type),
@@ -206,7 +206,7 @@
     size_t buffer_offset = 0;
 
     for (const auto& uniform : runtime_stage_->GetUniforms()) {
-      std::shared_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
+      std::unique_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
       switch (uniform.type) {
         case kSampledImage: {
           // Sampler uniforms are ordered in the IPLR according to their
@@ -237,9 +237,9 @@
           ShaderUniformSlot uniform_slot;
           uniform_slot.name = uniform.name.c_str();
           uniform_slot.ext_res_0 = uniform.location;
-          pass.BindResource(ShaderStage::kFragment,
-                            DescriptorType::kUniformBuffer, uniform_slot,
-                            metadata, std::move(buffer_view));
+          pass.BindDynamicResource(ShaderStage::kFragment,
+                                   DescriptorType::kUniformBuffer, uniform_slot,
+                                   std::move(metadata), std::move(buffer_view));
           buffer_index++;
           buffer_offset += uniform.GetSize();
           break;
@@ -274,14 +274,14 @@
               sizeof(float) * uniform_buffer.size(), alignment);
           pass.BindResource(ShaderStage::kFragment,
                             DescriptorType::kUniformBuffer, uniform_slot,
-                            ShaderMetadata{}, std::move(buffer_view));
+                            nullptr, std::move(buffer_view));
         }
       }
     }
 
     size_t sampler_index = 0;
     for (const auto& uniform : runtime_stage_->GetUniforms()) {
-      std::shared_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
+      std::unique_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
 
       switch (uniform.type) {
         case kSampledImage: {
@@ -296,9 +296,9 @@
           image_slot.name = uniform.name.c_str();
           image_slot.binding = uniform.binding;
           image_slot.texture_index = uniform.location - minimum_sampler_index;
-          pass.BindResource(ShaderStage::kFragment,
-                            DescriptorType::kSampledImage, image_slot,
-                            *metadata, input.texture, sampler);
+          pass.BindDynamicResource(ShaderStage::kFragment,
+                                   DescriptorType::kSampledImage, image_slot,
+                                   std::move(metadata), input.texture, sampler);
 
           sampler_index++;
           break;
diff --git a/impeller/entity/contents/test/recording_render_pass.cc b/impeller/entity/contents/test/recording_render_pass.cc
index 1eedfdb..822ce71 100644
--- a/impeller/entity/contents/test/recording_render_pass.cc
+++ b/impeller/entity/contents/test/recording_render_pass.cc
@@ -108,7 +108,7 @@
 bool RecordingRenderPass::BindResource(ShaderStage stage,
                                        DescriptorType type,
                                        const ShaderUniformSlot& slot,
-                                       const ShaderMetadata& metadata,
+                                       const ShaderMetadata* metadata,
                                        BufferView view) {
   pending_.BindResource(stage, type, slot, metadata, view);
   if (delegate_) {
@@ -118,25 +118,41 @@
 }
 
 // |RenderPass|
-bool RecordingRenderPass::BindResource(
+bool RecordingRenderPass::BindDynamicResource(
     ShaderStage stage,
     DescriptorType type,
     const ShaderUniformSlot& slot,
-    const std::shared_ptr<const ShaderMetadata>& metadata,
+    std::unique_ptr<ShaderMetadata> metadata,
     BufferView view) {
-  pending_.BindResource(stage, type, slot, metadata, view);
+  pending_.BindResource(stage, type, slot, metadata.get(), view);
   if (delegate_) {
-    return delegate_->BindResource(stage, type, slot, metadata, view);
+    return delegate_->BindDynamicResource(stage, type, slot,
+                                          std::move(metadata), view);
   }
   return true;
 }
 
 // |RenderPass|
+bool RecordingRenderPass::BindDynamicResource(
+    ShaderStage stage,
+    DescriptorType type,
+    const SampledImageSlot& slot,
+    std::unique_ptr<ShaderMetadata> metadata,
+    std::shared_ptr<const Texture> texture,
+    const std::unique_ptr<const Sampler>& sampler) {
+  pending_.BindResource(stage, type, slot, metadata.get(), texture, sampler);
+  if (delegate_) {
+    return delegate_->BindDynamicResource(
+        stage, type, slot, std::move(metadata), texture, sampler);
+  }
+  return true;
+}
+
 bool RecordingRenderPass::BindResource(
     ShaderStage stage,
     DescriptorType type,
     const SampledImageSlot& slot,
-    const ShaderMetadata& metadata,
+    const ShaderMetadata* metadata,
     std::shared_ptr<const Texture> texture,
     const std::unique_ptr<const Sampler>& sampler) {
   pending_.BindResource(stage, type, slot, metadata, texture, sampler);
diff --git a/impeller/entity/contents/test/recording_render_pass.h b/impeller/entity/contents/test/recording_render_pass.h
index 13ee909..3fb9b34 100644
--- a/impeller/entity/contents/test/recording_render_pass.h
+++ b/impeller/entity/contents/test/recording_render_pass.h
@@ -46,29 +46,36 @@
   // |RenderPass|
   fml::Status Draw() override;
 
-  // |RenderPass|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const ShaderUniformSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     BufferView view) override;
 
-  // |RenderPass|
-  bool BindResource(ShaderStage stage,
-                    DescriptorType type,
-                    const ShaderUniformSlot& slot,
-                    const std::shared_ptr<const ShaderMetadata>& metadata,
-                    BufferView view) override;
-
-  // |RenderPass|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const SampledImageSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     std::shared_ptr<const Texture> texture,
                     const std::unique_ptr<const Sampler>& sampler) override;
 
   // |RenderPass|
+  bool BindDynamicResource(ShaderStage stage,
+                           DescriptorType type,
+                           const ShaderUniformSlot& slot,
+                           std::unique_ptr<ShaderMetadata> metadata,
+                           BufferView view) override;
+
+  // |RenderPass|
+  bool BindDynamicResource(
+      ShaderStage stage,
+      DescriptorType type,
+      const SampledImageSlot& slot,
+      std::unique_ptr<ShaderMetadata> metadata,
+      std::shared_ptr<const Texture> texture,
+      const std::unique_ptr<const Sampler>& sampler) override;
+
+  // |RenderPass|
   void OnSetLabel(std::string_view label) override;
 
   // |RenderPass|
diff --git a/impeller/renderer/backend/metal/compute_pass_mtl.h b/impeller/renderer/backend/metal/compute_pass_mtl.h
index 64ff69a..e44975e 100644
--- a/impeller/renderer/backend/metal/compute_pass_mtl.h
+++ b/impeller/renderer/backend/metal/compute_pass_mtl.h
@@ -51,14 +51,14 @@
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const ShaderUniformSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     BufferView view) override;
 
   // |ComputePass|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const SampledImageSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     std::shared_ptr<const Texture> texture,
                     const std::unique_ptr<const Sampler>& sampler) override;
 
diff --git a/impeller/renderer/backend/metal/compute_pass_mtl.mm b/impeller/renderer/backend/metal/compute_pass_mtl.mm
index ee667fc..ccd51e6 100644
--- a/impeller/renderer/backend/metal/compute_pass_mtl.mm
+++ b/impeller/renderer/backend/metal/compute_pass_mtl.mm
@@ -82,7 +82,7 @@
 bool ComputePassMTL::BindResource(ShaderStage stage,
                                   DescriptorType type,
                                   const ShaderUniformSlot& slot,
-                                  const ShaderMetadata& metadata,
+                                  const ShaderMetadata* metadata,
                                   BufferView view) {
   if (!view.GetBuffer()) {
     return false;
@@ -109,7 +109,7 @@
     ShaderStage stage,
     DescriptorType type,
     const SampledImageSlot& slot,
-    const ShaderMetadata& metadata,
+    const ShaderMetadata* metadata,
     std::shared_ptr<const Texture> texture,
     const std::unique_ptr<const Sampler>& sampler) {
   if (!sampler || !texture->IsValid()) {
diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h
index ddef8a7..476cde4 100644
--- a/impeller/renderer/backend/metal/render_pass_mtl.h
+++ b/impeller/renderer/backend/metal/render_pass_mtl.h
@@ -99,24 +99,33 @@
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const ShaderUniformSlot& slot,
-                    const ShaderMetadata& metadata,
-                    BufferView view) override;
-
-  // |RenderPass|
-  bool BindResource(ShaderStage stage,
-                    DescriptorType type,
-                    const ShaderUniformSlot& slot,
-                    const std::shared_ptr<const ShaderMetadata>& metadata,
+                    const ShaderMetadata* metadata,
                     BufferView view) override;
 
   // |RenderPass|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const SampledImageSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     std::shared_ptr<const Texture> texture,
                     const std::unique_ptr<const Sampler>& sampler) override;
 
+  // |RenderPass|
+  bool BindDynamicResource(ShaderStage stage,
+                           DescriptorType type,
+                           const ShaderUniformSlot& slot,
+                           std::unique_ptr<ShaderMetadata> metadata,
+                           BufferView view) override;
+
+  // |RenderPass|
+  bool BindDynamicResource(
+      ShaderStage stage,
+      DescriptorType type,
+      const SampledImageSlot& slot,
+      std::unique_ptr<ShaderMetadata> metadata,
+      std::shared_ptr<const Texture> texture,
+      const std::unique_ptr<const Sampler>& sampler) override;
+
   RenderPassMTL(const RenderPassMTL&) = delete;
 
   RenderPassMTL& operator=(const RenderPassMTL&) = delete;
diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm
index 80cf59b..6a4ba15 100644
--- a/impeller/renderer/backend/metal/render_pass_mtl.mm
+++ b/impeller/renderer/backend/metal/render_pass_mtl.mm
@@ -385,17 +385,17 @@
 bool RenderPassMTL::BindResource(ShaderStage stage,
                                  DescriptorType type,
                                  const ShaderUniformSlot& slot,
-                                 const ShaderMetadata& metadata,
+                                 const ShaderMetadata* metadata,
                                  BufferView view) {
   return Bind(pass_bindings_, stage, slot.ext_res_0, view);
 }
 
 // |RenderPass|
-bool RenderPassMTL::BindResource(
+bool RenderPassMTL::BindDynamicResource(
     ShaderStage stage,
     DescriptorType type,
     const ShaderUniformSlot& slot,
-    const std::shared_ptr<const ShaderMetadata>& metadata,
+    std::unique_ptr<ShaderMetadata> metadata,
     BufferView view) {
   return Bind(pass_bindings_, stage, slot.ext_res_0, view);
 }
@@ -405,7 +405,20 @@
     ShaderStage stage,
     DescriptorType type,
     const SampledImageSlot& slot,
-    const ShaderMetadata& metadata,
+    const ShaderMetadata* metadata,
+    std::shared_ptr<const Texture> texture,
+    const std::unique_ptr<const Sampler>& sampler) {
+  if (!texture) {
+    return false;
+  }
+  return Bind(pass_bindings_, stage, slot.texture_index, sampler, *texture);
+}
+
+bool RenderPassMTL::BindDynamicResource(
+    ShaderStage stage,
+    DescriptorType type,
+    const SampledImageSlot& slot,
+    std::unique_ptr<ShaderMetadata> metadata,
     std::shared_ptr<const Texture> texture,
     const std::unique_ptr<const Sampler>& sampler) {
   if (!texture) {
diff --git a/impeller/renderer/backend/vulkan/compute_pass_vk.cc b/impeller/renderer/backend/vulkan/compute_pass_vk.cc
index 402d4bd..f4b61cd 100644
--- a/impeller/renderer/backend/vulkan/compute_pass_vk.cc
+++ b/impeller/renderer/backend/vulkan/compute_pass_vk.cc
@@ -134,7 +134,7 @@
 bool ComputePassVK::BindResource(ShaderStage stage,
                                  DescriptorType type,
                                  const ShaderUniformSlot& slot,
-                                 const ShaderMetadata& metadata,
+                                 const ShaderMetadata* metadata,
                                  BufferView view) {
   return BindResource(slot.binding, type, view);
 }
@@ -144,7 +144,7 @@
     ShaderStage stage,
     DescriptorType type,
     const SampledImageSlot& slot,
-    const ShaderMetadata& metadata,
+    const ShaderMetadata* metadata,
     std::shared_ptr<const Texture> texture,
     const std::unique_ptr<const Sampler>& sampler) {
   if (bound_image_offset_ >= kMaxBindings) {
diff --git a/impeller/renderer/backend/vulkan/compute_pass_vk.h b/impeller/renderer/backend/vulkan/compute_pass_vk.h
index b281775..78aac09 100644
--- a/impeller/renderer/backend/vulkan/compute_pass_vk.h
+++ b/impeller/renderer/backend/vulkan/compute_pass_vk.h
@@ -71,14 +71,14 @@
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const ShaderUniformSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     BufferView view) override;
 
   // |ResourceBinder|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const SampledImageSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     std::shared_ptr<const Texture> texture,
                     const std::unique_ptr<const Sampler>& sampler) override;
 
diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc
index 3808d84..3453233 100644
--- a/impeller/renderer/backend/vulkan/render_pass_vk.cc
+++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc
@@ -544,17 +544,16 @@
 bool RenderPassVK::BindResource(ShaderStage stage,
                                 DescriptorType type,
                                 const ShaderUniformSlot& slot,
-                                const ShaderMetadata& metadata,
+                                const ShaderMetadata* metadata,
                                 BufferView view) {
   return BindResource(slot.binding, type, view);
 }
 
-bool RenderPassVK::BindResource(
-    ShaderStage stage,
-    DescriptorType type,
-    const ShaderUniformSlot& slot,
-    const std::shared_ptr<const ShaderMetadata>& metadata,
-    BufferView view) {
+bool RenderPassVK::BindDynamicResource(ShaderStage stage,
+                                       DescriptorType type,
+                                       const ShaderUniformSlot& slot,
+                                       std::unique_ptr<ShaderMetadata> metadata,
+                                       BufferView view) {
   return BindResource(slot.binding, type, view);
 }
 
@@ -593,10 +592,20 @@
   return true;
 }
 
+bool RenderPassVK::BindDynamicResource(
+    ShaderStage stage,
+    DescriptorType type,
+    const SampledImageSlot& slot,
+    std::unique_ptr<ShaderMetadata> metadata,
+    std::shared_ptr<const Texture> texture,
+    const std::unique_ptr<const Sampler>& sampler) {
+  return BindResource(stage, type, slot, nullptr, texture, sampler);
+}
+
 bool RenderPassVK::BindResource(ShaderStage stage,
                                 DescriptorType type,
                                 const SampledImageSlot& slot,
-                                const ShaderMetadata& metadata,
+                                const ShaderMetadata* metadata,
                                 std::shared_ptr<const Texture> texture,
                                 const std::unique_ptr<const Sampler>& sampler) {
   if (bound_buffer_offset_ >= kMaxBindings) {
diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h
index 61ca753..d652f23 100644
--- a/impeller/renderer/backend/vulkan/render_pass_vk.h
+++ b/impeller/renderer/backend/vulkan/render_pass_vk.h
@@ -99,24 +99,33 @@
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const ShaderUniformSlot& slot,
-                    const ShaderMetadata& metadata,
-                    BufferView view) override;
-
-  // |RenderPass|
-  bool BindResource(ShaderStage stage,
-                    DescriptorType type,
-                    const ShaderUniformSlot& slot,
-                    const std::shared_ptr<const ShaderMetadata>& metadata,
+                    const ShaderMetadata* metadata,
                     BufferView view) override;
 
   // |ResourceBinder|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const SampledImageSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     std::shared_ptr<const Texture> texture,
                     const std::unique_ptr<const Sampler>& sampler) override;
 
+  // |RenderPass|
+  bool BindDynamicResource(ShaderStage stage,
+                           DescriptorType type,
+                           const ShaderUniformSlot& slot,
+                           std::unique_ptr<ShaderMetadata> metadata,
+                           BufferView view) override;
+
+  // |RenderPass|
+  bool BindDynamicResource(
+      ShaderStage stage,
+      DescriptorType type,
+      const SampledImageSlot& slot,
+      std::unique_ptr<ShaderMetadata> metadata,
+      std::shared_ptr<const Texture> texture,
+      const std::unique_ptr<const Sampler>& sampler) override;
+
   bool BindResource(size_t binding, DescriptorType type, BufferView view);
 
   // |RenderPass|
diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc
index 0ca99cb..513f694 100644
--- a/impeller/renderer/command.cc
+++ b/impeller/renderer/command.cc
@@ -29,64 +29,35 @@
 bool Command::BindResource(ShaderStage stage,
                            DescriptorType type,
                            const ShaderUniformSlot& slot,
-                           const ShaderMetadata& metadata,
+                           const ShaderMetadata* metadata,
                            BufferView view) {
   FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex);
   if (!view) {
     return false;
   }
-
-  switch (stage) {
-    case ShaderStage::kVertex:
-      vertex_bindings.buffers.emplace_back(BufferAndUniformSlot{
-          .slot = slot, .view = BufferResource(metadata, std::move(view))});
-      return true;
-    case ShaderStage::kFragment:
-      fragment_bindings.buffers.emplace_back(BufferAndUniformSlot{
-          .slot = slot, .view = BufferResource(metadata, std::move(view))});
-      return true;
-    case ShaderStage::kCompute:
-      VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
-    case ShaderStage::kUnknown:
-      return false;
-  }
-
-  return false;
+  BufferResource resouce = BufferResource(metadata, std::move(view));
+  return BindBuffer(stage, slot, std::move(resouce));
 }
 
-bool Command::BindResource(
-    ShaderStage stage,
-    DescriptorType type,
-    const ShaderUniformSlot& slot,
-    const std::shared_ptr<const ShaderMetadata>& metadata,
-    BufferView view) {
+bool Command::BindDynamicResource(ShaderStage stage,
+                                  DescriptorType type,
+                                  const ShaderUniformSlot& slot,
+                                  std::unique_ptr<ShaderMetadata> metadata,
+                                  BufferView view) {
   FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex);
   if (!view) {
     return false;
   }
+  BufferResource resouce =
+      BufferResource::MakeDynamic(std::move(metadata), std::move(view));
 
-  switch (stage) {
-    case ShaderStage::kVertex:
-      vertex_bindings.buffers.emplace_back(BufferAndUniformSlot{
-          .slot = slot, .view = BufferResource(*metadata, std::move(view))});
-      return true;
-    case ShaderStage::kFragment:
-      fragment_bindings.buffers.emplace_back(BufferAndUniformSlot{
-          .slot = slot, .view = BufferResource(*metadata, std::move(view))});
-      return true;
-    case ShaderStage::kCompute:
-      VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
-    case ShaderStage::kUnknown:
-      return false;
-  }
-
-  return false;
+  return BindBuffer(stage, slot, std::move(resouce));
 }
 
 bool Command::BindResource(ShaderStage stage,
                            DescriptorType type,
                            const SampledImageSlot& slot,
-                           const ShaderMetadata& metadata,
+                           const ShaderMetadata* metadata,
                            std::shared_ptr<const Texture> texture,
                            const std::unique_ptr<const Sampler>& sampler) {
   if (!sampler) {
@@ -95,21 +66,40 @@
   if (!texture || !texture->IsValid()) {
     return false;
   }
+  TextureResource resource = TextureResource(metadata, std::move(texture));
+  return BindTexture(stage, slot, std::move(resource), sampler);
+}
+
+bool Command::BindDynamicResource(
+    ShaderStage stage,
+    DescriptorType type,
+    const SampledImageSlot& slot,
+    std::unique_ptr<ShaderMetadata> metadata,
+    std::shared_ptr<const Texture> texture,
+    const std::unique_ptr<const Sampler>& sampler) {
+  if (!sampler) {
+    return false;
+  }
+  if (!texture || !texture->IsValid()) {
+    return false;
+  }
+  TextureResource resource =
+      TextureResource::MakeDynamic(std::move(metadata), std::move(texture));
+  return BindTexture(stage, slot, std::move(resource), sampler);
+}
+
+bool Command::BindBuffer(ShaderStage stage,
+                         const ShaderUniformSlot& slot,
+                         BufferResource resource) {
+  BufferAndUniformSlot data =
+      BufferAndUniformSlot{.slot = slot, .view = std::move(resource)};
 
   switch (stage) {
     case ShaderStage::kVertex:
-      vertex_bindings.sampled_images.emplace_back(TextureAndSampler{
-          .slot = slot,
-          .texture = TextureResource(metadata, std::move(texture)),
-          .sampler = &sampler,
-      });
+      vertex_bindings.buffers.push_back(std::move(data));
       return true;
     case ShaderStage::kFragment:
-      fragment_bindings.sampled_images.emplace_back(TextureAndSampler{
-          .slot = slot,
-          .texture = TextureResource(metadata, std::move(texture)),
-          .sampler = &sampler,
-      });
+      fragment_bindings.buffers.push_back(std::move(data));
       return true;
     case ShaderStage::kCompute:
       VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
@@ -120,4 +110,28 @@
   return false;
 }
 
+bool Command::BindTexture(ShaderStage stage,
+                          const SampledImageSlot& slot,
+                          TextureResource resource,
+                          const std::unique_ptr<const Sampler>& sampler) {
+  TextureAndSampler data = TextureAndSampler{
+      .slot = slot,
+      .texture = std::move(resource),
+      .sampler = &sampler,
+  };
+
+  switch (stage) {
+    case ShaderStage::kVertex:
+      vertex_bindings.sampled_images.push_back(std::move(data));
+      return true;
+    case ShaderStage::kFragment:
+      fragment_bindings.sampled_images.push_back((std::move(data)));
+      return true;
+    case ShaderStage::kCompute:
+      VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
+    case ShaderStage::kUnknown:
+      return false;
+  }
+}
+
 }  // namespace impeller
diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h
index a63f52f..29e41bb 100644
--- a/impeller/renderer/command.h
+++ b/impeller/renderer/command.h
@@ -39,15 +39,19 @@
   Resource(const ShaderMetadata* metadata, ResourceType p_resource)
       : resource(p_resource), metadata_(metadata) {}
 
-  Resource(const ShaderMetadata& metadata, ResourceType p_resource)
-      : resource(p_resource),
-        dynamic_metadata_(std::make_unique<ShaderMetadata>(metadata)) {}
-
   const ShaderMetadata* GetMetadata() const {
     return dynamic_metadata_ ? dynamic_metadata_.get() : metadata_;
   }
 
+  static Resource MakeDynamic(std::unique_ptr<ShaderMetadata> metadata,
+                              ResourceType p_resource) {
+    return Resource(std::move(metadata), p_resource);
+  }
+
  private:
+  Resource(std::unique_ptr<ShaderMetadata> metadata, ResourceType p_resource)
+      : resource(p_resource), dynamic_metadata_(std::move(metadata)) {}
+
   // Static shader metadata (typically generated by ImpellerC).
   const ShaderMetadata* metadata_ = nullptr;
 
@@ -184,31 +188,41 @@
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const ShaderUniformSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     BufferView view) override;
 
-  bool BindResource(ShaderStage stage,
-                    DescriptorType type,
-                    const ShaderUniformSlot& slot,
-                    const std::shared_ptr<const ShaderMetadata>& metadata,
-                    BufferView view);
-
   // |ResourceBinder|
   bool BindResource(ShaderStage stage,
                     DescriptorType type,
                     const SampledImageSlot& slot,
-                    const ShaderMetadata& metadata,
+                    const ShaderMetadata* metadata,
                     std::shared_ptr<const Texture> texture,
                     const std::unique_ptr<const Sampler>& sampler) override;
 
+  bool BindDynamicResource(ShaderStage stage,
+                           DescriptorType type,
+                           const ShaderUniformSlot& slot,
+                           std::unique_ptr<ShaderMetadata> metadata,
+                           BufferView view);
+
+  bool BindDynamicResource(ShaderStage stage,
+                           DescriptorType type,
+                           const SampledImageSlot& slot,
+                           std::unique_ptr<ShaderMetadata> metadata,
+                           std::shared_ptr<const Texture> texture,
+                           const std::unique_ptr<const Sampler>& sampler);
+
   bool IsValid() const { return pipeline && pipeline->IsValid(); }
 
  private:
-  template <class T>
-  bool DoBindResource(ShaderStage stage,
-                      const ShaderUniformSlot& slot,
-                      T metadata,
-                      BufferView view);
+  bool BindBuffer(ShaderStage stage,
+                  const ShaderUniformSlot& slot,
+                  BufferResource resource);
+
+  bool BindTexture(ShaderStage stage,
+                   const SampledImageSlot& slot,
+                   TextureResource resource,
+                   const std::unique_ptr<const Sampler>& sampler);
 };
 
 }  // namespace impeller
diff --git a/impeller/renderer/render_pass.cc b/impeller/renderer/render_pass.cc
index 016a7a1..600d378 100644
--- a/impeller/renderer/render_pass.cc
+++ b/impeller/renderer/render_pass.cc
@@ -216,29 +216,40 @@
 bool RenderPass::BindResource(ShaderStage stage,
                               DescriptorType type,
                               const ShaderUniformSlot& slot,
-                              const ShaderMetadata& metadata,
+                              const ShaderMetadata* metadata,
                               BufferView view) {
   return pending_.BindResource(stage, type, slot, metadata, view);
 }
 
-bool RenderPass::BindResource(
-    ShaderStage stage,
-    DescriptorType type,
-    const ShaderUniformSlot& slot,
-    const std::shared_ptr<const ShaderMetadata>& metadata,
-    BufferView view) {
-  return pending_.BindResource(stage, type, slot, metadata, std::move(view));
-}
-
 // |ResourceBinder|
 bool RenderPass::BindResource(ShaderStage stage,
                               DescriptorType type,
                               const SampledImageSlot& slot,
-                              const ShaderMetadata& metadata,
+                              const ShaderMetadata* metadata,
                               std::shared_ptr<const Texture> texture,
                               const std::unique_ptr<const Sampler>& sampler) {
   return pending_.BindResource(stage, type, slot, metadata, std::move(texture),
                                sampler);
 }
 
+bool RenderPass::BindDynamicResource(ShaderStage stage,
+                                     DescriptorType type,
+                                     const ShaderUniformSlot& slot,
+                                     std::unique_ptr<ShaderMetadata> metadata,
+                                     BufferView view) {
+  return pending_.BindDynamicResource(stage, type, slot, std::move(metadata),
+                                      std::move(view));
+}
+
+bool RenderPass::BindDynamicResource(
+    ShaderStage stage,
+    DescriptorType type,
+    const SampledImageSlot& slot,
+    std::unique_ptr<ShaderMetadata> metadata,
+    std::shared_ptr<const Texture> texture,
+    const std::unique_ptr<const Sampler>& sampler) {
+  return pending_.BindDynamicResource(stage, type, slot, std::move(metadata),
+                                      std::move(texture), sampler);
+}
+
 }  // namespace impeller
diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h
index 0ada25e..dc590d3 100644
--- a/impeller/renderer/render_pass.h
+++ b/impeller/renderer/render_pass.h
@@ -181,25 +181,33 @@
   virtual bool BindResource(ShaderStage stage,
                             DescriptorType type,
                             const ShaderUniformSlot& slot,
-                            const ShaderMetadata& metadata,
+                            const ShaderMetadata* metadata,
                             BufferView view) override;
 
-  virtual bool BindResource(
-      ShaderStage stage,
-      DescriptorType type,
-      const ShaderUniformSlot& slot,
-      const std::shared_ptr<const ShaderMetadata>& metadata,
-      BufferView view);
-
   // |ResourceBinder|
   virtual bool BindResource(
       ShaderStage stage,
       DescriptorType type,
       const SampledImageSlot& slot,
-      const ShaderMetadata& metadata,
+      const ShaderMetadata* metadata,
       std::shared_ptr<const Texture> texture,
       const std::unique_ptr<const Sampler>& sampler) override;
 
+  /// @brief Bind with dynamically generated shader metadata.
+  virtual bool BindDynamicResource(
+      ShaderStage stage,
+      DescriptorType type,
+      const SampledImageSlot& slot,
+      std::unique_ptr<ShaderMetadata> metadata,
+      std::shared_ptr<const Texture> texture,
+      const std::unique_ptr<const Sampler>& sampler);
+
+  virtual bool BindDynamicResource(ShaderStage stage,
+                                   DescriptorType type,
+                                   const ShaderUniformSlot& slot,
+                                   std::unique_ptr<ShaderMetadata> metadata,
+                                   BufferView view);
+
   //----------------------------------------------------------------------------
   /// @brief      Encode the recorded commands to the underlying command buffer.
   ///
diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc
index 3987d82..fdbb493 100644
--- a/lib/gpu/render_pass.cc
+++ b/lib/gpu/render_pass.cc
@@ -177,28 +177,34 @@
   render_pass_->SetPipeline(GetOrCreatePipeline());
 
   for (const auto& [_, buffer] : vertex_uniform_bindings) {
-    render_pass_->BindResource(impeller::ShaderStage::kVertex,
-                               impeller::DescriptorType::kUniformBuffer,
-                               buffer.slot, *buffer.view.GetMetadata(),
-                               buffer.view.resource);
+    render_pass_->BindDynamicResource(
+        impeller::ShaderStage::kVertex,
+        impeller::DescriptorType::kUniformBuffer, buffer.slot,
+        std::make_unique<impeller::ShaderMetadata>(*buffer.view.GetMetadata()),
+        buffer.view.resource);
   }
   for (const auto& [_, texture] : vertex_texture_bindings) {
-    render_pass_->BindResource(impeller::ShaderStage::kVertex,
-                               impeller::DescriptorType::kSampledImage,
-                               texture.slot, *texture.texture.GetMetadata(),
-                               texture.texture.resource, *texture.sampler);
+    render_pass_->BindDynamicResource(
+        impeller::ShaderStage::kVertex, impeller::DescriptorType::kSampledImage,
+        texture.slot,
+        std::make_unique<impeller::ShaderMetadata>(
+            *texture.texture.GetMetadata()),
+        texture.texture.resource, *texture.sampler);
   }
   for (const auto& [_, buffer] : fragment_uniform_bindings) {
-    render_pass_->BindResource(impeller::ShaderStage::kFragment,
-                               impeller::DescriptorType::kUniformBuffer,
-                               buffer.slot, *buffer.view.GetMetadata(),
-                               buffer.view.resource);
+    render_pass_->BindDynamicResource(
+        impeller::ShaderStage::kFragment,
+        impeller::DescriptorType::kUniformBuffer, buffer.slot,
+        std::make_unique<impeller::ShaderMetadata>(*buffer.view.GetMetadata()),
+        buffer.view.resource);
   }
   for (const auto& [_, texture] : fragment_texture_bindings) {
-    render_pass_->BindResource(impeller::ShaderStage::kFragment,
-                               impeller::DescriptorType::kSampledImage,
-                               texture.slot, *texture.texture.GetMetadata(),
-                               texture.texture.resource, *texture.sampler);
+    render_pass_->BindDynamicResource(
+        impeller::ShaderStage::kFragment,
+        impeller::DescriptorType::kSampledImage, texture.slot,
+        std::make_unique<impeller::ShaderMetadata>(
+            *texture.texture.GetMetadata()),
+        texture.texture.resource, *texture.sampler);
   }
 
   render_pass_->SetVertexBuffer(vertex_buffer);