From 80ca822bf2c3bb4eb98c510390e4e14b71073da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 7 Sep 2022 12:37:45 +0200 Subject: [PATCH] Create framebuffer variants on demand --- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 36 ++++++++-------- Common/GPU/Vulkan/VulkanRenderManager.cpp | 51 +++++++++++++---------- Common/GPU/Vulkan/VulkanRenderManager.h | 26 ++++++++---- Common/GPU/Vulkan/thin3d_vulkan.cpp | 6 ++- 4 files changed, 69 insertions(+), 50 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index fc9d34ec8d..618fecaf0d 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -615,13 +615,13 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { break; } case VKRStepType::COPY: - snprintf(buffer, sizeof(buffer), "COPY '%s' %s -> %s (%dx%d, %s)", step.tag, step.copy.src->tag.c_str(), step.copy.dst->tag.c_str(), step.copy.srcRect.extent.width, step.copy.srcRect.extent.height, AspectToString(step.copy.aspectMask)); + snprintf(buffer, sizeof(buffer), "COPY '%s' %s -> %s (%dx%d, %s)", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.copy.srcRect.extent.width, step.copy.srcRect.extent.height, AspectToString(step.copy.aspectMask)); break; case VKRStepType::BLIT: - snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)", step.tag, step.copy.src->tag.c_str(), step.copy.dst->tag.c_str(), step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height, AspectToString(step.blit.aspectMask)); + snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height, AspectToString(step.blit.aspectMask)); break; case VKRStepType::READBACK: - snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src->tag.c_str(), step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask)); + snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src->Tag(), step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask)); break; case VKRStepType::READBACK_IMAGE: snprintf(buffer, sizeof(buffer), "READBACK_IMAGE '%s' (%dx%d)", step.tag, step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height); @@ -780,14 +780,14 @@ const char *ImageLayoutToString(VkImageLayout layout) { void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) { const auto &r = pass.render; - const char *framebuf = r.framebuffer ? r.framebuffer->tag.c_str() : "backbuffer"; + const char *framebuf = r.framebuffer ? r.framebuffer->Tag() : "backbuffer"; int w = r.framebuffer ? r.framebuffer->width : vulkan_->GetBackbufferWidth(); int h = r.framebuffer ? r.framebuffer->height : vulkan_->GetBackbufferHeight(); INFO_LOG(G3D, "RENDER %s Begin(%s, draws: %d, %dx%d, %s, %s, %s)", pass.tag, framebuf, r.numDraws, w, h, RenderPassActionName(r.colorLoad), RenderPassActionName(r.depthLoad), RenderPassActionName(r.stencilLoad)); // TODO: Log these in detail. for (int i = 0; i < (int)pass.preTransitions.size(); i++) { - INFO_LOG(G3D, " PRETRANSITION: %s %s -> %s", pass.preTransitions[i].fb->tag.c_str(), AspectToString(pass.preTransitions[i].aspect), ImageLayoutToString(pass.preTransitions[i].targetLayout)); + INFO_LOG(G3D, " PRETRANSITION: %s %s -> %s", pass.preTransitions[i].fb->Tag(), AspectToString(pass.preTransitions[i].aspect), ImageLayoutToString(pass.preTransitions[i].targetLayout)); } if (verbose) { @@ -1317,8 +1317,14 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step _dbg_assert_(step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED); _dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED); + RPKey key{ + step.render.colorLoad, step.render.depthLoad, step.render.stencilLoad, + step.render.colorStore, step.render.depthStore, step.render.stencilStore, + }; + renderPass = GetRenderPass(key); + VKRFramebuffer *fb = step.render.framebuffer; - framebuf = fb->framebuf[step.render.renderPassType]; + framebuf = fb->Get(renderPass, step.render.renderPassType); w = fb->width; h = fb->height; @@ -1340,12 +1346,6 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step TransitionToOptimal(cmd, fb->color.image, fb->color.layout, fb->depth.image, fb->depth.layout, &recordBarrier_); - RPKey key{ - step.render.colorLoad, step.render.depthLoad, step.render.stencilLoad, - step.render.colorStore, step.render.depthStore, step.render.stencilStore, - }; - renderPass = GetRenderPass(key); - // The transition from the optimal format happens after EndRenderPass, now that we don't // do it as part of the renderpass itself anymore. @@ -1359,18 +1359,18 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step numClearVals = 2; } } else { - framebuf = backbuffer_; - - // Raw, rotated backbuffer size. - w = vulkan_->GetBackbufferWidth(); - h = vulkan_->GetBackbufferHeight(); - RPKey key{ VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE, }; renderPass = GetRenderPass(key); + framebuf = backbuffer_; + + // Raw, rotated backbuffer size. + w = vulkan_->GetBackbufferWidth(); + h = vulkan_->GetBackbufferHeight(); + Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 2; // We don't bother with a depth buffer here. clearVal[1].depthStencil.depth = 0.0f; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 8d5593d539..bbbc7d6f6d 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -129,36 +129,43 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) { return success; } -VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag) : vulkan_(vk) { +VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag) : vulkan_(vk), tag_(tag) { width = _width; height = _height; CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag); CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag); - for (int i = 0; i < RP_TYPE_COUNT; i++) { - VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - VkImageView views[2]{}; + // We create the actual framebuffer objects on demand, because some combinations might not make sense. +} - fbci.renderPass = compatibleRenderPass->pass[i]; - fbci.attachmentCount = 2; - fbci.pAttachments = views; - views[0] = color.imageView; - views[1] = depth.imageView; - fbci.width = width; - fbci.height = height; - fbci.layers = 1; - - VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[i]); - _assert_(res == VK_SUCCESS); - - if (tag && vk->Extensions().EXT_debug_utils) { - vk->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag).c_str()); - vk->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag).c_str()); - vk->SetDebugName(framebuf[i], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag).c_str()); - this->tag = tag; - } +VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType renderPassType) { + if (framebuf[(int)renderPassType]) { + return framebuf[(int)renderPassType]; } + + VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + VkImageView views[2]{}; + + fbci.renderPass = compatibleRenderPass->pass[(int)renderPassType]; + fbci.attachmentCount = 2; + fbci.pAttachments = views; + views[0] = color.imageView; + views[1] = depth.imageView; + fbci.width = width; + fbci.height = height; + fbci.layers = 1; + + VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)renderPassType]); + _assert_(res == VK_SUCCESS); + + if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) { + vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag_.c_str()).c_str()); + vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag_.c_str()).c_str()); + vulkan_->SetDebugName(framebuf[(int)renderPassType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str()); + } + + return framebuf[(int)renderPassType]; } VKRFramebuffer::~VKRFramebuffer() { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 808418c470..191f9d33e7 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -46,14 +46,23 @@ public: VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag); ~VKRFramebuffer(); - VkFramebuffer framebuf[RP_TYPE_COUNT]{}; - VKRImage color{}; - VKRImage depth{}; + VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType); + int width = 0; int height = 0; + VKRImage color{}; + VKRImage depth{}; + const char *Tag() const { + return tag_.c_str(); + } + + // TODO: Hide. VulkanContext *vulkan_; - std::string tag; +private: + VkFramebuffer framebuf[RP_TYPE_COUNT]{}; + + std::string tag_; }; enum class VKRRunType { @@ -124,8 +133,8 @@ struct VKRGraphicsPipelineDesc { VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; // Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish. - Promise *vertexShader; - Promise *fragmentShader; + Promise *vertexShader = nullptr; + Promise *fragmentShader = nullptr; VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; VkVertexInputAttributeDescription attrs[8]{}; @@ -133,11 +142,11 @@ struct VKRGraphicsPipelineDesc { VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; - VkPipelineLayout pipelineLayout; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; // Does not include the render pass type, it's passed in separately since the // desc is persistent. - RPKey rpKey; + RPKey rpKey{}; }; // All the data needed to create a compute pipeline. @@ -239,6 +248,7 @@ 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); diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 388939854c..418050fe20 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -273,6 +273,7 @@ public: } VKRGraphicsPipeline *pipeline = nullptr; + VKRGraphicsPipelineDesc vkrDesc; PipelineFlags flags; int stride[4]{}; @@ -1041,7 +1042,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { VKPipeline *pipeline = new VKPipeline(&renderManager_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags); - VKRGraphicsPipelineDesc gDesc{}; + VKRGraphicsPipelineDesc &gDesc = pipeline->vkrDesc; std::vector stages; stages.resize(desc.shaders.size()); @@ -1079,6 +1080,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { gDesc.blend0 = blend->attachments[0]; gDesc.cbs = blend->info; + gDesc.cbs.pAttachments = &gDesc.blend0; gDesc.dss = depth->info; @@ -1090,7 +1092,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { // We treat the three stencil states as a unit in other places, so let's do that here too. const VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK }; gDesc.ds.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2; - for (int i = 0; i < gDesc.ds.dynamicStateCount; i++) { + for (size_t i = 0; i < gDesc.ds.dynamicStateCount; i++) { gDesc.dynamicStates[i] = dynamics[i]; } gDesc.ds.pDynamicStates = gDesc.dynamicStates;