diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index b12daff7b3..2fed762c0d 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -57,6 +57,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock); CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks); CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); + CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 78cc86db12..6f60504a40 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -57,6 +57,7 @@ struct CompatFlags { bool RequireDefaultCPUClock; bool DisableReadbacks; bool DisableAccurateDepth; + bool MGS2AcidHack; }; class IniFile; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 963898d26c..7a5eeb3f0c 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -372,7 +372,7 @@ VkResult DrawEngineVulkan::RecreateDescriptorPool(FrameData &frame, int newSize) VkDescriptorPoolSize dpTypes[3]; dpTypes[0].descriptorCount = frame.descPoolSize * 3; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - dpTypes[1].descriptorCount = frame.descPoolSize * 2; // Don't use these for tess anymore, need max two per set. + dpTypes[1].descriptorCount = frame.descPoolSize * 3; // Don't use these for tess anymore, need max three per set. dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; dpTypes[2].descriptorCount = frame.descPoolSize; dpTypes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index b5d78c9595..e69bf7454a 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -42,6 +42,8 @@ #include "GPU/Vulkan/FramebufferVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/TextureCacheVulkan.h" +#include "thin3d/VulkanRenderManager.h" +#include "thin3d/VulkanQueueRunner.h" #include "Core/MIPS/MIPS.h" #include "Core/HLE/sceKernelThread.h" @@ -456,6 +458,15 @@ void GPU_Vulkan::InitDeviceObjects() { assert(!frameData_[i].push_); frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024); } + + VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + uint32_t hacks = 0; + if (PSP_CoreParameter().compat.flags().MGS2AcidHack) { + hacks |= QUEUE_HACK_MGS2_ACID; + } + if (hacks) { + rm->GetQueueRunner()->EnableHacks(hacks); + } } void GPU_Vulkan::DestroyDeviceObjects() { @@ -467,6 +478,10 @@ void GPU_Vulkan::DestroyDeviceObjects() { frameData_[i].push_ = nullptr; } } + + // Need to turn off hacks when shutting down the GPU. Don't want them running in the menu. + VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + rm->GetQueueRunner()->EnableHacks(0); } void GPU_Vulkan::DeviceLost() { diff --git a/assets/compat.ini b/assets/compat.ini index e977c75b48..8f9d668b05 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -325,4 +325,12 @@ NPHG00092 = true NPEG00044 = true NPJG00120 = true UCJS10114 = true -UCES01401 = true \ No newline at end of file +UCES01401 = true + +[MGS2AcidHack] +ULES00008 = true +ULJM08001 = true +ULJM05001 = true +ULAS42007 = true +ULUS10006 = true +ULUS10077 = true diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 32936bd4be..1e986b4195 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -472,6 +472,7 @@ void GLRenderManager::Run(int frame) { auto &initStepsOnThread = frameData_[frame].initSteps; // queueRunner_.LogSteps(stepsOnThread); queueRunner_.RunInitSteps(initStepsOnThread); + initStepsOnThread.clear(); // Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created. for (auto iter : frameData.activePushBuffers) { @@ -481,7 +482,6 @@ void GLRenderManager::Run(int frame) { queueRunner_.RunSteps(stepsOnThread); stepsOnThread.clear(); - initStepsOnThread.clear(); for (auto iter : frameData.activePushBuffers) { iter->MapDevice(bufferStrategy_); diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 6994c96bff..465159ddc7 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -347,7 +347,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { return pass; } -void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { +void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps) { // Optimizes renderpasses, then sequences them. // Planned optimizations: // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes @@ -397,6 +397,12 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { + // We want to turn a sequence of copy,render(1),copy,render(1),copy,render(1) to copy,copy,copy,render(n). + + for (int i = 0; i < (int)steps.size() - 3; i++) { + int last = -1; + if (!(steps[i]->stepType == VKRStepType::COPY && + steps[i + 1]->stepType == VKRStepType::RENDER && + steps[i + 2]->stepType == VKRStepType::COPY && + steps[i + 1]->render.numDraws == 1 && + steps[i]->copy.dst == steps[i + 2]->copy.dst)) + continue; + // Looks promising! Let's start by finding the last one. + for (int j = i; j < (int)steps.size(); j++) { + switch (steps[j]->stepType) { + case VKRStepType::RENDER: + if (steps[j]->render.numDraws > 1) + last = j - 1; + break; + case VKRStepType::COPY: + if (steps[j]->copy.dst != steps[i]->copy.dst) + last = j - 1; + break; + } + if (last != -1) + break; + } + + if (last != -1) { + // We've got a sequence from i to last that needs reordering. + // First, let's sort it, keeping the same length. + std::vector copies; + std::vector renders; + for (int n = i; n <= last; n++) { + if (steps[n]->stepType == VKRStepType::COPY) + copies.push_back(steps[n]); + else if (steps[n]->stepType == VKRStepType::RENDER) + renders.push_back(steps[n]); + } + // Write the copies back. TODO: Combine them too. + for (int j = 0; j < (int)copies.size(); j++) { + steps[i + j] = copies[j]; + } + // Write the renders back (so they will be deleted properly). + for (int j = 0; j < (int)renders.size(); j++) { + steps[i + j + copies.size()] = renders[j]; + } + assert(steps[i + j + copies.size()]->stepType == VKRStepType::RENDER); + // Combine the renders. + for (int j = 1; j < (int)renders.size(); j++) { + for (int k = 0; k < renders[j]->commands.size(); k++) { + steps[i + copies.size()]->commands.push_back(renders[j]->commands[k]); + } + steps[i + copies.size() + j]->stepType = VKRStepType::RENDER_SKIP; + } + // We're done. + break; + } + } +} + void VulkanQueueRunner::LogSteps(const std::vector &steps) { ILOG("======================================="); for (size_t i = 0; i < steps.size(); i++) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 0dd3ba8597..e7f6df1fa2 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -10,6 +10,10 @@ class VKRFramebuffer; struct VKRImage; +enum { + QUEUE_HACK_MGS2_ACID = 1, +}; + enum class VKRRenderCommand : uint8_t { BIND_PIPELINE, STENCIL, @@ -152,7 +156,9 @@ public: backbuffer_ = fb; backbufferImage_ = img; } - void RunSteps(VkCommandBuffer cmd, const std::vector &steps); + + // RunSteps can modify steps but will leave it in a valid state. + void RunSteps(VkCommandBuffer cmd, std::vector &steps); void LogSteps(const std::vector &steps); void CreateDeviceObjects(); @@ -205,6 +211,10 @@ public: return found; } + void EnableHacks(uint32_t hacks) { + hacksEnabled_ = hacks; + } + private: void InitBackbufferRenderPass(); @@ -223,6 +233,8 @@ private: void ResizeReadbackBuffer(VkDeviceSize requiredSize); + void ApplyMGSHack(std::vector &steps); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); @@ -244,4 +256,7 @@ private: VkDeviceMemory readbackMemory_ = VK_NULL_HANDLE; VkBuffer readbackBuffer_ = VK_NULL_HANDLE; VkDeviceSize readbackBufferSize_ = 0; + + // TODO: Enable based on compat.ini. + uint32_t hacksEnabled_ = 0; };