diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 59ee054845..4353a32981 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -32,11 +32,19 @@ enum class VKRRenderCommand : uint8_t { NUM_RENDER_COMMANDS, }; +enum PipelineFlags { + PIPELINE_FLAG_NONE = 0, + PIPELINE_FLAG_USES_LINES = (1 << 2), + PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3), + PIPELINE_FLAG_USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer. +}; + struct VkRenderData { VKRRenderCommand cmd; union { struct { VkPipeline pipeline; + PipelineFlags flags; } pipeline; struct { VkPipelineLayout pipelineLayout; @@ -143,6 +151,7 @@ struct VKRStep { int numReads; VkImageLayout finalColorLayout; VkImageLayout finalDepthStencilLayout; + u32 pipelineFlags; } render; struct { VKRFramebuffer *src; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index df224391a3..8dcdcd969d 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -524,6 +524,18 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { return frameData_[curFrame].initCmd; } +void VulkanRenderManager::EndCurRenderStep() { + // Save the accumulated pipeline flags so we can use that to configure the render pass. + // We'll often be able to avoid loading/saving the depth/stencil buffer. + if (curRenderStep_) { + curRenderStep_->render.pipelineFlags = curPipelineFlags_; + + // We no longer have a current render step. + curRenderStep_ = nullptr; + curPipelineFlags_ = 0; + } +} + void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, VKRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) { _dbg_assert_(insideFrame_); // Eliminate dupes, instantly convert to a clear if possible. @@ -568,15 +580,19 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR } // More redundant bind elimination. - if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color != VKRRenderPassAction::CLEAR && curRenderStep_->render.depth != VKRRenderPassAction::CLEAR && curRenderStep_->render.stencil != VKRRenderPassAction::CLEAR) { - // Can trivially kill the last empty render step. - _dbg_assert_(steps_.back() == curRenderStep_); - delete steps_.back(); - steps_.pop_back(); - curRenderStep_ = nullptr; - } - if (curRenderStep_ && curRenderStep_->commands.size() == 0) { - VLOG("Empty render step. Usually happens after uploading pixels.."); + if (curRenderStep_) { + if (curRenderStep_->commands.empty()) { + if (curRenderStep_->render.color != VKRRenderPassAction::CLEAR && curRenderStep_->render.depth != VKRRenderPassAction::CLEAR && curRenderStep_->render.stencil != VKRRenderPassAction::CLEAR) { + // Can trivially kill the last empty render step. + _dbg_assert_(steps_.back() == curRenderStep_); + delete steps_.back(); + steps_.pop_back(); + curRenderStep_ = nullptr; + } + VLOG("Empty render step. Usually happens after uploading pixels.."); + } + + EndCurRenderStep(); } // Older Mali drivers have issues with depth and stencil don't match load/clear/etc. @@ -649,6 +665,8 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma } } + EndCurRenderStep(); + VKRStep *step = new VKRStep{ VKRStepType::READBACK }; step->readback.aspectMask = aspectBits; step->readback.src = src; @@ -658,8 +676,6 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma step->tag = tag; steps_.push_back(step); - curRenderStep_ = nullptr; - FlushSync(); Draw::DataFormat srcFormat = Draw::DataFormat::UNDEFINED; @@ -704,6 +720,9 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) { _dbg_assert_(insideFrame_); + + EndCurRenderStep(); + VKRStep *step = new VKRStep{ VKRStepType::READBACK_IMAGE }; step->readback_image.image = image; step->readback_image.srcRect.offset = { x, y }; @@ -712,8 +731,6 @@ void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int step->tag = tag; steps_.push_back(step); - curRenderStep_ = nullptr; - FlushSync(); // Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM. @@ -975,6 +992,8 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, } } + EndCurRenderStep(); + VKRStep *step = new VKRStep{ VKRStepType::COPY }; step->copy.aspectMask = aspectMask; @@ -990,7 +1009,6 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); - curRenderStep_ = nullptr; } void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag) { @@ -1019,6 +1037,8 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, } } + EndCurRenderStep(); + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; step->blit.aspectMask = aspectMask; @@ -1035,7 +1055,6 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); - curRenderStep_ = nullptr; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int attachment) { @@ -1078,7 +1097,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Finish() { - curRenderStep_ = nullptr; + EndCurRenderStep(); // Let's do just a bit of cleanup on render commands now. for (auto &step : steps_) { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index d1a781c7b3..1aa2dc515b 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -104,11 +104,12 @@ public: void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag); void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag); - void BindPipeline(VkPipeline pipeline) { + void BindPipeline(VkPipeline pipeline, PipelineFlags flags) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != VK_NULL_HANDLE); VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; data.pipeline.pipeline = pipeline; + curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } @@ -265,6 +266,8 @@ public: private: bool InitBackbufferFramebuffers(int width, int height); bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. + void EndCurRenderStep(); + void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); void Submit(int frame, bool triggerFence); @@ -328,6 +331,8 @@ private: VKRStep *curRenderStep_ = nullptr; bool curStepHasViewport_ = false; bool curStepHasScissor_ = false; + u32 curPipelineFlags_ = 0; + std::vector steps_; bool splitSubmit_ = false; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 6fab2a9841..a27ebec428 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -245,7 +245,7 @@ public: class VKPipeline : public Pipeline { public: - VKPipeline(VulkanContext *vulkan, size_t size) : vulkan_(vulkan) { + VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : vulkan_(vulkan), flags(_flags) { uboSize_ = (int)size; ubo_ = new uint8_t[uboSize_]; } @@ -274,6 +274,8 @@ public: VkPipeline backbufferPipeline = VK_NULL_HANDLE; VkPipeline framebufferPipeline = VK_NULL_HANDLE; + + PipelineFlags flags; int stride[4]{}; int dynamicUniformSize = 0; @@ -1014,11 +1016,18 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { } Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { - VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float)); VKInputLayout *input = (VKInputLayout *)desc.inputLayout; VKBlendState *blend = (VKBlendState *)desc.blend; VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil; VKRasterState *raster = (VKRasterState *)desc.raster; + + u32 pipelineFlags = 0; + if (depth->info.depthTestEnable || depth->info.stencilTestEnable) { + pipelineFlags |= PIPELINE_FLAG_USES_DEPTH_STENCIL; + } + + VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags); + for (int i = 0; i < (int)input->bindings.size(); i++) { pipeline->stride[i] = input->bindings[i].stride; } @@ -1336,9 +1345,9 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { void VKContext::BindCompatiblePipeline() { VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass(); if (renderPass == renderManager_.GetBackbufferRenderPass()) { - renderManager_.BindPipeline(curPipeline_->backbufferPipeline); + renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags); } else { - renderManager_.BindPipeline(curPipeline_->framebufferPipeline); + renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags); } } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0033521449..25ed57ffbe 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -839,7 +839,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline); + renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags); if (pipeline != lastPipeline_) { if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -964,7 +964,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline); + renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) { gstate_c.Dirty(DIRTY_BLEND_STATE); diff --git a/GPU/Vulkan/FramebufferManagerVulkan.cpp b/GPU/Vulkan/FramebufferManagerVulkan.cpp index 399aa3cd87..31ba520907 100644 --- a/GPU/Vulkan/FramebufferManagerVulkan.cpp +++ b/GPU/Vulkan/FramebufferManagerVulkan.cpp @@ -225,7 +225,7 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer); - renderManager->BindPipeline(cur2DPipeline_); + renderManager->BindPipeline(cur2DPipeline_, (PipelineFlags)0); renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 2aa0546a8e..8533e10b7d 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -325,6 +325,9 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip vulkanPipeline->flags |= PIPELINE_FLAG_USES_BLEND_CONSTANT; if (key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) vulkanPipeline->flags |= PIPELINE_FLAG_USES_LINES; + if (dss.depthTestEnable || dss.stencilTestEnable) { + vulkanPipeline->flags |= PIPELINE_FLAG_USES_DEPTH_STENCIL; + } return vulkanPipeline; } @@ -550,7 +553,7 @@ void PipelineManagerVulkan::SetLineWidth(float lineWidth) { // Wipe all line-drawing pipelines. pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) { - if (value->UsesLines()) { + if (value->flags & PIPELINE_FLAG_USES_LINES) { if (value->pipeline) vulkan_->Delete().QueueDeletePipeline(value->pipeline); delete value; @@ -619,7 +622,6 @@ 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; diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index d7d47fd0ef..bb747bd29b 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -25,6 +25,8 @@ #include "GPU/Vulkan/VulkanUtil.h" #include "GPU/Vulkan/StateMappingVulkan.h" +#include "GPU/Vulkan/VulkanQueueRunner.h" + // PSP vertex format. enum class PspAttributeLocation { POSITION = 0, @@ -56,19 +58,14 @@ struct VulkanPipelineKey { std::string GetDescription(DebugShaderStringType stringType) const; }; -enum PipelineFlags { - PIPELINE_FLAG_USES_LINES = (1 << 2), - PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3), -}; - // Simply wraps a Vulkan pipeline, providing some metadata. struct VulkanPipeline { VkPipeline pipeline; int flags; // PipelineFlags enum above. - // Convenience. bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; } bool UsesLines() const { return (flags & PIPELINE_FLAG_USES_LINES) != 0; } + bool UsesDepthStencil() const { return (flags & PIPELINE_FLAG_USES_DEPTH_STENCIL) != 0; } }; class VulkanContext; diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index faaba54836..95baef8a59 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -180,7 +180,7 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, StencilUp } VkPipeline pipeline = vulkan2D_->GetPipeline(rp, stencilVs_, stencilFs_, false, Vulkan2D::VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS); - renderManager->BindPipeline(pipeline); + renderManager->BindPipeline(pipeline, PIPELINE_FLAG_USES_DEPTH_STENCIL); renderManager->SetViewport({ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }); renderManager->SetScissor({ { 0, 0, },{ (uint32_t)w, (uint32_t)h } }); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index db328b4349..7acd871dd2 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -635,7 +635,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(fbo, samplerNearest_, clutTexture->GetImageView(), samplerNearest_); VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - renderManager->BindPipeline(depalShader->pipeline); + renderManager->BindPipeline(depalShader->pipeline, (PipelineFlags)0); if (depth) { DepthScaleFactors scaleFactors = GetDepthScaleFactors();