Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "PipelineState.h"
21 :
22 : #include "lib/hash.h"
23 : #include "ps/CLogger.h"
24 : #include "ps/containers/StaticVector.h"
25 : #include "renderer/backend/vulkan/Device.h"
26 : #include "renderer/backend/vulkan/Framebuffer.h"
27 : #include "renderer/backend/vulkan/Mapping.h"
28 : #include "renderer/backend/vulkan/ShaderProgram.h"
29 : #include "renderer/backend/vulkan/Utilities.h"
30 :
31 : #include <algorithm>
32 :
33 : namespace Renderer
34 : {
35 :
36 : namespace Backend
37 : {
38 :
39 : namespace Vulkan
40 : {
41 :
42 : namespace
43 : {
44 :
45 0 : VkStencilOpState MakeStencilOpState(const SStencilOpState& opState)
46 : {
47 0 : VkStencilOpState result{};
48 0 : result.failOp = Mapping::FromStencilOp(opState.failOp);
49 0 : result.passOp = Mapping::FromStencilOp(opState.passOp);
50 0 : result.depthFailOp = Mapping::FromStencilOp(opState.depthFailOp);
51 0 : result.compareOp = Mapping::FromCompareOp(opState.compareOp);
52 0 : return result;
53 : }
54 :
55 : } // anonymous namespace
56 :
57 0 : size_t CGraphicsPipelineState::CacheKeyHash::operator()(const CacheKey& cacheKey) const
58 : {
59 0 : size_t seed = 0;
60 0 : hash_combine(seed, cacheKey.vertexInputLayoutUID);
61 0 : hash_combine(seed, cacheKey.framebufferUID);
62 0 : return seed;
63 : }
64 :
65 0 : bool CGraphicsPipelineState::CacheKeyEqual::operator()(const CacheKey& lhs, const CacheKey& rhs) const
66 : {
67 : return
68 0 : lhs.vertexInputLayoutUID == rhs.vertexInputLayoutUID &&
69 0 : lhs.framebufferUID == rhs.framebufferUID;
70 : }
71 :
72 : // static
73 0 : std::unique_ptr<CGraphicsPipelineState> CGraphicsPipelineState::Create(
74 : CDevice* device, const SGraphicsPipelineStateDesc& desc)
75 : {
76 0 : ENSURE(desc.shaderProgram);
77 0 : std::unique_ptr<CGraphicsPipelineState> pipelineState{new CGraphicsPipelineState()};
78 0 : pipelineState->m_Device = device;
79 0 : pipelineState->m_Desc = desc;
80 0 : return pipelineState;
81 : }
82 :
83 0 : CGraphicsPipelineState::~CGraphicsPipelineState()
84 : {
85 0 : for (const auto& it : m_PipelineMap)
86 : {
87 0 : if (it.second != VK_NULL_HANDLE)
88 0 : m_Device->ScheduleObjectToDestroy(
89 0 : VK_OBJECT_TYPE_PIPELINE, it.second, VK_NULL_HANDLE);
90 : }
91 0 : }
92 :
93 0 : VkPipeline CGraphicsPipelineState::GetOrCreatePipeline(
94 : const CVertexInputLayout* vertexInputLayout, CFramebuffer* framebuffer)
95 : {
96 0 : CShaderProgram* shaderProgram = m_Desc.shaderProgram->As<CShaderProgram>();
97 :
98 : const CacheKey cacheKey =
99 : {
100 0 : vertexInputLayout->GetUID(), framebuffer->GetUID()
101 0 : };
102 0 : auto it = m_PipelineMap.find(cacheKey);
103 0 : if (it != m_PipelineMap.end())
104 0 : return it->second;
105 :
106 0 : PS::StaticVector<VkVertexInputBindingDescription, 16> attributeBindings;
107 0 : PS::StaticVector<VkVertexInputAttributeDescription, 16> attributes;
108 :
109 0 : const VkPhysicalDeviceLimits& limits = m_Device->GetChoosenPhysicalDevice().properties.limits;
110 0 : const uint32_t maxVertexInputAttributes = limits.maxVertexInputAttributes;
111 0 : const uint32_t maxVertexInputAttributeOffset = limits.maxVertexInputAttributeOffset;
112 0 : for (const SVertexAttributeFormat& vertexAttributeFormat : vertexInputLayout->GetAttributes())
113 : {
114 0 : ENSURE(vertexAttributeFormat.bindingSlot < maxVertexInputAttributes);
115 0 : ENSURE(vertexAttributeFormat.offset < maxVertexInputAttributeOffset);
116 0 : const uint32_t streamLocation = shaderProgram->GetStreamLocation(vertexAttributeFormat.stream);
117 0 : if (streamLocation == std::numeric_limits<uint32_t>::max())
118 0 : continue;
119 0 : auto it = std::find_if(attributeBindings.begin(), attributeBindings.end(),
120 0 : [slot = vertexAttributeFormat.bindingSlot](const VkVertexInputBindingDescription& desc) -> bool
121 0 : {
122 0 : return desc.binding == slot;
123 0 : });
124 : const VkVertexInputBindingDescription desc{
125 0 : vertexAttributeFormat.bindingSlot,
126 0 : vertexAttributeFormat.stride,
127 0 : vertexAttributeFormat.rate == VertexAttributeRate::PER_INSTANCE
128 0 : ? VK_VERTEX_INPUT_RATE_INSTANCE
129 0 : : VK_VERTEX_INPUT_RATE_VERTEX };
130 0 : if (it == attributeBindings.end())
131 0 : attributeBindings.emplace_back(desc);
132 : else
133 : {
134 : // All attribute sharing the same binding slot should have the same description.
135 0 : ENSURE(desc.inputRate == it->inputRate && desc.stride == it->stride);
136 : }
137 0 : attributes.push_back({
138 : streamLocation,
139 0 : vertexAttributeFormat.bindingSlot,
140 0 : Mapping::FromFormat(vertexAttributeFormat.format),
141 0 : vertexAttributeFormat.offset
142 : });
143 : }
144 :
145 0 : VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo{};
146 0 : vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
147 0 : vertexInputCreateInfo.vertexBindingDescriptionCount = std::size(attributeBindings);
148 0 : vertexInputCreateInfo.pVertexBindingDescriptions = attributeBindings.data();
149 0 : vertexInputCreateInfo.vertexAttributeDescriptionCount = std::size(attributes);
150 0 : vertexInputCreateInfo.pVertexAttributeDescriptions = attributes.data();
151 :
152 0 : VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo{};
153 0 : inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
154 0 : inputAssemblyCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
155 0 : inputAssemblyCreateInfo.primitiveRestartEnable = VK_FALSE;
156 :
157 : // We don't need to specify sizes for viewports and scissors as they're in
158 : // dynamic state.
159 0 : VkViewport viewport{};
160 0 : viewport.x = 0.0f;
161 0 : viewport.y = 0.0f;
162 0 : viewport.width = 0.0f;
163 0 : viewport.height = 0.0f;
164 0 : viewport.minDepth = 0.0f;
165 0 : viewport.maxDepth = 1.0f;
166 :
167 0 : VkRect2D scissor{};
168 :
169 0 : VkPipelineViewportStateCreateInfo viewportStateCreateInfo{};
170 0 : viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
171 0 : viewportStateCreateInfo.viewportCount = 1;
172 0 : viewportStateCreateInfo.pViewports = &viewport;
173 0 : viewportStateCreateInfo.scissorCount = 1;
174 0 : viewportStateCreateInfo.pScissors = &scissor;
175 :
176 0 : VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo{};
177 0 : depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
178 0 : depthStencilStateCreateInfo.depthTestEnable =
179 0 : m_Desc.depthStencilState.depthTestEnabled ? VK_TRUE : VK_FALSE;
180 0 : depthStencilStateCreateInfo.depthWriteEnable =
181 0 : m_Desc.depthStencilState.depthWriteEnabled ? VK_TRUE : VK_FALSE;
182 0 : depthStencilStateCreateInfo.depthCompareOp =
183 0 : Mapping::FromCompareOp(m_Desc.depthStencilState.depthCompareOp);
184 0 : depthStencilStateCreateInfo.stencilTestEnable =
185 0 : m_Desc.depthStencilState.stencilTestEnabled ? VK_TRUE : VK_FALSE;
186 :
187 0 : depthStencilStateCreateInfo.front =
188 0 : MakeStencilOpState(m_Desc.depthStencilState.stencilFrontFace);
189 0 : depthStencilStateCreateInfo.front.reference = m_Desc.depthStencilState.stencilReference;
190 0 : depthStencilStateCreateInfo.front.compareMask = m_Desc.depthStencilState.stencilReadMask;
191 0 : depthStencilStateCreateInfo.front.writeMask = m_Desc.depthStencilState.stencilWriteMask;
192 :
193 0 : depthStencilStateCreateInfo.back =
194 0 : MakeStencilOpState(m_Desc.depthStencilState.stencilBackFace);
195 0 : depthStencilStateCreateInfo.back.reference = m_Desc.depthStencilState.stencilReference;
196 0 : depthStencilStateCreateInfo.back.compareMask = m_Desc.depthStencilState.stencilReadMask;
197 0 : depthStencilStateCreateInfo.back.writeMask = m_Desc.depthStencilState.stencilWriteMask;
198 :
199 0 : VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo{};
200 0 : rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
201 0 : rasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
202 0 : rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
203 :
204 0 : rasterizationStateCreateInfo.polygonMode =
205 0 : Mapping::FromPolygonMode(m_Desc.rasterizationState.polygonMode);
206 0 : rasterizationStateCreateInfo.cullMode =
207 0 : Mapping::FromCullMode(m_Desc.rasterizationState.cullMode);
208 0 : rasterizationStateCreateInfo.frontFace =
209 0 : m_Desc.rasterizationState.frontFace == FrontFace::CLOCKWISE
210 0 : ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE;
211 :
212 0 : rasterizationStateCreateInfo.depthBiasEnable =
213 0 : m_Desc.rasterizationState.depthBiasEnabled ? VK_TRUE : VK_FALSE;
214 0 : rasterizationStateCreateInfo.depthBiasConstantFactor =
215 0 : m_Desc.rasterizationState.depthBiasConstantFactor;
216 0 : rasterizationStateCreateInfo.depthBiasSlopeFactor =
217 0 : m_Desc.rasterizationState.depthBiasSlopeFactor;
218 0 : rasterizationStateCreateInfo.lineWidth = 1.0f;
219 :
220 0 : VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo{};
221 0 : multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
222 0 : multisampleStateCreateInfo.rasterizationSamples =
223 0 : Mapping::FromSampleCount(framebuffer->GetSampleCount());
224 0 : multisampleStateCreateInfo.minSampleShading = 1.0f;
225 :
226 0 : VkPipelineColorBlendAttachmentState colorBlendAttachmentState{};
227 0 : colorBlendAttachmentState.blendEnable = m_Desc.blendState.enabled ? VK_TRUE : VK_FALSE;
228 0 : colorBlendAttachmentState.colorBlendOp =
229 0 : Mapping::FromBlendOp(m_Desc.blendState.colorBlendOp);
230 0 : colorBlendAttachmentState.srcColorBlendFactor =
231 0 : Mapping::FromBlendFactor(m_Desc.blendState.srcColorBlendFactor);
232 0 : colorBlendAttachmentState.dstColorBlendFactor =
233 0 : Mapping::FromBlendFactor(m_Desc.blendState.dstColorBlendFactor);
234 0 : colorBlendAttachmentState.alphaBlendOp =
235 0 : Mapping::FromBlendOp(m_Desc.blendState.alphaBlendOp);
236 0 : colorBlendAttachmentState.srcAlphaBlendFactor =
237 0 : Mapping::FromBlendFactor(m_Desc.blendState.srcAlphaBlendFactor);
238 0 : colorBlendAttachmentState.dstAlphaBlendFactor =
239 0 : Mapping::FromBlendFactor(m_Desc.blendState.dstAlphaBlendFactor);
240 0 : colorBlendAttachmentState.colorWriteMask =
241 0 : Mapping::FromColorWriteMask(m_Desc.blendState.colorWriteMask);
242 :
243 0 : VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo{};
244 0 : colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
245 0 : colorBlendStateCreateInfo.logicOpEnable = VK_FALSE;
246 0 : colorBlendStateCreateInfo.logicOp = VK_LOGIC_OP_CLEAR;
247 0 : colorBlendStateCreateInfo.attachmentCount = 1;
248 0 : colorBlendStateCreateInfo.pAttachments = &colorBlendAttachmentState;
249 0 : colorBlendStateCreateInfo.blendConstants[0] = m_Desc.blendState.constant.r;
250 0 : colorBlendStateCreateInfo.blendConstants[1] = m_Desc.blendState.constant.g;
251 0 : colorBlendStateCreateInfo.blendConstants[2] = m_Desc.blendState.constant.b;
252 0 : colorBlendStateCreateInfo.blendConstants[3] = m_Desc.blendState.constant.a;
253 :
254 0 : const VkDynamicState dynamicStates[] =
255 : {
256 : VK_DYNAMIC_STATE_SCISSOR,
257 : VK_DYNAMIC_STATE_VIEWPORT
258 : };
259 :
260 0 : VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo{};
261 0 : dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
262 0 : dynamicStateCreateInfo.dynamicStateCount = static_cast<uint32_t>(std::size(dynamicStates));
263 0 : dynamicStateCreateInfo.pDynamicStates = dynamicStates;
264 :
265 0 : VkGraphicsPipelineCreateInfo pipelineCreateInfo{};
266 0 : pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
267 :
268 0 : pipelineCreateInfo.stageCount = shaderProgram->GetStages().size();
269 0 : pipelineCreateInfo.pStages = shaderProgram->GetStages().data();
270 :
271 0 : pipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo;
272 0 : pipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo;
273 0 : pipelineCreateInfo.pViewportState = &viewportStateCreateInfo;
274 0 : pipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo;
275 0 : pipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo;
276 : // If renderPass is not VK_NULL_HANDLE, the pipeline is being created with
277 : // fragment shader state, and subpass uses a depth/stencil attachment,
278 : // pDepthStencilState must be a not null pointer.
279 0 : if (framebuffer->GetDepthStencilAttachment())
280 0 : pipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo;
281 0 : pipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo;
282 0 : pipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;
283 :
284 0 : pipelineCreateInfo.layout = shaderProgram->GetPipelineLayout();
285 0 : pipelineCreateInfo.renderPass = framebuffer->GetRenderPass();
286 0 : pipelineCreateInfo.subpass = 0;
287 0 : pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
288 0 : pipelineCreateInfo.basePipelineIndex = -1;
289 :
290 0 : VkPipeline pipeline = VK_NULL_HANDLE;
291 0 : ENSURE_VK_SUCCESS(vkCreateGraphicsPipelines(
292 : m_Device->GetVkDevice(), VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline));
293 :
294 0 : m_PipelineMap[cacheKey] = pipeline;
295 :
296 0 : return pipeline;
297 : }
298 :
299 0 : IDevice* CGraphicsPipelineState::GetDevice()
300 : {
301 0 : return m_Device;
302 : }
303 :
304 : } // namespace Vulkan
305 :
306 : } // namespace Backend
307 :
308 3 : } // namespace Renderer
|