From a7dd6d6085043b785a3140cd5cac6c6d32c77671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 May 2017 17:20:22 +0200 Subject: [PATCH] Further steps towards Vulkan framebuffer support --- Common/Vulkan/VulkanContext.h | 3 + GPU/Common/FramebufferCommon.cpp | 4 +- GPU/GPU.vcxproj | 3 +- GPU/GPU.vcxproj.filters | 6 +- GPU/Vulkan/DrawEngineVulkan.cpp | 1 + GPU/Vulkan/FramebufferVulkan.cpp | 79 +------- GPU/Vulkan/FramebufferVulkan.h | 10 +- ext/native/thin3d/thin3d.h | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 276 ++++++++++++++++++++++++++-- pspautotests | 2 +- 10 files changed, 278 insertions(+), 107 deletions(-) diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index b1b5c77f17..c6f097ac45 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -258,6 +258,9 @@ public: VkCommandBuffer GetInitCommandBuffer(); + VkFramebuffer GetSurfaceFramebuffer() { + return framebuffers_[current_buffer]; + } // This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass. VkCommandBuffer GetSurfaceCommandBuffer() { return frame_[curFrame_ & 1].cmdBuf; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 9a985234de..8e7b1d2225 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -825,7 +825,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { if (displayFramebufPtr_ == 0) { DEBUG_LOG(FRAMEBUF, "Display disabled, displaying only black"); // No framebuffer to display! Clear to black. - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); + if (useBufferedRendering_) { + draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); + } return; } diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 3959984582..dce71c3d73 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -21,7 +21,8 @@ {457F45D2-556F-47BC-A31D-AFF0D15BEAED} GPU - + + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 9032efdb16..8d92e6f620 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -408,9 +408,6 @@ Vulkan - - Vulkan - Vulkan @@ -510,5 +507,8 @@ Common + + Vulkan + \ No newline at end of file diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 22cf8b3514..929d4f91a8 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -883,6 +883,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. // We let the framebuffer manager handle the clear. It can use renderpasses to optimize on tilers. + // If non-buffered though, it'll just do a plain clear. framebufferManager_->NotifyClear(gstate.isClearModeColorMask(), gstate.isClearModeAlphaMask(), gstate.isClearModeDepthMask(), result.color, result.depth); int scissorX1 = gstate.getScissorX1(); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 307aa03179..cb77192698 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -52,8 +52,6 @@ #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/VulkanUtil.h" -const VkFormat framebufFormat = VK_FORMAT_B8G8R8A8_UNORM; - static const char tex_fs[] = R"(#version 400 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable @@ -92,7 +90,8 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk pixelBufObj_(nullptr), currentPBO_(0), curFrame_(0), - pipelineBasicTex_(VK_NULL_HANDLE), + pipelineBasicTexBackBuffer_(VK_NULL_HANDLE), + pipelineBasicTexFrameBuffer_(VK_NULL_HANDLE), pipelinePostShader_(VK_NULL_HANDLE), vulkan2D_(vulkan) { @@ -117,67 +116,6 @@ void FramebufferManagerVulkan::SetShaderManager(ShaderManagerVulkan *sm) { } void FramebufferManagerVulkan::InitDeviceObjects() { - // Create a bunch of render pass objects, for normal rendering with a depth buffer, - // with and without pre-clearing of both depth/stencil and color, so 4 combos. - VkAttachmentDescription attachments[2] = {}; - attachments[0].format = framebufFormat; - 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; - - // TODO: Maybe LOAD_OP_DONT_CARE makes sense in some situations. Additionally, - // there is often no need to store the depth buffer afterwards, although hard to know up front. - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpLoadColorLoadDepth_); - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpClearColorLoadDepth_); - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpClearColorClearDepth_); - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpLoadColorClearDepth_); - // Initialize framedata for (int i = 0; i < 2; i++) { VkCommandPoolCreateInfo cp = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; @@ -196,7 +134,9 @@ void FramebufferManagerVulkan::InitDeviceObjects() { assert(fsBasicTex_ != VK_NULL_HANDLE); assert(vsBasicTex_ != VK_NULL_HANDLE); - pipelineBasicTex_ = vulkan2D_.GetPipeline(pipelineCache2D_, rpClearColorClearDepth_, vsBasicTex_, fsBasicTex_); + // Get a representative render pass and use when creating the pipeline. + pipelineBasicTexBackBuffer_ = vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_); + pipelineBasicTexFrameBuffer_ = vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS), vsBasicTex_, fsBasicTex_); VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; @@ -213,15 +153,6 @@ void FramebufferManagerVulkan::InitDeviceObjects() { } void FramebufferManagerVulkan::DestroyDeviceObjects() { - if (rpLoadColorLoadDepth_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorLoadDepth_); - if (rpClearColorLoadDepth_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeleteRenderPass(rpClearColorLoadDepth_); - if (rpClearColorClearDepth_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeleteRenderPass(rpClearColorClearDepth_); - if (rpLoadColorClearDepth_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorClearDepth_); - for (int i = 0; i < 2; i++) { if (frameData_[i].numCommandBuffers_ > 0) { vkFreeCommandBuffers(vulkan_->GetDevice(), frameData_[i].cmdPool_, frameData_[i].numCommandBuffers_, frameData_[i].commandBuffers_); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 95fc9cd0aa..72ab08338f 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -188,18 +188,14 @@ private: // This gets copied to the current frame's push buffer as needed. PostShaderUniforms postUniforms_; - // Renderpasses, all combination of preserving or clearing fb contents - VkRenderPass rpLoadColorLoadDepth_; - VkRenderPass rpClearColorLoadDepth_; - VkRenderPass rpLoadColorClearDepth_; - VkRenderPass rpClearColorClearDepth_; - VkPipelineCache pipelineCache2D_; // Basic shaders VkShaderModule fsBasicTex_; VkShaderModule vsBasicTex_; - VkPipeline pipelineBasicTex_; + // Might need different pipelines for rendering to backbuffer vs framebuffers due to color format incompatibility + VkPipeline pipelineBasicTexBackBuffer_; + VkPipeline pipelineBasicTexFrameBuffer_; // Postprocessing VkPipeline pipelinePostShader_; diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 1e2c9d0ca0..37cd4e40ba 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -321,6 +321,7 @@ enum class NativeObject { BACKBUFFER_COLOR_TEX, BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, + RENDERPASS, }; enum FBColorDepth { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 6c7beea45a..907110190c 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -411,7 +411,12 @@ public: std::vector GetFeatureList() const override; uintptr_t GetNativeObject(NativeObject obj) const override { - return 0; + switch (obj) { + case NativeObject::RENDERPASS: + return (uintptr_t)renderPasses_[0]; + default: + return 0; + } } void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; @@ -432,6 +437,13 @@ private: 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]; + VkCommandPool cmdPool_; VkDevice device_; VkQueue queue_; @@ -464,6 +476,9 @@ private: VulkanPushBuffer *push_ = nullptr; DeviceCaps caps_{}; + + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE;; + VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; static int GetBpp(VkFormat format) { @@ -714,9 +729,79 @@ 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 and without pre-clearing of both depth/stencil and color, so 4 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[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; + } + 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]); + } vulkan_->Delete().QueueDeleteCommandPool(cmdPool_); // This also destroys all descriptor sets. for (int i = 0; i < 2; i++) { @@ -748,26 +833,14 @@ void VKContext::BeginFrame() { scissor_.extent.height = pixel_yres; scissorDirty_ = true; viewportDirty_ = true; - - int colorval = 0xFF000000; - float depthVal = 0.0; - int stencilVal = 0; - - VkClearValue clearVal[2] = {}; - Uint8x4ToFloat4(colorval, clearVal[0].color.float32); - - // // Debug flicker - used to see if we swap at all. no longer necessary - // if (frameNum_ & 1) - // clearVal[0].color.float32[2] = 1.0f; - - clearVal[1].depthStencil.depth = depthVal; - clearVal[1].depthStencil.stencil = stencilVal; - - vulkan_->BeginSurfaceRenderPass(clearVal); } void VKContext::EndFrame() { - vulkan_->EndSurfaceRenderPass(); + if (curRenderPass_) { + vulkan_->EndSurfaceRenderPass(); + curRenderPass_ = VK_NULL_HANDLE; + curFramebuffer_ = VK_NULL_HANDLE; + } // Stop collecting data in the frame's data pushbuffer. push_->End(); @@ -1277,18 +1350,124 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { } } +// 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. +struct VKImage { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + VkImageLayout layout; +}; + +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); + + 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); + res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); + assert(res); + img.layout = initialLayout; + + 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 = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + ivci.subresourceRange.layerCount = 1; + ivci.subresourceRange.levelCount = 1; + res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view); + assert(res); + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (initialLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + break; + } + barrier.newLayout = initialLayout; + barrier.subresourceRange.aspectMask = ivci.subresourceRange.aspectMask; + vkCmdPipelineBarrier(vulkan->GetInitCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, 0, 0, 0, 1, &barrier); +} + // A VKFramebuffer is a VkFramebuffer plus all the textures it owns. class VKFramebuffer : public Framebuffer { public: + VKFramebuffer(VulkanContext *vk) : vulkan_(vk) {} + ~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); + } + VkFramebuffer framebuf; + VKImage color; + VKImage depth; int width; int height; +private: + VulkanContext *vulkan_; }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { - VKFramebuffer *fb = new VKFramebuffer(); + 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); return fb; } @@ -1305,11 +1484,67 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr // These functions should be self explanatory. void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { + VkFramebuffer framebuf; + int w; + int h; if (fbo) { VKFramebuffer *fb = (VKFramebuffer *)fbo; + framebuf = fb->framebuf; + w = fb->width; + h = fb->height; } else { - + framebuf = vulkan_->GetSurfaceFramebuffer(); + w = vulkan_->GetWidth(); + h = vulkan_->GetHeight(); } + + VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer(); + if (framebuf == curFramebuffer_) { + // 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. Also, TODO, do a single vkCmdClearAttachments to clear both. + if (rp.color == RPAction::CLEAR) { + VkClearAttachment clear{}; + clear.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + Uint8x4ToFloat4(rp.clearColor, clear.clearValue.color.float32); + clear.colorAttachment = 0; + VkClearRect rc{ {0,0,(uint32_t)w,(uint32_t)h}, 0, 1 }; + vkCmdClearAttachments(cmd, 1, &clear, 1, &rc); + } + if (rp.depth == RPAction::CLEAR) { + VkClearAttachment clear{}; + clear.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + clear.clearValue.depthStencil.depth = rp.clearDepth; + clear.clearValue.depthStencil.stencil = rp.clearStencil; + clear.colorAttachment = 0; + VkClearRect rc{ { 0,0,w,h }, 0, 1 }; + vkCmdClearAttachments(cmd, 1, &clear, 1, &rc); + } + // We're done. + return; + } + + // OK, we're switching framebuffers. + if (curRenderPass_ != VK_NULL_HANDLE) { + vkCmdEndRenderPass(cmd); + } + + VkClearValue clearVal[2] = {}; + Uint8x4ToFloat4(rp.clearColor, clearVal[0].color.float32); + clearVal[1].depthStencil.depth = rp.clearDepth; + clearVal[1].depthStencil.stencil = rp.clearStencil; + + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = fbo ? renderPasses_[RPIndex(rp.color, rp.depth)] : vulkan_->GetSurfaceRenderPass(); + 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 = 2; + rp_begin.pClearValues = clearVal; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + curFramebuffer_ = framebuf; + curRenderPass_ = rp_begin.renderPass; } // color must be 0, for now. @@ -1317,6 +1552,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne VKFramebuffer *fb = (VKFramebuffer *)fbo; } + void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { diff --git a/pspautotests b/pspautotests index e18cface3d..d02ba74070 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit e18cface3db64ccb96738dc128fe769b28fff65c +Subproject commit d02ba7407050f445edf9e908374ad4bf3b2f237b