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