dolphin/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp
Martino Fontana a14c88ba67 Remove unused imports
Yellow squiggly lines begone!
Done automatically on .cpp files through `run-clang-tidy`, with manual corrections to the mistakes.
If an import is directly used, but is technically unnecessary since it's recursively imported by something else, it is *not* removed.
The tool doesn't touch .h files, so I did some of them by hand while fixing errors due to old recursive imports.
Not everything is removed, but the cleanup should be substantial enough.
Because this done on Linux, code that isn't used on it is mostly untouched.
(Hopefully no open PR is depending on these imports...)
2026-01-25 16:12:15 +01:00

437 lines
20 KiB
C++

// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Vulkan/VKPipeline.h"
#include <array>
#include "Common/Assert.h"
#include "Common/EnumMap.h"
#include "Common/MsgHandler.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/VKShader.h"
#include "VideoBackends/Vulkan/VKTexture.h"
#include "VideoBackends/Vulkan/VKVertexFormat.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
namespace Vulkan
{
VKPipeline::VKPipeline(const AbstractPipelineConfig& config, VkPipeline pipeline,
VkPipelineLayout pipeline_layout, AbstractPipelineUsage usage)
: AbstractPipeline(config), m_pipeline(pipeline), m_pipeline_layout(pipeline_layout),
m_usage(usage)
{
}
VKPipeline::~VKPipeline()
{
vkDestroyPipeline(g_vulkan_context->GetDevice(), m_pipeline, nullptr);
}
static bool IsStripPrimitiveTopology(VkPrimitiveTopology topology)
{
return topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP ||
topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP ||
topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY ||
topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
}
static VkPipelineRasterizationStateCreateInfo
GetVulkanRasterizationState(const RasterizationState& state)
{
static constexpr std::array<VkCullModeFlags, 4> cull_modes = {
{VK_CULL_MODE_NONE, VK_CULL_MODE_BACK_BIT, VK_CULL_MODE_FRONT_BIT,
VK_CULL_MODE_FRONT_AND_BACK}};
bool depth_clamp = g_backend_info.bSupportsDepthClamp;
return {
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkPipelineRasterizationStateCreateFlags flags
depth_clamp, // VkBool32 depthClampEnable
VK_FALSE, // VkBool32 rasterizerDiscardEnable
VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode
cull_modes[u32(state.cull_mode.Value())], // VkCullModeFlags cullMode
VK_FRONT_FACE_CLOCKWISE, // VkFrontFace frontFace
VK_FALSE, // VkBool32 depthBiasEnable
0.0f, // float depthBiasConstantFactor
0.0f, // float depthBiasClamp
0.0f, // float depthBiasSlopeFactor
1.0f // float lineWidth
};
}
static VkPipelineMultisampleStateCreateInfo GetVulkanMultisampleState(const FramebufferState& state)
{
return {
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkPipelineMultisampleStateCreateFlags flags
static_cast<VkSampleCountFlagBits>(
state.samples.Value()), // VkSampleCountFlagBits rasterizationSamples
static_cast<bool>(state.per_sample_shading), // VkBool32 sampleShadingEnable
1.0f, // float minSampleShading
nullptr, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable
VK_FALSE // VkBool32 alphaToOneEnable
};
}
static VkPipelineDepthStencilStateCreateInfo GetVulkanDepthStencilState(const DepthState& state)
{
// Less/greater are swapped due to inverted depth.
VkCompareOp compare_op;
bool inverted_depth = !g_backend_info.bSupportsReversedDepthRange;
switch (state.func)
{
case CompareMode::Never:
compare_op = VK_COMPARE_OP_NEVER;
break;
case CompareMode::Less:
compare_op = inverted_depth ? VK_COMPARE_OP_GREATER : VK_COMPARE_OP_LESS;
break;
case CompareMode::Equal:
compare_op = VK_COMPARE_OP_EQUAL;
break;
case CompareMode::LEqual:
compare_op = inverted_depth ? VK_COMPARE_OP_GREATER_OR_EQUAL : VK_COMPARE_OP_LESS_OR_EQUAL;
break;
case CompareMode::Greater:
compare_op = inverted_depth ? VK_COMPARE_OP_LESS : VK_COMPARE_OP_GREATER;
break;
case CompareMode::NEqual:
compare_op = VK_COMPARE_OP_NOT_EQUAL;
break;
case CompareMode::GEqual:
compare_op = inverted_depth ? VK_COMPARE_OP_LESS_OR_EQUAL : VK_COMPARE_OP_GREATER_OR_EQUAL;
break;
case CompareMode::Always:
compare_op = VK_COMPARE_OP_ALWAYS;
break;
default:
PanicAlertFmt("Invalid compare mode {}", state.func);
compare_op = VK_COMPARE_OP_ALWAYS;
break;
}
return {
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkPipelineDepthStencilStateCreateFlags flags
state.test_enable, // VkBool32 depthTestEnable
state.update_enable, // VkBool32 depthWriteEnable
compare_op, // VkCompareOp depthCompareOp
VK_FALSE, // VkBool32 depthBoundsTestEnable
VK_FALSE, // VkBool32 stencilTestEnable
{}, // VkStencilOpState front
{}, // VkStencilOpState back
0.0f, // float minDepthBounds
1.0f // float maxDepthBounds
};
}
static VkPipelineColorBlendAttachmentState
GetVulkanAttachmentBlendState(const BlendingState& state, AbstractPipelineUsage usage)
{
VkPipelineColorBlendAttachmentState vk_state = {};
bool use_dual_source = state.use_dual_src;
vk_state.blendEnable = static_cast<VkBool32>(state.blend_enable);
vk_state.colorBlendOp = state.subtract ? VK_BLEND_OP_REVERSE_SUBTRACT : VK_BLEND_OP_ADD;
vk_state.alphaBlendOp = state.subtract_alpha ? VK_BLEND_OP_REVERSE_SUBTRACT : VK_BLEND_OP_ADD;
if (use_dual_source)
{
static constexpr Common::EnumMap<VkBlendFactor, SrcBlendFactor::InvDstAlpha> src_factors{
VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_DST_COLOR, VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_SRC1_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
};
static constexpr Common::EnumMap<VkBlendFactor, DstBlendFactor::InvDstAlpha> dst_factors{
VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_SRC_COLOR, VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_SRC1_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
};
vk_state.srcColorBlendFactor = src_factors[state.src_factor];
vk_state.srcAlphaBlendFactor = src_factors[state.src_factor_alpha];
vk_state.dstColorBlendFactor = dst_factors[state.dst_factor];
vk_state.dstAlphaBlendFactor = dst_factors[state.dst_factor_alpha];
}
else
{
static constexpr Common::EnumMap<VkBlendFactor, SrcBlendFactor::InvDstAlpha> src_factors{
VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_DST_COLOR, VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
};
static constexpr Common::EnumMap<VkBlendFactor, DstBlendFactor::InvDstAlpha> dst_factors{
VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_SRC_COLOR, VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
};
vk_state.srcColorBlendFactor = src_factors[state.src_factor];
vk_state.srcAlphaBlendFactor = src_factors[state.src_factor_alpha];
vk_state.dstColorBlendFactor = dst_factors[state.dst_factor];
vk_state.dstAlphaBlendFactor = dst_factors[state.dst_factor_alpha];
}
if (state.color_update)
{
vk_state.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;
}
else
{
vk_state.colorWriteMask = 0;
}
if (state.alpha_update)
vk_state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
return vk_state;
}
static VkPipelineColorBlendStateCreateInfo
GetVulkanColorBlendState(const BlendingState& state,
const VkPipelineColorBlendAttachmentState* attachments,
uint32_t num_attachments)
{
static constexpr std::array<VkLogicOp, 16> vk_logic_ops = {
{VK_LOGIC_OP_CLEAR, VK_LOGIC_OP_AND, VK_LOGIC_OP_AND_REVERSE, VK_LOGIC_OP_COPY,
VK_LOGIC_OP_AND_INVERTED, VK_LOGIC_OP_NO_OP, VK_LOGIC_OP_XOR, VK_LOGIC_OP_OR,
VK_LOGIC_OP_NOR, VK_LOGIC_OP_EQUIVALENT, VK_LOGIC_OP_INVERT, VK_LOGIC_OP_OR_REVERSE,
VK_LOGIC_OP_COPY_INVERTED, VK_LOGIC_OP_OR_INVERTED, VK_LOGIC_OP_NAND, VK_LOGIC_OP_SET}};
VkBool32 vk_logic_op_enable = static_cast<VkBool32>(state.logic_op_enable);
if (vk_logic_op_enable && !g_backend_info.bSupportsLogicOp)
{
// At the time of writing, Adreno and Mali drivers didn't support logic ops.
// The "emulation" through blending path has been removed, so just disable it completely.
// These drivers don't support dual-source blend either, so issues are to be expected.
vk_logic_op_enable = VK_FALSE;
}
VkLogicOp vk_logic_op =
vk_logic_op_enable ? vk_logic_ops[u32(state.logic_mode.Value())] : VK_LOGIC_OP_CLEAR;
VkPipelineColorBlendStateCreateInfo vk_state = {
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkPipelineColorBlendStateCreateFlags flags
vk_logic_op_enable, // VkBool32 logicOpEnable
vk_logic_op, // VkLogicOp logicOp
num_attachments, // uint32_t attachmentCount
attachments, // const VkPipelineColorBlendAttachmentState* pAttachments
{1.0f, 1.0f, 1.0f, 1.0f} // float blendConstants[4]
};
return vk_state;
}
std::unique_ptr<VKPipeline> VKPipeline::Create(const AbstractPipelineConfig& config)
{
DEBUG_ASSERT(config.vertex_shader && config.pixel_shader);
// Get render pass for config.
VkRenderPass render_pass = g_object_cache->GetRenderPass(
VKTexture::GetVkFormatForHostTextureFormat(config.framebuffer_state.color_texture_format),
VKTexture::GetVkFormatForHostTextureFormat(config.framebuffer_state.depth_texture_format),
config.framebuffer_state.samples, VK_ATTACHMENT_LOAD_OP_LOAD,
config.framebuffer_state.additional_color_attachment_count);
if (render_pass == VK_NULL_HANDLE)
{
PanicAlertFmt("Failed to get render pass");
return nullptr;
}
// Get pipeline layout.
VkPipelineLayout pipeline_layout;
switch (config.usage)
{
case AbstractPipelineUsage::GX:
pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
break;
case AbstractPipelineUsage::GXUber:
pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UBER);
break;
case AbstractPipelineUsage::Utility:
pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UTILITY);
break;
default:
PanicAlertFmt("Unknown pipeline layout.");
return nullptr;
}
// Declare descriptors for empty vertex buffers/attributes
static const VkPipelineVertexInputStateCreateInfo empty_vertex_input_state = {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkPipelineVertexInputStateCreateFlags flags
0, // uint32_t vertexBindingDescriptionCount
nullptr, // const VkVertexInputBindingDescription* pVertexBindingDescriptions
0, // uint32_t vertexAttributeDescriptionCount
nullptr // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions
};
// Vertex inputs
const VkPipelineVertexInputStateCreateInfo& vertex_input_state =
config.vertex_format ?
static_cast<const VertexFormat*>(config.vertex_format)->GetVertexInputStateInfo() :
empty_vertex_input_state;
// Input assembly
static constexpr std::array<VkPrimitiveTopology, 4> vk_primitive_topologies = {
{VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP}};
VkPipelineInputAssemblyStateCreateInfo input_assembly_state = {
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0,
vk_primitive_topologies[static_cast<u32>(config.rasterization_state.primitive.Value())],
VK_FALSE};
// See Vulkan spec, section 19:
// If topology is VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
// VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
// VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY or VK_PRIMITIVE_TOPOLOGY_PATCH_LIST,
// primitiveRestartEnable must be VK_FALSE
if (g_backend_info.bSupportsPrimitiveRestart &&
IsStripPrimitiveTopology(input_assembly_state.topology))
{
input_assembly_state.primitiveRestartEnable = VK_TRUE;
}
// Shaders to stages
VkPipelineShaderStageCreateInfo shader_stages[3];
uint32_t num_shader_stages = 0;
if (config.vertex_shader)
{
shader_stages[num_shader_stages++] = {
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr,
0,
VK_SHADER_STAGE_VERTEX_BIT,
static_cast<const VKShader*>(config.vertex_shader)->GetShaderModule(),
"main"};
}
if (config.geometry_shader)
{
shader_stages[num_shader_stages++] = {
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr,
0,
VK_SHADER_STAGE_GEOMETRY_BIT,
static_cast<const VKShader*>(config.geometry_shader)->GetShaderModule(),
"main"};
}
if (config.pixel_shader)
{
shader_stages[num_shader_stages++] = {
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr,
0,
VK_SHADER_STAGE_FRAGMENT_BIT,
static_cast<const VKShader*>(config.pixel_shader)->GetShaderModule(),
"main"};
}
// Fill in Vulkan descriptor structs from our state structures.
VkPipelineRasterizationStateCreateInfo rasterization_state =
GetVulkanRasterizationState(config.rasterization_state);
VkPipelineMultisampleStateCreateInfo multisample_state =
GetVulkanMultisampleState(config.framebuffer_state);
VkPipelineDepthStencilStateCreateInfo depth_stencil_state =
GetVulkanDepthStencilState(config.depth_state);
VkPipelineColorBlendAttachmentState blend_attachment_state =
GetVulkanAttachmentBlendState(config.blending_state, config.usage);
std::vector<VkPipelineColorBlendAttachmentState> blend_attachment_states;
blend_attachment_states.push_back(blend_attachment_state);
// Right now all our attachments have the same state
for (u8 i = 0; i < static_cast<u8>(config.framebuffer_state.additional_color_attachment_count);
i++)
{
blend_attachment_states.push_back(blend_attachment_state);
}
VkPipelineColorBlendStateCreateInfo blend_state =
GetVulkanColorBlendState(config.blending_state, blend_attachment_states.data(),
static_cast<uint32_t>(blend_attachment_states.size()));
static const VkDepthClampRangeEXT clamp_range = {0.0f, MAX_EFB_DEPTH};
static const VkPipelineViewportDepthClampControlCreateInfoEXT depth_clamp_state = {
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLAMP_CONTROL_CREATE_INFO_EXT, nullptr,
VK_DEPTH_CLAMP_MODE_USER_DEFINED_RANGE_EXT, // VkDepthClampModeEXT depthClampMode
&clamp_range // const VkDepthClampRangeEXT* pDepthClampRange
};
// This viewport isn't used, but needs to be specified anyway.
static const VkViewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
static const VkRect2D scissor = {{0, 0}, {1, 1}};
static const VkPipelineViewportStateCreateInfo viewport_state = {
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
g_backend_info.bSupportsUnrestrictedDepthRange ? &depth_clamp_state : nullptr,
0, // VkPipelineViewportStateCreateFlags flags;
1, // uint32_t viewportCount
&viewport, // const VkViewport* pViewports
1, // uint32_t scissorCount
&scissor // const VkRect2D* pScissors
};
// Set viewport and scissor dynamic state so we can change it elsewhere.
static const std::array<VkDynamicState, 2> dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
static const VkPipelineDynamicStateCreateInfo dynamic_state = {
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, nullptr,
0, // VkPipelineDynamicStateCreateFlags flags
static_cast<u32>(dynamic_states.size()), // uint32_t dynamicStateCount
dynamic_states.data() // const VkDynamicState* pDynamicStates
};
// Combine to full pipeline info structure.
VkGraphicsPipelineCreateInfo pipeline_info = {
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
nullptr, // VkStructureType sType
0, // VkPipelineCreateFlags flags
num_shader_stages, // uint32_t stageCount
shader_stages, // const VkPipelineShaderStageCreateInfo* pStages
&vertex_input_state, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState
&input_assembly_state, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState
nullptr, // const VkPipelineTessellationStateCreateInfo* pTessellationState
&viewport_state, // const VkPipelineViewportStateCreateInfo* pViewportState
&rasterization_state, // const VkPipelineRasterizationStateCreateInfo* pRasterizationState
&multisample_state, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState
&depth_stencil_state, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState
&blend_state, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState
&dynamic_state, // const VkPipelineDynamicStateCreateInfo* pDynamicState
pipeline_layout, // VkPipelineLayout layout
render_pass, // VkRenderPass renderPass
0, // uint32_t subpass
VK_NULL_HANDLE, // VkPipeline basePipelineHandle
-1 // int32_t basePipelineIndex
};
VkPipeline pipeline;
VkResult res =
vkCreateGraphicsPipelines(g_vulkan_context->GetDevice(), g_object_cache->GetPipelineCache(),
1, &pipeline_info, nullptr, &pipeline);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateGraphicsPipelines failed: ");
return VK_NULL_HANDLE;
}
return std::make_unique<VKPipeline>(config, pipeline, pipeline_layout, config.usage);
}
} // namespace Vulkan