code:
#include "dxvk_device.h"
#include "dxvk_meta_copy.h"

#include <dxvk_fullscreen_geom.h>
#include <dxvk_fullscreen_vert.h>
#include <dxvk_fullscreen_layer_vert.h>

#include <dxvk_copy_buffer_image.h>
#include <dxvk_copy_color_1d.h>
#include <dxvk_copy_color_2d.h>
#include <dxvk_copy_color_ms.h>
#include <dxvk_copy_depth_1d.h>
#include <dxvk_copy_depth_2d.h>
#include <dxvk_copy_depth_ms.h>
#include <dxvk_copy_depth_stencil_1d.h>
#include <dxvk_copy_depth_stencil_2d.h>
#include <dxvk_copy_depth_stencil_ms.h>

namespace dxvk {

  DxvkMetaCopyRenderPass::DxvkMetaCopyRenderPass(
    const Rc<vk::DeviceFn>&   vkd,
    const Rc<DxvkImageView>&  dstImageView,
    const Rc<DxvkImageView>&  srcImageView,
    const Rc<DxvkImageView>&  srcStencilView,
          bool                discardDst)
  : m_vkd           (vkd),
    m_dstImageView  (dstImageView),
    m_srcImageView  (srcImageView),
    m_srcStencilView(srcStencilView),
    m_renderPass    (createRenderPass(discardDst)),
    m_framebuffer   (createFramebuffer()) {

  }

  DxvkMetaCopyRenderPass::~DxvkMetaCopyRenderPass() {
    m_vkd->vkDestroyFramebuffer(m_vkd->device(), m_framebuffer, nullptr);
    m_vkd->vkDestroyRenderPass (m_vkd->device(), m_renderPass,  nullptr);
  }

  VkRenderPass DxvkMetaCopyRenderPass::createRenderPass(bool discard) const {
    auto aspect = m_dstImageView->info().aspect;

    VkPipelineStageFlags cpyStages = 0;
    VkAccessFlags        cpyAccess = 0;

    VkAttachmentDescription attachment;
    attachment.flags            = 0;
    attachment.format           = m_dstImageView->info().format;
    attachment.samples          = m_dstImageView->imageInfo().sampleCount;
    attachment.loadOp           = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.storeOp          = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.stencilLoadOp    = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.stencilStoreOp   = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout    = m_dstImageView->imageInfo().layout;
    attachment.finalLayout      = m_dstImageView->imageInfo().layout;

    if (discard) {
      attachment.loadOp         = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachment.stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachment.initialLayout  = VK_IMAGE_LAYOUT_UNDEFINED;
    }

    VkAttachmentReference attachmentRef;
    attachmentRef.attachment    = 0;
    attachmentRef.layout        = (aspect & VK_IMAGE_ASPECT_COLOR_BIT)
      ? m_dstImageView->pickLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
      : m_dstImageView->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);

    VkSubpassDescription subpass;
    subpass.flags                   = 0;
    subpass.pipelineBindPoint       = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.inputAttachmentCount    = 0;
    subpass.pInputAttachments       = nullptr;
    subpass.colorAttachmentCount    = 0;
    subpass.pColorAttachments       = nullptr;
    subpass.pResolveAttachments     = nullptr;
    subpass.pDepthStencilAttachment = nullptr;
    subpass.preserveAttachmentCount = 0;
    subpass.pPreserveAttachments    = nullptr;

    if (aspect & VK_IMAGE_ASPECT_COLOR_BIT) {
      subpass.colorAttachmentCount  = 1;
      subpass.pColorAttachments     = &attachmentRef;

      cpyStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
      cpyAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

      if (!discard)
        cpyAccess |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
    } else {
      subpass.pDepthStencilAttachment = &attachmentRef;

      cpyStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
                |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
      cpyAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

      if (!discard)
        cpyAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
    }

    // We have to be somewhat conservative here since we cannot assume
    // that the backend blocks stages that are only used for meta ops
    VkPipelineStageFlags extStages = m_dstImageView->imageInfo().stages | m_srcImageView->imageInfo().stages;
    VkAccessFlags        extAccess = m_dstImageView->imageInfo().access;

    std::array<VkSubpassDependency, 2> dependencies = {{
      { VK_SUBPASS_EXTERNAL, 0, extStages, cpyStages, 0,         cpyAccess, 0 },
      { 0, VK_SUBPASS_EXTERNAL, cpyStages, extStages, cpyAccess, extAccess, 0 },
    }};

    VkRenderPassCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.attachmentCount        = 1;
    info.pAttachments           = &attachment;
    info.subpassCount           = 1;
    info.pSubpasses             = &subpass;
    info.dependencyCount        = dependencies.size();
    info.pDependencies          = dependencies.data();

    VkRenderPass result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyRenderPass: Failed to create render pass");
    return result;
  }

  VkFramebuffer DxvkMetaCopyRenderPass::createFramebuffer() const {
    VkImageView             dstViewHandle   = m_dstImageView->handle();
    VkImageSubresourceRange dstSubresources = m_dstImageView->subresources();
    VkExtent3D              dstExtent       = m_dstImageView->mipLevelExtent(0);

    VkFramebufferCreateInfo fboInfo;
    fboInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    fboInfo.pNext           = nullptr;
    fboInfo.flags           = 0;
    fboInfo.renderPass      = m_renderPass;
    fboInfo.attachmentCount = 1;
    fboInfo.pAttachments    = &dstViewHandle;
    fboInfo.width           = dstExtent.width;
    fboInfo.height          = dstExtent.height;
    fboInfo.layers          = dstSubresources.layerCount;

    VkFramebuffer result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &fboInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyRenderPass: Failed to create target framebuffer");
    return result;
  }

  DxvkMetaCopyObjects::DxvkMetaCopyObjects(const DxvkDevice* device)
  : m_vkd         (device->vkd()),
    m_sampler     (createSampler()),
    m_color {
      createShaderModule(dxvk_copy_color_1d),
      createShaderModule(dxvk_copy_color_2d),
      createShaderModule(dxvk_copy_color_ms) },
    m_depth {
      createShaderModule(dxvk_copy_depth_1d),
      createShaderModule(dxvk_copy_depth_2d),
      createShaderModule(dxvk_copy_depth_ms) } {
    if (device->extensions().extShaderViewportIndexLayer) {
      m_shaderVert = createShaderModule(dxvk_fullscreen_layer_vert);
    } else {
      m_shaderVert = createShaderModule(dxvk_fullscreen_vert);
      m_shaderGeom = createShaderModule(dxvk_fullscreen_geom);
    }

    if (device->extensions().extShaderStencilExport) {
      m_depthStencil = {
        createShaderModule(dxvk_copy_depth_stencil_1d),
        createShaderModule(dxvk_copy_depth_stencil_2d),
        createShaderModule(dxvk_copy_depth_stencil_ms) };
    }
  }

  DxvkMetaCopyObjects::~DxvkMetaCopyObjects() {
    m_vkd->vkDestroyPipeline(m_vkd->device(), m_copyBufferImagePipeline.pipeHandle, nullptr);
    m_vkd->vkDestroyPipelineLayout(m_vkd->device(), m_copyBufferImagePipeline.pipeLayout, nullptr);
    m_vkd->vkDestroyDescriptorSetLayout(m_vkd->device(), m_copyBufferImagePipeline.dsetLayout, nullptr);

    for (const auto& pair : m_pipelines) {
      m_vkd->vkDestroyPipeline(m_vkd->device(), pair.second.pipeHandle, nullptr);
      m_vkd->vkDestroyPipelineLayout(m_vkd->device(), pair.second.pipeLayout, nullptr);
      m_vkd->vkDestroyDescriptorSetLayout (m_vkd->device(), pair.second.dsetLayout, nullptr);
      m_vkd->vkDestroyRenderPass(m_vkd->device(), pair.second.renderPass, nullptr);
    }

    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depthStencil.fragMs, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depthStencil.frag2D, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depthStencil.frag1D, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depth.fragMs, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depth.frag2D, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depth.frag1D, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_color.fragMs, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_color.frag2D, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_color.frag1D, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderGeom,   nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderVert,   nullptr);

    m_vkd->vkDestroySampler(m_vkd->device(), m_sampler, nullptr);
  }

  VkFormat DxvkMetaCopyObjects::getCopyDestinationFormat(
          VkImageAspectFlags    dstAspect,
          VkImageAspectFlags    srcAspect,
          VkFormat              srcFormat) const {
    if (srcAspect == dstAspect)
      return srcFormat;

    if (dstAspect == VK_IMAGE_ASPECT_COLOR_BIT
     && srcAspect == VK_IMAGE_ASPECT_DEPTH_BIT) {
      switch (srcFormat) {
        case VK_FORMAT_D16_UNORM:  return VK_FORMAT_R16_UNORM;
        case VK_FORMAT_D32_SFLOAT: return VK_FORMAT_R32_SFLOAT;
        default:                   return VK_FORMAT_UNDEFINED;
      }
    }

    if (dstAspect == VK_IMAGE_ASPECT_DEPTH_BIT
     && srcAspect == VK_IMAGE_ASPECT_COLOR_BIT) {
      switch (srcFormat) {
        case VK_FORMAT_R16_UNORM:  return VK_FORMAT_D16_UNORM;
        case VK_FORMAT_R32_SFLOAT: return VK_FORMAT_D32_SFLOAT;
        default:                   return VK_FORMAT_UNDEFINED;
      }
    }

    return VK_FORMAT_UNDEFINED;
  }

  DxvkMetaCopyPipeline DxvkMetaCopyObjects::getPipeline(
          VkImageViewType       viewType,
          VkFormat              dstFormat,
          VkSampleCountFlagBits dstSamples) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    DxvkMetaCopyPipelineKey key;
    key.viewType = viewType;
    key.format   = dstFormat;
    key.samples  = dstSamples;

    auto entry = m_pipelines.find(key);
    if (entry != m_pipelines.end())
      return entry->second;

    DxvkMetaCopyPipeline pipeline = createPipeline(key);
    m_pipelines.insert({ key, pipeline });
    return pipeline;
  }

  DxvkMetaCopyPipeline DxvkMetaCopyObjects::getCopyBufferImagePipeline() {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    if (!m_copyBufferImagePipeline.pipeHandle)
      m_copyBufferImagePipeline = createCopyBufferImagePipeline();

    return m_copyBufferImagePipeline;
  }

  VkSampler DxvkMetaCopyObjects::createSampler() const {
    VkSamplerCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.magFilter              = VK_FILTER_NEAREST;
    info.minFilter              = VK_FILTER_NEAREST;
    info.mipmapMode             = VK_SAMPLER_MIPMAP_MODE_NEAREST;
    info.addressModeU           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.addressModeV           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.addressModeW           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.mipLodBias             = 0.0f;
    info.anisotropyEnable       = VK_FALSE;
    info.maxAnisotropy          = 1.0f;
    info.compareEnable          = VK_FALSE;
    info.compareOp              = VK_COMPARE_OP_ALWAYS;
    info.minLod                 = 0.0f;
    info.maxLod                 = 0.0f;
    info.borderColor            = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
    info.unnormalizedCoordinates = VK_FALSE;

    VkSampler result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateSampler(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create sampler");
    return result;
  }

  VkShaderModule DxvkMetaCopyObjects::createShaderModule(
    const SpirvCodeBuffer&          code) const {
    VkShaderModuleCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.codeSize               = code.size();
    info.pCode                  = code.data();

    VkShaderModule result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateShaderModule(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create shader module");
    return result;
  }

  DxvkMetaCopyPipeline DxvkMetaCopyObjects::createCopyBufferImagePipeline() {
    DxvkMetaCopyPipeline pipeline;
    pipeline.renderPass = VK_NULL_HANDLE;

    std::array<VkDescriptorSetLayoutBinding, 2> bindings = {{
      { 0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr },
      { 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr },
    }};

    VkDescriptorSetLayoutCreateInfo setLayoutInfo;
    setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    setLayoutInfo.pNext = nullptr;
    setLayoutInfo.flags = 0;
    setLayoutInfo.bindingCount = bindings.size();
    setLayoutInfo.pBindings = bindings.data();

    if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(), &setLayoutInfo, nullptr, &pipeline.dsetLayout) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create descriptor set layout");

    VkPushConstantRange pushRange = { VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(DxvkCopyBufferImageArgs) };

    VkPipelineLayoutCreateInfo pipelineLayoutInfo;
    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipelineLayoutInfo.pNext = nullptr;
    pipelineLayoutInfo.flags = 0;
    pipelineLayoutInfo.setLayoutCount = 1;
    pipelineLayoutInfo.pSetLayouts = &pipeline.dsetLayout;
    pipelineLayoutInfo.pushConstantRangeCount = 1;
    pipelineLayoutInfo.pPushConstantRanges = &pushRange;

    if (m_vkd->vkCreatePipelineLayout(m_vkd->device(), &pipelineLayoutInfo, nullptr, &pipeline.pipeLayout) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create pipeline layout");

    VkShaderModule shaderModule = createShaderModule(dxvk_copy_buffer_image);

    VkComputePipelineCreateInfo pipelineInfo;
    pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
    pipelineInfo.pNext = nullptr;
    pipelineInfo.flags = 0;
    pipelineInfo.layout = pipeline.pipeLayout;
    pipelineInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    pipelineInfo.stage.pNext = nullptr;
    pipelineInfo.stage.flags = 0;
    pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
    pipelineInfo.stage.module = shaderModule;
    pipelineInfo.stage.pName = "main";
    pipelineInfo.stage.pSpecializationInfo = nullptr;
    pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
    pipelineInfo.basePipelineIndex = -1;

    if (m_vkd->vkCreateComputePipelines(m_vkd->device(), VK_NULL_HANDLE,
        1, &pipelineInfo, nullptr, &pipeline.pipeHandle) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create compute pipeline");

    m_vkd->vkDestroyShaderModule(m_vkd->device(), shaderModule, nullptr);
    return pipeline;
  }

  DxvkMetaCopyPipeline DxvkMetaCopyObjects::createPipeline(
    const DxvkMetaCopyPipelineKey&  key) {
    DxvkMetaCopyPipeline pipeline;
    pipeline.renderPass = this->createRenderPass(key);
    pipeline.dsetLayout = this->createDescriptorSetLayout(key);
    pipeline.pipeLayout = this->createPipelineLayout(pipeline.dsetLayout);
    pipeline.pipeHandle = this->createPipelineObject(key, pipeline.pipeLayout, pipeline.renderPass);
    return pipeline;
  }

  VkRenderPass DxvkMetaCopyObjects::createRenderPass(
    const DxvkMetaCopyPipelineKey&  key) const {
    auto aspect = imageFormatInfo(key.format)->aspectMask;

    VkAttachmentDescription attachment;
    attachment.flags            = 0;
    attachment.format           = key.format;
    attachment.samples          = key.samples;
    attachment.loadOp           = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.storeOp          = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.stencilLoadOp    = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.stencilStoreOp   = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout    = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout      = VK_IMAGE_LAYOUT_GENERAL;

    VkAttachmentReference attachmentRef;
    attachmentRef.attachment    = 0;
    attachmentRef.layout        = (aspect & VK_IMAGE_ASPECT_COLOR_BIT)
      ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
      : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkSubpassDescription subpass;
    subpass.flags                   = 0;
    subpass.pipelineBindPoint       = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.inputAttachmentCount    = 0;
    subpass.pInputAttachments       = nullptr;
    subpass.colorAttachmentCount    = 0;
    subpass.pColorAttachments       = nullptr;
    subpass.pResolveAttachments     = nullptr;
    subpass.pDepthStencilAttachment = nullptr;
    subpass.preserveAttachmentCount = 0;
    subpass.pPreserveAttachments    = nullptr;

    if (aspect & VK_IMAGE_ASPECT_COLOR_BIT) {
      subpass.colorAttachmentCount  = 1;
      subpass.pColorAttachments     = &attachmentRef;
    } else {
      subpass.pDepthStencilAttachment = &attachmentRef;
    }

    VkRenderPassCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.attachmentCount        = 1;
    info.pAttachments           = &attachment;
    info.subpassCount           = 1;
    info.pSubpasses             = &subpass;
    info.dependencyCount        = 0;
    info.pDependencies          = nullptr;

    VkRenderPass result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create render pass");
    return result;
  }

  VkDescriptorSetLayout DxvkMetaCopyObjects::createDescriptorSetLayout(
    const DxvkMetaCopyPipelineKey&  key) const {
    std::array<VkDescriptorSetLayoutBinding, 2> bindings;

    for (uint32_t i = 0; i < 2; i++) {
      bindings[i].binding             = i;
      bindings[i].descriptorType      = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
      bindings[i].descriptorCount     = 1;
      bindings[i].stageFlags          = VK_SHADER_STAGE_FRAGMENT_BIT;
      bindings[i].pImmutableSamplers  = &m_sampler;
    }

    VkDescriptorSetLayoutCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.bindingCount           = 1;
    info.pBindings              = bindings.data();

    auto format = imageFormatInfo(key.format);

    if (format->aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
      info.bindingCount = 2;

    VkDescriptorSetLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create descriptor set layout");
    return result;
  }

  VkPipelineLayout DxvkMetaCopyObjects::createPipelineLayout(
          VkDescriptorSetLayout     descriptorSetLayout) const {
    VkPushConstantRange push;
    push.stageFlags             = VK_SHADER_STAGE_FRAGMENT_BIT;
    push.offset                 = 0;
    push.size                   = sizeof(VkOffset2D);

    VkPipelineLayoutCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.setLayoutCount         = 1;
    info.pSetLayouts            = &descriptorSetLayout;
    info.pushConstantRangeCount = 1;
    info.pPushConstantRanges    = &push;

    VkPipelineLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreatePipelineLayout(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create pipeline layout");
    return result;
  }

  VkPipeline DxvkMetaCopyObjects::createPipelineObject(
    const DxvkMetaCopyPipelineKey&  key,
          VkPipelineLayout          pipelineLayout,
          VkRenderPass              renderPass) {
    auto aspect = imageFormatInfo(key.format)->aspectMask;

    std::array<VkPipelineShaderStageCreateInfo, 3> stages;
    uint32_t stageCount = 0;

    VkPipelineShaderStageCreateInfo& vsStage = stages[stageCount++];
    vsStage.sType               = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    vsStage.pNext               = nullptr;
    vsStage.flags               = 0;
    vsStage.stage               = VK_SHADER_STAGE_VERTEX_BIT;
    vsStage.module              = m_shaderVert;
    vsStage.pName               = "main";
    vsStage.pSpecializationInfo = nullptr;

    if (m_shaderGeom) {
      VkPipelineShaderStageCreateInfo& gsStage = stages[stageCount++];
      gsStage.sType               = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
      gsStage.pNext               = nullptr;
      gsStage.flags               = 0;
      gsStage.stage               = VK_SHADER_STAGE_GEOMETRY_BIT;
      gsStage.module              = m_shaderGeom;
      gsStage.pName               = "main";
      gsStage.pSpecializationInfo = nullptr;
    }

    VkPipelineShaderStageCreateInfo& psStage = stages[stageCount++];
    psStage.sType               = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    psStage.pNext               = nullptr;
    psStage.flags               = 0;
    psStage.stage               = VK_SHADER_STAGE_FRAGMENT_BIT;
    psStage.module              = VK_NULL_HANDLE;
    psStage.pName               = "main";
    psStage.pSpecializationInfo = nullptr;

    std::array<std::pair<const FragShaders*, VkImageAspectFlags>, 3> shaderSets = {{
      { &m_color,        VK_IMAGE_ASPECT_COLOR_BIT },
      { &m_depth,        VK_IMAGE_ASPECT_DEPTH_BIT },
      { &m_depthStencil, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },
    }};

    const FragShaders* shaderSet = nullptr;

    for (const auto& pair : shaderSets) {
      if (pair.second == aspect)
        shaderSet = pair.first;
    }

    if (!shaderSet)
      throw DxvkError("DxvkMetaCopyObjects: Unsupported aspect mask");

    if (key.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY)
      psStage.module = shaderSet->frag1D;
    else if (key.samples == VK_SAMPLE_COUNT_1_BIT)
      psStage.module = shaderSet->frag2D;
    else
      psStage.module = shaderSet->fragMs;

    std::array<VkDynamicState, 2> dynStates = {{
      VK_DYNAMIC_STATE_VIEWPORT,
      VK_DYNAMIC_STATE_SCISSOR,
    }};

    VkPipelineDynamicStateCreateInfo dynState;
    dynState.sType              = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
    dynState.pNext              = nullptr;
    dynState.flags              = 0;
    dynState.dynamicStateCount  = dynStates.size();
    dynState.pDynamicStates     = dynStates.data();

    VkPipelineVertexInputStateCreateInfo viState;
    viState.sType               = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    viState.pNext               = nullptr;
    viState.flags               = 0;
    viState.vertexBindingDescriptionCount   = 0;
    viState.pVertexBindingDescriptions      = nullptr;
    viState.vertexAttributeDescriptionCount = 0;
    viState.pVertexAttributeDescriptions    = nullptr;

    VkPipelineInputAssemblyStateCreateInfo iaState;
    iaState.sType               = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    iaState.pNext               = nullptr;
    iaState.flags               = 0;
    iaState.topology            = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    iaState.primitiveRestartEnable = VK_FALSE;

    VkPipelineViewportStateCreateInfo vpState;
    vpState.sType               = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    vpState.pNext               = nullptr;
    vpState.flags               = 0;
    vpState.viewportCount       = 1;
    vpState.pViewports          = nullptr;
    vpState.scissorCount        = 1;
    vpState.pScissors           = nullptr;

    VkPipelineRasterizationStateCreateInfo rsState;
    rsState.sType               = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rsState.pNext               = nullptr;
    rsState.flags               = 0;
    rsState.depthClampEnable    = VK_TRUE;
    rsState.rasterizerDiscardEnable = VK_FALSE;
    rsState.polygonMode         = VK_POLYGON_MODE_FILL;
    rsState.cullMode            = VK_CULL_MODE_NONE;
    rsState.frontFace           = VK_FRONT_FACE_COUNTER_CLOCKWISE;
    rsState.depthBiasEnable     = VK_FALSE;
    rsState.depthBiasConstantFactor = 0.0f;
    rsState.depthBiasClamp          = 0.0f;
    rsState.depthBiasSlopeFactor    = 0.0f;
    rsState.lineWidth           = 1.0f;

    uint32_t msMask = 0xFFFFFFFF;
    VkPipelineMultisampleStateCreateInfo msState;
    msState.sType                 = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    msState.pNext                 = nullptr;
    msState.flags                 = 0;
    msState.rasterizationSamples  = key.samples;
    msState.sampleShadingEnable   = key.samples != VK_SAMPLE_COUNT_1_BIT;
    msState.minSampleShading      = 1.0f;
    msState.pSampleMask           = &msMask;
    msState.alphaToCoverageEnable = VK_FALSE;
    msState.alphaToOneEnable      = VK_FALSE;

    VkPipelineColorBlendAttachmentState cbAttachment;
    cbAttachment.blendEnable         = VK_FALSE;
    cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
    cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
    cbAttachment.colorBlendOp        = VK_BLEND_OP_ADD;
    cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
    cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
    cbAttachment.alphaBlendOp        = VK_BLEND_OP_ADD;
    cbAttachment.colorWriteMask      =
      VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
      VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;

    VkPipelineColorBlendStateCreateInfo cbState;
    cbState.sType               = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    cbState.pNext               = nullptr;
    cbState.flags               = 0;
    cbState.logicOpEnable       = VK_FALSE;
    cbState.logicOp             = VK_LOGIC_OP_NO_OP;
    cbState.attachmentCount     = 1;
    cbState.pAttachments        = &cbAttachment;

    for (uint32_t i = 0; i < 4; i++)
      cbState.blendConstants[i] = 0.0f;

    VkStencilOpState stencilOp;
    stencilOp.failOp            = VK_STENCIL_OP_REPLACE;
    stencilOp.passOp            = VK_STENCIL_OP_REPLACE;
    stencilOp.depthFailOp       = VK_STENCIL_OP_REPLACE;
    stencilOp.compareOp         = VK_COMPARE_OP_ALWAYS;
    stencilOp.compareMask       = 0xFFFFFFFF;
    stencilOp.writeMask         = 0xFFFFFFFF;
    stencilOp.reference         = 0;

    VkPipelineDepthStencilStateCreateInfo dsState;
    dsState.sType               = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    dsState.pNext               = nullptr;
    dsState.flags               = 0;
    dsState.depthTestEnable     = VK_TRUE;
    dsState.depthWriteEnable    = VK_TRUE;
    dsState.depthCompareOp      = VK_COMPARE_OP_ALWAYS;
    dsState.depthBoundsTestEnable = VK_FALSE;
    dsState.stencilTestEnable   = VK_TRUE;
    dsState.front               = stencilOp;
    dsState.back                = stencilOp;
    dsState.minDepthBounds      = 0.0f;
    dsState.maxDepthBounds      = 1.0f;

    VkGraphicsPipelineCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.stageCount             = stageCount;
    info.pStages                = stages.data();
    info.pVertexInputState      = &viState;
    info.pInputAssemblyState    = &iaState;
    info.pTessellationState     = nullptr;
    info.pViewportState         = &vpState;
    info.pRasterizationState    = &rsState;
    info.pMultisampleState      = &msState;
    info.pColorBlendState       = (aspect & VK_IMAGE_ASPECT_COLOR_BIT) ? &cbState : nullptr;
    info.pDepthStencilState     = (aspect & VK_IMAGE_ASPECT_COLOR_BIT) ? nullptr : &dsState;
    info.pDynamicState          = &dynState;
    info.layout                 = pipelineLayout;
    info.renderPass             = renderPass;
    info.subpass                = 0;
    info.basePipelineHandle     = VK_NULL_HANDLE;
    info.basePipelineIndex      = -1;

    VkPipeline result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateGraphicsPipelines(m_vkd->device(), VK_NULL_HANDLE, 1, &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create graphics pipeline");
    return result;
  }

}


code:
#pragma once

#include <mutex>
#include <unordered_map>

#include "../spirv/spirv_code_buffer.h"

#include "dxvk_barrier.h"
#include "dxvk_cmdlist.h"
#include "dxvk_hash.h"
#include "dxvk_resource.h"

namespace dxvk {

  class DxvkDevice;

  /**
   * \brief Push constants for buffer image copies
   */
  struct DxvkCopyBufferImageArgs {
    VkOffset3D dstOffset; uint32_t pad0;
    VkOffset3D srcOffset; uint32_t pad1;
    VkExtent3D extent;    uint32_t pad2;
    VkExtent2D dstSize;
    VkExtent2D srcSize;
  };

  /**
   * \brief Copy pipeline
   *
   * Stores the objects for a single pipeline
   * that is used for fragment shader copies.
   */
  struct DxvkMetaCopyPipeline {
    VkRenderPass          renderPass;
    VkDescriptorSetLayout dsetLayout;
    VkPipelineLayout      pipeLayout;
    VkPipeline            pipeHandle;
  };

  /**
   * \brief Copy pipeline key
   *
   * Used to look up copy pipelines based
   * on the copy operation they support.
   */
  struct DxvkMetaCopyPipelineKey {
    VkImageViewType       viewType;
    VkFormat              format;
    VkSampleCountFlagBits samples;

    bool eq(const DxvkMetaCopyPipelineKey& other) const {
      return this->viewType == other.viewType
          && this->format   == other.format
          && this->samples  == other.samples;
    }

    size_t hash() const {
      return (uint32_t(format)  << 8)
           ^ (uint32_t(samples) << 4)
           ^ (uint32_t(viewType));
    }
  };

  /**
   * \brief Copy framebuffer and render pass
   *
   * Creates a framebuffer and render
   * pass object for an image view.
   */
  class DxvkMetaCopyRenderPass : public DxvkResource {

  public:

    DxvkMetaCopyRenderPass(
      const Rc<vk::DeviceFn>&   vkd,
      const Rc<DxvkImageView>&  dstImageView,
      const Rc<DxvkImageView>&  srcImageView,
      const Rc<DxvkImageView>&  srcStencilView,
            bool                discardDst);

    ~DxvkMetaCopyRenderPass();

    VkRenderPass renderPass() const {
      return m_renderPass;
    }

    VkFramebuffer framebuffer() const {
      return m_framebuffer;
    }

  private:

    Rc<vk::DeviceFn>  m_vkd;

    Rc<DxvkImageView> m_dstImageView;
    Rc<DxvkImageView> m_srcImageView;
    Rc<DxvkImageView> m_srcStencilView;

    VkRenderPass  m_renderPass  = VK_NULL_HANDLE;
    VkFramebuffer m_framebuffer = VK_NULL_HANDLE;

    VkRenderPass createRenderPass(bool discard) const;

    VkFramebuffer createFramebuffer() const;

  };

  /**
   * \brief Meta copy objects
   *
   * Meta copy operations are necessary in order
   * to copy data between color and depth images.
   */
  class DxvkMetaCopyObjects {

  public:

    DxvkMetaCopyObjects(const DxvkDevice* device);
    ~DxvkMetaCopyObjects();

    /**
     * \brief Queries color format for d->c copies
     *
     * Returns the color format that we need to use
     * as the destination image view format in case
     * of depth to color image copies.
     * \param [in] format Depth format
     * \returns Corresponding color format
     */
    VkFormat getCopyDestinationFormat(
            VkImageAspectFlags    dstAspect,
            VkImageAspectFlags    srcAspect,
            VkFormat              srcFormat) const;

    /**
     * \brief Creates pipeline for meta copy operation
     *
     * \param [in] viewType Image view type
     * \param [in] dstFormat Destination image format
     * \param [in] dstSamples Destination sample count
     * \returns Compatible pipeline for the operation
     */
    DxvkMetaCopyPipeline getPipeline(
            VkImageViewType       viewType,
            VkFormat              dstFormat,
            VkSampleCountFlagBits dstSamples);

    /**
     * \brief Creates pipeline for buffer image copy
     * \returns Compute pipeline for buffer image copies
     */
    DxvkMetaCopyPipeline getCopyBufferImagePipeline();

  private:

    struct FragShaders {
      VkShaderModule frag1D = VK_NULL_HANDLE;
      VkShaderModule frag2D = VK_NULL_HANDLE;
      VkShaderModule fragMs = VK_NULL_HANDLE;
    };

    Rc<vk::DeviceFn> m_vkd;

    VkSampler m_sampler;

    VkShaderModule m_shaderVert = VK_NULL_HANDLE;
    VkShaderModule m_shaderGeom = VK_NULL_HANDLE;

    FragShaders m_color;
    FragShaders m_depth;
    FragShaders m_depthStencil;

    dxvk::mutex m_mutex;

    std::unordered_map<
      DxvkMetaCopyPipelineKey,
      DxvkMetaCopyPipeline,
      DxvkHash, DxvkEq> m_pipelines;

    DxvkMetaCopyPipeline m_copyBufferImagePipeline = { };

    VkSampler createSampler() const;

    VkShaderModule createShaderModule(
      const SpirvCodeBuffer&          code) const;

    DxvkMetaCopyPipeline createCopyBufferImagePipeline();

    DxvkMetaCopyPipeline createPipeline(
      const DxvkMetaCopyPipelineKey&  key);

    VkRenderPass createRenderPass(
      const DxvkMetaCopyPipelineKey&  key) const;

    VkDescriptorSetLayout createDescriptorSetLayout(
      const DxvkMetaCopyPipelineKey&  key) const;

    VkPipelineLayout createPipelineLayout(
            VkDescriptorSetLayout     descriptorSetLayout) const;

    VkPipeline createPipelineObject(
      const DxvkMetaCopyPipelineKey&  key,
            VkPipelineLayout          pipelineLayout,
            VkRenderPass              renderPass);

  };

}

repo_name: orochi/dxvk


code:
#include "dxvk_meta_mipgen.h"

namespace dxvk {

  DxvkMetaMipGenRenderPass::DxvkMetaMipGenRenderPass(
    const Rc<vk::DeviceFn>&   vkd,
    const Rc<DxvkImageView>&  view)
  : m_vkd(vkd), m_view(view), m_renderPass(createRenderPass()) {
    // Determine view type based on image type
    const std::array<std::pair<VkImageViewType, VkImageViewType>, 3> viewTypes = {{
      { VK_IMAGE_VIEW_TYPE_1D_ARRAY, VK_IMAGE_VIEW_TYPE_1D_ARRAY },
      { VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_VIEW_TYPE_2D_ARRAY },
      { VK_IMAGE_VIEW_TYPE_3D,       VK_IMAGE_VIEW_TYPE_2D_ARRAY },
    }};

    m_srcViewType = viewTypes.at(uint32_t(view->imageInfo().type)).first;
    m_dstViewType = viewTypes.at(uint32_t(view->imageInfo().type)).second;

    // Create image views and framebuffers
    m_passes.resize(view->info().numLevels - 1);

    for (uint32_t i = 0; i < m_passes.size(); i++)
      m_passes.at(i) = this->createFramebuffer(i);
  }

  DxvkMetaMipGenRenderPass::~DxvkMetaMipGenRenderPass() {
    for (const auto& pass : m_passes) {
      m_vkd->vkDestroyFramebuffer(m_vkd->device(), pass.framebuffer, nullptr);
      m_vkd->vkDestroyImageView(m_vkd->device(), pass.dstView, nullptr);
      m_vkd->vkDestroyImageView(m_vkd->device(), pass.srcView, nullptr);
    }

    m_vkd->vkDestroyRenderPass(m_vkd->device(), m_renderPass, nullptr);
  }

  VkExtent3D DxvkMetaMipGenRenderPass::passExtent(uint32_t passId) const {
    VkExtent3D extent = m_view->mipLevelExtent(passId + 1);

    if (m_view->imageInfo().type != VK_IMAGE_TYPE_3D)
      extent.depth = m_view->info().numLayers;

    return extent;
  }

  VkRenderPass DxvkMetaMipGenRenderPass::createRenderPass() const {
    std::array<VkSubpassDependency, 2> subpassDeps = {{
      { VK_SUBPASS_EXTERNAL, 0,
        m_view->imageInfo().stages,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0 },
      { 0, VK_SUBPASS_EXTERNAL,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        m_view->imageInfo().stages,
        VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        m_view->imageInfo().access, 0 },
    }};

    VkAttachmentDescription attachment;
    attachment.flags            = 0;
    attachment.format           = m_view->info().format;
    attachment.samples          = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp           = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachment.storeOp          = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.stencilLoadOp    = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachment.stencilStoreOp   = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachment.initialLayout    = VK_IMAGE_LAYOUT_UNDEFINED;
    attachment.finalLayout      = m_view->imageInfo().layout;

    VkAttachmentReference attachmentRef;
    attachmentRef.attachment    = 0;
    attachmentRef.layout        = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    VkSubpassDescription subpass;
    subpass.flags               = 0;
    subpass.pipelineBindPoint   = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.inputAttachmentCount = 0;
    subpass.pInputAttachments   = nullptr;
    subpass.colorAttachmentCount = 1;
    subpass.pColorAttachments   = &attachmentRef;
    subpass.pResolveAttachments = nullptr;
    subpass.pDepthStencilAttachment = nullptr;
    subpass.preserveAttachmentCount = 0;
    subpass.pPreserveAttachments = nullptr;

    VkRenderPassCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.attachmentCount        = 1;
    info.pAttachments           = &attachment;
    info.subpassCount           = 1;
    info.pSubpasses             = &subpass;
    info.dependencyCount        = subpassDeps.size();
    info.pDependencies          = subpassDeps.data();

    VkRenderPass result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create render pass");
    return result;
  }

  DxvkMetaBlitPass DxvkMetaMipGenRenderPass::createFramebuffer(uint32_t pass) const {
    DxvkMetaBlitPass result;
    result.srcView      = VK_NULL_HANDLE;
    result.dstView      = VK_NULL_HANDLE;
    result.renderPass   = m_renderPass;
    result.framebuffer  = VK_NULL_HANDLE;

    // Common image view info
    VkImageViewCreateInfo viewInfo;
    viewInfo.sType      = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewInfo.pNext      = nullptr;
    viewInfo.flags      = 0;
    viewInfo.image      = m_view->imageHandle();
    viewInfo.format     = m_view->info().format;
    viewInfo.components = {
      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };

    // Create source image view, which points to
    // the one mip level we're going to sample.
    VkImageSubresourceRange srcSubresources;
    srcSubresources.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
    srcSubresources.baseMipLevel   = m_view->info().minLevel + pass;
    srcSubresources.levelCount     = 1;
    srcSubresources.baseArrayLayer = m_view->info().minLayer;
    srcSubresources.layerCount     = m_view->info().numLayers;

    viewInfo.viewType         = m_srcViewType;
    viewInfo.subresourceRange = srcSubresources;

    if (m_vkd->vkCreateImageView(m_vkd->device(), &viewInfo, nullptr, &result.srcView) != VK_SUCCESS)
      throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create source image view");

    // Create destination image view, which points
    // to the mip level we're going to render to.
    VkExtent3D dstExtent = m_view->mipLevelExtent(pass + 1);

    VkImageSubresourceRange dstSubresources;
    dstSubresources.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
    dstSubresources.baseMipLevel   = m_view->info().minLevel + pass + 1;
    dstSubresources.levelCount     = 1;

    if (m_view->imageInfo().type != VK_IMAGE_TYPE_3D) {
      dstSubresources.baseArrayLayer = m_view->info().minLayer;
      dstSubresources.layerCount     = m_view->info().numLayers;
    } else {
      dstSubresources.baseArrayLayer = 0;
      dstSubresources.layerCount     = dstExtent.depth;
    }

    viewInfo.viewType         = m_dstViewType;
    viewInfo.subresourceRange = dstSubresources;

    if (m_vkd->vkCreateImageView(m_vkd->device(), &viewInfo, nullptr, &result.dstView) != VK_SUCCESS)
      throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create target image view");

    // Create framebuffer using the destination
    // image view as its color attachment.
    VkFramebufferCreateInfo fboInfo;
    fboInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    fboInfo.pNext           = nullptr;
    fboInfo.flags           = 0;
    fboInfo.renderPass      = m_renderPass;
    fboInfo.attachmentCount = 1;
    fboInfo.pAttachments    = &result.dstView;
    fboInfo.width           = dstExtent.width;
    fboInfo.height          = dstExtent.height;
    fboInfo.layers          = dstSubresources.layerCount;

    if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &fboInfo, nullptr, &result.framebuffer) != VK_SUCCESS)
      throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create target framebuffer");

    return result;
  }

}


code:
#pragma once

#include <vector>

#include "dxvk_meta_blit.h"

namespace dxvk {

  /**
   * \brief Mip map generation render pass
   *
   * Stores image views, framebuffer objects and
   * a render pass object for mip map generation.
   * This must be created per image view.
   */
  class DxvkMetaMipGenRenderPass : public DxvkResource {

  public:

    DxvkMetaMipGenRenderPass(
      const Rc<vk::DeviceFn>&   vkd,
      const Rc<DxvkImageView>&  view);

    ~DxvkMetaMipGenRenderPass();

    /**
     * \brief Render pass handle
     * \returns Render pass handle
     */
    VkRenderPass renderPass() const {
      return m_renderPass;
    }

    /**
     * \brief Source image view type
     *
     * Use this to figure out which type the
     * resource descriptor needs to have.
     * \returns Source image view type
     */
    VkImageViewType viewType() const {
      return m_srcViewType;
    }

    /**
     * \brief Render pass count
     *
     * Number of mip levels to generate.
     * \returns Render pass count
     */
    uint32_t passCount() const {
      return m_passes.size();
    }

    /**
     * \brief Framebuffer handles
     *
     * Returns image view and framebuffer handles
     * required to generate a single mip level.
     * \param [in] pass Render pass index
     * \returns Object handles for the given pass
     */
    DxvkMetaBlitPass pass(uint32_t passId) const {
      return m_passes.at(passId);
    }

    /**
     * \brief Framebuffer size for a given pass
     *
     * Stores the width, height, and layer count
     * of the framebuffer for the given pass ID.
     */
    VkExtent3D passExtent(uint32_t passId) const;

  private:

    Rc<vk::DeviceFn>  m_vkd;
    Rc<DxvkImageView> m_view;

    VkRenderPass m_renderPass;

    VkImageViewType m_srcViewType;
    VkImageViewType m_dstViewType;

    std::vector<DxvkMetaBlitPass> m_passes;

    VkRenderPass createRenderPass() const;

    DxvkMetaBlitPass createFramebuffer(uint32_t pass) const;

  };

}


code:
#include "dxvk_meta_pack.h"
#include "dxvk_device.h"

#include <dxvk_pack_d24s8.h>
#include <dxvk_pack_d32s8.h>

#include <dxvk_unpack_d24s8_as_d32s8.h>
#include <dxvk_unpack_d24s8.h>
#include <dxvk_unpack_d32s8.h>

namespace dxvk {

  DxvkMetaPackObjects::DxvkMetaPackObjects(const DxvkDevice* device)
  : m_vkd             (device->vkd()),
    m_sampler         (createSampler()),
    m_dsetLayoutPack  (createPackDescriptorSetLayout()),
    m_dsetLayoutUnpack(createUnpackDescriptorSetLayout()),
    m_pipeLayoutPack  (createPipelineLayout(m_dsetLayoutPack, sizeof(DxvkMetaPackArgs))),
    m_pipeLayoutUnpack(createPipelineLayout(m_dsetLayoutUnpack, sizeof(DxvkMetaPackArgs))),
    m_templatePack    (createPackDescriptorUpdateTemplate()),
    m_templateUnpack  (createUnpackDescriptorUpdateTemplate()),
    m_pipePackD24S8   (createPipeline(m_pipeLayoutPack, dxvk_pack_d24s8)),
    m_pipePackD32S8   (createPipeline(m_pipeLayoutPack, dxvk_pack_d32s8)),
    m_pipeUnpackD24S8AsD32S8(createPipeline(m_pipeLayoutUnpack, dxvk_unpack_d24s8_as_d32s8)),
    m_pipeUnpackD24S8 (createPipeline(m_pipeLayoutUnpack, dxvk_unpack_d24s8)),
    m_pipeUnpackD32S8 (createPipeline(m_pipeLayoutUnpack, dxvk_unpack_d32s8)) {

  }

  DxvkMetaPackObjects::~DxvkMetaPackObjects() {
    m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeUnpackD32S8, nullptr);
    m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeUnpackD24S8, nullptr);
    m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeUnpackD24S8AsD32S8, nullptr);

    m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipePackD32S8, nullptr);
    m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipePackD24S8, nullptr);

    m_vkd->vkDestroyDescriptorUpdateTemplate(m_vkd->device(), m_templatePack, nullptr);
    m_vkd->vkDestroyDescriptorUpdateTemplate(m_vkd->device(), m_templateUnpack, nullptr);

    m_vkd->vkDestroyPipelineLayout(m_vkd->device(), m_pipeLayoutPack, nullptr);
    m_vkd->vkDestroyPipelineLayout(m_vkd->device(), m_pipeLayoutUnpack, nullptr);

    m_vkd->vkDestroyDescriptorSetLayout(m_vkd->device(), m_dsetLayoutPack, nullptr);
    m_vkd->vkDestroyDescriptorSetLayout(m_vkd->device(), m_dsetLayoutUnpack, nullptr);

    m_vkd->vkDestroySampler(m_vkd->device(), m_sampler, nullptr);
  }

  DxvkMetaPackPipeline DxvkMetaPackObjects::getPackPipeline(VkFormat format) {
    DxvkMetaPackPipeline result;
    result.dsetTemplate = m_templatePack;
    result.dsetLayout   = m_dsetLayoutPack;
    result.pipeLayout   = m_pipeLayoutPack;
    result.pipeHandle   = VK_NULL_HANDLE;

    switch (format) {
      case VK_FORMAT_D24_UNORM_S8_UINT:  result.pipeHandle = m_pipePackD24S8; break;
      case VK_FORMAT_D32_SFLOAT_S8_UINT: result.pipeHandle = m_pipePackD32S8; break;
      default: Logger::err(str::format("DxvkMetaPackObjects: Unknown format: ", format));
    }

    return result;
  }

  DxvkMetaPackPipeline DxvkMetaPackObjects::getUnpackPipeline(
          VkFormat        dstFormat,
          VkFormat        srcFormat) {
    DxvkMetaPackPipeline result;
    result.dsetTemplate = m_templateUnpack;
    result.dsetLayout   = m_dsetLayoutUnpack;
    result.pipeLayout   = m_pipeLayoutUnpack;
    result.pipeHandle   = VK_NULL_HANDLE;

    std::array<std::tuple<VkFormat, VkFormat, VkPipeline>, 3> pipeSelector = {{
      { VK_FORMAT_D24_UNORM_S8_UINT,  VK_FORMAT_D24_UNORM_S8_UINT,  m_pipeUnpackD24S8 },
      { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT,  m_pipeUnpackD24S8AsD32S8 },
      { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, m_pipeUnpackD32S8 },
    }};

    for (const auto& e : pipeSelector) {
      if (std::get<0>(e) == dstFormat
       && std::get<1>(e) == srcFormat)
        result.pipeHandle = std::get<2>(e);
    }

    return result;
  }

  VkSampler DxvkMetaPackObjects::createSampler() {
    VkSamplerCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.magFilter              = VK_FILTER_NEAREST;
    info.minFilter              = VK_FILTER_NEAREST;
    info.mipmapMode             = VK_SAMPLER_MIPMAP_MODE_NEAREST;
    info.addressModeU           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.addressModeV           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.addressModeW           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.mipLodBias             = 0.0f;
    info.anisotropyEnable       = VK_FALSE;
    info.maxAnisotropy          = 1.0f;
    info.compareEnable          = VK_FALSE;
    info.compareOp              = VK_COMPARE_OP_ALWAYS;
    info.minLod                 = 0.0f;
    info.maxLod                 = 0.0f;
    info.borderColor            = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
    info.unnormalizedCoordinates = VK_FALSE;

    VkSampler result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateSampler(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create sampler");
    return result;
  }

  VkDescriptorSetLayout DxvkMetaPackObjects::createPackDescriptorSetLayout() {
    std::array<VkDescriptorSetLayoutBinding, 3> bindings = {{
      { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,         1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr },
      { 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, &m_sampler },
      { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, &m_sampler },
    }};

    VkDescriptorSetLayoutCreateInfo dsetInfo;
    dsetInfo.sType        = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    dsetInfo.pNext        = nullptr;
    dsetInfo.flags        = 0;
    dsetInfo.bindingCount = bindings.size();
    dsetInfo.pBindings    = bindings.data();

    VkDescriptorSetLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(), &dsetInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create descriptor set layout");
    return result;
  }

  VkDescriptorSetLayout DxvkMetaPackObjects::createUnpackDescriptorSetLayout() {
    std::array<VkDescriptorSetLayoutBinding, 3> bindings = {{
      { 0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr },
      { 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr },
      { 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,       1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr },
    }};

    VkDescriptorSetLayoutCreateInfo dsetInfo;
    dsetInfo.sType        = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    dsetInfo.pNext        = nullptr;
    dsetInfo.flags        = 0;
    dsetInfo.bindingCount = bindings.size();
    dsetInfo.pBindings    = bindings.data();

    VkDescriptorSetLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(), &dsetInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create descriptor set layout");
    return result;
  }

  VkPipelineLayout DxvkMetaPackObjects::createPipelineLayout(
          VkDescriptorSetLayout       dsetLayout,
          size_t                      pushLayout) {
    VkPushConstantRange push;
    push.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
    push.offset     = 0;
    push.size       = pushLayout;

    VkPipelineLayoutCreateInfo layoutInfo;
    layoutInfo.sType                  = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    layoutInfo.pNext                  = nullptr;
    layoutInfo.flags                  = 0;
    layoutInfo.setLayoutCount         = 1;
    layoutInfo.pSetLayouts            = &dsetLayout;
    layoutInfo.pushConstantRangeCount = 1;
    layoutInfo.pPushConstantRanges    = &push;

    VkPipelineLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreatePipelineLayout(m_vkd->device(), &layoutInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create pipeline layout");
    return result;
  }

  VkDescriptorUpdateTemplate DxvkMetaPackObjects::createPackDescriptorUpdateTemplate() {
    std::array<VkDescriptorUpdateTemplateEntry, 3> bindings = {{
      { 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,         offsetof(DxvkMetaPackDescriptors, dstBuffer),  0 },
      { 1, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, offsetof(DxvkMetaPackDescriptors, srcDepth),   0 },
      { 2, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, offsetof(DxvkMetaPackDescriptors, srcStencil), 0 },
    }};

    VkDescriptorUpdateTemplateCreateInfo templateInfo;
    templateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO;
    templateInfo.pNext = nullptr;
    templateInfo.flags = 0;
    templateInfo.descriptorUpdateEntryCount = bindings.size();
    templateInfo.pDescriptorUpdateEntries   = bindings.data();
    templateInfo.templateType               = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
    templateInfo.descriptorSetLayout        = m_dsetLayoutPack;
    templateInfo.pipelineBindPoint          = VK_PIPELINE_BIND_POINT_COMPUTE;
    templateInfo.pipelineLayout             = m_pipeLayoutPack;
    templateInfo.set                        = 0;

    VkDescriptorUpdateTemplate result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateDescriptorUpdateTemplate(m_vkd->device(),
          &templateInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create descriptor update template");
    return result;
  }

  VkDescriptorUpdateTemplate DxvkMetaPackObjects::createUnpackDescriptorUpdateTemplate() {
    std::array<VkDescriptorUpdateTemplateEntry, 3> bindings = {{
      { 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, offsetof(DxvkMetaUnpackDescriptors, dstDepth),   0 },
      { 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, offsetof(DxvkMetaUnpackDescriptors, dstStencil), 0 },
      { 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,       offsetof(DxvkMetaUnpackDescriptors, srcBuffer),  0 },
    }};

    VkDescriptorUpdateTemplateCreateInfo templateInfo;
    templateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO;
    templateInfo.pNext = nullptr;
    templateInfo.flags = 0;
    templateInfo.descriptorUpdateEntryCount = bindings.size();
    templateInfo.pDescriptorUpdateEntries   = bindings.data();
    templateInfo.templateType               = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
    templateInfo.descriptorSetLayout        = m_dsetLayoutUnpack;
    templateInfo.pipelineBindPoint          = VK_PIPELINE_BIND_POINT_COMPUTE;
    templateInfo.pipelineLayout             = m_pipeLayoutUnpack;
    templateInfo.set                        = 0;

    VkDescriptorUpdateTemplate result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateDescriptorUpdateTemplate(m_vkd->device(),
          &templateInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create descriptor update template");
    return result;
  }

  VkPipeline DxvkMetaPackObjects::createPipeline(
          VkPipelineLayout      pipeLayout,
    const SpirvCodeBuffer&      code) {
    VkShaderModuleCreateInfo shaderInfo;
    shaderInfo.sType    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    shaderInfo.pNext    = nullptr;
    shaderInfo.flags    = 0;
    shaderInfo.codeSize = code.size();
    shaderInfo.pCode    = code.data();

    VkShaderModule module = VK_NULL_HANDLE;

    if (m_vkd->vkCreateShaderModule(m_vkd->device(), &shaderInfo, nullptr, &module) != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create shader module");

    VkPipelineShaderStageCreateInfo stageInfo;
    stageInfo.sType     = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    stageInfo.pNext     = nullptr;
    stageInfo.flags     = 0;
    stageInfo.stage     = VK_SHADER_STAGE_COMPUTE_BIT;
    stageInfo.module    = module;
    stageInfo.pName     = "main";
    stageInfo.pSpecializationInfo = nullptr;

    VkComputePipelineCreateInfo pipeInfo;
    pipeInfo.sType      = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
    pipeInfo.pNext      = nullptr;
    pipeInfo.flags      = 0;
    pipeInfo.stage      = stageInfo;
    pipeInfo.layout     = pipeLayout;
    pipeInfo.basePipelineHandle = VK_NULL_HANDLE;
    pipeInfo.basePipelineIndex  = -1;

    VkPipeline result = VK_NULL_HANDLE;

    VkResult status = m_vkd->vkCreateComputePipelines(
      m_vkd->device(), VK_NULL_HANDLE, 1, &pipeInfo, nullptr, &result);

    m_vkd->vkDestroyShaderModule(m_vkd->device(), module, nullptr);

    if (status != VK_SUCCESS)
      throw DxvkError("DxvkMetaPackObjects: Failed to create pipeline");
    return result;
  }

}


code:
#pragma once

#include "../spirv/spirv_code_buffer.h"

#include "dxvk_cmdlist.h"
#include "dxvk_resource.h"

namespace dxvk {

  /**
   * \brief Packing arguments
   *
   * Passed in as push constants
   * to the compute shader.
   */
  struct DxvkMetaPackArgs {
    VkOffset2D srcOffset;
    VkExtent2D srcExtent;
    VkOffset2D dstOffset;
    VkExtent2D dstExtent;
  };

  /**
   * \brief Packing pipeline
   *
   * Stores the objects for a single pipeline
   * that is used to pack depth-stencil image
   * data into a linear buffer.
   */
  struct DxvkMetaPackPipeline {
    VkDescriptorUpdateTemplateKHR dsetTemplate;
    VkDescriptorSetLayout         dsetLayout;
    VkPipelineLayout              pipeLayout;
    VkPipeline                    pipeHandle;
  };

  /**
   * \brief Packing descriptors
   */
  struct DxvkMetaPackDescriptors {
    VkDescriptorBufferInfo  dstBuffer;
    VkDescriptorImageInfo   srcDepth;
    VkDescriptorImageInfo   srcStencil;
  };

  /**
   * \brief Unpacking descriptors
   */
  struct DxvkMetaUnpackDescriptors {
    VkBufferView            dstDepth;
    VkBufferView            dstStencil;
    VkDescriptorBufferInfo  srcBuffer;
  };

  /**
   * \brief Depth-stencil pack objects
   *
   * Stores compute shaders and related objects
   * for depth-stencil image packing operations.
   */
  class DxvkMetaPackObjects {

  public:

    DxvkMetaPackObjects(const DxvkDevice* device);
    ~DxvkMetaPackObjects();

    /**
     * \brief Retrieves depth-stencil packing pipeline
     *
     * \param [in] format Destination format
     * \returns Data packing pipeline
     */
    DxvkMetaPackPipeline getPackPipeline(VkFormat format);

    /**
     * \brief Retrieves depth-stencil unpacking pipeline
     *
     * \param [in] dstFormat Destination image format
     * \param [in] srcFormat Source buffer format
     * \returns Data unpacking pipeline
     */
    DxvkMetaPackPipeline getUnpackPipeline(
            VkFormat        dstFormat,
            VkFormat        srcFormat);

  private:

    Rc<vk::DeviceFn>      m_vkd;

    VkSampler             m_sampler;

    VkDescriptorSetLayout m_dsetLayoutPack;
    VkDescriptorSetLayout m_dsetLayoutUnpack;

    VkPipelineLayout      m_pipeLayoutPack;
    VkPipelineLayout      m_pipeLayoutUnpack;

    VkDescriptorUpdateTemplateKHR m_templatePack;
    VkDescriptorUpdateTemplateKHR m_templateUnpack;

    VkPipeline            m_pipePackD24S8;
    VkPipeline            m_pipePackD32S8;

    VkPipeline            m_pipeUnpackD24S8AsD32S8;
    VkPipeline            m_pipeUnpackD24S8;
    VkPipeline            m_pipeUnpackD32S8;

    VkSampler createSampler();

    VkDescriptorSetLayout createPackDescriptorSetLayout();

    VkDescriptorSetLayout createUnpackDescriptorSetLayout();

    VkPipelineLayout createPipelineLayout(
            VkDescriptorSetLayout       dsetLayout,
            size_t                      pushLayout);

    VkDescriptorUpdateTemplateKHR createPackDescriptorUpdateTemplate();

    VkDescriptorUpdateTemplateKHR createUnpackDescriptorUpdateTemplate();

    VkPipeline createPipeline(
            VkPipelineLayout      pipeLayout,
      const SpirvCodeBuffer&      code);

  };

}


code:
#include "dxvk_device.h"
#include "dxvk_meta_resolve.h"

#include <dxvk_fullscreen_geom.h>
#include <dxvk_fullscreen_vert.h>
#include <dxvk_fullscreen_layer_vert.h>

#include <dxvk_resolve_frag_d.h>
#include <dxvk_resolve_frag_ds.h>
#include <dxvk_resolve_frag_f.h>
#include <dxvk_resolve_frag_f_amd.h>
#include <dxvk_resolve_frag_u.h>
#include <dxvk_resolve_frag_i.h>

namespace dxvk {

  DxvkMetaResolveRenderPass::DxvkMetaResolveRenderPass(
    const Rc<vk::DeviceFn>&   vkd,
    const Rc<DxvkImageView>&  dstImageView,
    const Rc<DxvkImageView>&  srcImageView,
    const Rc<DxvkImageView>&  srcStencilView,
          bool                discardDst)
  : m_vkd(vkd),
    m_dstImageView(dstImageView),
    m_srcImageView(srcImageView),
    m_srcStencilView(srcStencilView),
    m_renderPass  (createShaderRenderPass(discardDst)),
    m_framebuffer (createShaderFramebuffer()) { }

  DxvkMetaResolveRenderPass::DxvkMetaResolveRenderPass(
    const Rc<vk::DeviceFn>&        vkd,
    const Rc<DxvkImageView>&       dstImageView,
    const Rc<DxvkImageView>&       srcImageView,
          VkResolveModeFlagBitsKHR modeD,
          VkResolveModeFlagBitsKHR modeS)
  : m_vkd(vkd),
    m_dstImageView(dstImageView),
    m_srcImageView(srcImageView),
    m_renderPass  (createAttachmentRenderPass(modeD, modeS)),
    m_framebuffer (createAttachmentFramebuffer()) { }

  DxvkMetaResolveRenderPass::~DxvkMetaResolveRenderPass() {
    m_vkd->vkDestroyFramebuffer(m_vkd->device(), m_framebuffer, nullptr);
    m_vkd->vkDestroyRenderPass (m_vkd->device(), m_renderPass,  nullptr);
  }

  VkRenderPass DxvkMetaResolveRenderPass::createShaderRenderPass(bool discard) const {
    auto formatInfo = m_dstImageView->formatInfo();
    bool isColorImage = (formatInfo->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT);

    VkAttachmentDescription attachment;
    attachment.flags            = 0;
    attachment.format           = m_dstImageView->info().format;
    attachment.samples          = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp           = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.storeOp          = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.stencilLoadOp    = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.stencilStoreOp   = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout    = m_dstImageView->imageInfo().layout;
    attachment.finalLayout      = m_dstImageView->imageInfo().layout;

    if (discard) {
      attachment.loadOp         = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachment.stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachment.initialLayout  = VK_IMAGE_LAYOUT_UNDEFINED;
    }

    VkImageLayout layout = isColorImage
      ? m_dstImageView->pickLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
      : m_dstImageView->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);

    VkAttachmentReference dstRef;
    dstRef.attachment    = 0;
    dstRef.layout        = layout;

    VkSubpassDescription subpass;
    subpass.flags                   = 0;
    subpass.pipelineBindPoint       = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.inputAttachmentCount    = 0;
    subpass.pInputAttachments       = nullptr;
    subpass.colorAttachmentCount    = isColorImage ? 1 : 0;
    subpass.pColorAttachments       = isColorImage ? &dstRef : nullptr;
    subpass.pResolveAttachments     = nullptr;
    subpass.pDepthStencilAttachment = isColorImage ? nullptr : &dstRef;
    subpass.preserveAttachmentCount = 0;
    subpass.pPreserveAttachments    = nullptr;

    VkPipelineStageFlags cpyStages = 0;
    VkAccessFlags        cpyAccess = 0;

    if (isColorImage) {
      cpyStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
      cpyAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

      if (!discard)
        cpyAccess |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
    } else {
      cpyStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
                |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
      cpyAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

      if (!discard)
        cpyAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
    }

    // Resolve targets are required to be render targets
    VkPipelineStageFlags extStages = m_dstImageView->imageInfo().stages | m_srcImageView->imageInfo().stages;
    VkAccessFlags        extAccess = m_dstImageView->imageInfo().access;

    std::array<VkSubpassDependency, 2> dependencies = {{
      { VK_SUBPASS_EXTERNAL, 0, cpyStages, cpyStages, 0,         cpyAccess, 0 },
      { 0, VK_SUBPASS_EXTERNAL, cpyStages, extStages, cpyAccess, extAccess, 0 },
    }};

    VkRenderPassCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.attachmentCount        = 1;
    info.pAttachments           = &attachment;
    info.subpassCount           = 1;
    info.pSubpasses             = &subpass;
    info.dependencyCount        = dependencies.size();
    info.pDependencies          = dependencies.data();

    VkRenderPass result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveRenderPass: Failed to create render pass");
    return result;
  }

  VkRenderPass DxvkMetaResolveRenderPass::createAttachmentRenderPass(
          VkResolveModeFlagBitsKHR modeD,
          VkResolveModeFlagBitsKHR modeS) const {
    std::array<VkAttachmentDescription2KHR, 2> attachments;
    attachments[0].sType          = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
    attachments[0].pNext          = nullptr;
    attachments[0].flags          = 0;
    attachments[0].format         = m_srcImageView->info().format;
    attachments[0].samples        = m_srcImageView->imageInfo().sampleCount;
    attachments[0].loadOp         = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachments[0].storeOp        = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[0].stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[0].initialLayout  = m_srcImageView->imageInfo().layout;
    attachments[0].finalLayout    = m_srcImageView->imageInfo().layout;

    attachments[1].sType          = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
    attachments[1].pNext          = nullptr;
    attachments[1].flags          = 0;
    attachments[1].format         = m_dstImageView->info().format;
    attachments[1].samples        = VK_SAMPLE_COUNT_1_BIT;
    attachments[1].loadOp         = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachments[1].storeOp        = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[1].stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[1].initialLayout  = m_dstImageView->imageInfo().layout;
    attachments[1].finalLayout    = m_dstImageView->imageInfo().layout;

    if (modeD != VK_RESOLVE_MODE_NONE_KHR && modeS != VK_RESOLVE_MODE_NONE_KHR) {
      attachments[1].loadOp         = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachments[1].stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachments[1].initialLayout  = VK_IMAGE_LAYOUT_UNDEFINED;
    }

    VkAttachmentReference2KHR srcRef;
    srcRef.sType                = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
    srcRef.pNext                = nullptr;
    srcRef.attachment           = 0;
    srcRef.layout               = m_srcImageView->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
    srcRef.aspectMask           = m_srcImageView->formatInfo()->aspectMask;

    VkAttachmentReference2KHR dstRef;
    dstRef.sType                = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
    dstRef.pNext                = nullptr;
    dstRef.attachment           = 1;
    dstRef.layout               = m_dstImageView->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
    dstRef.aspectMask           = m_dstImageView->formatInfo()->aspectMask;

    VkSubpassDescriptionDepthStencilResolveKHR subpassResolve;
    subpassResolve.sType        = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
    subpassResolve.pNext        = nullptr;
    subpassResolve.depthResolveMode   = modeD;
    subpassResolve.stencilResolveMode = modeS;
    subpassResolve.pDepthStencilResolveAttachment = &dstRef;

    VkSubpassDescription2KHR subpass;
    subpass.sType               = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
    subpass.pNext               = &subpassResolve;
    subpass.flags               = 0;
    subpass.pipelineBindPoint   = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.viewMask            = 0;
    subpass.inputAttachmentCount = 0;
    subpass.pInputAttachments   = nullptr;
    subpass.colorAttachmentCount = 0;
    subpass.pColorAttachments   = nullptr;
    subpass.pResolveAttachments = nullptr;
    subpass.pDepthStencilAttachment = &srcRef;
    subpass.preserveAttachmentCount = 0;
    subpass.pPreserveAttachments = nullptr;

    VkPipelineStageFlags cpyStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    VkPipelineStageFlags extStages = m_dstImageView->imageInfo().stages | m_srcImageView->imageInfo().stages;
    VkAccessFlags cpyAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
    VkAccessFlags extAccess = m_dstImageView->imageInfo().access;

    std::array<VkSubpassDependency2KHR, 2> dependencies = {{
      { VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR, nullptr, VK_SUBPASS_EXTERNAL, 0, cpyStages, cpyStages, 0,         cpyAccess, 0 },
      { VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR, nullptr, 0, VK_SUBPASS_EXTERNAL, cpyStages, extStages, cpyAccess, extAccess, 0 },
    }};

    VkRenderPassCreateInfo2KHR info;
    info.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.attachmentCount        = attachments.size();
    info.pAttachments           = attachments.data();
    info.subpassCount           = 1;
    info.pSubpasses             = &subpass;
    info.dependencyCount        = dependencies.size();
    info.pDependencies          = dependencies.data();
    info.correlatedViewMaskCount = 0;
    info.pCorrelatedViewMasks   = nullptr;

    VkRenderPass result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateRenderPass2KHR(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveRenderPass: Failed to create render pass");
    return result;
  }

  VkFramebuffer DxvkMetaResolveRenderPass::createShaderFramebuffer() const {
    VkImageSubresourceRange dstSubresources = m_dstImageView->subresources();
    VkExtent3D              dstExtent       = m_dstImageView->mipLevelExtent(0);
    VkImageView             dstHandle       = m_dstImageView->handle();

    VkFramebufferCreateInfo fboInfo;
    fboInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    fboInfo.pNext           = nullptr;
    fboInfo.flags           = 0;
    fboInfo.renderPass      = m_renderPass;
    fboInfo.attachmentCount = 1;
    fboInfo.pAttachments    = &dstHandle;
    fboInfo.width           = dstExtent.width;
    fboInfo.height          = dstExtent.height;
    fboInfo.layers          = dstSubresources.layerCount;

    VkFramebuffer result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &fboInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveRenderPass: Failed to create target framebuffer");
    return result;
  }

  VkFramebuffer DxvkMetaResolveRenderPass::createAttachmentFramebuffer() const {
    VkImageSubresourceRange dstSubresources = m_dstImageView->subresources();
    VkExtent3D              dstExtent       = m_dstImageView->mipLevelExtent(0);

    std::array<VkImageView, 2> handles = {{
      m_srcImageView->handle(),
      m_dstImageView->handle(),
    }};

    VkFramebufferCreateInfo fboInfo;
    fboInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    fboInfo.pNext           = nullptr;
    fboInfo.flags           = 0;
    fboInfo.renderPass      = m_renderPass;
    fboInfo.attachmentCount = handles.size();
    fboInfo.pAttachments    = handles.data();
    fboInfo.width           = dstExtent.width;
    fboInfo.height          = dstExtent.height;
    fboInfo.layers          = dstSubresources.layerCount;

    VkFramebuffer result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &fboInfo, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveRenderPass: Failed to create target framebuffer");
    return result;
  }

  DxvkMetaResolveObjects::DxvkMetaResolveObjects(const DxvkDevice* device)
  : m_vkd         (device->vkd()),
    m_sampler     (createSampler()),
    m_shaderFragF (device->extensions().amdShaderFragmentMask
      ? createShaderModule(dxvk_resolve_frag_f_amd)
      : createShaderModule(dxvk_resolve_frag_f)),
    m_shaderFragU (createShaderModule(dxvk_resolve_frag_u)),
    m_shaderFragI (createShaderModule(dxvk_resolve_frag_i)),
    m_shaderFragD (createShaderModule(dxvk_resolve_frag_d)) {
    if (device->extensions().extShaderStencilExport)
      m_shaderFragDS = createShaderModule(dxvk_resolve_frag_ds);

    if (device->extensions().extShaderViewportIndexLayer) {
      m_shaderVert = createShaderModule(dxvk_fullscreen_layer_vert);
    } else {
      m_shaderVert = createShaderModule(dxvk_fullscreen_vert);
      m_shaderGeom = createShaderModule(dxvk_fullscreen_geom);
    }
  }

  DxvkMetaResolveObjects::~DxvkMetaResolveObjects() {
    for (const auto& pair : m_pipelines) {
      m_vkd->vkDestroyPipeline(m_vkd->device(), pair.second.pipeHandle, nullptr);
      m_vkd->vkDestroyPipelineLayout(m_vkd->device(), pair.second.pipeLayout, nullptr);
      m_vkd->vkDestroyDescriptorSetLayout(m_vkd->device(), pair.second.dsetLayout, nullptr);
      m_vkd->vkDestroyRenderPass(m_vkd->device(), pair.second.renderPass, nullptr);
    }

    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFragDS, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFragD, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFragF, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFragI, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFragU, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderGeom, nullptr);
    m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderVert, nullptr);

    m_vkd->vkDestroySampler(m_vkd->device(), m_sampler, nullptr);
  }

  DxvkMetaResolvePipeline DxvkMetaResolveObjects::getPipeline(
          VkFormat                  format,
          VkSampleCountFlagBits     samples,
          VkResolveModeFlagBitsKHR  depthResolveMode,
          VkResolveModeFlagBitsKHR  stencilResolveMode) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    DxvkMetaResolvePipelineKey key;
    key.format  = format;
    key.samples = samples;
    key.modeD   = depthResolveMode;
    key.modeS   = stencilResolveMode;

    auto entry = m_pipelines.find(key);
    if (entry != m_pipelines.end())
      return entry->second;

    DxvkMetaResolvePipeline pipeline = createPipeline(key);
    m_pipelines.insert({ key, pipeline });
    return pipeline;
  }

  VkSampler DxvkMetaResolveObjects::createSampler() const {
    VkSamplerCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.magFilter              = VK_FILTER_NEAREST;
    info.minFilter              = VK_FILTER_NEAREST;
    info.mipmapMode             = VK_SAMPLER_MIPMAP_MODE_NEAREST;
    info.addressModeU           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.addressModeV           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.addressModeW           = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    info.mipLodBias             = 0.0f;
    info.anisotropyEnable       = VK_FALSE;
    info.maxAnisotropy          = 1.0f;
    info.compareEnable          = VK_FALSE;
    info.compareOp              = VK_COMPARE_OP_ALWAYS;
    info.minLod                 = 0.0f;
    info.maxLod                 = 0.0f;
    info.borderColor            = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
    info.unnormalizedCoordinates = VK_FALSE;

    VkSampler result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateSampler(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveObjects: Failed to create sampler");
    return result;
  }

  VkShaderModule DxvkMetaResolveObjects::createShaderModule(
    const SpirvCodeBuffer&       code) const {
    VkShaderModuleCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.codeSize               = code.size();
    info.pCode                  = code.data();

    VkShaderModule result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateShaderModule(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create shader module");
    return result;
  }

  DxvkMetaResolvePipeline DxvkMetaResolveObjects::createPipeline(
    const DxvkMetaResolvePipelineKey& key) {
    DxvkMetaResolvePipeline pipeline;
    pipeline.renderPass = this->createRenderPass(key);
    pipeline.dsetLayout = this->createDescriptorSetLayout(key);
    pipeline.pipeLayout = this->createPipelineLayout(pipeline.dsetLayout);
    pipeline.pipeHandle = this->createPipelineObject(key, pipeline.pipeLayout, pipeline.renderPass);
    return pipeline;
  }

  VkRenderPass DxvkMetaResolveObjects::createRenderPass(
    const DxvkMetaResolvePipelineKey& key) {
    auto formatInfo = imageFormatInfo(key.format);
    bool isColorImage = (formatInfo->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT);

    VkAttachmentDescription attachment;
    attachment.flags            = 0;
    attachment.format           = key.format;
    attachment.samples          = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp           = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.storeOp          = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.stencilLoadOp    = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.stencilStoreOp   = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout    = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout      = VK_IMAGE_LAYOUT_GENERAL;

    VkImageLayout layout = isColorImage
      ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
      : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkAttachmentReference attachmentRef;
    attachmentRef.attachment    = 0;
    attachmentRef.layout        = layout;

    VkSubpassDescription subpass;
    subpass.flags                   = 0;
    subpass.pipelineBindPoint       = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.inputAttachmentCount    = 0;
    subpass.pInputAttachments       = nullptr;
    subpass.colorAttachmentCount    = isColorImage ? 1 : 0;
    subpass.pColorAttachments       = isColorImage ? &attachmentRef : nullptr;
    subpass.pResolveAttachments     = nullptr;
    subpass.pDepthStencilAttachment = isColorImage ? nullptr : &attachmentRef;
    subpass.preserveAttachmentCount = 0;
    subpass.pPreserveAttachments    = nullptr;

    VkRenderPassCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.attachmentCount        = 1;
    info.pAttachments           = &attachment;
    info.subpassCount           = 1;
    info.pSubpasses             = &subpass;
    info.dependencyCount        = 0;
    info.pDependencies          = nullptr;

    VkRenderPass result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveObjects: Failed to create render pass");
    return result;
  }

  VkDescriptorSetLayout DxvkMetaResolveObjects::createDescriptorSetLayout(
    const DxvkMetaResolvePipelineKey& key) {
    auto formatInfo = imageFormatInfo(key.format);

    std::array<VkDescriptorSetLayoutBinding, 2> bindings = {{
      { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, &m_sampler },
      { 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, &m_sampler },
    }};

    VkDescriptorSetLayoutCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.bindingCount           = 1;
    info.pBindings              = bindings.data();

    if ((formatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) && key.modeS != VK_RESOLVE_MODE_NONE_KHR)
      info.bindingCount = 2;

    VkDescriptorSetLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaResolveObjects: Failed to create descriptor set layout");
    return result;
  }

  VkPipelineLayout DxvkMetaResolveObjects::createPipelineLayout(
          VkDescriptorSetLayout  descriptorSetLayout) {
    VkPushConstantRange push;
    push.stageFlags             = VK_SHADER_STAGE_FRAGMENT_BIT;
    push.offset                 = 0;
    push.size                   = sizeof(VkOffset2D);

    VkPipelineLayoutCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.setLayoutCount         = 1;
    info.pSetLayouts            = &descriptorSetLayout;
    info.pushConstantRangeCount = 1;
    info.pPushConstantRanges    = &push;

    VkPipelineLayout result = VK_NULL_HANDLE;
    if (m_vkd->vkCreatePipelineLayout(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create pipeline layout");
    return result;
  }

  VkPipeline DxvkMetaResolveObjects::createPipelineObject(
    const DxvkMetaResolvePipelineKey& key,
          VkPipelineLayout       pipelineLayout,
          VkRenderPass           renderPass) {
    auto formatInfo = imageFormatInfo(key.format);
    bool isColorImage = formatInfo->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT;

    std::array<VkPipelineShaderStageCreateInfo, 3> stages;
    uint32_t stageCount = 0;

    std::array<VkSpecializationMapEntry, 3> specEntries = {{
      { 0, offsetof(DxvkMetaResolvePipelineKey, samples), sizeof(VkSampleCountFlagBits) },
      { 1, offsetof(DxvkMetaResolvePipelineKey, modeD),   sizeof(VkResolveModeFlagBitsKHR) },
      { 2, offsetof(DxvkMetaResolvePipelineKey, modeS),   sizeof(VkResolveModeFlagBitsKHR) },
    }};

    VkSpecializationInfo specInfo;
    specInfo.mapEntryCount      = specEntries.size();
    specInfo.pMapEntries        = specEntries.data();
    specInfo.dataSize           = sizeof(key);
    specInfo.pData              = &key;

    VkPipelineShaderStageCreateInfo& vsStage = stages[stageCount++];
    vsStage.sType               = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    vsStage.pNext               = nullptr;
    vsStage.flags               = 0;
    vsStage.stage               = VK_SHADER_STAGE_VERTEX_BIT;
    vsStage.module              = m_shaderVert;
    vsStage.pName               = "main";
    vsStage.pSpecializationInfo = nullptr;

    if (m_shaderGeom) {
      VkPipelineShaderStageCreateInfo& gsStage = stages[stageCount++];
      gsStage.sType               = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
      gsStage.pNext               = nullptr;
      gsStage.flags               = 0;
      gsStage.stage               = VK_SHADER_STAGE_GEOMETRY_BIT;
      gsStage.module              = m_shaderGeom;
      gsStage.pName               = "main";
      gsStage.pSpecializationInfo = nullptr;
    }

    VkPipelineShaderStageCreateInfo& psStage = stages[stageCount++];
    psStage.sType               = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    psStage.pNext               = nullptr;
    psStage.flags               = 0;
    psStage.stage               = VK_SHADER_STAGE_FRAGMENT_BIT;
    psStage.module              = VK_NULL_HANDLE;
    psStage.pName               = "main";
    psStage.pSpecializationInfo = &specInfo;

    if ((formatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) && key.modeS != VK_RESOLVE_MODE_NONE_KHR) {
      if (m_shaderFragDS) {
        psStage.module = m_shaderFragDS;
      } else {
        psStage.module = m_shaderFragD;
        Logger::err("DXVK: Stencil export not supported by device, skipping stencil resolve");
      }
    } else if (formatInfo->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)
      psStage.module = m_shaderFragD;
    else if (formatInfo->flags.test(DxvkFormatFlag::SampledUInt))
      psStage.module = m_shaderFragU;
    else if (formatInfo->flags.test(DxvkFormatFlag::SampledSInt))
      psStage.module = m_shaderFragI;
    else
      psStage.module = m_shaderFragF;

    std::array<VkDynamicState, 2> dynStates = {{
      VK_DYNAMIC_STATE_VIEWPORT,
      VK_DYNAMIC_STATE_SCISSOR,
    }};

    VkPipelineDynamicStateCreateInfo dynState;
    dynState.sType              = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
    dynState.pNext              = nullptr;
    dynState.flags              = 0;
    dynState.dynamicStateCount  = dynStates.size();
    dynState.pDynamicStates     = dynStates.data();

    VkPipelineVertexInputStateCreateInfo viState;
    viState.sType               = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    viState.pNext               = nullptr;
    viState.flags               = 0;
    viState.vertexBindingDescriptionCount   = 0;
    viState.pVertexBindingDescriptions      = nullptr;
    viState.vertexAttributeDescriptionCount = 0;
    viState.pVertexAttributeDescriptions    = nullptr;

    VkPipelineInputAssemblyStateCreateInfo iaState;
    iaState.sType               = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    iaState.pNext               = nullptr;
    iaState.flags               = 0;
    iaState.topology            = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    iaState.primitiveRestartEnable = VK_FALSE;

    VkPipelineViewportStateCreateInfo vpState;
    vpState.sType               = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    vpState.pNext               = nullptr;
    vpState.flags               = 0;
    vpState.viewportCount       = 1;
    vpState.pViewports          = nullptr;
    vpState.scissorCount        = 1;
    vpState.pScissors           = nullptr;

    VkPipelineRasterizationStateCreateInfo rsState;
    rsState.sType               = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rsState.pNext               = nullptr;
    rsState.flags               = 0;
    rsState.depthClampEnable    = VK_TRUE;
    rsState.rasterizerDiscardEnable = VK_FALSE;
    rsState.polygonMode         = VK_POLYGON_MODE_FILL;
    rsState.cullMode            = VK_CULL_MODE_NONE;
    rsState.frontFace           = VK_FRONT_FACE_COUNTER_CLOCKWISE;
    rsState.depthBiasEnable     = VK_FALSE;
    rsState.depthBiasConstantFactor = 0.0f;
    rsState.depthBiasClamp          = 0.0f;
    rsState.depthBiasSlopeFactor    = 0.0f;
    rsState.lineWidth           = 1.0f;

    uint32_t msMask = 0xFFFFFFFF;
    VkPipelineMultisampleStateCreateInfo msState;
    msState.sType                 = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    msState.pNext                 = nullptr;
    msState.flags                 = 0;
    msState.rasterizationSamples  = VK_SAMPLE_COUNT_1_BIT;
    msState.sampleShadingEnable   = VK_FALSE;
    msState.minSampleShading      = 1.0f;
    msState.pSampleMask           = &msMask;
    msState.alphaToCoverageEnable = VK_FALSE;
    msState.alphaToOneEnable      = VK_FALSE;

    VkPipelineColorBlendAttachmentState cbAttachment;
    cbAttachment.blendEnable         = VK_FALSE;
    cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
    cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
    cbAttachment.colorBlendOp        = VK_BLEND_OP_ADD;
    cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
    cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
    cbAttachment.alphaBlendOp        = VK_BLEND_OP_ADD;
    cbAttachment.colorWriteMask      =
      VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
      VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;

    VkPipelineColorBlendStateCreateInfo cbState;
    cbState.sType               = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    cbState.pNext               = nullptr;
    cbState.flags               = 0;
    cbState.logicOpEnable       = VK_FALSE;
    cbState.logicOp             = VK_LOGIC_OP_NO_OP;
    cbState.attachmentCount     = 1;
    cbState.pAttachments        = &cbAttachment;

    for (uint32_t i = 0; i < 4; i++)
      cbState.blendConstants[i] = 0.0f;

    VkStencilOpState stencilOp;
    stencilOp.failOp            = VK_STENCIL_OP_REPLACE;
    stencilOp.passOp            = VK_STENCIL_OP_REPLACE;
    stencilOp.depthFailOp       = VK_STENCIL_OP_REPLACE;
    stencilOp.compareOp         = VK_COMPARE_OP_ALWAYS;
    stencilOp.compareMask       = 0xFFFFFFFF;
    stencilOp.writeMask         = 0xFFFFFFFF;
    stencilOp.reference         = 0;

    VkPipelineDepthStencilStateCreateInfo dsState;
    dsState.sType               = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    dsState.pNext               = nullptr;
    dsState.flags               = 0;
    dsState.depthTestEnable     = key.modeD != VK_RESOLVE_MODE_NONE_KHR;
    dsState.depthWriteEnable    = key.modeD != VK_RESOLVE_MODE_NONE_KHR;
    dsState.depthCompareOp      = VK_COMPARE_OP_ALWAYS;
    dsState.depthBoundsTestEnable = VK_FALSE;
    dsState.stencilTestEnable   = key.modeS != VK_RESOLVE_MODE_NONE_KHR;
    dsState.front               = stencilOp;
    dsState.back                = stencilOp;
    dsState.minDepthBounds      = 0.0f;
    dsState.maxDepthBounds      = 1.0f;

    VkGraphicsPipelineCreateInfo info;
    info.sType                  = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    info.pNext                  = nullptr;
    info.flags                  = 0;
    info.stageCount             = stageCount;
    info.pStages                = stages.data();
    info.pVertexInputState      = &viState;
    info.pInputAssemblyState    = &iaState;
    info.pTessellationState     = nullptr;
    info.pViewportState         = &vpState;
    info.pRasterizationState    = &rsState;
    info.pMultisampleState      = &msState;
    info.pColorBlendState       = isColorImage ? &cbState : nullptr;
    info.pDepthStencilState     = isColorImage ? nullptr : &dsState;
    info.pDynamicState          = &dynState;
    info.layout                 = pipelineLayout;
    info.renderPass             = renderPass;
    info.subpass                = 0;
    info.basePipelineHandle     = VK_NULL_HANDLE;
    info.basePipelineIndex      = -1;

    VkPipeline result = VK_NULL_HANDLE;
    if (m_vkd->vkCreateGraphicsPipelines(m_vkd->device(), VK_NULL_HANDLE, 1, &info, nullptr, &result) != VK_SUCCESS)
      throw DxvkError("DxvkMetaCopyObjects: Failed to create graphics pipeline");
    return result;
  }

}


code:
#pragma once

#include <mutex>
#include <unordered_map>

#include "../spirv/spirv_code_buffer.h"

#include "dxvk_barrier.h"
#include "dxvk_cmdlist.h"
#include "dxvk_resource.h"

namespace dxvk {

  /**
   * \brief Resolve pipeline
   *
   * Stores the objects for a single pipeline
   * that is used for fragment shader resolve.
   */
  struct DxvkMetaResolvePipeline {
    VkRenderPass          renderPass;
    VkDescriptorSetLayout dsetLayout;
    VkPipelineLayout      pipeLayout;
    VkPipeline            pipeHandle;
  };

  /**
   * \brief Copy pipeline key
   *
   * Used to look up copy pipelines based
   * on the copy operation they support.
   */
  struct DxvkMetaResolvePipelineKey {
    VkFormat                  format;
    VkSampleCountFlagBits     samples;
    VkResolveModeFlagBitsKHR  modeD;
    VkResolveModeFlagBitsKHR  modeS;

    bool eq(const DxvkMetaResolvePipelineKey& other) const {
      return this->format  == other.format
          && this->samples == other.samples
          && this->modeD   == other.modeD
          && this->modeS   == other.modeS;
    }

    size_t hash() const {
      return (uint32_t(format)  << 4)
           ^ (uint32_t(samples) << 0)
           ^ (uint32_t(modeD)   << 12)
           ^ (uint32_t(modeS)   << 16);
    }
  };

  /**
   * \brief Meta resolve render pass
   *
   * Stores a framebuffer and image view objects
   * for a meta resolve operation. Can be tracked.
   */
  class DxvkMetaResolveRenderPass : public DxvkResource {

  public:

    DxvkMetaResolveRenderPass(
      const Rc<vk::DeviceFn>&   vkd,
      const Rc<DxvkImageView>&  dstImageView,
      const Rc<DxvkImageView>&  srcImageView,
      const Rc<DxvkImageView>&  srcStencilView,
            bool                discardDst);

    DxvkMetaResolveRenderPass(
      const Rc<vk::DeviceFn>&        vkd,
      const Rc<DxvkImageView>&       dstImageView,
      const Rc<DxvkImageView>&       srcImageView,
            VkResolveModeFlagBitsKHR modeD,
            VkResolveModeFlagBitsKHR modeS);

    ~DxvkMetaResolveRenderPass();

    VkRenderPass renderPass() const {
      return m_renderPass;
    }

    VkFramebuffer framebuffer() const {
      return m_framebuffer;
    }

  private:

    const Rc<vk::DeviceFn>  m_vkd;

    const Rc<DxvkImageView> m_dstImageView;
    const Rc<DxvkImageView> m_srcImageView;
    const Rc<DxvkImageView> m_srcStencilView;

    VkRenderPass  m_renderPass  = VK_NULL_HANDLE;
    VkFramebuffer m_framebuffer = VK_NULL_HANDLE;

    VkRenderPass createShaderRenderPass(bool discard) const;

    VkRenderPass createAttachmentRenderPass(
            VkResolveModeFlagBitsKHR modeD,
            VkResolveModeFlagBitsKHR modeS) const;

    VkFramebuffer createShaderFramebuffer() const;

    VkFramebuffer createAttachmentFramebuffer() const;

  };

  /**
   * \brief Meta resolve objects
   *
   * Implements resolve operations in fragment
   * shaders when using different formats.
   */
  class DxvkMetaResolveObjects {

  public:

    DxvkMetaResolveObjects(const DxvkDevice* device);
    ~DxvkMetaResolveObjects();

    /**
     * \brief Creates pipeline for meta copy operation
     *
     * \param [in] format Destination image format
     * \param [in] samples Destination sample count
     * \param [in] depthResolveMode Depth resolve mode
     * \param [in] stencilResolveMode Stencil resolve mode
     * \returns Compatible pipeline for the operation
     */
    DxvkMetaResolvePipeline getPipeline(
            VkFormat                  format,
            VkSampleCountFlagBits     samples,
            VkResolveModeFlagBitsKHR  depthResolveMode,
            VkResolveModeFlagBitsKHR  stencilResolveMode);

  private:

    Rc<vk::DeviceFn> m_vkd;

    VkSampler m_sampler;

    VkShaderModule m_shaderVert  = VK_NULL_HANDLE;
    VkShaderModule m_shaderGeom  = VK_NULL_HANDLE;
    VkShaderModule m_shaderFragF = VK_NULL_HANDLE;
    VkShaderModule m_shaderFragU = VK_NULL_HANDLE;
    VkShaderModule m_shaderFragI = VK_NULL_HANDLE;
    VkShaderModule m_shaderFragD = VK_NULL_HANDLE;
    VkShaderModule m_shaderFragDS = VK_NULL_HANDLE;

    dxvk::mutex m_mutex;

    std::unordered_map<
      DxvkMetaResolvePipelineKey,
      DxvkMetaResolvePipeline,
      DxvkHash, DxvkEq> m_pipelines;

    VkSampler createSampler() const;

    VkShaderModule createShaderModule(
      const SpirvCodeBuffer&          code) const;

    DxvkMetaResolvePipeline createPipeline(
      const DxvkMetaResolvePipelineKey& key);

    VkRenderPass createRenderPass(
      const DxvkMetaResolvePipelineKey& key);

    VkDescriptorSetLayout createDescriptorSetLayout(
      const DxvkMetaResolvePipelineKey& key);

    VkPipelineLayout createPipelineLayout(
            VkDescriptorSetLayout  descriptorSetLayout);

    VkPipeline createPipelineObject(
      const DxvkMetaResolvePipelineKey& key,
            VkPipelineLayout       pipelineLayout,
            VkRenderPass           renderPass);

  };

}


code:
#pragma once

#include "dxvk_gpu_event.h"
#include "dxvk_gpu_query.h"
#include "dxvk_memory.h"
#include "dxvk_meta_blit.h"
#include "dxvk_meta_clear.h"
#include "dxvk_meta_copy.h"
#include "dxvk_meta_mipgen.h"
#include "dxvk_meta_pack.h"
#include "dxvk_meta_resolve.h"
#include "dxvk_pipemanager.h"
#include "dxvk_renderpass.h"
#include "dxvk_unbound.h"

#include "../util/util_lazy.h"

namespace dxvk {

  class DxvkObjects {

  public:

    DxvkObjects(DxvkDevice* device)
    : m_device          (device),
      m_memoryManager   (device),
      m_renderPassPool  (device),
      m_pipelineManager (device, &m_renderPassPool),
      m_eventPool       (device),
      m_queryPool       (device),
      m_dummyResources  (device) {

    }

    DxvkMemoryAllocator& memoryManager() {
      return m_memoryManager;
    }

    DxvkRenderPassPool& renderPassPool() {
      return m_renderPassPool;
    }

    DxvkPipelineManager& pipelineManager() {
      return m_pipelineManager;
    }

    DxvkGpuEventPool& eventPool() {
      return m_eventPool;
    }

    DxvkGpuQueryPool& queryPool() {
      return m_queryPool;
    }

    DxvkUnboundResources& dummyResources() {
      return m_dummyResources;
    }

    DxvkMetaBlitObjects& metaBlit() {
      return m_metaBlit.get(m_device);
    }

    DxvkMetaClearObjects& metaClear() {
      return m_metaClear.get(m_device);
    }

    DxvkMetaCopyObjects& metaCopy() {
      return m_metaCopy.get(m_device);
    }

    DxvkMetaResolveObjects& metaResolve() {
      return m_metaResolve.get(m_device);
    }

    DxvkMetaPackObjects& metaPack() {
      return m_metaPack.get(m_device);
    }

  private:

    DxvkDevice*                   m_device;

    DxvkMemoryAllocator           m_memoryManager;
    DxvkRenderPassPool            m_renderPassPool;
    DxvkPipelineManager           m_pipelineManager;

    DxvkGpuEventPool              m_eventPool;
    DxvkGpuQueryPool              m_queryPool;

    DxvkUnboundResources          m_dummyResources;

    Lazy<DxvkMetaBlitObjects>     m_metaBlit;
    Lazy<DxvkMetaClearObjects>    m_metaClear;
    Lazy<DxvkMetaCopyObjects>     m_metaCopy;
    Lazy<DxvkMetaResolveObjects>  m_metaResolve;
    Lazy<DxvkMetaPackObjects>     m_metaPack;

  };

}