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