CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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