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 }