diff --git a/Common/Data/Collections/Hashmaps.h b/Common/Data/Collections/Hashmaps.h index 4c6664277cd9..8368c83db5dd 100644 --- a/Common/Data/Collections/Hashmaps.h +++ b/Common/Data/Collections/Hashmaps.h @@ -127,6 +127,16 @@ class DenseHashMap { } } + template + inline void IterateMut(T func) { + for (size_t i = 0; i < map.size(); i++) { + if (state[i] == BucketState::TAKEN) { + func(map[i].key, map[i].value); + } + } + } + + // Note! Does NOT delete any pointed-to data (in case you stored pointers in the map). void Clear() { memset(state.data(), (int)BucketState::FREE, state.size()); count_ = 0; diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 0464235eb52a..2bce9ebafffe 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -82,9 +82,9 @@ class D3D11DrawContext : public DrawContext { SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override; RasterState *CreateRasterState(const RasterStateDesc &desc) override; Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; - Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override; + Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override; Texture *CreateTexture(const TextureDesc &desc) override; - ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override; + ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override; Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; @@ -920,7 +920,7 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { return tex; } -ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) { +ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) { if (language != ShaderLanguage::HLSL_D3D11) { ERROR_LOG(G3D, "Unsupported shader language"); return nullptr; @@ -965,7 +965,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang } if (errorMsgs) { errors = std::string((const char *)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize()); - ERROR_LOG(G3D, "Failed compiling:\n%s\n%s", data, errors.c_str()); + ERROR_LOG(G3D, "Failed compiling %s:\n%s\n%s", tag, data, errors.c_str()); errorMsgs->Release(); } @@ -1003,7 +1003,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang return nullptr; } -Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { +Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) { D3D11Pipeline *dPipeline = new D3D11Pipeline(); dPipeline->blend = (D3D11BlendState *)desc.blend; dPipeline->depthStencil = (D3D11DepthStencilState *)desc.depthStencil; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index dc72ed898d50..855496111906 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -489,13 +489,13 @@ class D3D9Context : public DrawContext { } uint32_t GetDataFormatSupport(DataFormat fmt) const override; - ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override; + ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override; DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override; BlendState *CreateBlendState(const BlendStateDesc &desc) override; SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override; RasterState *CreateRasterState(const RasterStateDesc &desc) override; Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; - Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override; + Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override; InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override; Texture *CreateTexture(const TextureDesc &desc) override; @@ -692,7 +692,7 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID D3D9Context::~D3D9Context() { } -ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const std::string &tag) { +ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) { D3D9ShaderModule *shader = new D3D9ShaderModule(stage, tag); if (shader->Compile(device_, data, size)) { return shader; @@ -702,15 +702,15 @@ ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage } } -Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) { +Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) { if (!desc.shaders.size()) { - ERROR_LOG(G3D, "Pipeline requires at least one shader"); + ERROR_LOG(G3D, "Pipeline %s requires at least one shader", tag); return NULL; } D3D9Pipeline *pipeline = new D3D9Pipeline(); for (auto iter : desc.shaders) { if (!iter) { - ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline"); + ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline(%s)", tag); delete pipeline; return NULL; } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 5283b064ec6f..1d2e5822e2e9 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -336,9 +336,9 @@ class OpenGLContext : public DrawContext { BlendState *CreateBlendState(const BlendStateDesc &desc) override; SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override; RasterState *CreateRasterState(const RasterStateDesc &desc) override; - Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override; + Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override; InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override; - ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override; + ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override; Texture *CreateTexture(const TextureDesc &desc) override; Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; @@ -1079,7 +1079,7 @@ void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t off renderManager_.BufferSubdata(buf->buffer_, offset, size, dataCopy); } -Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { +Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) { if (!desc.shaders.size()) { ERROR_LOG(G3D, "Pipeline requires at least one shader"); return nullptr; @@ -1099,7 +1099,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { iter->AddRef(); pipeline->shaders.push_back(static_cast(iter)); } else { - ERROR_LOG(G3D, "ERROR: Tried to create graphics pipeline with a null shader module"); + ERROR_LOG(G3D, "ERROR: Tried to create graphics pipeline %s with a null shader module", tag); delete pipeline; return nullptr; } @@ -1118,7 +1118,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { pipeline->inputLayout = (OpenGLInputLayout *)desc.inputLayout; return pipeline; } else { - ERROR_LOG(G3D, "Failed to create pipeline - shaders failed to link"); + ERROR_LOG(G3D, "Failed to create pipeline %s - shaders failed to link", tag); delete pipeline; return nullptr; } @@ -1163,7 +1163,7 @@ void OpenGLContext::ApplySamplers() { } } -ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) { +ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) { OpenGLShaderModule *shader = new OpenGLShaderModule(&renderManager_, stage, tag); if (shader->Compile(&renderManager_, language, data, dataSize)) { return shader; diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 63351eaf450f..55edc0f31f86 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -1196,12 +1196,15 @@ void VulkanContext::DestroyDevice() { device_ = nullptr; } -bool VulkanContext::CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule) { +bool VulkanContext::CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule, const char *tag) { VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; sm.pCode = spirv.data(); sm.codeSize = spirv.size() * sizeof(uint32_t); sm.flags = 0; VkResult result = vkCreateShaderModule(device_, &sm, nullptr, shaderModule); + if (tag) { + SetDebugName(*shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, tag); + } if (result != VK_SUCCESS) { return false; } else { diff --git a/Common/GPU/Vulkan/VulkanContext.h b/Common/GPU/Vulkan/VulkanContext.h index b5750c77abfd..87a73a59fc2a 100644 --- a/Common/GPU/Vulkan/VulkanContext.h +++ b/Common/GPU/Vulkan/VulkanContext.h @@ -201,7 +201,7 @@ class VulkanContext { // Utility functions for shorter code VkFence CreateFence(bool presignalled); - bool CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule); + bool CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule, const char *tag); int GetBackbufferWidth() { return (int)swapChainExtent_.width; } int GetBackbufferHeight() { return (int)swapChainExtent_.height; } diff --git a/Common/GPU/Vulkan/VulkanMemory.cpp b/Common/GPU/Vulkan/VulkanMemory.cpp index a7a5277efa44..8206c80c912e 100644 --- a/Common/GPU/Vulkan/VulkanMemory.cpp +++ b/Common/GPU/Vulkan/VulkanMemory.cpp @@ -59,6 +59,8 @@ bool VulkanPushBuffer::AddBuffer() { return false; } + vulkan_->SetDebugName(info.buffer, VK_OBJECT_TYPE_BUFFER, name_); + buffers_.push_back(info); buf_ = buffers_.size() - 1; return true; @@ -222,5 +224,9 @@ VkResult VulkanDescSetPool::Recreate(bool grow) { info_.pPoolSizes = &sizes_[0]; info_.poolSizeCount = (uint32_t)sizes_.size(); - return vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_); + VkResult result = vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_); + if (result == VK_SUCCESS) { + vulkan_->SetDebugName(descPool_, VK_OBJECT_TYPE_DESCRIPTOR_POOL, tag_); + } + return result; } diff --git a/Common/GPU/Vulkan/VulkanMemory.h b/Common/GPU/Vulkan/VulkanMemory.h index 82ef924390ae..79a71d4ce6fd 100644 --- a/Common/GPU/Vulkan/VulkanMemory.h +++ b/Common/GPU/Vulkan/VulkanMemory.h @@ -143,8 +143,7 @@ class VulkanPushBuffer { // Only appropriate for use in a per-frame pool. class VulkanDescSetPool { public: - VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) { - } + VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) {} ~VulkanDescSetPool(); // Must call this before use: defines how to clear cache of ANY returned values from Allocate(). diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 9b7ae11ea5b5..10a88e623a80 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -27,13 +27,26 @@ static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) { } } +// We need to take the "max" of the features used in the two render passes. +RenderPassType MergeRPTypes(RenderPassType a, RenderPassType b) { + // Either both are backbuffer type, or neither are. + _dbg_assert_((a == RP_TYPE_BACKBUFFER) == (b == RP_TYPE_BACKBUFFER)); + if (a == b) { + // Trivial merging case. + return a; + } + // More cases to be added later. + return a; +} + void VulkanQueueRunner::CreateDeviceObjects() { INFO_LOG(G3D, "VulkanQueueRunner::CreateDeviceObjects"); - InitBackbufferRenderPass(); - RPKey key{ VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, - VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE }; - framebufferRenderPass_ = GetRenderPass(key); + RPKey key{ + VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, + VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE, + }; + compatibleRenderPass_ = GetRenderPass(key); #if 0 // Just to check whether it makes sense to split some of these. drawidx is way bigger than the others... @@ -117,78 +130,12 @@ void VulkanQueueRunner::DestroyDeviceObjects() { } readbackBufferSize_ = 0; - renderPasses_.Iterate([&](const RPKey &rpkey, VkRenderPass rp) { - _assert_(rp != VK_NULL_HANDLE); - vulkan_->Delete().QueueDeleteRenderPass(rp); + renderPasses_.IterateMut([&](const RPKey &rpkey, VKRRenderPass *rp) { + _assert_(rp); + rp->Destroy(vulkan_); + delete rp; }); renderPasses_.Clear(); - - _assert_(backbufferRenderPass_ != VK_NULL_HANDLE); - vulkan_->Delete().QueueDeleteRenderPass(backbufferRenderPass_); - backbufferRenderPass_ = VK_NULL_HANDLE; -} - -void VulkanQueueRunner::InitBackbufferRenderPass() { - VkAttachmentDescription attachments[2]; - attachments[0].format = vulkan_->GetSwapchainFormat(); - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // We don't want to preserve the backbuffer between frames so we really don't care. - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // We only render once to the backbuffer per frame so we can do this here. - attachments[0].flags = 0; - - attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer. - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Don't care about storing backbuffer Z - we clear it anyway. - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].flags = 0; - - VkAttachmentReference color_reference{}; - color_reference.attachment = 0; - color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depth_reference{}; - depth_reference.attachment = 1; - depth_reference.layout = attachments[1].finalLayout; - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = nullptr; - subpass.pDepthStencilAttachment = &depth_reference; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - // For the built-in layout transitions. - VkSubpassDependency dep{}; - dep.srcSubpass = VK_SUBPASS_EXTERNAL; - dep.dstSubpass = 0; - dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dep.srcAccessMask = 0; - dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp_info.attachmentCount = 2; - rp_info.pAttachments = attachments; - rp_info.subpassCount = 1; - rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 1; - rp_info.pDependencies = &dep; - - VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); - _assert_(res == VK_SUCCESS); } static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) { @@ -208,24 +155,19 @@ static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) { return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning } -VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { - auto pass = renderPasses_.Get(key); - if (pass) { - return pass; - } - +VkRenderPass CreateRP(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType) { VkAttachmentDescription attachments[2] = {}; - attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; + attachments[0].format = rpType == RP_TYPE_BACKBUFFER ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = ConvertLoadAction(key.colorLoadAction); attachments[0].storeOp = ConvertStoreAction(key.colorStoreAction); attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].initialLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[0].flags = 0; - attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + attachments[1].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = ConvertLoadAction(key.depthLoadAction); attachments[1].storeOp = ConvertStoreAction(key.depthStoreAction); @@ -255,15 +197,51 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; + // Not sure if this is really necessary. + VkSubpassDependency dep{}; + dep.srcSubpass = VK_SUBPASS_EXTERNAL; + dep.dstSubpass = 0; + dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.srcAccessMask = 0; + dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; rp.attachmentCount = 2; rp.pAttachments = attachments; rp.subpassCount = 1; rp.pSubpasses = &subpass; + if (rpType == RP_TYPE_BACKBUFFER) { + rp.dependencyCount = 1; + rp.pDependencies = &dep; + } - VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &pass); + VkRenderPass pass; + VkResult res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass); _assert_(res == VK_SUCCESS); _assert_(pass != VK_NULL_HANDLE); + return pass; +} + +VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType) { + // When we create a render pass, we create all "types" of it immediately, + // practical later when referring to it. Could change to on-demand if it feels motivated + // but I think the render pass objects are cheap. + if (!pass[(int)rpType]) { + pass[(int)rpType] = CreateRP(vulkan, key_, (RenderPassType)rpType); + } + return pass[(int)rpType]; +} + +// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827 +// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies +VKRRenderPass *VulkanQueueRunner::GetRenderPass(const RPKey &key) { + auto foundPass = renderPasses_.Get(key); + if (foundPass) { + return foundPass; + } + + VKRRenderPass *pass = new VKRRenderPass(key); renderPasses_.Insert(key, pass); return pass; } @@ -272,7 +250,8 @@ void VulkanQueueRunner::PreprocessSteps(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 - // as early as possible in the frame (Wipeout billboards). + // as early as possible in the frame (Wipeout billboards). This will require taking over more of descriptor management so we can + // substitute descriptors, alternatively using texture array layers creatively. for (int j = 0; j < (int)steps.size(); j++) { if (steps[j]->stepType == VKRStepType::RENDER && @@ -312,7 +291,7 @@ void VulkanQueueRunner::PreprocessSteps(std::vector &steps) { steps[i]->render.clearStencil = steps[j]->render.clearStencil; } MergeRenderAreaRectInto(&steps[i]->render.renderArea, steps[j]->render.renderArea); - + steps[i]->render.renderPassType = MergeRPTypes(steps[i]->render.renderPassType, steps[j]->render.renderPassType); // Cheaply skip the first step. steps[j]->stepType = VKRStepType::RENDER_SKIP; break; @@ -645,17 +624,23 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight(); int actual_w = step.render.renderArea.extent.width; int actual_h = step.render.renderArea.extent.height; - snprintf(buffer, sizeof(buffer), "RENDER %s (draws: %d, %dx%d/%dx%d, fb: %p, )", step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer); + const char *renderCmd; + switch (step.render.renderPassType) { + case RP_TYPE_BACKBUFFER: renderCmd = "BACKBUF"; break; + case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER"; break; + default: renderCmd = "N/A"; + } + snprintf(buffer, sizeof(buffer), "%s %s (draws: %d, %dx%d/%dx%d, fb: %p, )", renderCmd, step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer); 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); @@ -692,6 +677,8 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { // So we don't consider it for other things, maybe doesn't matter. src->dependencies.clear(); src->stepType = VKRStepType::RENDER_SKIP; + dst->render.pipelineFlags |= src->render.pipelineFlags; + dst->render.renderPassType = MergeRPTypes(dst->render.renderPassType, src->render.renderPassType); }; auto renderHasClear = [](const VKRStep *step) { const auto &r = step->render; @@ -814,14 +801,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) { @@ -830,9 +817,6 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) { case VKRRenderCommand::REMOVED: INFO_LOG(G3D, " (Removed)"); break; - case VKRRenderCommand::BIND_PIPELINE: - INFO_LOG(G3D, " BindPipeline(%x)", (int)(intptr_t)cmd.pipeline.pipeline); - break; case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: INFO_LOG(G3D, " BindGraphicsPipeline(%x)", (int)(intptr_t)cmd.graphics_pipeline.pipeline); break; @@ -1137,7 +1121,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c // This reads the layout of the color and depth images, and chooses a render pass using them that // will transition to the desired final layout. // NOTE: Flushes recordBarrier_. - PerformBindFramebufferAsRenderTarget(step, cmd); + VKRRenderPass *renderPass = PerformBindFramebufferAsRenderTarget(step, cmd); int curWidth = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth(); int curHeight = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight(); @@ -1159,29 +1143,28 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c int lastStencilCompareMask = -1; int lastStencilReference = -1; + const RenderPassType rpType = step.render.renderPassType; + for (const auto &c : commands) { switch (c.cmd) { case VKRRenderCommand::REMOVED: break; - // Still here to support binding of non-async pipelines. - case VKRRenderCommand::BIND_PIPELINE: - { - VkPipeline pipeline = c.pipeline.pipeline; - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - pipelineLayout = c.pipeline.pipelineLayout; - // Reset dynamic state so it gets refreshed with the new pipeline. - lastStencilWriteMask = -1; - lastStencilCompareMask = -1; - lastStencilReference = -1; - break; - } - case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: { VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline; if (graphicsPipeline != lastGraphicsPipeline) { - VkPipeline pipeline = graphicsPipeline->pipeline->BlockUntilReady(); + if (!graphicsPipeline->pipeline[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(). + // Unfortunately I don't know if we can fix it in any more sensible place than here. + // 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[rpType] = Promise::CreateEmpty(); + graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType), rpType); + } + + VkPipeline pipeline = graphicsPipeline->pipeline[rpType]->BlockUntilReady(); if (pipeline != VK_NULL_HANDLE) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); pipelineLayout = c.pipeline.pipelineLayout; @@ -1337,8 +1320,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } } -void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { - VkRenderPass renderPass; +VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { + VKRRenderPass *renderPass; int numClearVals = 0; VkClearValue clearVal[2]{}; VkFramebuffer framebuf; @@ -1348,8 +1331,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; + framebuf = fb->Get(renderPass, step.render.renderPassType); w = fb->width; h = fb->height; @@ -1371,12 +1360,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. @@ -1390,12 +1373,18 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step numClearVals = 2; } } else { + 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(); - renderPass = GetBackbufferRenderPass(); + 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; @@ -1403,7 +1392,7 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step } VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = renderPass; + rp_begin.renderPass = renderPass->Get(vulkan_, step.render.renderPassType); rp_begin.framebuffer = framebuf; VkRect2D rc = step.render.renderArea; @@ -1424,6 +1413,8 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step rp_begin.clearValueCount = numClearVals; rp_begin.pClearValues = numClearVals ? clearVal : nullptr; vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + + return renderPass; } void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index ce6dc9f5eb48..adb9e7d96e99 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -26,7 +26,6 @@ enum { enum class VKRRenderCommand : uint8_t { REMOVED, - BIND_PIPELINE, // raw pipeline BIND_GRAPHICS_PIPELINE, // async BIND_COMPUTE_PIPELINE, // async STENCIL, @@ -47,6 +46,14 @@ enum PipelineFlags { PIPELINE_FLAG_USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer. }; +// Pipelines need to be created for the right type of render pass. +enum RenderPassType { + RP_TYPE_BACKBUFFER, + RP_TYPE_COLOR_DEPTH, + // Later will add pure-color render passes. + RP_TYPE_COUNT, +}; + struct VkRenderData { VKRRenderCommand cmd; union { @@ -147,6 +154,8 @@ struct QueueProfileContext { double cpuEndTime; }; +class VKRRenderPass; + struct VKRStep { VKRStep(VKRStepType _type) : stepType(_type) {} ~VKRStep() {} @@ -159,6 +168,7 @@ struct VKRStep { union { struct { VKRFramebuffer *framebuffer; + // TODO: Look these up through renderPass? VKRRenderPassLoadAction colorLoad; VKRRenderPassLoadAction depthLoad; VKRRenderPassLoadAction stencilLoad; @@ -175,6 +185,9 @@ struct VKRStep { VkImageLayout finalDepthStencilLayout; u32 pipelineFlags; VkRect2D renderArea; + // Render pass type. Deduced after finishing recording the pass, from the used pipelines. + // NOTE: Storing the render pass here doesn't do much good, we change the compatible parameters (load/store ops) during step optimization. + RenderPassType renderPassType; } render; struct { VKRFramebuffer *src; @@ -204,9 +217,38 @@ struct VKRStep { }; }; +struct RPKey { + // Only render-pass-compatibility-volatile things can be here. + VKRRenderPassLoadAction colorLoadAction; + VKRRenderPassLoadAction depthLoadAction; + VKRRenderPassLoadAction stencilLoadAction; + VKRRenderPassStoreAction colorStoreAction; + VKRRenderPassStoreAction depthStoreAction; + VKRRenderPassStoreAction stencilStoreAction; +}; + +class VKRRenderPass { +public: + VKRRenderPass(const RPKey &key) : key_(key) {} + + VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType); + void Destroy(VulkanContext *vulkan) { + for (int i = 0; i < RP_TYPE_COUNT; i++) { + if (pass[i]) { + vulkan->Delete().QueueDeleteRenderPass(pass[i]); + } + } + } + +private: + VkRenderPass pass[RP_TYPE_COUNT]{}; + RPKey key_; +}; + class VulkanQueueRunner { public: VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan), renderPasses_(16) {} + void SetBackbuffer(VkFramebuffer fb, VkImage img) { backbuffer_ = fb; backbufferImage_ = img; @@ -221,14 +263,10 @@ class VulkanQueueRunner { void CreateDeviceObjects(); void DestroyDeviceObjects(); - VkRenderPass GetBackbufferRenderPass() const { - return backbufferRenderPass_; - } - // Get a render pass that's compatible with all our framebuffers. // Note that it's precached, cannot look up in the map as this might be on another thread. - VkRenderPass GetFramebufferRenderPass() const { - return framebufferRenderPass_; + VKRRenderPass *GetCompatibleRenderPass() const { + return compatibleRenderPass_; } inline int RPIndex(VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth) { @@ -237,20 +275,11 @@ class VulkanQueueRunner { void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels); - struct RPKey { - VKRRenderPassLoadAction colorLoadAction; - VKRRenderPassLoadAction depthLoadAction; - VKRRenderPassLoadAction stencilLoadAction; - VKRRenderPassStoreAction colorStoreAction; - VKRRenderPassStoreAction depthStoreAction; - VKRRenderPassStoreAction stencilStoreAction; - }; - - VkRenderPass GetRenderPass(const RPKey &key); + VKRRenderPass *GetRenderPass(const RPKey &key); - bool GetRenderPassKey(VkRenderPass passToFind, RPKey *outKey) const { + bool GetRenderPassKey(VKRRenderPass *passToFind, RPKey *outKey) const { bool found = false; - renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, VkRenderPass pass) { + renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, const VKRRenderPass *pass) { if (pass == passToFind) { found = true; *outKey = rpkey; @@ -273,9 +302,7 @@ class VulkanQueueRunner { } private: - void InitBackbufferRenderPass(); - - void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); + VKRRenderPass *PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); @@ -302,14 +329,12 @@ class VulkanQueueRunner { VkFramebuffer backbuffer_ = VK_NULL_HANDLE; VkImage backbufferImage_ = VK_NULL_HANDLE; - VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; - - // The "Compatible" render pass. Used when creating pipelines that render to "normal" framebuffers. - VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE; + // The "Compatible" render pass. Should be able to get rid of this soon. + VKRRenderPass *compatibleRenderPass_ = nullptr; // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. - // TODO: Create these on demand. - DenseHashMap renderPasses_; + // Each VKRRenderPass contains all compatibility classes (which attachments they have, etc). + DenseHashMap renderPasses_; // Readback buffer. Currently we only support synchronous readback, so we only really need one. // We size it generously. diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index a4e14a1b8d8f..52f0b8fb27d3 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -25,12 +25,8 @@ using namespace PPSSPP_VK; -bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { - if (!desc) { - // Already failed to create this one. - return false; - } - +// renderPass is an example of the "compatibility class" or RenderPassType type. +bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType) { // 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(); @@ -55,12 +51,30 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { ss[1].pName = "main"; ss[1].flags = 0; - desc->pipe.pStages = ss; - desc->pipe.stageCount = 2; + VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + pipe.pStages = ss; + pipe.stageCount = 2; + pipe.renderPass = compatibleRenderPass; + pipe.basePipelineIndex = 0; + pipe.pColorBlendState = &desc->cbs; + pipe.pDepthStencilState = &desc->dss; + pipe.pRasterizationState = &desc->rs; + + // We will use dynamic viewport state. + pipe.pVertexInputState = &desc->vis; + pipe.pViewportState = &desc->views; + pipe.pTessellationState = nullptr; + pipe.pDynamicState = &desc->ds; + pipe.pInputAssemblyState = &desc->inputAssembly; + pipe.pMultisampleState = &desc->ms; + pipe.layout = desc->pipelineLayout; + pipe.basePipelineHandle = VK_NULL_HANDLE; + pipe.basePipelineIndex = 0; + pipe.subpass = 0; double start = time_now_d(); VkPipeline vkpipeline; - VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline); + VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &pipe, nullptr, &vkpipeline); INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0); @@ -70,24 +84,46 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { // 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->Post(VK_NULL_HANDLE); + pipeline[rpType]->Post(VK_NULL_HANDLE); success = false; } else if (result != VK_SUCCESS) { - pipeline->Post(VK_NULL_HANDLE); + pipeline[rpType]->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline->Post(vkpipeline); + // Success! + if (!tag.empty()) { + vulkan->SetDebugName(vkpipeline, VK_OBJECT_TYPE_PIPELINE, tag.c_str()); + } + pipeline[rpType]->Post(vkpipeline); } - // Having the desc stick around can be useful for debugging. -#ifndef _DEBUG - delete desc; - desc = nullptr; -#endif return success; } +void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) { + for (int i = 0; i < RP_TYPE_COUNT; i++) { + if (!pipeline[i]) + continue; + VkPipeline pipeline = this->pipeline[i]->BlockUntilReady(); + vulkan->Delete().QueueDeletePipeline(pipeline); + } + vulkan->Delete().QueueCallback([](void *p) { + VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p; + delete pipeline; + }, this); +} + +u32 VKRGraphicsPipeline::GetVariantsBitmask() const { + u32 bitmask = 0; + for (int i = 0; i < RP_TYPE_COUNT; i++) { + if (pipeline[i]) { + bitmask |= 1 << i; + } + } + return bitmask; +} + bool VKRComputePipeline::Create(VulkanContext *vulkan) { if (!desc) { // Already failed to create this one. @@ -110,17 +146,25 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) { return success; } -VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, 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); + // We create the actual framebuffer objects on demand, because some combinations might not make sense. +} + +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 = renderPass; + fbci.renderPass = compatibleRenderPass->Get(vulkan_, renderPassType); fbci.attachmentCount = 2; fbci.pAttachments = views; views[0] = color.imageView; @@ -129,15 +173,16 @@ VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRen fbci.height = height; fbci.layers = 1; - VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf); + VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)renderPassType]); _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, VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag).c_str()); - this->tag = tag; + 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() { @@ -155,8 +200,10 @@ VKRFramebuffer::~VKRFramebuffer() { } if (depth.depthSampleView) vulkan_->Delete().QueueDeleteImageView(depth.depthSampleView); - if (framebuf) - vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + for (auto &fb : framebuf) { + if (fb) + vulkan_->Delete().QueueDeleteFramebuffer(fb); + } } void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) { @@ -493,7 +540,7 @@ void VulkanRenderManager::CompileThreadFunc() { for (auto &entry : toCompile) { switch (entry.type) { case CompileQueueEntry::Type::GRAPHICS: - entry.graphics->Create(vulkan_); + entry.graphics->Create(vulkan_, entry.compatibleRenderPass, entry.renderPassType); break; case CompileQueueEntry::Type::COMPUTE: entry.compute->Create(vulkan_); @@ -576,7 +623,9 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile VLOG("PUSH: Fencing %d", curFrame); - vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); + if (vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX) == VK_ERROR_DEVICE_LOST) { + _assert_msg_(false, "Device lost in vkWaitForFences"); + } vkResetFences(device, 1, &frameData.fence); // Can't set this until after the fence. @@ -663,24 +712,91 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { return frameData_[curFrame].initCmd; } +VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask, const char *tag) { + VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(); + _dbg_assert_(desc->vertexShader); + _dbg_assert_(desc->fragmentShader); + pipeline->desc = desc; + pipeline->tag = tag; + if (curRenderStep_) { + // The common case + pipelinesToCheck_.push_back(pipeline); + } else { + if (!variantBitmask) { + WARN_LOG(G3D, "WARNING: Will not compile any variants of pipeline, not in renderpass and empty variantBitmask"); + } + // Presumably we're in initialization, loading the shader cache. + // Look at variantBitmask to see what variants we should queue up. + RPKey key{ + VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, + VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE, + }; + VKRRenderPass *compatibleRenderPass = queueRunner_.GetRenderPass(key); + compileMutex_.lock(); + for (int i = 0; i < RP_TYPE_COUNT; i++) { + if (!(variantBitmask & (1 << i))) + continue; + RenderPassType rpType = (RenderPassType)i; + pipeline->pipeline[rpType] = Promise::CreateEmpty(); + compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType)); + } + compileMutex_.unlock(); + } + return pipeline; +} + +VKRComputePipeline *VulkanRenderManager::CreateComputePipeline(VKRComputePipelineDesc *desc) { + VKRComputePipeline *pipeline = new VKRComputePipeline(); + pipeline->desc = desc; + compileMutex_.lock(); + compileQueue_.push_back(CompileQueueEntry(pipeline)); + compileCond_.notify_one(); + compileMutex_.unlock(); + return pipeline; +} + void VulkanRenderManager::EndCurRenderStep() { + if (!curRenderStep_) + return; + + RPKey key{ + curRenderStep_->render.colorLoad, curRenderStep_->render.depthLoad, curRenderStep_->render.stencilLoad, + curRenderStep_->render.colorStore, curRenderStep_->render.depthStore, curRenderStep_->render.stencilStore, + }; + RenderPassType rpType = RP_TYPE_COLOR_DEPTH; + curRenderStep_->render.pipelineFlags = curPipelineFlags_; + if (!curRenderStep_->render.framebuffer) { + rpType = RP_TYPE_BACKBUFFER; + } + + VKRRenderPass *renderPass = queueRunner_.GetRenderPass(key); + curRenderStep_->render.renderPassType = rpType; + // 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 don't do this optimization for very small targets, probably not worth it. - if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) { - curRenderStep_->render.renderArea = curRenderArea_.ToVkRect2D(); - } else { - curRenderStep_->render.renderArea.offset = {}; - curRenderStep_->render.renderArea.extent = { (uint32_t)curWidth_, (uint32_t)curHeight_ }; + compileMutex_.lock(); + for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) { + if (!pipeline->pipeline[rpType]) { + pipeline->pipeline[rpType] = Promise::CreateEmpty(); + compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType)); } - curRenderArea_.Reset(); + } + compileCond_.notify_one(); + compileMutex_.unlock(); + pipelinesToCheck_.clear(); - // We no longer have a current render step. - curRenderStep_ = nullptr; - curPipelineFlags_ = 0; + // We don't do this optimization for very small targets, probably not worth it. + if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) { + curRenderStep_->render.renderArea = curRenderArea_.ToVkRect2D(); + } else { + curRenderStep_->render.renderArea.offset = {}; + curRenderStep_->render.renderArea.extent = { (uint32_t)curWidth_, (uint32_t)curHeight_ }; } + curRenderArea_.Reset(); + + // We no longer have a current render step. + curRenderStep_ = nullptr; + curPipelineFlags_ = 0; } void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) { @@ -907,9 +1023,8 @@ bool VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { // We share the same depth buffer but have multiple color buffers, see the loop below. VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; - VLOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb_info.renderPass = queueRunner_.GetBackbufferRenderPass(); + fb_info.renderPass = queueRunner_.GetCompatibleRenderPass()->Get(vulkan_, RP_TYPE_BACKBUFFER); fb_info.attachmentCount = 2; fb_info.pAttachments = attachments; fb_info.width = width; @@ -1017,7 +1132,6 @@ static void CleanupRenderCommands(std::vector *cmds) { case VKRRenderCommand::REMOVED: continue; - case VKRRenderCommand::BIND_PIPELINE: case VKRRenderCommand::VIEWPORT: case VKRRenderCommand::SCISSOR: case VKRRenderCommand::BLEND: diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 67dff36880f6..4cc7aafdfccc 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -43,17 +43,26 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int class VKRFramebuffer { public: - VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height, const char *tag); + VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag); ~VKRFramebuffer(); - VkFramebuffer framebuf = VK_NULL_HANDLE; - 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,15 +133,20 @@ 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]{}; VkVertexInputBindingDescription ibd{}; VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; - VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + + // Does not include the render pass type, it's passed in separately since the + // desc is persistent. + RPKey rpKey{}; }; // All the data needed to create a compute pipeline. @@ -141,19 +155,18 @@ struct VKRComputePipelineDesc { VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; }; -// Wrapped pipeline. +// Wrapped pipeline. Doesn't own desc. struct VKRGraphicsPipeline { - VKRGraphicsPipeline() { - pipeline = Promise::CreateEmpty(); - } - ~VKRGraphicsPipeline() { - delete desc; - } + bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType); - VKRGraphicsPipelineDesc *desc = nullptr; - Promise *pipeline; + // This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this. + void QueueForDeletion(VulkanContext *vulkan); - bool Create(VulkanContext *vulkan); + u32 GetVariantsBitmask() const; + + VKRGraphicsPipelineDesc *desc = nullptr; // not owned! + Promise *pipeline[RP_TYPE_COUNT]{}; + std::string tag; }; struct VKRComputePipeline { @@ -170,13 +183,16 @@ struct VKRComputePipeline { }; struct CompileQueueEntry { - CompileQueueEntry(VKRGraphicsPipeline *p) : type(Type::GRAPHICS), graphics(p) {} - CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p) {} + CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType) + : type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType) {} + CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p), renderPassType(RP_TYPE_COLOR_DEPTH) {} enum class Type { GRAPHICS, COMPUTE, }; Type type; + VkRenderPass compatibleRenderPass; + RenderPassType renderPassType; VKRGraphicsPipeline *graphics = nullptr; VKRComputePipeline *compute = nullptr; }; @@ -228,40 +244,23 @@ class VulkanRenderManager { // 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. - VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) { - VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(); - pipeline->desc = desc; - compileMutex_.lock(); - compileQueue_.push_back(CompileQueueEntry(pipeline)); - compileCond_.notify_one(); - compileMutex_.unlock(); - return pipeline; - } + // We delay creating pipelines until the end of the current render pass, so we can create the right type immediately. + // Unless a variantBitmask is passed in, in which case we can just go ahead. + // WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it. + VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask, const char *tag); + VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc); - VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc) { - VKRComputePipeline *pipeline = new VKRComputePipeline(); - pipeline->desc = desc; + void NudgeCompilerThread() { compileMutex_.lock(); - compileQueue_.push_back(CompileQueueEntry(pipeline)); compileCond_.notify_one(); compileMutex_.unlock(); - return pipeline; - } - - void BindPipeline(VkPipeline pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { - _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); - _dbg_assert_(pipeline != VK_NULL_HANDLE); - VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; - data.pipeline.pipeline = pipeline; - data.pipeline.pipelineLayout = pipelineLayout; - curPipelineFlags_ |= flags; - curRenderStep_->commands.push_back(data); } void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; + pipelinesToCheck_.push_back(pipeline); data.graphics_pipeline.pipeline = pipeline; data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; @@ -431,20 +430,6 @@ class VulkanRenderManager { VkCommandBuffer GetInitCmd(); - VkRenderPass GetBackbufferRenderPass() { - return queueRunner_.GetBackbufferRenderPass(); - } - VkRenderPass GetFramebufferRenderPass() { - return queueRunner_.GetFramebufferRenderPass(); - } - VkRenderPass GetCompatibleRenderPass() { - if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) { - return queueRunner_.GetFramebufferRenderPass(); - } else { - return queueRunner_.GetBackbufferRenderPass(); - } - } - // Gets a frame-unique ID of the current step being recorded. Can be used to figure out // when the current step has changed, which means the caller will need to re-record its state. int GetCurrentStepId() const { @@ -581,6 +566,9 @@ class VulkanRenderManager { std::mutex compileMutex_; std::vector compileQueue_; + // pipelines to check and possibly create at the end of the current render pass. + std::vector pipelinesToCheck_; + // Swap chain management struct SwapchainImageData { VkImage image; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 8a1112d4465c..bb206fd1cb62 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -31,6 +31,7 @@ #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanImage.h" #include "Common/GPU/Vulkan/VulkanMemory.h" +#include "Common/Thread/Promise.h" #include "Core/Config.h" @@ -192,6 +193,7 @@ class VKShaderModule : public ShaderModule { const std::string &GetSource() const { return source_; } ~VKShaderModule() { if (module_) { + DEBUG_LOG(G3D, "Queueing %s (shmodule %p) for release", tag_.c_str(), module_); vulkan_->Delete().QueueDeleteShaderModule(module_); } } @@ -217,7 +219,7 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con std::vector spirv; std::string errorMessage; if (!GLSLtoSPV(vkstage_, source_.c_str(), GLSLVariant::VULKAN, spirv, &errorMessage)) { - WARN_LOG(G3D, "Shader compile to module failed: %s", errorMessage.c_str()); + WARN_LOG(G3D, "Shader compile to module failed (%s): %s", tag_.c_str(), errorMessage.c_str()); return false; } @@ -230,10 +232,10 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con } #endif - if (vulkan->CreateShaderModule(spirv, &module_)) { + if (vulkan->CreateShaderModule(spirv, &module_, vkstage_ == VK_SHADER_STAGE_VERTEX_BIT ? "thin3d_vs" : "thin3d_fs")) { ok_ = true; } else { - WARN_LOG(G3D, "vkCreateShaderModule failed"); + WARN_LOG(G3D, "vkCreateShaderModule failed (%s)", tag_.c_str()); ok_ = false; } return ok_; @@ -248,13 +250,18 @@ class VKInputLayout : public InputLayout { class VKPipeline : public Pipeline { public: - VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : flags(_flags), vulkan_(vulkan) { + VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags, const char *tag) : vulkan_(vulkan), flags(_flags), tag_(tag) { uboSize_ = (int)size; ubo_ = new uint8_t[uboSize_]; } ~VKPipeline() { - vulkan_->Delete().QueueDeletePipeline(backbufferPipeline); - vulkan_->Delete().QueueDeletePipeline(framebufferPipeline); + DEBUG_LOG(G3D, "Queueing %s (pipeline) for release", tag_.c_str()); + if (pipeline) { + pipeline->QueueForDeletion(vulkan_); + } + for (auto dep : deps) { + dep->Release(); + } delete[] ubo_; } @@ -272,10 +279,12 @@ class VKPipeline : public Pipeline { return uboSize_; } - VkPipeline backbufferPipeline = VK_NULL_HANDLE; - VkPipeline framebufferPipeline = VK_NULL_HANDLE; - + VKRGraphicsPipeline *pipeline = nullptr; + VKRGraphicsPipelineDesc vkrDesc; PipelineFlags flags; + + std::vector deps; + int stride[4]{}; int dynamicUniformSize = 0; @@ -285,6 +294,7 @@ class VKPipeline : public Pipeline { VulkanContext *vulkan_; uint8_t *ubo_; int uboSize_; + std::string tag_; }; class VKTexture; @@ -374,8 +384,8 @@ class VKContext : public DrawContext { InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override; SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override; RasterState *CreateRasterState(const RasterStateDesc &desc) override; - Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override; - ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override; + Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override; + ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override; Texture *CreateTexture(const TextureDesc &desc) override; Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; @@ -429,7 +439,7 @@ class VKContext : public DrawContext { void DrawIndexed(int vertexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; - void BindCompatiblePipeline(); + void BindCurrentPipeline(); void ApplyDynamicState(); void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -466,13 +476,6 @@ class VKContext : public DrawContext { switch (obj) { case NativeObject::CONTEXT: return (uint64_t)vulkan_; - case NativeObject::FRAMEBUFFER_RENDERPASS: - // Return a representative renderpass. - return (uint64_t)renderManager_.GetFramebufferRenderPass(); - case NativeObject::BACKBUFFER_RENDERPASS: - return (uint64_t)renderManager_.GetBackbufferRenderPass(); - case NativeObject::COMPATIBLE_RENDERPASS: - return (uint64_t)renderManager_.GetCompatibleRenderPass(); case NativeObject::INIT_COMMANDBUFFER: return (uint64_t)renderManager_.GetInitCmd(); case NativeObject::BOUND_TEXTURE0_IMAGEVIEW: @@ -893,6 +896,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) VkResult res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_); _assert_(VK_SUCCESS == res); + vulkan_->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "thin3d_d_layout"); + VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pl.pPushConstantRanges = nullptr; pl.pushConstantRangeCount = 0; @@ -901,6 +906,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_); _assert_(VK_SUCCESS == res); + vulkan_->SetDebugName(pipelineLayout_, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "thin3d_p_layout"); + VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); _assert_(VK_SUCCESS == res); @@ -1036,7 +1043,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { return descSet; } -Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { +Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) { VKInputLayout *input = (VKInputLayout *)desc.inputLayout; VKBlendState *blend = (VKBlendState *)desc.blend; VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil; @@ -1047,7 +1054,26 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { pipelineFlags |= PIPELINE_FLAG_USES_DEPTH_STENCIL; } - VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags); + VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags, tag); + + VKRGraphicsPipelineDesc &gDesc = pipeline->vkrDesc; + + std::vector stages; + stages.resize(desc.shaders.size()); + + for (auto &iter : desc.shaders) { + VKShaderModule *vkshader = (VKShaderModule *)iter; + vkshader->AddRef(); + pipeline->deps.push_back(vkshader); + if (vkshader->GetStage() == ShaderStage::Vertex) { + gDesc.vertexShader = Promise::AlreadyDone(vkshader->Get()); + } else if (vkshader->GetStage() == ShaderStage::Fragment) { + gDesc.fragmentShader = Promise::AlreadyDone(vkshader->Get()); + } else { + ERROR_LOG(G3D, "Bad stage"); + return nullptr; + } + } if (input) { for (int i = 0; i < (int)input->bindings.size(); i++) { @@ -1056,84 +1082,51 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } else { pipeline->stride[0] = 0; } + _dbg_assert_(input->bindings.size() == 1); + _dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount); - std::vector stages; - stages.resize(desc.shaders.size()); - int i = 0; - for (auto &iter : desc.shaders) { - VKShaderModule *vkshader = (VKShaderModule *)iter; - if (!vkshader) { - ERROR_LOG(G3D, "CreateGraphicsPipeline got passed a null shader"); - return nullptr; - } - VkPipelineShaderStageCreateInfo &stage = stages[i++]; - stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stage.pNext = nullptr; - stage.pSpecializationInfo = nullptr; - stage.stage = StageToVulkan(vkshader->GetStage()); - stage.module = vkshader->Get(); - stage.pName = "main"; - stage.flags = 0; + gDesc.ibd = input->bindings[0]; + for (int i = 0; i < input->attributes.size(); i++) { + gDesc.attrs[i] = input->attributes[i]; } + gDesc.vis.vertexAttributeDescriptionCount = input->visc.vertexAttributeDescriptionCount; + gDesc.vis.vertexBindingDescriptionCount = input->visc.vertexBindingDescriptionCount; + gDesc.vis.pVertexBindingDescriptions = &gDesc.ibd; + gDesc.vis.pVertexAttributeDescriptions = gDesc.attrs; + + gDesc.blend0 = blend->attachments[0]; + gDesc.cbs = blend->info; + gDesc.cbs.pAttachments = &gDesc.blend0; - VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; - inputAssembly.topology = primToVK[(int)desc.prim]; - inputAssembly.primitiveRestartEnable = false; + gDesc.dss = depth->info; + + raster->ToVulkan(&gDesc.rs); + + // Copy bindings from input layout. + gDesc.inputAssembly.topology = primToVK[(int)desc.prim]; // We treat the three stencil states as a unit in other places, so let's do that here too. - 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 }; - VkPipelineDynamicStateCreateInfo dynamicInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; - dynamicInfo.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2; - dynamicInfo.pDynamicStates = dynamics; + 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 (size_t i = 0; i < gDesc.ds.dynamicStateCount; i++) { + gDesc.dynamicStates[i] = dynamics[i]; + } + gDesc.ds.pDynamicStates = gDesc.dynamicStates; + + gDesc.ms.pSampleMask = nullptr; + gDesc.ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - ms.pSampleMask = nullptr; - ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + gDesc.views.viewportCount = 1; + gDesc.views.scissorCount = 1; + gDesc.views.pViewports = nullptr; // dynamic + gDesc.views.pScissors = nullptr; // dynamic - VkPipelineViewportStateCreateInfo vs{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; - vs.viewportCount = 1; - vs.scissorCount = 1; - vs.pViewports = nullptr; // dynamic - vs.pScissors = nullptr; // dynamic + gDesc.pipelineLayout = pipelineLayout_; VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - raster->ToVulkan(&rs); - - VkPipelineVertexInputStateCreateInfo emptyVisc{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - - VkGraphicsPipelineCreateInfo createInfo[2]{}; - for (auto &info : createInfo) { - info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = 0; - info.stageCount = (uint32_t)stages.size(); - info.pStages = stages.data(); - info.pColorBlendState = &blend->info; - info.pDepthStencilState = &depth->info; - info.pDynamicState = &dynamicInfo; - info.pInputAssemblyState = &inputAssembly; - info.pTessellationState = nullptr; - info.pMultisampleState = &ms; - info.pVertexInputState = input ? &input->visc : &emptyVisc; - info.pRasterizationState = &rs; - info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. - info.layout = pipelineLayout_; - info.subpass = 0; - } - - createInfo[0].renderPass = renderManager_.GetBackbufferRenderPass(); - createInfo[1].renderPass = renderManager_.GetFramebufferRenderPass(); - - // OK, need to create new pipelines. - VkPipeline pipelines[2]{}; - VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 2, createInfo, nullptr, pipelines); - if (result != VK_SUCCESS) { - ERROR_LOG(G3D, "Failed to create graphics pipeline"); - delete pipeline; - return nullptr; - } + raster->ToVulkan(&gDesc.rs); - pipeline->backbufferPipeline = pipelines[0]; - pipeline->framebufferPipeline = pipelines[1]; + pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, 1 << RP_TYPE_BACKBUFFER, tag ? tag : "thin3d"); if (desc.uniformDesc) { pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize; @@ -1291,12 +1284,12 @@ void VKContext::BindTextures(int start, int count, Texture **textures) { } } -ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const std::string &tag) { +ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) { VKShaderModule *shader = new VKShaderModule(stage, tag); if (shader->Compile(vulkan_, language, data, size)) { return shader; } else { - ERROR_LOG(G3D, "Failed to compile shader:\n%s", (const char *)LineNumberString((const char *)data).c_str()); + ERROR_LOG(G3D, "Failed to compile shader %s:\n%s", tag, (const char *)LineNumberString((const char *)data).c_str()); shader->Release(); return nullptr; } @@ -1338,7 +1331,7 @@ void VKContext::Draw(int vertexCount, int offset) { return; } - BindCompatiblePipeline(); + BindCurrentPipeline(); ApplyDynamicState(); renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset); } @@ -1358,7 +1351,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { return; } - BindCompatiblePipeline(); + BindCurrentPipeline(); ApplyDynamicState(); renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16); } @@ -1374,18 +1367,13 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { return; } - BindCompatiblePipeline(); + BindCurrentPipeline(); ApplyDynamicState(); renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount); } -void VKContext::BindCompatiblePipeline() { - VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass(); - if (renderPass == renderManager_.GetBackbufferRenderPass()) { - renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags, pipelineLayout_); - } else { - renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags, pipelineLayout_); - } +void VKContext::BindCurrentPipeline() { + renderManager_.BindPipeline(curPipeline_->pipeline, curPipeline_->flags, pipelineLayout_); } void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { @@ -1497,7 +1485,7 @@ Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { VkCommandBuffer cmd = renderManager_.GetInitCmd(); // TODO: We always create with depth here, even when it's not needed (such as color temp FBOs). // Should optimize those away. - VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetFramebufferRenderPass(), desc.width, desc.height, desc.tag); + VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.tag); return new VKFramebuffer(vkrfb); } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 21d605b6cda1..6196f494f93e 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -239,9 +239,6 @@ enum class NativeObject { BACKBUFFER_COLOR_TEX, BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, - COMPATIBLE_RENDERPASS, - BACKBUFFER_RENDERPASS, - FRAMEBUFFER_RENDERPASS, INIT_COMMANDBUFFER, BOUND_TEXTURE0_IMAGEVIEW, BOUND_TEXTURE1_IMAGEVIEW, @@ -619,8 +616,8 @@ class DrawContext { virtual RasterState *CreateRasterState(const RasterStateDesc &desc) = 0; // virtual ComputePipeline CreateComputePipeline(const ComputePipelineDesc &desc) = 0 virtual InputLayout *CreateInputLayout(const InputLayoutDesc &desc) = 0; - virtual ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag = "thin3d") = 0; - virtual Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) = 0; + virtual ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag = "thin3d") = 0; + virtual Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) = 0; // Note that these DO NOT AddRef so you must not ->Release presets unless you manually AddRef them. ShaderModule *GetVshaderPreset(VertexShaderPreset preset) { return vsPresets_[preset]; } diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 4145b4b6c159..6fae6d7a2b37 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -79,6 +79,7 @@ class Promise { // Returns T if the data is ready, nullptr if it's not. T Poll() { + _dbg_assert_(this != nullptr); std::lock_guard guard(readyMutex_); if (ready_) { return data_; @@ -95,6 +96,7 @@ class Promise { } T BlockUntilReady() { + _dbg_assert_(this != nullptr); std::lock_guard guard(readyMutex_); if (ready_) { return data_; diff --git a/GPU/Common/Draw2D.cpp b/GPU/Common/Draw2D.cpp index cd54704e9ab0..ff7aab316965 100644 --- a/GPU/Common/Draw2D.cpp +++ b/GPU/Common/Draw2D.cpp @@ -64,6 +64,7 @@ Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) { writer.EndFSMain("outColor", FSFLAG_NONE); return Draw2DPipelineInfo{ + "draw2d_copy_color", RASTER_COLOR, RASTER_COLOR, }; @@ -77,6 +78,7 @@ Draw2DPipelineInfo GenerateDraw2DCopyDepthFs(ShaderWriter &writer) { writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH); return Draw2DPipelineInfo{ + "draw2d_copy_depth", RASTER_DEPTH, RASTER_DEPTH, }; @@ -95,6 +97,7 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthFs(ShaderWriter &writer) { writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH); return Draw2DPipelineInfo{ + "draw2d_565_to_depth", RASTER_COLOR, RASTER_DEPTH, }; @@ -119,6 +122,7 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthDeswizzleFs(ShaderWriter &writer) { writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH); return Draw2DPipelineInfo{ + "draw2d_565_to_depth_deswizzle", RASTER_COLOR, RASTER_DEPTH }; @@ -198,7 +202,7 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::functionCreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "draw2d_fs"); + ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), info.tag); _assert_(fs); @@ -237,7 +241,7 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::functionCreateGraphicsPipeline(pipelineDesc); + Draw::Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, info.tag); fs->Release(); diff --git a/GPU/Common/Draw2D.h b/GPU/Common/Draw2D.h index 2df15581b0cd..d44f7ff6134e 100644 --- a/GPU/Common/Draw2D.h +++ b/GPU/Common/Draw2D.h @@ -31,6 +31,7 @@ inline RasterChannel Draw2DSourceChannel(Draw2DShader shader) { } struct Draw2DPipelineInfo { + const char *tag; RasterChannel readChannel; RasterChannel writeChannel; Slice samplers; diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 42205de0e757..db1d886b1ffa 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -36,8 +36,11 @@ #define WRITE(p, ...) p.F(__VA_ARGS__) -bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, std::string *errorString) { +bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, FragmentShaderFlags *fragmentShaderFlags, std::string *errorString) { *uniformMask = 0; + if (fragmentShaderFlags) { + *fragmentShaderFlags = (FragmentShaderFlags)0; + } errorString->clear(); bool highpFog = false; diff --git a/GPU/Common/FragmentShaderGenerator.h b/GPU/Common/FragmentShaderGenerator.h index b459543a50ac..85c651cf8bf6 100644 --- a/GPU/Common/FragmentShaderGenerator.h +++ b/GPU/Common/FragmentShaderGenerator.h @@ -17,6 +17,7 @@ #pragma once +#include "Common/Common.h" #include "Common/GPU/Shader.h" #include "Common/GPU/thin3d.h" @@ -39,4 +40,10 @@ struct FShaderID; // For stencil upload #define CONST_PS_STENCILVALUE 11 -bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, std::string *errorString); +// Can technically be deduced from the fragment shader ID, but this is safer. +enum class FragmentShaderFlags : u32 { + FS_FLAG_INPUT_ATTACHMENT = 1, +}; +ENUM_CLASS_BITOPS(FragmentShaderFlags); + +bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, FragmentShaderFlags *fragmentShaderFlags, std::string *errorString); diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 552e62a0c5bb..e43037407936 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -439,7 +439,7 @@ Draw::Pipeline *PresentationCommon::CreatePipeline(std::vectorCreateRasterState({}); PipelineDesc pipelineDesc{ Primitive::TRIANGLE_LIST, shaders, inputLayout, depth, blendstateOff, rasterNoCull, uniformDesc }; - Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc); + Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, "presentation"); inputLayout->Release(); depth->Release(); diff --git a/GPU/Common/ReinterpretFramebuffer.cpp b/GPU/Common/ReinterpretFramebuffer.cpp index b30c71e504b5..09f2552a9b82 100644 --- a/GPU/Common/ReinterpretFramebuffer.cpp +++ b/GPU/Common/ReinterpretFramebuffer.cpp @@ -185,6 +185,7 @@ Draw2DPipelineInfo GenerateReinterpretFragmentShader(ShaderWriter &writer, GEBuf writer.EndFSMain("outColor", FSFLAG_NONE); return Draw2DPipelineInfo{ + "reinterpret", RASTER_COLOR, RASTER_COLOR, }; diff --git a/GPU/Common/StencilCommon.cpp b/GPU/Common/StencilCommon.cpp index 44cc9304bbe9..81749ef67877 100644 --- a/GPU/Common/StencilCommon.cpp +++ b/GPU/Common/StencilCommon.cpp @@ -237,7 +237,7 @@ bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilU { stencilUploadVs, stencilUploadFs }, inputLayout, stencilWrite, blendOff, rasterNoCull, &stencilUBDesc, }; - stencilUploadPipeline_ = draw_->CreateGraphicsPipeline(stencilWriteDesc); + stencilUploadPipeline_ = draw_->CreateGraphicsPipeline(stencilWriteDesc, "stencil_upload"); _assert_(stencilUploadPipeline_); delete[] fsCode; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 36c34e20a48e..8d58346a8ccb 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1866,10 +1866,9 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma return true; } break; - default: - WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat)); - return false; } + WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat)); + return false; } else if (texFormat == GE_TFMT_5650 && bufferFormat == GE_FORMAT_DEPTH16) { // We can also "depal" 565 format, this is used to read depth buffers as 565 on occasion (#15491). return true; diff --git a/GPU/Common/TextureShaderCommon.cpp b/GPU/Common/TextureShaderCommon.cpp index 79417a482ef6..bebc72d516b3 100644 --- a/GPU/Common/TextureShaderCommon.cpp +++ b/GPU/Common/TextureShaderCommon.cpp @@ -213,6 +213,7 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe Draw2DPipeline *ts = draw2D_->Create2DPipeline([=](ShaderWriter &writer) -> Draw2DPipelineInfo { GenerateDepalFs(writer, config); return Draw2DPipelineInfo{ + "depal", config.bufferFormat == GE_FORMAT_DEPTH16 ? RASTER_DEPTH : RASTER_COLOR, RASTER_COLOR, samplers diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index 34c8980ad0b6..a8537c8545bc 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -224,7 +224,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader ** // Fragment shader not in cache. Let's compile it. std::string genErrorString; uint64_t uniformMask; - GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &genErrorString); + GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &genErrorString); fs = new D3D11FragmentShader(device_, featureLevel_, FSID, codeBuffer_, useHWTransform); fsCache_[FSID] = fs; } else { diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 1700206a02ee..6e20ec45e07e 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -623,7 +623,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat // Fragment shader not in cache. Let's compile it. std::string errorString; uint64_t uniformMask; - bool success = GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &errorString); + bool success = GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &errorString); // We're supposed to handle all possible cases. _assert_(success); fs = new PSShader(device_, FSID, codeBuffer_); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index a1ae7edb6111..2780fa77994c 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -709,7 +709,7 @@ void ShaderManagerGLES::DirtyLastShader() { Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) { uint64_t uniformMask; std::string errorString; - if (!GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &errorString)) { + if (!GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &errorString)) { ERROR_LOG(G3D, "Shader gen error: %s", errorString.c_str()); return nullptr; } diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index f1f12932558e..b30218b44a08 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -94,7 +94,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 cmd = op >> 24; u32 data = op & 0xFFFFFF; - static constexpr char *primTypes[8] = { + static const char * const primTypes[8] = { "POINTS", "LINES", "LINE_STRIP", @@ -765,7 +765,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { float g = (float)((data>>8) & 0xff)/255.0f; float b = (float)(data>>16)/255.0f; - static constexpr char *lightColorTypes[] = { + static const char * const lightColorTypes[] = { "ambient", "diffuse", "specular", diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0ada8b9b06b6..e8ebd485d105 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -141,6 +141,7 @@ void DrawEngineVulkan::InitDeviceObjects() { dsl.pBindings = bindings; VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_); _dbg_assert_(VK_SUCCESS == res); + vulkan->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "drawengine_d_layout"); static constexpr int DEFAULT_DESC_POOL_SIZE = 512; std::vector dpTypes; @@ -179,6 +180,8 @@ void DrawEngineVulkan::InitDeviceObjects() { res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_); _dbg_assert_(VK_SUCCESS == res); + vulkan->SetDebugName(pipelineLayout_, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "drawengine_p_layout"); + VkSamplerCreateInfo samp{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; @@ -768,9 +771,7 @@ void DrawEngineVulkan::DoFlush() { } _dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader"); - Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, true); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. return; @@ -895,9 +896,7 @@ void DrawEngineVulkan::DoFlush() { } shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); - Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. decodedVerts_ = 0; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 89092838840e..a88fe235a546 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -18,6 +18,10 @@ using namespace PPSSPP_VK; +u32 VulkanPipeline::GetVariantsBitmask() const { + return pipeline->GetVariantsBitmask(); +} + PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) { // The pipeline cache is created on demand (or explicitly through Load). } @@ -30,21 +34,12 @@ PipelineManagerVulkan::~PipelineManagerVulkan() { } void PipelineManagerVulkan::Clear() { - // This should kill off all the shaders at once. - // This could also be an opportunity to store the whole cache to disk. Will need to also - // store the keys. - pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) { - if (value->pipeline) { - VkPipeline pipeline = value->pipeline->pipeline->BlockUntilReady(); - vulkan_->Delete().QueueDeletePipeline(pipeline); - vulkan_->Delete().QueueCallback([](void *p) { - VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p; - delete pipeline; - }, value->pipeline); - } else { + if (!value->pipeline) { // Something went wrong. ERROR_LOG(G3D, "Null pipeline found in PipelineManagerVulkan::Clear - didn't wait for asyncs?"); + } else { + value->pipeline->QueueForDeletion(vulkan_); } delete value; }); @@ -175,9 +170,10 @@ static std::string CutFromMain(std::string str) { } static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache, - VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key, - const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { - VKRGraphicsPipelineDesc *desc = new VKRGraphicsPipelineDesc(); + VkPipelineLayout layout, const VulkanPipelineRasterStateKey &key, + const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { + VulkanPipeline *vulkanPipeline = new VulkanPipeline(); + VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc; desc->pipelineCache = pipelineCache; PROFILE_THIS_SCOPE("pipelinebuild"); @@ -298,31 +294,10 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, views.pViewports = nullptr; // dynamic views.pScissors = nullptr; // dynamic - VkGraphicsPipelineCreateInfo &pipe = desc->pipe; - pipe.flags = 0; - pipe.stageCount = 2; - pipe.basePipelineIndex = 0; - - pipe.pColorBlendState = &desc->cbs; - pipe.pDepthStencilState = &desc->dss; - pipe.pRasterizationState = &desc->rs; - - // We will use dynamic viewport state. - pipe.pVertexInputState = &desc->vis; - pipe.pViewportState = &desc->views; - pipe.pTessellationState = nullptr; - pipe.pDynamicState = &desc->ds; - pipe.pInputAssemblyState = &desc->inputAssembly; - pipe.pMultisampleState = &desc->ms; - pipe.layout = layout; - pipe.basePipelineHandle = VK_NULL_HANDLE; - pipe.basePipelineIndex = 0; - pipe.renderPass = renderPass; - pipe.subpass = 0; - - VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc); + desc->pipelineLayout = layout; + + VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, variantBitmask, "game"); - VulkanPipeline *vulkanPipeline = new VulkanPipeline(); vulkanPipeline->pipeline = pipeline; vulkanPipeline->flags = 0; if (useBlendConstant) @@ -335,7 +310,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, return vulkanPipeline; } -VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { +VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { if (!pipelineCache_) { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); @@ -343,10 +318,8 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * } VulkanPipelineKey key{}; - _assert_msg_(renderPass, "Can't create a pipeline with a null renderpass"); key.raster = rasterKey; - key.renderPass = renderPass; key.useHWTransform = useHwTransform; key.vShader = vs->GetModule(); key.fShader = fs->GetModule(); @@ -357,8 +330,8 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * return iter; VulkanPipeline *pipeline = CreateVulkanPipeline( - renderManager, pipelineCache_, layout, renderPass, - rasterKey, decFmt, vs, fs, useHwTransform); + renderManager, pipelineCache_, layout, + rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask); pipelines_.Insert(key, pipeline); // Don't return placeholder null pipelines. @@ -564,9 +537,8 @@ struct StoredVulkanPipelineKey { VShaderID vShaderID; FShaderID fShaderID; uint32_t vtxFmtId; - bool useHWTransform; - bool backbufferPass; - VulkanQueueRunner::RPKey renderPassKey; + uint32_t variants; + bool useHWTransform; // TODO: Still needed? // For std::set. Better zero-initialize the struct properly for this to work. bool operator < (const StoredVulkanPipelineKey &other) const { @@ -623,18 +595,11 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha key.useHWTransform = pkey.useHWTransform; key.fShaderID = fshader->GetID(); key.vShaderID = vshader->GetID(); + key.variants = value->GetVariantsBitmask(); if (key.useHWTransform) { // NOTE: This is not a vtype, but a decoded vertex format. key.vtxFmtId = pkey.vtxFmtId; } - // Figure out what kind of renderpass this pipeline uses. - if (pkey.renderPass == queueRunner->GetBackbufferRenderPass()) { - key.backbufferPass = true; - key.renderPassKey = {}; - } else { - key.backbufferPass = false; - queueRunner->GetRenderPassKey(pkey.renderPass, &key.renderPassKey); - } keys.insert(key); }); @@ -740,22 +705,16 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha continue; } - VkRenderPass rp; - if (key.backbufferPass) { - rp = queueRunner->GetBackbufferRenderPass(); - } else { - rp = queueRunner->GetRenderPass(key.renderPassKey); - } - DecVtxFormat fmt; fmt.InitializeFromID(key.vtxFmtId); - VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, rp, key.raster, - key.useHWTransform ? &fmt : 0, - vs, fs, key.useHWTransform); + VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform, key.variants); if (!pipeline) { pipelineCreateFailCount += 1; } } + + rm->NudgeCompilerThread(); + NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines, %d failed).", (int)size, pipelineCreateFailCount); return true; } diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index a6ab695cefa4..af32aa81bd59 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -28,13 +28,14 @@ #include "GPU/Vulkan/VulkanUtil.h" #include "GPU/Vulkan/StateMappingVulkan.h" #include "GPU/Vulkan/VulkanQueueRunner.h" +#include "GPU/Vulkan/VulkanRenderManager.h" struct VKRGraphicsPipeline; class VulkanRenderManager; struct VulkanPipelineKey { VulkanPipelineRasterStateKey raster; // prim is included here - VkRenderPass renderPass; + VKRRenderPass *renderPass; Promise *vShader; Promise *fShader; uint32_t vtxFmtId; @@ -53,11 +54,14 @@ struct VulkanPipelineKey { // Simply wraps a Vulkan pipeline, providing some metadata. struct VulkanPipeline { VKRGraphicsPipeline *pipeline; + VKRGraphicsPipelineDesc desc; int flags; // PipelineFlags enum above. 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; } + + u32 GetVariantsBitmask() const; }; class VulkanContext; @@ -71,7 +75,8 @@ class PipelineManagerVulkan { PipelineManagerVulkan(VulkanContext *ctx); ~PipelineManagerVulkan(); - VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform); + // variantMask is only used when loading pipelines from cache. + VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask); int GetNumPipelines() const { return (int)pipelines_.size(); } void Clear(); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index eb30aa472810..b3e5ff339f56 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -71,7 +71,7 @@ Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade VkShaderModule shaderModule = VK_NULL_HANDLE; if (success) { - success = vulkan->CreateShaderModule(spirv, &shaderModule); + success = vulkan->CreateShaderModule(spirv, &shaderModule, stage == VK_SHADER_STAGE_VERTEX_BIT ? "game_vertex" : "game_fragment"); #ifdef SHADERLOG OutputDebugStringA("OK"); #endif @@ -81,7 +81,7 @@ Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade }; #ifdef _DEBUG - // Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator. + // Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator which is HEAVILY used by glslang. return Promise::AlreadyDone(compile()); #else return Promise::Spawn(&g_threadManager, compile, TaskType::CPU_COMPUTE); @@ -89,8 +89,8 @@ Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade } -VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code) - : vulkan_(vulkan), id_(id) { +VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, FragmentShaderFlags flags, const char *code) + : vulkan_(vulkan), id_(id), flags_(flags) { source_ = code; module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); if (!module_) { @@ -276,9 +276,10 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader // Fragment shader not in cache. Let's compile it. std::string genErrorString; uint64_t uniformMask = 0; // Not used - bool success = GenerateFragmentShader(FSID, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &genErrorString); + FragmentShaderFlags flags; + bool success = GenerateFragmentShader(FSID, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString); _assert_msg_(success, "FS gen error: %s", genErrorString.c_str()); - fs = new VulkanFragmentShader(vulkan, FSID, codeBuffer_); + fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_); fsCache_.Insert(FSID, fs); } @@ -370,7 +371,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 22 +#define CACHE_VERSION 25 struct VulkanCacheHeader { uint32_t magic; uint32_t version; @@ -417,10 +418,11 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { } std::string genErrorString; uint64_t uniformMask = 0; - if (!GenerateFragmentShader(id, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &genErrorString)) { + FragmentShaderFlags flags; + if (!GenerateFragmentShader(id, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString)) { return false; } - VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, codeBuffer_); + VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_); fsCache_.Insert(id, fs); } diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 50915be6a2c7..6593a366f4da 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -36,7 +36,7 @@ class VulkanPushBuffer; class VulkanFragmentShader { public: - VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code); + VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, FragmentShaderFlags flags, const char *code); ~VulkanFragmentShader(); const std::string &source() const { return source_; } @@ -45,7 +45,9 @@ class VulkanFragmentShader { std::string GetShaderString(DebugShaderStringType type) const; Promise *GetModule() { return module_; } - const FShaderID &GetID() { return id_; } + const FShaderID &GetID() const { return id_; } + + FragmentShaderFlags Flags() const { return flags_; } protected: Promise *module_ = nullptr; @@ -54,6 +56,7 @@ class VulkanFragmentShader { std::string source_; bool failed_ = false; FShaderID id_; + FragmentShaderFlags flags_; }; class VulkanVertexShader { @@ -68,7 +71,7 @@ class VulkanVertexShader { std::string GetShaderString(DebugShaderStringType type) const; Promise *GetModule() { return module_; } - const VShaderID &GetID() { return id_; } + const VShaderID &GetID() const { return id_; } protected: Promise *module_ = nullptr; diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index 4664f60c8a6b..fb82f9a47414 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -44,7 +44,7 @@ VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits return VK_NULL_HANDLE; } else { VkShaderModule module; - if (vulkan->CreateShaderModule(spirv, &module)) { + if (vulkan->CreateShaderModule(spirv, &module, stage == VK_SHADER_STAGE_VERTEX_BIT ? "system_vs" : "system_fs")) { return module; } else { return VK_NULL_HANDLE; diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 0320a5a8583f..b46fd7e42eb4 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -390,39 +390,39 @@ void GPUDriverTestScreen::DiscardTest() { { draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), discardFragShader_ }, inputLayout, depthStencilWrite, blendOffNoColor, rasterNoCull, &vsColBufDesc, }; - discardWriteDepthStencil_ = draw->CreateGraphicsPipeline(discardDesc); + discardWriteDepthStencil_ = draw->CreateGraphicsPipeline(discardDesc, "test"); discardDesc.depthStencil = depthWrite; - discardWriteDepth_ = draw->CreateGraphicsPipeline(discardDesc); + discardWriteDepth_ = draw->CreateGraphicsPipeline(discardDesc, "test"); discardDesc.depthStencil = stencilWrite; - discardWriteStencil_ = draw->CreateGraphicsPipeline(discardDesc); + discardWriteStencil_ = draw->CreateGraphicsPipeline(discardDesc, "test"); PipelineDesc testDesc{ Primitive::TRIANGLE_LIST, { draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D) }, inputLayout, stencilEqual, blendOff, rasterNoCull, &vsColBufDesc, }; - drawTestStencilEqual_ = draw->CreateGraphicsPipeline(testDesc); + drawTestStencilEqual_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = stencilEqualDepthAlways; - drawTestStencilEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc); + drawTestStencilEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = stencilNotEqual; - drawTestStencilNotEqual_ = draw->CreateGraphicsPipeline(testDesc); + drawTestStencilNotEqual_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = stenciNotEqualDepthAlways; - drawTestStencilNotEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc); + drawTestStencilNotEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = stencilAlwaysDepthTestGreater; - drawTestStencilAlwaysDepthGreater_ = draw->CreateGraphicsPipeline(testDesc); + drawTestStencilAlwaysDepthGreater_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = stencilAlwaysDepthTestLessEqual; - drawTestStencilAlwaysDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc); + drawTestStencilAlwaysDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = depthTestGreater; - drawTestDepthGreater_ = draw->CreateGraphicsPipeline(testDesc); + drawTestDepthGreater_ = draw->CreateGraphicsPipeline(testDesc, "test"); testDesc.depthStencil = depthTestLessEqual; - drawTestDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc); + drawTestDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc, "test"); inputLayout->Release(); blendOff->Release(); @@ -557,14 +557,14 @@ void GPUDriverTestScreen::ShaderTest() { { adrenoLogicDiscardVertShader_, adrenoLogicDiscardFragShader_ }, inputLayout, depthStencilOff, blendOff, rasterNoCull, &vsColBufDesc, }; - adrenoLogicDiscardPipeline_ = draw->CreateGraphicsPipeline(adrenoLogicDiscardDesc); + adrenoLogicDiscardPipeline_ = draw->CreateGraphicsPipeline(adrenoLogicDiscardDesc, "test"); PipelineDesc flatDesc{ Primitive::TRIANGLE_LIST, { flatVertShader_, flatFragShader_ }, inputLayout, depthStencilOff, blendOff, rasterNoCull, &vsColBufDesc, }; - flatShadingPipeline_ = draw->CreateGraphicsPipeline(flatDesc); + flatShadingPipeline_ = draw->CreateGraphicsPipeline(flatDesc, "test"); inputLayout->Release(); blendOff->Release(); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 871e77de184f..93b11fd53eb1 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -950,13 +950,13 @@ bool CreateGlobalPipelines() { inputLayout, depth, blendNormal, rasterNoCull, &vsTexColBufDesc, }; - colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc); + colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc, "global_color"); if (!colorPipeline) { // Something really critical is wrong, don't care much about correct releasing of the states. return false; } - texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc); + texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc, "global_texcolor"); if (!texColorPipeline) { // Something really critical is wrong, don't care much about correct releasing of the states. return false; diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index d90d893ff36b..7bdb0b3e8c5d 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -30,27 +30,27 @@ bool GenerateFShader(FShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs case ShaderLanguage::GLSL_VULKAN: { ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN); - return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString); + return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString); } case ShaderLanguage::GLSL_1xx: { ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx); - return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString); + return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString); } case ShaderLanguage::GLSL_3xx: { ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx); - return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString); + return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString); } case ShaderLanguage::HLSL_D3D9: { ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D9); - return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString); + return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString); } case ShaderLanguage::HLSL_D3D11: { ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11); - return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString); + return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString); } default: return false;