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] 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 | 1133 +++++++++++++++++++++++-- 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, 1462 insertions(+), 1464 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index a5af3a8083..20ec8b0bd3 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 925240b5c7..e8204dba4c 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -195,8 +195,7 @@ private: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: // 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 @@ private: 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 @@ private: 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 19291746ae..41cf32c824 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 50b9b0b651..51ffbe5e22 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -26,16 +26,16 @@ public: // 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 e097d44eca..734ae34ac8 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::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; +void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint) { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + EndCurrentRenderpass(frameData.mainCmd); +} - case VKR_SCISSOR: - vkCmdSetScissor(cmdbuf, 0, 1, &c.scissor.scissor); - break; +void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter) { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + EndCurrentRenderpass(frameData.mainCmd); +} - 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 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 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: - { - 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"); - ; - } +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? } - delete renderPasses_[i]; } - renderPasses_.clear(); + + /* + 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::Sync(VkCommandBuffer cmd) { +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 < 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(); +} + +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 VkRenderCmd::SCISSOR: + vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); + break; + + case VkRenderCmd::BLEND: + vkCmdSetBlendConstants(cmd, c.blendColor.color); + 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 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 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 (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; + } + 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; + } + 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, ©); + } +} + +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 c4c59485eb..fbc8f0976c 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 @@ public: 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 @@ public: 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 renderPasses_; - VKRRenderPass *curRp_; + std::vector steps_; + std::vector stepsOnThread_; + std::mutex rpLock_; + + VKRStep *curStep_; + + 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 c174e5898e..896067b97e 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 a5387dfb9d..098a16a5f6 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -255,14 +255,15 @@ private: // 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 @@ private: 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 @@ private: 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 335edcf44e..96e08b8923 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 9cea56729b..2ae17c63b1 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 b9d8e905e9..576315ff9b 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 630bebd05f..fefb35544b 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -54,7 +54,7 @@ public: // 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 @@ public: 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 e326e11c54..ef322d90e2 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 2801a06a40..84e9da1a7e 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 dcbe746160..5e18ad9927 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 @@ public: } 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 554bdca80b..e7ca762369 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -116,7 +116,7 @@ public: } } - 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 ad40c89a44..118bd48fab 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -529,7 +529,7 @@ public: 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 65dcdd4a33..1334a26e2c 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -560,7 +560,7 @@ public: } } - 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 6620921b43..2d409843cd 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 @@ public: 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 @@ private: 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 @@ public: void EndFrame() override; void FlushState() override { - ApplyDynamicState(); } void WaitRenderCompletion(Framebuffer *fbo) override; @@ -460,18 +450,20 @@ public: 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 @@ public: 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 @@ 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]; - 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 @@ private: 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 @@ private: 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 }