diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 0a59aa6593..eebe5d5691 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -35,49 +35,52 @@ struct TextureHandle {
     u32 sampler;
 };
 
-struct DescriptorLayoutTuple {
-    vk::DescriptorSetLayout descriptor_set_layout;
-    vk::PipelineLayout pipeline_layout;
-    vk::DescriptorUpdateTemplateKHR descriptor_update_template;
-};
-
 class DescriptorLayoutBuilder {
 public:
-    DescriptorLayoutTuple Create(const vk::Device& device) {
-        DescriptorLayoutTuple result;
-        if (!bindings.empty()) {
-            result.descriptor_set_layout = device.CreateDescriptorSetLayout({
-                .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
-                .pNext = nullptr,
-                .flags = 0,
-                .bindingCount = static_cast<u32>(bindings.size()),
-                .pBindings = bindings.data(),
-            });
+    DescriptorLayoutBuilder(const vk::Device& device_) : device{&device_} {}
+
+    vk::DescriptorSetLayout CreateDescriptorSetLayout() const {
+        if (bindings.empty()) {
+            return nullptr;
         }
-        result.pipeline_layout = device.CreatePipelineLayout({
+        return device->CreateDescriptorSetLayout({
+            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .bindingCount = static_cast<u32>(bindings.size()),
+            .pBindings = bindings.data(),
+        });
+    }
+
+    vk::DescriptorUpdateTemplateKHR CreateTemplate(VkDescriptorSetLayout descriptor_set_layout,
+                                                   VkPipelineLayout pipeline_layout) const {
+        if (entries.empty()) {
+            return nullptr;
+        }
+        return device->CreateDescriptorUpdateTemplateKHR({
+            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR,
+            .pNext = nullptr,
+            .flags = 0,
+            .descriptorUpdateEntryCount = static_cast<u32>(entries.size()),
+            .pDescriptorUpdateEntries = entries.data(),
+            .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
+            .descriptorSetLayout = descriptor_set_layout,
+            .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+            .pipelineLayout = pipeline_layout,
+            .set = 0,
+        });
+    }
+
+    vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const {
+        return device->CreatePipelineLayout({
             .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
             .pNext = nullptr,
             .flags = 0,
-            .setLayoutCount = result.descriptor_set_layout ? 1U : 0U,
-            .pSetLayouts = bindings.empty() ? nullptr : result.descriptor_set_layout.address(),
+            .setLayoutCount = descriptor_set_layout ? 1U : 0U,
+            .pSetLayouts = bindings.empty() ? nullptr : &descriptor_set_layout,
             .pushConstantRangeCount = 0,
             .pPushConstantRanges = nullptr,
         });
-        if (!entries.empty()) {
-            result.descriptor_update_template = device.CreateDescriptorUpdateTemplateKHR({
-                .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR,
-                .pNext = nullptr,
-                .flags = 0,
-                .descriptorUpdateEntryCount = static_cast<u32>(entries.size()),
-                .pDescriptorUpdateEntries = entries.data(),
-                .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
-                .descriptorSetLayout = *result.descriptor_set_layout,
-                .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
-                .pipelineLayout = *result.pipeline_layout,
-                .set = 0,
-            });
-        }
-        return result;
     }
 
     void Add(const Shader::Info& info, VkShaderStageFlags stage) {
@@ -113,6 +116,7 @@ private:
         offset += sizeof(DescriptorUpdateEntry);
     }
 
+    const vk::Device* device{};
     boost::container::small_vector<VkDescriptorSetLayoutBinding, 32> bindings;
     boost::container::small_vector<VkDescriptorUpdateTemplateEntryKHR, 32> entries;
     u32 binding{};
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index a444d55d38..7608578393 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -237,7 +237,7 @@ VkDescriptorSet VKComputePass::CommitDescriptorSet(
         return nullptr;
     }
     const VkDescriptorSet set = descriptor_allocator->Commit();
-    update_descriptor_queue.Send(*descriptor_template, set);
+    update_descriptor_queue.Send(descriptor_template.address(), set);
     return set;
 }
 
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 1c3249e3c3..fb19bb4b9d 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -17,13 +17,6 @@
 #include "video_core/vulkan_common/vulkan_wrapper.h"
 
 namespace Vulkan {
-namespace {
-DescriptorLayoutTuple CreateLayout(const Device& device, const Shader::Info& info) {
-    DescriptorLayoutBuilder builder;
-    builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
-    return builder.Create(device.GetLogical());
-}
-} // Anonymous namespace
 
 ComputePipeline::ComputePipeline(const Device& device, VKDescriptorPool& descriptor_pool,
                                  VKUpdateDescriptorQueue& update_descriptor_queue_,
@@ -31,10 +24,12 @@ ComputePipeline::ComputePipeline(const Device& device, VKDescriptorPool& descrip
                                  vk::ShaderModule spv_module_)
     : update_descriptor_queue{update_descriptor_queue_}, info{info_},
       spv_module(std::move(spv_module_)) {
-    DescriptorLayoutTuple tuple{CreateLayout(device, info)};
-    descriptor_set_layout = std::move(tuple.descriptor_set_layout);
-    pipeline_layout = std::move(tuple.pipeline_layout);
-    descriptor_update_template = std::move(tuple.descriptor_update_template);
+    DescriptorLayoutBuilder builder{device.GetLogical()};
+    builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
+
+    descriptor_set_layout = builder.CreateDescriptorSetLayout();
+    pipeline_layout = builder.CreatePipelineLayout(*descriptor_set_layout);
+    descriptor_update_template = builder.CreateTemplate(*descriptor_set_layout, *pipeline_layout);
     descriptor_allocator = DescriptorAllocator(descriptor_pool, *descriptor_set_layout);
 
     auto func{[this, &device] {
@@ -128,7 +123,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
         return;
     }
     const VkDescriptorSet descriptor_set{descriptor_allocator.Commit()};
-    update_descriptor_queue.Send(*descriptor_update_template, descriptor_set);
+    update_descriptor_queue.Send(descriptor_update_template.address(), descriptor_set);
     scheduler.Record([this, descriptor_set](vk::CommandBuffer cmdbuf) {
         cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
                                   descriptor_set, nullptr);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index ddc08b8c49..d17b79e020 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -27,8 +27,8 @@ using VideoCore::Surface::PixelFormat;
 using VideoCore::Surface::PixelFormatFromDepthFormat;
 using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
 
-DescriptorLayoutTuple CreateLayout(const Device& device, std::span<const Shader::Info> infos) {
-    DescriptorLayoutBuilder builder;
+DescriptorLayoutBuilder MakeBuilder(const Device& device, std::span<const Shader::Info> infos) {
+    DescriptorLayoutBuilder builder{device.GetLogical()};
     for (size_t index = 0; index < infos.size(); ++index) {
         static constexpr std::array stages{
             VK_SHADER_STAGE_VERTEX_BIT,
@@ -39,7 +39,7 @@ DescriptorLayoutTuple CreateLayout(const Device& device, std::span<const Shader:
         };
         builder.Add(infos[index], stages.at(index));
     }
-    return builder.Create(device.GetLogical());
+    return builder;
 }
 
 template <class StencilFace>
@@ -124,13 +124,15 @@ GraphicsPipeline::GraphicsPipeline(Tegra::Engines::Maxwell3D& maxwell3d_,
     std::ranges::transform(infos, stage_infos.begin(),
                            [](const Shader::Info* info) { return info ? *info : Shader::Info{}; });
 
-    DescriptorLayoutTuple tuple{CreateLayout(device, stage_infos)};
-    descriptor_set_layout = std::move(tuple.descriptor_set_layout);
-    pipeline_layout = std::move(tuple.pipeline_layout);
-    descriptor_update_template = std::move(tuple.descriptor_update_template);
+    DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
+    descriptor_set_layout = builder.CreateDescriptorSetLayout();
     descriptor_allocator = DescriptorAllocator(descriptor_pool, *descriptor_set_layout);
 
-    auto func{[this, &device, &render_pass_cache] {
+    auto func{[this, &device, &render_pass_cache, builder] {
+        const VkDescriptorSetLayout set_layout{*descriptor_set_layout};
+        pipeline_layout = builder.CreatePipelineLayout(set_layout);
+        descriptor_update_template = builder.CreateTemplate(set_layout, *pipeline_layout);
+
         const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(state))};
         MakePipeline(device, render_pass);
         building_flag.test_and_set();
@@ -206,11 +208,11 @@ void GraphicsPipeline::Configure(bool is_indexed) {
         return;
     }
     const VkDescriptorSet descriptor_set{descriptor_allocator.Commit()};
-    update_descriptor_queue.Send(*descriptor_update_template, descriptor_set);
+    update_descriptor_queue.Send(descriptor_update_template.address(), descriptor_set);
 
-    scheduler.Record([descriptor_set, layout = *pipeline_layout](vk::CommandBuffer cmdbuf) {
-        cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
-                                  nullptr);
+    scheduler.Record([this, descriptor_set](vk::CommandBuffer cmdbuf) {
+        cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
+                                  descriptor_set, nullptr);
     });
 }
 
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index e3d9debf45..5972619649 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -620,7 +620,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_,
       kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, device{device_},
       scheduler{scheduler_}, descriptor_pool{descriptor_pool_},
       update_descriptor_queue{update_descriptor_queue_}, render_pass_cache{render_pass_cache_},
-      buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, workers(11, "PipelineBuilder") {
+      buffer_cache{buffer_cache_}, texture_cache{texture_cache_},
+      workers(11, "yuzu:PipelineBuilder") {
     const auto& float_control{device.FloatControlProperties()};
     const VkDriverIdKHR driver_id{device.GetDriverID()};
     base_profile = Shader::Profile{
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index dc45fdcb19..bea9b80123 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -36,12 +36,12 @@ void VKUpdateDescriptorQueue::Acquire() {
     upload_start = payload_cursor;
 }
 
-void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template,
+void VKUpdateDescriptorQueue::Send(const VkDescriptorUpdateTemplateKHR* update_template,
                                    VkDescriptorSet set) {
     const void* const data = upload_start;
     const vk::Device* const logical = &device.GetLogical();
     scheduler.Record([data, logical, set, update_template](vk::CommandBuffer) {
-        logical->UpdateDescriptorSet(set, update_template, data);
+        logical->UpdateDescriptorSet(set, *update_template, data);
     });
 }
 
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index d35e77c447..82bc9920c1 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -39,7 +39,7 @@ public:
 
     void Acquire();
 
-    void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set);
+    void Send(const VkDescriptorUpdateTemplateKHR* update_template, VkDescriptorSet set);
 
     void AddSampledImage(VkImageView image_view, VkSampler sampler) {
         *(payload_cursor++) = VkDescriptorImageInfo{