/ src / video_core / renderer_vulkan / vk_blit_helper.cpp
vk_blit_helper.cpp
  1  // Copyright 2022 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #include "common/vector_math.h"
  6  #include "video_core/renderer_vulkan/vk_blit_helper.h"
  7  #include "video_core/renderer_vulkan/vk_instance.h"
  8  #include "video_core/renderer_vulkan/vk_renderpass_cache.h"
  9  #include "video_core/renderer_vulkan/vk_scheduler.h"
 10  #include "video_core/renderer_vulkan/vk_shader_util.h"
 11  #include "video_core/renderer_vulkan/vk_texture_runtime.h"
 12  
 13  #include "video_core/host_shaders/format_reinterpreter/vulkan_d24s8_to_rgba8_comp.h"
 14  #include "video_core/host_shaders/full_screen_triangle_vert.h"
 15  #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag.h"
 16  #include "video_core/host_shaders/vulkan_depth_to_buffer_comp.h"
 17  
 18  namespace Vulkan {
 19  
 20  using VideoCore::PixelFormat;
 21  
 22  namespace {
 23  struct PushConstants {
 24      std::array<float, 2> tex_scale;
 25      std::array<float, 2> tex_offset;
 26  };
 27  
 28  struct ComputeInfo {
 29      Common::Vec2i src_offset;
 30      Common::Vec2i dst_offset;
 31      Common::Vec2i src_extent;
 32  };
 33  
 34  inline constexpr vk::PushConstantRange COMPUTE_PUSH_CONSTANT_RANGE{
 35      .stageFlags = vk::ShaderStageFlagBits::eCompute,
 36      .offset = 0,
 37      .size = sizeof(ComputeInfo),
 38  };
 39  
 40  constexpr std::array<vk::DescriptorSetLayoutBinding, 3> COMPUTE_BINDINGS = {{
 41      {0, vk::DescriptorType::eSampledImage, 1, vk::ShaderStageFlagBits::eCompute},
 42      {1, vk::DescriptorType::eSampledImage, 1, vk::ShaderStageFlagBits::eCompute},
 43      {2, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eCompute},
 44  }};
 45  
 46  constexpr std::array<vk::DescriptorSetLayoutBinding, 3> COMPUTE_BUFFER_BINDINGS = {{
 47      {0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eCompute},
 48      {1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eCompute},
 49      {2, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute},
 50  }};
 51  
 52  constexpr std::array<vk::DescriptorSetLayoutBinding, 2> TWO_TEXTURES_BINDINGS = {{
 53      {0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
 54      {1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
 55  }};
 56  
 57  inline constexpr vk::PushConstantRange PUSH_CONSTANT_RANGE{
 58      .stageFlags = vk::ShaderStageFlagBits::eVertex,
 59      .offset = 0,
 60      .size = sizeof(PushConstants),
 61  };
 62  constexpr vk::PipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{
 63      .vertexBindingDescriptionCount = 0,
 64      .pVertexBindingDescriptions = nullptr,
 65      .vertexAttributeDescriptionCount = 0,
 66      .pVertexAttributeDescriptions = nullptr,
 67  };
 68  constexpr vk::PipelineInputAssemblyStateCreateInfo PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO{
 69      .topology = vk::PrimitiveTopology::eTriangleList,
 70      .primitiveRestartEnable = VK_FALSE,
 71  };
 72  constexpr vk::PipelineViewportStateCreateInfo PIPELINE_VIEWPORT_STATE_CREATE_INFO{
 73      .viewportCount = 1,
 74      .pViewports = nullptr,
 75      .scissorCount = 1,
 76      .pScissors = nullptr,
 77  };
 78  constexpr vk::PipelineRasterizationStateCreateInfo PIPELINE_RASTERIZATION_STATE_CREATE_INFO{
 79      .depthClampEnable = VK_FALSE,
 80      .rasterizerDiscardEnable = VK_FALSE,
 81      .polygonMode = vk::PolygonMode::eFill,
 82      .cullMode = vk::CullModeFlagBits::eBack,
 83      .frontFace = vk::FrontFace::eClockwise,
 84      .depthBiasEnable = VK_FALSE,
 85      .depthBiasConstantFactor = 0.0f,
 86      .depthBiasClamp = 0.0f,
 87      .depthBiasSlopeFactor = 0.0f,
 88      .lineWidth = 1.0f,
 89  };
 90  constexpr vk::PipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE_INFO{
 91      .rasterizationSamples = vk::SampleCountFlagBits::e1,
 92      .sampleShadingEnable = VK_FALSE,
 93      .minSampleShading = 0.0f,
 94      .pSampleMask = nullptr,
 95      .alphaToCoverageEnable = VK_FALSE,
 96      .alphaToOneEnable = VK_FALSE,
 97  };
 98  constexpr std::array DYNAMIC_STATES{
 99      vk::DynamicState::eViewport,
100      vk::DynamicState::eScissor,
101  };
102  constexpr vk::PipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
103      .dynamicStateCount = static_cast<u32>(DYNAMIC_STATES.size()),
104      .pDynamicStates = DYNAMIC_STATES.data(),
105  };
106  constexpr vk::PipelineColorBlendStateCreateInfo PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO{
107      .logicOpEnable = VK_FALSE,
108      .logicOp = vk::LogicOp::eClear,
109      .attachmentCount = 0,
110      .pAttachments = nullptr,
111      .blendConstants = std::array{0.0f, 0.0f, 0.0f, 0.0f},
112  };
113  constexpr vk::PipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO{
114      .depthTestEnable = VK_TRUE,
115      .depthWriteEnable = VK_TRUE,
116      .depthCompareOp = vk::CompareOp::eAlways,
117      .depthBoundsTestEnable = VK_FALSE,
118      .stencilTestEnable = VK_FALSE,
119      .front = vk::StencilOpState{},
120      .back = vk::StencilOpState{},
121      .minDepthBounds = 0.0f,
122      .maxDepthBounds = 0.0f,
123  };
124  
125  template <vk::Filter filter>
126  inline constexpr vk::SamplerCreateInfo SAMPLER_CREATE_INFO{
127      .magFilter = filter,
128      .minFilter = filter,
129      .mipmapMode = vk::SamplerMipmapMode::eNearest,
130      .addressModeU = vk::SamplerAddressMode::eClampToBorder,
131      .addressModeV = vk::SamplerAddressMode::eClampToBorder,
132      .addressModeW = vk::SamplerAddressMode::eClampToBorder,
133      .mipLodBias = 0.0f,
134      .anisotropyEnable = VK_FALSE,
135      .maxAnisotropy = 0.0f,
136      .compareEnable = VK_FALSE,
137      .compareOp = vk::CompareOp::eNever,
138      .minLod = 0.0f,
139      .maxLod = 0.0f,
140      .borderColor = vk::BorderColor::eFloatOpaqueWhite,
141      .unnormalizedCoordinates = VK_FALSE,
142  };
143  
144  constexpr vk::PipelineLayoutCreateInfo PipelineLayoutCreateInfo(
145      const vk::DescriptorSetLayout* set_layout, bool compute = false) {
146      return vk::PipelineLayoutCreateInfo{
147          .setLayoutCount = 1,
148          .pSetLayouts = set_layout,
149          .pushConstantRangeCount = 1,
150          .pPushConstantRanges = (compute ? &COMPUTE_PUSH_CONSTANT_RANGE : &PUSH_CONSTANT_RANGE),
151      };
152  }
153  
154  constexpr std::array<vk::PipelineShaderStageCreateInfo, 2> MakeStages(
155      vk::ShaderModule vertex_shader, vk::ShaderModule fragment_shader) {
156      return std::array{
157          vk::PipelineShaderStageCreateInfo{
158              .stage = vk::ShaderStageFlagBits::eVertex,
159              .module = vertex_shader,
160              .pName = "main",
161          },
162          vk::PipelineShaderStageCreateInfo{
163              .stage = vk::ShaderStageFlagBits::eFragment,
164              .module = fragment_shader,
165              .pName = "main",
166          },
167      };
168  }
169  
170  constexpr vk::PipelineShaderStageCreateInfo MakeStages(vk::ShaderModule compute_shader) {
171      return vk::PipelineShaderStageCreateInfo{
172          .stage = vk::ShaderStageFlagBits::eCompute,
173          .module = compute_shader,
174          .pName = "main",
175      };
176  }
177  
178  } // Anonymous namespace
179  
180  BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, DescriptorPool& pool,
181                         RenderpassCache& renderpass_cache_)
182      : instance{instance_}, scheduler{scheduler_}, renderpass_cache{renderpass_cache_},
183        device{instance.GetDevice()}, compute_provider{instance, pool, COMPUTE_BINDINGS},
184        compute_buffer_provider{instance, pool, COMPUTE_BUFFER_BINDINGS},
185        two_textures_provider{instance, pool, TWO_TEXTURES_BINDINGS},
186        compute_pipeline_layout{
187            device.createPipelineLayout(PipelineLayoutCreateInfo(&compute_provider.Layout(), true))},
188        compute_buffer_pipeline_layout{device.createPipelineLayout(
189            PipelineLayoutCreateInfo(&compute_buffer_provider.Layout(), true))},
190        two_textures_pipeline_layout{
191            device.createPipelineLayout(PipelineLayoutCreateInfo(&two_textures_provider.Layout()))},
192        full_screen_vert{Compile(HostShaders::FULL_SCREEN_TRIANGLE_VERT,
193                                 vk::ShaderStageFlagBits::eVertex, device)},
194        d24s8_to_rgba8_comp{Compile(HostShaders::VULKAN_D24S8_TO_RGBA8_COMP,
195                                    vk::ShaderStageFlagBits::eCompute, device)},
196        depth_to_buffer_comp{Compile(HostShaders::VULKAN_DEPTH_TO_BUFFER_COMP,
197                                     vk::ShaderStageFlagBits::eCompute, device)},
198        blit_depth_stencil_frag{Compile(HostShaders::VULKAN_BLIT_DEPTH_STENCIL_FRAG,
199                                        vk::ShaderStageFlagBits::eFragment, device)},
200        d24s8_to_rgba8_pipeline{MakeComputePipeline(d24s8_to_rgba8_comp, compute_pipeline_layout)},
201        depth_to_buffer_pipeline{
202            MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)},
203        depth_blit_pipeline{MakeDepthStencilBlitPipeline()},
204        linear_sampler{device.createSampler(SAMPLER_CREATE_INFO<vk::Filter::eLinear>)},
205        nearest_sampler{device.createSampler(SAMPLER_CREATE_INFO<vk::Filter::eNearest>)} {
206  
207      if (instance.HasDebuggingToolAttached()) {
208          SetObjectName(device, compute_pipeline_layout, "BlitHelper: compute_pipeline_layout");
209          SetObjectName(device, compute_buffer_pipeline_layout,
210                        "BlitHelper: compute_buffer_pipeline_layout");
211          SetObjectName(device, two_textures_pipeline_layout,
212                        "BlitHelper: two_textures_pipeline_layout");
213          SetObjectName(device, full_screen_vert, "BlitHelper: full_screen_vert");
214          SetObjectName(device, d24s8_to_rgba8_comp, "BlitHelper: d24s8_to_rgba8_comp");
215          SetObjectName(device, depth_to_buffer_comp, "BlitHelper: depth_to_buffer_comp");
216          SetObjectName(device, blit_depth_stencil_frag, "BlitHelper: blit_depth_stencil_frag");
217          SetObjectName(device, d24s8_to_rgba8_pipeline, "BlitHelper: d24s8_to_rgba8_pipeline");
218          SetObjectName(device, depth_to_buffer_pipeline, "BlitHelper: depth_to_buffer_pipeline");
219          if (depth_blit_pipeline) {
220              SetObjectName(device, depth_blit_pipeline, "BlitHelper: depth_blit_pipeline");
221          }
222          SetObjectName(device, linear_sampler, "BlitHelper: linear_sampler");
223          SetObjectName(device, nearest_sampler, "BlitHelper: nearest_sampler");
224      }
225  }
226  
227  BlitHelper::~BlitHelper() {
228      device.destroyPipelineLayout(compute_pipeline_layout);
229      device.destroyPipelineLayout(compute_buffer_pipeline_layout);
230      device.destroyPipelineLayout(two_textures_pipeline_layout);
231      device.destroyShaderModule(full_screen_vert);
232      device.destroyShaderModule(d24s8_to_rgba8_comp);
233      device.destroyShaderModule(depth_to_buffer_comp);
234      device.destroyShaderModule(blit_depth_stencil_frag);
235      device.destroyPipeline(depth_to_buffer_pipeline);
236      device.destroyPipeline(d24s8_to_rgba8_pipeline);
237      device.destroyPipeline(depth_blit_pipeline);
238      device.destroySampler(linear_sampler);
239      device.destroySampler(nearest_sampler);
240  }
241  
242  void BindBlitState(vk::CommandBuffer cmdbuf, vk::PipelineLayout layout,
243                     const VideoCore::TextureBlit& blit) {
244      const vk::Offset2D offset{
245          .x = std::min<s32>(blit.dst_rect.left, blit.dst_rect.right),
246          .y = std::min<s32>(blit.dst_rect.bottom, blit.dst_rect.top),
247      };
248      const vk::Extent2D extent{
249          .width = blit.dst_rect.GetWidth(),
250          .height = blit.dst_rect.GetHeight(),
251      };
252      const vk::Viewport viewport{
253          .x = static_cast<float>(offset.x),
254          .y = static_cast<float>(offset.y),
255          .width = static_cast<float>(extent.width),
256          .height = static_cast<float>(extent.height),
257          .minDepth = 0.0f,
258          .maxDepth = 1.0f,
259      };
260      const vk::Rect2D scissor{
261          .offset = offset,
262          .extent = extent,
263      };
264      const float scale_x = static_cast<float>(blit.src_rect.GetWidth());
265      const float scale_y = static_cast<float>(blit.src_rect.GetHeight());
266      const PushConstants push_constants{
267          .tex_scale = {scale_x, scale_y},
268          .tex_offset = {static_cast<float>(blit.src_rect.left),
269                         static_cast<float>(blit.src_rect.bottom)},
270      };
271      cmdbuf.setViewport(0, viewport);
272      cmdbuf.setScissor(0, scissor);
273      cmdbuf.pushConstants(layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants),
274                           &push_constants);
275  }
276  
277  bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest,
278                                    const VideoCore::TextureBlit& blit) {
279      if (!instance.IsShaderStencilExportSupported()) {
280          LOG_ERROR(Render_Vulkan, "Unable to emulate depth stencil images");
281          return false;
282      }
283  
284      const vk::Rect2D dst_render_area = {
285          .offset = {0, 0},
286          .extent = {dest.GetScaledWidth(), dest.GetScaledHeight()},
287      };
288  
289      std::array<DescriptorData, 2> textures{};
290      textures[0].image_info = vk::DescriptorImageInfo{
291          .sampler = nearest_sampler,
292          .imageView = source.DepthView(),
293          .imageLayout = vk::ImageLayout::eGeneral,
294      };
295      textures[1].image_info = vk::DescriptorImageInfo{
296          .sampler = nearest_sampler,
297          .imageView = source.StencilView(),
298          .imageLayout = vk::ImageLayout::eGeneral,
299      };
300  
301      const auto descriptor_set = two_textures_provider.Acquire(textures);
302  
303      const RenderPass depth_pass = {
304          .framebuffer = dest.Framebuffer(),
305          .render_pass =
306              renderpass_cache.GetRenderpass(PixelFormat::Invalid, dest.pixel_format, false),
307          .render_area = dst_render_area,
308      };
309      renderpass_cache.BeginRendering(depth_pass);
310  
311      scheduler.Record([blit, descriptor_set, this](vk::CommandBuffer cmdbuf) {
312          const vk::PipelineLayout layout = two_textures_pipeline_layout;
313  
314          cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, depth_blit_pipeline);
315          cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, descriptor_set, {});
316          BindBlitState(cmdbuf, layout, blit);
317          cmdbuf.draw(3, 1, 0, 0);
318      });
319      scheduler.MakeDirty(StateFlags::Pipeline);
320      return true;
321  }
322  
323  bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
324                                        const VideoCore::TextureCopy& copy) {
325      std::array<DescriptorData, 3> textures{};
326      textures[0].image_info = vk::DescriptorImageInfo{
327          .imageView = source.DepthView(),
328          .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
329      };
330      textures[1].image_info = vk::DescriptorImageInfo{
331          .imageView = source.StencilView(),
332          .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
333      };
334      textures[2].image_info = vk::DescriptorImageInfo{
335          .imageView = dest.ImageView(),
336          .imageLayout = vk::ImageLayout::eGeneral,
337      };
338  
339      const auto descriptor_set = compute_provider.Acquire(textures);
340  
341      renderpass_cache.EndRendering();
342      scheduler.Record([this, descriptor_set, copy, src_image = source.Image(),
343                        dst_image = dest.Image()](vk::CommandBuffer cmdbuf) {
344          const std::array pre_barriers = {
345              vk::ImageMemoryBarrier{
346                  .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
347                  .dstAccessMask = vk::AccessFlagBits::eShaderRead,
348                  .oldLayout = vk::ImageLayout::eGeneral,
349                  .newLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
350                  .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
351                  .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
352                  .image = src_image,
353                  .subresourceRange{
354                      .aspectMask =
355                          vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
356                      .baseMipLevel = 0,
357                      .levelCount = VK_REMAINING_MIP_LEVELS,
358                      .baseArrayLayer = 0,
359                      .layerCount = VK_REMAINING_ARRAY_LAYERS,
360                  },
361              },
362              vk::ImageMemoryBarrier{
363                  .srcAccessMask = vk::AccessFlagBits::eNone,
364                  .dstAccessMask = vk::AccessFlagBits::eShaderWrite,
365                  .oldLayout = vk::ImageLayout::eUndefined,
366                  .newLayout = vk::ImageLayout::eGeneral,
367                  .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
368                  .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
369                  .image = dst_image,
370                  .subresourceRange{
371                      .aspectMask = vk::ImageAspectFlagBits::eColor,
372                      .baseMipLevel = 0,
373                      .levelCount = VK_REMAINING_MIP_LEVELS,
374                      .baseArrayLayer = 0,
375                      .layerCount = VK_REMAINING_ARRAY_LAYERS,
376                  },
377              },
378          };
379          const std::array post_barriers = {
380              vk::ImageMemoryBarrier{
381                  .srcAccessMask = vk::AccessFlagBits::eShaderRead,
382                  .dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite |
383                                   vk::AccessFlagBits::eDepthStencilAttachmentRead,
384                  .oldLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
385                  .newLayout = vk::ImageLayout::eGeneral,
386                  .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
387                  .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
388                  .image = src_image,
389                  .subresourceRange{
390                      .aspectMask =
391                          vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
392                      .baseMipLevel = 0,
393                      .levelCount = VK_REMAINING_MIP_LEVELS,
394                      .baseArrayLayer = 0,
395                      .layerCount = VK_REMAINING_ARRAY_LAYERS,
396                  },
397              },
398              vk::ImageMemoryBarrier{
399                  .srcAccessMask = vk::AccessFlagBits::eShaderWrite,
400                  .dstAccessMask = vk::AccessFlagBits::eTransferRead,
401                  .oldLayout = vk::ImageLayout::eGeneral,
402                  .newLayout = vk::ImageLayout::eGeneral,
403                  .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
404                  .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
405                  .image = dst_image,
406                  .subresourceRange{
407                      .aspectMask = vk::ImageAspectFlagBits::eColor,
408                      .baseMipLevel = 0,
409                      .levelCount = VK_REMAINING_MIP_LEVELS,
410                      .baseArrayLayer = 0,
411                      .layerCount = VK_REMAINING_ARRAY_LAYERS,
412                  },
413              }};
414          cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eEarlyFragmentTests |
415                                     vk::PipelineStageFlagBits::eLateFragmentTests,
416                                 vk::PipelineStageFlagBits::eComputeShader,
417                                 vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers);
418  
419          cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, compute_pipeline_layout, 0,
420                                    descriptor_set, {});
421          cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, d24s8_to_rgba8_pipeline);
422  
423          const ComputeInfo info = {
424              .src_offset = Common::Vec2i{static_cast<int>(copy.src_offset.x),
425                                          static_cast<int>(copy.src_offset.y)},
426              .dst_offset = Common::Vec2i{static_cast<int>(copy.dst_offset.x),
427                                          static_cast<int>(copy.dst_offset.y)},
428          };
429          cmdbuf.pushConstants(compute_pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0,
430                               sizeof(info), &info);
431  
432          cmdbuf.dispatch(copy.extent.width / 8, copy.extent.height / 8, 1);
433  
434          cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
435                                 vk::PipelineStageFlagBits::eEarlyFragmentTests |
436                                     vk::PipelineStageFlagBits::eLateFragmentTests |
437                                     vk::PipelineStageFlagBits::eTransfer,
438                                 vk::DependencyFlagBits::eByRegion, {}, {}, post_barriers);
439      });
440      return true;
441  }
442  
443  bool BlitHelper::DepthToBuffer(Surface& source, vk::Buffer buffer,
444                                 const VideoCore::BufferTextureCopy& copy) {
445      std::array<DescriptorData, 3> textures{};
446      textures[0].image_info = vk::DescriptorImageInfo{
447          .sampler = nearest_sampler,
448          .imageView = source.DepthView(),
449          .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
450      };
451      textures[1].image_info = vk::DescriptorImageInfo{
452          .sampler = nearest_sampler,
453          .imageView = source.StencilView(),
454          .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
455      };
456      textures[2].buffer_info = vk::DescriptorBufferInfo{
457          .buffer = buffer,
458          .offset = copy.buffer_offset,
459          .range = copy.buffer_size,
460      };
461  
462      const auto descriptor_set = compute_buffer_provider.Acquire(textures);
463  
464      renderpass_cache.EndRendering();
465      scheduler.Record([this, descriptor_set, copy, src_image = source.Image(),
466                        extent = source.RealExtent(false)](vk::CommandBuffer cmdbuf) {
467          const vk::ImageMemoryBarrier pre_barrier = {
468              .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
469              .dstAccessMask = vk::AccessFlagBits::eShaderRead,
470              .oldLayout = vk::ImageLayout::eGeneral,
471              .newLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
472              .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
473              .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
474              .image = src_image,
475              .subresourceRange{
476                  .aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
477                  .baseMipLevel = 0,
478                  .levelCount = VK_REMAINING_MIP_LEVELS,
479                  .baseArrayLayer = 0,
480                  .layerCount = VK_REMAINING_ARRAY_LAYERS,
481              },
482          };
483          const vk::ImageMemoryBarrier post_barrier = {
484              .srcAccessMask = vk::AccessFlagBits::eShaderRead,
485              .dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite |
486                               vk::AccessFlagBits::eDepthStencilAttachmentRead,
487              .oldLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
488              .newLayout = vk::ImageLayout::eGeneral,
489              .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
490              .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
491              .image = src_image,
492              .subresourceRange{
493                  .aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
494                  .baseMipLevel = 0,
495                  .levelCount = VK_REMAINING_MIP_LEVELS,
496                  .baseArrayLayer = 0,
497                  .layerCount = VK_REMAINING_ARRAY_LAYERS,
498              },
499          };
500          cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eEarlyFragmentTests |
501                                     vk::PipelineStageFlagBits::eLateFragmentTests,
502                                 vk::PipelineStageFlagBits::eComputeShader,
503                                 vk::DependencyFlagBits::eByRegion, {}, {}, pre_barrier);
504  
505          cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, compute_buffer_pipeline_layout,
506                                    0, descriptor_set, {});
507          cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, depth_to_buffer_pipeline);
508  
509          const ComputeInfo info = {
510              .src_offset = Common::Vec2i{static_cast<int>(copy.texture_rect.left),
511                                          static_cast<int>(copy.texture_rect.bottom)},
512              .src_extent =
513                  Common::Vec2i{static_cast<int>(extent.width), static_cast<int>(extent.height)},
514          };
515          cmdbuf.pushConstants(compute_buffer_pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0,
516                               sizeof(ComputeInfo), &info);
517  
518          cmdbuf.dispatch(copy.texture_rect.GetWidth() / 8, copy.texture_rect.GetHeight() / 8, 1);
519  
520          cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
521                                 vk::PipelineStageFlagBits::eEarlyFragmentTests |
522                                     vk::PipelineStageFlagBits::eLateFragmentTests |
523                                     vk::PipelineStageFlagBits::eTransfer,
524                                 vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier);
525      });
526      return true;
527  }
528  
529  vk::Pipeline BlitHelper::MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout) {
530      const vk::ComputePipelineCreateInfo compute_info = {
531          .stage = MakeStages(shader),
532          .layout = layout,
533      };
534  
535      if (const auto result = device.createComputePipeline({}, compute_info);
536          result.result == vk::Result::eSuccess) {
537          return result.value;
538      } else {
539          LOG_CRITICAL(Render_Vulkan, "Compute pipeline creation failed!");
540          UNREACHABLE();
541      }
542  }
543  
544  vk::Pipeline BlitHelper::MakeDepthStencilBlitPipeline() {
545      if (!instance.IsShaderStencilExportSupported()) {
546          return VK_NULL_HANDLE;
547      }
548  
549      const std::array stages = MakeStages(full_screen_vert, blit_depth_stencil_frag);
550      const auto renderpass = renderpass_cache.GetRenderpass(VideoCore::PixelFormat::Invalid,
551                                                             VideoCore::PixelFormat::D24S8, false);
552      vk::GraphicsPipelineCreateInfo depth_stencil_info = {
553          .stageCount = static_cast<u32>(stages.size()),
554          .pStages = stages.data(),
555          .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
556          .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
557          .pTessellationState = nullptr,
558          .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
559          .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
560          .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
561          .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
562          .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
563          .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
564          .layout = two_textures_pipeline_layout,
565          .renderPass = renderpass,
566      };
567  
568      if (const auto result = device.createGraphicsPipeline({}, depth_stencil_info);
569          result.result == vk::Result::eSuccess) {
570          return result.value;
571      } else {
572          LOG_CRITICAL(Render_Vulkan, "Depth stencil blit pipeline creation failed!");
573          UNREACHABLE();
574      }
575      return VK_NULL_HANDLE;
576  }
577  
578  } // namespace Vulkan