diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 30598c92e9..5a25170220 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -131,7 +131,7 @@ void GPU_Vulkan::LoadCache(std::string filename) { VkRenderPass renderPass = g_Config.iRenderingMode == FB_BUFFERED_MODE ? (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS) : (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS); - result = pipelineManager_->LoadCache(f, false, shaderManagerVulkan_, &drawEngine_, drawEngine_.GetPipelineLayout(), renderPass); + result = pipelineManager_->LoadCache(f, false, shaderManagerVulkan_, draw_, drawEngine_.GetPipelineLayout(), renderPass); } fclose(f); if (!result) { @@ -148,7 +148,7 @@ void GPU_Vulkan::SaveCache(std::string filename) { if (!f) return; shaderManagerVulkan_->SaveCache(f); - pipelineManager_->SaveCache(f, false, shaderManagerVulkan_); + pipelineManager_->SaveCache(f, false, shaderManagerVulkan_, draw_); INFO_LOG(G3D, "Saved Vulkan pipeline cache"); fclose(f); } diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index b28f36d8c5..32a59d27b9 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -11,6 +11,9 @@ #include "GPU/Vulkan/PipelineManagerVulkan.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Common/DrawEngineCommon.h" +#include "ext/native/thin3d/thin3d.h" +#include "ext/native/thin3d/VulkanRenderManager.h" +#include "ext/native/thin3d/VulkanQueueRunner.h" PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : vulkan_(vulkan), pipelines_(256) { // The pipeline cache is created on demand (or explicitly through Load). @@ -540,7 +543,25 @@ struct VkPipelineCacheHeader { uint8_t uuid[VK_UUID_SIZE]; }; -void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager) { +struct StoredVulkanPipelineKey { + VulkanPipelineRasterStateKey raster; + VShaderID vShaderID; + FShaderID fShaderID; + uint32_t vtxFmtId; + bool useHWTransform; + bool backbufferPass; + VulkanQueueRunner::RPKey renderPassKey; + + // For std::set. Better zero-initialize the struct properly for this to work. + bool operator < (const StoredVulkanPipelineKey &other) const { + return memcmp(this, &other, sizeof(*this)) < 0; + } +}; + +void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext) { + VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + VulkanQueueRunner *queueRunner = rm->GetQueueRunner(); + size_t dataSize = 0; uint32_t size; @@ -569,6 +590,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha // Make sure the set of pipelines we write is "unique". std::set keys; + // TODO: Use derivative pipelines when possible, helps Mali driver pipeline creation speed at least. pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) { if (failed) return; @@ -587,6 +609,14 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha // NOTE: This is not a vtype, but a decoded vertex format. key.vtxFmtId = pkey.vtxFmtId; } + // Figure out what kind of renderpass this pipeline uses. + if (pkey.renderPass == queueRunner->GetBackbufferRenderPass()) { + key.backbufferPass = true; + key.renderPassKey = {}; + } else { + key.backbufferPass = false; + queueRunner->GetRenderPassKey(pkey.renderPass, &key.renderPassKey); + } keys.insert(key); }); @@ -610,7 +640,10 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha NOTICE_LOG(G3D, "Saved Vulkan pipeline ID cache (%d unique pipelines/%d).", (int)keys.size(), (int)pipelines_.size()); } -bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, DrawEngineCommon *drawEngine, VkPipelineLayout layout, VkRenderPass renderPass) { +bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VkPipelineLayout layout, VkRenderPass renderPass) { + VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + VulkanQueueRunner *queueRunner = rm->GetQueueRunner(); + uint32_t size = 0; if (loadRawPipelineCache) { fread(&size, sizeof(size), 1, file); @@ -673,6 +706,14 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha ERROR_LOG(G3D, "Failed to find vs or fs in of pipeline %d in cache", (int)i); continue; } + + VkRenderPass rp; + if (key.backbufferPass) { + rp = queueRunner->GetBackbufferRenderPass(); + } else { + rp = queueRunner->GetRenderPass(key.renderPassKey); + } + DecVtxFormat fmt; fmt.InitializeFromID(key.vtxFmtId); GetOrCreatePipeline(layout, renderPass, key.raster, diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 1ef673b9b7..07d886bfe6 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -56,19 +56,6 @@ struct VulkanPipelineKey { std::string GetDescription(DebugShaderStringType stringType) const; }; -struct StoredVulkanPipelineKey { - VulkanPipelineRasterStateKey raster; - VShaderID vShaderID; - FShaderID fShaderID; - uint32_t vtxFmtId; - bool useHWTransform; - - // For std::set. Better zero-initialize the struct properly for this to work. - bool operator < (const StoredVulkanPipelineKey &other) const { - return memcmp(this, &other, sizeof(*this)) < 0; - } -}; - enum PipelineFlags { PIPELINE_FLAG_USES_LINES = (1 << 2), PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3), @@ -109,8 +96,8 @@ public: std::vector DebugGetObjectIDs(DebugShaderType type); // Saves data for faster creation next time. - void SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager); - bool LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, DrawEngineCommon *drawEngine, VkPipelineLayout layout, VkRenderPass renderPass); + void SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext); + bool LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VkPipelineLayout layout, VkRenderPass renderPass); private: DenseHashMap pipelines_; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 666f47aa52..8c36edc25a 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -350,7 +350,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 6 +#define CACHE_VERSION 9 struct VulkanCacheHeader { uint32_t magic; uint32_t version; diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 63e9c6efb9..caa8f396e3 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -506,6 +506,8 @@ handleELF: if (File::Exists(screenshotPath)) { if (readFileToString(false, screenshotPath.c_str(), info_->icon.data)) { info_->icon.dataLoaded = true; + } else { + ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str()); } } break; @@ -795,6 +797,8 @@ void GameInfoCache::SetupTexture(std::shared_ptr &info, Draw::DrawCont tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT); if (tex.texture) { tex.timeLoaded = time_now_d(); + } else { + ERROR_LOG(G3D, "Failed creating texture"); } } if ((info->wantFlags & GAMEINFO_WANTBGDATA) == 0) { diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 1bc6753934..fbce3e7862 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -312,9 +312,7 @@ void SavedataBrowser::Refresh() { if (!isState && File::Exists(path_ + fileInfo[i].name + "/PARAM.SFO")) isSaveData = true; - if (isSaveData) { - savedataButtons.push_back(new SavedataButton(fileInfo[i].fullName, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT))); - } else if (isState) { + if (isSaveData || isState) { savedataButtons.push_back(new SavedataButton(fileInfo[i].fullName, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT))); } } diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index bd26a87209..6994c96bff 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -153,8 +153,7 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { assert(res == VK_SUCCESS); } -VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction, VkImageLayout prevColorLayout, VkImageLayout prevDepthLayout, VkImageLayout finalColorLayout) { - RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction, prevColorLayout, prevDepthLayout, finalColorLayout }; +VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { auto pass = renderPasses_.Get(key); if (pass) { return pass; @@ -163,7 +162,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio VkAttachmentDescription attachments[2] = {}; attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - switch (colorLoadAction) { + switch (key.colorLoadAction) { case VKRRenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; @@ -182,14 +181,14 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio attachments[0].initialLayout = VK_IMAGE_LAYOUT_GENERAL; attachments[0].finalLayout = VK_IMAGE_LAYOUT_GENERAL; #else - attachments[0].initialLayout = prevColorLayout; - attachments[0].finalLayout = finalColorLayout; + attachments[0].initialLayout = key.prevColorLayout; + attachments[0].finalLayout = key.finalColorLayout; #endif attachments[0].flags = 0; attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - switch (depthLoadAction) { + switch (key.depthLoadAction) { case VKRRenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; @@ -200,7 +199,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } - switch (stencilLoadAction) { + switch (key.stencilLoadAction) { case VKRRenderPassAction::CLEAR: attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; @@ -217,7 +216,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio attachments[1].initialLayout = VK_IMAGE_LAYOUT_GENERAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL; #else - attachments[1].initialLayout = prevDepthLayout; + attachments[1].initialLayout = key.prevDepthLayout; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; #endif attachments[1].flags = 0; @@ -244,7 +243,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio VkSubpassDependency deps[2]{}; int numDeps = 0; - switch (prevColorLayout) { + switch (key.prevColorLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: // Already the right color layout. Unclear that we need to do a lot here.. break; @@ -266,11 +265,11 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; break; default: - _dbg_assert_msg_(G3D, false, "GetRenderPass: Unexpected color layout %d", (int)prevColorLayout); + _dbg_assert_msg_(G3D, false, "GetRenderPass: Unexpected color layout %d", (int)key.prevColorLayout); break; } - switch (prevDepthLayout) { + switch (key.prevDepthLayout) { case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: // Already the right depth layout. Unclear that we need to do a lot here.. break; @@ -287,7 +286,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; break; default: - _dbg_assert_msg_(G3D, false, "PerformBindRT: Unexpected depth layout %d", (int)prevDepthLayout); + _dbg_assert_msg_(G3D, false, "PerformBindRT: Unexpected depth layout %d", (int)key.prevDepthLayout); break; } @@ -302,7 +301,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio // And the final transition. // Don't need to transition it if VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL. - switch (finalColorLayout) { + switch (key.finalColorLayout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: deps[numDeps].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index d8e63eefad..e9ad43fad7 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -174,11 +174,38 @@ public: void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels); -private: - // Only call this from the render thread! - VkRenderPass GetRenderPass(VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction, - VkImageLayout prevColorLayout, VkImageLayout prevDepthLayout, VkImageLayout finalColorLayout); + struct RPKey { + VKRRenderPassAction colorLoadAction; + VKRRenderPassAction depthLoadAction; + VKRRenderPassAction stencilLoadAction; + VkImageLayout prevColorLayout; + VkImageLayout prevDepthLayout; + VkImageLayout finalColorLayout; + // TODO: Also pre-transition depth, for copies etc. + }; + // Only call this from the render thread! Also ok during initialization (LoadCache). + VkRenderPass GetRenderPass( + VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction, + VkImageLayout prevColorLayout, VkImageLayout prevDepthLayout, VkImageLayout finalColorLayout) { + RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction, prevColorLayout, prevDepthLayout, finalColorLayout }; + return GetRenderPass(key); + } + + VkRenderPass GetRenderPass(const RPKey &key); + + bool GetRenderPassKey(VkRenderPass passToFind, RPKey *outKey) const { + bool found = false; + renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, VkRenderPass pass) { + if (pass == passToFind) { + found = true; + *outKey = rpkey; + } + }); + return found; + } + +private: void InitBackbufferRenderPass(); void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); @@ -208,16 +235,6 @@ private: VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE; - struct RPKey { - VKRRenderPassAction colorAction; - VKRRenderPassAction depthAction; - VKRRenderPassAction stencilAction; - VkImageLayout prevColorLayout; - VkImageLayout prevDepthLayout; - VkImageLayout finalColorLayout; - // TODO: Also pre-transition depth, for copies etc. - }; - // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // TODO: Create these on demand. DenseHashMap renderPasses_; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index d70c62dd74..812943b571 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -234,6 +234,11 @@ public: return vulkan_; } + // Be careful with this. Only meant to be used for fetching render passes for shader cache initialization. + VulkanQueueRunner *GetQueueRunner() { + return &queueRunner_; + } + private: bool InitBackbufferFramebuffers(int width, int height); bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.