From 84ed793adf7d15a20b346c421df0f81417661f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 16 Aug 2017 23:03:30 +0200 Subject: [PATCH 01/28] Initial work on render queue manager --- Common/Common.vcxproj | 2 + Common/Common.vcxproj.filters | 4 +- Common/Vulkan/VulkanRenderManager.cpp | 67 +++++++++++++ Common/Vulkan/VulkanRenderManager.h | 131 ++++++++++++++++++++++++++ ext/native/thin3d/thin3d.h | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 6 ++ 6 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 Common/Vulkan/VulkanRenderManager.cpp create mode 100644 Common/Vulkan/VulkanRenderManager.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 86758c422991..58a8fac99a8c 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -249,6 +249,7 @@ + @@ -320,6 +321,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 0251f2b33459..c3ae5eea9e28 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -75,6 +75,7 @@ + @@ -138,6 +139,7 @@ + @@ -153,4 +155,4 @@ {c14d66ef-5f7c-4565-975a-72774e7ccfb9} - \ No newline at end of file + diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp new file mode 100644 index 000000000000..bbe821eadf04 --- /dev/null +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -0,0 +1,67 @@ +#include "VulkanRenderManager.h" + +void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + // If this is the first drawing command, merge it into the pass. + if (curRp_->numDraws == 0) { + curRp_->clearColor = clearColor; + curRp_->clearZ = clearZ; + curRp_->clearStencil = clearStencil; + curRp_->clearMask = clearMask; + } else { + VkRenderData data{ VKR_CLEAR }; + data.clear.clearColor = clearColor; + data.clear.clearZ = clearZ; + data.clear.clearStencil = clearStencil; + data.clear.clearMask = clearMask; + curRp_->commands.push_back(data); + } +} + +void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { + // Optimizes renderpasses, then sequences them. + for (int i = 0; i < renderPasses_.size(); i++) { + auto &commands = renderPasses_[i]->commands; + for (const auto &c : commands) { + switch (c.cmd) { + case VKR_VIEWPORT: + vkCmdSetViewport(cmdbuf, 0, 1, &c.viewport.vp); + break; + + case VKR_SCISSOR: + vkCmdSetScissor(cmdbuf, 0, 1, &c.scissor.scissor); + break; + + case VKR_DRAW_INDEXED: + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmdbuf, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDrawIndexed(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); + break; + + case VKR_DRAW: + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDraw(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0); + break; + + case VKR_STENCIL: + vkCmdSetStencilWriteMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + vkCmdSetStencilCompareMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + vkCmdSetStencilReference(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + break; + + case VKR_BLEND: + vkCmdSetBlendConstants(cmdbuf, c.blendColor.color); + break; + + case VKR_CLEAR: + // vkCmdClearAttachments + break; + } + } + } +} + +void VulkanRenderManager::Sync(VkCommandBuffer cmd) { + +} diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h new file mode 100644 index 000000000000..2f724ef62782 --- /dev/null +++ b/Common/Vulkan/VulkanRenderManager.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +#include "Common/Vulkan/VulkanContext.h" +#include "thin3d/thin3d.h" + +// Takes the role that a GL driver does of sequencing and optimizing render passes. +// Only draws and binds are handled here, resource creation and allocations are handled as normal. + + +enum VkRenderCmd : uint8_t { + VKR_STENCIL, + VKR_BLEND, + VKR_VIEWPORT, + VKR_SCISSOR, + VKR_CLEAR, + VKR_DRAW, + VKR_DRAW_INDEXED, +}; + +struct VkRenderData { + VkRenderCmd cmd; + union { + struct { + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; + int offset; + int count; + } draw; + struct { + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; // might need to increase at some point + VkDeviceSize voffset; + VkBuffer ibuffer; + VkDeviceSize ioffset; + int16_t count; + int16_t instances; + } drawIndexed; + struct { + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc + } clear; + + struct { + VkViewport vp; + } viewport; + struct { + VkRect2D scissor; + } scissor; + struct { + uint8_t stencilWriteMask; + uint8_t stencilCompareMask; + uint8_t stencilRef; + } stencil; + struct { + float color[4]; + } blendColor; + struct { + + } beginRp; + struct { + + } endRp; + }; +}; + +struct VKRRenderPass { + VkFramebuffer framebuffer; + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask = 0; // VK_IMAGE_ASPECT_COLOR_BIT etc + int dontCareMask = 0; + int numDraws; + std::vector commands; +}; + +class VulkanRenderManager { +public: + void SetViewport(VkViewport vp) { + VkRenderData data{ VKR_VIEWPORT }; + data.viewport.vp = vp; + curRp_->commands.push_back(data); + } + + void SetScissor(VkRect2D rc) { + VkRenderData data{ VKR_SCISSOR }; + data.scissor.scissor = rc; + curRp_->commands.push_back(data); + } + + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); + + void Draw(VkPipeline pipeline, VkBuffer vbuffer, int offset, int count) { + VkRenderData data{ VKR_DRAW }; + data.draw.vbuffer = vbuffer; + data.draw.offset = offset; + curRp_->commands.push_back(data); + curRp_->numDraws++; + } + + void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count) { + VkRenderData data{ VKR_DRAW }; + data.drawIndexed.pipeline = pipeline; + data.drawIndexed.vbuffer = vbuffer; + data.drawIndexed.voffset = voffset; + data.drawIndexed.ibuffer = ibuffer; + data.drawIndexed.ioffset = ioffset; + curRp_->commands.push_back(data); + curRp_->numDraws++; + } + + void Flush(VkCommandBuffer cmd); + + // Bad for performance but sometimes necessary for synchonous CPU readbacks (screenshots and whatnot). + void Sync(VkCommandBuffer cmd); + + std::vector renderPasses_; + VKRRenderPass *curRp_; +}; \ No newline at end of file diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 78c8ea6b2bee..dcbe7461600e 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -325,6 +325,7 @@ enum class NativeObject { CURRENT_RENDERPASS, RENDERPASS_COMMANDBUFFER, BOUND_TEXTURE_IMAGEVIEW, + RENDER_MANAGER, }; enum FBColorDepth { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index a9005e17ac43..4e13dd3b3642 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -34,6 +34,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" #include "Common/Vulkan/VulkanMemory.h" +#include "Common/Vulkan/VulkanRenderManager.h" // We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point. // binding 0 - uniform data @@ -472,6 +473,9 @@ class VKContext : public DrawContext { return (uintptr_t)cmd_; case NativeObject::BOUND_TEXTURE_IMAGEVIEW: return (uintptr_t)boundImageView_[0]; + + case NativeObject::RENDER_MANAGER: + return renderManager_; default: return 0; } @@ -491,6 +495,8 @@ class VKContext : public DrawContext { VulkanContext *vulkan_ = nullptr; + VulkanRenderManager renderManager_; + VKPipeline *curPipeline_ = nullptr; VKBuffer *curVBuffers_[4]{}; int curVBufferOffsets_[4]{}; From 417b96a1b07727887050563f9bf4d7348580248d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 18 Aug 2017 15:08:40 +0200 Subject: [PATCH 02/28] Use RenderManager for thin3d stuff --- Common/Vulkan/VulkanRenderManager.cpp | 57 +++++++++++++++++-- Common/Vulkan/VulkanRenderManager.h | 53 +++++++++++++++--- ext/native/thin3d/thin3d_vulkan.cpp | 81 +++++++++------------------ 3 files changed, 123 insertions(+), 68 deletions(-) diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index bbe821eadf04..e097d44eca37 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -1,5 +1,18 @@ +#include "base/logging.h" + #include "VulkanRenderManager.h" +void VulkanRenderManager::BeginFrameWrites() { + vulkan_->BeginFrame(); + renderPasses_.push_back(new VKRRenderPass); + curRp_ = renderPasses_.back(); +} + +void VulkanRenderManager::EndFrame() { + vulkan_->EndFrame(); +} + + void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { // If this is the first drawing command, merge it into the pass. if (curRp_->numDraws == 0) { @@ -32,6 +45,7 @@ void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { break; case VKR_DRAW_INDEXED: + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmdbuf, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); @@ -39,9 +53,10 @@ void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { break; case VKR_DRAW: - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); - vkCmdDraw(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0); + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + vkCmdDraw(cmdbuf, c.draw.count, 1, 0, 0); break; case VKR_STENCIL: @@ -55,11 +70,45 @@ void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { break; case VKR_CLEAR: - // vkCmdClearAttachments + { + int numAttachments = 0; + VkClearRect rc{}; + rc.baseArrayLayer = 0; + rc.layerCount = 1; + rc.rect.extent.width = curWidth_; + rc.rect.extent.height = curHeight_; + VkClearAttachment attachments[2]; + if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.colorAttachment = 0; + Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); + } + if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = 0; + if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + attachment.clearValue.depthStencil.depth = c.clear.clearZ; + attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; + attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + if (numAttachments) { + vkCmdClearAttachments(cmdbuf, numAttachments, attachments, 1, &rc); + } break; } + default: + ELOG("Unimpl queue command"); + ; + } } + delete renderPasses_[i]; } + renderPasses_.clear(); } void VulkanRenderManager::Sync(VkCommandBuffer cmd) { diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index 2f724ef62782..c4c59485ebc5 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -1,13 +1,17 @@ #pragma once #include +#include #include "Common/Vulkan/VulkanContext.h" +#include "math/dataconv.h" #include "thin3d/thin3d.h" // Takes the role that a GL driver does of sequencing and optimizing render passes. -// Only draws and binds are handled here, resource creation and allocations are handled as normal. +// Only draws and binds are handled here, resource creation and allocations are handled as normal - +// that's the nice thing with Vulkan. +// The cool thing is that you can Flush on a different thread than you record the commands on! enum VkRenderCmd : uint8_t { VKR_STENCIL, @@ -29,7 +33,7 @@ struct VkRenderData { int numUboOffsets; uint32_t uboOffsets[3]; VkBuffer vbuffer; - int offset; + VkDeviceSize voffset; int count; } draw; struct { @@ -44,6 +48,7 @@ struct VkRenderData { VkDeviceSize ioffset; int16_t count; int16_t instances; + VkIndexType indexType; } drawIndexed; struct { uint32_t clearColor; @@ -88,44 +93,76 @@ struct VKRRenderPass { class VulkanRenderManager { public: - void SetViewport(VkViewport vp) { + VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) {} + + // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. + void BeginFrameWrites(); + void EndFrame(); + + void SetViewport(const VkViewport &vp) { VkRenderData data{ VKR_VIEWPORT }; data.viewport.vp = vp; curRp_->commands.push_back(data); } - void SetScissor(VkRect2D rc) { + void SetScissor(const VkRect2D &rc) { VkRenderData data{ VKR_SCISSOR }; data.scissor.scissor = rc; curRp_->commands.push_back(data); } + void SetBlendFactor(float color[4]) { + VkRenderData data{ VKR_BLEND }; + CopyFloat4(data.blendColor.color, color); + curRp_->commands.push_back(data); + } + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); - void Draw(VkPipeline pipeline, VkBuffer vbuffer, int offset, int count) { + void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { VkRenderData data{ VKR_DRAW }; + data.draw.count = count; + data.draw.pipeline = pipeline; + data.draw.pipelineLayout = layout; + data.draw.ds = descSet; data.draw.vbuffer = vbuffer; - data.draw.offset = offset; + data.draw.voffset = voffset; + data.draw.numUboOffsets = numUboOffsets; + for (int i = 0; i < numUboOffsets; i++) + data.draw.uboOffsets[i] = uboOffsets[i]; curRp_->commands.push_back(data); curRp_->numDraws++; } - void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count) { + void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { VkRenderData data{ VKR_DRAW }; + data.drawIndexed.count = count; data.drawIndexed.pipeline = pipeline; + data.drawIndexed.pipelineLayout = layout; + data.drawIndexed.ds = descSet; data.drawIndexed.vbuffer = vbuffer; data.drawIndexed.voffset = voffset; data.drawIndexed.ibuffer = ibuffer; data.drawIndexed.ioffset = ioffset; + data.drawIndexed.numUboOffsets = numUboOffsets; + for (int i = 0; i < numUboOffsets; i++) + data.drawIndexed.uboOffsets[i] = uboOffsets[i]; + data.drawIndexed.indexType = indexType; curRp_->commands.push_back(data); curRp_->numDraws++; } + // Can run on a different thread! Just make sure to use BeginFrameWrites. void Flush(VkCommandBuffer cmd); - // Bad for performance but sometimes necessary for synchonous CPU readbacks (screenshots and whatnot). + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(VkCommandBuffer cmd); std::vector renderPasses_; VKRRenderPass *curRp_; + +private: + VulkanContext *vulkan_; + int curWidth_; + int curHeight_; }; \ No newline at end of file diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 4e13dd3b3642..6620921b436e 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -135,6 +135,7 @@ static const VkStencilOp stencilOpToVK[8] = { VK_STENCIL_OP_DECREMENT_AND_WRAP, }; +// TODO: Replace with the one from dataconv static inline void Uint8x4ToFloat4(uint32_t u, float f[4]) { f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f); f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f); @@ -475,7 +476,7 @@ class VKContext : public DrawContext { return (uintptr_t)boundImageView_[0]; case NativeObject::RENDER_MANAGER: - return renderManager_; + return (uintptr_t)&renderManager_; default: return 0; } @@ -701,7 +702,7 @@ bool VKTexture::Create(const TextureDesc &desc) { } VKContext::VKContext(VulkanContext *vulkan) - : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{} { + : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; @@ -892,7 +893,7 @@ VkCommandBuffer VKContext::AllocCmdBuf() { } void VKContext::BeginFrame() { - vulkan_->BeginFrame(); + renderManager_.BeginFrameWrites(); FrameData &frame = frame_[frameNum_]; frame.startCmdBufs_ = 0; @@ -920,6 +921,7 @@ void VKContext::WaitRenderCompletion(Framebuffer *fbo) { void VKContext::EndFrame() { EndCurrentRenderpass(); + renderManager_.Flush(cmd_); if (cmd_) Crash(); @@ -934,7 +936,8 @@ void VKContext::EndFrame() { // Stop collecting data in the frame's data pushbuffer. push_->End(); - vulkan_->EndFrame(); + + renderManager_.EndFrame(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) @@ -1109,16 +1112,16 @@ void VKContext::SetViewports(int count, Viewport *viewports) { } void VKContext::SetBlendFactor(float color[4]) { - vkCmdSetBlendConstants(cmd_, color); + renderManager_.SetBlendFactor(color); } void VKContext::ApplyDynamicState() { if (scissorDirty_) { - vkCmdSetScissor(cmd_, 0, 1, &scissor_); + renderManager_.SetScissor(scissor_); scissorDirty_ = false; } if (viewportDirty_) { - vkCmdSetViewport(cmd_, 0, 1, &viewport_); + renderManager_.SetViewport(viewport_); viewportDirty_ = false; } } @@ -1303,13 +1306,9 @@ void VKContext::Draw(int vertexCount, int offset) { uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - vkCmdDraw(cmd_, vertexCount, 1, offset, 0); + + renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } void VKContext::DrawIndexed(int vertexCount, int offset) { @@ -1323,17 +1322,9 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf); size_t ibBindOffset = push_->Push(ibuf->GetData(), ibuf->GetSize(), &vulkanIbuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); - VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - vkCmdBindIndexBuffer(cmd_, vulkanIbuf, ibBindOffset, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(cmd_, vertexCount, 1, 0, offset, 0); + renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1343,52 +1334,30 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); VkBuffer buffers[1] = { vulkanVbuf }; VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - vkCmdDraw(cmd_, vertexCount, 1, 0, 0); + + renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, vbBindOffset, vertexCount); } // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. -void VKContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { +void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { if (!curRenderPass_) { ELOG("Clear: Need an active render pass"); return; } - int numAttachments = 0; - VkClearRect rc{}; - rc.baseArrayLayer = 0; - rc.layerCount = 1; - rc.rect.extent.width = curWidth_; - rc.rect.extent.height = curHeight_; - VkClearAttachment attachments[2]; - if (mask & FBChannel::FB_COLOR_BIT) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - Uint8x4ToFloat4(colorval, attachment.clearValue.color.float32); - } - if (mask & (FBChannel::FB_DEPTH_BIT | FBChannel::FB_STENCIL_BIT)) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = 0; - if (mask & FBChannel::FB_DEPTH_BIT) { - attachment.clearValue.depthStencil.depth = depthVal; - attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (mask & FBChannel::FB_STENCIL_BIT) { - attachment.clearValue.depthStencil.stencil = stencilVal; - attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - if (numAttachments) { - vkCmdClearAttachments(cmd_, numAttachments, attachments, 1, &rc); - } + int mask = 0; + if (clearMask & FBChannel::FB_COLOR_BIT) + mask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (clearMask & FBChannel::FB_DEPTH_BIT) + mask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (clearMask & FBChannel::FB_STENCIL_BIT) + mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.Clear(colorval, depthVal, stencilVal, mask); } DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan) { From 0a0494ef8ed7d9fedd43e05cc85094cba209378a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 19 Aug 2017 17:32:10 +0200 Subject: [PATCH 03/28] It builds! With some shortcuts, of course. --- Common/Vulkan/VulkanContext.cpp | 439 +--------- Common/Vulkan/VulkanContext.h | 99 +-- Common/Vulkan/VulkanImage.cpp | 17 +- Common/Vulkan/VulkanImage.h | 10 +- Common/Vulkan/VulkanRenderManager.cpp | 1113 +++++++++++++++++++++++-- Common/Vulkan/VulkanRenderManager.h | 266 +++++- GPU/Vulkan/DrawEngineVulkan.cpp | 27 +- GPU/Vulkan/DrawEngineVulkan.h | 13 +- GPU/Vulkan/FramebufferVulkan.cpp | 53 +- GPU/Vulkan/TextureCacheVulkan.cpp | 13 +- GPU/Vulkan/VulkanUtil.cpp | 11 +- GPU/Vulkan/VulkanUtil.h | 7 +- Windows/GPU/WindowsVulkanContext.cpp | 6 +- ext/native/math/dataconv.h | 11 + ext/native/thin3d/thin3d.h | 5 +- ext/native/thin3d/thin3d_d3d11.cpp | 2 +- ext/native/thin3d/thin3d_d3d9.cpp | 2 +- ext/native/thin3d/thin3d_gl.cpp | 2 +- ext/native/thin3d/thin3d_vulkan.cpp | 810 ++---------------- 19 files changed, 1452 insertions(+), 1454 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index a5af3a8083ff..20ec8b0bd316 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -167,120 +167,14 @@ void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); } -VkCommandBuffer VulkanContext::GetInitCommandBuffer() { +void VulkanContext::BeginFrame() { FrameData *frame = &frame_[curFrame_]; - if (!frame->hasInitCommands) { - VulkanBeginCommandBuffer(frame->cmdInit); - frame->hasInitCommands = true; - } - return frame_[curFrame_].cmdInit; -} - -void VulkanContext::QueueBeforeSurfaceRender(VkCommandBuffer cmd) { - cmdQueue_.push_back(cmd); -} - -VkCommandBuffer VulkanContext::BeginFrame() { - FrameData *frame = &frame_[curFrame_]; - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device_, swap_chain_, UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer); - // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR - // return codes - assert(res == VK_SUCCESS); - - // Make sure the very last command buffer from the frame before the previous has been fully executed. - WaitAndResetFence(frame->fence); - // Process pending deletes. frame->deleteList.PerformDeletes(device_); - - // Reset both command buffers in one fell swoop. - // Note that on the first frame, there might already be commands so don't reset in that case. - if (!frame->hasInitCommands) { - vkResetCommandPool(device_, frame->cmdPool, 0); - } - - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; - res = vkBeginCommandBuffer(frame->cmdBuf, &begin); - - TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image); - return frame->cmdBuf; -} - -VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) { - FrameData *frame = &frame_[curFrame_]; - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = surface_render_pass_; - rp_begin.framebuffer = framebuffers_[current_buffer]; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = width_; - rp_begin.renderArea.extent.height = height_; - rp_begin.clearValueCount = 2; - rp_begin.pClearValues = clear_values; - vkCmdBeginRenderPass(frame->cmdBuf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - return frame->cmdBuf; -} - -void VulkanContext::EndSurfaceRenderPass() { - FrameData *frame = &frame_[curFrame_]; - // ILOG("VulkanContext::EndSurfaceRenderPass"); - vkCmdEndRenderPass(frame->cmdBuf); } void VulkanContext::EndFrame() { - FrameData *frame = &frame_[curFrame_]; - TransitionToPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image); - - VkResult res = vkEndCommandBuffer(frame->cmdBuf); - assert(res == VK_SUCCESS); - - // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. - // This way we bunch up all the initialization needed for the frame, we render to - // other buffers before the back buffer, and then last we render to the backbuffer. - - int numCmdBufs = 0; - std::vector cmdBufs; - if (frame->hasInitCommands) { - vkEndCommandBuffer(frame->cmdInit); - cmdBufs.push_back(frame->cmdInit); - frame->hasInitCommands = false; - } - for (auto cmd : cmdQueue_) { - cmdBufs.push_back(cmd); - } - cmdQueue_.clear(); - cmdBufs.push_back(frame->cmdBuf); - - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; - VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; - submit_info.pWaitDstStageMask = waitStage; - submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); - submit_info.pCommandBuffers = cmdBufs.data(); - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; - res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence); - assert(res == VK_SUCCESS); - - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present.swapchainCount = 1; - present.pSwapchains = &swap_chain_; - present.pImageIndices = ¤t_buffer; - present.pWaitSemaphores = &renderingCompleteSemaphore_; - present.waitSemaphoreCount = 1; - present.pResults = NULL; - - res = vkQueuePresentKHR(gfx_queue_, &present); - // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI - // return codes - assert(!res); - - frame->deleteList.Take(globalDeleteList_); + frame_[curFrame_].deleteList.Take(globalDeleteList_); curFrame_++; if (curFrame_ >= inflightFrames_) { curFrame_ = 0; @@ -308,72 +202,19 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem return false; } -void VulkanBeginCommandBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - VkCommandBufferBeginInfo cmd_buf_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - cmd_buf_info.pInheritanceInfo = nullptr; - cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - res = vkBeginCommandBuffer(cmd, &cmd_buf_info); - assert(res == VK_SUCCESS); -} - -bool VulkanContext::InitObjects(bool depthPresent) { - int physical_device = 0; // TODO +bool VulkanContext::InitObjects() { InitQueue(); - // Create frame data - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - VkResult U_ASSERT_ONLY res; - VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; - cmd_pool_info.queueFamilyIndex = graphics_queue_family_index_; - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; - - res = vkCreateCommandPool(device_, &cmd_pool_info, NULL, &frame_[i].cmdPool); - assert(res == VK_SUCCESS); - - VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - cmd_alloc.commandPool = frame_[i].cmdPool; - cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_alloc.commandBufferCount = 2; - - VkCommandBuffer cmdBuf[2]; - res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf); - assert(res == VK_SUCCESS); - frame_[i].cmdBuf = cmdBuf[0]; - frame_[i].cmdInit = cmdBuf[1]; - frame_[i].fence = CreateFence(true); // So it can be instantly waited on - } - - VkCommandBuffer cmd = GetInitCommandBuffer(); - if (!InitSwapchain(cmd)) { + if (!InitSwapchain()) { return false; } - InitDepthStencilBuffer(cmd); - - InitSurfaceRenderPass(depthPresent, true); - InitFramebuffers(depthPresent); - - // The init command buffer will be executed as part of the first frame. - return true; } void VulkanContext::DestroyObjects() { - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - VkCommandBuffer cmdBuf[2]; - cmdBuf[0] = frame_[i].cmdBuf; - cmdBuf[1] = frame_[i].cmdInit; - vkFreeCommandBuffers(device_, frame_[i].cmdPool, 2, cmdBuf); - vkDestroyCommandPool(device_, frame_[i].cmdPool, nullptr); - } - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - vkDestroyFence(device_, frame_[i].fence, nullptr); - } - - DestroyFramebuffers(); - DestroySurfaceRenderPass(); - DestroyDepthStencilBuffer(); - DestroySwapChain(); + if (swapchain_ != VK_NULL_HANDLE) + vkDestroySwapchainKHR(device_, swapchain_, nullptr); + swapchain_ = VK_NULL_HANDLE; // If there happen to be any pending deletes, now is a good time. for (int i = 0; i < ARRAY_SIZE(frame_); i++) { @@ -670,78 +511,6 @@ void VulkanContext::DestroyDebugMsgCallback() { } } -void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - bool U_ASSERT_ONLY pass; - - const VkFormat depth_format = deviceInfo_.preferredDepthStencilFormat; - int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = depth_format; - image_info.extent.width = width_; - image_info.extent.height = height_; - image_info.extent.depth = 1; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = NULL; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - image_info.flags = 0; - - VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; - - VkMemoryRequirements mem_reqs; - - depth.format = depth_format; - - res = vkCreateImage(device_, &image_info, NULL, &depth.image); - assert(res == VK_SUCCESS); - - vkGetImageMemoryRequirements(device_, depth.image, &mem_reqs); - - mem_alloc.allocationSize = mem_reqs.size; - /* Use the memory properties to determine the type of memory required */ - pass = MemoryTypeFromProperties(mem_reqs.memoryTypeBits, - 0, /* No requirements */ - &mem_alloc.memoryTypeIndex); - assert(pass); - - res = vkAllocateMemory(device_, &mem_alloc, NULL, &depth.mem); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(device_, depth.image, depth.mem, 0); - assert(res == VK_SUCCESS); - - TransitionImageLayout2(cmd, depth.image, - aspectMask, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); - - VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - depth_view_info.image = depth.image; - depth_view_info.format = depth_format; - depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; - depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; - depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; - depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; - depth_view_info.subresourceRange.aspectMask = aspectMask; - depth_view_info.subresourceRange.baseMipLevel = 0; - depth_view_info.subresourceRange.levelCount = 1; - depth_view_info.subresourceRange.baseArrayLayer = 0; - depth_view_info.subresourceRange.layerCount = 1; - depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - depth_view_info.flags = 0; - - res = vkCreateImageView(device_, &depth_view_info, NULL, &depth.view); - assert(res == VK_SUCCESS); -} - #ifdef _WIN32 void VulkanContext::InitSurfaceWin32(HINSTANCE conn, HWND wnd) { connection = conn; @@ -855,39 +624,32 @@ void VulkanContext::InitQueue() { // supported format will be returned. if (formatCount == 0 || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) { ILOG("swapchain_format: Falling back to B8G8R8A8_UNORM"); - swapchain_format = VK_FORMAT_B8G8R8A8_UNORM; + swapchainFormat_ = VK_FORMAT_B8G8R8A8_UNORM; } else { - swapchain_format = VK_FORMAT_UNDEFINED; + swapchainFormat_ = VK_FORMAT_UNDEFINED; for (uint32_t i = 0; i < formatCount; ++i) { if (surfFormats[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) { continue; } if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { - swapchain_format = surfFormats[i].format; + swapchainFormat_ = surfFormats[i].format; break; } } - if (swapchain_format == VK_FORMAT_UNDEFINED) { + if (swapchainFormat_ == VK_FORMAT_UNDEFINED) { // Okay, take the first one then. - swapchain_format = surfFormats[0].format; + swapchainFormat_ = surfFormats[0].format; } - ILOG("swapchain_format: %d (/%d)", swapchain_format, formatCount); + ILOG("swapchain_format: %d (/%d)", swapchainFormat_, formatCount); } delete[] surfFormats; vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_); ILOG("gfx_queue_: %p", gfx_queue_); - - VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - semaphoreCreateInfo.flags = 0; - res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &acquireSemaphore_); - assert(res == VK_SUCCESS); - res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_); - assert(res == VK_SUCCESS); } -bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { +bool VulkanContext::InitSwapchain() { VkResult U_ASSERT_ONLY res; VkSurfaceCapabilitiesKHR surfCapabilities; @@ -967,7 +729,7 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { VkSwapchainCreateInfoKHR swap_chain_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; swap_chain_info.surface = surface_; swap_chain_info.minImageCount = desiredNumberOfSwapChainImages; - swap_chain_info.imageFormat = swapchain_format; + swap_chain_info.imageFormat = swapchainFormat_; swap_chain_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swap_chain_info.imageExtent.width = swapChainExtent.width; swap_chain_info.imageExtent.height = swapChainExtent.height; @@ -988,141 +750,15 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; } - res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swap_chain_); + res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swapchain_); assert(res == VK_SUCCESS); if (res != VK_SUCCESS) { return false; } - res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, nullptr); - assert(res == VK_SUCCESS); - - ILOG("Vulkan swapchain image count: %d", swapchainImageCount); - - VkImage* swapchainImages = new VkImage[swapchainImageCount]; - assert(swapchainImages); - res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, swapchainImages); - assert(res == VK_SUCCESS); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - swap_chain_buffer sc_buffer; - - VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - color_image_view.format = swapchain_format; - color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; - color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; - color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; - color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; - color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - color_image_view.subresourceRange.baseMipLevel = 0; - color_image_view.subresourceRange.levelCount = 1; - color_image_view.subresourceRange.baseArrayLayer = 0; - color_image_view.subresourceRange.layerCount = 1; - color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; - color_image_view.flags = 0; - - sc_buffer.image = swapchainImages[i]; - - // TODO: Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring - // in image to render to will be to transition them away from that. - TransitionImageLayout2(cmd, sc_buffer.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - - color_image_view.image = sc_buffer.image; - - res = vkCreateImageView(device_, &color_image_view, NULL, &sc_buffer.view); - swapChainBuffers.push_back(sc_buffer); - assert(res == VK_SUCCESS); - } - - delete[] swapchainImages; - current_buffer = 0; return true; } -void VulkanContext::InitSurfaceRenderPass(bool include_depth, bool clear) { - // Need attachments for render target and depth buffer - VkAttachmentDescription attachments[2]{}; - attachments[0].format = swapchain_format; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - 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_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].flags = 0; - - if (include_depth) { - attachments[1].format = depth.format; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - 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 = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - 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 = include_depth ? &depth_reference : nullptr; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp_info.attachmentCount = include_depth ? 2 : 1; - rp_info.pAttachments = attachments; - rp_info.subpassCount = 1; - rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 0; - rp_info.pDependencies = nullptr; - - VkResult U_ASSERT_ONLY res = vkCreateRenderPass(device_, &rp_info, nullptr, &surface_render_pass_); - assert(res == VK_SUCCESS); -} - -void VulkanContext::InitFramebuffers(bool include_depth) { - VkResult U_ASSERT_ONLY res; - VkImageView attachments[2]; - attachments[1] = depth.view; - - ILOG("InitFramebuffers: %dx%d", width_, height_); - VkFramebufferCreateInfo fb_info{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb_info.renderPass = surface_render_pass_; - fb_info.attachmentCount = include_depth ? 2 : 1; - fb_info.pAttachments = attachments; - fb_info.width = width_; - fb_info.height = height_; - fb_info.layers = 1; - - framebuffers_.resize(swapchainImageCount); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - attachments[0] = swapChainBuffers[i].view; - res = vkCreateFramebuffer(device_, &fb_info, nullptr, &framebuffers_[i]); - assert(res == VK_SUCCESS); - } -} - VkFence VulkanContext::CreateFence(bool presignalled) { VkFence fence; VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; @@ -1131,49 +767,6 @@ VkFence VulkanContext::CreateFence(bool presignalled) { return fence; } -void VulkanContext::WaitAndResetFence(VkFence fence) { - vkWaitForFences(device_, 1, &fence, true, UINT64_MAX); - vkResetFences(device_, 1, &fence); -} - -void VulkanContext::DestroyDepthStencilBuffer() { - if (depth.view != VK_NULL_HANDLE) - vkDestroyImageView(device_, depth.view, nullptr); - if (depth.image != VK_NULL_HANDLE) - vkDestroyImage(device_, depth.image, nullptr); - if (depth.mem != VK_NULL_HANDLE) - vkFreeMemory(device_, depth.mem, nullptr); - - depth.view = VK_NULL_HANDLE; - depth.image = VK_NULL_HANDLE; - depth.mem = VK_NULL_HANDLE; -} - -void VulkanContext::DestroySwapChain() { - for (uint32_t i = 0; i < swapchainImageCount; i++) { - vkDestroyImageView(device_, swapChainBuffers[i].view, nullptr); - } - if (swap_chain_ != VK_NULL_HANDLE) - vkDestroySwapchainKHR(device_, swap_chain_, nullptr); - swap_chain_ = VK_NULL_HANDLE; - swapChainBuffers.clear(); - vkDestroySemaphore(device_, acquireSemaphore_, nullptr); - vkDestroySemaphore(device_, renderingCompleteSemaphore_, nullptr); -} - -void VulkanContext::DestroyFramebuffers() { - for (uint32_t i = 0; i < framebuffers_.size(); i++) { - vkDestroyFramebuffer(device_, framebuffers_[i], nullptr); - } - framebuffers_.clear(); -} - -void VulkanContext::DestroySurfaceRenderPass() { - if (surface_render_pass_ != VK_NULL_HANDLE) - vkDestroyRenderPass(device_, surface_render_pass_, nullptr); - surface_render_pass_ = VK_NULL_HANDLE; -} - void VulkanContext::DestroyDevice() { vkDestroyDevice(device_, nullptr); device_ = nullptr; diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 925240b5c776..e8204dba4c80 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -195,8 +195,7 @@ class VulkanDeleteList { std::vector callbacks_; }; -// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc. -// Optionally, it can create a depth buffer for you as well. +// VulkanContext manages the device and swapchain, and deferred deletion of objects. class VulkanContext { public: VulkanContext(); @@ -227,19 +226,12 @@ class VulkanContext { void ReinitSurfaceAndroid(int width, int height); #endif void InitQueue(); - bool InitObjects(bool depthPresent); - bool InitSwapchain(VkCommandBuffer cmd); - void InitSurfaceRenderPass(bool include_depth, bool clear); - void InitFramebuffers(bool include_depth); - void InitDepthStencilBuffer(VkCommandBuffer cmd); + bool InitObjects(); + bool InitSwapchain(); // Also destroys the surface. void DestroyObjects(); - void DestroySurfaceRenderPass(); - void DestroyFramebuffers(); - void DestroySwapChain(); - void DestroyDepthStencilBuffer(); void DestroyDevice(); void WaitUntilQueueIdle(); @@ -248,41 +240,17 @@ class VulkanContext { VkFence CreateFence(bool presignalled); bool CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule); - void WaitAndResetFence(VkFence fence); + int GetBackbufferWidth() { return width_; } + int GetBackbufferHeight() { return height_; } - int GetWidth() { return width_; } - int GetHeight() { return height_; } - - VkCommandBuffer GetInitCommandBuffer(); - - VkFramebuffer GetSurfaceFramebuffer() { - return framebuffers_[current_buffer]; - } - // This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass. - VkCommandBuffer GetSurfaceCommandBuffer() { - return frame_[curFrame_].cmdBuf; - } - - VkCommandBuffer BeginFrame(); - // The surface render pass is special because it has to acquire the backbuffer, and may thus "block". - // Use the returned command buffer to enqueue commands that render to the backbuffer. - // To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd). - VkCommandBuffer BeginSurfaceRenderPass(VkClearValue clear_values[2]); - // May eventually need the ability to break and resume the backbuffer render pass in a few rare cases. - void EndSurfaceRenderPass(); + void BeginFrame(); void EndFrame(); - void QueueBeforeSurfaceRender(VkCommandBuffer cmd); - bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr); void DestroyDebugMsgCallback(); - VkRenderPass GetSurfaceRenderPass() const { - return surface_render_pass_; - } - VkPhysicalDevice GetPhysicalDevice(int n = 0) const { return physical_devices_[n]; } @@ -327,8 +295,19 @@ class VulkanContext { return inflightFrames_; } + int GetCurFrame() const { + return curFrame_; + } + + VkSwapchainKHR GetSwapchain() const { + return swapchain_; + } + VkFormat GetSwapchainFormat() const { + return swapchainFormat_; + } + enum { - MAX_INFLIGHT_FRAMES = 2, + MAX_INFLIGHT_FRAMES = 3, }; private: @@ -340,9 +319,6 @@ class VulkanContext { bool CheckLayers(const std::vector &layer_props, const std::vector &layer_names) const; - VkSemaphore acquireSemaphore_; - VkSemaphore renderingCompleteSemaphore_; - #ifdef _WIN32 HINSTANCE connection = nullptr; // hInstance - Windows Instance HWND window = nullptr; // hWnd - window handle @@ -380,36 +356,15 @@ class VulkanContext { // Custom collection of things that are good to know VulkanPhysicalDeviceInfo deviceInfo_; - struct swap_chain_buffer { - VkImage image; - VkImageView view; - }; - // Swap chain int width_ = 0; int height_ = 0; int flags_ = 0; - VkFormat swapchain_format = VK_FORMAT_UNDEFINED; - std::vector framebuffers_; - uint32_t swapchainImageCount = 0; - VkSwapchainKHR swap_chain_ = VK_NULL_HANDLE; - std::vector swapChainBuffers; int inflightFrames_ = MAX_INFLIGHT_FRAMES; - // Manages flipping command buffers for the backbuffer render pass. - // It is recommended to do the same for other rendering passes. struct FrameData { - FrameData() : hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {} - - VkFence fence; - bool hasInitCommands; - - // TODO: Move to frame data - VkCommandPool cmdPool; - VkCommandBuffer cmdInit; - VkCommandBuffer cmdBuf; - + FrameData() {} VulkanDeleteList deleteList; }; FrameData frame_[MAX_INFLIGHT_FRAMES]; @@ -421,15 +376,9 @@ class VulkanContext { std::vector msg_callbacks; - struct { - VkFormat format; - VkImage image = VK_NULL_HANDLE; - VkDeviceMemory mem = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - } depth; + VkSwapchainKHR swapchain_; + VkFormat swapchainFormat_; - VkRenderPass surface_render_pass_ = VK_NULL_HANDLE; - uint32_t current_buffer = 0; uint32_t queue_count = 0; VkPhysicalDeviceFeatures featuresAvailable_; @@ -438,15 +387,15 @@ class VulkanContext { std::vector cmdQueue_; }; -// Stand-alone utility functions -void VulkanBeginCommandBuffer(VkCommandBuffer cmd); - // Detailed control. void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); +void TransitionFromPresent(VkCommandBuffer cmd, VkImage image); +void TransitionToPresent(VkCommandBuffer cmd, VkImage image); + // GLSL compiler void init_glslang(); void finalize_glslang(); diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 19291746ae80..41cf32c8243a 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -84,11 +84,9 @@ uint8_t *VulkanTexture::Lock(int level, int *rowPitch) { return (uint8_t *)data; } -void VulkanTexture::Unlock() { +void VulkanTexture::Unlock(VkCommandBuffer cmd) { vkUnmapMemory(vulkan_->GetDevice(), mappableMemory); - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - // if we already have an image, queue it for destruction and forget it. Wipe(); @@ -227,11 +225,9 @@ static bool IsDepthStencilFormat(VkFormat format) { } } -bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { +bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { Wipe(); - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - tex_width = w; tex_height = h; numMips_ = numMips; @@ -336,7 +332,7 @@ bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI return true; } -void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { +void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { VkBufferImageCopy copy_region = {}; copy_region.bufferOffset = offset; copy_region.bufferRowLength = (uint32_t)rowLength; @@ -349,12 +345,10 @@ void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buf copy_region.imageSubresource.baseArrayLayer = 0; copy_region.imageSubresource.layerCount = 1; - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); } -void VulkanTexture::EndCreate() { - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); +void VulkanTexture::EndCreate(VkCommandBuffer cmd) { TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -362,8 +356,7 @@ void VulkanTexture::EndCreate() { VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } -void VulkanTexture::TransitionForUpload() { - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); +void VulkanTexture::TransitionForUpload(VkCommandBuffer cmd) { TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 50b9b0b65146..51ffbe5e22c8 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -26,16 +26,16 @@ class VulkanTexture { // been called. VkResult Create(int w, int h, VkFormat format); uint8_t *Lock(int level, int *rowPitch); - void Unlock(); + void Unlock(VkCommandBuffer cmd); // Fast uploads from buffer. Mipmaps supported. // Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip. // When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. - bool CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); - void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels - void EndCreate(); + bool CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); + void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels + void EndCreate(VkCommandBuffer cmd); - void TransitionForUpload(); + void TransitionForUpload(VkCommandBuffer cmd); int GetNumMips() const { return numMips_; } void Destroy(); diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index e097d44eca37..734ae34ac8de 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -1,116 +1,1079 @@ #include "base/logging.h" #include "VulkanRenderManager.h" +#include "VulkanContext.h" + +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { + VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + ici.arrayLayers = 1; + ici.mipLevels = 1; + ici.extent.width = width; + ici.extent.height = height; + ici.extent.depth = 1; + ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + ici.imageType = VK_IMAGE_TYPE_2D; + ici.samples = VK_SAMPLE_COUNT_1_BIT; + ici.tiling = VK_IMAGE_TILING_OPTIMAL; + ici.format = format; + ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (color) { + ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } else { + ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); + + // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers + + VkMemoryRequirements memreq; + vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq); + + VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + alloc.allocationSize = memreq.size; + vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex); + VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory); + assert(res == VK_SUCCESS); + res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); + assert(res == VK_SUCCESS); + + VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + + VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + ivci.format = ici.format; + ivci.image = img.image; + ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; + ivci.subresourceRange.aspectMask = aspects; + ivci.subresourceRange.layerCount = 1; + ivci.subresourceRange.levelCount = 1; + res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView); + assert(res == VK_SUCCESS); + + VkPipelineStageFlagBits dstStage; + VkAccessFlagBits dstAccessMask; + switch (initialLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + break; + } + + TransitionImageLayout2(cmd, img.image, aspects, + VK_IMAGE_LAYOUT_UNDEFINED, initialLayout, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, + 0, dstAccessMask); + img.layout = initialLayout; +} + +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPool); + assert(res == VK_SUCCESS); + + VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + cmd_alloc.commandPool = frameData_[i].cmdPool; + cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_alloc.commandBufferCount = 2; + + VkCommandBuffer cmdBuf[2]; + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, cmdBuf); + assert(res == VK_SUCCESS); + frameData_[i].mainCmd = cmdBuf[0]; + frameData_[i].initCmd = cmdBuf[1]; + frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on + } + + VkSwapchainKHR swapChain = vulkan_->GetSwapchain(); + VkResult res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, nullptr); + assert(res == VK_SUCCESS); + + ILOG("Vulkan swapchain image count: %d", swapchainImageCount); + + VkImage* swapchainImages = new VkImage[swapchainImageCount]; + assert(swapchainImages); + res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, swapchainImages); + assert(res == VK_SUCCESS); + + VkCommandBuffer cmdInit = frameData_[0].initCmd; + frameData_[0].hasInitCommands = true; + + for (uint32_t i = 0; i < swapchainImageCount; i++) { + SwapchainImageData sc_buffer; + + VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + color_image_view.format = vulkan_->GetSwapchainFormat(); + color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; + color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; + color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; + color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; + color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + color_image_view.subresourceRange.baseMipLevel = 0; + color_image_view.subresourceRange.levelCount = 1; + color_image_view.subresourceRange.baseArrayLayer = 0; + color_image_view.subresourceRange.layerCount = 1; + color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + color_image_view.flags = 0; + + sc_buffer.image = swapchainImages[i]; + + // Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring + // in image to render to will be to transition them away from that. + TransitionImageLayout2(cmdInit, sc_buffer.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + color_image_view.image = sc_buffer.image; + + res = vkCreateImageView(vulkan_->GetDevice(), + &color_image_view, NULL, &sc_buffer.view); + swapchainImages_.push_back(sc_buffer); + assert(res == VK_SUCCESS); + } + delete[] swapchainImages; + current_buffer = 0; + + VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + semaphoreCreateInfo.flags = 0; + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); + assert(res == VK_SUCCESS); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); + assert(res == VK_SUCCESS); + + InitDepthStencilBuffer(cmdInit); + InitSurfaceRenderPass(); +} + +VulkanRenderManager::~VulkanRenderManager() { + VkDevice device = vulkan_->GetDevice(); + for (uint32_t i = 0; i < swapchainImageCount; i++) { + vkDestroyImageView(device, swapchainImages_[i].view, nullptr); + } + swapchainImages_.clear(); + vkDestroySemaphore(device, acquireSemaphore_, nullptr); + vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; + vkFreeCommandBuffers(device, frameData_[i].cmdPool, 2, cmdBuf); + vkDestroyCommandPool(device, frameData_[i].cmdPool, nullptr); + vkDestroyFence(device, frameData_[i].fence, nullptr); + } + if (backbufferRenderPass_ != VK_NULL_HANDLE) + vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); + for (uint32_t i = 0; i < framebuffers_.size(); i++) { + vkDestroyFramebuffer(device, framebuffers_[i], nullptr); + } + framebuffers_.clear(); + for (int i = 0; i < 9; i++) { + vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); + } +} + +void VulkanRenderManager::ThreadFunc() { + while (true) { + std::unique_lock lock(mutex_); + condVar_.wait(lock); + if (steps_.size()) { + stepsOnThread_ = std::move(steps_); + } + // ... + } +} void VulkanRenderManager::BeginFrameWrites() { vulkan_->BeginFrame(); - renderPasses_.push_back(new VKRRenderPass); - curRp_ = renderPasses_.back(); + + VkDevice device = vulkan_->GetDevice(); + + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + + // Make sure the very last command buffer from the frame before the previous has been fully executed. + vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); + vkResetFences(device, 1, &frameData.fence); + + // Reset both command buffers in one fell swoop. + // Note that on the first frame, there might already be commands so don't reset in that case. + if (!frameData.hasInitCommands) { + vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPool, 0); + } + + VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin.pInheritanceInfo = nullptr; + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); +} + +VkCommandBuffer VulkanRenderManager::GetInitCmd() { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + if (!frameData_->hasInitCommands) { + VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin.pInheritanceInfo = nullptr; + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + } + return frameData_[vulkan_->GetCurFrame()].initCmd; } void VulkanRenderManager::EndFrame() { + FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + TransitionToPresent(frame.mainCmd, swapchainImages_[current_buffer].image); + + VkResult res = vkEndCommandBuffer(frame.mainCmd); + assert(res == VK_SUCCESS); + + // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. + // This way we bunch up all the initialization needed for the frame, we render to + // other buffers before the back buffer, and then last we render to the backbuffer. + + int numCmdBufs = 0; + std::vector cmdBufs; + if (frame.hasInitCommands) { + vkEndCommandBuffer(frame.initCmd); + cmdBufs.push_back(frame.initCmd); + frame.hasInitCommands = false; + } + + cmdBufs.push_back(frame.mainCmd); + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; + submit_info.pWaitDstStageMask = waitStage; + submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); + submit_info.pCommandBuffers = cmdBufs.data(); + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &renderingCompleteSemaphore; + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); + assert(res == VK_SUCCESS); + + VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + present.swapchainCount = 1; + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); + present.pSwapchains = &swapchain; + present.pImageIndices = ¤t_buffer; + present.pWaitSemaphores = &renderingCompleteSemaphore; + present.waitSemaphoreCount = 1; + present.pResults = nullptr; + + res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); + // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI + // return codes + assert(res == VK_SUCCESS); + vulkan_->EndFrame(); } +void VulkanRenderManager::Sync() { + +} + +void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb) { + // This is what queues up new passes, and can end previous ones. +} + + +void VulkanRenderManager::BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value) { + FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = backbufferRenderPass_; + rp_begin.framebuffer = framebuffers_[current_buffer]; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = curWidth_; + rp_begin.renderArea.extent.height = curHeight_; + rp_begin.clearValueCount = 1; + rp_begin.pClearValues = &clear_value; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); +} + +void VulkanRenderManager::EndSurfaceRenderPass(VkCommandBuffer cmd) { + // ILOG("VulkanContext::EndSurfaceRenderPass"); + vkCmdEndRenderPass(cmd); +} + +void VulkanRenderManager::InitFramebuffers() { + VkResult U_ASSERT_ONLY res; + VkImageView attachments[1]; + + ILOG("InitFramebuffers: %dx%d", curWidth_, curHeight_); + VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + fb_info.renderPass = backbufferRenderPass_; + fb_info.attachmentCount = 1; + fb_info.pAttachments = attachments; + fb_info.width = curWidth_; + fb_info.height = curHeight_; + fb_info.layers = 1; + + framebuffers_.resize(swapchainImageCount); + + for (uint32_t i = 0; i < swapchainImageCount; i++) { + attachments[0] = swapchainImages_[i].view; + res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]); + assert(res == VK_SUCCESS); + } +} + +void VulkanRenderManager::InitSurfaceRenderPass() { + VkResult U_ASSERT_ONLY res; + + 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_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + attachments[1].format = depth_.format; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + 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 = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + 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; + + VkRenderPassCreateInfo rp_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp_info.pNext = nullptr; + rp_info.attachmentCount = 2; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 0; + rp_info.pDependencies = nullptr; + + res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, NULL, &backbufferRenderPass_); + assert(res == VK_SUCCESS); +} + +void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { + VkResult U_ASSERT_ONLY res; + bool U_ASSERT_ONLY pass; + + const VkFormat depth_format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = depth_format; + image_info.extent.width = curWidth_; + image_info.extent.height = curHeight_; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.queueFamilyIndexCount = 0; + image_info.pQueueFamilyIndices = nullptr; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image_info.flags = 0; + + VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkMemoryRequirements mem_reqs; + + depth_.format = depth_format; + + VkDevice device = vulkan_->GetDevice(); + res = vkCreateImage(device, &image_info, NULL, &depth_.image); + assert(res == VK_SUCCESS); + + vkGetImageMemoryRequirements(device, depth_.image, &mem_reqs); + + mem_alloc.allocationSize = mem_reqs.size; + // Use the memory properties to determine the type of memory required + pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, + 0, /* No requirements */ + &mem_alloc.memoryTypeIndex); + assert(pass); + + res = vkAllocateMemory(device, &mem_alloc, NULL, &depth_.mem); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(device, depth_.image, depth_.mem, 0); + assert(res == VK_SUCCESS); + + TransitionImageLayout2(cmd, depth_.image, + aspectMask, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + + VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + depth_view_info.image = depth_.image; + depth_view_info.format = depth_format; + depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; + depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; + depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; + depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; + depth_view_info.subresourceRange.aspectMask = aspectMask; + depth_view_info.subresourceRange.baseMipLevel = 0; + depth_view_info.subresourceRange.levelCount = 1; + depth_view_info.subresourceRange.baseArrayLayer = 0; + depth_view_info.subresourceRange.layerCount = 1; + depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + depth_view_info.flags = 0; + + res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view); + assert(res == VK_SUCCESS); +} + +void VulkanRenderManager::InitRenderpasses() { + // Create a bunch of render pass objects, for normal rendering with a depth buffer, + // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. + VkAttachmentDescription attachments[2] = {}; + attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + 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_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + 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 = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + 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; + + VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp.attachmentCount = 2; + rp.pAttachments = attachments; + rp.subpassCount = 1; + rp.pSubpasses = &subpass; + rp.dependencyCount = 0; + rp.pDependencies = nullptr; + + for (int depth = 0; depth < 3; depth++) { + switch ((RenderPassAction)depth) { + case RenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case RenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case RenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + } + for (int color = 0; color < 3; color++) { + switch ((RenderPassAction)color) { + case RenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case RenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case RenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + } + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RenderPassAction)color, (RenderPassAction)depth)]); + } + } +} + +void VulkanRenderManager::BeginRenderPass() { + std::unique_lock lock(rpLock_); + + VKRStep *pass = new VKRStep(VKStepType::RENDER); + pass->stepType = VKStepType::RENDER; + steps_.push_back(pass); + curStep_ = steps_.back(); +} void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); // If this is the first drawing command, merge it into the pass. - if (curRp_->numDraws == 0) { - curRp_->clearColor = clearColor; - curRp_->clearZ = clearZ; - curRp_->clearStencil = clearStencil; - curRp_->clearMask = clearMask; + if (curStep_->render.numDraws == 0) { + curStep_->render.clearColor = clearColor; + curStep_->render.clearDepth = clearZ; + curStep_->render.clearStencil = clearStencil; + curStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; + curStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; } else { - VkRenderData data{ VKR_CLEAR }; + VkRenderData data{ VkRenderCmd::CLEAR }; data.clear.clearColor = clearColor; data.clear.clearZ = clearZ; data.clear.clearStencil = clearStencil; data.clear.clearMask = clearMask; - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); + } +} + +void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint) { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + EndCurrentRenderpass(frameData.mainCmd); +} + +void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter) { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + EndCurrentRenderpass(frameData.mainCmd); +} + +VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { + // Should just mark the dependency and return the image. + for (int i = 0; i < (int)steps_.size() - 1; i++) { + if (steps_[i]->stepType == VKStepType::RENDER && steps_[i]->render.framebuffer == fb) { + if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) + steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + else + Crash(); // May need to shadow the framebuffer? + } } + + /* + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->color.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + } + TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + srcAccessMask, VK_ACCESS_SHADER_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + */ + return fb->color.imageView; } -void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { +void VulkanRenderManager::Flush() { + { + std::unique_lock lock(rpLock_); + stepsOnThread_ = std::move(steps_); + } + + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + VkCommandBuffer cmd = frameData.mainCmd; + + VkDevice device = vulkan_->GetDevice(); + + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer); + // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR + // return codes + assert(res == VK_SUCCESS); + TransitionFromPresent(frameData.mainCmd, swapchainImages_[current_buffer].image); + // Optimizes renderpasses, then sequences them. - for (int i = 0; i < renderPasses_.size(); i++) { - auto &commands = renderPasses_[i]->commands; - for (const auto &c : commands) { - switch (c.cmd) { - case VKR_VIEWPORT: - vkCmdSetViewport(cmdbuf, 0, 1, &c.viewport.vp); - break; + for (int i = 0; i < stepsOnThread_.size(); i++) { + const VKRStep &step = *stepsOnThread_[i]; + switch (step.stepType) { + case VKStepType::RENDER: + PerformRenderPass(step, cmd); + break; + case VKStepType::COPY: + PerformCopy(step, cmd); + break; + case VKStepType::BLIT: + PerformBlit(step, cmd); + break; + case VKStepType::READBACK: + // PerformReadback + break; + } + delete stepsOnThread_[i]; + } + stepsOnThread_.clear(); +} - case VKR_SCISSOR: - vkCmdSetScissor(cmdbuf, 0, 1, &c.scissor.scissor); - break; +void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { + PerformBindFramebufferAsRenderTarget(step, cmd); + auto &commands = step.commands; + for (const auto &c : commands) { + switch (c.cmd) { + case VkRenderCmd::VIEWPORT: + vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); + break; - case VKR_DRAW_INDEXED: - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); - vkCmdBindIndexBuffer(cmdbuf, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); - vkCmdDrawIndexed(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); - break; + case VkRenderCmd::SCISSOR: + vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); + break; - case VKR_DRAW: - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.draw.vbuffer, &c.draw.voffset); - vkCmdDraw(cmdbuf, c.draw.count, 1, 0, 0); - break; + case VkRenderCmd::BLEND: + vkCmdSetBlendConstants(cmd, c.blendColor.color); + break; - case VKR_STENCIL: - vkCmdSetStencilWriteMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); - vkCmdSetStencilCompareMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); - vkCmdSetStencilReference(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); - break; + case VkRenderCmd::STENCIL: + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + break; - case VKR_BLEND: - vkCmdSetBlendConstants(cmdbuf, c.blendColor.color); - break; + case VkRenderCmd::DRAW_INDEXED: + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); + break; - case VKR_CLEAR: - { - int numAttachments = 0; - VkClearRect rc{}; - rc.baseArrayLayer = 0; - rc.layerCount = 1; - rc.rect.extent.width = curWidth_; - rc.rect.extent.height = curHeight_; - VkClearAttachment attachments[2]; - if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); - } - if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = 0; - if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { - attachment.clearValue.depthStencil.depth = c.clear.clearZ; - attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { - attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; - attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } + case VkRenderCmd::DRAW: + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + vkCmdDraw(cmd, c.draw.count, 1, 0, 0); + break; + + case VkRenderCmd::CLEAR: + { + int numAttachments = 0; + VkClearRect rc{}; + rc.baseArrayLayer = 0; + rc.layerCount = 1; + rc.rect.extent.width = curWidth_; + rc.rect.extent.height = curHeight_; + VkClearAttachment attachments[2]; + if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.colorAttachment = 0; + Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); + } + if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = 0; + if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + attachment.clearValue.depthStencil.depth = c.clear.clearZ; + attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; } - if (numAttachments) { - vkCmdClearAttachments(cmdbuf, numAttachments, attachments, 1, &rc); + if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; + attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; } + } + if (numAttachments) { + vkCmdClearAttachments(cmd, numAttachments, attachments, 1, &rc); + } + break; + } + default: + ELOG("Unimpl queue command"); + ; + } + } + vkCmdEndRenderPass(cmd); +} + +void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { + VkFramebuffer framebuf; + int w; + int h; + VkImageLayout prevLayout; + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + framebuf = fb->framebuf; + w = fb->width; + h = fb->height; + prevLayout = fb->color.layout; + } else { + framebuf = framebuffers_[current_buffer]; + w = vulkan_->GetBackbufferWidth(); + h = vulkan_->GetBackbufferHeight(); + } + + if (framebuf == curFramebuffer_) { + if (framebuf == 0) + Crash(); + if (!curRenderPass_) + Crash(); + + // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. + // We will try to avoid this as much as possible. + VkClearAttachment clear[2]{}; + int count = 0; + if (step.render.color == RenderPassAction::CLEAR) { + clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + Uint8x4ToFloat4(clear[count].clearValue.color.float32, step.render.clearColor); + clear[count].colorAttachment = 0; + count++; + } + + if (step.render.depthStencil == RenderPassAction::CLEAR) { + clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + clear[count].clearValue.depthStencil.depth = step.render.clearDepth; + clear[count].clearValue.depthStencil.stencil = step.render.clearStencil; + clear[count].colorAttachment = 0; + count++; + } + + if (count > 0) { + VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 }; + vkCmdClearAttachments(cmd, count, clear, 1, &rc); + } + // We're done. + return; + } + + // OK, we're switching framebuffers. + if (curRenderPass_) { + vkCmdEndRenderPass(cmd); + curRenderPass_ = VK_NULL_HANDLE; + curFramebuffer_ = VK_NULL_HANDLE; + } + + VkRenderPass renderPass; + int numClearVals = 0; + VkClearValue clearVal[2]; + memset(clearVal, 0, sizeof(clearVal)); + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + // Now, if the image needs transitioning, let's transition. + // The backbuffer does not, that's handled by VulkanContext. + if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->color.image; + barrier.srcAccessMask = 0; + switch (fb->color.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; break; } - default: - ELOG("Unimpl queue command"); - ; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + // TODO: Double-check these flags. Should be fine. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->color.layout = barrier.newLayout; + } + if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->depth.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->depth.image; + barrier.srcAccessMask = 0; + switch (fb->depth.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; } + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + // TODO: Double-check these flags. Should be fine. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->depth.layout = barrier.newLayout; + } + + renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; + // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); + if (step.render.color == RenderPassAction::CLEAR) { + Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); + numClearVals = 1; + } + if (step.render.depthStencil == RenderPassAction::CLEAR) { + clearVal[1].depthStencil.depth = step.render.clearDepth; + clearVal[1].depthStencil.stencil = step.render.clearStencil; + numClearVals = 2; + } + } else { + renderPass = GetSurfaceRenderPass(); + numClearVals = 2; + } + + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = renderPass; + rp_begin.framebuffer = framebuf; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = w; + rp_begin.renderArea.extent.height = h; + rp_begin.clearValueCount = numClearVals; + rp_begin.pClearValues = numClearVals ? clearVal : nullptr; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + curFramebuffer_ = framebuf; + curRenderPass_ = renderPass; + curWidth_ = w; + curHeight_ = h; +} + +void VulkanRenderManager::EndCurrentRenderpass(VkCommandBuffer cmd) { + if (curRenderPass_) { + vkCmdEndRenderPass(cmd); + curRenderPass_ = nullptr; + } +} + +void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { + VKRFramebuffer *src = step.copy.src; + VKRFramebuffer *dst = step.copy.dst; + + VkImageCopy copy{}; + copy.srcOffset.x = step.copy.srcRect.offset.x; + copy.srcOffset.y = step.copy.srcRect.offset.y; + copy.srcOffset.z = 0; + copy.srcSubresource.mipLevel = 0; + copy.srcSubresource.layerCount = 1; + copy.dstOffset.x = step.copy.dstPos.x; + copy.dstOffset.y = step.copy.dstPos.y; + copy.dstOffset.z = 0; + copy.dstSubresource.mipLevel = 0; + copy.dstSubresource.layerCount = 1; + copy.extent.width = step.copy.srcRect.extent.width; + copy.extent.height = step.copy.srcRect.extent.height; + copy.extent.depth = 1; + + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + int srcCount = 0; + int dstCount = 0; + + // First source barriers. + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + // TODO: Fix the pipe bits to be bit less conservative. + if (srcCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); + } + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + copy.srcSubresource.aspectMask = 0; + copy.dstSubresource.aspectMask = 0; + if (step.copy.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; } - delete renderPasses_[i]; + if (step.copy.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); } - renderPasses_.clear(); } -void VulkanRenderManager::Sync(VkCommandBuffer cmd) { +void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + + VKRFramebuffer *src = step.blit.src; + VKRFramebuffer *dst = step.blit.dst; + + int srcCount = 0; + int dstCount = 0; + + VkImageBlit blit{}; + blit.srcOffsets[0].x = step.blit.srcRect.offset.x; + blit.srcOffsets[0].y = step.blit.srcRect.offset.y; + blit.srcOffsets[0].z = 0; + blit.srcOffsets[1].x = step.blit.srcRect.offset.x + step.blit.srcRect.extent.width; + blit.srcOffsets[1].y = step.blit.srcRect.offset.y + step.blit.srcRect.extent.height; + blit.srcOffsets[1].z = 1; + blit.srcSubresource.mipLevel = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0].x = step.blit.dstRect.offset.x; + blit.dstOffsets[0].y = step.blit.dstRect.offset.y; + blit.dstOffsets[0].z = 0; + blit.dstOffsets[1].x = step.blit.dstRect.offset.x + step.blit.dstRect.extent.width; + blit.dstOffsets[1].y = step.blit.dstRect.offset.y + step.blit.dstRect.extent.height; + blit.dstOffsets[1].z = 1; + blit.dstSubresource.mipLevel = 0; + blit.dstSubresource.layerCount = 1; + + // First source barriers. + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + // TODO: Fix the pipe bits to be bit less conservative. + if (srcCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); + } + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + blit.srcSubresource.aspectMask = 0; + blit.dstSubresource.aspectMask = 0; + if (step.blit.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.blit.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, step.blit.filter); + } +} + +void VulkanRenderManager::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + img.layout = barrier.newLayout; +} + +void VulkanRenderManager::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + img.layout = barrier.newLayout; } diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index c4c59485ebc5..fbc8f0976c8d 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include "Common/Vulkan/VulkanContext.h" #include "math/dataconv.h" @@ -13,14 +15,14 @@ // The cool thing is that you can Flush on a different thread than you record the commands on! -enum VkRenderCmd : uint8_t { - VKR_STENCIL, - VKR_BLEND, - VKR_VIEWPORT, - VKR_SCISSOR, - VKR_CLEAR, - VKR_DRAW, - VKR_DRAW_INDEXED, +enum class VkRenderCmd : uint8_t { + STENCIL, + BLEND, + VIEWPORT, + SCISSOR, + CLEAR, + DRAW, + DRAW_INDEXED, }; struct VkRenderData { @@ -80,47 +82,160 @@ struct VkRenderData { }; }; -struct VKRRenderPass { - VkFramebuffer framebuffer; - uint32_t clearColor; - float clearZ; - int clearStencil; - int clearMask = 0; // VK_IMAGE_ASPECT_COLOR_BIT etc - int dontCareMask = 0; - int numDraws; +enum class VKStepType : uint8_t { + RENDER, + COPY, + BLIT, + READBACK, +}; + +class VKRFramebuffer; + +enum class RenderPassAction { + DONT_CARE, + CLEAR, + KEEP, +}; + +struct VKRStep { + VKRStep(VKStepType _type) : stepType(_type) {} + VKStepType stepType; std::vector commands; + union { + struct { + VKRFramebuffer *framebuffer; + RenderPassAction color; + RenderPassAction depthStencil; + uint32_t clearColor; + float clearDepth; + int clearStencil; + int numDraws; + VkImageLayout finalColorLayout; + } render; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkOffset2D dstPos; + int aspectMask; + } copy; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkRect2D dstRect; + int aspectMask; + VkFilter filter; + } blit; + struct { + VKRFramebuffer *src; + void *destPtr; + VkRect2D srcRect; + } readback; + }; +}; + +// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine +// to let them have individual non-pooled allocations. Until it's not fine. We'll see. +struct VKImage { + VkImage image; + VkImageView imageView; + VkDeviceMemory memory; + VkImageLayout layout; +}; +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); + +class VKRFramebuffer { +public: + VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height) : vulkan_(vk) { + width = _width; + height = _height; + + CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); + CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); + + VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + VkImageView views[2]{}; + + fbci.renderPass = renderPass; + fbci.attachmentCount = 2; + fbci.pAttachments = views; + views[0] = color.imageView; + views[1] = depth.imageView; + fbci.width = width; + fbci.height = height; + fbci.layers = 1; + + vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf); + } + ~VKRFramebuffer() { + vulkan_->Delete().QueueDeleteImage(color.image); + vulkan_->Delete().QueueDeleteImage(depth.image); + vulkan_->Delete().QueueDeleteImageView(color.imageView); + vulkan_->Delete().QueueDeleteImageView(depth.imageView); + vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); + vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); + + vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + } + + int numShadows = 1; // TODO: Support this. + + VkFramebuffer framebuf = VK_NULL_HANDLE; + VKImage color{}; + VKImage depth{}; + int width = 0; + int height = 0; + +private: + VulkanContext *vulkan_; }; class VulkanRenderManager { public: - VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) {} + VulkanRenderManager(VulkanContext *vulkan); + ~VulkanRenderManager(); + + void ThreadFunc(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrameWrites(); void EndFrame(); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb); + VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); + + void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint); + void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter); + + void BeginRenderPass(); + void SetViewport(const VkViewport &vp) { - VkRenderData data{ VKR_VIEWPORT }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::VIEWPORT }; data.viewport.vp = vp; - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } void SetScissor(const VkRect2D &rc) { - VkRenderData data{ VKR_SCISSOR }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::SCISSOR }; data.scissor.scissor = rc; - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } void SetBlendFactor(float color[4]) { - VkRenderData data{ VKR_BLEND }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::BLEND }; CopyFloat4(data.blendColor.color, color); - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { - VkRenderData data{ VKR_DRAW }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::DRAW }; data.draw.count = count; data.draw.pipeline = pipeline; data.draw.pipelineLayout = layout; @@ -130,12 +245,13 @@ class VulkanRenderManager { data.draw.numUboOffsets = numUboOffsets; for (int i = 0; i < numUboOffsets; i++) data.draw.uboOffsets[i] = uboOffsets[i]; - curRp_->commands.push_back(data); - curRp_->numDraws++; + curStep_->commands.push_back(data); + curStep_->render.numDraws++; } void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { - VkRenderData data{ VKR_DRAW }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; @@ -148,21 +264,103 @@ class VulkanRenderManager { for (int i = 0; i < numUboOffsets; i++) data.drawIndexed.uboOffsets[i] = uboOffsets[i]; data.drawIndexed.indexType = indexType; - curRp_->commands.push_back(data); - curRp_->numDraws++; + curStep_->commands.push_back(data); + curStep_->render.numDraws++; } // Can run on a different thread! Just make sure to use BeginFrameWrites. - void Flush(VkCommandBuffer cmd); + void Flush(); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). - void Sync(VkCommandBuffer cmd); + void Sync(); + + std::vector steps_; + std::vector stepsOnThread_; + std::mutex rpLock_; + + VKRStep *curStep_; - std::vector renderPasses_; - VKRRenderPass *curRp_; + VkCommandBuffer GetInitCmd(); + VkCommandBuffer GetSurfaceCommandBuffer() { + return frameData_[vulkan_->GetCurFrame()].mainCmd; + } + VkRenderPass GetSurfaceRenderPass() const { + return backbufferRenderPass_; + } + VkRenderPass GetRenderPass(int i) const { + return renderPasses_[i]; + } private: + void InitFramebuffers(); + void InitSurfaceRenderPass(); + void InitRenderpasses(); + void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. + + // The surface render pass is special because it has to acquire the backbuffer, and may thus "block". + // Use the returned command buffer to enqueue commands that render to the backbuffer. + // To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd). + void BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value); + // May eventually need the ability to break and resume the backbuffer render pass in a few rare cases. + void EndSurfaceRenderPass(VkCommandBuffer cmd); + + void EndCurrentRenderpass(VkCommandBuffer cmd); + + void 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); + + inline int RPIndex(RenderPassAction color, RenderPassAction depth) { + return (int)depth * 3 + (int)color; + } + + static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + + VkSemaphore acquireSemaphore_; + VkSemaphore renderingCompleteSemaphore; + + // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. + // TODO: Create these on demand. + VkRenderPass renderPasses_[9]; + + struct FrameData { + VkFence fence; + VkCommandPool cmdPool; + VkCommandBuffer initCmd; + VkCommandBuffer mainCmd; + bool hasInitCommands; + }; + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; + VulkanContext *vulkan_; int curWidth_; int curHeight_; -}; \ No newline at end of file + + std::thread submissionThread; + std::mutex mutex_; + std::condition_variable condVar_; + + struct SwapchainImageData { + VkImage image; + VkImageView view; + }; + std::vector framebuffers_; + std::vector swapchainImages_; + uint32_t swapchainImageCount; + uint32_t current_buffer = 0; + VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; + struct DepthBufferInfo { + VkFormat format; + VkImage image = VK_NULL_HANDLE; + VkDeviceMemory mem = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + }; + DepthBufferInfo depth_; + // Interpreter state + VkFramebuffer curFramebuffer_; + VkRenderPass curRenderPass_; + +}; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index c174e5898ee8..896067b97e43 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -94,7 +94,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra InitDeviceObjects(); - tessDataTransfer = new TessellationDataTransferVulkan(vulkan); + tessDataTransfer = new TessellationDataTransferVulkan(vulkan, draw); } void DrawEngineVulkan::InitDeviceObjects() { @@ -294,10 +294,11 @@ void DrawEngineVulkan::BeginFrame() { // TODO : Find a better place to do this. if (!nullTexture_) { + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); nullTexture_ = new VulkanTexture(vulkan_); int w = 8; int h = 8; - nullTexture_->CreateDirect(w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); uint32_t bindOffset; VkBuffer bindBuf; @@ -308,8 +309,8 @@ void DrawEngineVulkan::BeginFrame() { data[y*w + x] = 0; // black } } - nullTexture_->UploadMip(0, w, h, bindBuf, bindOffset, w); - nullTexture_->EndCreate(); + nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w); + nullTexture_->EndCreate(cmdInit); } DirtyAllUBOs(); @@ -643,13 +644,15 @@ void DrawEngineVulkan::DoFlush() { // TODO: Should be enough to update this once per frame? gpuStats.numTrackedVertexArrays = (int)vai_.size(); + /* VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); if (cmd != lastCmd_) { lastPipeline_ = nullptr; lastCmd_ = cmd; // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - } + }*/ + VkCommandBuffer cmd = VK_NULL_HANDLE; VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS); if (!rp) @@ -1087,11 +1090,12 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) { int rowPitch; + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position if (prevSize < size) { prevSize = size; - data_tex[0]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[0]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } pos = (float *)data_tex[0]->Lock(0, &rowPitch); @@ -1100,7 +1104,7 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po if (prevSizeTex < size) { prevSizeTex = size; - data_tex[1]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[1]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } tex = (float *)data_tex[1]->Lock(0, &rowPitch); } @@ -1110,19 +1114,20 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po if (prevSizeCol < sizeColor) { prevSizeCol = sizeColor; - data_tex[2]->CreateDirect(sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[2]->CreateDirect(cmd, sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } col = (float *)data_tex[2]->Lock(0, &rowPitch); } void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position - data_tex[0]->Unlock(); + data_tex[0]->Unlock(cmd); // Texcoords if (hasTexCoords) - data_tex[1]->Unlock(); + data_tex[1]->Unlock(cmd); // Color - data_tex[2]->Unlock(); + data_tex[2]->Unlock(cmd); } diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index a5387dfb9dbf..098a16a5f637 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -255,14 +255,15 @@ class DrawEngineVulkan : public DrawEngineCommon { // Hardware tessellation class TessellationDataTransferVulkan : public TessellationDataTransfer { private: - VulkanContext *vulkan; + VulkanContext *vulkan_; + Draw::DrawContext *draw_; VulkanTexture *data_tex[3]; VkSampler sampler; public: - TessellationDataTransferVulkan(VulkanContext *vulkan) - : TessellationDataTransfer(), vulkan(vulkan), data_tex(), sampler() { + TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) + : TessellationDataTransfer(), vulkan_(vulkan), draw_(draw), data_tex(), sampler() { for (int i = 0; i < 3; i++) - data_tex[i] = new VulkanTexture(vulkan); + data_tex[i] = new VulkanTexture(vulkan_); CreateSampler(); } @@ -270,7 +271,7 @@ class DrawEngineVulkan : public DrawEngineCommon { for (int i = 0; i < 3; i++) delete data_tex[i]; - vulkan->Delete().QueueDeleteSampler(sampler); + vulkan_->Delete().QueueDeleteSampler(sampler); } void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; void PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) override; @@ -301,7 +302,7 @@ class DrawEngineVulkan : public DrawEngineCommon { samp.minLod = 0.0f; samp.mipLodBias = 0.0f; - VkResult res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &sampler); + VkResult res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &sampler); assert(res == VK_SUCCESS); } }; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 335edcf44e8f..96e08b892395 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -28,6 +28,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanMemory.h" #include "Common/Vulkan/VulkanImage.h" +#include "Common/Vulkan/VulkanRenderManager.h" #include "Common/ColorConv.h" #include "Core/Host.h" #include "Core/MemMap.h" @@ -131,7 +132,7 @@ void FramebufferManagerVulkan::InitDeviceObjects() { assert(vsBasicTex_ != VK_NULL_HANDLE); // Prime the 2D pipeline cache. - vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_); + vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_); VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; @@ -234,13 +235,15 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor drawPixelsTex_ = nullptr; } + VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); + if (!drawPixelsTex_) { drawPixelsTex_ = new VulkanTexture(vulkan_); - drawPixelsTex_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); // Initialize backbuffer texture for DrawPixels drawPixelsTexFormat_ = srcPixelFormat; } else { - drawPixelsTex_->TransitionForUpload(); + drawPixelsTex_->TransitionForUpload(initCmd); } // TODO: We can just change the texture format and flip some bits around instead of this. @@ -297,25 +300,22 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor VkBuffer buffer; size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer); - drawPixelsTex_->UploadMip(0, width, height, buffer, (uint32_t)offset, width); - drawPixelsTex_->EndCreate(); + drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width); + drawPixelsTex_->EndCreate(initCmd); overrideImageView_ = drawPixelsTex_->GetImageView(); } void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) { - VkViewport vp; - vp.minDepth = 0.0; - vp.maxDepth = 1.0; - vp.x = (float)x; - vp.y = (float)y; - vp.width = (float)w; - vp.height = (float)h; - + Draw::Viewport vp; + vp.MinDepth = 0.0; + vp.MaxDepth = 1.0; + vp.TopLeftX = (float)x; + vp.TopLeftY = (float)y; + vp.Width = (float)w; + vp.Height = (float)h; // Since we're about to override it. - draw_->FlushState(); - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - vkCmdSetViewport(cmd, 0, 1, &vp); + draw_->SetViewports(1, &vp); } void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { @@ -359,18 +359,15 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa // TODO: Should probably use draw_ directly and not go low level VulkanPushBuffer *push = frameData_[curFrame_].push_; - - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); if ((flags & DRAWTEX_KEEP_TEX) == 0) overrideImageView_ = VK_NULL_HANDLE; - vulkan2D_.BindDescriptorSet(cmd, view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cur2DPipeline_); + VkDescriptorSet descSet = vulkan2D_.GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer); - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset); - vkCmdDraw(cmd, 4, 1, 0, 0); + renderManager->Draw(cur2DPipeline_, vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } void FramebufferManagerVulkan::Bind2DShader() { @@ -645,18 +642,6 @@ void FramebufferManagerVulkan::BeginFrameVulkan() { frame.push_->Reset(); frame.push_->Begin(vulkan_); - - if (!useBufferedRendering_) { - // TODO: This hackery should not be necessary. Is it? Need to check. - // We only use a single command buffer in this case. - VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer(); - VkRect2D scissor; - scissor.offset = { 0, 0 }; - scissor.extent = { (uint32_t)pixelWidth_, (uint32_t)pixelHeight_ }; - vkCmdSetScissor(cmd, 0, 1, &scissor); - } else { - // Each render pass will set up scissor again. - } } void FramebufferManagerVulkan::EndFrame() { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 9cea56729b73..2ae17c63b1ab 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -466,6 +466,7 @@ VkFormat ToVulkanFormat(ReplacedTextureFormat fmt) { void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceImages) { entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // For the estimate, we assume cluts always point to 8888 for simplicity. cacheSizeEstimate_ += EstimateTexMemoryUsage(entry); @@ -588,7 +589,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm break; } - bool allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -607,7 +608,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) { @@ -615,7 +616,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm entry->vkTex = nullptr; } } else { - entry->vkTex->texture_->TransitionForUpload(); + entry->vkTex->texture_->TransitionForUpload(cmdInit); } lastBoundTexture = entry->vkTex; @@ -652,7 +653,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm } else { if (fakeMipmap) { LoadTextureLevel(*entry, (uint8_t *)data, stride, level, scaleFactor, dstFmt); - entry->vkTex->texture_->UploadMip(0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->texture_->UploadMip(cmdInit, 0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); break; } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); @@ -661,7 +662,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight); } } - entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->texture_->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); } if (maxLevel == 0) { @@ -674,7 +675,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm } } - entry->vkTex->texture_->EndCreate(); + entry->vkTex->texture_->EndCreate(cmdInit); gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); gstate_c.SetTextureSimpleAlpha(entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN); diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index b9d8e905e928..576315ff9b35 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -27,11 +27,11 @@ VulkanFBO::~VulkanFBO() { delete depthStencil_; } -void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { +void VulkanFBO::Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { color_ = new VulkanTexture(vulkan); VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - color_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); - depthStencil_->CreateDirect(width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); + color_->CreateDirect(cmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); + depthStencil_->CreateDirect(cmd, width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() }; @@ -334,11 +334,6 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade } } -void Vulkan2D::BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1) { - VkDescriptorSet descSet = GetDescriptorSet(tex1, sampler1, VK_NULL_HANDLE, VK_NULL_HANDLE); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 0, nullptr); -} - VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error) { std::vector spirv; bool success = GLSLtoSPV(stage, code, spirv, error); diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 630bebd05f03..fefb35544b29 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -54,7 +54,7 @@ class VulkanFBO { // Depth-format is chosen automatically depending on hardware support. // Color format will be 32-bit RGBA. - void Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); + void Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); VulkanTexture *GetColor() { return color_; } VulkanTexture *GetDepthStencil() { return depthStencil_; } @@ -84,15 +84,12 @@ class Vulkan2D { void Shutdown(); VkPipeline GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); - + VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void BeginFrame(); void EndFrame(); VkDescriptorSet GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2); - // Simple way - void BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1); - struct Vertex { float x, y, z; float u, v; diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index e326e11c5423..ef322d90e232 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -189,7 +189,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); } g_Vulkan->InitSurfaceWin32(hInst, hWnd); - if (!g_Vulkan->InitObjects(true)) { + if (!g_Vulkan->InitObjects()) { Shutdown(); return false; } @@ -219,10 +219,12 @@ void WindowsVulkanContext::SwapBuffers() { void WindowsVulkanContext::Resize() { g_Vulkan->WaitUntilQueueIdle(); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); g_Vulkan->ReinitSurfaceWin32(); - g_Vulkan->InitObjects(true); + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void WindowsVulkanContext::SwapInterval(int interval) { diff --git a/ext/native/math/dataconv.h b/ext/native/math/dataconv.h index 2801a06a40fb..84e9da1a7e40 100644 --- a/ext/native/math/dataconv.h +++ b/ext/native/math/dataconv.h @@ -105,6 +105,17 @@ inline void CopyFloat3(float dest[3], const float src[3]) { dest[2] = src[2]; } +inline void CopyFloat4(float dest[3], const float src[3]) { +#ifdef _M_SSE + _mm_storeu_ps(dest, _mm_loadu_ps(src)); +#else + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +#endif +} + inline void CopyFloat1To4(float dest[4], const float src) { #ifdef _M_SSE _mm_storeu_ps(dest, _mm_set_ss(src)); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index dcbe7461600e..5e18ad99270d 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -321,9 +321,10 @@ enum class NativeObject { BACKBUFFER_COLOR_TEX, BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, + BACKBUFFER_RENDERPASS, COMPATIBLE_RENDERPASS, CURRENT_RENDERPASS, - RENDERPASS_COMMANDBUFFER, + INIT_COMMANDBUFFER, BOUND_TEXTURE_IMAGEVIEW, RENDER_MANAGER, }; @@ -678,7 +679,7 @@ class DrawContext { } virtual std::string GetInfoString(InfoField info) const = 0; - virtual uintptr_t GetNativeObject(NativeObject obj) const = 0; + virtual uintptr_t GetNativeObject(NativeObject obj) = 0; virtual void HandleEvent(Event ev, int width, int height, void *param1 = nullptr, void *param2 = nullptr) = 0; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 554bdca80bea..e7ca762369c3 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -116,7 +116,7 @@ class D3D11DrawContext : public DrawContext { } } - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::DEVICE: return (uintptr_t)device_; diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index ad40c89a4435..118bd48fab58 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -529,7 +529,7 @@ class D3D9Context : public DrawContext { void DrawUP(const void *vdata, int vertexCount) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal); - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::CONTEXT: return (uintptr_t)d3d_; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 65dcdd4a332a..1334a26e2cfe 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -560,7 +560,7 @@ class OpenGLContext : public DrawContext { } } - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { return 0; } diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 6620921b436e..2d409843cd5d 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -319,9 +319,9 @@ struct DescriptorSetKey { class VKTexture : public Texture { public: - VKTexture(VulkanContext *vulkan, const TextureDesc &desc) + VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc) : vulkan_(vulkan), format_(desc.format), mipLevels_(desc.mipLevels) { - Create(desc); + Create(cmd, desc); } ~VKTexture() { @@ -331,9 +331,9 @@ class VKTexture : public Texture { VkImageView GetImageView() { return vkTex_->GetImageView(); } private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); + void SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); - bool Create(const TextureDesc &desc); + bool Create(VkCommandBuffer cmd, const TextureDesc &desc); void Destroy() { if (vkTex_) { @@ -351,15 +351,6 @@ class VKTexture : public Texture { DataFormat format_; }; -// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine -// to let them have individual non-pooled allocations. Until it's not fine. We'll see. -struct VKImage { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - VkImageLayout layout; -}; - class VKContext : public DrawContext { public: VKContext(VulkanContext *vulkan); @@ -434,7 +425,6 @@ class VKContext : public DrawContext { void EndFrame() override; void FlushState() override { - ApplyDynamicState(); } void WaitRenderCompletion(Framebuffer *fbo) override; @@ -460,18 +450,20 @@ class VKContext : public DrawContext { std::vector GetFeatureList() const override; std::vector GetExtensionList() const override; - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::COMPATIBLE_RENDERPASS: // Return a representative renderpass. - if (curRenderPass_ == vulkan_->GetSurfaceRenderPass()) + if (curRenderPass_ == renderManager_.GetSurfaceRenderPass()) return (uintptr_t)curRenderPass_; else - return (uintptr_t)renderPasses_[0]; + return (uintptr_t)renderManager_.GetRenderPass(0); + case NativeObject::BACKBUFFER_RENDERPASS: + return (uintptr_t)renderManager_.GetSurfaceRenderPass(); case NativeObject::CURRENT_RENDERPASS: return (uintptr_t)curRenderPass_; - case NativeObject::RENDERPASS_COMMANDBUFFER: - return (uintptr_t)cmd_; + case NativeObject::INIT_COMMANDBUFFER: + return (uintptr_t)renderManager_.GetInitCmd(); case NativeObject::BOUND_TEXTURE_IMAGEVIEW: return (uintptr_t)boundImageView_[0]; @@ -485,15 +477,6 @@ class VKContext : public DrawContext { void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; private: - void ApplyDynamicState(); - void DirtyDynamicState(); - - void EndCurrentRenderpass(); - VkCommandBuffer AllocCmdBuf(); - - static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - VulkanContext *vulkan_ = nullptr; VulkanRenderManager renderManager_; @@ -508,23 +491,10 @@ class VKContext : public DrawContext { VkPipelineLayout pipelineLayout_; VkPipelineCache pipelineCache_; - inline int RPIndex(RPAction color, RPAction depth) { - return (int)depth * 3 + (int)color; - } - - // Renderpasses, all combination of preserving or clearing or dont-care-ing fb contents. - VkRenderPass renderPasses_[9]; - VkDevice device_; VkQueue queue_; int queueFamilyIndex_; - // State to apply at the next draw call if viewportDirty or scissorDirty are true. - bool viewportDirty_; - VkViewport viewport_{}; - bool scissorDirty_; - VkRect2D scissor_; - int curWidth_ = -1; int curHeight_ = -1; @@ -538,11 +508,6 @@ class VKContext : public DrawContext { struct FrameData { VulkanPushBuffer *pushBuffer; - VkCommandPool cmdPool_; - VkCommandBuffer cmdBufs[MAX_FRAME_COMMAND_BUFFERS]; - int startCmdBufs_; - int numCmdBufs; - // Per-frame descriptor set cache. As it's per frame and reset every frame, we don't need to // worry about invalidating descriptors pointing to deleted textures. // However! ARM is not a fan of doing it this way. @@ -559,7 +524,6 @@ class VKContext : public DrawContext { VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; VkRenderPass curRenderPass_ = VK_NULL_HANDLE; - VkCommandBuffer cmd_ = VK_NULL_HANDLE; }; static int GetBpp(VkFormat format) { @@ -683,7 +647,7 @@ enum class TextureState { PENDING_DESTRUCTION, }; -bool VKTexture::Create(const TextureDesc &desc) { +bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) { // Zero-sized textures not allowed. if (desc.width * desc.height * desc.depth == 0) return false; @@ -695,14 +659,14 @@ bool VKTexture::Create(const TextureDesc &desc) { vkTex_ = new VulkanTexture(vulkan_); if (desc.initData.size()) { for (int i = 0; i < (int)desc.initData.size(); i++) { - this->SetImageData(0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); + this->SetImageData(cmd, 0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); } } return true; } VKContext::VKContext(VulkanContext *vulkan) - : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { + : vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; @@ -715,16 +679,6 @@ VKContext::VKContext(VulkanContext *vulkan) queue_ = vulkan->GetGraphicsQueue(); queueFamilyIndex_ = vulkan->GetGraphicsQueueFamilyIndex(); - scissor_.offset.x = 0; - scissor_.offset.y = 0; - scissor_.extent.width = pixel_xres; - scissor_.extent.height = pixel_yres; - viewport_.x = 0; - viewport_.y = 0; - viewport_.width = pixel_xres; - viewport_.height = pixel_yres; - viewport_.minDepth = 0.0f; - viewport_.maxDepth = 0.0f; memset(boundTextures_, 0, sizeof(boundTextures_)); VkDescriptorPoolSize dpTypes[2]; @@ -744,10 +698,6 @@ VKContext::VKContext(VulkanContext *vulkan) p.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex(); for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); - assert(VK_SUCCESS == res); - res = vkCreateCommandPool(device_, &p, nullptr, &frame_[i].cmdPool_); - assert(VK_SUCCESS == res); frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024); } @@ -780,125 +730,25 @@ VKContext::VKContext(VulkanContext *vulkan) assert(VK_SUCCESS == res); pipelineCache_ = vulkan_->CreatePipelineCache(); - - // Create a bunch of render pass objects, for normal rendering with a depth buffer, - // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. - VkAttachmentDescription attachments[2] = {}; - attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - 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_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].flags = 0; - - attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - 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 = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = NULL; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = NULL; - subpass.pDepthStencilAttachment = &depth_reference; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = NULL; - - VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp.attachmentCount = 2; - rp.pAttachments = attachments; - rp.subpassCount = 1; - rp.pSubpasses = &subpass; - rp.dependencyCount = 0; - rp.pDependencies = NULL; - - for (int depth = 0; depth < 3; depth++) { - switch ((RPAction)depth) { - case RPAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RPAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RPAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - for (int color = 0; color < 3; color++) { - switch ((RPAction)color) { - case RPAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RPAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RPAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RPAction)color, (RPAction)depth)]); - } - } } VKContext::~VKContext() { - for (int i = 0; i < 9; i++) { - vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); - } // This also destroys all descriptor sets. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].descSets_.clear(); vulkan_->Delete().QueueDeleteDescriptorPool(frame_[i].descriptorPool); frame_[i].pushBuffer->Destroy(vulkan_); delete frame_[i].pushBuffer; - vulkan_->Delete().QueueDeleteCommandPool(frame_[i].cmdPool_); } vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); } -// Effectively wiped every frame, just allocate new ones! -VkCommandBuffer VKContext::AllocCmdBuf() { - FrameData *frame = &frame_[frameNum_]; - - if (frame->numCmdBufs >= MAX_FRAME_COMMAND_BUFFERS) - Crash(); - - if (frame->cmdBufs[frame->numCmdBufs]) { - VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++]; - if (!cmdBuf) - Crash(); - return cmdBuf; - } - - VkCommandBufferAllocateInfo alloc{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - alloc.commandBufferCount = 1; - alloc.commandPool = frame->cmdPool_; - alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - VkResult result = vkAllocateCommandBuffers(vulkan_->GetDevice(), &alloc, &frame->cmdBufs[frame->numCmdBufs]); - assert(result == VK_SUCCESS); - VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++]; - if (!cmdBuf) - Crash(); - return cmdBuf; -} - void VKContext::BeginFrame() { renderManager_.BeginFrameWrites(); FrameData &frame = frame_[frameNum_]; - frame.startCmdBufs_ = 0; - frame.numCmdBufs = 0; - vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0); push_ = frame.pushBuffer; // OK, we now know that nothing is reading from this frame's data pushbuffer, @@ -909,10 +759,7 @@ void VKContext::BeginFrame() { VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); assert(result == VK_SUCCESS); - scissor_.extent.width = pixel_xres; - scissor_.extent.height = pixel_yres; - scissorDirty_ = true; - viewportDirty_ = true; + SetScissorRect(0, 0, pixel_xres, pixel_yres); } void VKContext::WaitRenderCompletion(Framebuffer *fbo) { @@ -920,31 +767,16 @@ void VKContext::WaitRenderCompletion(Framebuffer *fbo) { } void VKContext::EndFrame() { - EndCurrentRenderpass(); - renderManager_.Flush(cmd_); - - if (cmd_) - Crash(); - - // Cap off and submit all the command buffers we recorded during the frame. - FrameData &frame = frame_[frameNum_]; - for (int i = frame.startCmdBufs_; i < frame.numCmdBufs; i++) { - vkEndCommandBuffer(frame.cmdBufs[i]); - vulkan_->QueueBeforeSurfaceRender(frame.cmdBufs[i]); - } - frame.startCmdBufs_ = frame.numCmdBufs; - // Stop collecting data in the frame's data pushbuffer. push_->End(); + renderManager_.Flush(); renderManager_.EndFrame(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) frameNum_ = 0; push_ = nullptr; - - DirtyDynamicState(); } VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { @@ -1075,7 +907,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. info.layout = pipelineLayout_; info.subpass = 0; - info.renderPass = vulkan_->GetSurfaceRenderPass(); + info.renderPass = renderManager_.GetSurfaceRenderPass(); // OK, need to create a new pipeline. VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline); @@ -1092,22 +924,20 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } void VKContext::SetScissorRect(int left, int top, int width, int height) { - scissor_.offset.x = left; - scissor_.offset.y = top; - scissor_.extent.width = width; - scissor_.extent.height = height; - scissorDirty_ = true; + VkRect2D scissor{ {left, top}, {(uint32_t)width, (uint32_t)height} }; + renderManager_.SetScissor(scissor); } void VKContext::SetViewports(int count, Viewport *viewports) { if (count > 0) { - viewport_.x = viewports[0].TopLeftX; - viewport_.y = viewports[0].TopLeftY; - viewport_.width = viewports[0].Width; - viewport_.height = viewports[0].Height; - viewport_.minDepth = viewports[0].MinDepth; - viewport_.maxDepth = viewports[0].MaxDepth; - viewportDirty_ = true; + VkViewport viewport; + viewport.x = viewports[0].TopLeftX; + viewport.y = viewports[0].TopLeftY; + viewport.width = viewports[0].Width; + viewport.height = viewports[0].Height; + viewport.minDepth = viewports[0].MinDepth; + viewport.maxDepth = viewports[0].MaxDepth; + renderManager_.SetViewport(viewport); } } @@ -1115,22 +945,6 @@ void VKContext::SetBlendFactor(float color[4]) { renderManager_.SetBlendFactor(color); } -void VKContext::ApplyDynamicState() { - if (scissorDirty_) { - renderManager_.SetScissor(scissor_); - scissorDirty_ = false; - } - if (viewportDirty_) { - renderManager_.SetViewport(viewport_); - viewportDirty_ = false; - } -} - -void VKContext::DirtyDynamicState() { - scissorDirty_ = true; - viewportDirty_ = true; -} - InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { VKInputLayout *vl = new VKInputLayout(); vl->visc = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; @@ -1156,10 +970,10 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { } Texture *VKContext::CreateTexture(const TextureDesc &desc) { - return new VKTexture(vulkan_, desc); + return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc); } -void VKTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { +void VKTexture::SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { VkFormat vulkanFormat = DataFormatToVulkan(format_); if (stride == 0) { stride = width * (int)DataFormatSizeInBytes(format_); @@ -1172,7 +986,7 @@ void VKTexture::SetImageData(int x, int y, int z, int width, int height, int dep for (int y = 0; y < height; y++) { memcpy(dstData + rowPitch * y, data + stride * y, width * bytesPerPixel); } - vkTex_->Unlock(); + vkTex_->Unlock(cmd); } inline void CopySide(VkStencilOpState &dest, const StencilSide &src) { @@ -1297,8 +1111,6 @@ void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } void VKContext::Draw(int vertexCount, int offset) { - ApplyDynamicState(); - VKBuffer *vbuf = curVBuffers_[0]; VkBuffer vulkanVbuf; @@ -1312,8 +1124,6 @@ void VKContext::Draw(int vertexCount, int offset) { } void VKContext::DrawIndexed(int vertexCount, int offset) { - ApplyDynamicState(); - VKBuffer *ibuf = static_cast(curIBuffer_); VKBuffer *vbuf = static_cast(curVBuffers_[0]); @@ -1328,13 +1138,10 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { } void VKContext::DrawUP(const void *vdata, int vertexCount) { - ApplyDynamicState(); - VkBuffer vulkanVbuf, vulkanUBObuf; size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - VkBuffer buffers[1] = { vulkanVbuf }; VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); @@ -1445,585 +1252,82 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { } } -void CreateImage(VulkanContext *vulkan, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { - VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - ici.arrayLayers = 1; - ici.mipLevels = 1; - ici.extent.width = width; - ici.extent.height = height; - ici.extent.depth = 1; - ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - ici.imageType = VK_IMAGE_TYPE_2D; - ici.samples = VK_SAMPLE_COUNT_1_BIT; - ici.tiling = VK_IMAGE_TILING_OPTIMAL; - ici.format = format; - ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - if (color) { - ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - } else { - ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); - - // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers - - VkMemoryRequirements memreq; - vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq); - - VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - alloc.allocationSize = memreq.size; - vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex); - VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory); - assert(res == VK_SUCCESS); - res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); - assert(res == VK_SUCCESS); - - VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - - VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - ivci.format = ici.format; - ivci.image = img.image; - ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; - ivci.subresourceRange.aspectMask = aspects; - ivci.subresourceRange.layerCount = 1; - ivci.subresourceRange.levelCount = 1; - res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view); - assert(res == VK_SUCCESS); - - VkPipelineStageFlagBits dstStage; - VkAccessFlagBits dstAccessMask; - switch (initialLayout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - break; - } - - TransitionImageLayout2(vulkan->GetInitCommandBuffer(), img.image, aspects, - VK_IMAGE_LAYOUT_UNDEFINED, initialLayout, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, - 0, dstAccessMask); -} - // A VKFramebuffer is a VkFramebuffer (note caps difference) plus all the textures it owns. // It also has a reference to the command buffer that it was last rendered to with. // If it needs to be transitioned, and the frame number matches, use it, otherwise // use this frame's init command buffer. class VKFramebuffer : public Framebuffer { public: - VKFramebuffer(VulkanContext *vk) : vulkan_(vk) {} + VKFramebuffer(VulkanContext *vk, VKRFramebuffer *fb) : vulkan_(vk), buf_(fb) {} ~VKFramebuffer() { - vulkan_->Delete().QueueDeleteImage(color.image); - vulkan_->Delete().QueueDeleteImage(depth.image); - vulkan_->Delete().QueueDeleteImageView(color.view); - vulkan_->Delete().QueueDeleteImageView(depth.view); - vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); - vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); - vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + delete buf_; } - VkFramebuffer framebuf = VK_NULL_HANDLE; - VKImage color{}; - VKImage depth{}; - int width = 0; - int height = 0; - - // These belong together, see above. - VkCommandBuffer cmdBuf = VK_NULL_HANDLE; - int frameCount = 0; - + VKRFramebuffer *GetFB() const { return buf_; } private: + VKRFramebuffer *buf_; VulkanContext *vulkan_; }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { - VKFramebuffer *fb = new VKFramebuffer(vulkan_); - fb->width = desc.width; - fb->height = desc.height; - - CreateImage(vulkan_, fb->color, fb->width, fb->height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); - CreateImage(vulkan_, fb->depth, fb->width, fb->height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); - - VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - VkImageView views[2]{}; - - fbci.renderPass = renderPasses_[0]; - fbci.attachmentCount = 2; - fbci.pAttachments = views; - views[0] = fb->color.view; - views[1] = fb->depth.view; - fbci.width = fb->width; - fbci.height = fb->height; - fbci.layers = 1; - - vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &fb->framebuf); + VkCommandBuffer cmd = renderManager_.GetInitCmd(); + VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetRenderPass(0), desc.width, desc.height); + VKFramebuffer *fb = new VKFramebuffer(vulkan_, vkrfb); return fb; } void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { - // Can't copy during render passes. - EndCurrentRenderpass(); - VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - VkImageCopy copy{}; - copy.srcOffset.x = x; - copy.srcOffset.y = y; - copy.srcOffset.z = z; - copy.srcSubresource.mipLevel = level; - copy.srcSubresource.layerCount = 1; - copy.dstOffset.x = dstX; - copy.dstOffset.y = dstY; - copy.dstOffset.z = dstZ; - copy.dstSubresource.mipLevel = dstLevel; - copy.dstSubresource.layerCount = 1; - copy.extent.width = width; - copy.extent.height = height; - copy.extent.depth = depth; - - // We're gonna tack copies onto the src's command buffer, if it's from this frame. - // If from a previous frame, just do it in frame init. - VkCommandBuffer cmd = src->cmdBuf; - if (src->frameCount != frameNum_) { - // TODO: What about the case where dst->frameCount == frameNum_ here? - // That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest. - cmd = vulkan_->GetInitCommandBuffer(); - } - - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; - - // First source barriers. - if (channelBits & FB_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - // TODO: Fix the pipe bits to be bit less conservative. - if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (channelBits & FB_COLOR_BIT) { - copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); - } - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - copy.srcSubresource.aspectMask = 0; - copy.dstSubresource.aspectMask = 0; - if (channelBits & FB_DEPTH_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (channelBits & FB_STENCIL_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); - } + renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }); } bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - // We're gonna tack blits onto the src's command buffer, if it's from this frame. - // If from a previous frame, just do it in frame init. - VkCommandBuffer cmd = src->cmdBuf; - if (src->frameCount != frameNum_) { - // TODO: What about the case where dst->frameCount == frameNum_ here? - // That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest. - cmd = vulkan_->GetInitCommandBuffer(); - } - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; - - VkImageBlit blit{}; - blit.srcOffsets[0].x = srcX1; - blit.srcOffsets[0].y = srcY1; - blit.srcOffsets[0].z = 0; - blit.srcOffsets[1].x = srcX2; - blit.srcOffsets[1].y = srcY2; - blit.srcOffsets[1].z = 1; - blit.srcSubresource.mipLevel = 0; - blit.srcSubresource.layerCount = 1; - blit.dstOffsets[0].x = dstX1; - blit.dstOffsets[0].y = dstY1; - blit.dstOffsets[0].z = 0; - blit.dstOffsets[1].x = dstX2; - blit.dstOffsets[1].y = dstY2; - blit.dstOffsets[1].z = 1; - blit.dstSubresource.mipLevel = 0; - blit.dstSubresource.layerCount = 1; - - // First source barriers. - if (channelBits & FB_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - // TODO: Fix the pipe bits to be bit less conservative. - if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (channelBits & FB_COLOR_BIT) { - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); - } - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - blit.srcSubresource.aspectMask = 0; - blit.dstSubresource.aspectMask = 0; - if (channelBits & FB_DEPTH_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (channelBits & FB_STENCIL_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); - } + renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); return true; } -void VKContext::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - img.layout = barrier.newLayout; -} - -void VKContext::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - img.layout = barrier.newLayout; -} - -void VKContext::EndCurrentRenderpass() { - if (curRenderPass_ != VK_NULL_HANDLE) { - vkCmdEndRenderPass(cmd_); - curRenderPass_ = VK_NULL_HANDLE; - curFramebuffer_ = VK_NULL_HANDLE; - cmd_ = VK_NULL_HANDLE; - } -} - void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { - VkFramebuffer framebuf; - VkCommandBuffer cmdBuf; - int w; - int h; - VkImageLayout prevLayout; - if (fbo) { - VKFramebuffer *fb = (VKFramebuffer *)fbo; - framebuf = fb->framebuf; - w = fb->width; - h = fb->height; - prevLayout = fb->color.layout; - cmdBuf = fb->cmdBuf; - } else { - framebuf = vulkan_->GetSurfaceFramebuffer(); - w = vulkan_->GetWidth(); - h = vulkan_->GetHeight(); - cmdBuf = vulkan_->GetSurfaceCommandBuffer(); - } - - if (framebuf == curFramebuffer_) { - if (framebuf == 0) - Crash(); - if (!curRenderPass_) - Crash(); - // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. - // We will try to avoid this as much as possible. - VkClearAttachment clear[2]{}; - int count = 0; - if (rp.color == RPAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - Uint8x4ToFloat4(rp.clearColor, clear[count].clearValue.color.float32); - clear[count].colorAttachment = 0; - count++; - } - if (rp.depth == RPAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - clear[count].clearValue.depthStencil.depth = rp.clearDepth; - clear[count].clearValue.depthStencil.stencil = rp.clearStencil; - clear[count].colorAttachment = 0; - count++; - } - if (count > 0) { - VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 }; - vkCmdClearAttachments(cmdBuf, count, clear, 1, &rc); - } - // We're done. - return; - } - - // OK, we're switching framebuffers. - EndCurrentRenderpass(); - VkRenderPass renderPass; - int numClearVals = 0; - VkClearValue clearVal[2]; - memset(clearVal, 0, sizeof(clearVal)); - if (fbo) { - VKFramebuffer *fb = (VKFramebuffer *)fbo; - fb->cmdBuf = AllocCmdBuf(); - if (!fb->cmdBuf) - Crash(); - fb->frameCount = frameNum_; - cmd_ = fb->cmdBuf; - VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - VkResult res = vkBeginCommandBuffer(cmd_, &begin); - assert(res == VK_SUCCESS); - // Now, if the image needs transitioning, let's transition. - // The backbuffer does not, that's handled by VulkanContext. - if (fb->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkAccessFlagBits srcAccessMask; - VkPipelineStageFlagBits srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_UNDEFINED: - srcAccessMask = (VkAccessFlagBits)0; - srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - break; - default: - assert(0); - break; - } - TransitionImageLayout2(cmd_, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } - if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkAccessFlagBits srcAccessMask; - VkPipelineStageFlagBits srcStage; - switch (fb->depth.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_UNDEFINED: - srcAccessMask = (VkAccessFlagBits)0; - srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - break; - default: - assert(0); - break; - } - TransitionImageLayout2(cmd_, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); - fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } - - renderPass = renderPasses_[RPIndex(rp.color, rp.depth)]; - // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); - if (rp.color == RPAction::CLEAR) { - Uint8x4ToFloat4(rp.clearColor, clearVal[0].color.float32); - numClearVals = 1; - } - if (rp.depth == RPAction::CLEAR) { - clearVal[1].depthStencil.depth = rp.clearDepth; - clearVal[1].depthStencil.stencil = rp.clearStencil; - numClearVals = 2; - } - } else { - cmd_ = vulkan_->GetSurfaceCommandBuffer(); - renderPass = vulkan_->GetSurfaceRenderPass(); - numClearVals = 2; - } - - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = renderPass; - rp_begin.framebuffer = framebuf; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = w; - rp_begin.renderArea.extent.height = h; - rp_begin.clearValueCount = numClearVals; - rp_begin.pClearValues = numClearVals ? clearVal : nullptr; - vkCmdBeginRenderPass(cmd_, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - curFramebuffer_ = framebuf; - curRenderPass_ = renderPass; - curWidth_ = w; - curHeight_ = h; + VKFramebuffer *fb = (VKFramebuffer *)fbo; + renderManager_.BindFramebufferAsRenderTarget(fb->GetFB()); } // color must be 0, for now. void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - boundImageView_[0] = fb->color.view; - // If we already have the right layout, nothing else to do. - if (fb->color.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - return; - - VkCommandBuffer transitionCmdBuf; - if (fb->cmdBuf && fb->frameCount == frameNum_) { - // If the framebuffer has a "live" command buffer, we can directly use it to transition it for sampling. - transitionCmdBuf = fb->cmdBuf; - } else { - // If not, we can just do it at the "start" of the frame. - transitionCmdBuf = vulkan_->GetInitCommandBuffer(); - } - VkAccessFlags srcAccessMask; - VkPipelineStageFlags srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - } - TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - srcAccessMask, VK_ACCESS_SHADER_READ_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + int aspect = 0; + if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; + renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { - // TODO: Insert transition at the end of the previous command buffer, or the one that rendered to it last. - VKFramebuffer *fb = (VKFramebuffer *)fbo; - return (uintptr_t)fb->color.image; + return 0; } void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { VKFramebuffer *fb = (VKFramebuffer *)fbo; if (fb) { - *w = fb->width; - *h = fb->height; + *w = fb->GetFB()->width; + *h = fb->GetFB()->height; } else { - *w = vulkan_->GetWidth(); - *h = vulkan_->GetHeight(); + *w = vulkan_->GetBackbufferWidth(); + *h = vulkan_->GetBackbufferHeight(); } } void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { + switch (ev) { + case Event::LOST_BACKBUFFER: + break; + case Event::GOT_BACKBUFFER: + break; + } // Noop } From 833916a9069804607446a6523a983bdbf54607d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 12:55:30 +0200 Subject: [PATCH 04/28] A multitude of fixes. The UI now works correctly, though of course ingame still broken. --- Common/Vulkan/VulkanMemory.cpp | 2 +- Common/Vulkan/VulkanRenderManager.cpp | 327 +++++++++++++------------- Common/Vulkan/VulkanRenderManager.h | 112 ++++----- Windows/GPU/WindowsVulkanContext.cpp | 6 +- ext/native/thin3d/thin3d_vulkan.cpp | 28 ++- 5 files changed, 242 insertions(+), 233 deletions(-) diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index 03983c62a6b1..260be7d9d69b 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -47,9 +47,9 @@ bool VulkanPushBuffer::AddBuffer() { return false; } + // Make validation happy. VkMemoryRequirements reqs; vkGetBufferMemoryRequirements(device_, info.buffer, &reqs); - // TODO: We really should use memoryTypeIndex here.. // Okay, that's the buffer. Now let's allocate some memory for it. diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index 734ae34ac8de..eab92174aa7b 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -3,7 +3,7 @@ #include "VulkanRenderManager.h" #include "VulkanContext.h" -void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; ici.arrayLayers = 1; ici.mipLevels = 1; @@ -74,10 +74,17 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int w } VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { + VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + semaphoreCreateInfo.flags = 0; + VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); + assert(res == VK_SUCCESS); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); + assert(res == VK_SUCCESS); + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPool); assert(res == VK_SUCCESS); @@ -93,23 +100,23 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan frameData_[i].initCmd = cmdBuf[1]; frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } +} - VkSwapchainKHR swapChain = vulkan_->GetSwapchain(); - VkResult res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, nullptr); +void VulkanRenderManager::CreateBackbuffers() { + VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr); assert(res == VK_SUCCESS); - ILOG("Vulkan swapchain image count: %d", swapchainImageCount); + ILOG("Vulkan swapchain image count: %d", swapchainImageCount_); - VkImage* swapchainImages = new VkImage[swapchainImageCount]; - assert(swapchainImages); - res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, swapchainImages); + VkImage* swapchainImages = new VkImage[swapchainImageCount_]; + res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages); assert(res == VK_SUCCESS); - VkCommandBuffer cmdInit = frameData_[0].initCmd; - frameData_[0].hasInitCommands = true; + VkCommandBuffer cmdInit = GetInitCmd(); - for (uint32_t i = 0; i < swapchainImageCount; i++) { + for (uint32_t i = 0; i < swapchainImageCount_; i++) { SwapchainImageData sc_buffer; + sc_buffer.image = swapchainImages[i]; VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; color_image_view.format = vulkan_->GetSwapchainFormat(); @@ -124,8 +131,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan color_image_view.subresourceRange.layerCount = 1; color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; color_image_view.flags = 0; - - sc_buffer.image = swapchainImages[i]; + color_image_view.image = sc_buffer.image; // Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring // in image to render to will be to transition them away from that. @@ -135,33 +141,36 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - color_image_view.image = sc_buffer.image; - res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, NULL, &sc_buffer.view); swapchainImages_.push_back(sc_buffer); assert(res == VK_SUCCESS); } delete[] swapchainImages; - current_buffer = 0; - - VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - semaphoreCreateInfo.flags = 0; - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); - assert(res == VK_SUCCESS); - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); - assert(res == VK_SUCCESS); - - InitDepthStencilBuffer(cmdInit); - InitSurfaceRenderPass(); + current_buffer = -1; + + InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. + InitBackbufferRenderPass(); // Must be before InitFramebuffers. + InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + InitRenderpasses(); + curWidth_ = -1; + curHeight_ = -1; } -VulkanRenderManager::~VulkanRenderManager() { +void VulkanRenderManager::DestroyBackbuffers() { VkDevice device = vulkan_->GetDevice(); - for (uint32_t i = 0; i < swapchainImageCount; i++) { - vkDestroyImageView(device, swapchainImages_[i].view, nullptr); + for (uint32_t i = 0; i < swapchainImageCount_; i++) { + vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view); } + vulkan_->Delete().QueueDeleteImageView(depth_.view); + vulkan_->Delete().QueueDeleteImage(depth_.image); + vulkan_->Delete().QueueDeleteDeviceMemory(depth_.mem); swapchainImages_.clear(); +} + +VulkanRenderManager::~VulkanRenderManager() { + VkDevice device = vulkan_->GetDevice(); + vulkan_->WaitUntilQueueIdle(); vkDestroySemaphore(device, acquireSemaphore_, nullptr); vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -176,58 +185,64 @@ VulkanRenderManager::~VulkanRenderManager() { vkDestroyFramebuffer(device, framebuffers_[i], nullptr); } framebuffers_.clear(); - for (int i = 0; i < 9; i++) { - vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); + for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { + // vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); + vkDestroyRenderPass(device, renderPasses_[i], nullptr); } } +// TODO: Activate this code. void VulkanRenderManager::ThreadFunc() { while (true) { std::unique_lock lock(mutex_); condVar_.wait(lock); - if (steps_.size()) { - stepsOnThread_ = std::move(steps_); - } - // ... + Flush(); } } -void VulkanRenderManager::BeginFrameWrites() { - vulkan_->BeginFrame(); +void VulkanRenderManager::BeginFrame() { VkDevice device = vulkan_->GetDevice(); FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, ¤t_buffer); + assert(res == VK_SUCCESS); + // Make sure the very last command buffer from the frame before the previous has been fully executed. vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); - // Reset both command buffers in one fell swoop. - // Note that on the first frame, there might already be commands so don't reset in that case. - if (!frameData.hasInitCommands) { - vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPool, 0); - } + // Must be after the fence - this performs deletes. + vulkan_->BeginFrame(); - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + insideFrame_ = true; } VkCommandBuffer VulkanRenderManager::GetInitCmd() { - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - if (!frameData_->hasInitCommands) { + // assert(insideFrame_ || firstFrame_); + + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (!frameData.hasInitCommands) { VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin); + assert(res == VK_SUCCESS); + frameData.hasInitCommands = true; } return frameData_[vulkan_->GetCurFrame()].initCmd; } +// After flush. Should probably be part of it? void VulkanRenderManager::EndFrame() { + insideFrame_ = false; + FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + TransitionToPresent(frame.mainCmd, swapchainImages_[current_buffer].image); VkResult res = vkEndCommandBuffer(frame.mainCmd); @@ -243,10 +258,13 @@ void VulkanRenderManager::EndFrame() { vkEndCommandBuffer(frame.initCmd); cmdBufs.push_back(frame.initCmd); frame.hasInitCommands = false; + ILOG("Frame %d had init commands", vulkan_->GetCurFrame()); } cmdBufs.push_back(frame.mainCmd); + vulkan_->EndFrame(); + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; @@ -259,74 +277,63 @@ void VulkanRenderManager::EndFrame() { res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); assert(res == VK_SUCCESS); + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; - VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); present.pSwapchains = &swapchain; present.pImageIndices = ¤t_buffer; present.pWaitSemaphores = &renderingCompleteSemaphore; present.waitSemaphoreCount = 1; present.pResults = nullptr; - res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI // return codes assert(res == VK_SUCCESS); - - vulkan_->EndFrame(); } void VulkanRenderManager::Sync() { } -void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb) { +void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { + VKRStep *step = new VKRStep{ VKRStepType::RENDER }; // This is what queues up new passes, and can end previous ones. + step->render.framebuffer = fb; + step->render.color = color; + step->render.depthStencil = depth; + step->render.clearColor = clearColor; + step->render.clearDepth = clearDepth; + step->render.clearStencil = clearStencil; + steps_.push_back(step); + curRenderStep_ = step; + curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth(); + curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); } - -void VulkanRenderManager::BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value) { - FrameData &frame = frameData_[vulkan_->GetCurFrame()]; - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = backbufferRenderPass_; - rp_begin.framebuffer = framebuffers_[current_buffer]; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = curWidth_; - rp_begin.renderArea.extent.height = curHeight_; - rp_begin.clearValueCount = 1; - rp_begin.pClearValues = &clear_value; - vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); -} - -void VulkanRenderManager::EndSurfaceRenderPass(VkCommandBuffer cmd) { - // ILOG("VulkanContext::EndSurfaceRenderPass"); - vkCmdEndRenderPass(cmd); -} - -void VulkanRenderManager::InitFramebuffers() { +void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { VkResult U_ASSERT_ONLY res; - VkImageView attachments[1]; + // We share the same depth buffer but have multiple color buffers, see the loop below. + VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; - ILOG("InitFramebuffers: %dx%d", curWidth_, curHeight_); + ILOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; fb_info.renderPass = backbufferRenderPass_; - fb_info.attachmentCount = 1; + fb_info.attachmentCount = 2; fb_info.pAttachments = attachments; - fb_info.width = curWidth_; - fb_info.height = curHeight_; + fb_info.width = width; + fb_info.height = height; fb_info.layers = 1; - framebuffers_.resize(swapchainImageCount); + framebuffers_.resize(swapchainImageCount_); - for (uint32_t i = 0; i < swapchainImageCount; i++) { + for (uint32_t i = 0; i < swapchainImageCount_; i++) { attachments[0] = swapchainImages_[i].view; res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]); assert(res == VK_SUCCESS); } } -void VulkanRenderManager::InitSurfaceRenderPass() { +void VulkanRenderManager::InitBackbufferRenderPass() { VkResult U_ASSERT_ONLY res; VkAttachmentDescription attachments[2]; @@ -340,6 +347,7 @@ void VulkanRenderManager::InitSurfaceRenderPass() { attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[0].flags = 0; + assert(depth_.format != VK_FORMAT_UNDEFINED); attachments[1].format = depth_.format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -392,8 +400,8 @@ void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = depth_format; - image_info.extent.width = curWidth_; - image_info.extent.height = curHeight_; + image_info.extent.width = vulkan_->GetBackbufferWidth(); + image_info.extent.height = vulkan_->GetBackbufferHeight(); image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; @@ -509,64 +517,70 @@ void VulkanRenderManager::InitRenderpasses() { rp.pDependencies = nullptr; for (int depth = 0; depth < 3; depth++) { - switch ((RenderPassAction)depth) { - case RenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + switch ((VKRRenderPassAction)depth) { + case VKRRenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case VKRRenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case VKRRenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } for (int color = 0; color < 3; color++) { - switch ((RenderPassAction)color) { - case RenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + switch ((VKRRenderPassAction)color) { + case VKRRenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RenderPassAction)color, (RenderPassAction)depth)]); + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth)]); } } } -void VulkanRenderManager::BeginRenderPass() { - std::unique_lock lock(rpLock_); - - VKRStep *pass = new VKRStep(VKStepType::RENDER); - pass->stepType = VKStepType::RENDER; - steps_.push_back(pass); - curStep_ = steps_.back(); -} - void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); // If this is the first drawing command, merge it into the pass. - if (curStep_->render.numDraws == 0) { - curStep_->render.clearColor = clearColor; - curStep_->render.clearDepth = clearZ; - curStep_->render.clearStencil = clearStencil; - curStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; - curStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; + if (curRenderStep_->render.numDraws == 0) { + curRenderStep_->render.clearColor = clearColor; + curRenderStep_->render.clearDepth = clearZ; + curRenderStep_->render.clearStencil = clearStencil; + curRenderStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; + curRenderStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; } else { - VkRenderData data{ VkRenderCmd::CLEAR }; + VkRenderData data{ VKRRenderCommand::CLEAR }; data.clear.clearColor = clearColor; data.clear.clearZ = clearZ; data.clear.clearStencil = clearStencil; data.clear.clearMask = clearMask; - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } } -void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint) { - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - EndCurrentRenderpass(frameData.mainCmd); +void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { + VKRStep *step = new VKRStep{ VKRStepType::COPY }; + step->copy.aspectMask = aspectMask; + step->copy.src = src; + step->copy.srcRect = srcRect; + step->copy.dst = dst; + step->copy.dstPos = dstPos; + std::unique_lock lock(mutex_); + steps_.push_back(step); + curRenderStep_ = nullptr; } -void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter) { - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - EndCurrentRenderpass(frameData.mainCmd); +void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; + step->blit.aspectMask = aspectMask; + step->blit.src = src; + step->blit.srcRect = srcRect; + step->blit.dst = dst; + step->blit.dstRect = dstRect; + step->blit.filter = filter; + std::unique_lock lock(mutex_); + steps_.push_back(step); + curRenderStep_ = nullptr; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { // Should just mark the dependency and return the image. for (int i = 0; i < (int)steps_.size() - 1; i++) { - if (steps_[i]->stepType == VKStepType::RENDER && steps_[i]->render.framebuffer == fb) { + if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; else @@ -598,37 +612,43 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { { - std::unique_lock lock(rpLock_); + std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); + curRenderStep_ = nullptr; } FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - VkCommandBuffer cmd = frameData.mainCmd; VkDevice device = vulkan_->GetDevice(); - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer); + VkCommandBuffer cmd = frameData.mainCmd; + + VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin.pInheritanceInfo = nullptr; + VkResult res = vkBeginCommandBuffer(cmd, &begin); + assert(res == VK_SUCCESS); + // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes + // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? assert(res == VK_SUCCESS); - TransitionFromPresent(frameData.mainCmd, swapchainImages_[current_buffer].image); + TransitionFromPresent(cmd, swapchainImages_[current_buffer].image); // Optimizes renderpasses, then sequences them. for (int i = 0; i < stepsOnThread_.size(); i++) { const VKRStep &step = *stepsOnThread_[i]; switch (step.stepType) { - case VKStepType::RENDER: + case VKRStepType::RENDER: PerformRenderPass(step, cmd); break; - case VKStepType::COPY: + case VKRStepType::COPY: PerformCopy(step, cmd); break; - case VKStepType::BLIT: + case VKRStepType::BLIT: PerformBlit(step, cmd); break; - case VKStepType::READBACK: + case VKRStepType::READBACK: // PerformReadback break; } @@ -642,25 +662,25 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { - case VkRenderCmd::VIEWPORT: + case VKRRenderCommand::VIEWPORT: vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); break; - case VkRenderCmd::SCISSOR: + case VKRRenderCommand::SCISSOR: vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); break; - case VkRenderCmd::BLEND: + case VKRRenderCommand::BLEND: vkCmdSetBlendConstants(cmd, c.blendColor.color); break; - case VkRenderCmd::STENCIL: + case VKRRenderCommand::STENCIL: vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); break; - case VkRenderCmd::DRAW_INDEXED: + case VKRRenderCommand::DRAW_INDEXED: vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); @@ -668,14 +688,14 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); break; - case VkRenderCmd::DRAW: + case VKRRenderCommand::DRAW: vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); vkCmdDraw(cmd, c.draw.count, 1, 0, 0); break; - case VkRenderCmd::CLEAR: + case VKRRenderCommand::CLEAR: { int numAttachments = 0; VkClearRect rc{}; @@ -735,21 +755,19 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st if (framebuf == curFramebuffer_) { if (framebuf == 0) Crash(); - if (!curRenderPass_) - Crash(); // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. // We will try to avoid this as much as possible. VkClearAttachment clear[2]{}; int count = 0; - if (step.render.color == RenderPassAction::CLEAR) { + if (step.render.color == VKRRenderPassAction::CLEAR) { clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; Uint8x4ToFloat4(clear[count].clearValue.color.float32, step.render.clearColor); clear[count].colorAttachment = 0; count++; } - if (step.render.depthStencil == RenderPassAction::CLEAR) { + if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; clear[count].clearValue.depthStencil.depth = step.render.clearDepth; clear[count].clearValue.depthStencil.stencil = step.render.clearStencil; @@ -765,13 +783,6 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st return; } - // OK, we're switching framebuffers. - if (curRenderPass_) { - vkCmdEndRenderPass(cmd); - curRenderPass_ = VK_NULL_HANDLE; - curFramebuffer_ = VK_NULL_HANDLE; - } - VkRenderPass renderPass; int numClearVals = 0; VkClearValue clearVal[2]; @@ -835,18 +846,20 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); - if (step.render.color == RenderPassAction::CLEAR) { + if (step.render.color == VKRRenderPassAction::CLEAR) { Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 1; } - if (step.render.depthStencil == RenderPassAction::CLEAR) { + if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { clearVal[1].depthStencil.depth = step.render.clearDepth; clearVal[1].depthStencil.stencil = step.render.clearStencil; numClearVals = 2; } } else { renderPass = GetSurfaceRenderPass(); - numClearVals = 2; + numClearVals = 2; // We don't bother with a depth buffer here. + clearVal[1].depthStencil.depth = 0.0f; + clearVal[1].depthStencil.stencil = 0; } VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; @@ -859,17 +872,9 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st rp_begin.clearValueCount = numClearVals; rp_begin.pClearValues = numClearVals ? clearVal : nullptr; vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - curFramebuffer_ = framebuf; - curRenderPass_ = renderPass; curWidth_ = w; curHeight_ = h; -} - -void VulkanRenderManager::EndCurrentRenderpass(VkCommandBuffer cmd) { - if (curRenderPass_) { - vkCmdEndRenderPass(cmd); - curRenderPass_ = nullptr; - } + curFramebuffer_ = framebuf; } void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { @@ -1020,7 +1025,7 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) } } -void VulkanRenderManager::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; @@ -1049,7 +1054,7 @@ void VulkanRenderManager::SetupTransitionToTransferSrc(VKImage &img, VkImageMemo img.layout = barrier.newLayout; } -void VulkanRenderManager::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index fbc8f0976c8d..490fea3e1bb0 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -15,7 +15,7 @@ // The cool thing is that you can Flush on a different thread than you record the commands on! -enum class VkRenderCmd : uint8_t { +enum class VKRRenderCommand : uint8_t { STENCIL, BLEND, VIEWPORT, @@ -26,7 +26,7 @@ enum class VkRenderCmd : uint8_t { }; struct VkRenderData { - VkRenderCmd cmd; + VKRRenderCommand cmd; union { struct { VkPipeline pipeline; @@ -82,7 +82,7 @@ struct VkRenderData { }; }; -enum class VKStepType : uint8_t { +enum class VKRStepType : uint8_t { RENDER, COPY, BLIT, @@ -91,21 +91,21 @@ enum class VKStepType : uint8_t { class VKRFramebuffer; -enum class RenderPassAction { +enum class VKRRenderPassAction { DONT_CARE, CLEAR, KEEP, }; struct VKRStep { - VKRStep(VKStepType _type) : stepType(_type) {} - VKStepType stepType; + VKRStep(VKRStepType _type) : stepType(_type) {} + VKRStepType stepType; std::vector commands; union { struct { VKRFramebuffer *framebuffer; - RenderPassAction color; - RenderPassAction depthStencil; + VKRRenderPassAction color; + VKRRenderPassAction depthStencil; uint32_t clearColor; float clearDepth; int clearStencil; @@ -137,13 +137,13 @@ struct VKRStep { // Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine // to let them have individual non-pooled allocations. Until it's not fine. We'll see. -struct VKImage { +struct VKRImage { VkImage image; VkImageView imageView; VkDeviceMemory memory; VkImageLayout layout; }; -void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); class VKRFramebuffer { public: @@ -175,15 +175,14 @@ class VKRFramebuffer { vulkan_->Delete().QueueDeleteImageView(depth.imageView); vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); - vulkan_->Delete().QueueDeleteFramebuffer(framebuf); } int numShadows = 1; // TODO: Support this. VkFramebuffer framebuf = VK_NULL_HANDLE; - VKImage color{}; - VKImage depth{}; + VKRImage color{}; + VKRImage depth{}; int width = 0; int height = 0; @@ -199,43 +198,41 @@ class VulkanRenderManager { void ThreadFunc(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. - void BeginFrameWrites(); + void BeginFrame(); void EndFrame(); - void BindFramebufferAsRenderTarget(VKRFramebuffer *fb); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); - void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint); - void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter); - - void BeginRenderPass(); + void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); + void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); void SetViewport(const VkViewport &vp) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::VIEWPORT }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::VIEWPORT }; data.viewport.vp = vp; - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } void SetScissor(const VkRect2D &rc) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::SCISSOR }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::SCISSOR }; data.scissor.scissor = rc; - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } void SetBlendFactor(float color[4]) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::BLEND }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::BLEND }; CopyFloat4(data.blendColor.color, color); - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::DRAW }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; data.draw.pipeline = pipeline; data.draw.pipelineLayout = layout; @@ -245,13 +242,13 @@ class VulkanRenderManager { data.draw.numUboOffsets = numUboOffsets; for (int i = 0; i < numUboOffsets; i++) data.draw.uboOffsets[i] = uboOffsets[i]; - curStep_->commands.push_back(data); - curStep_->render.numDraws++; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; } void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::DRAW_INDEXED }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; @@ -264,8 +261,8 @@ class VulkanRenderManager { for (int i = 0; i < numUboOffsets; i++) data.drawIndexed.uboOffsets[i] = uboOffsets[i]; data.drawIndexed.indexType = indexType; - curStep_->commands.push_back(data); - curStep_->render.numDraws++; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; } // Can run on a different thread! Just make sure to use BeginFrameWrites. @@ -274,12 +271,6 @@ class VulkanRenderManager { // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); - std::vector steps_; - std::vector stepsOnThread_; - std::mutex rpLock_; - - VKRStep *curStep_; - VkCommandBuffer GetInitCmd(); VkCommandBuffer GetSurfaceCommandBuffer() { return frameData_[vulkan_->GetCurFrame()].mainCmd; @@ -291,33 +282,27 @@ class VulkanRenderManager { return renderPasses_[i]; } + void CreateBackbuffers(); + void DestroyBackbuffers(); + private: - void InitFramebuffers(); - void InitSurfaceRenderPass(); + void InitBackbufferFramebuffers(int width, int height); + void InitBackbufferRenderPass(); void InitRenderpasses(); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. - // The surface render pass is special because it has to acquire the backbuffer, and may thus "block". - // Use the returned command buffer to enqueue commands that render to the backbuffer. - // To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd). - void BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value); - // May eventually need the ability to break and resume the backbuffer render pass in a few rare cases. - void EndSurfaceRenderPass(VkCommandBuffer cmd); - - void EndCurrentRenderpass(VkCommandBuffer cmd); - void 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); - inline int RPIndex(RenderPassAction color, RenderPassAction depth) { + inline int RPIndex(VKRRenderPassAction color, VKRRenderPassAction depth) { return (int)depth * 3 + (int)color; } - static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); VkSemaphore acquireSemaphore_; VkSemaphore renderingCompleteSemaphore; @@ -331,17 +316,21 @@ class VulkanRenderManager { VkCommandPool cmdPool; VkCommandBuffer initCmd; VkCommandBuffer mainCmd; - bool hasInitCommands; + bool hasInitCommands = false; }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; VulkanContext *vulkan_; int curWidth_; int curHeight_; + bool insideFrame_ = false; std::thread submissionThread; std::mutex mutex_; std::condition_variable condVar_; + std::vector steps_; + std::vector stepsOnThread_; + VKRStep *curRenderStep_; struct SwapchainImageData { VkImage image; @@ -349,18 +338,17 @@ class VulkanRenderManager { }; std::vector framebuffers_; std::vector swapchainImages_; - uint32_t swapchainImageCount; + uint32_t swapchainImageCount_; uint32_t current_buffer = 0; VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; struct DepthBufferInfo { - VkFormat format; + VkFormat format = VK_FORMAT_UNDEFINED; VkImage image = VK_NULL_HANDLE; VkDeviceMemory mem = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; }; DepthBufferInfo depth_; // Interpreter state - VkFramebuffer curFramebuffer_; - VkRenderPass curRenderPass_; - + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + // VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index ef322d90e232..6f87a00cd713 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -128,7 +128,6 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep return false; if (msgCode == 64) // Another useless perf warning that will be seen less and less as we optimize - vkCmdClearAttachments() issued on command buffer object 0x00000195296C6D40 prior to any Draw Cmds. It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw. return false; - #ifdef _WIN32 std::string msg = message.str(); OutputDebugStringA(msg.c_str()); @@ -197,10 +196,13 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); bool success = draw_->CreatePresets(); assert(success); // Doesn't fail, we include the compiler. - return success; + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + return true; } void WindowsVulkanContext::Shutdown() { + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + delete draw_; draw_ = nullptr; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 2d409843cd5d..bb5e9dc64b4d 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -699,6 +699,8 @@ VKContext::VKContext(VulkanContext *vulkan) for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024); + VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); + assert(res == VK_SUCCESS); } // binding 0 - uniform data @@ -746,7 +748,7 @@ VKContext::~VKContext() { } void VKContext::BeginFrame() { - renderManager_.BeginFrameWrites(); + renderManager_.BeginFrame(); FrameData &frame = frame_[frameNum_]; push_ = frame.pushBuffer; @@ -758,8 +760,6 @@ void VKContext::BeginFrame() { frame.descSets_.clear(); VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); assert(result == VK_SUCCESS); - - SetScissorRect(0, 0, pixel_xres, pixel_yres); } void VKContext::WaitRenderCompletion(Framebuffer *fbo) { @@ -1146,7 +1146,7 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, vbBindOffset, vertexCount); + renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. @@ -1279,20 +1279,32 @@ void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }); + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }, aspectMask); } bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, aspectMask, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); return true; } void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - renderManager_.BindFramebufferAsRenderTarget(fb->GetFB()); + VKRRenderPassAction color = (VKRRenderPassAction)rp.color; // same values. + VKRRenderPassAction depth = (VKRRenderPassAction)rp.color; // same values. + renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); } // color must be 0, for now. @@ -1324,8 +1336,10 @@ void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { switch (ev) { case Event::LOST_BACKBUFFER: + renderManager_.DestroyBackbuffers(); break; case Event::GOT_BACKBUFFER: + renderManager_.CreateBackbuffers(); break; } // Noop From 02f76ae4a8851aa1a1af57c533704dc5260bff86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 13:25:45 +0200 Subject: [PATCH 05/28] Rendering basics now works. --- Common/Common.vcxproj | 2 + Common/Common.vcxproj.filters | 16 +- Common/Vulkan/VulkanQueueRunner.cpp | 0 Common/Vulkan/VulkanQueueRunner.h | 6 + Common/Vulkan/VulkanRenderManager.cpp | 208 ++++++++++++++++++-------- Common/Vulkan/VulkanRenderManager.h | 53 +++++-- GPU/Common/FramebufferCommon.cpp | 2 +- GPU/Vulkan/DrawEngineVulkan.cpp | 70 ++++----- GPU/Vulkan/DrawEngineVulkan.h | 9 +- GPU/Vulkan/FramebufferVulkan.cpp | 4 +- GPU/Vulkan/StateMappingVulkan.cpp | 22 +-- GPU/Vulkan/StateMappingVulkan.h | 5 +- ext/native/thin3d/thin3d.h | 4 +- ext/native/thin3d/thin3d_vulkan.cpp | 28 ++-- 14 files changed, 257 insertions(+), 172 deletions(-) create mode 100644 Common/Vulkan/VulkanQueueRunner.cpp create mode 100644 Common/Vulkan/VulkanQueueRunner.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 58a8fac99a8c..07fc66510c72 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -249,6 +249,7 @@ + @@ -321,6 +322,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index c3ae5eea9e28..413f08cb8b29 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -75,7 +75,12 @@ - + + Vulkan + + + Vulkan + @@ -139,7 +144,12 @@ - + + Vulkan + + + Vulkan + @@ -155,4 +165,4 @@ {c14d66ef-5f7c-4565-975a-72774e7ccfb9} - + \ No newline at end of file diff --git a/Common/Vulkan/VulkanQueueRunner.cpp b/Common/Vulkan/VulkanQueueRunner.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Common/Vulkan/VulkanQueueRunner.h b/Common/Vulkan/VulkanQueueRunner.h new file mode 100644 index 000000000000..9da875a95c95 --- /dev/null +++ b/Common/Vulkan/VulkanQueueRunner.h @@ -0,0 +1,6 @@ +#pragma once + +class VulkanQueueRunner { +public: + +}; \ No newline at end of file diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index eab92174aa7b..a2e2d7fd1f1b 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -141,13 +141,12 @@ void VulkanRenderManager::CreateBackbuffers() { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - res = vkCreateImageView(vulkan_->GetDevice(), - &color_image_view, NULL, &sc_buffer.view); + res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, NULL, &sc_buffer.view); swapchainImages_.push_back(sc_buffer); assert(res == VK_SUCCESS); } delete[] swapchainImages; - current_buffer = -1; + curSwapchainImage_ = -1; InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. InitBackbufferRenderPass(); // Must be before InitFramebuffers. @@ -186,7 +185,6 @@ VulkanRenderManager::~VulkanRenderManager() { } framebuffers_.clear(); for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { - // vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); vkDestroyRenderPass(device, renderPasses_[i], nullptr); } } @@ -201,14 +199,13 @@ void VulkanRenderManager::ThreadFunc() { } void VulkanRenderManager::BeginFrame() { - VkDevice device = vulkan_->GetDevice(); FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, ¤t_buffer); + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); assert(res == VK_SUCCESS); // Make sure the very last command buffer from the frame before the previous has been fully executed. @@ -243,7 +240,7 @@ void VulkanRenderManager::EndFrame() { FrameData &frame = frameData_[vulkan_->GetCurFrame()]; - TransitionToPresent(frame.mainCmd, swapchainImages_[current_buffer].image); + TransitionToPresent(frame.mainCmd, swapchainImages_[curSwapchainImage_].image); VkResult res = vkEndCommandBuffer(frame.mainCmd); assert(res == VK_SUCCESS); @@ -281,7 +278,7 @@ void VulkanRenderManager::EndFrame() { VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; present.pSwapchains = &swapchain; - present.pImageIndices = ¤t_buffer; + present.pImageIndices = &curSwapchainImage_; present.pWaitSemaphores = &renderingCompleteSemaphore; present.waitSemaphoreCount = 1; present.pResults = nullptr; @@ -296,6 +293,14 @@ void VulkanRenderManager::Sync() { } void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { + // Eliminate dupes. + if (steps_.size() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) { + if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) { + // We don't move to a new step, this bind was unnecessary. + return; + } + } + VKRStep *step = new VKRStep{ VKRStepType::RENDER }; // This is what queues up new passes, and can end previous ones. step->render.framebuffer = fb; @@ -304,7 +309,10 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR step->render.clearColor = clearColor; step->render.clearDepth = clearDepth; step->render.clearStencil = clearStencil; + step->render.numDraws = 0; + step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; steps_.push_back(step); + curRenderStep_ = step; curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth(); curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); @@ -579,34 +587,20 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { // Should just mark the dependency and return the image. - for (int i = 0; i < (int)steps_.size() - 1; i++) { + for (int i = (int)steps_.size() - 1; i >= 0; i--) { if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { - if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) + // If this framebuffer was rendered to earlier in this frame, make sure to pre-transition it to the correct layout. + if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - else - Crash(); // May need to shadow the framebuffer? + break; + } + else { + // May need to shadow the framebuffer if we re-order passes later. + } } } - /* - VkAccessFlags srcAccessMask; - VkPipelineStageFlags srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - } - TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - srcAccessMask, VK_ACCESS_SHADER_READ_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - */ + curRenderStep_->preTransitions.push_back({ fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); return fb->color.imageView; } @@ -633,7 +627,7 @@ void VulkanRenderManager::Flush() { // return codes // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? assert(res == VK_SUCCESS); - TransitionFromPresent(cmd, swapchainImages_[current_buffer].image); + TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage_].image); // Optimizes renderpasses, then sequences them. for (int i = 0; i < stepsOnThread_.size(); i++) { @@ -658,8 +652,60 @@ void VulkanRenderManager::Flush() { } void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { + // TODO: If there are multiple, we can transition them together. + for (const auto &iter : step.preTransitions) { + if (iter.fb->color.layout != iter.targetLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = iter.fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = iter.fb->color.image; + barrier.srcAccessMask = 0; + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + barrier.newLayout = iter.targetLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + break; + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier(cmd, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + iter.fb->color.layout = barrier.newLayout; + } + } + PerformBindFramebufferAsRenderTarget(step, cmd); + + VKRFramebuffer *fb = step.render.framebuffer; + auto &commands = step.commands; + + // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only + // valid when a pipeline is bound with those as dynamic state. So we need to add some state tracking here + // for this to be correct. This is a bit of a pain but also will let us eliminate redundant calls. + for (const auto &c : commands) { switch (c.cmd) { case VKRRenderCommand::VIEWPORT: @@ -733,6 +779,41 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } vkCmdEndRenderPass(cmd); + + // Transition the framebuffer if requested. + if (fb && step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->color.image; + barrier.srcAccessMask = 0; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + default: + Crash(); + } + barrier.newLayout = step.render.finalColorLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + // we're between passes so it's OK. + // ARM Best Practices guide recommends these stage bits. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->color.layout = barrier.newLayout; + } } void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { @@ -747,7 +828,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st h = fb->height; prevLayout = fb->color.layout; } else { - framebuf = framebuffers_[current_buffer]; + framebuf = framebuffers_[curSwapchainImage_]; w = vulkan_->GetBackbufferWidth(); h = vulkan_->GetBackbufferHeight(); } @@ -792,56 +873,51 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st // Now, if the image needs transitioning, let's transition. // The backbuffer does not, that's handled by VulkanContext. if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; switch (fb->color.layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; } - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; + + TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->depth.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->depth.image; - barrier.srcAccessMask = 0; + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; switch (fb->depth.layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; } - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->depth.layout = barrier.newLayout; + TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); + fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; @@ -856,7 +932,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st numClearVals = 2; } } else { - renderPass = GetSurfaceRenderPass(); + renderPass = GetBackbufferRenderpass(); numClearVals = 2; // We don't bother with a depth buffer here. clearVal[1].depthStencil.depth = 0.0f; clearVal[1].depthStencil.stencil = 0; diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index 490fea3e1bb0..e46072a22321 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -97,10 +97,16 @@ enum class VKRRenderPassAction { KEEP, }; +struct TransitionRequest { + VKRFramebuffer *fb; + VkImageLayout targetLayout; +}; + struct VKRStep { VKRStep(VKRStepType _type) : stepType(_type) {} VKRStepType stepType; std::vector commands; + std::vector preTransitions; union { struct { VKRFramebuffer *framebuffer; @@ -221,6 +227,15 @@ class VulkanRenderManager { curRenderStep_->commands.push_back(data); } + void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::STENCIL }; + data.stencil.stencilWriteMask = writeMask; + data.stencil.stencilCompareMask = compareMask; + data.stencil.stencilRef = refValue; + curRenderStep_->commands.push_back(data); + } + void SetBlendFactor(float color[4]) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::BLEND }; @@ -230,7 +245,7 @@ class VulkanRenderManager { void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); - void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { + void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; @@ -246,10 +261,11 @@ class VulkanRenderManager { curRenderStep_->render.numDraws++; } - void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { + void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; + data.drawIndexed.instances = numInstances; data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; data.drawIndexed.ds = descSet; @@ -272,15 +288,19 @@ class VulkanRenderManager { void Sync(); VkCommandBuffer GetInitCmd(); - VkCommandBuffer GetSurfaceCommandBuffer() { - return frameData_[vulkan_->GetCurFrame()].mainCmd; - } - VkRenderPass GetSurfaceRenderPass() const { + VkRenderPass GetBackbufferRenderpass() const { return backbufferRenderPass_; } VkRenderPass GetRenderPass(int i) const { return renderPasses_[i]; } + VkRenderPass GetCompatibleRenderpass() const { + if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) { + return GetRenderPass(0); + } else { + return backbufferRenderPass_; + } + } void CreateBackbuffers(); void DestroyBackbuffers(); @@ -304,13 +324,15 @@ class VulkanRenderManager { static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + // Permanent objects VkSemaphore acquireSemaphore_; VkSemaphore renderingCompleteSemaphore; - + VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // TODO: Create these on demand. VkRenderPass renderPasses_[9]; + // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { VkFence fence; VkCommandPool cmdPool; @@ -320,18 +342,23 @@ class VulkanRenderManager { }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; - VulkanContext *vulkan_; + // Submission time state int curWidth_; int curHeight_; bool insideFrame_ = false; + VKRStep *curRenderStep_; + VKRFramebuffer *boundFramebuffer_; + std::vector steps_; + // Execution time state + VulkanContext *vulkan_; std::thread submissionThread; std::mutex mutex_; std::condition_variable condVar_; - std::vector steps_; std::vector stepsOnThread_; - VKRStep *curRenderStep_; + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + // Swap chain management struct SwapchainImageData { VkImage image; VkImageView view; @@ -339,8 +366,7 @@ class VulkanRenderManager { std::vector framebuffers_; std::vector swapchainImages_; uint32_t swapchainImageCount_; - uint32_t current_buffer = 0; - VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; + uint32_t curSwapchainImage_ = 0; struct DepthBufferInfo { VkFormat format = VK_FORMAT_UNDEFINED; VkImage image = VK_NULL_HANDLE; @@ -348,7 +374,4 @@ class VulkanRenderManager { VkImageView view = VK_NULL_HANDLE; }; DepthBufferInfo depth_; - // Interpreter state - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; - // VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 266d48a480b8..c10f7ac6535d 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -809,7 +809,7 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) { } void FramebufferManagerCommon::CopyDisplayToOutput() { - DownloadFramebufferOnSwitch(currentRenderVfb_); + // DownloadFramebufferOnSwitch(currentRenderVfb_); currentRenderVfb_ = 0; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 896067b97e43..692ce13ec2e3 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -23,6 +23,7 @@ #include "profiler/profiler.h" #include "Common/MemoryUtil.h" +#include "Common/Vulkan/VulkanRenderManager.h" #include "Core/MemMap.h" #include "Core/Host.h" #include "Core/System.h" @@ -275,7 +276,6 @@ void DrawEngineVulkan::DeviceRestore(VulkanContext *vulkan) { } void DrawEngineVulkan::BeginFrame() { - lastCmd_ = VK_NULL_HANDLE; lastPipeline_ = nullptr; FrameData *frame = &frame_[curFrame_]; @@ -644,19 +644,12 @@ void DrawEngineVulkan::DoFlush() { // TODO: Should be enough to update this once per frame? gpuStats.numTrackedVertexArrays = (int)vai_.size(); - /* - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - if (cmd != lastCmd_) { - lastPipeline_ = nullptr; - lastCmd_ = cmd; - // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - }*/ - VkCommandBuffer cmd = VK_NULL_HANDLE; - - VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS); - if (!rp) - Crash(); + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + // HACK: These two lines should only execute if we started on a new render pass. + lastPipeline_ = nullptr; + // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. + // gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); FrameData *frame = &frame_[curFrame_]; @@ -873,26 +866,28 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { + VulkanPipeline *pipeline = lastPipeline_; + if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); + Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); + pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); if (!pipeline) { // Already logged, let's bail out. return; } if (pipeline != lastPipeline_) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); } lastPipeline_ = pipeline; } - ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); + ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + lastPipeline_ = pipeline; } lastPrim_ = prim; @@ -902,28 +897,22 @@ void DrawEngineVulkan::DoFlush() { VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf); { - PROFILE_THIS_SCOPE("vkdraw"); + PROFILE_THIS_SCOPE("renderman_q"); const uint32_t dynamicUBOOffsets[3] = { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); + // vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); int stride = dec_->GetDecVtxFmt().stride; - VkDeviceSize offsets[1] = { vbOffset }; if (useElements) { if (!ibuf) ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); - // TODO (maybe): Avoid rebinding vertex/index buffers if the vertex size stays the same by using the offset arguments. - // Not sure if actually worth it, binding buffers should be fast. - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdBindIndexBuffer(cmd, ibuf, ibOffset, VK_INDEX_TYPE_UINT16); int numInstances = (gstate_c.bezier || gstate_c.spline) ? numPatches : 1; - vkCmdDrawIndexed(cmd, vertexCount, numInstances, 0, 0, 0); + renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); } else { - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdDraw(cmd, vertexCount, 1, 0, 0); + renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } } else { @@ -977,27 +966,28 @@ void DrawEngineVulkan::DoFlush() { if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } - + VulkanPipeline *pipeline = lastPipeline_; if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); + pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); if (!pipeline) { // Already logged, let's bail out. return; } if (pipeline != lastPipeline_) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw. if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); } lastPipeline_ = pipeline; } - ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); + ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + lastPipeline_ = pipeline; } lastPrim_ = prim; @@ -1011,25 +1001,19 @@ void DrawEngineVulkan::DoFlush() { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - PROFILE_THIS_SCOPE("vkdrawsoft"); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); + PROFILE_THIS_SCOPE("renderman_q"); if (drawIndexed) { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * numTrans, &ibuf); VkDeviceSize offsets[1] = { vbOffset }; - // TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdBindIndexBuffer(cmd, ibuf, ibOffset, VK_INDEX_TYPE_UINT16); - vkCmdDrawIndexed(cmd, numTrans, 1, 0, 0, 0); + renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vbuf); VkDeviceSize offsets[1] = { vbOffset }; - // TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdDraw(cmd, numTrans, 1, 0, 0); + renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); } } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 098a16a5f637..5ecd84ad618a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -113,6 +113,8 @@ class VertexArrayInfoVulkan { u8 flags = 0; }; +class VulkanRenderManager; + // Handles transform, lighting and drawing. class DrawEngineVulkan : public DrawEngineCommon { public: @@ -161,6 +163,10 @@ class DrawEngineVulkan : public DrawEngineCommon { void DirtyAllUBOs(); + void DirtyPipeline() { + lastPipeline_ = nullptr; + } + VulkanPushBuffer *GetPushBufferForTextureData() { return frame_[curFrame_].pushUBO; } @@ -171,7 +177,7 @@ class DrawEngineVulkan : public DrawEngineCommon { private: struct FrameData; - void ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); + void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void InitDeviceObjects(); @@ -191,7 +197,6 @@ class DrawEngineVulkan : public DrawEngineCommon { // We use a single descriptor set layout for all PSP draws. VkDescriptorSetLayout descriptorSetLayout_; VkPipelineLayout pipelineLayout_; - VkCommandBuffer lastCmd_ = VK_NULL_HANDLE; VulkanPipeline *lastPipeline_; VkDescriptorSet lastDs_ = VK_NULL_HANDLE; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 96e08b892395..3422f8a11752 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -132,8 +132,8 @@ void FramebufferManagerVulkan::InitDeviceObjects() { assert(vsBasicTex_ != VK_NULL_HANDLE); // Prime the 2D pipeline cache. - vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); - vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_); + // vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); + // vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index dc390ef2af23..3e919eec92df 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Vulkan/VulkanLoader.h" +#include "Common/Vulkan/VulkanRenderManager.h" #include "math/dataconv.h" #include "GPU/Math3D.h" @@ -369,7 +370,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } } -void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { +void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { // At this point, we know if the vertices are full alpha or not. // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { @@ -386,24 +387,15 @@ void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencil } if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { - vkCmdSetScissor(cmd, 0, 1, &dynState_.scissor); - vkCmdSetViewport(cmd, 0, 1, &dynState_.viewport); + renderManager->SetScissor(dynState_.scissor); + renderManager->SetViewport(dynState_.viewport); } - if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { - if (dynState_.useStencil) { - vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilWriteMask); - vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilCompareMask); - if (!applyStencilRef) { - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilRef); - } - } - } - if (applyStencilRef) { - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, stencilRef); + if ((gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE) && dynState_.useStencil) || applyStencilRef) { + renderManager->SetStencilParams(dynState_.stencilWriteMask, dynState_.stencilCompareMask, applyStencilRef ? stencilRef : dynState_.stencilRef); } if (gstate_c.IsDirty(DIRTY_BLEND_STATE) && useBlendConstant) { float bc[4]; Uint8x4ToFloat4(bc, dynState_.blendColor); - vkCmdSetBlendConstants(cmd, bc); + renderManager->SetBlendFactor(bc); } } diff --git a/GPU/Vulkan/StateMappingVulkan.h b/GPU/Vulkan/StateMappingVulkan.h index e4b67a3b57aa..f1d8013f69d8 100644 --- a/GPU/Vulkan/StateMappingVulkan.h +++ b/GPU/Vulkan/StateMappingVulkan.h @@ -57,7 +57,4 @@ struct VulkanPipelineRasterStateKey { size_t size = sizeof(VulkanPipelineRasterStateKey); return memcmp(this, &other, size) < 0; } -}; - -class ShaderManagerVulkan; -void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); +}; \ No newline at end of file diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 5e18ad99270d..fdb68a38493c 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -321,9 +321,9 @@ enum class NativeObject { BACKBUFFER_COLOR_TEX, BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, - BACKBUFFER_RENDERPASS, COMPATIBLE_RENDERPASS, - CURRENT_RENDERPASS, + BACKBUFFER_RENDERPASS, + FRAMEBUFFER_RENDERPASS, INIT_COMMANDBUFFER, BOUND_TEXTURE_IMAGEVIEW, RENDER_MANAGER, diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index bb5e9dc64b4d..967b27c85e12 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -452,16 +452,13 @@ class VKContext : public DrawContext { uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { - case NativeObject::COMPATIBLE_RENDERPASS: + case NativeObject::FRAMEBUFFER_RENDERPASS: // Return a representative renderpass. - if (curRenderPass_ == renderManager_.GetSurfaceRenderPass()) - return (uintptr_t)curRenderPass_; - else - return (uintptr_t)renderManager_.GetRenderPass(0); + return (uintptr_t)renderManager_.GetRenderPass(0); case NativeObject::BACKBUFFER_RENDERPASS: - return (uintptr_t)renderManager_.GetSurfaceRenderPass(); - case NativeObject::CURRENT_RENDERPASS: - return (uintptr_t)curRenderPass_; + return (uintptr_t)renderManager_.GetBackbufferRenderpass(); + case NativeObject::COMPATIBLE_RENDERPASS: + return (uintptr_t)renderManager_.GetCompatibleRenderpass(); case NativeObject::INIT_COMMANDBUFFER: return (uintptr_t)renderManager_.GetInitCmd(); case NativeObject::BOUND_TEXTURE_IMAGEVIEW: @@ -470,6 +467,7 @@ class VKContext : public DrawContext { case NativeObject::RENDER_MANAGER: return (uintptr_t)&renderManager_; default: + Crash(); return 0; } } @@ -521,9 +519,6 @@ class VKContext : public DrawContext { VulkanPushBuffer *push_ = nullptr; DeviceCaps caps_{}; - - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; - VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; static int GetBpp(VkFormat format) { @@ -907,7 +902,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. info.layout = pipelineLayout_; info.subpass = 0; - info.renderPass = renderManager_.GetSurfaceRenderPass(); + info.renderPass = renderManager_.GetBackbufferRenderpass(); // OK, need to create a new pipeline. VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline); @@ -1134,7 +1129,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, VK_INDEX_TYPE_UINT32); + renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1151,11 +1146,6 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { - if (!curRenderPass_) { - ELOG("Clear: Need an active render pass"); - return; - } - int mask = 0; if (clearMask & FBChannel::FB_COLOR_BIT) mask |= VK_IMAGE_ASPECT_COLOR_BIT; @@ -1315,7 +1305,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; - renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); + boundImageView_[0] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { From 1c5bd0f8dcea934806906430066888055a135183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 16:28:35 +0200 Subject: [PATCH 06/28] Move the VulkanRenderManager stuff to thin3d, fits in better there. --- CMakeLists.txt | 5 ++++- Common/Common.vcxproj | 4 ---- Common/Common.vcxproj.filters | 12 ------------ GPU/Vulkan/DrawEngineVulkan.cpp | 4 ++-- GPU/Vulkan/FramebufferVulkan.cpp | 2 +- GPU/Vulkan/StateMappingVulkan.cpp | 2 +- android/jni/app-android.cpp | 9 ++++++--- ext/native/native.vcxproj | 4 ++++ ext/native/native.vcxproj.filters | 12 ++++++++++++ ext/native/profiler/profiler.h | 3 ++- .../native/thin3d}/VulkanQueueRunner.cpp | 0 .../Vulkan => ext/native/thin3d}/VulkanQueueRunner.h | 0 .../native/thin3d}/VulkanRenderManager.cpp | 4 ++-- .../native/thin3d}/VulkanRenderManager.h | 0 ext/native/thin3d/thin3d_vulkan.cpp | 2 +- 15 files changed, 35 insertions(+), 28 deletions(-) rename {Common/Vulkan => ext/native/thin3d}/VulkanQueueRunner.cpp (100%) rename {Common/Vulkan => ext/native/thin3d}/VulkanQueueRunner.h (100%) rename {Common/Vulkan => ext/native/thin3d}/VulkanRenderManager.cpp (99%) rename {Common/Vulkan => ext/native/thin3d}/VulkanRenderManager.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cdbdff811b1..a3fc60adde86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -803,7 +803,10 @@ endif() set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp) if(VULKAN) - set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} ext/native/thin3d/thin3d_vulkan.cpp) + set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} + ext/native/thin3d/thin3d_vulkan.cpp + ext/native/thin3d/VulkanRenderManager.cpp + ext/native/thin3d/VulkanRenderManager.h) endif() if(WIN32) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 07fc66510c72..86758c422991 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -249,8 +249,6 @@ - - @@ -322,8 +320,6 @@ - - diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 413f08cb8b29..0251f2b33459 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -75,12 +75,6 @@ - - Vulkan - - - Vulkan - @@ -144,12 +138,6 @@ - - Vulkan - - - Vulkan - diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 692ce13ec2e3..0cc249cf9a85 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -21,9 +21,9 @@ #include "base/timeutil.h" #include "math/dataconv.h" #include "profiler/profiler.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/MemoryUtil.h" -#include "Common/Vulkan/VulkanRenderManager.h" #include "Core/MemMap.h" #include "Core/Host.h" #include "Core/System.h" @@ -646,7 +646,7 @@ void DrawEngineVulkan::DoFlush() { VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - // HACK: These two lines should only execute if we started on a new render pass. + // HACK: These two lines should only execute if we started on a new render pass. Can't tell from in here though... lastPipeline_ = nullptr; // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. // gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 3422f8a11752..bf33417a4a7b 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -28,7 +28,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanMemory.h" #include "Common/Vulkan/VulkanImage.h" -#include "Common/Vulkan/VulkanRenderManager.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/ColorConv.h" #include "Core/Host.h" #include "Core/MemMap.h" diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 3e919eec92df..3c170cc7842a 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -16,7 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Vulkan/VulkanLoader.h" -#include "Common/Vulkan/VulkanRenderManager.h" +#include "thin3d/VulkanRenderManager.h" #include "math/dataconv.h" #include "GPU/Math3D.h" diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 5ed28dbd46ef..e0e772d1c154 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -312,19 +312,20 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); } - g_Vulkan->InitObjects(true); + g_Vulkan->InitObjects(); draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); bool success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. assert(success); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); return true; } void AndroidVulkanContext::Shutdown() { ILOG("AndroidVulkanContext::Shutdown"); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); delete draw_; draw_ = nullptr; NativeShutdownGraphics(); - g_Vulkan->WaitUntilQueueIdle(); g_Vulkan->DestroyObjects(); g_Vulkan->DestroyDebugMsgCallback(); @@ -341,11 +342,13 @@ void AndroidVulkanContext::SwapBuffers() { void AndroidVulkanContext::Resize() { g_Vulkan->WaitUntilQueueIdle(); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); // backbufferResize updated these values. TODO: Notify another way? g_Vulkan->ReinitSurfaceAndroid(pixel_xres, pixel_yres); - g_Vulkan->InitObjects(g_Vulkan); + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void AndroidVulkanContext::SwapInterval(int interval) { diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index ea47f94dea4f..681a99b45338 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -240,6 +240,8 @@ + + @@ -697,6 +699,8 @@ + + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index e551ed8b0e7f..57aee62de401 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -320,6 +320,12 @@ gfx + + thin3d + + + thin3d + @@ -781,6 +787,12 @@ math + + thin3d + + + thin3d + diff --git a/ext/native/profiler/profiler.h b/ext/native/profiler/profiler.h index e4cd55d255be..f615e2e88ca1 100644 --- a/ext/native/profiler/profiler.h +++ b/ext/native/profiler/profiler.h @@ -2,6 +2,8 @@ #include +// NOTE : This profiler is very single-threaded. Cannot be used from multiple threads yet. + // #define USE_PROFILER #ifdef USE_PROFILER @@ -14,7 +16,6 @@ void internal_profiler_end_frame(); int internal_profiler_enter(const char *category_name); // Returns the category number. void internal_profiler_leave(int category); - const char *Profiler_GetCategoryName(int i); int Profiler_GetNumCategories(); int Profiler_GetHistoryLength(); diff --git a/Common/Vulkan/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp similarity index 100% rename from Common/Vulkan/VulkanQueueRunner.cpp rename to ext/native/thin3d/VulkanQueueRunner.cpp diff --git a/Common/Vulkan/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h similarity index 100% rename from Common/Vulkan/VulkanQueueRunner.h rename to ext/native/thin3d/VulkanQueueRunner.h diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp similarity index 99% rename from Common/Vulkan/VulkanRenderManager.cpp rename to ext/native/thin3d/VulkanRenderManager.cpp index a2e2d7fd1f1b..195083b35f5b 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -1,7 +1,7 @@ #include "base/logging.h" -#include "VulkanRenderManager.h" -#include "VulkanContext.h" +#include "Common/Vulkan/VulkanContext.h" +#include "thin3d/VulkanRenderManager.h" void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; diff --git a/Common/Vulkan/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h similarity index 100% rename from Common/Vulkan/VulkanRenderManager.h rename to ext/native/thin3d/VulkanRenderManager.h diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 967b27c85e12..3644db1a1263 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -30,11 +30,11 @@ #include "math/lin/matrix4x4.h" #include "math/dataconv.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" #include "Common/Vulkan/VulkanMemory.h" -#include "Common/Vulkan/VulkanRenderManager.h" // We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point. // binding 0 - uniform data From c788dc896abc1224a2a6872fff60d933eead5619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 17:18:54 +0200 Subject: [PATCH 07/28] Synchronization is HARD. need a rethink, methinks. --- Common/Vulkan/VulkanContext.cpp | 12 +-- ext/native/thin3d/VulkanRenderManager.cpp | 95 +++++++++++++++++------ ext/native/thin3d/VulkanRenderManager.h | 11 ++- ext/native/thin3d/thin3d_vulkan.cpp | 1 - 4 files changed, 85 insertions(+), 34 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 20ec8b0bd316..a49cd5882400 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -216,12 +216,6 @@ void VulkanContext::DestroyObjects() { vkDestroySwapchainKHR(device_, swapchain_, nullptr); swapchain_ = VK_NULL_HANDLE; - // If there happen to be any pending deletes, now is a good time. - for (int i = 0; i < ARRAY_SIZE(frame_); i++) { - frame_[i].deleteList.PerformDeletes(device_); - } - Delete().PerformDeletes(device_); - vkDestroySurfaceKHR(instance_, surface_, nullptr); surface_ = VK_NULL_HANDLE; } @@ -768,6 +762,12 @@ VkFence VulkanContext::CreateFence(bool presignalled) { } void VulkanContext::DestroyDevice() { + // If there happen to be any pending deletes, now is a good time. + for (int i = 0; i < ARRAY_SIZE(frame_); i++) { + frame_[i].deleteList.PerformDeletes(device_); + } + Delete().PerformDeletes(device_); + vkDestroyDevice(device_, nullptr); device_ = nullptr; } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 195083b35f5b..bedd901a3fe2 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -2,6 +2,9 @@ #include "Common/Vulkan/VulkanContext.h" #include "thin3d/VulkanRenderManager.h" +#include "thread/threadutil.h" + +const bool useThread = true; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; @@ -85,19 +88,21 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPool); + VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolInit); + assert(res == VK_SUCCESS); + res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolMain); assert(res == VK_SUCCESS); VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - cmd_alloc.commandPool = frameData_[i].cmdPool; + cmd_alloc.commandPool = frameData_[i].cmdPoolInit; cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_alloc.commandBufferCount = 2; + cmd_alloc.commandBufferCount = 1; - VkCommandBuffer cmdBuf[2]; - res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, cmdBuf); + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].initCmd); + assert(res == VK_SUCCESS); + cmd_alloc.commandPool = frameData_[i].cmdPoolMain; + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].mainCmd); assert(res == VK_SUCCESS); - frameData_[i].mainCmd = cmdBuf[0]; - frameData_[i].initCmd = cmdBuf[1]; frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } } @@ -154,9 +159,21 @@ void VulkanRenderManager::CreateBackbuffers() { InitRenderpasses(); curWidth_ = -1; curHeight_ = -1; + + // Start the thread. + if (useThread) { + run_ = true; + thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this); + } } void VulkanRenderManager::DestroyBackbuffers() { + // Stop the thread. + if (useThread) { + run_ = false; + condVar_.notify_all(); + thread_.join(); + } VkDevice device = vulkan_->GetDevice(); for (uint32_t i = 0; i < swapchainImageCount_; i++) { vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view); @@ -168,14 +185,15 @@ void VulkanRenderManager::DestroyBackbuffers() { } VulkanRenderManager::~VulkanRenderManager() { + run_ = false; VkDevice device = vulkan_->GetDevice(); vulkan_->WaitUntilQueueIdle(); vkDestroySemaphore(device, acquireSemaphore_, nullptr); vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; - vkFreeCommandBuffers(device, frameData_[i].cmdPool, 2, cmdBuf); - vkDestroyCommandPool(device, frameData_[i].cmdPool, nullptr); + vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); + vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); vkDestroyFence(device, frameData_[i].fence, nullptr); } if (backbufferRenderPass_ != VK_NULL_HANDLE) @@ -191,10 +209,15 @@ VulkanRenderManager::~VulkanRenderManager() { // TODO: Activate this code. void VulkanRenderManager::ThreadFunc() { - while (true) { + setCurrentThreadName("RenderMan"); + while (run_) { std::unique_lock lock(mutex_); condVar_.wait(lock); - Flush(); + if (frameAvailable_) { + Run(); + EndFrame(); + frameAvailable_ = false; + } } } @@ -203,12 +226,16 @@ void VulkanRenderManager::BeginFrame() { FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); - assert(res == VK_SUCCESS); - // Make sure the very last command buffer from the frame before the previous has been fully executed. + if (useThread) { + // Can't wait for this fence until it's actually been enqueued. + // Will replace this with a condvar if it works. + while (!frameData.readyForFence) { + ; + } + frameData.readyForFence = false; + } + vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); @@ -238,7 +265,7 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { void VulkanRenderManager::EndFrame() { insideFrame_ = false; - FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + FrameData &frame = frameData_[curFrame_]; TransitionToPresent(frame.mainCmd, swapchainImages_[curSwapchainImage_].image); @@ -255,13 +282,11 @@ void VulkanRenderManager::EndFrame() { vkEndCommandBuffer(frame.initCmd); cmdBufs.push_back(frame.initCmd); frame.hasInitCommands = false; - ILOG("Frame %d had init commands", vulkan_->GetCurFrame()); + ILOG("Frame %d had init commands", curFrame_); } cmdBufs.push_back(frame.mainCmd); - vulkan_->EndFrame(); - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; @@ -274,6 +299,10 @@ void VulkanRenderManager::EndFrame() { res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); assert(res == VK_SUCCESS); + if (useThread) { + frame.readyForFence = true; + } + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; @@ -605,22 +634,38 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Flush() { - { - std::unique_lock lock(mutex_); + curFrame_ = vulkan_->GetCurFrame(); + frameAvailable_ = true; + if (!useThread) { + Run(); + EndFrame(); + } else { + condVar_.notify_all(); + } + vulkan_->EndFrame(); +} + +void VulkanRenderManager::Run() { + //if ({ + // std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); curRenderStep_ = nullptr; - } - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + FrameData &frameData = frameData_[curFrame_]; VkDevice device = vulkan_->GetDevice(); + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); + assert(res == VK_SUCCESS); + VkCommandBuffer cmd = frameData.mainCmd; VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(cmd, &begin); + res = vkBeginCommandBuffer(cmd, &begin); assert(res == VK_SUCCESS); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index e46072a22321..ed3237a3fd9b 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -283,6 +283,7 @@ class VulkanRenderManager { // Can run on a different thread! Just make sure to use BeginFrameWrites. void Flush(); + void Run(); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); @@ -334,8 +335,11 @@ class VulkanRenderManager { // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { + bool readyForFence = true; VkFence fence; - VkCommandPool cmdPool; + // These are on different threads so need separate pools. + VkCommandPool cmdPoolInit; + VkCommandPool cmdPoolMain; VkCommandBuffer initCmd; VkCommandBuffer mainCmd; bool hasInitCommands = false; @@ -351,8 +355,11 @@ class VulkanRenderManager { std::vector steps_; // Execution time state + int curFrame_; + volatile bool frameAvailable_ = false; + bool run_ = true; VulkanContext *vulkan_; - std::thread submissionThread; + std::thread thread_; std::mutex mutex_; std::condition_variable condVar_; std::vector stepsOnThread_; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 3644db1a1263..b8c22cc177a3 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -766,7 +766,6 @@ void VKContext::EndFrame() { push_->End(); renderManager_.Flush(); - renderManager_.EndFrame(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) From 022657ebb36014058c4dcd7b25e5158c91ec9ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 17:44:28 +0200 Subject: [PATCH 08/28] This makes a little more sense, but still hangs (frameAvailable = true while stuck on condvar) --- ext/native/thin3d/VulkanRenderManager.cpp | 53 +++++++++++++---------- ext/native/thin3d/VulkanRenderManager.h | 6 +-- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index bedd901a3fe2..1cdc6ffe9eba 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -214,9 +214,10 @@ void VulkanRenderManager::ThreadFunc() { std::unique_lock lock(mutex_); condVar_.wait(lock); if (frameAvailable_) { - Run(); - EndFrame(); + int frame = threadFrame_; frameAvailable_ = false; + Run(frame); + EndFrame(frame); } } } @@ -224,7 +225,8 @@ void VulkanRenderManager::ThreadFunc() { void VulkanRenderManager::BeginFrame() { VkDevice device = vulkan_->GetDevice(); - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; // Make sure the very last command buffer from the frame before the previous has been fully executed. if (useThread) { @@ -236,10 +238,12 @@ void VulkanRenderManager::BeginFrame() { frameData.readyForFence = false; } + ILOG("Fencing %d", curFrame); vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); // Must be after the fence - this performs deletes. + ILOG("BeginFrame %d", curFrame); vulkan_->BeginFrame(); insideFrame_ = true; @@ -258,18 +262,18 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { assert(res == VK_SUCCESS); frameData.hasInitCommands = true; } - return frameData_[vulkan_->GetCurFrame()].initCmd; + return frameData_[curFrame].initCmd; } // After flush. Should probably be part of it? -void VulkanRenderManager::EndFrame() { +void VulkanRenderManager::EndFrame(int frame) { insideFrame_ = false; - FrameData &frame = frameData_[curFrame_]; + FrameData &frameData = frameData_[frame]; - TransitionToPresent(frame.mainCmd, swapchainImages_[curSwapchainImage_].image); + TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage_].image); - VkResult res = vkEndCommandBuffer(frame.mainCmd); + VkResult res = vkEndCommandBuffer(frameData.mainCmd); assert(res == VK_SUCCESS); // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. @@ -278,14 +282,13 @@ void VulkanRenderManager::EndFrame() { int numCmdBufs = 0; std::vector cmdBufs; - if (frame.hasInitCommands) { - vkEndCommandBuffer(frame.initCmd); - cmdBufs.push_back(frame.initCmd); - frame.hasInitCommands = false; - ILOG("Frame %d had init commands", curFrame_); + if (frameData.hasInitCommands) { + vkEndCommandBuffer(frameData.initCmd); + cmdBufs.push_back(frameData.initCmd); + frameData.hasInitCommands = false; } - cmdBufs.push_back(frame.mainCmd); + cmdBufs.push_back(frameData.mainCmd); VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; @@ -296,11 +299,12 @@ void VulkanRenderManager::EndFrame() { submit_info.pCommandBuffers = cmdBufs.data(); submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &renderingCompleteSemaphore; - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); assert(res == VK_SUCCESS); if (useThread) { - frame.readyForFence = true; + ILOG("Frame %d.readyForFence = true", frame); + frameData.readyForFence = true; } VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); @@ -634,24 +638,28 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Flush() { - curFrame_ = vulkan_->GetCurFrame(); - frameAvailable_ = true; + while (frameAvailable_) + ; + int curFrame = vulkan_->GetCurFrame(); if (!useThread) { - Run(); - EndFrame(); + Run(curFrame); + EndFrame(curFrame); } else { + frameAvailable_ = true; + threadFrame_ = curFrame; condVar_.notify_all(); } vulkan_->EndFrame(); } -void VulkanRenderManager::Run() { +void VulkanRenderManager::Run(int frame) { + ILOG("Running frame %d", frame); //if ({ // std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); curRenderStep_ = nullptr; - FrameData &frameData = frameData_[curFrame_]; + FrameData &frameData = frameData_[frame]; VkDevice device = vulkan_->GetDevice(); @@ -694,6 +702,7 @@ void VulkanRenderManager::Run() { delete stepsOnThread_[i]; } stepsOnThread_.clear(); + ILOG("Finished running frame %d", frame); } void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index ed3237a3fd9b..2d8d55c80e56 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -205,7 +205,7 @@ class VulkanRenderManager { // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrame(); - void EndFrame(); + void EndFrame(int frame); void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -283,7 +283,7 @@ class VulkanRenderManager { // Can run on a different thread! Just make sure to use BeginFrameWrites. void Flush(); - void Run(); + void Run(int frame); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); @@ -355,7 +355,7 @@ class VulkanRenderManager { std::vector steps_; // Execution time state - int curFrame_; + int threadFrame_; volatile bool frameAvailable_ = false; bool run_ = true; VulkanContext *vulkan_; From 48ac334f4f16efa928d2857c225934e66235c9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Aug 2017 15:39:23 +0200 Subject: [PATCH 09/28] Vulkan: Don't forget to specify stencil load op in render passes --- ext/native/thin3d/VulkanRenderManager.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 1cdc6ffe9eba..214b506f7ac4 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -4,7 +4,7 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" -const bool useThread = true; +const bool useThread = false; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; @@ -559,9 +559,18 @@ void VulkanRenderManager::InitRenderpasses() { for (int depth = 0; depth < 3; depth++) { switch ((VKRRenderPassAction)depth) { - case VKRRenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case VKRRenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case VKRRenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + case VKRRenderPassAction::CLEAR: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + case VKRRenderPassAction::KEEP: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + case VKRRenderPassAction::DONT_CARE: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; } for (int color = 0; color < 3; color++) { switch ((VKRRenderPassAction)color) { From d3672d9e57f85356cc7efcf453b01f45d7d8dc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 18:05:05 +0200 Subject: [PATCH 10/28] Make sure not to begin and end the init command buffer on different threads. --- ext/native/thin3d/VulkanRenderManager.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 214b506f7ac4..099bf77edae4 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -283,7 +283,6 @@ void VulkanRenderManager::EndFrame(int frame) { int numCmdBufs = 0; std::vector cmdBufs; if (frameData.hasInitCommands) { - vkEndCommandBuffer(frameData.initCmd); cmdBufs.push_back(frameData.initCmd); frameData.hasInitCommands = false; } @@ -647,14 +646,20 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Flush() { - while (frameAvailable_) - ; + curRenderStep_ = nullptr; int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (frameData.hasInitCommands) { + vkEndCommandBuffer(frameData.initCmd); + } if (!useThread) { Run(curFrame); EndFrame(curFrame); } else { - frameAvailable_ = true; + { + std::unique_lock lock(mutex_); + frameAvailable_ = true; + } threadFrame_ = curFrame; condVar_.notify_all(); } @@ -666,7 +671,6 @@ void VulkanRenderManager::Run(int frame) { //if ({ // std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); - curRenderStep_ = nullptr; FrameData &frameData = frameData_[frame]; From 72a41cd5245e68ab053d961eda709d77baab2535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 25 Aug 2017 16:21:45 +0200 Subject: [PATCH 11/28] Let's try a mutex. Doesn't completely solve the problem though.. --- ext/native/thin3d/VulkanRenderManager.cpp | 4 ++++ ext/native/thin3d/VulkanRenderManager.h | 1 + 2 files changed, 5 insertions(+) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 099bf77edae4..1d3aed3537e2 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -215,9 +215,11 @@ void VulkanRenderManager::ThreadFunc() { condVar_.wait(lock); if (frameAvailable_) { int frame = threadFrame_; + frameData_[frame].mutex.lock(); frameAvailable_ = false; Run(frame); EndFrame(frame); + frameData_[frame].mutex.unlock(); } } } @@ -236,6 +238,7 @@ void VulkanRenderManager::BeginFrame() { ; } frameData.readyForFence = false; + frameData.mutex.lock(); } ILOG("Fencing %d", curFrame); @@ -656,6 +659,7 @@ void VulkanRenderManager::Flush() { Run(curFrame); EndFrame(curFrame); } else { + frameData.mutex.unlock(); { std::unique_lock lock(mutex_); frameAvailable_ = true; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 2d8d55c80e56..73c1f3a4d5eb 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -335,6 +335,7 @@ class VulkanRenderManager { // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { + std::mutex mutex; bool readyForFence = true; VkFence fence; // These are on different threads so need separate pools. From c403edfa890238afd225abf34e767bcebda1907e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Aug 2017 15:55:47 +0200 Subject: [PATCH 12/28] Avoid re-binding pipelines. --- GPU/Software/SoftGpu.cpp | 13 ++++++------- ext/native/thin3d/VulkanRenderManager.cpp | 12 ++++++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 0a08068a2b1f..812c164cb2ef 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -146,13 +146,6 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { if (!draw_) return; - float dstwidth = (float)PSP_CoreParameter().pixelWidth; - float dstheight = (float)PSP_CoreParameter().pixelHeight; - - Draw::Viewport viewport = {0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f}; - draw_->SetViewports(1, &viewport); - draw_->SetScissorRect(0, 0, dstwidth, dstheight); - float u0 = 0.0f; float u1; @@ -216,6 +209,9 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { fbTex = draw_->CreateTexture(desc); + float dstwidth = (float)PSP_CoreParameter().pixelWidth; + float dstheight = (float)PSP_CoreParameter().pixelHeight; + float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL); @@ -242,6 +238,9 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { std::swap(v0, v1); } draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE }); + Draw::Viewport viewport = { 0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f }; + draw_->SetViewports(1, &viewport); + draw_->SetScissorRect(0, 0, dstwidth, dstheight); Draw::SamplerState *sampler; if (g_Config.iBufFilter == SCALE_NEAREST) { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 1d3aed3537e2..a4631bc09935 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -771,6 +771,8 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer VKRFramebuffer *fb = step.render.framebuffer; + VkPipeline lastPipeline = VK_NULL_HANDLE; + auto &commands = step.commands; // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only @@ -798,7 +800,10 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW_INDEXED: - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); + if (c.drawIndexed.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); + lastPipeline = c.drawIndexed.pipeline; + } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); @@ -806,7 +811,10 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW: - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + if (c.draw.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + lastPipeline = c.draw.pipeline; + } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); vkCmdDraw(cmd, c.draw.count, 1, 0, 0); From 3e2b810c39f85bff4cd08013f2d58c7a6dceaba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Aug 2017 17:10:10 +0200 Subject: [PATCH 13/28] Improve synchronization, though there are very weird issues. Possibly need to run the swap chain from the thread that created it? --- Windows/GPU/WindowsVulkanContext.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 205 +++++++++++----------- ext/native/thin3d/VulkanRenderManager.h | 35 ++-- 3 files changed, 124 insertions(+), 118 deletions(-) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 6f87a00cd713..2bddafba38e3 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -171,7 +171,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m Version gitVer(PPSSPP_GIT_VERSION); g_Vulkan = new VulkanContext(); - if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_MAILBOX)) { + if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_FIFO_RELAXED)) { *error_message = g_Vulkan->InitError(); return false; } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a4631bc09935..5501bba622f8 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -4,6 +4,7 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" +// TODO: Using a thread here is unfinished and does not work correctly. const bool useThread = false; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { @@ -81,7 +82,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan semaphoreCreateInfo.flags = 0; VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); assert(res == VK_SUCCESS); - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore_); assert(res == VK_SUCCESS); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -111,8 +112,6 @@ void VulkanRenderManager::CreateBackbuffers() { VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr); assert(res == VK_SUCCESS); - ILOG("Vulkan swapchain image count: %d", swapchainImageCount_); - VkImage* swapchainImages = new VkImage[swapchainImageCount_]; res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages); assert(res == VK_SUCCESS); @@ -151,7 +150,6 @@ void VulkanRenderManager::CreateBackbuffers() { assert(res == VK_SUCCESS); } delete[] swapchainImages; - curSwapchainImage_ = -1; InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. InitBackbufferRenderPass(); // Must be before InitFramebuffers. @@ -168,10 +166,19 @@ void VulkanRenderManager::CreateBackbuffers() { } void VulkanRenderManager::DestroyBackbuffers() { - // Stop the thread. if (useThread) { run_ = false; - condVar_.notify_all(); + // Stop the thread. + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + { + std::unique_lock lock(frameData_[i].push_mutex); + frameData_[i].push_condVar.notify_all(); + } + { + std::unique_lock lock(frameData_[i].pull_mutex); + frameData_[i].pull_condVar.notify_all(); + } + } thread_.join(); } VkDevice device = vulkan_->GetDevice(); @@ -189,7 +196,7 @@ VulkanRenderManager::~VulkanRenderManager() { VkDevice device = vulkan_->GetDevice(); vulkan_->WaitUntilQueueIdle(); vkDestroySemaphore(device, acquireSemaphore_, nullptr); - vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); + vkDestroySemaphore(device, renderingCompleteSemaphore_, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); @@ -210,17 +217,25 @@ VulkanRenderManager::~VulkanRenderManager() { // TODO: Activate this code. void VulkanRenderManager::ThreadFunc() { setCurrentThreadName("RenderMan"); + int threadFrame = -1; // Increment first, start at 0. while (run_) { - std::unique_lock lock(mutex_); - condVar_.wait(lock); - if (frameAvailable_) { - int frame = threadFrame_; - frameData_[frame].mutex.lock(); - frameAvailable_ = false; - Run(frame); - EndFrame(frame); - frameData_[frame].mutex.unlock(); + { + threadFrame++; + if (threadFrame >= vulkan_->GetInflightFrames()) + threadFrame = 0; + FrameData &frameData = frameData_[threadFrame]; + std::unique_lock lock(frameData.pull_mutex); + while (!frameData.readyForRun && run_) { + ILOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); + frameData.pull_condVar.wait(lock); + } + ILOG("PULL: frame[%d].readyForRun = false", threadFrame); + frameData.readyForRun = false; + if (!run_) // quick exit if bailing. + return; } + ILOG("PULL: Running frame %d", threadFrame); + Run(threadFrame); } } @@ -232,21 +247,20 @@ void VulkanRenderManager::BeginFrame() { // Make sure the very last command buffer from the frame before the previous has been fully executed. if (useThread) { - // Can't wait for this fence until it's actually been enqueued. - // Will replace this with a condvar if it works. + std::unique_lock lock(frameData.push_mutex); while (!frameData.readyForFence) { - ; + ILOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); + frameData.push_condVar.wait(lock); } frameData.readyForFence = false; - frameData.mutex.lock(); } - - ILOG("Fencing %d", curFrame); + + ILOG("PUSH: Fencing %d", curFrame); vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); // Must be after the fence - this performs deletes. - ILOG("BeginFrame %d", curFrame); + ILOG("PUSH: BeginFrame %d", curFrame); vulkan_->BeginFrame(); insideFrame_ = true; @@ -268,61 +282,6 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { return frameData_[curFrame].initCmd; } -// After flush. Should probably be part of it? -void VulkanRenderManager::EndFrame(int frame) { - insideFrame_ = false; - - FrameData &frameData = frameData_[frame]; - - TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage_].image); - - VkResult res = vkEndCommandBuffer(frameData.mainCmd); - assert(res == VK_SUCCESS); - - // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. - // This way we bunch up all the initialization needed for the frame, we render to - // other buffers before the back buffer, and then last we render to the backbuffer. - - int numCmdBufs = 0; - std::vector cmdBufs; - if (frameData.hasInitCommands) { - cmdBufs.push_back(frameData.initCmd); - frameData.hasInitCommands = false; - } - - cmdBufs.push_back(frameData.mainCmd); - - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; - VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; - submit_info.pWaitDstStageMask = waitStage; - submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); - submit_info.pCommandBuffers = cmdBufs.data(); - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore; - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); - assert(res == VK_SUCCESS); - - if (useThread) { - ILOG("Frame %d.readyForFence = true", frame); - frameData.readyForFence = true; - } - - VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present.swapchainCount = 1; - present.pSwapchains = &swapchain; - present.pImageIndices = &curSwapchainImage_; - present.pWaitSemaphores = &renderingCompleteSemaphore; - present.waitSemaphoreCount = 1; - present.pResults = nullptr; - res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); - // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI - // return codes - assert(res == VK_SUCCESS); -} - void VulkanRenderManager::Sync() { } @@ -656,33 +615,27 @@ void VulkanRenderManager::Flush() { vkEndCommandBuffer(frameData.initCmd); } if (!useThread) { + frameData.steps = std::move(steps_); Run(curFrame); - EndFrame(curFrame); } else { - frameData.mutex.unlock(); - { - std::unique_lock lock(mutex_); - frameAvailable_ = true; - } - threadFrame_ = curFrame; - condVar_.notify_all(); + std::unique_lock lock(frameData.pull_mutex); + ILOG("PUSH: Frame[%d].readyForRun = true", curFrame); + frameData.steps = std::move(steps_); + frameData.readyForRun = true; + frameData.pull_condVar.notify_all(); } vulkan_->EndFrame(); } void VulkanRenderManager::Run(int frame) { - ILOG("Running frame %d", frame); - //if ({ - // std::unique_lock lock(mutex_); - stepsOnThread_ = std::move(steps_); - FrameData &frameData = frameData_[frame]; - + auto &stepsOnThread_ = frameData_[frame].steps; VkDevice device = vulkan_->GetDevice(); + uint32_t curSwapchainImage = 0; // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage); assert(res == VK_SUCCESS); VkCommandBuffer cmd = frameData.mainCmd; @@ -697,14 +650,14 @@ void VulkanRenderManager::Run(int frame) { // return codes // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? assert(res == VK_SUCCESS); - TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage_].image); + TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage].image); // Optimizes renderpasses, then sequences them. for (int i = 0; i < stepsOnThread_.size(); i++) { const VKRStep &step = *stepsOnThread_[i]; switch (step.stepType) { case VKRStepType::RENDER: - PerformRenderPass(step, cmd); + PerformRenderPass(step, cmd, curSwapchainImage); break; case VKRStepType::COPY: PerformCopy(step, cmd); @@ -719,10 +672,62 @@ void VulkanRenderManager::Run(int frame) { delete stepsOnThread_[i]; } stepsOnThread_.clear(); - ILOG("Finished running frame %d", frame); + insideFrame_ = false; + + TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage].image); + + res = vkEndCommandBuffer(frameData.mainCmd); + assert(res == VK_SUCCESS); + + // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. + // This way we bunch up all the initialization needed for the frame, we render to + // other buffers before the back buffer, and then last we render to the backbuffer. + + int numCmdBufs = 0; + std::vector cmdBufs; + if (frameData.hasInitCommands) { + cmdBufs.push_back(frameData.initCmd); + frameData.hasInitCommands = false; + } + + cmdBufs.push_back(frameData.mainCmd); + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; + submit_info.pWaitDstStageMask = waitStage; + submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); + submit_info.pCommandBuffers = cmdBufs.data(); + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); + assert(res == VK_SUCCESS); + + if (useThread) { + ILOG("PULL: Frame %d.readyForFence = true", frame); + std::unique_lock lock(frameData.push_mutex); + frameData.readyForFence = true; + frameData.push_condVar.notify_all(); + } + + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); + VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + present.swapchainCount = 1; + present.pSwapchains = &swapchain; + present.pImageIndices = &curSwapchainImage; + present.pWaitSemaphores = &renderingCompleteSemaphore_; + present.waitSemaphoreCount = 1; + present.pResults = nullptr; + res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); + // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI + // return codes + assert(res == VK_SUCCESS); + + ILOG("PULL: Finished running frame %d", frame); } -void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { +void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { // TODO: If there are multiple, we can transition them together. for (const auto &iter : step.preTransitions) { if (iter.fb->color.layout != iter.targetLayout) { @@ -767,7 +772,7 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } - PerformBindFramebufferAsRenderTarget(step, cmd); + PerformBindFramebufferAsRenderTarget(step, cmd, swapChainImage); VKRFramebuffer *fb = step.render.framebuffer; @@ -895,7 +900,7 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } -void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { +void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { VkFramebuffer framebuf; int w; int h; @@ -907,7 +912,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st h = fb->height; prevLayout = fb->color.layout; } else { - framebuf = framebuffers_[curSwapchainImage_]; + framebuf = framebuffers_[swapChainImage]; w = vulkan_->GetBackbufferWidth(); h = vulkan_->GetBackbufferHeight(); } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 73c1f3a4d5eb..e6ee367451cb 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -205,7 +205,12 @@ class VulkanRenderManager { // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrame(); - void EndFrame(int frame); + // Can run on a different thread! Just make sure to use BeginFrameWrites. + void Flush(); + void Run(int frame); + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). + void Sync(); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -281,13 +286,6 @@ class VulkanRenderManager { curRenderStep_->render.numDraws++; } - // Can run on a different thread! Just make sure to use BeginFrameWrites. - void Flush(); - void Run(int frame); - - // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). - void Sync(); - VkCommandBuffer GetInitCmd(); VkRenderPass GetBackbufferRenderpass() const { return backbufferRenderPass_; @@ -312,9 +310,9 @@ class VulkanRenderManager { void InitRenderpasses(); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. - void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); + void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd, int swapChainImage); - void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); + void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd, int swapChainImage); void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); @@ -327,7 +325,7 @@ class VulkanRenderManager { // Permanent objects VkSemaphore acquireSemaphore_; - VkSemaphore renderingCompleteSemaphore; + VkSemaphore renderingCompleteSemaphore_; VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // TODO: Create these on demand. @@ -335,8 +333,15 @@ class VulkanRenderManager { // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { - std::mutex mutex; + std::mutex push_mutex; + std::condition_variable push_condVar; + + std::mutex pull_mutex; + std::condition_variable pull_condVar; + bool readyForFence = true; + + bool readyForRun = false; VkFence fence; // These are on different threads so need separate pools. VkCommandPool cmdPoolInit; @@ -344,6 +349,7 @@ class VulkanRenderManager { VkCommandBuffer initCmd; VkCommandBuffer mainCmd; bool hasInitCommands = false; + std::vector steps; }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; @@ -356,14 +362,10 @@ class VulkanRenderManager { std::vector steps_; // Execution time state - int threadFrame_; - volatile bool frameAvailable_ = false; bool run_ = true; VulkanContext *vulkan_; std::thread thread_; std::mutex mutex_; - std::condition_variable condVar_; - std::vector stepsOnThread_; VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; // Swap chain management @@ -374,7 +376,6 @@ class VulkanRenderManager { std::vector framebuffers_; std::vector swapchainImages_; uint32_t swapchainImageCount_; - uint32_t curSwapchainImage_ = 0; struct DepthBufferInfo { VkFormat format = VK_FORMAT_UNDEFINED; VkImage image = VK_NULL_HANDLE; From b5e06f3c7dda7f18f09e631d14856523922c5410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 23 Oct 2017 14:53:35 +0200 Subject: [PATCH 14/28] VulkanRenderManager: BindPipeline should really be its own command. Tighten up some image transitions. --- GPU/Vulkan/DrawEngineVulkan.cpp | 16 +++--- GPU/Vulkan/FramebufferVulkan.cpp | 3 +- ext/native/thin3d/VulkanRenderManager.cpp | 67 ++++++++++++++--------- ext/native/thin3d/VulkanRenderManager.h | 22 +++++--- ext/native/thin3d/thin3d_vulkan.cpp | 12 +++- 5 files changed, 75 insertions(+), 45 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0cc249cf9a85..3d326d7556e7 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -866,7 +866,6 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - VulkanPipeline *pipeline = lastPipeline_; if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { @@ -874,11 +873,12 @@ void DrawEngineVulkan::DoFlush() { } Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); if (!pipeline) { // Already logged, let's bail out. return; } + renderManager->BindPipeline(pipeline->pipeline); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -910,9 +910,9 @@ void DrawEngineVulkan::DoFlush() { if (!ibuf) ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); int numInstances = (gstate_c.bezier || gstate_c.spline) ? numPatches : 1; - renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); } else { - renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); + renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } } else { @@ -966,7 +966,6 @@ void DrawEngineVulkan::DoFlush() { if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } - VulkanPipeline *pipeline = lastPipeline_; if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { @@ -974,7 +973,8 @@ void DrawEngineVulkan::DoFlush() { } Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + renderManager->BindPipeline(pipeline->pipeline); if (!pipeline) { // Already logged, let's bail out. return; @@ -1008,12 +1008,12 @@ void DrawEngineVulkan::DoFlush() { vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * numTrans, &ibuf); VkDeviceSize offsets[1] = { vbOffset }; - renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vbuf); VkDeviceSize offsets[1] = { vbOffset }; - renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); + renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); } } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index bf33417a4a7b..a9b5d0a997e5 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -367,7 +367,8 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa VkDescriptorSet descSet = vulkan2D_.GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer); - renderManager->Draw(cur2DPipeline_, vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); + renderManager->BindPipeline(cur2DPipeline_); + renderManager->Draw(vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } void FramebufferManagerVulkan::Bind2DShader() { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 5501bba622f8..f7357b86e80e 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -749,6 +749,10 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; default: Crash(); break; @@ -786,6 +790,13 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer for (const auto &c : commands) { switch (c.cmd) { + case VKRRenderCommand::BIND_PIPELINE: + if (c.pipeline.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); + lastPipeline = c.pipeline.pipeline; + } + break; + case VKRRenderCommand::VIEWPORT: vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); break; @@ -805,10 +816,6 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW_INDEXED: - if (c.drawIndexed.pipeline != lastPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); - lastPipeline = c.drawIndexed.pipeline; - } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); @@ -816,10 +823,6 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW: - if (c.draw.pipeline != lastPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); - lastPipeline = c.draw.pipeline; - } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); vkCmdDraw(cmd, c.draw.count, 1, 0, 0); @@ -1061,32 +1064,33 @@ void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) int srcCount = 0; int dstCount = 0; + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; // First source barriers. if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); } if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); } } // We can't copy only depth or only stencil unfortunately. if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } } - // TODO: Fix the pipe bits to be bit less conservative. if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); } if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); } if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { @@ -1137,32 +1141,33 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) blit.dstSubresource.mipLevel = 0; blit.dstSubresource.layerCount = 1; + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; // First source barriers. if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); } if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); } } // We can't copy only depth or only stencil unfortunately. if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } } - // TODO: Fix the pipe bits to be bit less conservative. if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); } if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); } if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { @@ -1185,7 +1190,7 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) } } -void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; @@ -1194,16 +1199,20 @@ void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMem barrier.srcAccessMask = 0; switch (img.layout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; default: Crash(); @@ -1211,10 +1220,12 @@ void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMem barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; } -void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; @@ -1224,15 +1235,19 @@ void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMem switch (img.layout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; default: Crash(); @@ -1240,5 +1255,7 @@ void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMem barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index e6ee367451cb..6f4ffb5bffab 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -16,6 +16,7 @@ // The cool thing is that you can Flush on a different thread than you record the commands on! enum class VKRRenderCommand : uint8_t { + BIND_PIPELINE, STENCIL, BLEND, VIEWPORT, @@ -30,6 +31,8 @@ struct VkRenderData { union { struct { VkPipeline pipeline; + } pipeline; + struct { VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; @@ -39,7 +42,6 @@ struct VkRenderData { int count; } draw; struct { - VkPipeline pipeline; VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; @@ -58,7 +60,6 @@ struct VkRenderData { int clearStencil; int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc } clear; - struct { VkViewport vp; } viewport; @@ -218,6 +219,13 @@ class VulkanRenderManager { void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); + void BindPipeline(VkPipeline pipeline) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; + data.pipeline.pipeline = pipeline; + curRenderStep_->commands.push_back(data); + } + void SetViewport(const VkViewport &vp) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::VIEWPORT }; @@ -250,11 +258,10 @@ class VulkanRenderManager { void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); - void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { + void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; - data.draw.pipeline = pipeline; data.draw.pipelineLayout = layout; data.draw.ds = descSet; data.draw.vbuffer = vbuffer; @@ -266,12 +273,11 @@ class VulkanRenderManager { curRenderStep_->render.numDraws++; } - void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { + void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.instances = numInstances; - data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; data.drawIndexed.ds = descSet; data.drawIndexed.vbuffer = vbuffer; @@ -320,8 +326,8 @@ class VulkanRenderManager { return (int)depth * 3 + (int)color; } - static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); // Permanent objects VkSemaphore acquireSemaphore_; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b8c22cc177a3..b3a323ca6402 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1114,7 +1114,9 @@ void VKContext::Draw(int vertexCount, int offset) { VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } void VKContext::DrawIndexed(int vertexCount, int offset) { @@ -1128,7 +1130,9 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1140,7 +1144,9 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. From c4f0afc8a28da597f713be466a5b574b6cece298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 9 Oct 2017 12:43:56 +0200 Subject: [PATCH 15/28] Clean out some obsolete code --- GPU/Vulkan/FramebufferVulkan.cpp | 42 +++-------------------- GPU/Vulkan/VulkanUtil.cpp | 27 --------------- GPU/Vulkan/VulkanUtil.h | 27 --------------- ext/native/thin3d/VulkanQueueRunner.cpp | 1 + ext/native/thin3d/VulkanRenderManager.cpp | 10 +++--- 5 files changed, 9 insertions(+), 98 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index a9b5d0a997e5..f9c6946e977d 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -175,7 +175,6 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() { } void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) { - // if (!useBufferedRendering_) { float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); @@ -196,9 +195,6 @@ void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, boo if (clearDepth) { SetDepthUpdated(); } - //} else { - // TODO: Clever render pass magic. - //} } void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) { @@ -486,50 +482,20 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // When updating VRAM, it need to be exact format. - if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { - switch (nvfb->format) { - case GE_FORMAT_4444: - nvfb->colorDepth = VK_FBO_4444; - break; - case GE_FORMAT_5551: - nvfb->colorDepth = VK_FBO_5551; - break; - case GE_FORMAT_565: - nvfb->colorDepth = VK_FBO_565; - break; - case GE_FORMAT_8888: - default: - nvfb->colorDepth = VK_FBO_8888; - break; - } - } + nvfb->colorDepth = Draw::FBO_8888; - /* - nvfb->fbo = CreateFramebuffer(nvfb->width, nvfb->height, 1, false, (FBOColorDepth)nvfb->colorDepth); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); if (!(nvfb->fbo)) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; } - BindFramebufferAsRenderTarget(nvfb->fbo); - */ + draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); return true; } void FramebufferManagerVulkan::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // _assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer"); - - // Discard the previous contents of this buffer where possible. - /* - if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { - BindFramebufferAsRenderTargetnvfb->fbo); - GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT }; - glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments); - } else if (gl_extensions.IsGLES) { - BindFramebufferAsRenderTargetnvfb->fbo); - } - */ + // Nothing to do here. } void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) { diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index 576315ff9b35..caf06cbac8ad 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -20,33 +20,6 @@ #include "Common/Vulkan/VulkanContext.h" #include "GPU/Vulkan/VulkanUtil.h" -VulkanFBO::VulkanFBO() : color_(nullptr), depthStencil_(nullptr) {} - -VulkanFBO::~VulkanFBO() { - delete color_; - delete depthStencil_; -} - -void VulkanFBO::Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { - color_ = new VulkanTexture(vulkan); - VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - color_->CreateDirect(cmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); - depthStencil_->CreateDirect(cmd, width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); - - VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() }; - - VkFramebufferCreateInfo fb = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb.pAttachments = views; - fb.attachmentCount = 2; - fb.flags = 0; - fb.renderPass = rp_compatible; - fb.width = width; - fb.height = height; - fb.layers = 1; - - vkCreateFramebuffer(vulkan->GetDevice(), &fb, nullptr, &framebuffer_); -} - Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan), curFrame_(0) { InitDeviceObjects(); } diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index fefb35544b29..3c32a63fac26 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -42,33 +42,6 @@ // // Each FBO will get its own command buffer for each pass. -// -struct VulkanFBOPass { - VkCommandBuffer cmd; -}; - -class VulkanFBO { -public: - VulkanFBO(); - ~VulkanFBO(); - - // Depth-format is chosen automatically depending on hardware support. - // Color format will be 32-bit RGBA. - void Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); - - VulkanTexture *GetColor() { return color_; } - VulkanTexture *GetDepthStencil() { return depthStencil_; } - - VkFramebuffer GetFramebuffer() { return framebuffer_; } - -private: - VulkanTexture *color_; - VulkanTexture *depthStencil_; - - // This point specifically to color and depth. - VkFramebuffer framebuffer_; -}; - // Similar to a subset of Thin3D, but separate. // This is used for things like postprocessing shaders, depal, etc. // No UBO data is used, only PushConstants. diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index e69de29bb2d1..ad86d0bcecb8 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -0,0 +1 @@ +#include "VulkanQueueRunner.h" diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index f7357b86e80e..804b67797794 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -80,9 +80,9 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; semaphoreCreateInfo.flags = 0; - VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); + VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore_); assert(res == VK_SUCCESS); - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore_); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_); assert(res == VK_SUCCESS); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -145,7 +145,7 @@ void VulkanRenderManager::CreateBackbuffers() { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, NULL, &sc_buffer.view); + res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, nullptr, &sc_buffer.view); swapchainImages_.push_back(sc_buffer); assert(res == VK_SUCCESS); } @@ -267,8 +267,6 @@ void VulkanRenderManager::BeginFrame() { } VkCommandBuffer VulkanRenderManager::GetInitCmd() { - // assert(insideFrame_ || firstFrame_); - int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (!frameData.hasInitCommands) { @@ -389,7 +387,7 @@ void VulkanRenderManager::InitBackbufferRenderPass() { rp_info.dependencyCount = 0; rp_info.pDependencies = nullptr; - res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, NULL, &backbufferRenderPass_); + res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); assert(res == VK_SUCCESS); } From d016bfe2219bd274ef92d89a88a2cead886ffed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 17:19:00 +0200 Subject: [PATCH 16/28] Fix some resource leaks and excessive logging in VulkanRenderManager --- Windows/GPU/WindowsVulkanContext.cpp | 9 +++- ext/native/thin3d/VulkanRenderManager.cpp | 56 ++++++++++++++--------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 2bddafba38e3..2c4a8992f42e 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -171,7 +171,13 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m Version gitVer(PPSSPP_GIT_VERSION); g_Vulkan = new VulkanContext(); - if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_FIFO_RELAXED)) { + + // int vulkanFlags = VULKAN_FLAG_PRESENT_FIFO_RELAXED; + int vulkanFlags = VULKAN_FLAG_PRESENT_MAILBOX; + if (g_validate_) { + vulkanFlags |= VULKAN_FLAG_VALIDATE; + } + if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), vulkanFlags)) { *error_message = g_Vulkan->InitError(); return false; } @@ -225,6 +231,7 @@ void WindowsVulkanContext::Resize() { g_Vulkan->DestroyObjects(); g_Vulkan->ReinitSurfaceWin32(); + g_Vulkan->InitObjects(); draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 804b67797794..6a058eeed7f3 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -4,6 +4,12 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" +#ifdef _DEBUG +#define VLOG ILOG +#else +#define VLOG(...) +#endif + // TODO: Using a thread here is unfinished and does not work correctly. const bool useThread = false; @@ -106,6 +112,9 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan assert(res == VK_SUCCESS); frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } + + InitBackbufferRenderPass(); + InitRenderpasses(); } void VulkanRenderManager::CreateBackbuffers() { @@ -152,9 +161,7 @@ void VulkanRenderManager::CreateBackbuffers() { delete[] swapchainImages; InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. - InitBackbufferRenderPass(); // Must be before InitFramebuffers. InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - InitRenderpasses(); curWidth_ = -1; curHeight_ = -1; @@ -188,6 +195,12 @@ void VulkanRenderManager::DestroyBackbuffers() { vulkan_->Delete().QueueDeleteImageView(depth_.view); vulkan_->Delete().QueueDeleteImage(depth_.image); vulkan_->Delete().QueueDeleteDeviceMemory(depth_.mem); + for (uint32_t i = 0; i < framebuffers_.size(); i++) { + assert(framebuffers_[i] != VK_NULL_HANDLE); + vulkan_->Delete().QueueDeleteFramebuffer(framebuffers_[i]); + } + framebuffers_.clear(); + swapchainImages_.clear(); } @@ -201,17 +214,16 @@ VulkanRenderManager::~VulkanRenderManager() { VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); + vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); + vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); vkDestroyFence(device, frameData_[i].fence, nullptr); } - if (backbufferRenderPass_ != VK_NULL_HANDLE) - vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); - for (uint32_t i = 0; i < framebuffers_.size(); i++) { - vkDestroyFramebuffer(device, framebuffers_[i], nullptr); - } - framebuffers_.clear(); for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { + assert(renderPasses_[i] != VK_NULL_HANDLE); vkDestroyRenderPass(device, renderPasses_[i], nullptr); } + assert(backbufferRenderPass_ != VK_NULL_HANDLE); + vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); } // TODO: Activate this code. @@ -226,15 +238,15 @@ void VulkanRenderManager::ThreadFunc() { FrameData &frameData = frameData_[threadFrame]; std::unique_lock lock(frameData.pull_mutex); while (!frameData.readyForRun && run_) { - ILOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); + VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); frameData.pull_condVar.wait(lock); } - ILOG("PULL: frame[%d].readyForRun = false", threadFrame); + VLOG("PULL: frame[%d].readyForRun = false", threadFrame); frameData.readyForRun = false; if (!run_) // quick exit if bailing. return; } - ILOG("PULL: Running frame %d", threadFrame); + VLOG("PULL: Running frame %d", threadFrame); Run(threadFrame); } } @@ -249,18 +261,18 @@ void VulkanRenderManager::BeginFrame() { if (useThread) { std::unique_lock lock(frameData.push_mutex); while (!frameData.readyForFence) { - ILOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); + VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); frameData.push_condVar.wait(lock); } frameData.readyForFence = false; } - ILOG("PUSH: Fencing %d", curFrame); + VLOG("PUSH: Fencing %d", curFrame); vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); // Must be after the fence - this performs deletes. - ILOG("PUSH: BeginFrame %d", curFrame); + VLOG("PUSH: BeginFrame %d", curFrame); vulkan_->BeginFrame(); insideFrame_ = true; @@ -315,7 +327,7 @@ void 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 }; - ILOG("InitFramebuffers: %dx%d", width, height); + VLOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; fb_info.renderPass = backbufferRenderPass_; fb_info.attachmentCount = 2; @@ -347,8 +359,7 @@ void VulkanRenderManager::InitBackbufferRenderPass() { attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[0].flags = 0; - assert(depth_.format != VK_FORMAT_UNDEFINED); - attachments[1].format = depth_.format; + 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_STORE; @@ -537,7 +548,8 @@ void VulkanRenderManager::InitRenderpasses() { case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth)]); + int index = RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth); + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[index]); } } } @@ -617,7 +629,7 @@ void VulkanRenderManager::Flush() { Run(curFrame); } else { std::unique_lock lock(frameData.pull_mutex); - ILOG("PUSH: Frame[%d].readyForRun = true", curFrame); + VLOG("PUSH: Frame[%d].readyForRun = true", curFrame); frameData.steps = std::move(steps_); frameData.readyForRun = true; frameData.pull_condVar.notify_all(); @@ -703,7 +715,7 @@ void VulkanRenderManager::Run(int frame) { assert(res == VK_SUCCESS); if (useThread) { - ILOG("PULL: Frame %d.readyForFence = true", frame); + VLOG("PULL: Frame %d.readyForFence = true", frame); std::unique_lock lock(frameData.push_mutex); frameData.readyForFence = true; frameData.push_condVar.notify_all(); @@ -722,7 +734,7 @@ void VulkanRenderManager::Run(int frame) { // return codes assert(res == VK_SUCCESS); - ILOG("PULL: Finished running frame %d", frame); + VLOG("PULL: Finished running frame %d", frame); } void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { @@ -1006,7 +1018,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st } renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; - // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); + // VLOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); if (step.render.color == VKRRenderPassAction::CLEAR) { Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 1; From 8639664a6a09bb1c594bc1f322b7e52ac3b040fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 19:05:09 +0200 Subject: [PATCH 17/28] Warning fixes, CMakeLists.txt/Android.mk fixes --- ext/native/Android.mk | 2 ++ ext/native/thin3d/thin3d_vulkan.cpp | 20 ++++---------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 14310e227628..ad1271daba7b 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -85,6 +85,8 @@ LOCAL_SRC_FILES :=\ thin3d/thin3d.cpp \ thin3d/thin3d_gl.cpp \ thin3d/thin3d_vulkan.cpp \ + thin3d/VulkanRenderManager.cpp \ + thin3d/VulkanQueueRunner.cpp \ ui/view.cpp \ ui/viewgroup.cpp \ ui/ui.cpp \ diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b3a323ca6402..5a371da48fb7 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -135,14 +135,6 @@ static const VkStencilOp stencilOpToVK[8] = { VK_STENCIL_OP_DECREMENT_AND_WRAP, }; -// TODO: Replace with the one from dataconv -static inline void Uint8x4ToFloat4(uint32_t u, float f[4]) { - f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f); - f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f); - f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f); - f[3] = ((u >> 24) & 0xFF) * (1.0f / 255.0f); -} - class VKBlendState : public BlendState { public: VkPipelineColorBlendStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; @@ -493,9 +485,6 @@ class VKContext : public DrawContext { VkQueue queue_; int queueFamilyIndex_; - int curWidth_ = -1; - int curHeight_ = -1; - enum { MAX_BOUND_TEXTURES = 1, MAX_FRAME_COMMAND_BUFFERS = 256, @@ -1140,8 +1129,6 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); renderManager_.BindPipeline(curPipeline_->vkpipeline); @@ -1253,20 +1240,19 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { // use this frame's init command buffer. class VKFramebuffer : public Framebuffer { public: - VKFramebuffer(VulkanContext *vk, VKRFramebuffer *fb) : vulkan_(vk), buf_(fb) {} + VKFramebuffer(VKRFramebuffer *fb) : buf_(fb) {} ~VKFramebuffer() { delete buf_; } VKRFramebuffer *GetFB() const { return buf_; } private: VKRFramebuffer *buf_; - VulkanContext *vulkan_; }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { VkCommandBuffer cmd = renderManager_.GetInitCmd(); VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetRenderPass(0), desc.width, desc.height); - VKFramebuffer *fb = new VKFramebuffer(vulkan_, vkrfb); + VKFramebuffer *fb = new VKFramebuffer(vkrfb); return fb; } @@ -1336,6 +1322,8 @@ void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void case Event::GOT_BACKBUFFER: renderManager_.CreateBackbuffers(); break; + default: + break; } // Noop } From 9b183f1f4be878d2f9b9daad285b874f8dda411e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 20:26:21 +0200 Subject: [PATCH 18/28] Forgot to add the CMakeLists.txt change to the last commit. --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3fc60adde86..30acdec62ad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -806,7 +806,9 @@ if(VULKAN) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} ext/native/thin3d/thin3d_vulkan.cpp ext/native/thin3d/VulkanRenderManager.cpp - ext/native/thin3d/VulkanRenderManager.h) + ext/native/thin3d/VulkanRenderManager.h + ext/native/thin3d/VulkanQueueRunner.cpp + ext/native/thin3d/VulkanQueueRunner.h) endif() if(WIN32) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} From 85cb604a2477e1e52832bbacb408811dd52c2e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 20:28:12 +0200 Subject: [PATCH 19/28] Fix (some) crashes on blits. Validation issues remain. --- GPU/Common/FramebufferCommon.cpp | 9 +++++++-- GPU/Common/FramebufferCommon.h | 6 +++++- GPU/GPUCommon.cpp | 2 ++ GPU/Vulkan/FramebufferVulkan.cpp | 3 +++ ext/native/thin3d/VulkanRenderManager.cpp | 21 ++++++++++++++++++--- ext/native/thin3d/VulkanRenderManager.h | 2 -- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index c10f7ac6535d..f8298293890e 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -141,7 +141,7 @@ bool FramebufferManagerCommon::UpdateSize() { void FramebufferManagerCommon::BeginFrame() { DecimateFBOs(); - currentRenderVfb_ = 0; + currentRenderVfb_ = nullptr; updateVRAM_ = !(g_Config.iRenderingMode == FB_NON_BUFFERED_MODE || g_Config.iRenderingMode == FB_BUFFERED_MODE); } @@ -1043,6 +1043,11 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { void FramebufferManagerCommon::DecimateFBOs() { currentRenderVfb_ = 0; + for (auto iter : fbosToDelete_) { + delete iter; + } + fbosToDelete_.clear(); + for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used); @@ -1153,7 +1158,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min(vfb->bufferWidth, vfb->width), std::min(vfb->height, vfb->bufferHeight), 0); } } - delete old.fbo; + fbosToDelete_.push_back(old.fbo); if (needGLESRebinds_) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 2fd39d9a0dbd..62befe69135a 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -190,7 +190,7 @@ class FramebufferManagerCommon { virtual ~FramebufferManagerCommon(); virtual void Init(); - void BeginFrame(); + virtual void BeginFrame(); void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); void DestroyFramebuf(VirtualFramebuffer *v); @@ -209,6 +209,8 @@ class FramebufferManagerCommon { FramebufferHeuristicParams inputs; GetFramebufferHeuristicInputs(&inputs, gstate); VirtualFramebuffer *vfb = DoSetRenderFrameBuffer(inputs, skipDrawReason); + _dbg_assert_msg_(G3D, vfb, "DoSetRenderFramebuffer must return a valid framebuffer."); + _dbg_assert_msg_(G3D, currentRenderVfb_, "DoSetRenderFramebuffer must set a valid framebuffer."); return vfb; } } @@ -412,6 +414,8 @@ class FramebufferManagerCommon { std::map tempFBOs_; + std::vector fbosToDelete_; + // Aggressively delete unused FBOs to save gpu memory. enum { FBO_OLD_AGE = 5, diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 53b39e6d322e..852c37b6157f 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1083,6 +1083,8 @@ void GPUCommon::ProcessEvent(GPUEvent ev) { break; case GPU_EVENT_COPY_DISPLAY_TO_OUTPUT: + // Ending the frame will unbind the framebuffer. Better finish any outstanding draw calls... + drawEngineCommon_->DispatchFlush(); CopyDisplayToOutputInternal(); break; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index f9c6946e977d..5cf7ab2cbd6c 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -93,6 +93,9 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk vulkan2D_(vulkan) { InitDeviceObjects(); + + // After a blit we do need to rebind for the VulkanRenderManager to know what to do. + needGLESRebinds_ = true; } FramebufferManagerVulkan::~FramebufferManagerVulkan() { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 6a058eeed7f3..d8dd08ad3fa3 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -580,6 +580,9 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->copy.srcRect = srcRect; step->copy.dst = dst; step->copy.dstPos = dstPos; + + // TODO: Validate or clip copy-rectangles here. + std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; @@ -593,6 +596,9 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->blit.dst = dst; step->blit.dstRect = dstRect; step->blit.filter = filter; + + // TODO: Validate blit-rectangles here. + std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; @@ -985,6 +991,9 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; + default: + Crash(); + break; } TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, @@ -1009,6 +1018,9 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; + default: + Crash(); + break; } TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, fb->color.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, @@ -1130,9 +1142,8 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) VKRFramebuffer *src = step.blit.src; VKRFramebuffer *dst = step.blit.dst; - int srcCount = 0; - int dstCount = 0; - + // If any validation needs to be performed here, it should probably have been done + // already when the blit was queued. So don't validate here. VkImageBlit blit{}; blit.srcOffsets[0].x = step.blit.srcRect.offset.x; blit.srcOffsets[0].y = step.blit.srcRect.offset.y; @@ -1153,6 +1164,10 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) VkPipelineStageFlags srcStage = 0; VkPipelineStageFlags dstStage = 0; + + int srcCount = 0; + int dstCount = 0; + // First source barriers. if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 6f4ffb5bffab..4f45e0e6d414 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -212,7 +212,6 @@ class VulkanRenderManager { // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); - void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -364,7 +363,6 @@ class VulkanRenderManager { int curHeight_; bool insideFrame_ = false; VKRStep *curRenderStep_; - VKRFramebuffer *boundFramebuffer_; std::vector steps_; // Execution time state From 12f858098432cc7a324fa8574742d9f21859524f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 21:19:42 +0200 Subject: [PATCH 20/28] Assorted fixes --- GPU/Common/FramebufferCommon.cpp | 9 +++-- ext/native/thin3d/VulkanRenderManager.cpp | 43 ++++++++++++++++++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index f8298293890e..3291f9475426 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -307,7 +307,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame bool vfbFormatChanged = false; // Find a matching framebuffer - VirtualFramebuffer *vfb = 0; + VirtualFramebuffer *vfb = nullptr; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *v = vfbs_[i]; if (v->fb_address == params.fb_address) { @@ -1095,6 +1095,9 @@ void FramebufferManagerCommon::DecimateFBOs() { void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, u16 h, bool force, bool skipCopy) { VirtualFramebuffer old = *vfb; + int oldWidth = vfb->bufferWidth; + int oldHeight = vfb->bufferHeight; + if (force) { vfb->bufferWidth = w; vfb->bufferHeight = h; @@ -1151,11 +1154,11 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); if (old.fbo) { - INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %i x %i x %i", vfb->fb_address, w, h, vfb->format); + INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %d x %d x %d", vfb->fb_address, w, h, vfb->format); if (vfb->fbo) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); if (!skipCopy && !g_Config.bDisableSlowFramebufEffects) { - BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min(vfb->bufferWidth, vfb->width), std::min(vfb->height, vfb->bufferHeight), 0); + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0); } } fbosToDelete_.push_back(old.fbo); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index d8dd08ad3fa3..558258c52896 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -574,22 +574,42 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten } void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { + _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, dstPos.x >= 0, "dstPos offset x < 0"); + _dbg_assert_msg_(G3D, dstPos.y >= 0, "dstPos offset y < 0"); + _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= dst->width, "dstPos + extent x > width"); + _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= dst->height, "dstPos + extent y > height"); + VKRStep *step = new VKRStep{ VKRStepType::COPY }; + step->copy.aspectMask = aspectMask; step->copy.src = src; step->copy.srcRect = srcRect; step->copy.dst = dst; step->copy.dstPos = dstPos; - // TODO: Validate or clip copy-rectangles here. - std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; + curFramebuffer_ = nullptr; } void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { + _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + + _dbg_assert_msg_(G3D, dstRect.offset.x >= 0, "dstrect offset x < 0"); + _dbg_assert_msg_(G3D, dstRect.offset.y >= 0, "dstrect offset y < 0"); + _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= dst->width, "dstrect offset x + extent > width"); + _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= dst->height, "dstrect offset y + extent > height"); + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; + step->blit.aspectMask = aspectMask; step->blit.src = src; step->blit.srcRect = srcRect; @@ -597,11 +617,10 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->blit.dstRect = dstRect; step->blit.filter = filter; - // TODO: Validate blit-rectangles here. - std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; + curFramebuffer_ = nullptr; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { @@ -625,6 +644,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; + curFramebuffer_ = nullptr; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { @@ -792,6 +812,7 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } + // This is supposed to bind a vulkan render pass to the command buffer. PerformBindFramebufferAsRenderTarget(step, cmd, swapChainImage); VKRFramebuffer *fb = step.render.framebuffer; @@ -936,6 +957,8 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st h = vulkan_->GetBackbufferHeight(); } +#if 0 + // This part is based on faulty old thinking. if (framebuf == curFramebuffer_) { if (framebuf == 0) Crash(); @@ -966,6 +989,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st // We're done. return; } +#endif VkRenderPass renderPass; int numClearVals = 0; @@ -1022,8 +1046,8 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st Crash(); break; } - TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + TransitionImageLayout2(cmd, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; @@ -1086,8 +1110,8 @@ void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) int srcCount = 0; int dstCount = 0; - VkPipelineStageFlags srcStage; - VkPipelineStageFlags dstStage; + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; // First source barriers. if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { @@ -1200,6 +1224,9 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); } + + // TODO: Need to check if the depth format is blittable. + // Actually, we should probably almost always use copies rather than blits for depth buffers. if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { blit.srcSubresource.aspectMask = 0; blit.dstSubresource.aspectMask = 0; From 613cc46285f12400d67b1b3dd6aacbfd051f83ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 21:56:39 +0200 Subject: [PATCH 21/28] Remove a bad check making us use the wrong way to copy depth buffers. Add a cap so we can try to unify BlitFramebufferDepth later. --- GPU/Vulkan/FramebufferVulkan.cpp | 6 ++++-- ext/native/thin3d/thin3d.h | 2 ++ ext/native/thin3d/thin3d_d3d11.cpp | 3 +++ ext/native/thin3d/thin3d_d3d9.cpp | 2 ++ ext/native/thin3d/thin3d_gl.cpp | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 5 ++++- 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 5cf7ab2cbd6c..d6124cc467c9 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -434,13 +434,15 @@ void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, Vir bool matchingDepthBuffer = src->z_address == dst->z_address && src->z_stride != 0 && dst->z_stride != 0; bool matchingSize = src->width == dst->width && src->height == dst->height; bool matchingRenderSize = src->renderWidth == dst->renderWidth && src->renderHeight == dst->renderHeight; - - if (gstate_c.Supports(GPU_SUPPORTS_ANY_COPY_IMAGE) && matchingDepthBuffer && matchingRenderSize && matchingSize) { + if (matchingDepthBuffer && matchingRenderSize && matchingSize) { + // TODO: Currently, this copies depth AND stencil, which is a problem. See #9740. draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, src->renderWidth, src->renderHeight, 1, Draw::FB_DEPTH_BIT); } else if (matchingDepthBuffer && matchingSize) { + /* int w = std::min(src->renderWidth, dst->renderWidth); int h = std::min(src->renderHeight, dst->renderHeight); draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST); + */ } } diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index fdb68a38493c..6b3a6dfac991 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -555,6 +555,8 @@ struct DeviceCaps { bool logicOpSupported; bool framebufferCopySupported; bool framebufferBlitSupported; + bool framebufferDepthCopySupported; + bool framebufferDepthBlitSupported; }; struct TextureDesc { diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index e7ca762369c3..a40d4c4bfc97 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -217,6 +217,9 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.depthRangeMinusOneToOne = false; caps_.framebufferBlitSupported = false; + caps_.framebufferCopySupported = true; + caps_.framebufferDepthBlitSupported = false; + caps_.framebufferDepthCopySupported = true; D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 118bd48fab58..3ac305857516 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -601,6 +601,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.tesselationShaderSupported = false; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = false; + caps_.framebufferDepthBlitSupported = true; + caps_.framebufferDepthCopySupported = false; if (d3d) { D3DDISPLAYMODE displayMode; d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 1334a26e2cfe..36532fabe958 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -602,6 +602,7 @@ OpenGLContext::OpenGLContext() { caps_.preferredDepthBufferFormat = DataFormat::D24_S8; } caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object; + caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported; } OpenGLContext::~OpenGLContext() { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 5a371da48fb7..005523b99175 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -658,7 +658,10 @@ VKContext::VKContext(VulkanContext *vulkan) caps_.dualSourceBlend = vulkan->GetFeaturesAvailable().dualSrcBlend != 0; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = true; - caps_.preferredDepthBufferFormat = DataFormat::D24_S8; + caps_.framebufferDepthBlitSupported = false; // Can be checked for. + caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case. + caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. + device_ = vulkan->GetDevice(); queue_ = vulkan->GetGraphicsQueue(); From 54e4eab1c153a83134acf18a865e32eb7caf6f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 22:04:25 +0200 Subject: [PATCH 22/28] Warning fixes --- ext/native/thin3d/VulkanRenderManager.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 558258c52896..90881a303e8f 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -576,12 +576,12 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); - _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); - _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height"); _dbg_assert_msg_(G3D, dstPos.x >= 0, "dstPos offset x < 0"); _dbg_assert_msg_(G3D, dstPos.y >= 0, "dstPos offset y < 0"); - _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= dst->width, "dstPos + extent x > width"); - _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= dst->height, "dstPos + extent y > height"); + _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= (uint32_t)dst->width, "dstPos + extent x > width"); + _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= (uint32_t)dst->height, "dstPos + extent y > height"); VKRStep *step = new VKRStep{ VKRStepType::COPY }; @@ -600,13 +600,13 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); - _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); - _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height"); _dbg_assert_msg_(G3D, dstRect.offset.x >= 0, "dstrect offset x < 0"); _dbg_assert_msg_(G3D, dstRect.offset.y >= 0, "dstrect offset y < 0"); - _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= dst->width, "dstrect offset x + extent > width"); - _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= dst->height, "dstrect offset y + extent > height"); + _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= (uint32_t)dst->width, "dstrect offset x + extent > width"); + _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= (uint32_t)dst->height, "dstrect offset y + extent > height"); VKRStep *step = new VKRStep{ VKRStepType::BLIT }; From 8644e85d3da990180d6a80d4518f2cdf6c052916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 22:09:08 +0200 Subject: [PATCH 23/28] Remove misguided hack. --- GPU/GPUCommon.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 852c37b6157f..53b39e6d322e 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1083,8 +1083,6 @@ void GPUCommon::ProcessEvent(GPUEvent ev) { break; case GPU_EVENT_COPY_DISPLAY_TO_OUTPUT: - // Ending the frame will unbind the framebuffer. Better finish any outstanding draw calls... - drawEngineCommon_->DispatchFlush(); CopyDisplayToOutputInternal(); break; From 07e8b4ff1aefdf5108dc21d20d1f109f6eedc8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 00:55:09 +0200 Subject: [PATCH 24/28] Bump descriptor set limits, which became insufficient with the addition of tesselation (should really use separate big desc layouts for them) --- GPU/Vulkan/DrawEngineVulkan.cpp | 10 ++++++---- ext/native/thin3d/VulkanRenderManager.cpp | 6 +++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 3d326d7556e7..80245e4282df 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -126,7 +126,8 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[4].binding = DRAW_BINDING_DYNUBO_BONE; - // Hardware tessellation + // Hardware tessellation. TODO: Don't allocate these unless actually drawing splines. + // Will require additional bindings[5].descriptorCount = 1; bindings[5].pImmutableSamplers = nullptr; bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -152,9 +153,9 @@ void DrawEngineVulkan::InitDeviceObjects() { assert(VK_SUCCESS == res); VkDescriptorPoolSize dpTypes[2]; - dpTypes[0].descriptorCount = 4096; + dpTypes[0].descriptorCount = 8192; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - dpTypes[1].descriptorCount = 2048; + dpTypes[1].descriptorCount = 8192 + 4096; // Due to the tess stuff, we need a LOT of these. Most will be empty... dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; @@ -536,7 +537,8 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView descAlloc.descriptorPool = frame->descPool; descAlloc.descriptorSetCount = 1; VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc); - assert(result == VK_SUCCESS); + // Even in release mode, this is bad. + _assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptors in pool. sz=%d", (int)frame->descSets.size()); // We just don't write to the slots we don't care about. VkWriteDescriptorSet writes[7]; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 90881a303e8f..a6930938b338 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -758,7 +758,11 @@ void VulkanRenderManager::Run(int frame) { res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI // return codes - assert(res == VK_SUCCESS); + if (res == VK_ERROR_OUT_OF_DATE_KHR) { + // ignore, it'll be fine. this happens sometimes during resizes, and we do make sure to recreate the swap chain. + } else { + assert(res == VK_SUCCESS); + } VLOG("PULL: Finished running frame %d", frame); } From ecad09d18f060102666483eede7854db52d9af18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 01:17:10 +0200 Subject: [PATCH 25/28] 32-bit android buildfix attempt --- ext/native/thin3d/VulkanRenderManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a6930938b338..f6e8699ab0d1 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -1,3 +1,4 @@ +#include #include "base/logging.h" #include "Common/Vulkan/VulkanContext.h" @@ -594,7 +595,7 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; - curFramebuffer_ = nullptr; + curFramebuffer_ = VK_NULL_HANDLE; } void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { @@ -620,7 +621,7 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; - curFramebuffer_ = nullptr; + curFramebuffer_ = VK_NULL_HANDLE; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { @@ -644,7 +645,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; - curFramebuffer_ = nullptr; + curFramebuffer_ = VK_N; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { From 73125762396fc4246a77fd25cfd4f451f96ce056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 11:33:52 +0200 Subject: [PATCH 26/28] More buildfixes, warning fix, memory leak fix --- GPU/D3D11/GPU_D3D11.cpp | 3 ++- GPU/Directx9/GPU_DX9.cpp | 2 +- GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 11 +++++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 95dadcf07da2..cc3f06f16e19 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -317,12 +317,13 @@ void GPU_D3D11::EndHostFrame() { } void GPU_D3D11::BeginFrameInternal() { + GPUCommon::BeginFrameInternal(); + textureCacheD3D11_->StartFrame(); drawEngine_.BeginFrame(); depalShaderCache_->Decimate(); // fragmentTestCache_.Decimate(); - GPUCommon::BeginFrameInternal(); shaderManagerD3D11_->DirtyLastShader(); framebufferManagerD3D11_->BeginFrame(); diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 3221e487a950..0dd93e713754 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -295,7 +295,7 @@ void GPU_DX9::BeginFrameInternal() { GPUCommon::BeginFrameInternal(); shaderManagerDX9_->DirtyShader(); - framebufferManagerDX9_->BeginFrame(); + framebufferManager_->BeginFrame(); } void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 80245e4282df..537dfdc756a8 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -242,7 +242,7 @@ void DrawEngineVulkan::FrameData::Destroy(VulkanContext *vulkan) { } void DrawEngineVulkan::DestroyDeviceObjects() { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].Destroy(vulkan_); } if (depalSampler_ != VK_NULL_HANDLE) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index f6e8699ab0d1..daa07812cbf3 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -60,12 +60,12 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView); assert(res == VK_SUCCESS); - VkPipelineStageFlagBits dstStage; + VkPipelineStageFlags dstStage; VkAccessFlagBits dstAccessMask; switch (initialLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; @@ -73,7 +73,10 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + default: + Crash(); break; } @@ -645,7 +648,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; - curFramebuffer_ = VK_N; + curFramebuffer_ = VK_NULL_HANDLE; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { From 5456194a696283bc325479e886d30b301c0b2feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 12:28:36 +0200 Subject: [PATCH 27/28] Fix integer overflow affecting very large draw calls --- ext/native/thin3d/VulkanRenderManager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 4f45e0e6d414..780481f66964 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -39,7 +39,7 @@ struct VkRenderData { uint32_t uboOffsets[3]; VkBuffer vbuffer; VkDeviceSize voffset; - int count; + uint32_t count; } draw; struct { VkPipelineLayout pipelineLayout; @@ -50,7 +50,7 @@ struct VkRenderData { VkDeviceSize voffset; VkBuffer ibuffer; VkDeviceSize ioffset; - int16_t count; + uint32_t count; int16_t instances; VkIndexType indexType; } drawIndexed; From 1e9984dbf688070aa79a538e85897d883e21e2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 13:00:27 +0200 Subject: [PATCH 28/28] Another buildfix --- ext/native/thin3d/VulkanRenderManager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index daa07812cbf3..2550c2b6fc1f 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -14,6 +14,11 @@ // TODO: Using a thread here is unfinished and does not work correctly. const bool useThread = false; +#ifndef UINT64_MAX +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + + void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; ici.arrayLayers = 1; @@ -26,12 +31,14 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int ici.samples = VK_SAMPLE_COUNT_1_BIT; ici.tiling = VK_IMAGE_TILING_OPTIMAL; ici.format = format; + // Strictly speaking we don't yet need VK_IMAGE_USAGE_SAMPLED_BIT for depth buffers since we do not yet sample depth buffers. ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; if (color) { ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } else { ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } + vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers