[Impeller] Wire image sampler descriptors for Vulkan (#36641)

diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h
index 2de845d..f399084 100644
--- a/impeller/compiler/code_gen_template.h
+++ b/impeller/compiler/code_gen_template.h
@@ -107,6 +107,8 @@
     "{{sampled_image.name}}",      // name
     {{sampled_image.ext_res_0}}u,  // texture
     {{sampled_image.ext_res_1}}u,  // sampler
+    {{sampled_image.binding}}u,    // binding
+    {{sampled_image.set}}u,        // set
   };
   static ShaderMetadata kMetadata{{camel_case(sampled_image.name)}};
 {% endfor %}
diff --git a/impeller/renderer/backend/vulkan/allocator_vk.cc b/impeller/renderer/backend/vulkan/allocator_vk.cc
index 720c310..de0d338 100644
--- a/impeller/renderer/backend/vulkan/allocator_vk.cc
+++ b/impeller/renderer/backend/vulkan/allocator_vk.cc
@@ -26,7 +26,7 @@
                          const vk::Instance& instance,
                          PFN_vkGetInstanceProcAddr get_instance_proc_address,
                          PFN_vkGetDeviceProcAddr get_device_proc_address)
-    : context_(context) {
+    : context_(context), device_(logical_device) {
   VmaVulkanFunctions proc_table = {};
   proc_table.vkGetInstanceProcAddr = get_instance_proc_address;
   proc_table.vkGetDeviceProcAddr = get_device_proc_address;
@@ -100,6 +100,22 @@
     return nullptr;
   }
 
+  vk::ImageViewCreateInfo view_create_info = {};
+  view_create_info.image = img;
+  view_create_info.viewType = vk::ImageViewType::e2D;
+  view_create_info.format = image_create_info.format;
+  view_create_info.subresourceRange.aspectMask =
+      vk::ImageAspectFlagBits::eColor;
+  view_create_info.subresourceRange.levelCount = image_create_info.mipLevels;
+  view_create_info.subresourceRange.layerCount = image_create_info.arrayLayers;
+
+  auto img_view_res = device_.createImageView(view_create_info);
+  if (img_view_res.result != vk::Result::eSuccess) {
+    VALIDATION_LOG << "Unable to create an image view: "
+                   << vk::to_string(img_view_res.result);
+    return nullptr;
+  }
+
   auto texture_info = std::make_unique<TextureInfoVK>(TextureInfoVK{
       .backing_type = TextureBackingTypeVK::kAllocatedTexture,
       .allocated_texture =
@@ -108,6 +124,7 @@
               .allocation = allocation,
               .allocation_info = allocation_info,
               .image = img,
+              .image_view = img_view_res.value,
           },
   });
   return std::make_shared<TextureVK>(desc, &context_, std::move(texture_info));
diff --git a/impeller/renderer/backend/vulkan/allocator_vk.h b/impeller/renderer/backend/vulkan/allocator_vk.h
index a5ac445..5225845 100644
--- a/impeller/renderer/backend/vulkan/allocator_vk.h
+++ b/impeller/renderer/backend/vulkan/allocator_vk.h
@@ -23,6 +23,7 @@
 
   VmaAllocator allocator_ = {};
   ContextVK& context_;
+  vk::Device device_;
   bool is_valid_ = false;
 
   AllocatorVK(ContextVK& context,
diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc
index 4beb8c1..1179f2d 100644
--- a/impeller/renderer/backend/vulkan/render_pass_vk.cc
+++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc
@@ -13,9 +13,13 @@
 #include "impeller/renderer/backend/vulkan/device_buffer_vk.h"
 #include "impeller/renderer/backend/vulkan/formats_vk.h"
 #include "impeller/renderer/backend/vulkan/pipeline_vk.h"
+#include "impeller/renderer/backend/vulkan/sampler_vk.h"
 #include "impeller/renderer/backend/vulkan/surface_producer_vk.h"
 #include "impeller/renderer/backend/vulkan/texture_vk.h"
+#include "impeller/renderer/sampler.h"
 #include "impeller/renderer/shader_types.h"
+#include "vulkan/vulkan_enums.hpp"
+#include "vulkan/vulkan_structs.hpp"
 
 namespace impeller {
 
@@ -77,52 +81,10 @@
   const uint32_t frame_num = tex_info.frame_num;
 
   // layout transition.
-  {
-    auto pool = command_buffer_.getPool();
-    vk::CommandBufferAllocateInfo alloc_info =
-        vk::CommandBufferAllocateInfo()
-            .setCommandPool(pool)
-            .setLevel(vk::CommandBufferLevel::ePrimary)
-            .setCommandBufferCount(1);
-    auto cmd_buf_res = device_.allocateCommandBuffersUnique(alloc_info);
-    if (cmd_buf_res.result != vk::Result::eSuccess) {
-      VALIDATION_LOG << "Failed to allocate command buffer: "
-                     << vk::to_string(cmd_buf_res.result);
-      return false;
-    }
-    auto transition_cmd = std::move(cmd_buf_res.value[0]);
-
-    vk::CommandBufferBeginInfo begin_info;
-    auto res = transition_cmd->begin(begin_info);
-
-    vk::ImageMemoryBarrier barrier =
-        vk::ImageMemoryBarrier()
-            .setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentRead)
-            .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
-            .setOldLayout(vk::ImageLayout::eUndefined)
-            .setNewLayout(vk::ImageLayout::eColorAttachmentOptimal)
-            .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
-            .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
-            .setImage(tex_info.swapchain_image->GetImage())
-            .setSubresourceRange(
-                vk::ImageSubresourceRange()
-                    .setAspectMask(vk::ImageAspectFlagBits::eColor)
-                    .setBaseMipLevel(0)
-                    .setLevelCount(1)
-                    .setBaseArrayLayer(0)
-                    .setLayerCount(1));
-    transition_cmd->pipelineBarrier(
-        vk::PipelineStageFlagBits::eColorAttachmentOutput,
-        vk::PipelineStageFlagBits::eColorAttachmentOutput, {}, nullptr, nullptr,
-        barrier);
-
-    res = transition_cmd->end();
-    if (res != vk::Result::eSuccess) {
-      VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
-      return false;
-    }
-
-    surface_producer_->QueueCommandBuffer(frame_num, std::move(transition_cmd));
+  if (!TransitionImageLayout(frame_num, tex_info.swapchain_image->GetImage(),
+                             vk::ImageLayout::eUndefined,
+                             vk::ImageLayout::eColorAttachmentOptimal)) {
+    return false;
   }
 
   vk::ClearValue clear_value;
@@ -162,7 +124,7 @@
       continue;
     }
 
-    if (!EncodeCommand(context, command)) {
+    if (!EncodeCommand(frame_num, context, command)) {
       return false;
     }
   }
@@ -188,14 +150,16 @@
   return false;
 }
 
-bool RenderPassVK::EncodeCommand(const Context& context,
+bool RenderPassVK::EncodeCommand(uint32_t frame_num,
+                                 const Context& context,
                                  const Command& command) const {
   SetViewportAndScissor(command);
 
   auto& pipeline_vk = PipelineVK::Cast(*command.pipeline);
   PipelineCreateInfoVK* pipeline_create_info = pipeline_vk.GetCreateInfo();
 
-  if (!AllocateAndBindDescriptorSets(context, command, pipeline_create_info)) {
+  if (!AllocateAndBindDescriptorSets(frame_num, context, command,
+                                     pipeline_create_info)) {
     return false;
   }
 
@@ -243,6 +207,7 @@
 }
 
 bool RenderPassVK::AllocateAndBindDescriptorSets(
+    uint32_t frame_num,
     const Context& context,
     const Command& command,
     PipelineCreateInfoVK* pipeline_create_info) const {
@@ -269,13 +234,15 @@
   }
 
   auto desc_sets = desc_sets_res.value;
-  bool update_vertex_descriptors = UpdateDescriptorSets(
-      "vertex_bindings", command.vertex_bindings, allocator, desc_sets[0]);
+  bool update_vertex_descriptors =
+      UpdateDescriptorSets(frame_num, "vertex_bindings",
+                           command.vertex_bindings, allocator, desc_sets[0]);
   if (!update_vertex_descriptors) {
     return false;
   }
-  bool update_frag_descriptors = UpdateDescriptorSets(
-      "fragment_bindings", command.fragment_bindings, allocator, desc_sets[0]);
+  bool update_frag_descriptors =
+      UpdateDescriptorSets(frame_num, "fragment_bindings",
+                           command.fragment_bindings, allocator, desc_sets[0]);
   if (!update_frag_descriptors) {
     return false;
   }
@@ -285,12 +252,15 @@
   return true;
 }
 
-bool RenderPassVK::UpdateDescriptorSets(const char* label,
+bool RenderPassVK::UpdateDescriptorSets(uint32_t frame_num,
+                                        const char* label,
                                         const Bindings& bindings,
                                         Allocator& allocator,
                                         vk::DescriptorSet desc_set) const {
   std::vector<vk::WriteDescriptorSet> writes;
   std::vector<vk::DescriptorBufferInfo> buffer_infos;
+  std::vector<vk::DescriptorImageInfo> image_infos;
+
   for (const auto& [buffer_index, view] : bindings.buffers) {
     const auto& buffer_view = view.resource.buffer;
 
@@ -330,6 +300,42 @@
     writes.push_back(setWrite);
   }
 
+  for (const auto& [index, sampler_handle] : bindings.samplers) {
+    if (bindings.textures.find(index) == bindings.textures.end()) {
+      VALIDATION_LOG << "Missing texture for sampler: " << index;
+      return false;
+    }
+
+    const auto& texture_vk =
+        TextureVK::Cast(*bindings.textures.at(index).resource);
+
+    const Sampler& sampler = *sampler_handle.resource;
+    const SamplerVK& sampler_vk = SamplerVK::Cast(sampler);
+
+    const SampledImageSlot& slot = bindings.sampled_images.at(index);
+
+    if (!TransitionImageLayout(frame_num, texture_vk.GetImage(),
+                               vk::ImageLayout::eUndefined,
+                               vk::ImageLayout::eGeneral)) {
+      return false;
+    }
+
+    vk::DescriptorImageInfo desc_image_info;
+    desc_image_info.setImageLayout(vk::ImageLayout::eGeneral);
+    desc_image_info.setSampler(sampler_vk.GetSamplerVK());
+    desc_image_info.setImageView(texture_vk.GetImageView());
+    image_infos.push_back(desc_image_info);
+
+    vk::WriteDescriptorSet setWrite;
+    setWrite.setDstSet(desc_set);
+    setWrite.setDstBinding(slot.binding);
+    setWrite.setDescriptorCount(1);
+    setWrite.setDescriptorType(vk::DescriptorType::eCombinedImageSampler);
+    setWrite.setPImageInfo(&image_infos.back());
+
+    writes.push_back(setWrite);
+  }
+
   std::array<vk::CopyDescriptorSet, 0> copies;
   device_.updateDescriptorSets(writes, copies);
 
@@ -374,4 +380,56 @@
   return std::move(res.value);
 }
 
+bool RenderPassVK::TransitionImageLayout(uint32_t frame_num,
+                                         vk::Image image,
+                                         vk::ImageLayout layout_old,
+                                         vk::ImageLayout layout_new) const {
+  auto pool = command_buffer_.getPool();
+  vk::CommandBufferAllocateInfo alloc_info =
+      vk::CommandBufferAllocateInfo()
+          .setCommandPool(pool)
+          .setLevel(vk::CommandBufferLevel::ePrimary)
+          .setCommandBufferCount(1);
+  auto cmd_buf_res = device_.allocateCommandBuffersUnique(alloc_info);
+  if (cmd_buf_res.result != vk::Result::eSuccess) {
+    VALIDATION_LOG << "Failed to allocate command buffer: "
+                   << vk::to_string(cmd_buf_res.result);
+    return false;
+  }
+  auto transition_cmd = std::move(cmd_buf_res.value[0]);
+
+  vk::CommandBufferBeginInfo begin_info;
+  auto res = transition_cmd->begin(begin_info);
+
+  vk::ImageMemoryBarrier barrier =
+      vk::ImageMemoryBarrier()
+          .setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentRead)
+          .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
+          .setOldLayout(layout_old)
+          .setNewLayout(layout_new)
+          .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
+          .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
+          .setImage(image)
+          .setSubresourceRange(
+              vk::ImageSubresourceRange()
+                  .setAspectMask(vk::ImageAspectFlagBits::eColor)
+                  .setBaseMipLevel(0)
+                  .setLevelCount(1)
+                  .setBaseArrayLayer(0)
+                  .setLayerCount(1));
+  transition_cmd->pipelineBarrier(
+      vk::PipelineStageFlagBits::eColorAttachmentOutput,
+      vk::PipelineStageFlagBits::eColorAttachmentOutput, {}, nullptr, nullptr,
+      barrier);
+
+  res = transition_cmd->end();
+  if (res != vk::Result::eSuccess) {
+    VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
+    return false;
+  }
+
+  surface_producer_->QueueCommandBuffer(frame_num, std::move(transition_cmd));
+  return true;
+}
+
 }  // namespace impeller
diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h
index c6c551d..3530cbb 100644
--- a/impeller/renderer/backend/vulkan/render_pass_vk.h
+++ b/impeller/renderer/backend/vulkan/render_pass_vk.h
@@ -12,6 +12,7 @@
 #include "impeller/renderer/command.h"
 #include "impeller/renderer/render_pass.h"
 #include "impeller/renderer/render_target.h"
+#include "vulkan/vulkan_enums.hpp"
 #include "vulkan/vulkan_structs.hpp"
 
 namespace impeller {
@@ -48,16 +49,20 @@
   // |RenderPass|
   bool OnEncodeCommands(const Context& context) const override;
 
-  bool EncodeCommand(const Context& context, const Command& command) const;
+  bool EncodeCommand(uint32_t frame_num,
+                     const Context& context,
+                     const Command& command) const;
 
   bool AllocateAndBindDescriptorSets(
+      uint32_t frame_num,
       const Context& context,
       const Command& command,
       PipelineCreateInfoVK* pipeline_create_info) const;
 
   bool EndCommandBuffer(uint32_t frame_num);
 
-  bool UpdateDescriptorSets(const char* label,
+  bool UpdateDescriptorSets(uint32_t frame_num,
+                            const char* label,
                             const Bindings& bindings,
                             Allocator& allocator,
                             vk::DescriptorSet desc_set) const;
@@ -67,6 +72,11 @@
   vk::Framebuffer CreateFrameBuffer(
       const WrappedTextureInfoVK& wrapped_texture_info) const;
 
+  bool TransitionImageLayout(uint32_t frame_num,
+                             vk::Image image,
+                             vk::ImageLayout layout_old,
+                             vk::ImageLayout layout_new) const;
+
   FML_DISALLOW_COPY_AND_ASSIGN(RenderPassVK);
 };
 
diff --git a/impeller/renderer/backend/vulkan/sampler_vk.cc b/impeller/renderer/backend/vulkan/sampler_vk.cc
index 161fa57..6212bed 100644
--- a/impeller/renderer/backend/vulkan/sampler_vk.cc
+++ b/impeller/renderer/backend/vulkan/sampler_vk.cc
@@ -7,8 +7,12 @@
 namespace impeller {
 SamplerVK::~SamplerVK() {}
 
+vk::Sampler SamplerVK::GetSamplerVK() const {
+  return sampler_.get();
+}
+
 SamplerVK::SamplerVK(SamplerDescriptor desc, vk::UniqueSampler sampler)
-    : Sampler(desc), sampler_(std::move(sampler)) {
+    : Sampler(std::move(desc)), sampler_(std::move(sampler)) {
   is_valid_ = true;
 }
 
diff --git a/impeller/renderer/backend/vulkan/sampler_vk.h b/impeller/renderer/backend/vulkan/sampler_vk.h
index 15d0820..4c11bd3 100644
--- a/impeller/renderer/backend/vulkan/sampler_vk.h
+++ b/impeller/renderer/backend/vulkan/sampler_vk.h
@@ -21,6 +21,8 @@
   // |Sampler|
   ~SamplerVK() override;
 
+  vk::Sampler GetSamplerVK() const;
+
  private:
   friend SamplerLibraryVK;
 
diff --git a/impeller/renderer/backend/vulkan/texture_vk.cc b/impeller/renderer/backend/vulkan/texture_vk.cc
index 66ad2cf..829cef6 100644
--- a/impeller/renderer/backend/vulkan/texture_vk.cc
+++ b/impeller/renderer/backend/vulkan/texture_vk.cc
@@ -81,6 +81,17 @@
   return texture_info_->backing_type == TextureBackingTypeVK::kWrappedTexture;
 }
 
+vk::ImageView TextureVK::GetImageView() const {
+  switch (texture_info_->backing_type) {
+    case TextureBackingTypeVK::kUnknownType:
+      return nullptr;
+    case TextureBackingTypeVK::kAllocatedTexture:
+      return texture_info_->allocated_texture.image_view;
+    case TextureBackingTypeVK::kWrappedTexture:
+      return texture_info_->wrapped_texture.swapchain_image->GetImageView();
+  }
+}
+
 vk::Image TextureVK::GetImage() const {
   switch (texture_info_->backing_type) {
     case TextureBackingTypeVK::kUnknownType:
diff --git a/impeller/renderer/backend/vulkan/texture_vk.h b/impeller/renderer/backend/vulkan/texture_vk.h
index 632e9e7..918ed5f 100644
--- a/impeller/renderer/backend/vulkan/texture_vk.h
+++ b/impeller/renderer/backend/vulkan/texture_vk.h
@@ -29,6 +29,7 @@
   VmaAllocation allocation = nullptr;
   VmaAllocationInfo allocation_info = {};
   VkImage image = nullptr;
+  VkImageView image_view = nullptr;
 };
 
 struct TextureInfoVK {
@@ -52,6 +53,8 @@
 
   vk::Image GetImage() const;
 
+  vk::ImageView GetImageView() const;
+
   TextureInfoVK* GetTextureInfo() const;
 
  private:
diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc
index cac5108..e5380a8 100644
--- a/impeller/renderer/command.cc
+++ b/impeller/renderer/command.cc
@@ -108,9 +108,11 @@
   switch (stage) {
     case ShaderStage::kVertex:
       vertex_bindings.samplers[slot.sampler_index] = {&metadata, sampler};
+      vertex_bindings.sampled_images[slot.sampler_index] = slot;
       return true;
     case ShaderStage::kFragment:
       fragment_bindings.samplers[slot.sampler_index] = {&metadata, sampler};
+      fragment_bindings.sampled_images[slot.sampler_index] = slot;
       return true;
     case ShaderStage::kCompute:
       VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h
index c804c0e..3b3d1c0 100644
--- a/impeller/renderer/command.h
+++ b/impeller/renderer/command.h
@@ -42,6 +42,7 @@
 
 struct Bindings {
   std::map<size_t, ShaderUniformSlot> uniforms;
+  std::map<size_t, SampledImageSlot> sampled_images;
   std::map<size_t, BufferResource> buffers;
   std::map<size_t, TextureResource> textures;
   std::map<size_t, SamplerResource> samplers;
diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h
index 3afb01b..3a9bab2 100644
--- a/impeller/renderer/shader_types.h
+++ b/impeller/renderer/shader_types.h
@@ -114,6 +114,8 @@
   const char* name;
   size_t texture_index;
   size_t sampler_index;
+  size_t binding;
+  size_t set;
 
   constexpr bool HasTexture() const { return texture_index < 32u; }