From e3679df6cfe44c470175c6eba8d77739bfc6c774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Nov 2022 18:20:30 +0100 Subject: [PATCH] Fix switching between MSAA levels --- Common/GPU/Vulkan/VulkanFramebuffer.cpp | 12 +++++++---- Common/GPU/Vulkan/VulkanFramebuffer.h | 3 ++- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 13 ++++++++++-- Common/GPU/Vulkan/VulkanRenderManager.cpp | 26 +++++++++++++++++------ Common/GPU/Vulkan/VulkanRenderManager.h | 9 +++++++- Common/GPU/Vulkan/thin3d_vulkan.cpp | 4 ++-- Common/GPU/thin3d.h | 2 +- GPU/Common/FramebufferManagerCommon.cpp | 11 +++++----- GPU/Common/FramebufferManagerCommon.h | 1 + GPU/Common/PresentationCommon.cpp | 4 ++-- GPU/Common/TextureCacheCommon.cpp | 2 +- 11 files changed, 62 insertions(+), 25 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.cpp b/Common/GPU/Vulkan/VulkanFramebuffer.cpp index c3bdcb4ae5..a7a93e208d 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.cpp +++ b/Common/GPU/Vulkan/VulkanFramebuffer.cpp @@ -35,7 +35,7 @@ void VKRImage::Delete(VulkanContext *vulkan) { } } -VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _numSamples, bool createDepthStencilBuffer, const char *tag) +VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag) : vulkan_(vk), tag_(tag), width(_width), height(_height), numLayers(_numLayers) { _dbg_assert_(tag); @@ -45,8 +45,8 @@ VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRe CreateImage(vulkan_, initCmd, depth, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag); } - if (_numSamples > 1) { - sampleCount = MultiSampleLevelToFlagBits(_numSamples); + if (_multiSampleLevel > 0) { + sampleCount = MultiSampleLevelToFlagBits(_multiSampleLevel); // TODO: Create a different tag for these? CreateImage(vulkan_, initCmd, msaaColor, width, height, numLayers, sampleCount, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag); @@ -425,8 +425,12 @@ VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType, Vk _dbg_assert_(!((rpType & RenderPassType::MULTISAMPLE) && sampleCount == VK_SAMPLE_COUNT_1_BIT)); - if (!pass[(int)rpType]) { + if (!pass[(int)rpType] || sampleCounts[(int)rpType] != sampleCount) { + if (pass[(int)rpType]) { + vulkan->Delete().QueueDeleteRenderPass(pass[(int)rpType]); + } pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType, sampleCount); + sampleCounts[(int)rpType] = sampleCount; } return pass[(int)rpType]; } diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.h b/Common/GPU/Vulkan/VulkanFramebuffer.h index 8ee9e84235..fc584f4b8c 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.h +++ b/Common/GPU/Vulkan/VulkanFramebuffer.h @@ -58,7 +58,7 @@ struct VKRImage { class VKRFramebuffer { public: - VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _numSamples, bool createDepthStencilBuffer, const char *tag); + VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag); ~VKRFramebuffer(); VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType); @@ -159,5 +159,6 @@ public: private: // TODO: Might be better off with a hashmap once the render pass type count grows really large.. VkRenderPass pass[(size_t)RenderPassType::TYPE_COUNT]{}; + VkSampleCountFlagBits sampleCounts[(size_t)RenderPassType::TYPE_COUNT]; RPKey key_; }; diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index ee5a7ec4ed..7f7585c28e 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1324,6 +1324,15 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c { VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline; if (graphicsPipeline != lastGraphicsPipeline) { + VkSampleCountFlagBits fbSampleCount = step.render.framebuffer ? step.render.framebuffer->sampleCount : VK_SAMPLE_COUNT_1_BIT; + + if (RenderPassTypeHasMultisample(rpType) && fbSampleCount != graphicsPipeline->SampleCount()) { + // Sample count might have started mismatching if the user changed multisampling mode. Let's get rid of all variants + // and start over. + // (We could avoid this if sample count was baked into the rpType, but I don't want that to grow too big). + graphicsPipeline->DestroyAllVariants(vulkan_); + } + if (!graphicsPipeline->pipeline[(size_t)rpType]) { // NOTE: If render steps got merged, it can happen that, as they ended during recording, // they didn't know their final render pass type so they created the wrong pipelines in EndCurRenderStep(). @@ -1331,11 +1340,11 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c // Maybe a middle pass. But let's try to just block and compile here for now, this doesn't // happen all that much. graphicsPipeline->pipeline[(size_t)rpType] = Promise::CreateEmpty(); - VkSampleCountFlagBits sampleCount = step.render.framebuffer ? step.render.framebuffer->sampleCount : VK_SAMPLE_COUNT_1_BIT; - graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType, sampleCount), rpType, sampleCount); + graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType, fbSampleCount), rpType, fbSampleCount); } VkPipeline pipeline = graphicsPipeline->pipeline[(size_t)rpType]->BlockUntilReady(); + if (pipeline != VK_NULL_HANDLE) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); pipelineLayout = c.pipeline.pipelineLayout; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 4a8c8c7227..0fed2b71a8 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -28,6 +28,15 @@ using namespace PPSSPP_VK; // renderPass is an example of the "compatibility class" or RenderPassType type. bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount) { + bool multisample = RenderPassTypeHasMultisample(rpType); + if (multisample) { + if (sampleCount_ != VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM) { + _assert_(sampleCount == sampleCount_); + } else { + sampleCount_ = sampleCount; + } + } + // 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(); @@ -70,7 +79,7 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR pipe.pRasterizationState = &desc->rs; VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - ms.rasterizationSamples = sampleCount; + ms.rasterizationSamples = multisample ? sampleCount : VK_SAMPLE_COUNT_1_BIT; // We will use dynamic viewport state. pipe.pVertexInputState = &desc->vis; @@ -107,8 +116,8 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR success = false; } else { // Success! - if (!tag.empty()) { - vulkan->SetDebugName(vkpipeline, VK_OBJECT_TYPE_PIPELINE, tag.c_str()); + if (!tag_.empty()) { + vulkan->SetDebugName(vkpipeline, VK_OBJECT_TYPE_PIPELINE, tag_.c_str()); } pipeline[(size_t)rpType]->Post(vkpipeline); } @@ -116,7 +125,7 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR return success; } -void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) { +void VKRGraphicsPipeline::DestroyAllVariants(VulkanContext *vulkan) { for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) { if (!this->pipeline[i]) continue; @@ -125,7 +134,13 @@ void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) { if (pipeline) { vulkan->Delete().QueueDeletePipeline(pipeline); } + this->pipeline[i] = nullptr; } + sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM; +} + +void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) { + DestroyAllVariants(vulkan); vulkan->Delete().QueueCallback([](void *p) { VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p; delete pipeline; @@ -485,11 +500,10 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { } VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, const char *tag) { - VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(); + VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(tag); _dbg_assert_(desc->vertexShader); _dbg_assert_(desc->fragmentShader); pipeline->desc = desc; - pipeline->tag = tag; if (curRenderStep_) { // The common case pipelinesToCheck_.push_back(pipeline); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index a2c9abf9b4..218e87524c 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -115,6 +115,7 @@ struct VKRComputePipelineDesc { // Wrapped pipeline. Doesn't own desc. struct VKRGraphicsPipeline { + VKRGraphicsPipeline(const char *tag) : tag_(tag) {} ~VKRGraphicsPipeline() { for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) { delete pipeline[i]; @@ -123,6 +124,8 @@ struct VKRGraphicsPipeline { bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount); + void DestroyAllVariants(VulkanContext *vulkan); + // This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this. void QueueForDeletion(VulkanContext *vulkan); @@ -132,7 +135,11 @@ struct VKRGraphicsPipeline { VKRGraphicsPipelineDesc *desc = nullptr; // not owned! Promise *pipeline[(size_t)RenderPassType::TYPE_COUNT]{}; - std::string tag; + + VkSampleCountFlagBits SampleCount() const { return sampleCount_; } +private: + std::string tag_; + VkSampleCountFlagBits sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM; }; struct VKRComputePipeline { diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index b7a0bb2ed1..a215ff4528 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1590,13 +1590,13 @@ private: }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { - _assert_(desc.numSamples > 0); + _assert_(desc.multiSampleLevel >= 0); _assert_(desc.numLayers > 0); _assert_(desc.width > 0); _assert_(desc.height > 0); VkCommandBuffer cmd = renderManager_.GetInitCmd(); - VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.numSamples, desc.z_stencil, desc.tag); + VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.multiSampleLevel, desc.z_stencil, desc.tag); return new VKFramebuffer(vkrfb); } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index cfe4af4b34..da3cc0a35f 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -300,7 +300,7 @@ struct FramebufferDesc { int height; int depth; int numLayers; - int numSamples; + int multiSampleLevel; // 0 = 1xaa, 1 = 2xaa, and so on. bool z_stencil; const char *tag; // For graphics debuggers }; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 4fc69de515..724d123cb7 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -89,7 +89,7 @@ void FramebufferManagerCommon::Init() { } bool FramebufferManagerCommon::UpdateRenderSize() { - const bool newRender = renderWidth_ != (float)PSP_CoreParameter().renderWidth || renderHeight_ != (float)PSP_CoreParameter().renderHeight; + const bool newRender = renderWidth_ != (float)PSP_CoreParameter().renderWidth || renderHeight_ != (float)PSP_CoreParameter().renderHeight || msaaLevel_ != g_Config.iMultiSampleLevel; const int effectiveBloomHack = PSP_CoreParameter().compat.flags().ForceLowerResolutionForEffectsOn ? 3 : g_Config.iBloomHack; @@ -99,6 +99,7 @@ bool FramebufferManagerCommon::UpdateRenderSize() { renderWidth_ = (float)PSP_CoreParameter().renderWidth; renderHeight_ = (float)PSP_CoreParameter().renderHeight; renderScaleFactor_ = (float)PSP_CoreParameter().renderScaleFactor; + msaaLevel_ = g_Config.iMultiSampleLevel; bloomHack_ = effectiveBloomHack; useBufferedRendering_ = newBuffered; @@ -2022,7 +2023,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd char name[64]; snprintf(name, sizeof(name), "%08x_color_RAM", vfb->fb_address); textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED); - vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 1, true, name }); + vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 0, true, name }); vfbs_.push_back(vfb); u32 byteSize = ColorBufferByteSize(vfb); @@ -2075,7 +2076,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram snprintf(name, sizeof(name), "download_temp"); // TODO: We don't have a way to create a depth-only framebuffer yet. // Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses. - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, 1, channel == RASTER_DEPTH ? true : false, name }); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, 0, channel == RASTER_DEPTH ? true : false, name }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight); delete nvfb; @@ -2469,7 +2470,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u char name[128]; snprintf(name, sizeof(name), "tempfbo_%s_%dx%d", TempFBOReasonToString(reason), w / renderScaleFactor_, h / renderScaleFactor_); - Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), 1, z_stencil, name }); + Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), 0, z_stencil, name }); if (!fbo) { return nullptr; } @@ -3159,7 +3160,7 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi char tag[128]; FormatFramebufferName(vfb, tag, sizeof(tag)); - vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 1, true, tag }); + vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 0, true, tag }); vfbs_.push_back(vfb); } diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 676c024f7c..0a22fdb366 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -545,6 +545,7 @@ protected: float renderWidth_ = 0.0f; float renderHeight_ = 0.0f; + int msaaLevel_ = 0; int renderScaleFactor_ = 1; int pixelWidth_ = 0; int pixelHeight_ = 0; diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index b94ba7d202..e07eec1499 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -270,7 +270,7 @@ bool PresentationCommon::UpdatePostShader() { previousIndex_ = 0; for (int i = 0; i < FRAMES; ++i) { - previousFramebuffers_[i] = draw_->CreateFramebuffer({ w, h, 1, 1, 1, false, "inter_presentation" }); + previousFramebuffers_[i] = draw_->CreateFramebuffer({ w, h, 1, 1, 0, false, "inter_presentation" }); if (!previousFramebuffers_[i]) { DestroyPostShader(); return false; @@ -386,7 +386,7 @@ bool PresentationCommon::AllocateFramebuffer(int w, int h) { } // No depth/stencil for post processing - Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, 1, false, "presentation" }); + Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, 0, false, "presentation" }); if (!fbo) { return false; } diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 308bcbd490..cd6899cacb 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1299,7 +1299,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { desc.depth = 1; desc.z_stencil = false; desc.numLayers = 1; - desc.numSamples = 1; + desc.multiSampleLevel = 0; desc.tag = "dynamic_clut"; dynamicClutFbo_ = draw_->CreateFramebuffer(desc); desc.tag = "dynamic_clut_temp";