diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp
index 4f3c19cdc9..846d7ca3f2 100644
--- a/gfx/drivers_shader/shader_vulkan.cpp
+++ b/gfx/drivers_shader/shader_vulkan.cpp
@@ -708,11 +708,12 @@ bool Pass::init_pipeline_layout()
    }
 
    // Semantic textures.
-   for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
+   for (auto &semantic : reflection.semantic_textures)
    {
-      if (reflection.semantic_texture_mask & (1u << i))
+      for (auto &texture : semantic)
       {
-         auto &texture = reflection.semantic_textures[i];
+         if (!texture.texture)
+            continue;
 
          VkShaderStageFlags stages = 0;
          if (texture.stage_mask & SLANG_STAGE_VERTEX_MASK)
@@ -1069,17 +1070,17 @@ void Pass::set_texture(VkDescriptorSet set, unsigned binding,
 void Pass::set_semantic_texture(VkDescriptorSet set,
       slang_texture_semantic semantic, const Texture &texture)
 {
-   if (reflection.semantic_texture_mask & (1u << semantic))
-      set_texture(set, reflection.semantic_textures[semantic].binding, texture);
+   if (reflection.semantic_textures[semantic][0].texture)
+      set_texture(set, reflection.semantic_textures[semantic][0].binding, texture);
 }
 
 void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic,
       unsigned width, unsigned height)
 {
-   if (data && (reflection.semantic_texture_ubo_mask & (1 << semantic)))
+   if (data && reflection.semantic_textures[semantic][0].uniform)
    {
       build_vec4(
-            reinterpret_cast<float *>(data + reflection.semantic_textures[semantic].ubo_offset),
+            reinterpret_cast<float *>(data + reflection.semantic_textures[semantic][0].ubo_offset),
             width,
             height);
    }
@@ -1088,7 +1089,7 @@ void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic sem
 void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic,
       unsigned width, unsigned height)
 {
-   if (data && (reflection.semantic_ubo_mask & (1 << semantic)))
+   if (data && reflection.semantics[semantic].uniform)
    {
       build_vec4(
             reinterpret_cast<float *>(data + reflection.semantics[semantic].ubo_offset),
@@ -1108,7 +1109,7 @@ void Pass::build_semantic_texture(VkDescriptorSet set, uint8_t *buffer,
 void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer,
       const float *mvp, const Texture &original, const Texture &source)
 {
-   if (buffer && (reflection.semantic_ubo_mask & (1u << SLANG_SEMANTIC_MVP)))
+   if (buffer && reflection.semantics[SLANG_SEMANTIC_MVP].uniform)
    {
       size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].ubo_offset;
       if (mvp)
diff --git a/gfx/drivers_shader/slang_reflection.cpp b/gfx/drivers_shader/slang_reflection.cpp
index 82f64467dc..ff27c81540 100644
--- a/gfx/drivers_shader/slang_reflection.cpp
+++ b/gfx/drivers_shader/slang_reflection.cpp
@@ -22,14 +22,46 @@
 using namespace std;
 using namespace spir2cross;
 
+static bool slang_texture_semantic_is_array(slang_texture_semantic sem)
+{
+   switch (sem)
+   {
+      case SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY:
+      case SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT:
+      case SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK:
+         return true;
+
+      default:
+         return false;
+   }
+}
+
+slang_reflection::slang_reflection()
+{
+   for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
+   {
+      semantic_textures[i].resize(
+            slang_texture_semantic_is_array(static_cast<slang_texture_semantic>(i))
+            ? 0 : 1);
+   }
+}
+
 static const char *texture_semantic_names[] = {
    "Original",
    "Source",
+   "OriginalHistory",
+   "PassOutput",
+   "PassFeedback",
+   nullptr
 };
 
 static const char *texture_semantic_uniform_names[] = {
    "OriginalSize",
    "SourceSize",
+   "OriginalHistorySize",
+   "PassOutputSize",
+   "PassFeedbackSize",
+   nullptr
 };
 
 static const char *semantic_uniform_names[] = {
@@ -38,28 +70,45 @@ static const char *semantic_uniform_names[] = {
    "FinalViewportSize",
 };
 
-static slang_texture_semantic slang_name_to_texture_semantic(const string &name)
+static slang_texture_semantic slang_name_to_texture_semantic_array(const string &name, const char **names,
+      unsigned *index)
 {
    unsigned i = 0;
-   for (auto n : texture_semantic_names)
+   while (*names)
    {
-      if (name == n)
-         return static_cast<slang_texture_semantic>(i);
+      auto n = *names;
+      auto semantic = static_cast<slang_texture_semantic>(i);
+      if (slang_texture_semantic_is_array(semantic))
+      {
+         size_t baselen = strlen(n);
+         int cmp = strncmp(n, name.c_str(), baselen);
+
+         if (cmp == 0)
+         {
+            *index = strtoul(name.c_str() + baselen, nullptr, 0);
+            return semantic;
+         }
+      }
+      else if (name == n)
+      {
+         *index = 0;
+         return semantic;
+      }
+
       i++;
+      names++;
    }
    return SLANG_INVALID_TEXTURE_SEMANTIC;
 }
 
-static slang_texture_semantic slang_uniform_name_to_texture_semantic(const string &name)
+static slang_texture_semantic slang_name_to_texture_semantic(const string &name, unsigned *index)
 {
-   unsigned i = 0;
-   for (auto n : texture_semantic_uniform_names)
-   {
-      if (name == n)
-         return static_cast<slang_texture_semantic>(i);
-      i++;
-   }
-   return SLANG_INVALID_TEXTURE_SEMANTIC;
+   return slang_name_to_texture_semantic_array(name, texture_semantic_names, index);
+}
+
+static slang_texture_semantic slang_uniform_name_to_texture_semantic(const string &name, unsigned *index)
+{
+   return slang_name_to_texture_semantic_array(name, texture_semantic_uniform_names, index);
 }
 
 static slang_semantic slang_uniform_name_to_semantic(const string &name)
@@ -75,50 +124,64 @@ static slang_semantic slang_uniform_name_to_semantic(const string &name)
    return SLANG_INVALID_SEMANTIC;
 }
 
-static bool set_ubo_texture_offset(slang_reflection *reflection, slang_texture_semantic semantic,
+template <typename T>
+static void resize_minimum(T &vec, unsigned minimum)
+{
+   if (vec.size() < minimum)
+      vec.resize(minimum);
+}
+
+static bool set_ubo_texture_offset(slang_reflection *reflection,
+      slang_texture_semantic semantic, unsigned index,
       size_t offset)
 {
-   if (reflection->semantic_texture_ubo_mask & (1u << semantic))
+   resize_minimum(reflection->semantic_textures[semantic], index + 1);
+   auto &sem = reflection->semantic_textures[semantic][index];
+
+   if (sem.uniform)
    {
-      if (reflection->semantic_textures[semantic].ubo_offset != offset)
+      if (sem.ubo_offset != offset)
       {
-         RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s (%u vs. %u).\n",
+         RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s #%u (%u vs. %u).\n",
                texture_semantic_uniform_names[semantic],
-               unsigned(reflection->semantic_textures[semantic].ubo_offset),
+               index,
+               unsigned(sem.ubo_offset),
                unsigned(offset));
          return false;
       }
    }
-   reflection->semantic_texture_ubo_mask |= 1u << semantic;
-   reflection->semantic_textures[semantic].ubo_offset = offset;
+   sem.uniform = true;
+   sem.ubo_offset = offset;
    return true;
 }
 
 static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic,
       size_t offset, unsigned num_components)
 {
-   if (reflection->semantic_ubo_mask & (1u << semantic))
+   auto &sem = reflection->semantics[semantic];
+
+   if (sem.uniform)
    {
-      if (reflection->semantics[semantic].ubo_offset != offset)
+      if (sem.ubo_offset != offset)
       {
          RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s (%u vs. %u).\n",
                semantic_uniform_names[semantic],
-               unsigned(reflection->semantics[semantic].ubo_offset),
+               unsigned(sem.ubo_offset),
                unsigned(offset));
          return false;
       }
 
-      if (reflection->semantics[semantic].num_components != num_components)
+      if (sem.num_components != num_components)
       {
          RARCH_ERR("[slang]: Vertex and fragment have different components for same semantic %s (%u vs. %u).\n",
                semantic_uniform_names[semantic],
-               unsigned(reflection->semantics[semantic].num_components),
+               unsigned(sem.num_components),
                unsigned(num_components));
       }
    }
-   reflection->semantic_ubo_mask |= 1u << semantic;
-   reflection->semantics[semantic].ubo_offset = offset;
-   reflection->semantics[semantic].num_components = num_components;
+   sem.uniform = true;
+   sem.ubo_offset = offset;
+   sem.num_components = num_components;
    return true;
 }
 
@@ -157,8 +220,10 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r
    {
       auto &name = compiler.get_member_name(resource.type_id, range.index);
       auto &type = compiler.get_type(compiler.get_type(resource.type_id).member_types[range.index]);
-      slang_semantic sem = slang_uniform_name_to_semantic(name);
-      slang_texture_semantic tex_sem = slang_uniform_name_to_texture_semantic(name);
+
+      unsigned tex_sem_index = 0;
+      auto sem = slang_uniform_name_to_semantic(name);
+      auto tex_sem = slang_uniform_name_to_texture_semantic(name, &tex_sem_index);
 
       if (sem != SLANG_INVALID_SEMANTIC)
       {
@@ -179,7 +244,7 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r
             return false;
          }
 
-         if (!set_ubo_texture_offset(reflection, tex_sem, range.offset))
+         if (!set_ubo_texture_offset(reflection, tex_sem, tex_sem_index, range.offset))
             return false;
       }
       else
@@ -346,7 +411,8 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
       }
       binding_mask |= 1 << binding;
 
-      slang_texture_semantic index = slang_name_to_texture_semantic(texture.name);
+      unsigned array_index = 0;
+      slang_texture_semantic index = slang_name_to_texture_semantic(texture.name, &array_index);
       
       if (index == SLANG_INVALID_TEXTURE_SEMANTIC)
       {
@@ -354,17 +420,25 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
          return false;
       }
 
-      auto &semantic = reflection->semantic_textures[index];
+      resize_minimum(reflection->semantic_textures[index], array_index + 1);
+      auto &semantic = reflection->semantic_textures[index][array_index];
       semantic.binding = binding;
       semantic.stage_mask = SLANG_STAGE_FRAGMENT_MASK;
-      reflection->semantic_texture_mask |= 1 << index;
+      semantic.texture = true;
    }
 
    RARCH_LOG("[slang]: Reflection\n");
    RARCH_LOG("[slang]:   Textures:\n");
    for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
-      if (reflection->semantic_texture_mask & (1u << i))
-         RARCH_LOG("[slang]:      %s\n", texture_semantic_names[i]);
+   {
+      unsigned index = 0;
+      for (auto &sem : reflection->semantic_textures[i])
+      {
+         if (sem.texture)
+            RARCH_LOG("[slang]:      %s (#%u)\n", texture_semantic_names[i], index);
+         index++;
+      }
+   }
 
    RARCH_LOG("[slang]:\n");
    RARCH_LOG("[slang]:   Uniforms (Vertex: %s, Fragment: %s):\n",
@@ -372,7 +446,7 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
          reflection->ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no");
    for (unsigned i = 0; i < SLANG_NUM_SEMANTICS; i++)
    {
-      if (reflection->semantic_ubo_mask & (1u << i))
+      if (reflection->semantics[i].uniform)
       {
          RARCH_LOG("[slang]:      %s (Offset: %u)\n", semantic_uniform_names[i],
                unsigned(reflection->semantics[i].ubo_offset));
@@ -381,10 +455,16 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
 
    for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
    {
-      if (reflection->semantic_texture_ubo_mask & (1u << i))
+      unsigned index = 0;
+      for (auto &sem : reflection->semantic_textures[i])
       {
-         RARCH_LOG("[slang]:      %s (Offset: %u)\n", texture_semantic_uniform_names[i],
-               unsigned(reflection->semantic_textures[i].ubo_offset));
+         if (sem.uniform)
+         {
+            RARCH_LOG("[slang]:      %s (#%u) (Offset: %u)\n", texture_semantic_uniform_names[i],
+                  index,
+                  unsigned(sem.ubo_offset));
+         }
+         index++;
       }
    }
 
diff --git a/gfx/drivers_shader/slang_reflection.hpp b/gfx/drivers_shader/slang_reflection.hpp
index d5f5a90617..94228b2757 100644
--- a/gfx/drivers_shader/slang_reflection.hpp
+++ b/gfx/drivers_shader/slang_reflection.hpp
@@ -22,9 +22,31 @@
 // Textures with built-in meaning.
 enum slang_texture_semantic
 {
+   // The input texture to the filter chain.
+   // Canonical name: "Original".
    SLANG_TEXTURE_SEMANTIC_ORIGINAL = 0,
+
+   // The output from pass N - 1 if executing pass N, or ORIGINAL
+   // if pass #0 is executed.
+   // Canonical name: "Source".
    SLANG_TEXTURE_SEMANTIC_SOURCE = 1,
 
+   // The original inputs with a history back in time.
+   // Canonical name: "OriginalHistory#", e.g. "OriginalHistory2" <- Two frames back.
+   // "OriginalHistory0" is an alias for SEMANTIC_ORIGINAL.
+   // Size name: "OriginalHistorySize#".
+   SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY = 2,
+
+   // The output from pass #N, where pass #0 is the first pass.
+   // Canonical name: "PassOutput#", e.g. "PassOutput3".
+   // Size name: "PassOutputSize#".
+   SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT = 3,
+
+   // The output from pass #N, one frame ago where pass #0 is the first pass.
+   // It is not valid to use the pass feedback from a pass which is not offscreen.
+   // Canonical name: "PassFeedback#", e.g. "PassFeedback2".
+   SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK = 4,
+
    SLANG_NUM_TEXTURE_SEMANTICS,
    SLANG_INVALID_TEXTURE_SEMANTIC = -1
 };
@@ -44,6 +66,8 @@ enum slang_stage
    SLANG_STAGE_VERTEX_MASK = 1 << 0,
    SLANG_STAGE_FRAGMENT_MASK = 1 << 1
 };
+
+// Vulkan minimum limit.
 #define SLANG_NUM_BINDINGS 16
 
 struct slang_texture_semantic_meta
@@ -51,27 +75,28 @@ struct slang_texture_semantic_meta
    size_t ubo_offset = 0;
    unsigned binding = 0;
    uint32_t stage_mask = 0;
+
+   bool texture = false;
+   bool uniform = false;
 };
 
 struct slang_semantic_meta
 {
    size_t ubo_offset = 0;
    unsigned num_components = 0;
+   bool uniform = false;
 };
 
 struct slang_reflection
 {
-   slang_reflection() = default;
+   slang_reflection();
 
    size_t ubo_size = 0;
    unsigned ubo_binding = 0;
    uint32_t ubo_stage_mask = 0;
 
-   slang_texture_semantic_meta semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS];
+   std::vector<slang_texture_semantic_meta> semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS];
    slang_semantic_meta semantics[SLANG_NUM_SEMANTICS];
-   uint32_t semantic_texture_mask = 0;
-   uint32_t semantic_texture_ubo_mask = 0;
-   uint32_t semantic_ubo_mask = 0;
 };
 
 bool slang_reflect_spirv(const std::vector<uint32_t> &vertex,