diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 899bd56655..21a444f894 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -693,6 +693,48 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { return frameData_[curFrame].initCmd; } +VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask) { + VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(); + _dbg_assert_(desc->vertexShader); + _dbg_assert_(desc->fragmentShader); + pipeline->desc = desc; + if (curRenderStep_) { + // The common case + pipelinesToCheck_.push_back(pipeline); + } else { + if (!variantBitmask) { + WARN_LOG(G3D, "WARNING: Will not compile any variants of pipeline, not in renderpass and empty variantBitmask"); + } + // Presumably we're in initialization, loading the shader cache. + // Look at variantBitmask to see what variants we should queue up. + RPKey key{ + VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, + VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE, + }; + VKRRenderPass *compatibleRenderPass = queueRunner_.GetRenderPass(key); + compileMutex_.lock(); + for (int i = 0; i < RP_TYPE_COUNT; i++) { + if (!(variantBitmask & (1 << i))) + continue; + RenderPassType rpType = (RenderPassType)i; + pipeline->pipeline[rpType] = Promise::CreateEmpty(); + compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType)); + } + compileMutex_.unlock(); + } + return pipeline; +} + +VKRComputePipeline *VulkanRenderManager::CreateComputePipeline(VKRComputePipelineDesc *desc) { + VKRComputePipeline *pipeline = new VKRComputePipeline(); + pipeline->desc = desc; + compileMutex_.lock(); + compileQueue_.push_back(CompileQueueEntry(pipeline)); + compileCond_.notify_one(); + compileMutex_.unlock(); + return pipeline; +} + void VulkanRenderManager::EndCurRenderStep() { if (!curRenderStep_) return; @@ -713,13 +755,15 @@ void VulkanRenderManager::EndCurRenderStep() { // Save the accumulated pipeline flags so we can use that to configure the render pass. // We'll often be able to avoid loading/saving the depth/stencil buffer. compileMutex_.lock(); - for (VKRGraphicsPipeline *pipeline : pipelinesToCreate_) { - pipeline->pipeline[rpType] = Promise::CreateEmpty(); - compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType)); + for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) { + if (!pipeline->pipeline[rpType]) { + pipeline->pipeline[rpType] = Promise::CreateEmpty(); + compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType)); + } } compileCond_.notify_one(); compileMutex_.unlock(); - pipelinesToCreate_.clear(); + pipelinesToCheck_.clear(); // We don't do this optimization for very small targets, probably not worth it. if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) { @@ -959,7 +1003,6 @@ bool VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { // We share the same depth buffer but have multiple color buffers, see the loop below. VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; - VLOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; fb_info.renderPass = queueRunner_.GetCompatibleRenderPass()->Get(vulkan_, RP_TYPE_BACKBUFFER); fb_info.attachmentCount = 2; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 191f9d33e7..24a3fca123 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -166,6 +166,16 @@ struct VKRGraphicsPipeline { delete desc; } + u32 GetVariantsBitmask() const { + u32 bitmask = 0; + for (int i = 0; i < RP_TYPE_COUNT; i++) { + if (pipeline[i]) { + bitmask |= 1 << i; + } + } + return bitmask; + } + VKRGraphicsPipelineDesc *desc = nullptr; Promise *pipeline[RP_TYPE_COUNT]; @@ -248,30 +258,22 @@ public: // Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding // stalling the emulation thread as much as possible. // We delay creating pipelines until the end of the current render pass, so we can create the right type immediately. - // WARNING: desc must stick around! It's not enough to build it on the stack. - VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) { - VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(); - _dbg_assert_(desc->vertexShader); - _dbg_assert_(desc->fragmentShader); - pipeline->desc = desc; - pipelinesToCreate_.push_back(pipeline); - return pipeline; - } + // Unless a variantBitmask is passed in, in which case we can just go ahead. + // WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it. + VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask); + VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc); - VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc) { - VKRComputePipeline *pipeline = new VKRComputePipeline(); - pipeline->desc = desc; + void NudgeCompilerThread() { compileMutex_.lock(); - compileQueue_.push_back(CompileQueueEntry(pipeline)); compileCond_.notify_one(); compileMutex_.unlock(); - return pipeline; } void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; + pipelinesToCheck_.push_back(pipeline); data.graphics_pipeline.pipeline = pipeline; data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; @@ -577,8 +579,8 @@ private: std::mutex compileMutex_; std::vector compileQueue_; - // pipelines to create at the end of the current render pass. - std::vector pipelinesToCreate_; + // pipelines to check and possibly create at the end of the current render pass. + std::vector pipelinesToCheck_; // Swap chain management struct SwapchainImageData { diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 418050fe20..d456aadcfb 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1110,7 +1110,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; raster->ToVulkan(&gDesc.rs); - pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc);; + pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, 1 << RP_TYPE_BACKBUFFER); if (desc.uniformDesc) { pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 36c34e20a4..8d58346a8c 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1866,10 +1866,9 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma return true; } break; - default: - WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat)); - return false; } + WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat)); + return false; } else if (texFormat == GE_TFMT_5650 && bufferFormat == GE_FORMAT_DEPTH16) { // We can also "depal" 565 format, this is used to read depth buffers as 565 on occasion (#15491). return true; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 9bf95b59b7..8f4cecfd81 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -768,7 +768,7 @@ void DrawEngineVulkan::DoFlush() { } _dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader"); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. return; @@ -893,7 +893,7 @@ void DrawEngineVulkan::DoFlush() { } shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. decodedVerts_ = 0; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index a8b3f327bf..76423fae84 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -18,6 +18,10 @@ using namespace PPSSPP_VK; +u32 VulkanPipeline::GetVariantsBitmask() const { + return pipeline->GetVariantsBitmask(); +} + PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) { // The pipeline cache is created on demand (or explicitly through Load). } @@ -30,10 +34,6 @@ PipelineManagerVulkan::~PipelineManagerVulkan() { } void PipelineManagerVulkan::Clear() { - // This should kill off all the shaders at once. - // This could also be an opportunity to store the whole cache to disk. Will need to also - // store the keys. - pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) { for (int i = 0; i < RP_TYPE_COUNT; i++) { if (value->pipeline) { @@ -180,7 +180,7 @@ static std::string CutFromMain(std::string str) { static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &key, - const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { + const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { VKRGraphicsPipelineDesc *desc = new VKRGraphicsPipelineDesc(); desc->pipelineCache = pipelineCache; @@ -304,7 +304,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, desc->pipelineLayout = layout; - VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc); + VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, variantBitmask); VulkanPipeline *vulkanPipeline = new VulkanPipeline(); vulkanPipeline->pipeline = pipeline; @@ -319,7 +319,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, return vulkanPipeline; } -VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { +VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { if (!pipelineCache_) { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); @@ -340,7 +340,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * VulkanPipeline *pipeline = CreateVulkanPipeline( renderManager, pipelineCache_, layout, - rasterKey, decFmt, vs, fs, useHwTransform); + rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask); pipelines_.Insert(key, pipeline); // Don't return placeholder null pipelines. @@ -546,7 +546,8 @@ struct StoredVulkanPipelineKey { VShaderID vShaderID; FShaderID fShaderID; uint32_t vtxFmtId; - bool useHWTransform; + uint32_t variants; + bool useHWTransform; // TODO: Still needed? // For std::set. Better zero-initialize the struct properly for this to work. bool operator < (const StoredVulkanPipelineKey &other) const { @@ -603,6 +604,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha key.useHWTransform = pkey.useHWTransform; key.fShaderID = fshader->GetID(); key.vShaderID = vshader->GetID(); + key.variants = value->GetVariantsBitmask(); if (key.useHWTransform) { // NOTE: This is not a vtype, but a decoded vertex format. key.vtxFmtId = pkey.vtxFmtId; @@ -714,11 +716,14 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha DecVtxFormat fmt; fmt.InitializeFromID(key.vtxFmtId); - VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform); + VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform, key.variants); if (!pipeline) { pipelineCreateFailCount += 1; } } + + rm->NudgeCompilerThread(); + NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines, %d failed).", (int)size, pipelineCreateFailCount); return true; } diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 5c90723518..da4a59af62 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -58,6 +58,8 @@ struct VulkanPipeline { bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; } bool UsesLines() const { return (flags & PIPELINE_FLAG_USES_LINES) != 0; } bool UsesDepthStencil() const { return (flags & PIPELINE_FLAG_USES_DEPTH_STENCIL) != 0; } + + u32 GetVariantsBitmask() const; }; class VulkanContext; @@ -71,7 +73,8 @@ public: PipelineManagerVulkan(VulkanContext *ctx); ~PipelineManagerVulkan(); - VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform); + // variantMask is only used when loading pipelines from cache. + VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask); int GetNumPipelines() const { return (int)pipelines_.size(); } void Clear(); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 3d9074a259..3eb71ee8d8 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -371,7 +371,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 24 +#define CACHE_VERSION 25 struct VulkanCacheHeader { uint32_t magic; uint32_t version; @@ -391,10 +391,6 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { if (header.featureFlags != gstate_c.featureFlags) return false; - // Temporarily disable shader cache. - if (true) - return false; - VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); for (int i = 0; i < header.numVertexShaders; i++) { VShaderID id;