diff --git a/src/xenia/gpu/vulkan/pipeline_cache.cc b/src/xenia/gpu/vulkan/pipeline_cache.cc
index e09931833..ec6c28eac 100644
--- a/src/xenia/gpu/vulkan/pipeline_cache.cc
+++ b/src/xenia/gpu/vulkan/pipeline_cache.cc
@@ -50,8 +50,8 @@ VulkanShader* PipelineCache::LoadShader(ShaderType shader_type,
   // Always create the shader and stash it away.
   // We need to track it even if it fails translation so we know not to try
   // again.
-  VulkanShader* shader =
-      new VulkanShader(shader_type, data_hash, host_address, dword_count);
+  VulkanShader* shader = new VulkanShader(device_, shader_type, data_hash,
+                                          host_address, dword_count);
   shader_map_.insert({data_hash, shader});
 
   // Perform translation.
@@ -85,6 +85,8 @@ VulkanShader* PipelineCache::LoadShader(ShaderType shader_type,
 
 bool PipelineCache::ConfigurePipeline(VkCommandBuffer command_buffer,
                                       VkRenderPass render_pass,
+                                      VulkanShader* vertex_shader,
+                                      VulkanShader* pixel_shader,
                                       PrimitiveType primitive_type) {
   return false;
 }
diff --git a/src/xenia/gpu/vulkan/pipeline_cache.h b/src/xenia/gpu/vulkan/pipeline_cache.h
index 56727e67a..00e36ef12 100644
--- a/src/xenia/gpu/vulkan/pipeline_cache.h
+++ b/src/xenia/gpu/vulkan/pipeline_cache.h
@@ -42,15 +42,12 @@ class PipelineCache {
   // in the command buffer is issued at this time.
   // Returns whether the pipeline could be successfully created.
   bool ConfigurePipeline(VkCommandBuffer command_buffer,
-                         VkRenderPass render_pass,
+                         VkRenderPass render_pass, VulkanShader* vertex_shader,
+                         VulkanShader* pixel_shader,
                          PrimitiveType primitive_type);
 
   // Currently configured pipeline layout, if any.
   VkPipelineLayout current_pipeline_layout() const { return nullptr; }
-  // Currently configured vertex shader, if any.
-  VulkanShader* current_vertex_shader() const { return nullptr; }
-  // Currently configured pixel shader, if any.
-  VulkanShader* current_pixel_shader() const { return nullptr; }
 
   // Clears all cached content.
   void ClearCache();
diff --git a/src/xenia/gpu/vulkan/render_cache.cc b/src/xenia/gpu/vulkan/render_cache.cc
index fef05f11f..de25fb2e3 100644
--- a/src/xenia/gpu/vulkan/render_cache.cc
+++ b/src/xenia/gpu/vulkan/render_cache.cc
@@ -28,12 +28,22 @@ RenderCache::RenderCache(RegisterFile* register_file,
 
 RenderCache::~RenderCache() = default;
 
-VkRenderPass RenderCache::BeginRenderPass(VkCommandBuffer command_buffer) {
-  return nullptr;
+VkRenderPass RenderCache::BeginRenderPass(VkCommandBuffer command_buffer,
+                                          VulkanShader* vertex_shader,
+                                          VulkanShader* pixel_shader) {
+  assert_null(current_command_buffer_);
+  current_command_buffer_ = command_buffer;
+
+  // Lookup or construct a render pass compatible with our current state.
+  VkRenderPass render_pass = nullptr;
+
+  return render_pass;
 }
 
 void RenderCache::EndRenderPass() {
-  //
+  assert_not_null(current_command_buffer_);
+  auto command_buffer = current_command_buffer_;
+  current_command_buffer_ = nullptr;
 }
 
 void RenderCache::ClearCache() {
diff --git a/src/xenia/gpu/vulkan/render_cache.h b/src/xenia/gpu/vulkan/render_cache.h
index fb7c84e6a..ceeea2a07 100644
--- a/src/xenia/gpu/vulkan/render_cache.h
+++ b/src/xenia/gpu/vulkan/render_cache.h
@@ -12,6 +12,7 @@
 
 #include "xenia/gpu/register_file.h"
 #include "xenia/gpu/shader.h"
+#include "xenia/gpu/vulkan/vulkan_shader.h"
 #include "xenia/gpu/xenos.h"
 #include "xenia/ui/vulkan/vulkan.h"
 #include "xenia/ui/vulkan/vulkan_device.h"
@@ -20,15 +21,29 @@ namespace xe {
 namespace gpu {
 namespace vulkan {
 
-// Configures and caches pipelines based on render state.
-// This is responsible for properly setting all state required for a draw
-// including shaders, various blend/etc options, and input configuration.
+// Manages the virtualized EDRAM and the render target cache.
+//
+// On the 360 the render target is an opaque block of memory in EDRAM that's
+// only accessible via resolves. We use this to our advantage to simulate
+// something like it as best we can by having a shared backing memory with
+// a multitude of views for each tile location in EDRAM.
+//
+// This allows us to have the same base address write to the same memory
+// regardless of framebuffer format. Resolving then uses whatever format the
+// resolve requests straight from the backing memory.
 class RenderCache {
  public:
   RenderCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device);
   ~RenderCache();
 
-  VkRenderPass BeginRenderPass(VkCommandBuffer command_buffer);
+  // Begins a render pass targeting the state-specified framebuffer formats.
+  // The command buffer will be transitioned into the render pass phase.
+  VkRenderPass BeginRenderPass(VkCommandBuffer command_buffer,
+                               VulkanShader* vertex_shader,
+                               VulkanShader* pixel_shader);
+
+  // Ends the current render pass.
+  // The command buffer will be transitioned out of the render pass phase.
   void EndRenderPass();
 
   // Clears all cached content.
diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h
index 3545fb72d..3f18a7be1 100644
--- a/src/xenia/gpu/vulkan/texture_cache.h
+++ b/src/xenia/gpu/vulkan/texture_cache.h
@@ -20,9 +20,7 @@ namespace xe {
 namespace gpu {
 namespace vulkan {
 
-// Configures and caches pipelines based on render state.
-// This is responsible for properly setting all state required for a draw
-// including shaders, various blend/etc options, and input configuration.
+//
 class TextureCache {
  public:
   TextureCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device);
diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc
index 6490de44a..8047bd202 100644
--- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc
+++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc
@@ -175,6 +175,16 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type,
     return IssueCopy();
   }
 
+  // Shaders will have already been defined by previous loads.
+  // We need the to do just about anything so validate here.
+  auto vertex_shader = static_cast<VulkanShader*>(active_vertex_shader());
+  auto pixel_shader = static_cast<VulkanShader*>(active_pixel_shader());
+  if (!vertex_shader || !vertex_shader->is_valid() || !pixel_shader ||
+      !pixel_shader->is_valid()) {
+    // Skipped because we can't understand the shader.
+    return true;
+  }
+
   // TODO(benvanik): bigger batches.
   command_buffer_pool_->BeginBatch();
   VkCommandBuffer command_buffer = command_buffer_pool_->AcquireEntry();
@@ -188,7 +198,8 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type,
 
   // Begin the render pass.
   // This will setup our framebuffer and begin the pass in the command buffer.
-  VkRenderPass render_pass = render_cache_->BeginRenderPass(command_buffer);
+  VkRenderPass render_pass = render_cache_->BeginRenderPass(
+      command_buffer, vertex_shader, pixel_shader);
   if (!render_pass) {
     return false;
   }
@@ -197,14 +208,13 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type,
   // This encodes all render state (blend, depth, etc), our shader stages,
   // and our vertex input layout.
   if (!pipeline_cache_->ConfigurePipeline(command_buffer, render_pass,
+                                          vertex_shader, pixel_shader,
                                           primitive_type)) {
     render_cache_->EndRenderPass();
     return false;
   }
 
   // Upload the constants the shaders require.
-  auto vertex_shader = pipeline_cache_->current_vertex_shader();
-  auto pixel_shader = pipeline_cache_->current_pixel_shader();
   auto vertex_constant_offset = buffer_cache_->UploadConstantRegisters(
       vertex_shader->constant_register_map());
   auto pixel_constant_offset = buffer_cache_->UploadConstantRegisters(
diff --git a/src/xenia/gpu/vulkan/vulkan_gpu_flags.h b/src/xenia/gpu/vulkan/vulkan_gpu_flags.h
index c78637a47..b5a00c74a 100644
--- a/src/xenia/gpu/vulkan/vulkan_gpu_flags.h
+++ b/src/xenia/gpu/vulkan/vulkan_gpu_flags.h
@@ -12,4 +12,6 @@
 
 #include <gflags/gflags.h>
 
+#define FINE_GRAINED_DRAW_SCOPES 1
+
 #endif  // XENIA_GPU_VULKAN_VULKAN_GPU_FLAGS_H_
diff --git a/src/xenia/gpu/vulkan/vulkan_shader.cc b/src/xenia/gpu/vulkan/vulkan_shader.cc
index 8624480a3..b3c72abf3 100644
--- a/src/xenia/gpu/vulkan/vulkan_shader.cc
+++ b/src/xenia/gpu/vulkan/vulkan_shader.cc
@@ -9,20 +9,47 @@
 
 #include "xenia/gpu/vulkan/vulkan_shader.h"
 
+#include "xenia/base/assert.h"
 #include "xenia/base/logging.h"
 #include "xenia/base/math.h"
+#include "xenia/ui/vulkan/vulkan_util.h"
 
 namespace xe {
 namespace gpu {
 namespace vulkan {
 
-VulkanShader::VulkanShader(ShaderType shader_type, uint64_t data_hash,
-                           const uint32_t* dword_ptr, uint32_t dword_count)
-    : Shader(shader_type, data_hash, dword_ptr, dword_count) {}
+using xe::ui::vulkan::CheckResult;
 
-VulkanShader::~VulkanShader() = default;
+VulkanShader::VulkanShader(VkDevice device, ShaderType shader_type,
+                           uint64_t data_hash, const uint32_t* dword_ptr,
+                           uint32_t dword_count)
+    : Shader(shader_type, data_hash, dword_ptr, dword_count), device_(device) {}
 
-bool VulkanShader::Prepare() { return true; }
+VulkanShader::~VulkanShader() {
+  if (shader_module_) {
+    vkDestroyShaderModule(device_, shader_module_, nullptr);
+    shader_module_ = nullptr;
+  }
+}
+
+bool VulkanShader::Prepare() {
+  assert_null(shader_module_);
+  assert_true(is_valid());
+
+  // Create the shader module.
+  VkShaderModuleCreateInfo shader_info;
+  shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+  shader_info.pNext = nullptr;
+  shader_info.flags = 0;
+  shader_info.codeSize = translated_binary_.size();
+  shader_info.pCode =
+      reinterpret_cast<const uint32_t*>(translated_binary_.data());
+  auto err =
+      vkCreateShaderModule(device_, &shader_info, nullptr, &shader_module_);
+  CheckResult(err, "vkCreateShaderModule");
+
+  return true;
+}
 
 }  // namespace vulkan
 }  // namespace gpu
diff --git a/src/xenia/gpu/vulkan/vulkan_shader.h b/src/xenia/gpu/vulkan/vulkan_shader.h
index cc1d51e2a..97dbd5822 100644
--- a/src/xenia/gpu/vulkan/vulkan_shader.h
+++ b/src/xenia/gpu/vulkan/vulkan_shader.h
@@ -21,15 +21,17 @@ namespace vulkan {
 
 class VulkanShader : public Shader {
  public:
-  VulkanShader(ShaderType shader_type, uint64_t data_hash,
+  VulkanShader(VkDevice device, ShaderType shader_type, uint64_t data_hash,
                const uint32_t* dword_ptr, uint32_t dword_count);
   ~VulkanShader() override;
 
+  // Available only if the shader is_valid and has been prepared.
   VkShaderModule shader_module() const { return shader_module_; }
 
   bool Prepare();
 
  private:
+   VkDevice device_ = nullptr;
   VkShaderModule shader_module_ = nullptr;
 };