1 #include <cstring>
2 #include <memory>
3 #include <set>
4 #include <sstream>
5
6 #include "Common/Profiler/Profiler.h"
7
8 #include "Common/Log.h"
9 #include "Common/StringUtils.h"
10 #include "Common/GPU/Vulkan/VulkanContext.h"
11 #include "GPU/Vulkan/VulkanUtil.h"
12 #include "GPU/Vulkan/PipelineManagerVulkan.h"
13 #include "GPU/Vulkan/ShaderManagerVulkan.h"
14 #include "GPU/Common/DrawEngineCommon.h"
15 #include "Common/GPU/thin3d.h"
16 #include "Common/GPU/Vulkan/VulkanRenderManager.h"
17 #include "Common/GPU/Vulkan/VulkanQueueRunner.h"
18
19 using namespace PPSSPP_VK;
20
PipelineManagerVulkan(VulkanContext * vulkan)21 PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) {
22 // The pipeline cache is created on demand (or explicitly through Load).
23 }
24
~PipelineManagerVulkan()25 PipelineManagerVulkan::~PipelineManagerVulkan() {
26 Clear();
27 if (pipelineCache_ != VK_NULL_HANDLE)
28 vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
29 }
30
Clear()31 void PipelineManagerVulkan::Clear() {
32 // This should kill off all the shaders at once.
33 // This could also be an opportunity to store the whole cache to disk. Will need to also
34 // store the keys.
35
36 pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
37 if (value->pipeline)
38 vulkan_->Delete().QueueDeletePipeline(value->pipeline);
39 delete value;
40 });
41
42 pipelines_.Clear();
43 }
44
DeviceLost()45 void PipelineManagerVulkan::DeviceLost() {
46 Clear();
47 if (pipelineCache_ != VK_NULL_HANDLE)
48 vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
49 }
50
DeviceRestore(VulkanContext * vulkan)51 void PipelineManagerVulkan::DeviceRestore(VulkanContext *vulkan) {
52 vulkan_ = vulkan;
53 // The pipeline cache is created on demand.
54 }
55
56 struct DeclTypeInfo {
57 VkFormat type;
58 const char *name;
59 };
60
61 static const DeclTypeInfo VComp[] = {
62 { VK_FORMAT_UNDEFINED, "NULL" }, // DEC_NONE,
63 { VK_FORMAT_R32_SFLOAT, "R32_SFLOAT " }, // DEC_FLOAT_1,
64 { VK_FORMAT_R32G32_SFLOAT, "R32G32_SFLOAT " }, // DEC_FLOAT_2,
65 { VK_FORMAT_R32G32B32_SFLOAT, "R32G32B32_SFLOAT " }, // DEC_FLOAT_3,
66 { VK_FORMAT_R32G32B32A32_SFLOAT, "R32G32B32A32_SFLOAT " }, // DEC_FLOAT_4,
67
68 { VK_FORMAT_R8G8B8A8_SNORM, "R8G8B8A8_SNORM" }, // DEC_S8_3,
69 { VK_FORMAT_R16G16B16A16_SNORM, "R16G16B16A16_SNORM " }, // DEC_S16_3,
70
71 { VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_1,
72 { VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_2,
73 { VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_3,
74 { VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_4,
75 { VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_1,
76 { VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_2,
77 { VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_3,
78 { VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_4,
79 };
80
VertexAttribSetup(VkVertexInputAttributeDescription * attr,int fmt,int offset,PspAttributeLocation location)81 static void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) {
82 _assert_(fmt != DEC_NONE);
83 _assert_(fmt < ARRAY_SIZE(VComp));
84 attr->location = (uint32_t)location;
85 attr->binding = 0;
86 attr->format = VComp[fmt].type;
87 attr->offset = offset;
88 }
89
90 // Returns the number of attributes that were set.
91 // We could cache these AttributeDescription arrays (with pspFmt as the key), but hardly worth bothering
92 // as we will only call this code when we need to create a new VkPipeline.
SetupVertexAttribs(VkVertexInputAttributeDescription attrs[],const DecVtxFormat & decFmt)93 static int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
94 int count = 0;
95 if (decFmt.w0fmt != 0) {
96 VertexAttribSetup(&attrs[count++], decFmt.w0fmt, decFmt.w0off, PspAttributeLocation::W1);
97 }
98 if (decFmt.w1fmt != 0) {
99 VertexAttribSetup(&attrs[count++], decFmt.w1fmt, decFmt.w1off, PspAttributeLocation::W2);
100 }
101 if (decFmt.uvfmt != 0) {
102 VertexAttribSetup(&attrs[count++], decFmt.uvfmt, decFmt.uvoff, PspAttributeLocation::TEXCOORD);
103 }
104 if (decFmt.c0fmt != 0) {
105 VertexAttribSetup(&attrs[count++], decFmt.c0fmt, decFmt.c0off, PspAttributeLocation::COLOR0);
106 }
107 if (decFmt.c1fmt != 0) {
108 VertexAttribSetup(&attrs[count++], decFmt.c1fmt, decFmt.c1off, PspAttributeLocation::COLOR1);
109 }
110 if (decFmt.nrmfmt != 0) {
111 VertexAttribSetup(&attrs[count++], decFmt.nrmfmt, decFmt.nrmoff, PspAttributeLocation::NORMAL);
112 }
113 // Position is always there.
114 VertexAttribSetup(&attrs[count++], decFmt.posfmt, decFmt.posoff, PspAttributeLocation::POSITION);
115 return count;
116 }
117
SetupVertexAttribsPretransformed(VkVertexInputAttributeDescription attrs[],bool needsUV,bool needsColor1)118 static int SetupVertexAttribsPretransformed(VkVertexInputAttributeDescription attrs[], bool needsUV, bool needsColor1) {
119 int count = 0;
120 VertexAttribSetup(&attrs[count++], DEC_FLOAT_4, 0, PspAttributeLocation::POSITION);
121 if (needsUV) {
122 VertexAttribSetup(&attrs[count++], DEC_FLOAT_3, 16, PspAttributeLocation::TEXCOORD);
123 }
124 VertexAttribSetup(&attrs[count++], DEC_U8_4, 28, PspAttributeLocation::COLOR0);
125 if (needsColor1) {
126 VertexAttribSetup(&attrs[count++], DEC_U8_4, 32, PspAttributeLocation::COLOR1);
127 }
128 return count;
129 }
130
UsesBlendConstant(int factor)131 static bool UsesBlendConstant(int factor) {
132 switch (factor) {
133 case VK_BLEND_FACTOR_CONSTANT_ALPHA:
134 case VK_BLEND_FACTOR_CONSTANT_COLOR:
135 case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA:
136 case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR:
137 return true;
138 default:
139 return false;
140 }
141 }
142
CutFromMain(std::string str)143 static std::string CutFromMain(std::string str) {
144 std::vector<std::string> lines;
145 SplitString(str, '\n', lines);
146
147 std::string rebuilt;
148 bool foundStart = false;
149 int c = 0;
150 for (const std::string &str : lines) {
151 if (startsWith(str, "void main")) {
152 foundStart = true;
153 rebuilt += StringFromFormat("... (cut %d lines)\n", c);
154 }
155 if (foundStart) {
156 rebuilt += str + "\n";
157 }
158 c++;
159 }
160 return rebuilt;
161 }
162
CreateVulkanPipeline(VkDevice device,VkPipelineCache pipelineCache,VkPipelineLayout layout,VkRenderPass renderPass,const VulkanPipelineRasterStateKey & key,const DecVtxFormat * decFmt,VulkanVertexShader * vs,VulkanFragmentShader * fs,bool useHwTransform,float lineWidth)163 static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache,
164 VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key,
165 const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, float lineWidth) {
166 PROFILE_THIS_SCOPE("pipelinebuild");
167 bool useBlendConstant = false;
168
169 VkPipelineColorBlendAttachmentState blend0{};
170 blend0.blendEnable = key.blendEnable;
171 if (key.blendEnable) {
172 blend0.colorBlendOp = (VkBlendOp)key.blendOpColor;
173 blend0.alphaBlendOp = (VkBlendOp)key.blendOpAlpha;
174 blend0.srcColorBlendFactor = (VkBlendFactor)key.srcColor;
175 blend0.srcAlphaBlendFactor = (VkBlendFactor)key.srcAlpha;
176 blend0.dstColorBlendFactor = (VkBlendFactor)key.destColor;
177 blend0.dstAlphaBlendFactor = (VkBlendFactor)key.destAlpha;
178 }
179 blend0.colorWriteMask = key.colorWriteMask;
180
181 VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
182 cbs.flags = 0;
183 cbs.pAttachments = &blend0;
184 cbs.attachmentCount = 1;
185 cbs.logicOpEnable = key.logicOpEnable;
186 if (key.logicOpEnable)
187 cbs.logicOp = (VkLogicOp)key.logicOp;
188 else
189 cbs.logicOp = VK_LOGIC_OP_COPY;
190
191 VkPipelineDepthStencilStateCreateInfo dss{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
192 dss.depthBoundsTestEnable = false;
193 dss.stencilTestEnable = key.stencilTestEnable;
194 if (key.stencilTestEnable) {
195 dss.front.compareOp = (VkCompareOp)key.stencilCompareOp;
196 dss.front.passOp = (VkStencilOp)key.stencilPassOp;
197 dss.front.failOp = (VkStencilOp)key.stencilFailOp;
198 dss.front.depthFailOp = (VkStencilOp)key.stencilDepthFailOp;
199 // Back stencil is always the same as front on PSP.
200 memcpy(&dss.back, &dss.front, sizeof(dss.front));
201 }
202 dss.depthTestEnable = key.depthTestEnable;
203 if (key.depthTestEnable) {
204 dss.depthCompareOp = (VkCompareOp)key.depthCompareOp;
205 dss.depthWriteEnable = key.depthWriteEnable;
206 }
207
208 VkDynamicState dynamicStates[8]{};
209 int numDyn = 0;
210 if (key.blendEnable &&
211 (UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) {
212 dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
213 useBlendConstant = true;
214 }
215 dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR;
216 dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT;
217 if (key.stencilTestEnable) {
218 dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
219 dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
220 dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
221 }
222
223 VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
224 ds.flags = 0;
225 ds.pDynamicStates = dynamicStates;
226 ds.dynamicStateCount = numDyn;
227
228 VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
229 rs.flags = 0;
230 rs.depthBiasEnable = false;
231 rs.cullMode = key.cullMode;
232 rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
233 rs.lineWidth = lineWidth;
234 rs.rasterizerDiscardEnable = false;
235 rs.polygonMode = VK_POLYGON_MODE_FILL;
236 rs.depthClampEnable = key.depthClampEnable;
237
238 VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
239 ms.pSampleMask = nullptr;
240 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
241
242 VkPipelineShaderStageCreateInfo ss[2]{};
243 ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
244 ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
245 ss[0].pSpecializationInfo = nullptr;
246 ss[0].module = vs->GetModule();
247 ss[0].pName = "main";
248 ss[0].flags = 0;
249 ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
250 ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
251 ss[1].pSpecializationInfo = nullptr;
252 ss[1].module = fs->GetModule();
253 ss[1].pName = "main";
254 ss[1].flags = 0;
255
256 if (!ss[0].module || !ss[1].module) {
257 ERROR_LOG(G3D, "Failed creating graphics pipeline - bad shaders");
258 // Create a placeholder to avoid creating over and over if shader compiler broken.
259 VulkanPipeline *nullPipeline = new VulkanPipeline();
260 nullPipeline->pipeline = VK_NULL_HANDLE;
261 nullPipeline->flags = 0;
262 return nullPipeline;
263 }
264
265 VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
266 inputAssembly.flags = 0;
267 inputAssembly.topology = (VkPrimitiveTopology)key.topology;
268 inputAssembly.primitiveRestartEnable = false;
269 int vertexStride = 0;
270
271 VkVertexInputAttributeDescription attrs[8];
272 int attributeCount;
273 if (useHwTransform) {
274 attributeCount = SetupVertexAttribs(attrs, *decFmt);
275 vertexStride = decFmt->stride;
276 } else {
277 bool needsUV = vs->GetID().Bit(VS_BIT_DO_TEXTURE);
278 bool needsColor1 = vs->GetID().Bit(VS_BIT_LMODE);
279 attributeCount = SetupVertexAttribsPretransformed(attrs, needsUV, needsColor1);
280 vertexStride = 36;
281 }
282
283 VkVertexInputBindingDescription ibd{};
284 ibd.binding = 0;
285 ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
286 ibd.stride = vertexStride;
287
288 VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
289 vis.flags = 0;
290 vis.vertexBindingDescriptionCount = 1;
291 vis.pVertexBindingDescriptions = &ibd;
292 vis.vertexAttributeDescriptionCount = attributeCount;
293 vis.pVertexAttributeDescriptions = attrs;
294
295 VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
296 views.flags = 0;
297 views.viewportCount = 1;
298 views.scissorCount = 1;
299 views.pViewports = nullptr; // dynamic
300 views.pScissors = nullptr; // dynamic
301
302 VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
303 pipe.flags = 0;
304 pipe.stageCount = 2;
305 pipe.pStages = ss;
306 pipe.basePipelineIndex = 0;
307
308 pipe.pColorBlendState = &cbs;
309 pipe.pDepthStencilState = &dss;
310 pipe.pRasterizationState = &rs;
311
312 // We will use dynamic viewport state.
313 pipe.pVertexInputState = &vis;
314 pipe.pViewportState = &views;
315 pipe.pTessellationState = nullptr;
316 pipe.pDynamicState = &ds;
317 pipe.pInputAssemblyState = &inputAssembly;
318 pipe.pMultisampleState = &ms;
319 pipe.layout = layout;
320 pipe.basePipelineHandle = VK_NULL_HANDLE;
321 pipe.basePipelineIndex = 0;
322 pipe.renderPass = renderPass;
323 pipe.subpass = 0;
324
325 VkPipeline pipeline;
326 VkResult result = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipe, nullptr, &pipeline);
327 if (result != VK_SUCCESS) {
328 ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
329 if (result == VK_INCOMPLETE) {
330 // Typical Adreno return value. It'll usually also log something vague, like "Failed to link shaders".
331 // Let's log some stuff and try to stumble along.
332 ERROR_LOG(G3D, "VS source code:\n%s\n", CutFromMain(vs->GetShaderString(SHADER_STRING_SOURCE_CODE)).c_str());
333 ERROR_LOG(G3D, "FS source code:\n%s\n", CutFromMain(fs->GetShaderString(SHADER_STRING_SOURCE_CODE)).c_str());
334 } else {
335 _dbg_assert_msg_(false, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
336 }
337 // Create a placeholder to avoid creating over and over if something is broken.
338 VulkanPipeline *nullPipeline = new VulkanPipeline();
339 nullPipeline->pipeline = VK_NULL_HANDLE;
340 nullPipeline->flags = 0;
341 return nullPipeline;
342 }
343
344 VulkanPipeline *vulkanPipeline = new VulkanPipeline();
345 vulkanPipeline->pipeline = pipeline;
346 vulkanPipeline->flags = 0;
347 if (useBlendConstant)
348 vulkanPipeline->flags |= PIPELINE_FLAG_USES_BLEND_CONSTANT;
349 if (key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP)
350 vulkanPipeline->flags |= PIPELINE_FLAG_USES_LINES;
351 if (dss.depthTestEnable || dss.stencilTestEnable) {
352 vulkanPipeline->flags |= PIPELINE_FLAG_USES_DEPTH_STENCIL;
353 }
354 return vulkanPipeline;
355 }
356
GetOrCreatePipeline(VkPipelineLayout layout,VkRenderPass renderPass,const VulkanPipelineRasterStateKey & rasterKey,const DecVtxFormat * decFmt,VulkanVertexShader * vs,VulkanFragmentShader * fs,bool useHwTransform)357 VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
358 if (!pipelineCache_) {
359 VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
360 VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
361 _assert_(VK_SUCCESS == res);
362 }
363
364 VulkanPipelineKey key{};
365 _assert_msg_(renderPass, "Can't create a pipeline with a null renderpass");
366
367 key.raster = rasterKey;
368 key.renderPass = renderPass;
369 key.useHWTransform = useHwTransform;
370 key.vShader = vs->GetModule();
371 key.fShader = fs->GetModule();
372 key.vtxFmtId = useHwTransform ? decFmt->id : 0;
373
374 auto iter = pipelines_.Get(key);
375 if (iter)
376 return iter;
377
378 VulkanPipeline *pipeline = CreateVulkanPipeline(
379 vulkan_->GetDevice(), pipelineCache_, layout, renderPass,
380 rasterKey, decFmt, vs, fs, useHwTransform, lineWidth_);
381 pipelines_.Insert(key, pipeline);
382
383 // Don't return placeholder null pipelines.
384 if (pipeline && pipeline->pipeline) {
385 return pipeline;
386 } else {
387 return nullptr;
388 }
389 }
390
DebugGetObjectIDs(DebugShaderType type)391 std::vector<std::string> PipelineManagerVulkan::DebugGetObjectIDs(DebugShaderType type) {
392 std::vector<std::string> ids;
393 switch (type) {
394 case SHADER_TYPE_PIPELINE:
395 {
396 pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
397 std::string id;
398 key.ToString(&id);
399 ids.push_back(id);
400 });
401 }
402 break;
403 default:
404 break;
405 }
406 return ids;
407 }
408
409 static const char *const topologies[8] = {
410 "POINTLIST",
411 "LINELIST",
412 "LINESTRIP",
413 "TRILIST",
414 "TRISTRIP",
415 "TRIFAN",
416 };
417
418 static const char *const blendOps[8] = {
419 "ADD",
420 "SUB",
421 "REVSUB",
422 "MIN",
423 "MAX",
424 };
425
426 static const char *const compareOps[8] = {
427 "NEVER",
428 "<",
429 "==",
430 "<=",
431 ">",
432 ">=",
433 "!=",
434 "ALWAYS",
435 };
436
437 static const char *const logicOps[] = {
438 "CLEAR",
439 "AND",
440 "AND_REV",
441 "COPY",
442 "AND_INV",
443 "NOOP",
444 "XOR",
445 "OR",
446 "NOR",
447 "EQUIV",
448 "INVERT",
449 "OR_REV",
450 "COPY_INV",
451 "OR_INV",
452 "NAND",
453 "SET",
454 };
455
456 static const char *const stencilOps[8] = {
457 "KEEP",
458 "ZERO",
459 "REPLACE",
460 "INC_CLAMP",
461 "DEC_CLAMP",
462 "INVERT",
463 "INC_WRAP",
464 "DEC_WRAP",
465 };
466
467 static const char *const blendFactors[19] = {
468 "ZERO",
469 "ONE",
470 "SRC_COLOR",
471 "ONE_MINUS_SRC_COLOR",
472 "DST_COLOR",
473 "ONE_MINUS_DST_COLOR",
474 "SRC_ALPHA",
475 "ONE_MINUS_SRC_ALPHA",
476 "DST_ALPHA",
477 "ONE_MINUS_DST_ALPHA",
478 "CONSTANT_COLOR",
479 "ONE_MINUS_CONSTANT_COLOR",
480 "CONSTANT_ALPHA",
481 "ONE_MINUS_CONSTANT_ALPHA",
482 "SRC_ALPHA_SATURATE",
483 "SRC1_COLOR",
484 "ONE_MINUS_SRC1_COLOR",
485 "SRC1_ALPHA",
486 "ONE_MINUS_SRC1_ALPHA",
487 };
488
DebugGetObjectString(std::string id,DebugShaderType type,DebugShaderStringType stringType)489 std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
490 if (type != SHADER_TYPE_PIPELINE)
491 return "N/A";
492
493 VulkanPipelineKey pipelineKey;
494 pipelineKey.FromString(id);
495
496 VulkanPipeline *iter = pipelines_.Get(pipelineKey);
497 if (!iter) {
498 return "";
499 }
500
501 std::string str = pipelineKey.GetDescription(stringType);
502 return StringFromFormat("%p: %s", iter, str.c_str());
503 }
504
GetDescription(DebugShaderStringType stringType) const505 std::string VulkanPipelineKey::GetDescription(DebugShaderStringType stringType) const {
506 switch (stringType) {
507 case SHADER_STRING_SHORT_DESC:
508 {
509 std::stringstream str;
510 str << topologies[raster.topology] << " ";
511 if (raster.blendEnable) {
512 str << "Blend(C:" << blendOps[raster.blendOpColor] << "/"
513 << blendFactors[raster.srcColor] << ":" << blendFactors[raster.destColor] << " ";
514 if (raster.blendOpAlpha != VK_BLEND_OP_ADD ||
515 raster.srcAlpha != VK_BLEND_FACTOR_ONE ||
516 raster.destAlpha != VK_BLEND_FACTOR_ZERO) {
517 str << "A:" << blendOps[raster.blendOpAlpha] << "/"
518 << blendFactors[raster.srcColor] << ":" << blendFactors[raster.destColor] << " ";
519 }
520 str << ") ";
521 }
522 if (raster.colorWriteMask != 0xF) {
523 str << "Mask(";
524 for (int i = 0; i < 4; i++) {
525 if (raster.colorWriteMask & (1 << i)) {
526 str << "RGBA"[i];
527 } else {
528 str << "_";
529 }
530 }
531 str << ") ";
532 }
533 if (raster.depthTestEnable) {
534 str << "Depth(";
535 if (raster.depthWriteEnable)
536 str << "W, ";
537 if (raster.depthCompareOp)
538 str << compareOps[raster.depthCompareOp & 7];
539 str << ") ";
540 }
541 if (raster.stencilTestEnable) {
542 str << "Stencil(";
543 str << compareOps[raster.stencilCompareOp & 7] << " ";
544 str << stencilOps[raster.stencilPassOp & 7] << "/";
545 str << stencilOps[raster.stencilFailOp & 7] << "/";
546 str << stencilOps[raster.stencilDepthFailOp& 7];
547 str << ") ";
548 }
549 if (raster.logicOpEnable) {
550 str << "Logic(" << logicOps[raster.logicOp & 15] << ") ";
551 }
552 if (useHWTransform) {
553 str << "HWX ";
554 }
555 if (vtxFmtId) {
556 str << "V(" << StringFromFormat("%08x", vtxFmtId) << ") "; // TODO: Format nicer.
557 } else {
558 str << "SWX ";
559 }
560 return str.str();
561 }
562
563 case SHADER_STRING_SOURCE_CODE:
564 {
565 return "N/A";
566 }
567 default:
568 return "N/A";
569 }
570 }
571
SetLineWidth(float lineWidth)572 void PipelineManagerVulkan::SetLineWidth(float lineWidth) {
573 if (lineWidth_ == lineWidth)
574 return;
575 lineWidth_ = lineWidth;
576
577 // Wipe all line-drawing pipelines.
578 pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
579 if (value->flags & PIPELINE_FLAG_USES_LINES) {
580 if (value->pipeline)
581 vulkan_->Delete().QueueDeletePipeline(value->pipeline);
582 delete value;
583 pipelines_.Remove(key);
584 }
585 });
586 }
587
588 // For some reason this struct is only defined in the spec, not in the headers.
589 struct VkPipelineCacheHeader {
590 uint32_t headerSize;
591 VkPipelineCacheHeaderVersion version;
592 uint32_t vendorId;
593 uint32_t deviceId;
594 uint8_t uuid[VK_UUID_SIZE];
595 };
596
597 struct StoredVulkanPipelineKey {
598 VulkanPipelineRasterStateKey raster;
599 VShaderID vShaderID;
600 FShaderID fShaderID;
601 uint32_t vtxFmtId;
602 bool useHWTransform;
603 bool backbufferPass;
604 VulkanQueueRunner::RPKey renderPassKey;
605
606 // For std::set. Better zero-initialize the struct properly for this to work.
operator <StoredVulkanPipelineKey607 bool operator < (const StoredVulkanPipelineKey &other) const {
608 return memcmp(this, &other, sizeof(*this)) < 0;
609 }
610 };
611
612 // If you're looking for how to invalidate the cache, it's done in ShaderManagerVulkan, look for CACHE_VERSION and increment it.
613 // (Header of the same file this is stored in).
SaveCache(FILE * file,bool saveRawPipelineCache,ShaderManagerVulkan * shaderManager,Draw::DrawContext * drawContext)614 void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext) {
615 VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
616 VulkanQueueRunner *queueRunner = rm->GetQueueRunner();
617
618 size_t dataSize = 0;
619 uint32_t size;
620
621 if (saveRawPipelineCache) {
622 // WARNING: See comment in LoadCache before using this path.
623 VkResult result = vkGetPipelineCacheData(vulkan_->GetDevice(), pipelineCache_, &dataSize, nullptr);
624 uint32_t size = (uint32_t)dataSize;
625 if (result != VK_SUCCESS) {
626 size = 0;
627 fwrite(&size, sizeof(size), 1, file);
628 return;
629 }
630 std::unique_ptr<uint8_t[]> buffer(new uint8_t[dataSize]);
631 vkGetPipelineCacheData(vulkan_->GetDevice(), pipelineCache_, &dataSize, buffer.get());
632 size = (uint32_t)dataSize;
633 fwrite(&size, sizeof(size), 1, file);
634 fwrite(buffer.get(), 1, size, file);
635 NOTICE_LOG(G3D, "Saved Vulkan pipeline cache (%d bytes).", (int)size);
636 }
637
638 size_t seekPosOnFailure = ftell(file);
639
640 bool failed = false;
641 bool writeFailed = false;
642 // Since we don't include the full pipeline key, there can be duplicates,
643 // caused by things like switching from buffered to non-buffered rendering.
644 // Make sure the set of pipelines we write is "unique".
645 std::set<StoredVulkanPipelineKey> keys;
646
647 pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) {
648 if (failed)
649 return;
650 VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader);
651 VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader);
652 if (!vshader || !fshader) {
653 failed = true;
654 return;
655 }
656 StoredVulkanPipelineKey key{};
657 key.raster = pkey.raster;
658 key.useHWTransform = pkey.useHWTransform;
659 key.fShaderID = fshader->GetID();
660 key.vShaderID = vshader->GetID();
661 if (key.useHWTransform) {
662 // NOTE: This is not a vtype, but a decoded vertex format.
663 key.vtxFmtId = pkey.vtxFmtId;
664 }
665 // Figure out what kind of renderpass this pipeline uses.
666 if (pkey.renderPass == queueRunner->GetBackbufferRenderPass()) {
667 key.backbufferPass = true;
668 key.renderPassKey = {};
669 } else {
670 key.backbufferPass = false;
671 queueRunner->GetRenderPassKey(pkey.renderPass, &key.renderPassKey);
672 }
673 keys.insert(key);
674 });
675
676 // Write the number of pipelines.
677 size = (uint32_t)keys.size();
678 writeFailed = writeFailed || fwrite(&size, sizeof(size), 1, file) != 1;
679
680 // Write the pipelines.
681 for (auto &key : keys) {
682 writeFailed = writeFailed || fwrite(&key, sizeof(key), 1, file) != 1;
683 }
684
685 if (failed) {
686 ERROR_LOG(G3D, "Failed to write pipeline cache, some shader was missing");
687 // Write a zero in the right place so it doesn't try to load the pipelines next time.
688 size = 0;
689 fseek(file, (long)seekPosOnFailure, SEEK_SET);
690 writeFailed = fwrite(&size, sizeof(size), 1, file) != 1;
691 if (writeFailed) {
692 ERROR_LOG(G3D, "Failed to write pipeline cache, disk full?");
693 }
694 return;
695 }
696 if (writeFailed) {
697 ERROR_LOG(G3D, "Failed to write pipeline cache, disk full?");
698 } else {
699 NOTICE_LOG(G3D, "Saved Vulkan pipeline ID cache (%d unique pipelines/%d).", (int)keys.size(), (int)pipelines_.size());
700 }
701 }
702
LoadCache(FILE * file,bool loadRawPipelineCache,ShaderManagerVulkan * shaderManager,Draw::DrawContext * drawContext,VkPipelineLayout layout)703 bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VkPipelineLayout layout) {
704 VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
705 VulkanQueueRunner *queueRunner = rm->GetQueueRunner();
706
707 uint32_t size = 0;
708 if (loadRawPipelineCache) {
709 // WARNING: Do not use this path until after reading and implementing https://zeux.io/2019/07/17/serializing-pipeline-cache/ !
710 bool success = fread(&size, sizeof(size), 1, file) == 1;
711 if (!size || !success) {
712 WARN_LOG(G3D, "Zero-sized Vulkan pipeline cache.");
713 return true;
714 }
715 std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
716 success = fread(buffer.get(), 1, size, file) == size;
717 // Verify header.
718 VkPipelineCacheHeader *header = (VkPipelineCacheHeader *)buffer.get();
719 if (!success || header->version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE) {
720 // Bad header, don't do anything.
721 WARN_LOG(G3D, "Bad Vulkan pipeline cache header - ignoring");
722 return false;
723 }
724 if (0 != memcmp(header->uuid, vulkan_->GetPhysicalDeviceProperties().properties.pipelineCacheUUID, VK_UUID_SIZE)) {
725 // Wrong hardware/driver/etc.
726 WARN_LOG(G3D, "Bad Vulkan pipeline cache UUID - ignoring");
727 return false;
728 }
729
730 VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
731 pc.pInitialData = buffer.get();
732 pc.initialDataSize = size;
733 pc.flags = 0;
734 VkPipelineCache cache;
735 VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &cache);
736 if (res != VK_SUCCESS) {
737 return false;
738 }
739 if (!pipelineCache_) {
740 pipelineCache_ = cache;
741 } else {
742 vkMergePipelineCaches(vulkan_->GetDevice(), pipelineCache_, 1, &cache);
743 }
744 NOTICE_LOG(G3D, "Loaded Vulkan pipeline cache (%d bytes).", (int)size);
745 } else {
746 if (!pipelineCache_) {
747 VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
748 VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
749 if (res != VK_SUCCESS) {
750 return false;
751 }
752 }
753 }
754
755 // Read the number of pipelines.
756 bool failed = fread(&size, sizeof(size), 1, file) != 1;
757
758 NOTICE_LOG(G3D, "Creating %d pipelines...", size);
759 int pipelineCreateFailCount = 0;
760 for (uint32_t i = 0; i < size; i++) {
761 if (failed || cancelCache_) {
762 break;
763 }
764 StoredVulkanPipelineKey key;
765 failed = failed || fread(&key, sizeof(key), 1, file) != 1;
766 if (failed) {
767 ERROR_LOG(G3D, "Truncated Vulkan pipeline cache file");
768 continue;
769 }
770 VulkanVertexShader *vs = shaderManager->GetVertexShaderFromID(key.vShaderID);
771 VulkanFragmentShader *fs = shaderManager->GetFragmentShaderFromID(key.fShaderID);
772 if (!vs || !fs) {
773 failed = true;
774 ERROR_LOG(G3D, "Failed to find vs or fs in of pipeline %d in cache", (int)i);
775 continue;
776 }
777
778 VkRenderPass rp;
779 if (key.backbufferPass) {
780 rp = queueRunner->GetBackbufferRenderPass();
781 } else {
782 rp = queueRunner->GetRenderPass(key.renderPassKey);
783 }
784
785 DecVtxFormat fmt;
786 fmt.InitializeFromID(key.vtxFmtId);
787 VulkanPipeline *pipeline = GetOrCreatePipeline(layout, rp, key.raster,
788 key.useHWTransform ? &fmt : 0,
789 vs, fs, key.useHWTransform);
790 if (!pipeline) {
791 pipelineCreateFailCount += 1;
792 }
793 }
794 NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines, %d failed).", (int)size, pipelineCreateFailCount);
795 return true;
796 }
797
CancelCache()798 void PipelineManagerVulkan::CancelCache() {
799 cancelCache_ = true;
800 }
801