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/ShaderManagerVulkan.cpp
Views: 1401
// Copyright (c) 2015- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#ifdef _WIN3218//#define SHADERLOG19#endif2021#include "Common/LogReporting.h"22#include "Common/Math/lin/matrix4x4.h"23#include "Common/Math/math_util.h"24#include "Common/Data/Convert/SmallDataConvert.h"25#include "Common/Profiler/Profiler.h"26#include "Common/GPU/thin3d.h"27#include "Common/Data/Encoding/Utf8.h"28#include "Common/TimeUtil.h"29#include "Common/MemoryUtil.h"3031#include "Common/StringUtils.h"32#include "Common/GPU/Vulkan/VulkanContext.h"33#include "Common/GPU/Vulkan/VulkanMemory.h"34#include "Common/Log.h"35#include "Common/CommonTypes.h"36#include "Core/Config.h"37#include "GPU/Math3D.h"38#include "GPU/GPUState.h"39#include "GPU/ge_constants.h"40#include "GPU/Common/FragmentShaderGenerator.h"41#include "GPU/Common/VertexShaderGenerator.h"42#include "GPU/Common/GeometryShaderGenerator.h"43#include "GPU/Vulkan/ShaderManagerVulkan.h"44#include "GPU/Vulkan/DrawEngineVulkan.h"45#include "GPU/Vulkan/FramebufferManagerVulkan.h"4647// Most drivers treat vkCreateShaderModule as pretty much a memcpy. What actually48// takes time here, and makes this worthy of parallelization, is GLSLtoSPV.49// Takes ownership over tag.50// This always returns something, checking the return value for null is not meaningful.51static Promise<VkShaderModule> *CompileShaderModuleAsync(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *tag) {52auto compile = [=] {53PROFILE_THIS_SCOPE("shadercomp");5455std::string errorMessage;56std::vector<uint32_t> spirv;5758bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage);5960if (!errorMessage.empty()) {61if (success) {62ERROR_LOG(Log::G3D, "Warnings in shader compilation!");63} else {64ERROR_LOG(Log::G3D, "Error in shader compilation!");65}66std::string numberedSource = LineNumberString(code);67ERROR_LOG(Log::G3D, "Messages: %s", errorMessage.c_str());68ERROR_LOG(Log::G3D, "Shader source:\n%s", numberedSource.c_str());69#if PPSSPP_PLATFORM(WINDOWS)70OutputDebugStringA("Error messages:\n");71OutputDebugStringA(errorMessage.c_str());72OutputDebugStringA(numberedSource.c_str());73#endif74Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code);75}7677VkShaderModule shaderModule = VK_NULL_HANDLE;78if (success) {79const char *createTag = tag ? tag->c_str() : nullptr;80if (!createTag) {81switch (stage) {82case VK_SHADER_STAGE_VERTEX_BIT: createTag = "game_vertex"; break;83case VK_SHADER_STAGE_FRAGMENT_BIT: createTag = "game_fragment"; break;84case VK_SHADER_STAGE_GEOMETRY_BIT: createTag = "game_geometry"; break;85case VK_SHADER_STAGE_COMPUTE_BIT: createTag = "game_compute"; break;86default: break;87}88}8990success = vulkan->CreateShaderModule(spirv, &shaderModule, createTag);91#ifdef SHADERLOG92OutputDebugStringA("OK");93#endif94delete tag;95}96return shaderModule;97};9899#if defined(_DEBUG)100// Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator which is HEAVILY used by glslang.101bool singleThreaded = true;102#else103bool singleThreaded = false;104#endif105106if (singleThreaded) {107return Promise<VkShaderModule>::AlreadyDone(compile());108} else {109return Promise<VkShaderModule>::Spawn(&g_threadManager, compile, TaskType::DEDICATED_THREAD);110}111}112113VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, FragmentShaderFlags flags, const char *code)114: vulkan_(vulkan), id_(id), flags_(flags) {115_assert_(!id.is_invalid());116source_ = code;117module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str(), new std::string(FragmentShaderDesc(id)));118VERBOSE_LOG(Log::G3D, "Compiled fragment shader:\n%s\n", (const char *)code);119}120121VulkanFragmentShader::~VulkanFragmentShader() {122if (module_) {123VkShaderModule shaderModule = module_->BlockUntilReady();124if (shaderModule) {125vulkan_->Delete().QueueDeleteShaderModule(shaderModule);126}127vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *m) {128auto module = (Promise<VkShaderModule> *)m;129delete module;130}, module_);131}132}133134std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) const {135switch (type) {136case SHADER_STRING_SOURCE_CODE:137return source_;138case SHADER_STRING_SHORT_DESC:139return FragmentShaderDesc(id_);140default:141return "N/A";142}143}144145VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, VertexShaderFlags flags, const char *code, bool useHWTransform)146: vulkan_(vulkan), useHWTransform_(useHWTransform), flags_(flags), id_(id) {147_assert_(!id.is_invalid());148source_ = code;149module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str(), new std::string(VertexShaderDesc(id)));150VERBOSE_LOG(Log::G3D, "Compiled vertex shader:\n%s\n", (const char *)code);151}152153VulkanVertexShader::~VulkanVertexShader() {154if (module_) {155VkShaderModule shaderModule = module_->BlockUntilReady();156if (shaderModule) {157vulkan_->Delete().QueueDeleteShaderModule(shaderModule);158}159vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *m) {160auto module = (Promise<VkShaderModule> *)m;161delete module;162}, module_);163}164}165166std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) const {167switch (type) {168case SHADER_STRING_SOURCE_CODE:169return source_;170case SHADER_STRING_SHORT_DESC:171return VertexShaderDesc(id_);172default:173return "N/A";174}175}176177VulkanGeometryShader::VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code)178: vulkan_(vulkan), id_(id) {179_assert_(!id.is_invalid());180source_ = code;181module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_GEOMETRY_BIT, source_.c_str(), new std::string(GeometryShaderDesc(id).c_str()));182VERBOSE_LOG(Log::G3D, "Compiled geometry shader:\n%s\n", (const char *)code);183}184185VulkanGeometryShader::~VulkanGeometryShader() {186if (module_) {187VkShaderModule shaderModule = module_->BlockUntilReady();188if (shaderModule) {189vulkan_->Delete().QueueDeleteShaderModule(shaderModule);190}191vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *m) {192auto module = (Promise<VkShaderModule> *)m;193delete module;194}, module_);195}196}197198std::string VulkanGeometryShader::GetShaderString(DebugShaderStringType type) const {199switch (type) {200case SHADER_STRING_SOURCE_CODE:201return source_;202case SHADER_STRING_SHORT_DESC:203return GeometryShaderDesc(id_);204default:205return "N/A";206}207}208209static constexpr size_t CODE_BUFFER_SIZE = 32768;210211ShaderManagerVulkan::ShaderManagerVulkan(Draw::DrawContext *draw)212: ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16), gsCache_(16) {213codeBuffer_ = new char[CODE_BUFFER_SIZE];214VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT);215uboAlignment_ = vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment;216217uniforms_ = (Uniforms *)AllocateAlignedMemory(sizeof(Uniforms), 16);218219static_assert(sizeof(uniforms_->ub_base) <= 512, "ub_base grew too big");220static_assert(sizeof(uniforms_->ub_lights) <= 512, "ub_lights grew too big");221static_assert(sizeof(uniforms_->ub_bones) <= 384, "ub_bones grew too big");222}223224ShaderManagerVulkan::~ShaderManagerVulkan() {225FreeAlignedMemory(uniforms_);226Clear();227delete[] codeBuffer_;228}229230void ShaderManagerVulkan::DeviceLost() {231Clear();232draw_ = nullptr;233}234235void ShaderManagerVulkan::DeviceRestore(Draw::DrawContext *draw) {236VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT);237draw_ = draw;238uboAlignment_ = vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment;239}240241void ShaderManagerVulkan::Clear() {242fsCache_.Iterate([&](const FShaderID &key, VulkanFragmentShader *shader) {243delete shader;244});245vsCache_.Iterate([&](const VShaderID &key, VulkanVertexShader *shader) {246delete shader;247});248gsCache_.Iterate([&](const GShaderID &key, VulkanGeometryShader *shader) {249delete shader;250});251fsCache_.Clear();252vsCache_.Clear();253gsCache_.Clear();254lastFSID_.set_invalid();255lastVSID_.set_invalid();256lastGSID_.set_invalid();257gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);258}259260void ShaderManagerVulkan::ClearShaders() {261Clear();262DirtyLastShader();263gstate_c.Dirty(DIRTY_ALL_UNIFORMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);264}265266void ShaderManagerVulkan::DirtyLastShader() {267// Forget the last shader ID268lastFSID_.set_invalid();269lastVSID_.set_invalid();270lastGSID_.set_invalid();271lastVShader_ = nullptr;272lastFShader_ = nullptr;273lastGShader_ = nullptr;274gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE);275}276277uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) {278uint64_t dirty = gstate_c.GetDirtyUniforms();279if (dirty != 0) {280if (dirty & DIRTY_BASE_UNIFORMS)281BaseUpdateUniforms(&uniforms_->ub_base, dirty, false, useBufferedRendering);282if (dirty & DIRTY_LIGHT_UNIFORMS)283LightUpdateUniforms(&uniforms_->ub_lights, dirty);284if (dirty & DIRTY_BONE_UNIFORMS)285BoneUpdateUniforms(&uniforms_->ub_bones, dirty);286}287gstate_c.CleanUniforms();288return dirty;289}290291void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat, bool useSkinInDecode) {292VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);293294VShaderID VSID;295VulkanVertexShader *vs = nullptr;296if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) {297gstate_c.Clean(DIRTY_VERTEXSHADER_STATE);298ComputeVertexShaderID(&VSID, decoder, useHWTransform, useHWTessellation, weightsAsFloat, useSkinInDecode);299if (VSID == lastVSID_) {300_dbg_assert_(lastVShader_ != nullptr);301vs = lastVShader_;302} else if (!vsCache_.Get(VSID, &vs)) {303// Vertex shader not in cache. Let's compile it.304std::string genErrorString;305uint64_t uniformMask = 0; // Not used306uint32_t attributeMask = 0; // Not used307VertexShaderFlags flags{};308bool success = GenerateVertexShader(VSID, codeBuffer_, compat_, draw_->GetBugs(), &attributeMask, &uniformMask, &flags, &genErrorString);309_assert_msg_(success, "VS gen error: %s", genErrorString.c_str());310_assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_));311312// Don't need to re-lookup anymore, now that we lock wider.313vs = new VulkanVertexShader(vulkan, VSID, flags, codeBuffer_, useHWTransform);314vsCache_.Insert(VSID, vs);315}316lastVShader_ = vs;317lastVSID_ = VSID;318} else {319VSID = lastVSID_;320vs = lastVShader_;321}322*vshader = vs;323324FShaderID FSID;325VulkanFragmentShader *fs = nullptr;326if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) {327gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE);328ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs());329if (FSID == lastFSID_) {330_dbg_assert_(lastFShader_ != nullptr);331fs = lastFShader_;332} else if (!fsCache_.Get(FSID, &fs)) {333// Fragment shader not in cache. Let's compile it.334std::string genErrorString;335uint64_t uniformMask = 0; // Not used336FragmentShaderFlags flags{};337bool success = GenerateFragmentShader(FSID, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString);338_assert_msg_(success, "FS gen error: %s", genErrorString.c_str());339_assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_));340341fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_);342fsCache_.Insert(FSID, fs);343}344lastFShader_ = fs;345lastFSID_ = FSID;346} else {347FSID = lastFSID_;348fs = lastFShader_;349}350*fshader = fs;351352GShaderID GSID;353VulkanGeometryShader *gs = nullptr;354if (gstate_c.IsDirty(DIRTY_GEOMETRYSHADER_STATE)) {355gstate_c.Clean(DIRTY_GEOMETRYSHADER_STATE);356ComputeGeometryShaderID(&GSID, draw_->GetBugs(), prim);357if (GSID == lastGSID_) {358// it's ok for this to be null.359gs = lastGShader_;360} else if (GSID.Bit(GS_BIT_ENABLED)) {361if (!gsCache_.Get(GSID, &gs)) {362// Geometry shader not in cache. Let's compile it.363std::string genErrorString;364bool success = GenerateGeometryShader(GSID, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString);365_assert_msg_(success, "GS gen error: %s", genErrorString.c_str());366_assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_));367368gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_);369gsCache_.Insert(GSID, gs);370}371} else {372gs = nullptr;373}374lastGShader_ = gs;375lastGSID_ = GSID;376} else {377GSID = lastGSID_;378gs = lastGShader_;379}380*gshader = gs;381382_dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE));383_dbg_assert_(FSID.Bit(FS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE));384if (GSID.Bit(GS_BIT_ENABLED)) {385_dbg_assert_(GSID.Bit(GS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE));386}387388_dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was computed");389}390391std::vector<std::string> ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType type) {392std::vector<std::string> ids;393switch (type) {394case SHADER_TYPE_VERTEX:395vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *shader) {396std::string idstr;397id.ToString(&idstr);398ids.push_back(idstr);399});400break;401case SHADER_TYPE_FRAGMENT:402fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *shader) {403std::string idstr;404id.ToString(&idstr);405ids.push_back(idstr);406});407break;408case SHADER_TYPE_GEOMETRY:409gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) {410std::string idstr;411id.ToString(&idstr);412ids.push_back(idstr);413});414break;415default:416break;417}418return ids;419}420421std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {422ShaderID shaderId;423shaderId.FromString(id);424switch (type) {425case SHADER_TYPE_VERTEX:426{427VulkanVertexShader *vs;428if (vsCache_.Get(VShaderID(shaderId), &vs)) {429return vs ? vs->GetShaderString(stringType) : "null (bad)";430} else {431return "";432}433}434case SHADER_TYPE_FRAGMENT:435{436VulkanFragmentShader *fs;437if (fsCache_.Get(FShaderID(shaderId), &fs)) {438return fs ? fs->GetShaderString(stringType) : "null (bad)";439} else {440return "";441}442}443case SHADER_TYPE_GEOMETRY:444{445VulkanGeometryShader *gs;446if (gsCache_.Get(GShaderID(shaderId), &gs)) {447return gs ? gs->GetShaderString(stringType) : "null (bad)";448} else {449return "";450}451}452default:453return "N/A";454}455}456457VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModule module) {458VulkanVertexShader *vs = nullptr;459vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *shader) {460Promise<VkShaderModule> *p = shader->GetModule();461VkShaderModule m = p->BlockUntilReady();462if (m == module)463vs = shader;464});465return vs;466}467468VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderModule module) {469VulkanFragmentShader *fs = nullptr;470fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *shader) {471Promise<VkShaderModule> *p = shader->GetModule();472VkShaderModule m = p->BlockUntilReady();473if (m == module)474fs = shader;475});476return fs;477}478479VulkanGeometryShader *ShaderManagerVulkan::GetGeometryShaderFromModule(VkShaderModule module) {480VulkanGeometryShader *gs = nullptr;481gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) {482Promise<VkShaderModule> *p = shader->GetModule();483VkShaderModule m = p->BlockUntilReady();484if (m == module)485gs = shader;486});487return gs;488}489490// Shader cache.491//492// We simply store the IDs of the shaders used during gameplay. On next startup of493// the same game, we simply compile all the shaders from the start, so we don't have to494// compile them on the fly later. We also store the Vulkan pipeline cache, so if it contains495// pipelines compiled from SPIR-V matching these shaders, pipeline creation will be practically496// instantaneous.497498enum class VulkanCacheDetectFlags {499EQUAL_DEPTH = 1,500};501502#define CACHE_HEADER_MAGIC 0xff51f420503#define CACHE_VERSION 51504505struct VulkanCacheHeader {506uint32_t magic;507uint32_t version;508uint32_t useFlags;509uint32_t detectFlags;510int numVertexShaders;511int numFragmentShaders;512int numGeometryShaders;513};514515bool ShaderManagerVulkan::LoadCacheFlags(FILE *f, DrawEngineVulkan *drawEngine) {516VulkanCacheHeader header{};517long pos = ftell(f);518bool success = fread(&header, sizeof(header), 1, f) == 1;519// We'll read it again later, this is just to check the flags.520success = success && fseek(f, pos, SEEK_SET) == 0;521if (!success || header.magic != CACHE_HEADER_MAGIC) {522WARN_LOG(Log::G3D, "Shader cache magic mismatch");523return false;524}525if (header.version != CACHE_VERSION) {526WARN_LOG(Log::G3D, "Shader cache version mismatch, %d, expected %d", header.version, CACHE_VERSION);527return false;528}529530if ((header.detectFlags & (uint32_t)VulkanCacheDetectFlags::EQUAL_DEPTH) != 0) {531drawEngine->SetEverUsedExactEqualDepth(true);532}533534return true;535}536537bool ShaderManagerVulkan::LoadCache(FILE *f) {538VulkanCacheHeader header{};539bool success = fread(&header, sizeof(header), 1, f) == 1;540// We don't need to validate magic/version again, done in LoadCacheFlags().541542if (header.useFlags != gstate_c.GetUseFlags()) {543// This can simply be a result of sawExactEqualDepth_ having been flipped to true in the previous run.544// Let's just keep going.545WARN_LOG(Log::G3D, "Shader cache useFlags mismatch, %08x, expected %08x", header.useFlags, gstate_c.GetUseFlags());546} else {547// We're compiling shaders now, so they haven't changed anymore.548gstate_c.useFlagsChanged = false;549}550551int failCount = 0;552553VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);554for (int i = 0; i < header.numVertexShaders; i++) {555VShaderID id;556if (fread(&id, sizeof(id), 1, f) != 1) {557ERROR_LOG(Log::G3D, "Vulkan shader cache truncated (in VertexShaders)");558return false;559}560bool useHWTransform = id.Bit(VS_BIT_USE_HW_TRANSFORM);561std::string genErrorString;562uint32_t attributeMask = 0;563uint64_t uniformMask = 0;564VertexShaderFlags flags;565if (!GenerateVertexShader(id, codeBuffer_, compat_, draw_->GetBugs(), &attributeMask, &uniformMask, &flags, &genErrorString)) {566ERROR_LOG(Log::G3D, "Failed to generate vertex shader during cache load");567// We just ignore this one and carry on.568failCount++;569continue;570}571_assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_));572// Don't add the new shader if already compiled - though this should no longer happen.573if (!vsCache_.ContainsKey(id)) {574VulkanVertexShader *vs = new VulkanVertexShader(vulkan, id, flags, codeBuffer_, useHWTransform);575vsCache_.Insert(id, vs);576}577}578uint32_t vendorID = vulkan->GetPhysicalDeviceProperties().properties.vendorID;579580for (int i = 0; i < header.numFragmentShaders; i++) {581FShaderID id;582if (fread(&id, sizeof(id), 1, f) != 1) {583ERROR_LOG(Log::G3D, "Vulkan shader cache truncated (in FragmentShaders)");584return false;585}586std::string genErrorString;587uint64_t uniformMask = 0;588FragmentShaderFlags flags;589if (!GenerateFragmentShader(id, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString)) {590ERROR_LOG(Log::G3D, "Failed to generate fragment shader during cache load");591// We just ignore this one and carry on.592failCount++;593continue;594}595_assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_));596if (!fsCache_.ContainsKey(id)) {597VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_);598fsCache_.Insert(id, fs);599}600}601602// If it's not enabled, don't create shaders cached from earlier runs - creation will likely fail.603if (gstate_c.Use(GPU_USE_GS_CULLING)) {604for (int i = 0; i < header.numGeometryShaders; i++) {605GShaderID id;606if (fread(&id, sizeof(id), 1, f) != 1) {607ERROR_LOG(Log::G3D, "Vulkan shader cache truncated (in GeometryShaders)");608return false;609}610std::string genErrorString;611if (!GenerateGeometryShader(id, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString)) {612ERROR_LOG(Log::G3D, "Failed to generate geometry shader during cache load");613// We just ignore this one and carry on.614failCount++;615continue;616}617_assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_));618if (!gsCache_.ContainsKey(id)) {619VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_);620gsCache_.Insert(id, gs);621}622}623}624625NOTICE_LOG(Log::G3D, "ShaderCache: Loaded %d vertex, %d fragment shaders and %d geometry shaders (failed %d)", header.numVertexShaders, header.numFragmentShaders, header.numGeometryShaders, failCount);626return true;627}628629void ShaderManagerVulkan::SaveCache(FILE *f, DrawEngineVulkan *drawEngine) {630VulkanCacheHeader header{};631header.magic = CACHE_HEADER_MAGIC;632header.version = CACHE_VERSION;633header.useFlags = gstate_c.GetUseFlags();634header.detectFlags = 0;635if (drawEngine->EverUsedExactEqualDepth())636header.detectFlags |= (uint32_t)VulkanCacheDetectFlags::EQUAL_DEPTH;637header.numVertexShaders = (int)vsCache_.size();638header.numFragmentShaders = (int)fsCache_.size();639header.numGeometryShaders = (int)gsCache_.size();640bool writeFailed = fwrite(&header, sizeof(header), 1, f) != 1;641vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *vs) {642writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1;643});644fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *fs) {645writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1;646});647gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *gs) {648writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1;649});650if (writeFailed) {651ERROR_LOG(Log::G3D, "Failed to write Vulkan shader cache, disk full?");652} else {653NOTICE_LOG(Log::G3D, "Saved %d vertex and %d fragment shaders", header.numVertexShaders, header.numFragmentShaders);654}655}656657658