Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Vulkan/PipelineManagerVulkan.cpp
5663 views
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/PipelineManagerVulkan.h"
12
#include "GPU/Vulkan/ShaderManagerVulkan.h"
13
#include "GPU/Common/ShaderId.h"
14
#include "GPU/GPUDefinitions.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
21
u32 VulkanPipeline::GetVariantsBitmask() const {
22
return pipeline->GetVariantsBitmask();
23
}
24
25
PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) {
26
// The pipeline cache is created on demand (or explicitly through Load).
27
}
28
29
PipelineManagerVulkan::~PipelineManagerVulkan() {
30
// Block on all pipelines to make sure any background compiles are done.
31
// This is very important to do before we start trying to tear down the shaders - otherwise, we might
32
// be deleting shaders before queued pipeline creations that use them are performed.
33
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
34
if (value->pipeline) {
35
value->pipeline->BlockUntilCompiled();
36
}
37
});
38
39
Clear();
40
if (pipelineCache_ != VK_NULL_HANDLE)
41
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
42
vulkan_ = nullptr;
43
}
44
45
void PipelineManagerVulkan::Clear() {
46
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
47
if (!value->pipeline) {
48
// Something went wrong.
49
ERROR_LOG(Log::G3D, "Null pipeline found in PipelineManagerVulkan::Clear - didn't wait for asyncs?");
50
} else {
51
value->pipeline->QueueForDeletion(vulkan_);
52
}
53
delete value;
54
});
55
56
pipelines_.Clear();
57
}
58
59
void PipelineManagerVulkan::InvalidateMSAAPipelines() {
60
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
61
value->pipeline->DestroyVariants(vulkan_, true);
62
});
63
}
64
65
void PipelineManagerVulkan::DeviceLost() {
66
Clear();
67
if (pipelineCache_ != VK_NULL_HANDLE)
68
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
69
vulkan_ = nullptr;
70
}
71
72
void PipelineManagerVulkan::DeviceRestore(VulkanContext *vulkan) {
73
vulkan_ = vulkan;
74
// The pipeline cache is created on demand.
75
}
76
77
struct DeclTypeInfo {
78
VkFormat type;
79
const char *name;
80
};
81
82
static const DeclTypeInfo VComp[] = {
83
{ VK_FORMAT_UNDEFINED, "NULL" }, // DEC_NONE,
84
{ VK_FORMAT_R32_SFLOAT, "R32_SFLOAT " }, // DEC_FLOAT_1,
85
{ VK_FORMAT_R32G32_SFLOAT, "R32G32_SFLOAT " }, // DEC_FLOAT_2,
86
{ VK_FORMAT_R32G32B32_SFLOAT, "R32G32B32_SFLOAT " }, // DEC_FLOAT_3,
87
{ VK_FORMAT_R32G32B32A32_SFLOAT, "R32G32B32A32_SFLOAT " }, // DEC_FLOAT_4,
88
89
{ VK_FORMAT_R8G8B8A8_SNORM, "R8G8B8A8_SNORM" }, // DEC_S8_3,
90
{ VK_FORMAT_R16G16B16A16_SNORM, "R16G16B16A16_SNORM " }, // DEC_S16_3,
91
92
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_1,
93
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_2,
94
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_3,
95
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_4,
96
{ VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_1,
97
{ VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_2,
98
{ VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_3,
99
{ VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_4,
100
};
101
102
static void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) {
103
_assert_(fmt != DEC_NONE);
104
_assert_(fmt < ARRAY_SIZE(VComp));
105
attr->location = (uint32_t)location;
106
attr->binding = 0;
107
attr->format = VComp[fmt].type;
108
attr->offset = offset;
109
}
110
111
// Returns the number of attributes that were set.
112
// We could cache these AttributeDescription arrays (with pspFmt as the key), but hardly worth bothering
113
// as we will only call this code when we need to create a new VkPipeline.
114
static int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
115
int count = 0;
116
if (decFmt.w0fmt != 0) {
117
VertexAttribSetup(&attrs[count++], decFmt.w0fmt, decFmt.w0off, PspAttributeLocation::W1);
118
}
119
if (decFmt.w1fmt != 0) {
120
VertexAttribSetup(&attrs[count++], decFmt.w1fmt, decFmt.w1off, PspAttributeLocation::W2);
121
}
122
if (decFmt.uvfmt != 0) {
123
VertexAttribSetup(&attrs[count++], decFmt.uvfmt, decFmt.uvoff, PspAttributeLocation::TEXCOORD);
124
}
125
if (decFmt.c0fmt != 0) {
126
VertexAttribSetup(&attrs[count++], decFmt.c0fmt, decFmt.c0off, PspAttributeLocation::COLOR0);
127
}
128
if (decFmt.c1fmt != 0) {
129
VertexAttribSetup(&attrs[count++], decFmt.c1fmt, decFmt.c1off, PspAttributeLocation::COLOR1);
130
}
131
if (decFmt.nrmfmt != 0) {
132
VertexAttribSetup(&attrs[count++], decFmt.nrmfmt, decFmt.nrmoff, PspAttributeLocation::NORMAL);
133
}
134
// Position is always there.
135
VertexAttribSetup(&attrs[count++], DecVtxFormat::PosFmt(), decFmt.posoff, PspAttributeLocation::POSITION);
136
return count;
137
}
138
139
static int SetupVertexAttribsPretransformed(VkVertexInputAttributeDescription attrs[], bool needsUV, bool needsColor1, bool needsFog) {
140
int count = 0;
141
VertexAttribSetup(&attrs[count++], DEC_FLOAT_4, offsetof(TransformedVertex, pos), PspAttributeLocation::POSITION);
142
if (needsUV) {
143
VertexAttribSetup(&attrs[count++], DEC_FLOAT_3, offsetof(TransformedVertex, uv), PspAttributeLocation::TEXCOORD);
144
}
145
VertexAttribSetup(&attrs[count++], DEC_U8_4, offsetof(TransformedVertex, color0), PspAttributeLocation::COLOR0);
146
if (needsColor1) {
147
VertexAttribSetup(&attrs[count++], DEC_U8_4, offsetof(TransformedVertex, color1), PspAttributeLocation::COLOR1);
148
}
149
if (needsFog) {
150
VertexAttribSetup(&attrs[count++], DEC_FLOAT_1, offsetof(TransformedVertex, fog), PspAttributeLocation::NORMAL);
151
}
152
return count;
153
}
154
155
static bool UsesBlendConstant(int factor) {
156
switch (factor) {
157
case VK_BLEND_FACTOR_CONSTANT_ALPHA:
158
case VK_BLEND_FACTOR_CONSTANT_COLOR:
159
case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA:
160
case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR:
161
return true;
162
default:
163
return false;
164
}
165
}
166
167
static std::string CutFromMain(const std::string &str) {
168
std::vector<std::string> lines;
169
SplitString(str, '\n', lines);
170
171
std::string rebuilt;
172
bool foundStart = false;
173
int c = 0;
174
for (const std::string &str : lines) {
175
if (startsWith(str, "void main")) {
176
foundStart = true;
177
rebuilt += StringFromFormat("... (cut %d lines)\n", c);
178
}
179
if (foundStart) {
180
rebuilt += str + "\n";
181
}
182
c++;
183
}
184
return rebuilt;
185
}
186
187
static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
188
VKRPipelineLayout *layout, PipelineFlags pipelineFlags, VkSampleCountFlagBits sampleCount, const VulkanPipelineRasterStateKey &key,
189
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask, bool cacheLoad) {
190
_assert_(fs && vs);
191
192
if (!fs || !fs->GetModule()) {
193
ERROR_LOG(Log::G3D, "Fragment shader missing in CreateVulkanPipeline");
194
return nullptr;
195
}
196
if (!vs || !vs->GetModule()) {
197
ERROR_LOG(Log::G3D, "Vertex shader missing in CreateVulkanPipeline");
198
return nullptr;
199
}
200
if (gs && !gs->GetModule()) {
201
ERROR_LOG(Log::G3D, "Geometry shader missing in CreateVulkanPipeline");
202
return nullptr;
203
}
204
205
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
206
vulkanPipeline->desc = new VKRGraphicsPipelineDesc();
207
VKRGraphicsPipelineDesc *desc = vulkanPipeline->desc;
208
desc->pipelineCache = pipelineCache;
209
210
desc->fragmentShader = fs->GetModule();
211
desc->vertexShader = vs->GetModule();
212
desc->geometryShader = gs ? gs->GetModule() : nullptr;
213
214
PROFILE_THIS_SCOPE("pipelinebuild");
215
bool useBlendConstant = false;
216
217
VkPipelineColorBlendAttachmentState &blend0 = desc->blend0;
218
blend0.blendEnable = key.blendEnable;
219
if (key.blendEnable) {
220
blend0.colorBlendOp = (VkBlendOp)key.blendOpColor;
221
blend0.alphaBlendOp = (VkBlendOp)key.blendOpAlpha;
222
blend0.srcColorBlendFactor = (VkBlendFactor)key.srcColor;
223
blend0.srcAlphaBlendFactor = (VkBlendFactor)key.srcAlpha;
224
blend0.dstColorBlendFactor = (VkBlendFactor)key.destColor;
225
blend0.dstAlphaBlendFactor = (VkBlendFactor)key.destAlpha;
226
}
227
blend0.colorWriteMask = key.colorWriteMask;
228
229
VkPipelineColorBlendStateCreateInfo &cbs = desc->cbs;
230
cbs.flags = 0;
231
cbs.pAttachments = &blend0;
232
cbs.attachmentCount = 1;
233
cbs.logicOpEnable = key.logicOpEnable;
234
if (key.logicOpEnable)
235
cbs.logicOp = (VkLogicOp)key.logicOp;
236
else
237
cbs.logicOp = VK_LOGIC_OP_COPY;
238
239
VkPipelineDepthStencilStateCreateInfo &dss = desc->dss;
240
dss.depthBoundsTestEnable = false;
241
dss.stencilTestEnable = key.stencilTestEnable;
242
if (key.stencilTestEnable) {
243
dss.front.compareOp = (VkCompareOp)key.stencilCompareOp;
244
dss.front.passOp = (VkStencilOp)key.stencilPassOp;
245
dss.front.failOp = (VkStencilOp)key.stencilFailOp;
246
dss.front.depthFailOp = (VkStencilOp)key.stencilDepthFailOp;
247
// Back stencil is always the same as front on PSP.
248
memcpy(&dss.back, &dss.front, sizeof(dss.front));
249
}
250
dss.depthTestEnable = key.depthTestEnable;
251
if (key.depthTestEnable) {
252
dss.depthCompareOp = (VkCompareOp)key.depthCompareOp;
253
dss.depthWriteEnable = key.depthWriteEnable;
254
}
255
256
VkDynamicState *dynamicStates = &desc->dynamicStates[0];
257
int numDyn = 0;
258
if (key.blendEnable &&
259
(UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) {
260
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
261
useBlendConstant = true;
262
}
263
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR;
264
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT;
265
if (key.stencilTestEnable) {
266
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
267
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
268
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
269
}
270
271
VkPipelineDynamicStateCreateInfo &ds = desc->ds;
272
ds.flags = 0;
273
ds.pDynamicStates = dynamicStates;
274
ds.dynamicStateCount = numDyn;
275
276
VkPipelineRasterizationStateCreateInfo &rs = desc->rs;
277
rs.flags = 0;
278
rs.depthBiasEnable = false;
279
rs.cullMode = key.cullMode;
280
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
281
rs.lineWidth = 1.0f;
282
rs.rasterizerDiscardEnable = false;
283
rs.polygonMode = VK_POLYGON_MODE_FILL;
284
rs.depthClampEnable = key.depthClampEnable;
285
286
if (renderManager->GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) {
287
ChainStruct(rs, &desc->rs_provoking);
288
desc->rs_provoking.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
289
}
290
291
desc->fragmentShaderSource = fs->GetShaderString(SHADER_STRING_SOURCE_CODE);
292
desc->vertexShaderSource = vs->GetShaderString(SHADER_STRING_SOURCE_CODE);
293
if (gs) {
294
desc->geometryShaderSource = gs->GetShaderString(SHADER_STRING_SOURCE_CODE);
295
}
296
297
_dbg_assert_(key.topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
298
_dbg_assert_(key.topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
299
desc->topology = (VkPrimitiveTopology)key.topology;
300
301
int vertexStride = 0;
302
VkVertexInputAttributeDescription *attrs = &desc->attrs[0];
303
304
int attributeCount;
305
if (useHwTransform) {
306
attributeCount = SetupVertexAttribs(attrs, *decFmt);
307
vertexStride = decFmt->stride;
308
} else {
309
bool needsUV = true;
310
bool needsColor1 = vs->GetID().Bit(VS_BIT_LMODE);
311
attributeCount = SetupVertexAttribsPretransformed(attrs, needsUV, needsColor1, true);
312
vertexStride = (int)sizeof(TransformedVertex);
313
}
314
315
VkVertexInputBindingDescription &ibd = desc->ibd;
316
ibd.binding = 0;
317
ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
318
ibd.stride = vertexStride;
319
320
VkPipelineVertexInputStateCreateInfo &vis = desc->vis;
321
vis.flags = 0;
322
vis.vertexBindingDescriptionCount = 1;
323
vis.pVertexBindingDescriptions = &desc->ibd;
324
vis.vertexAttributeDescriptionCount = attributeCount;
325
vis.pVertexAttributeDescriptions = attrs;
326
327
VkPipelineViewportStateCreateInfo &views = desc->views;
328
views.flags = 0;
329
views.viewportCount = 1;
330
views.scissorCount = 1;
331
views.pViewports = nullptr; // dynamic
332
views.pScissors = nullptr; // dynamic
333
334
desc->pipelineLayout = layout;
335
336
std::string tag = "game";
337
#ifdef _DEBUG
338
tag = FragmentShaderDesc(fs->GetID()) + " VS " + VertexShaderDesc(vs->GetID());
339
#endif
340
341
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, pipelineFlags, variantBitmask, sampleCount, cacheLoad, tag.c_str());
342
343
vulkanPipeline->pipeline = pipeline;
344
if (useBlendConstant) {
345
pipelineFlags |= PipelineFlags::USES_BLEND_CONSTANT;
346
}
347
if (gs) {
348
pipelineFlags |= PipelineFlags::USES_GEOMETRY_SHADER;
349
}
350
if (dss.depthTestEnable || dss.stencilTestEnable) {
351
pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL;
352
}
353
vulkanPipeline->pipelineFlags = pipelineFlags;
354
return vulkanPipeline;
355
}
356
357
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VKRPipelineLayout *layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask, int multiSampleLevel, bool cacheLoad) {
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
366
key.raster = rasterKey;
367
key.useHWTransform = useHwTransform;
368
key.vShader = vs->GetModule();
369
key.fShader = fs->GetModule();
370
key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE;
371
key.vtxFmtId = useHwTransform ? decFmt->id : 0;
372
373
VulkanPipeline *pipeline;
374
if (pipelines_.Get(key, &pipeline)) {
375
return pipeline;
376
}
377
378
PipelineFlags pipelineFlags = (PipelineFlags)0;
379
if (fs->Flags() & FragmentShaderFlags::USES_DISCARD) {
380
pipelineFlags |= PipelineFlags::USES_DISCARD;
381
}
382
if (fs->Flags() & FragmentShaderFlags::USES_FLAT_SHADING) {
383
pipelineFlags |= PipelineFlags::USES_FLAT_SHADING;
384
}
385
if (vs->Flags() & VertexShaderFlags::MULTI_VIEW) {
386
pipelineFlags |= PipelineFlags::USES_MULTIVIEW;
387
}
388
389
VkSampleCountFlagBits sampleCount = MultiSampleLevelToFlagBits(multiSampleLevel);
390
391
pipeline = CreateVulkanPipeline(
392
renderManager, pipelineCache_, layout, pipelineFlags, sampleCount,
393
rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask, cacheLoad);
394
395
// If the above failed, we got a null pipeline. We still insert it to keep track.
396
pipelines_.Insert(key, pipeline);
397
398
// Don't return placeholder null pipelines.
399
if (pipeline && pipeline->pipeline) {
400
return pipeline;
401
} else {
402
return nullptr;
403
}
404
}
405
406
std::vector<std::string> PipelineManagerVulkan::DebugGetObjectIDs(DebugShaderType type) const {
407
std::vector<std::string> ids;
408
switch (type) {
409
case SHADER_TYPE_PIPELINE:
410
{
411
ids.reserve(pipelines_.size());
412
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
413
std::string id;
414
key.ToString(&id);
415
ids.push_back(id);
416
});
417
}
418
break;
419
default:
420
break;
421
}
422
return ids;
423
}
424
425
static const char *const topologies[8] = {
426
"POINTS",
427
"LINES",
428
"LINESTRIP",
429
"TRIS",
430
"TRISTRIP",
431
"TRIFAN",
432
};
433
434
static const char *const blendOps[8] = {
435
"ADD",
436
"SUB",
437
"RSUB",
438
"MIN",
439
"MAX",
440
};
441
442
static const char *const compareOps[8] = {
443
"NEVER",
444
"<",
445
"==",
446
"<=",
447
">",
448
">=",
449
"!=",
450
"ALWAYS",
451
};
452
453
static const char *const logicOps[] = {
454
"CLEAR",
455
"AND",
456
"AND_REV",
457
"COPY",
458
"AND_INV",
459
"NOOP",
460
"XOR",
461
"OR",
462
"NOR",
463
"EQUIV",
464
"INVERT",
465
"OR_REV",
466
"COPY_INV",
467
"OR_INV",
468
"NAND",
469
"SET",
470
};
471
472
static const char *const stencilOps[8] = {
473
"KEEP",
474
"ZERO",
475
"REPL",
476
"INC_SAT",
477
"DEC_SAT",
478
"INVERT",
479
"INC_WRAP",
480
"DEC_WRAP",
481
};
482
483
static const char *const blendFactors[19] = {
484
"ZERO",
485
"ONE",
486
"SRC_COL",
487
"INV_SRC_COL",
488
"DST_COL",
489
"INV_DST_COL",
490
"SRC_A",
491
"INV_SRC_A",
492
"DST_A",
493
"INV_DST_A",
494
"CONSTANT_COL",
495
"INV_CONST_COL",
496
"CONSTANT_A",
497
"INV_CONST_A",
498
"SRC_A_SAT",
499
"SRC1_COL",
500
"INV_SRC1_COL",
501
"SRC1_A",
502
"INV_SRC1_A",
503
};
504
505
std::string PipelineManagerVulkan::DebugGetObjectString(const std::string &id, DebugShaderType type, DebugShaderStringType stringType, ShaderManagerVulkan *shaderManager) {
506
if (type != SHADER_TYPE_PIPELINE)
507
return "N/A";
508
509
VulkanPipelineKey pipelineKey;
510
pipelineKey.FromString(id);
511
512
VulkanPipeline *pipeline;
513
if (!pipelines_.Get(pipelineKey, &pipeline)) {
514
return "N/A (missing)";
515
}
516
_assert_(pipeline != nullptr);
517
u32 variants = pipeline->GetVariantsBitmask();
518
519
std::string keyDescription = pipelineKey.GetDescription(stringType, shaderManager);
520
return StringFromFormat("%s. v: %08x", keyDescription.c_str(), variants);
521
}
522
523
std::string VulkanPipelineKey::GetRasterStateDesc(bool lineBreaks) const {
524
std::stringstream str;
525
str << topologies[raster.topology] << " ";
526
if (useHWTransform) {
527
str << "HWX ";
528
}
529
if (vtxFmtId) {
530
str << "Vfmt(" << StringFromFormat("%08x", vtxFmtId) << ") "; // TODO: Format nicer.
531
} else {
532
str << "SWX ";
533
}
534
if (lineBreaks) str << std::endl;
535
if (raster.blendEnable) {
536
str << "Blend(C:" << blendOps[raster.blendOpColor] << "/"
537
<< blendFactors[raster.srcColor] << ":" << blendFactors[raster.destColor] << " ";
538
if (raster.blendOpAlpha != VK_BLEND_OP_ADD ||
539
raster.srcAlpha != VK_BLEND_FACTOR_ONE ||
540
raster.destAlpha != VK_BLEND_FACTOR_ZERO) {
541
str << "A:" << blendOps[raster.blendOpAlpha] << "/"
542
<< blendFactors[raster.srcColor] << ":" << blendFactors[raster.destColor] << " ";
543
}
544
str << ") ";
545
if (lineBreaks) str << std::endl;
546
}
547
if (raster.colorWriteMask != 0xF) {
548
str << "Mask(";
549
for (int i = 0; i < 4; i++) {
550
if (raster.colorWriteMask & (1 << i)) {
551
str << "RGBA"[i];
552
} else {
553
str << "_";
554
}
555
}
556
str << ") ";
557
if (lineBreaks) str << std::endl;
558
}
559
if (raster.depthTestEnable) {
560
str << "Z(";
561
if (raster.depthWriteEnable)
562
str << "W, ";
563
if (raster.depthCompareOp)
564
str << compareOps[raster.depthCompareOp & 7];
565
str << ") ";
566
if (lineBreaks) str << std::endl;
567
}
568
if (raster.stencilTestEnable) {
569
str << "Stenc(";
570
str << compareOps[raster.stencilCompareOp & 7] << " ";
571
str << stencilOps[raster.stencilPassOp & 7] << "/";
572
str << stencilOps[raster.stencilFailOp & 7] << "/";
573
str << stencilOps[raster.stencilDepthFailOp & 7];
574
str << ") ";
575
if (lineBreaks) str << std::endl;
576
}
577
if (raster.logicOpEnable) {
578
str << "Logic(" << logicOps[raster.logicOp & 15] << ") ";
579
if (lineBreaks) str << std::endl;
580
}
581
return str.str();
582
}
583
584
std::string VulkanPipelineKey::GetDescription(DebugShaderStringType stringType, ShaderManagerVulkan *shaderManager) const {
585
switch (stringType) {
586
case SHADER_STRING_SHORT_DESC:
587
// Just show the raster state. Also show brief VS/FS IDs?
588
return GetRasterStateDesc(false);
589
590
case SHADER_STRING_SOURCE_CODE:
591
{
592
// More detailed description of all the parts of the pipeline.
593
VkShaderModule fsModule = this->fShader->BlockUntilReady();
594
VkShaderModule vsModule = this->vShader->BlockUntilReady();
595
VkShaderModule gsModule = this->gShader ? this->gShader->BlockUntilReady() : VK_NULL_HANDLE;
596
597
std::stringstream str;
598
str << "VS: " << VertexShaderDesc(shaderManager->GetVertexShaderFromModule(vsModule)->GetID()) << std::endl;
599
str << "FS: " << FragmentShaderDesc(shaderManager->GetFragmentShaderFromModule(fsModule)->GetID()) << std::endl;
600
if (gsModule) {
601
str << "GS: " << GeometryShaderDesc(shaderManager->GetGeometryShaderFromModule(gsModule)->GetID()) << std::endl;
602
}
603
str << GetRasterStateDesc(true);
604
return str.str();
605
}
606
607
default:
608
return "N/A";
609
}
610
}
611
612
// For some reason this struct is only defined in the spec, not in the headers.
613
struct VkPipelineCacheHeader {
614
uint32_t headerSize;
615
VkPipelineCacheHeaderVersion version;
616
uint32_t vendorId;
617
uint32_t deviceId;
618
uint8_t uuid[VK_UUID_SIZE];
619
};
620
621
struct StoredVulkanPipelineKey {
622
VulkanPipelineRasterStateKey raster;
623
VShaderID vShaderID;
624
FShaderID fShaderID;
625
GShaderID gShaderID;
626
uint32_t vtxFmtId;
627
uint32_t variants;
628
bool useHWTransform; // TODO: Still needed?
629
630
// For std::set. Better zero-initialize the struct properly for this to work.
631
bool operator < (const StoredVulkanPipelineKey &other) const {
632
return memcmp(this, &other, sizeof(*this)) < 0;
633
}
634
};
635
636
// If you're looking for how to invalidate the cache, it's done in ShaderManagerVulkan, look for CACHE_VERSION and increment it.
637
// (Header of the same file this is stored in).
638
void PipelineManagerVulkan::SavePipelineCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext) {
639
VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
640
VulkanQueueRunner *queueRunner = rm->GetQueueRunner();
641
642
size_t dataSize = 0;
643
uint32_t size;
644
645
if (saveRawPipelineCache) {
646
// WARNING: See comment in LoadCache before using this path.
647
VkResult result = vkGetPipelineCacheData(vulkan_->GetDevice(), pipelineCache_, &dataSize, nullptr);
648
uint32_t size = (uint32_t)dataSize;
649
if (result != VK_SUCCESS) {
650
size = 0;
651
fwrite(&size, sizeof(size), 1, file);
652
return;
653
}
654
auto buffer = std::make_unique<uint8_t[]>(dataSize);
655
vkGetPipelineCacheData(vulkan_->GetDevice(), pipelineCache_, &dataSize, buffer.get());
656
size = (uint32_t)dataSize;
657
fwrite(&size, sizeof(size), 1, file);
658
fwrite(buffer.get(), 1, size, file);
659
NOTICE_LOG(Log::G3D, "Saved Vulkan pipeline cache (%d bytes).", (int)size);
660
}
661
662
int64_t seekPosOnFailure = File::Ftell(file);
663
664
bool failed = false;
665
bool writeFailed = false;
666
// Since we don't include the full pipeline key, there can be duplicates,
667
// caused by things like switching from buffered to non-buffered rendering.
668
// Make sure the set of pipelines we write is "unique".
669
std::set<StoredVulkanPipelineKey> keys;
670
671
pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) {
672
if (failed)
673
return;
674
VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady());
675
VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady());
676
VulkanGeometryShader *gshader = nullptr;
677
if (pkey.gShader) {
678
gshader = shaderManager->GetGeometryShaderFromModule(pkey.gShader->BlockUntilReady());
679
if (!gshader)
680
failed = true;
681
}
682
if (!vshader || !fshader || failed) {
683
failed = true;
684
return;
685
}
686
_dbg_assert_(pkey.raster.topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST && pkey.raster.topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
687
StoredVulkanPipelineKey key{};
688
key.raster = pkey.raster;
689
key.useHWTransform = pkey.useHWTransform;
690
key.fShaderID = fshader->GetID();
691
key.vShaderID = vshader->GetID();
692
key.gShaderID = gshader ? gshader->GetID() : GShaderID();
693
key.variants = value->GetVariantsBitmask();
694
if (key.useHWTransform) {
695
// NOTE: This is not a vtype, but a decoded vertex format.
696
key.vtxFmtId = pkey.vtxFmtId;
697
}
698
keys.insert(key);
699
});
700
701
// Write the number of pipelines.
702
size = (uint32_t)keys.size();
703
writeFailed = writeFailed || fwrite(&size, sizeof(size), 1, file) != 1;
704
705
// Write the pipelines.
706
for (auto &key : keys) {
707
writeFailed = writeFailed || fwrite(&key, sizeof(key), 1, file) != 1;
708
}
709
710
if (failed) {
711
ERROR_LOG(Log::G3D, "Failed to write pipeline cache, some shader was missing");
712
// Write a zero in the right place so it doesn't try to load the pipelines next time.
713
size = 0;
714
File::Fseek(file, seekPosOnFailure, SEEK_SET);
715
writeFailed = fwrite(&size, sizeof(size), 1, file) != 1;
716
if (writeFailed) {
717
ERROR_LOG(Log::G3D, "Failed to write pipeline cache, disk full?");
718
}
719
return;
720
}
721
if (writeFailed) {
722
ERROR_LOG(Log::G3D, "Failed to write pipeline cache, disk full?");
723
} else {
724
NOTICE_LOG(Log::G3D, "Saved Vulkan pipeline ID cache (%d unique pipelines/%d).", (int)keys.size(), (int)pipelines_.size());
725
}
726
}
727
728
bool PipelineManagerVulkan::LoadPipelineCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VKRPipelineLayout *layout, int multiSampleLevel) {
729
VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
730
VulkanQueueRunner *queueRunner = rm->GetQueueRunner();
731
732
uint32_t size = 0;
733
if (loadRawPipelineCache) {
734
NOTICE_LOG(Log::G3D, "WARNING: Using the badly tested raw pipeline cache path!!!!");
735
// WARNING: Do not use this path until after reading and implementing https://zeux.io/2019/07/17/serializing-pipeline-cache/ !
736
bool success = fread(&size, sizeof(size), 1, file) == 1;
737
if (!size || !success) {
738
WARN_LOG(Log::G3D, "Zero-sized Vulkan pipeline cache.");
739
return true;
740
}
741
auto buffer = std::make_unique<uint8_t[]>(size);
742
success = fread(buffer.get(), 1, size, file) == size;
743
// Verify header.
744
VkPipelineCacheHeader *header = (VkPipelineCacheHeader *)buffer.get();
745
if (!success || header->version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE) {
746
// Bad header, don't do anything.
747
WARN_LOG(Log::G3D, "Bad Vulkan pipeline cache header - ignoring");
748
return false;
749
}
750
if (0 != memcmp(header->uuid, vulkan_->GetPhysicalDeviceProperties().properties.pipelineCacheUUID, VK_UUID_SIZE)) {
751
// Wrong hardware/driver/etc.
752
WARN_LOG(Log::G3D, "Bad Vulkan pipeline cache UUID - ignoring");
753
return false;
754
}
755
756
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
757
pc.pInitialData = buffer.get();
758
pc.initialDataSize = size;
759
pc.flags = 0;
760
VkPipelineCache cache;
761
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &cache);
762
if (res != VK_SUCCESS) {
763
return false;
764
}
765
if (!pipelineCache_) {
766
pipelineCache_ = cache;
767
} else {
768
vkMergePipelineCaches(vulkan_->GetDevice(), pipelineCache_, 1, &cache);
769
}
770
NOTICE_LOG(Log::G3D, "Loaded Vulkan binary pipeline cache (%d bytes).", (int)size);
771
// Note that after loading the cache, it's still a good idea to pre-create the various pipelines.
772
} else {
773
if (!pipelineCache_) {
774
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
775
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
776
if (res != VK_SUCCESS) {
777
WARN_LOG(Log::G3D, "vkCreatePipelineCache failed (%08x), highly unexpected", (u32)res);
778
return false;
779
}
780
}
781
}
782
783
// Read the number of pipelines.
784
bool failed = fread(&size, sizeof(size), 1, file) != 1;
785
786
NOTICE_LOG(Log::G3D, "Creating %d pipelines from cache (%dx MSAA)...", size, (1 << multiSampleLevel));
787
int pipelineCreateFailCount = 0;
788
int shaderFailCount = 0;
789
for (uint32_t i = 0; i < size; i++) {
790
if (failed) {
791
break;
792
}
793
StoredVulkanPipelineKey key;
794
failed = failed || fread(&key, sizeof(key), 1, file) != 1;
795
if (failed) {
796
ERROR_LOG(Log::G3D, "Truncated Vulkan pipeline cache file, stopping.");
797
break;
798
}
799
800
if (key.raster.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || key.raster.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST) {
801
WARN_LOG(Log::G3D, "Bad raster key in cache, ignoring");
802
continue;
803
}
804
805
VulkanVertexShader *vs = shaderManager->GetVertexShaderFromID(key.vShaderID);
806
VulkanFragmentShader *fs = shaderManager->GetFragmentShaderFromID(key.fShaderID);
807
VulkanGeometryShader *gs = shaderManager->GetGeometryShaderFromID(key.gShaderID);
808
if (!vs || !fs || (!gs && key.gShaderID.Bit(GS_BIT_ENABLED))) {
809
// We just ignore this one, it'll get created later if needed.
810
// Probably some useFlags mismatch.
811
WARN_LOG(Log::G3D, "Failed to find vs or fs in pipeline %d in cache, skipping pipeline", (int)i);
812
continue;
813
}
814
815
// Avoid creating multisampled shaders if it's not enabled, as that results in an invalid combination.
816
// Note that variantsToBuild is NOT directly a RenderPassType! instead, it's a collection of (1 << RenderPassType).
817
u32 variantsToBuild = key.variants;
818
if (multiSampleLevel == 0) {
819
for (u32 i = 0; i < (int)RenderPassType::TYPE_COUNT; i++) {
820
if (RenderPassTypeHasMultisample((RenderPassType)i)) {
821
variantsToBuild &= ~(1 << i);
822
}
823
}
824
}
825
826
DecVtxFormat fmt;
827
fmt.InitializeFromID(key.vtxFmtId);
828
VulkanPipeline *pipeline = GetOrCreatePipeline(
829
rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, gs, key.useHWTransform, variantsToBuild, multiSampleLevel, true);
830
if (!pipeline) {
831
pipelineCreateFailCount += 1;
832
}
833
}
834
835
rm->NudgeCompilerThread();
836
837
// The rest of the work is async, we can't know here if it'll succeed.
838
return true;
839
}
840
841