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