diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index b00ec67a01..92b7fb7e83 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1182,18 +1182,11 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: { - VKRGraphicsPipeline *pipeline = c.graphics_pipeline.pipeline; - if (pipeline->Pending()) { - // Stall processing, waiting for the compile queue to catch up. - std::unique_lock lock(compileDoneMutex_); - while (!pipeline->pipeline) { - compileDone_.wait(lock); - } - } - if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); + VkPipeline pipeline = c.graphics_pipeline.pipeline->BlockUntilReady(); + if (pipeline != lastGraphicsPipeline && pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); pipelineLayout = c.pipeline.pipelineLayout; - lastGraphicsPipeline = pipeline->pipeline; + lastGraphicsPipeline = pipeline; // Reset dynamic state so it gets refreshed with the new pipeline. lastStencilWriteMask = -1; lastStencilCompareMask = -1; @@ -1204,18 +1197,11 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::BIND_COMPUTE_PIPELINE: { - VKRComputePipeline *pipeline = c.compute_pipeline.pipeline; - if (pipeline->Pending()) { - // Stall processing, waiting for the compile queue to catch up. - std::unique_lock lock(compileDoneMutex_); - while (!pipeline->pipeline) { - compileDone_.wait(lock); - } - } - if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); + VkPipeline pipeline = c.graphics_pipeline.pipeline->BlockUntilReady(); + if (pipeline != lastComputePipeline && pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); pipelineLayout = c.pipeline.pipelineLayout; - lastComputePipeline = pipeline->pipeline; + lastComputePipeline = pipeline; } break; } @@ -1335,7 +1321,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } default: ERROR_LOG(G3D, "Unimpl queue command"); - ; } } vkCmdEndRenderPass(cmd); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 401a125d6b..1d1c4efe8f 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -4,7 +4,7 @@ #include #include - +#include "Common/Thread/Promise.h" #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanBarrier.h" @@ -55,11 +55,11 @@ struct VkRenderData { VkPipelineLayout pipelineLayout; } pipeline; struct { - VKRGraphicsPipeline *pipeline; + Promise *pipeline; VkPipelineLayout pipelineLayout; } graphics_pipeline; struct { - VKRComputePipeline *pipeline; + Promise *pipeline; VkPipelineLayout pipelineLayout; } compute_pipeline; struct { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index ba2c64f758..7741fb2e44 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -5,6 +5,7 @@ #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Common/TimeUtil.h" #include "Common/GPU/Vulkan/VulkanAlloc.h" #include "Common/GPU/Vulkan/VulkanContext.h" @@ -29,23 +30,54 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { // Already failed to create this one. return false; } + + // Fill in the last part of the desc since now it's time to block. + VkShaderModule vs = desc->vertexShader->BlockUntilReady(); + VkShaderModule fs = desc->fragmentShader->BlockUntilReady(); + + if (!vs || !fs) { + ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules"); + // We're kinda screwed here? + return false; + } + + VkPipelineShaderStageCreateInfo ss[2]{}; + ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + ss[0].pSpecializationInfo = nullptr; + ss[0].module = vs; + ss[0].pName = "main"; + ss[0].flags = 0; + ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + ss[1].pSpecializationInfo = nullptr; + ss[1].module = fs; + ss[1].pName = "main"; + ss[1].flags = 0; + + desc->pipe.pStages = ss; + desc->pipe.stageCount = 2; + + double start = time_now_d(); VkPipeline vkpipeline; VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline); + INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0); + bool success = true; if (result == VK_INCOMPLETE) { // Bad (disallowed by spec) return value seen on Adreno in Burnout :( Try to ignore? // Would really like to log more here, we could probably attach more info to desc. // // At least create a null placeholder to avoid creating over and over if something is broken. - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); success = false; } else if (result != VK_SUCCESS) { - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline = vkpipeline; + pipeline->Post(vkpipeline); } delete desc; @@ -63,11 +95,11 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) { bool success = true; if (result != VK_SUCCESS) { - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating compute pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline = vkpipeline; + pipeline->Post(vkpipeline); } delete desc; @@ -449,6 +481,12 @@ void VulkanRenderManager::CompileThreadFunc() { if (!run_) { break; } + + INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); + + // TODO: Here we can sort the pending pipelines by vertex and fragment shaders, + // and split up further. + // Those with the same pairs of shaders should be on the same thread. for (auto &entry : toCompile) { switch (entry.type) { case CompileQueueEntry::Type::GRAPHICS: diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index a541bea57e..4b69247525 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -12,6 +12,7 @@ #include #include +#include "Common/Thread/Promise.h" #include "Common/System/Display.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/Data/Convert/SmallDataConvert.h" @@ -121,7 +122,11 @@ struct VKRGraphicsPipelineDesc { VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - VkPipelineShaderStageCreateInfo shaderStageInfo[2]{}; + + // Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish. + Promise *vertexShader; + Promise *fragmentShader; + VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; VkVertexInputAttributeDescription attrs[8]{}; VkVertexInputBindingDescription ibd{}; @@ -139,10 +144,12 @@ struct VKRComputePipelineDesc { // Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame. struct VKRGraphicsPipeline { VKRGraphicsPipeline() { - pipeline = VK_NULL_HANDLE; + pipeline = Promise::CreateEmpty(); } + VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid. - std::atomic pipeline; + + Promise *pipeline; bool Create(VulkanContext *vulkan); bool Pending() const { @@ -155,7 +162,7 @@ struct VKRComputePipeline { pipeline = VK_NULL_HANDLE; } VKRComputePipelineDesc *desc = nullptr; - std::atomic pipeline; + Promise *pipeline; bool Create(VulkanContext *vulkan); bool Pending() const { @@ -256,7 +263,7 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; - data.graphics_pipeline.pipeline = pipeline; + data.graphics_pipeline.pipeline = pipeline->pipeline; data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); @@ -266,7 +273,7 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE }; - data.compute_pipeline.pipeline = pipeline; + data.compute_pipeline.pipeline = pipeline->pipeline; data.compute_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); diff --git a/Common/Thread/Channel.h b/Common/Thread/Channel.h index 9ccbbd7096..b6b23fdebe 100644 --- a/Common/Thread/Channel.h +++ b/Common/Thread/Channel.h @@ -19,7 +19,7 @@ struct Mailbox { std::mutex mutex_; std::condition_variable condvar_; - T data_ = nullptr; + T data_{}; T Wait() { std::unique_lock lock(mutex_); @@ -43,7 +43,7 @@ struct Mailbox { std::unique_lock lock(mutex_); if (!data_) { data_ = data; - condvar_.notify_one(); + condvar_.notify_all(); return true; } else { // Already has value. diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index fe2ddb3807..4145b4b6c1 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -1,7 +1,9 @@ #pragma once #include +#include +#include "Common/Log.h" #include "Common/Thread/Channel.h" #include "Common/Thread/ThreadManager.h" @@ -33,6 +35,7 @@ public: // Has ownership over the data. Single use. // TODO: Split Mailbox (rx_ and tx_) up into separate proxy objects. // NOTE: Poll/BlockUntilReady should only be used from one thread. +// TODO: Make movable? template class Promise { public: @@ -47,15 +50,36 @@ public: return promise; } + static Promise *AlreadyDone(T data) { + Promise *promise = new Promise(); + promise->data_ = data; + promise->ready_ = true; + return promise; + } + + static Promise *CreateEmpty() { + Mailbox *mailbox = new Mailbox(); + Promise *promise = new Promise(); + promise->rx_ = mailbox; + return promise; + } + + // Allow an empty promise to spawn, too, in case we want to delay it. + void SpawnEmpty(ThreadManager *threadman, std::function fun, TaskType taskType) { + PromiseTask *task = new PromiseTask(fun, rx_, taskType); + threadman->EnqueueTask(task); + } + ~Promise() { + std::lock_guard guard(readyMutex_); // A promise should have been fulfilled before it's destroyed. _assert_(ready_); _assert_(!rx_); - delete data_; } // Returns T if the data is ready, nullptr if it's not. T Poll() { + std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { @@ -71,6 +95,7 @@ public: } T BlockUntilReady() { + std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { @@ -82,10 +107,17 @@ public: } } + // For outside injection of data, when not using Spawn + void Post(T data) { + rx_->Send(data); + } + private: Promise() {} - T data_ = nullptr; + // Promise can only be constructed in Spawn (or AlreadyDone). + T data_{}; bool ready_ = false; - Mailbox *rx_; + std::mutex readyMutex_; + Mailbox *rx_ = nullptr; }; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 86fa279b9c..8909283884 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -36,7 +36,7 @@ void PipelineManagerVulkan::Clear() { pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) { if (value->pipeline) { - VkPipeline pipeline = value->pipeline->pipeline; + VkPipeline pipeline = value->pipeline->pipeline->BlockUntilReady(); vulkan_->Delete().QueueDeletePipeline(pipeline); vulkan_->Delete().QueueCallback([](void *p) { VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p; @@ -256,28 +256,8 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, ms.pSampleMask = nullptr; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkPipelineShaderStageCreateInfo *ss = &desc->shaderStageInfo[0]; - ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - ss[0].pSpecializationInfo = nullptr; - ss[0].module = vs->GetModule(); - ss[0].pName = "main"; - ss[0].flags = 0; - ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - ss[1].pSpecializationInfo = nullptr; - ss[1].module = fs->GetModule(); - ss[1].pName = "main"; - ss[1].flags = 0; - - if (!ss[0].module || !ss[1].module) { - ERROR_LOG(G3D, "Failed creating graphics pipeline - bad shaders"); - // Create a placeholder to avoid creating over and over if shader compiler broken. - VulkanPipeline *nullPipeline = new VulkanPipeline(); - nullPipeline->pipeline = VK_NULL_HANDLE; - nullPipeline->flags = 0; - return nullPipeline; - } + desc->fragmentShader = fs->GetModule(); + desc->vertexShader = vs->GetModule(); VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly; inputAssembly.flags = 0; @@ -321,7 +301,6 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkGraphicsPipelineCreateInfo &pipe = desc->pipe; pipe.flags = 0; pipe.stageCount = 2; - pipe.pStages = ss; pipe.basePipelineIndex = 0; pipe.pColorBlendState = &desc->cbs; @@ -633,8 +612,8 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) { if (failed) return; - VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader); - VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader); + VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady()); + VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady()); if (!vshader || !fshader) { failed = true; return; diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 37d542e9a0..a6ab695cef 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -18,7 +18,9 @@ #pragma once #include + #include "Common/Data/Collections/Hashmaps.h" +#include "Common/Thread/Promise.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/ShaderId.h" @@ -33,8 +35,8 @@ class VulkanRenderManager; struct VulkanPipelineKey { VulkanPipelineRasterStateKey raster; // prim is included here VkRenderPass renderPass; - VkShaderModule vShader; - VkShaderModule fShader; + Promise *vShader; + Promise *fShader; uint32_t vtxFmtId; bool useHWTransform; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 400f4d180c..439602ae46 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -42,62 +42,62 @@ #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/FramebufferManagerVulkan.h" -VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { - PROFILE_THIS_SCOPE("shadercomp"); +// Most drivers treat vkCreateShaderModule as pretty much a memcpy. What actually +// takes time here, and makes this worthy of parallelization, is GLSLtoSPV. +Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { + return Promise::Spawn(&g_threadManager, [=] { + PROFILE_THIS_SCOPE("shadercomp"); - std::string errorMessage; - std::vector spirv; + std::string errorMessage; + std::vector spirv; - bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); + bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); - VkShaderModule shaderModule = VK_NULL_HANDLE; - - if (!errorMessage.empty()) { - if (success) { - ERROR_LOG(G3D, "Warnings in shader compilation!"); - } else { - ERROR_LOG(G3D, "Error in shader compilation!"); + if (!errorMessage.empty()) { + if (success) { + ERROR_LOG(G3D, "Warnings in shader compilation!"); + } else { + ERROR_LOG(G3D, "Error in shader compilation!"); + } + ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); + ERROR_LOG(G3D, "Shader source:\n%s", code); +#ifdef SHADERLOG + OutputDebugStringA(LineNumberString(code).c_str()); + OutputDebugStringA("Error messages:\n"); + OutputDebugStringA(errorMessage.c_str()); +#endif + Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } - ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); - ERROR_LOG(G3D, "Shader source:\n%s", code); -#ifdef SHADERLOG - OutputDebugStringA(LineNumberString(code).c_str()); - OutputDebugStringA("Error messages:\n"); - OutputDebugStringA(errorMessage.c_str()); -#endif - Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); - } else { -#ifdef SHADERLOG - OutputDebugStringA(LineNumberString(code).c_str()); -#endif -#ifdef SHADERLOG - OutputDebugStringA("OK\n"); -#endif - } - if (success) { - vulkan->CreateShaderModule(spirv, &shaderModule); - } + VkShaderModule shaderModule = VK_NULL_HANDLE; + if (success) { + success = vulkan->CreateShaderModule(spirv, &shaderModule); +#ifdef SHADERLOG + OutputDebugStringA("OK"); +#endif + } - return shaderModule; + return shaderModule; + }, TaskType::CPU_COMPUTE); } VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code) : vulkan_(vulkan), id_(id) { source_ = code; - module_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); if (!module_) { failed_ = true; - return; } else { VERBOSE_LOG(G3D, "Compiled fragment shader:\n%s\n", (const char *)code); } } VulkanFragmentShader::~VulkanFragmentShader() { - if (module_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteShaderModule(module_); + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; } } @@ -115,18 +115,19 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform) : vulkan_(vulkan), useHWTransform_(useHWTransform), id_(id) { source_ = code; - module_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); if (!module_) { failed_ = true; - return; } else { VERBOSE_LOG(G3D, "Compiled vertex shader:\n%s\n", (const char *)code); } } VulkanVertexShader::~VulkanVertexShader() { - if (module_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteShaderModule(module_); + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; } } @@ -334,7 +335,9 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModule module) { VulkanVertexShader *vs = nullptr; vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *shader) { - if (shader->GetModule() == module) + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) vs = shader; }); return vs; @@ -343,7 +346,9 @@ VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModul VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderModule module) { VulkanFragmentShader *fs = nullptr; fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *shader) { - if (shader->GetModule() == module) + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) fs = shader; }); return fs; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index ff7e5d8c5c..50915be6a2 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -20,6 +20,7 @@ #include #include +#include "Common/Thread/Promise.h" #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/Vulkan/VulkanMemory.h" #include "GPU/Common/ShaderCommon.h" @@ -43,11 +44,11 @@ public: bool Failed() const { return failed_; } std::string GetShaderString(DebugShaderStringType type) const; - VkShaderModule GetModule() const { return module_; } + Promise *GetModule() { return module_; } const FShaderID &GetID() { return id_; } protected: - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *module_ = nullptr; VulkanContext *vulkan_; std::string source_; @@ -66,11 +67,11 @@ public: bool UseHWTransform() const { return useHWTransform_; } std::string GetShaderString(DebugShaderStringType type) const; - VkShaderModule GetModule() const { return module_; } + Promise *GetModule() { return module_; } const VShaderID &GetID() { return id_; } protected: - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *module_ = nullptr; VulkanContext *vulkan_; std::string source_; diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index 56ae1987d1..fbacbb1219 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -1094,8 +1094,8 @@ AVI Dump started. = AVI dump started AVI Dump stopped. = AVI dump stopped Cache ISO in RAM = Cachea hela ISO:n i RAM Change CPU Clock = Ändra CPU-frekvens (kan orsaka instabilitet) -Color Saturation = Color Saturation -Color Tint = Color Tint +Color Saturation = Färgmättnad +Color Tint = Färgförskjutning Error: load undo state is from a different game = Error: load undo state is from a different game Failed to load state for load undo. Error in the file system. = Failed to load state for load undo. Error in the file system. Floating symbols = Floating symbols diff --git a/ext/gason/gason.h b/ext/gason/gason.h index 922751099f..cddb3615a7 100644 --- a/ext/gason/gason.h +++ b/ext/gason/gason.h @@ -114,10 +114,10 @@ public: JsonAllocator() : head(nullptr) {}; JsonAllocator(const JsonAllocator &) = delete; JsonAllocator &operator=(const JsonAllocator &) = delete; - JsonAllocator(JsonAllocator &&x) : head(x.head) { + JsonAllocator(JsonAllocator &&x) noexcept : head(x.head) { x.head = nullptr; } - JsonAllocator &operator=(JsonAllocator &&x) { + JsonAllocator &operator=(JsonAllocator &&x) noexcept { head = x.head; x.head = nullptr; return *this;