duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

vulkan_pipeline.cpp (11428B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "vulkan_pipeline.h"
      5 #include "vulkan_builders.h"
      6 #include "vulkan_device.h"
      7 
      8 #include "common/assert.h"
      9 #include "common/error.h"
     10 #include "common/log.h"
     11 
     12 Log_SetChannel(VulkanDevice);
     13 
     14 VulkanShader::VulkanShader(GPUShaderStage stage, VkShaderModule mod) : GPUShader(stage), m_module(mod)
     15 {
     16 }
     17 
     18 VulkanShader::~VulkanShader()
     19 {
     20   vkDestroyShaderModule(VulkanDevice::GetInstance().GetVulkanDevice(), m_module, nullptr);
     21 }
     22 
     23 void VulkanShader::SetDebugName(std::string_view name)
     24 {
     25   Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_module, name);
     26 }
     27 
     28 std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
     29                                                                 Error* error)
     30 {
     31   VkShaderModule mod;
     32 
     33   const VkShaderModuleCreateInfo ci = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, nullptr, 0, data.size(),
     34                                        reinterpret_cast<const u32*>(data.data())};
     35   VkResult res = vkCreateShaderModule(m_device, &ci, nullptr, &mod);
     36   if (res != VK_SUCCESS)
     37   {
     38     LOG_VULKAN_ERROR(res, "vkCreateShaderModule() failed: ");
     39     Error::SetStringFmt(error, "vkCreateShaderModule() failed: {}", Vulkan::VkResultToString(res));
     40     return {};
     41   }
     42 
     43   return std::unique_ptr<GPUShader>(new VulkanShader(stage, mod));
     44 }
     45 
     46 std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
     47                                                                 std::string_view source, const char* entry_point,
     48                                                                 DynamicHeapArray<u8>* out_binary, Error* error)
     49 {
     50   if (language == GPUShaderLanguage::SPV)
     51   {
     52     if (out_binary)
     53       out_binary->assign(reinterpret_cast<const u8*>(source.data()), source.length());
     54 
     55     return CreateShaderFromBinary(
     56       stage, std::span<const u8>(reinterpret_cast<const u8*>(source.data()), source.length()), error);
     57   }
     58 
     59   DynamicHeapArray<u8> local_binary;
     60   DynamicHeapArray<u8>* dest_binary = out_binary ? out_binary : &local_binary;
     61   if (!CompileGLSLShaderToVulkanSpv(stage, language, source, entry_point, !m_debug_device,
     62                                     m_optional_extensions.vk_khr_shader_non_semantic_info, dest_binary, error))
     63   {
     64     return {};
     65   }
     66 
     67   AssertMsg((dest_binary->size() % 4) == 0, "Compile result should be 4 byte aligned.");
     68 
     69   return CreateShaderFromBinary(stage, dest_binary->cspan(), error);
     70 }
     71 
     72 //////////////////////////////////////////////////////////////////////////
     73 
     74 VulkanPipeline::VulkanPipeline(VkPipeline pipeline, Layout layout, u8 vertices_per_primitive,
     75                                RenderPassFlag render_pass_flags)
     76   : GPUPipeline(), m_pipeline(pipeline), m_layout(layout), m_vertices_per_primitive(vertices_per_primitive),
     77     m_render_pass_flags(render_pass_flags)
     78 {
     79 }
     80 
     81 VulkanPipeline::~VulkanPipeline()
     82 {
     83   VulkanDevice::GetInstance().DeferPipelineDestruction(m_pipeline);
     84 }
     85 
     86 void VulkanPipeline::SetDebugName(std::string_view name)
     87 {
     88   Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_pipeline, name);
     89 }
     90 
     91 std::unique_ptr<GPUPipeline> VulkanDevice::CreatePipeline(const GPUPipeline::GraphicsConfig& config, Error* error)
     92 {
     93   static constexpr std::array<std::pair<VkPrimitiveTopology, u32>, static_cast<u32>(GPUPipeline::Primitive::MaxCount)>
     94     primitives = {{
     95       {VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 1},     // Points
     96       {VK_PRIMITIVE_TOPOLOGY_LINE_LIST, 2},      // Lines
     97       {VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 3},  // Triangles
     98       {VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 3}, // TriangleStrips
     99     }};
    100 
    101   static constexpr u32 MAX_COMPONENTS = 4;
    102   static constexpr const VkFormat format_mapping[static_cast<u8>(
    103     GPUPipeline::VertexAttribute::Type::MaxCount)][MAX_COMPONENTS] = {
    104     {VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT}, // Float
    105     {VK_FORMAT_R8_UINT, VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8B8_UINT, VK_FORMAT_R8G8B8A8_UINT},                   // UInt8
    106     {VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8B8_SINT, VK_FORMAT_R8G8B8A8_SINT},                   // SInt8
    107     {VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8A8_UNORM},           // UNorm8
    108     {VK_FORMAT_R16_UINT, VK_FORMAT_R16G16_UINT, VK_FORMAT_R16G16B16_UINT, VK_FORMAT_R16G16B16A16_UINT},     // UInt16
    109     {VK_FORMAT_R16_SINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16B16_SINT, VK_FORMAT_R16G16B16A16_SINT},     // SInt16
    110     {VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16B16_UNORM, VK_FORMAT_R16G16B16A16_UNORM}, // UNorm16
    111     {VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32B32_UINT, VK_FORMAT_R32G32B32A32_UINT},     // UInt32
    112     {VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32B32_SINT, VK_FORMAT_R32G32B32A32_SINT},     // SInt32
    113   };
    114 
    115   static constexpr std::array<VkCullModeFlagBits, static_cast<u32>(GPUPipeline::CullMode::MaxCount)> cull_mapping = {{
    116     VK_CULL_MODE_NONE,      // None
    117     VK_CULL_MODE_FRONT_BIT, // Front
    118     VK_CULL_MODE_BACK_BIT,  // Back
    119   }};
    120 
    121   static constexpr std::array<VkCompareOp, static_cast<u32>(GPUPipeline::DepthFunc::MaxCount)> compare_mapping = {{
    122     VK_COMPARE_OP_NEVER,            // Never
    123     VK_COMPARE_OP_ALWAYS,           // Always
    124     VK_COMPARE_OP_LESS,             // Less
    125     VK_COMPARE_OP_LESS_OR_EQUAL,    // LessEqual
    126     VK_COMPARE_OP_GREATER,          // Greater
    127     VK_COMPARE_OP_GREATER_OR_EQUAL, // GreaterEqual
    128     VK_COMPARE_OP_EQUAL,            // Equal
    129   }};
    130 
    131   static constexpr std::array<VkBlendFactor, static_cast<u32>(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{
    132     VK_BLEND_FACTOR_ZERO,                     // Zero
    133     VK_BLEND_FACTOR_ONE,                      // One
    134     VK_BLEND_FACTOR_SRC_COLOR,                // SrcColor
    135     VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,      // InvSrcColor
    136     VK_BLEND_FACTOR_DST_COLOR,                // DstColor
    137     VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,      // InvDstColor
    138     VK_BLEND_FACTOR_SRC_ALPHA,                // SrcAlpha
    139     VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,      // InvSrcAlpha
    140     VK_BLEND_FACTOR_SRC1_ALPHA,               // SrcAlpha1
    141     VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,     // InvSrcAlpha1
    142     VK_BLEND_FACTOR_DST_ALPHA,                // DstAlpha
    143     VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,      // InvDstAlpha
    144     VK_BLEND_FACTOR_CONSTANT_COLOR,           // ConstantAlpha
    145     VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, // InvConstantAlpha
    146   }};
    147 
    148   static constexpr std::array<VkBlendOp, static_cast<u32>(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{
    149     VK_BLEND_OP_ADD,              // Add
    150     VK_BLEND_OP_SUBTRACT,         // Subtract
    151     VK_BLEND_OP_REVERSE_SUBTRACT, // ReverseSubtract
    152     VK_BLEND_OP_MIN,              // Min
    153     VK_BLEND_OP_MAX,              // Max
    154   }};
    155 
    156   Vulkan::GraphicsPipelineBuilder gpb;
    157   gpb.SetVertexShader(static_cast<const VulkanShader*>(config.vertex_shader)->GetModule());
    158   gpb.SetFragmentShader(static_cast<const VulkanShader*>(config.fragment_shader)->GetModule());
    159 
    160   if (config.geometry_shader)
    161     gpb.SetGeometryShader(static_cast<const VulkanShader*>(config.geometry_shader)->GetModule());
    162 
    163   if (!config.input_layout.vertex_attributes.empty())
    164   {
    165     gpb.AddVertexBuffer(0, config.input_layout.vertex_stride);
    166     for (u32 i = 0; i < static_cast<u32>(config.input_layout.vertex_attributes.size()); i++)
    167     {
    168       const GPUPipeline::VertexAttribute& va = config.input_layout.vertex_attributes[i];
    169       DebugAssert(va.components > 0 && va.components <= MAX_COMPONENTS);
    170       gpb.AddVertexAttribute(
    171         i, 0, format_mapping[static_cast<u8>(va.type.GetValue())][static_cast<u8>(va.components.GetValue() - 1)],
    172         va.offset);
    173     }
    174   }
    175 
    176   const auto [vk_topology, vertices_per_primitive] = primitives[static_cast<u8>(config.primitive)];
    177   gpb.SetPrimitiveTopology(vk_topology);
    178 
    179   // Line width?
    180 
    181   gpb.SetRasterizationState(VK_POLYGON_MODE_FILL,
    182                             cull_mapping[static_cast<u8>(config.rasterization.cull_mode.GetValue())],
    183                             VK_FRONT_FACE_CLOCKWISE);
    184   if (config.samples > 1)
    185     gpb.SetMultisamples(config.samples, config.per_sample_shading);
    186   gpb.SetDepthState(config.depth.depth_test != GPUPipeline::DepthFunc::Always || config.depth.depth_write,
    187                     config.depth.depth_write, compare_mapping[static_cast<u8>(config.depth.depth_test.GetValue())]);
    188   gpb.SetNoStencilState();
    189 
    190   for (u32 i = 0; i < MAX_RENDER_TARGETS; i++)
    191   {
    192     if (config.color_formats[i] == GPUTexture::Format::Unknown)
    193       break;
    194 
    195     gpb.SetBlendAttachment(i, config.blend.enable, blend_mapping[static_cast<u8>(config.blend.src_blend.GetValue())],
    196                            blend_mapping[static_cast<u8>(config.blend.dst_blend.GetValue())],
    197                            op_mapping[static_cast<u8>(config.blend.blend_op.GetValue())],
    198                            blend_mapping[static_cast<u8>(config.blend.src_alpha_blend.GetValue())],
    199                            blend_mapping[static_cast<u8>(config.blend.dst_alpha_blend.GetValue())],
    200                            op_mapping[static_cast<u8>(config.blend.alpha_blend_op.GetValue())],
    201                            config.blend.write_mask);
    202   }
    203 
    204   const auto blend_constants = config.blend.GetConstantFloatColor();
    205   gpb.SetBlendConstants(blend_constants[0], blend_constants[1], blend_constants[2], blend_constants[3]);
    206 
    207   gpb.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
    208   gpb.AddDynamicState(VK_DYNAMIC_STATE_SCISSOR);
    209 
    210   gpb.SetPipelineLayout(m_pipeline_layouts[static_cast<size_t>(GetPipelineLayoutType(config.render_pass_flags))]
    211                                           [static_cast<size_t>(config.layout)]);
    212 
    213   if (m_optional_extensions.vk_khr_dynamic_rendering && (m_optional_extensions.vk_khr_dynamic_rendering_local_read ||
    214                                                          !(config.render_pass_flags & GPUPipeline::ColorFeedbackLoop)))
    215   {
    216     gpb.SetDynamicRendering();
    217 
    218     for (u32 i = 0; i < MAX_RENDER_TARGETS; i++)
    219     {
    220       if (config.color_formats[i] == GPUTexture::Format::Unknown)
    221         break;
    222 
    223       gpb.AddDynamicRenderingColorAttachment(
    224         VulkanDevice::TEXTURE_FORMAT_MAPPING[static_cast<u8>(config.color_formats[i])]);
    225     }
    226 
    227     if (config.depth_format != GPUTexture::Format::Unknown)
    228     {
    229       gpb.SetDynamicRenderingDepthAttachment(VulkanDevice::TEXTURE_FORMAT_MAPPING[static_cast<u8>(config.depth_format)],
    230                                              VK_FORMAT_UNDEFINED);
    231     }
    232 
    233     if (config.render_pass_flags & GPUPipeline::ColorFeedbackLoop)
    234     {
    235       DebugAssert(m_optional_extensions.vk_khr_dynamic_rendering_local_read &&
    236                   config.color_formats[0] != GPUTexture::Format::Unknown);
    237       gpb.AddDynamicRenderingInputAttachment(0);
    238     }
    239   }
    240   else
    241   {
    242     const VkRenderPass render_pass = GetRenderPass(config);
    243     DebugAssert(render_pass != VK_NULL_HANDLE);
    244     gpb.SetRenderPass(render_pass, 0);
    245   }
    246 
    247   const VkPipeline pipeline = gpb.Create(m_device, m_pipeline_cache, false, error);
    248   if (!pipeline)
    249     return {};
    250 
    251   return std::unique_ptr<GPUPipeline>(
    252     new VulkanPipeline(pipeline, config.layout, static_cast<u8>(vertices_per_primitive), config.render_pass_flags));
    253 }