diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 664f107ffe..95ff12aa60 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -24,9 +24,6 @@ #include "GPU/Common/GPUDebugInterface.h" #include "Common/MemoryUtil.h" -#include -#include - enum TextureFiltering { TEX_FILTER_AUTO = 1, TEX_FILTER_NEAREST = 2, diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 99093aaf4b..09c17b003a 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -338,6 +338,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 9ad5e7fe0c..4fe276f335 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -403,5 +403,6 @@ + \ No newline at end of file diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp new file mode 100644 index 0000000000..d9bbf8e6b1 --- /dev/null +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -0,0 +1,9 @@ +#include "GPU/Vulkan/TextureCacheVulkan.h" + +bool TextureCacheVulkan::AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset) { + return false; +} + +void TextureCacheVulkan::DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) { + +} diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index fac5cde8c9..64f2c5bbd1 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -29,12 +29,8 @@ public: return false; } - bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset = 0) override { - return false; - } - void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) override { - - } + bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset = 0) override; + void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) override; void DownloadFramebufferForClut(u32 clutAddr, u32 bytes) override { diff --git a/ext/native/thin3d/VulkanContext.cpp b/ext/native/thin3d/VulkanContext.cpp index 98eef940cd..0b0556dc4a 100644 --- a/ext/native/thin3d/VulkanContext.cpp +++ b/ext/native/thin3d/VulkanContext.cpp @@ -55,8 +55,6 @@ VulkanContext::VulkanContext(const char *app_name, uint32_t flags) swapchainImageCount(0), swap_chain_(nullptr), cmd_pool_(nullptr), - cmd_(nullptr), - cmdInitActive_(false), dbgCreateMsgCallback(nullptr), dbgDestroyMsgCallback(nullptr), queue_count(0), @@ -176,12 +174,24 @@ void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier); } +VkCommandBuffer VulkanContext::GetInitCommandBuffer() { + FrameData *frame = &frame_[curFrame_]; + if (!frame->hasInitCommands) { + VulkanBeginCommandBuffer(frame->cmdInit); + frame->hasInitCommands = true; + } + return frame_[curFrame_].cmdInit; +} + VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) { FrameData *frame = &frame_[curFrame_]; // Make sure the command buffer from the frame before the previous has been fully executed. WaitAndResetFence(frame->fence); + // Process pending deletes. + frame->deleteList.PerformDeletes(device_); + // 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? VkResult res = fpAcquireNextImageKHR(device_, swap_chain_, @@ -198,7 +208,7 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[ begin.pNext = NULL; begin.flags = 0; begin.pInheritanceInfo = nullptr; - res = vkBeginCommandBuffer(cmd_, &begin); + res = vkBeginCommandBuffer(frame->cmdBuf, &begin); TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image); @@ -219,6 +229,7 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[ } void VulkanContext::WaitUntilQueueIdle() { + // Should almost never be used vkQueueWaitIdle(gfx_queue_); } @@ -238,38 +249,6 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem return false; } -// Terrible for performance! -void vk_submit_sync(VkDevice device, VkSemaphore waitForSemaphore, VkQueue queue, VkCommandBuffer cmd) { - const VkCommandBuffer cmd_bufs[] = { cmd }; - - VkFenceCreateInfo fenceInfo; - VkFence drawFence; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.pNext = NULL; - fenceInfo.flags = 0; - vkCreateFence(device, &fenceInfo, NULL, &drawFence); - - VkSubmitInfo submit_info[1] = {}; - submit_info[0].pNext = NULL; - submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info[0].waitSemaphoreCount = waitForSemaphore ? 1 : 0; - submit_info[0].pWaitSemaphores = waitForSemaphore ? &waitForSemaphore : NULL; - submit_info[0].commandBufferCount = 1; - submit_info[0].pCommandBuffers = cmd_bufs; - submit_info[0].signalSemaphoreCount = 0; - submit_info[0].pSignalSemaphores = NULL; - - /* Queue the command buffer for execution */ - VkResult res = vkQueueSubmit(queue, 1, submit_info, drawFence); - assert(res == VK_SUCCESS); - - do { - res = vkWaitForFences(device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); - } while (res != VK_SUCCESS && res != VK_TIMEOUT); - - vkDestroyFence(device, drawFence, NULL); -} - void VulkanContext::EndSurfaceRenderPass() { FrameData *frame = &frame_[curFrame_]; vkCmdEndRenderPass(frame->cmdBuf); @@ -279,19 +258,27 @@ void VulkanContext::EndSurfaceRenderPass() { VkResult res = vkEndCommandBuffer(frame->cmdBuf); assert(res == VK_SUCCESS); - VkSubmitInfo submit_info = {}; - submit_info.pNext = NULL; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &frame->cmdBuf; - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = NULL; - res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence); - assert(res == VK_SUCCESS); + int numCmdBufs = 0; + VkCommandBuffer cmdBufs[2]; - // At this point we are certain that acquireSemaphore is of no further use and can be destroyed. + if (frame->hasInitCommands) { + vkEndCommandBuffer(frame->cmdInit); + cmdBufs[numCmdBufs++] = frame->cmdInit; + frame->hasInitCommands = false; + } + cmdBufs[numCmdBufs++] = frame->cmdBuf; + + VkSubmitInfo submit_info[1] = {}; + submit_info[0].pNext = NULL; + submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info[0].waitSemaphoreCount = 1; + submit_info[0].pWaitSemaphores = &acquireSemaphore; + submit_info[0].commandBufferCount = numCmdBufs; + submit_info[0].pCommandBuffers = cmdBufs; + submit_info[0].signalSemaphoreCount = 0; + submit_info[0].pSignalSemaphores = NULL; + res = vkQueueSubmit(gfx_queue_, 1, submit_info, frame->fence); + assert(res == VK_SUCCESS); VkPresentInfoKHR present; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; @@ -308,59 +295,59 @@ void VulkanContext::EndSurfaceRenderPass() { // return codes assert(!res); + frame->deleteList.Ingest(globalDeleteList_); curFrame_ ^= 1; } -void VulkanContext::BeginInitCommandBuffer() { - assert(!cmdInitActive_); - VulkanBeginCommandBuffer(cmd_); - cmdInitActive_ = true; -} - -void VulkanContext::SubmitInitCommandBufferSync() { - if (cmdInitActive_) { - vkEndCommandBuffer(cmd_); - vk_submit_sync(device_, 0, gfx_queue_, cmd_); - cmdInitActive_ = false; - } +void VulkanBeginCommandBuffer(VkCommandBuffer cmd) { + VkResult U_ASSERT_ONLY res; + VkCommandBufferBeginInfo cmd_buf_info = {}; + cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_info.pNext = NULL; + cmd_buf_info.pInheritanceInfo = nullptr; + cmd_buf_info.flags = 0; + res = vkBeginCommandBuffer(cmd, &cmd_buf_info); + assert(res == VK_SUCCESS); } void VulkanContext::InitObjects(HINSTANCE hInstance, HWND hWnd, bool depthPresent) { InitSurfaceAndQueue(hInstance, hWnd); InitCommandPool(); - InitCommandBuffer(); - BeginInitCommandBuffer(); - InitSwapchain(); - InitDepthStencilBuffer(); - - InitSurfaceRenderPass(depthPresent, true); - InitFramebuffers(depthPresent); - SubmitInitCommandBufferSync(); - // Create frame data - VkCommandBufferAllocateInfo cmd = {}; - cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmd.pNext = NULL; - cmd.commandPool = cmd_pool_; - cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd.commandBufferCount = 2; + VkCommandBufferAllocateInfo cmd_alloc = {}; + cmd_alloc.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_alloc.pNext = NULL; + cmd_alloc.commandPool = cmd_pool_; + cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_alloc.commandBufferCount = 4; - VkCommandBuffer cmdBuf[2]; - VkResult res = vkAllocateCommandBuffers(device_, &cmd, cmdBuf); + VkCommandBuffer cmdBuf[4]; + VkResult res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf); assert(res == VK_SUCCESS); frame_[0].cmdBuf = cmdBuf[0]; + frame_[0].cmdInit = cmdBuf[1]; frame_[0].fence = CreateFence(true); // So it can be instantly waited on - frame_[1].cmdBuf = cmdBuf[1]; + frame_[1].cmdBuf = cmdBuf[2]; + frame_[1].cmdInit = cmdBuf[3]; frame_[1].fence = CreateFence(true); + + VkCommandBuffer cmd = GetInitCommandBuffer(); + InitSwapchain(cmd); + InitDepthStencilBuffer(cmd); + + InitSurfaceRenderPass(depthPresent, true); + InitFramebuffers(depthPresent); + + // The init command buffer will be executed as part of the first frame. } void VulkanContext::DestroyObjects() { - VkCommandBuffer cmdBuf[2] = { frame_[0].cmdBuf, frame_[1].cmdBuf }; + VkCommandBuffer cmdBuf[4] = { frame_[0].cmdBuf, frame_[0].cmdInit, frame_[1].cmdBuf, frame_[1].cmdInit}; - vkFreeCommandBuffers(device_, cmd_pool_, 2, cmdBuf); + vkFreeCommandBuffers(device_, cmd_pool_, sizeof(cmdBuf)/sizeof(cmdBuf[0]), cmdBuf); vkDestroyFence(device_, frame_[0].fence, nullptr); vkDestroyFence(device_, frame_[1].fence, nullptr); @@ -368,7 +355,6 @@ void VulkanContext::DestroyObjects() { DestroySurfaceRenderPass(); DestroyDepthStencilBuffer(); DestroySwapChain(); - DestroyCommandBuffer(); DestroyCommandPool(); } @@ -498,6 +484,9 @@ VkResult VulkanContext::InitDeviceExtensionProperties(layer_properties &layer_pr return res; } +/* + * TODO: function description here + */ VkResult VulkanContext::InitDeviceLayerProperties() { uint32_t device_layer_count; VkLayerProperties *vk_props = NULL; @@ -670,7 +659,7 @@ void VulkanContext::DestroyDebugMsgCallback() { } } -void VulkanContext::InitDepthStencilBuffer() { +void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; VkImageCreateInfo image_info = {}; @@ -752,7 +741,7 @@ void VulkanContext::InitDepthStencilBuffer() { assert(res == VK_SUCCESS); /* Set the image layout to depth stencil optimal */ - TransitionImageLayout(cmd_, depth.image, + TransitionImageLayout(cmd, depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -867,7 +856,7 @@ void VulkanContext::InitSurfaceAndQueue(HINSTANCE conn, HWND wnd) { assert(res == VK_SUCCESS); } -void VulkanContext::InitSwapchain() { +void VulkanContext::InitSwapchain(VkCommandBuffer cmd) { VkResult U_ASSERT_ONLY res; VkSurfaceCapabilitiesKHR surfCapabilities; @@ -991,7 +980,7 @@ void VulkanContext::InitSwapchain() { // 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. - TransitionImageLayout(cmd_, sc_buffer.image, + TransitionImageLayout(cmd, sc_buffer.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); @@ -1105,31 +1094,6 @@ void VulkanContext::InitCommandPool() { assert(res == VK_SUCCESS); } -void VulkanContext::InitCommandBuffer() { - VkResult U_ASSERT_ONLY res; - - VkCommandBufferAllocateInfo cmd = {}; - cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmd.pNext = NULL; - cmd.commandPool = cmd_pool_; - cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd.commandBufferCount = 1; - - res = vkAllocateCommandBuffers(device_, &cmd, &this->cmd_); - assert(res == VK_SUCCESS); -} - -void VulkanBeginCommandBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - VkCommandBufferBeginInfo cmd_buf_info = {}; - cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmd_buf_info.pNext = NULL; - cmd_buf_info.flags = 0; - cmd_buf_info.pInheritanceInfo = nullptr; - res = vkBeginCommandBuffer(cmd, &cmd_buf_info); - assert(res == VK_SUCCESS); -} - void VulkanTexture::Create(VulkanContext *vulkan, int w, int h) { tex_width = w; tex_height = h; @@ -1139,7 +1103,7 @@ void VulkanTexture::Create(VulkanContext *vulkan, int w, int h) { VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(vulkan->GetPhysicalDevice(), VK_FORMAT_R8G8B8A8_UNORM, &formatProps); - /* See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data */ + // See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; VkImageCreateInfo image_create_info = {}; @@ -1154,8 +1118,7 @@ void VulkanTexture::Create(VulkanContext *vulkan, int w, int h) { image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_LINEAR; - image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : - VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -1198,9 +1161,9 @@ uint8_t *VulkanTexture::Lock(VulkanContext *vulkan, int *rowPitch) { void *data; /* Get the subresource layout so we know what the row pitch is */ - vkGetImageSubresourceLayout(vulkan->device_, mappableImage, &subres, &layout); + vkGetImageSubresourceLayout(vulkan->GetDevice(), mappableImage, &subres, &layout); - VkResult res = vkMapMemory(vulkan->device_, mappableMemory, 0, mem_reqs.size, 0, &data); + VkResult res = vkMapMemory(vulkan->GetDevice(), mappableMemory, 0, mem_reqs.size, 0, &data); assert(res == VK_SUCCESS); *rowPitch = (int)layout.rowPitch; @@ -1208,17 +1171,16 @@ uint8_t *VulkanTexture::Lock(VulkanContext *vulkan, int *rowPitch) { } void VulkanTexture::Unlock(VulkanContext *vulkan) { - vkUnmapMemory(vulkan->device_, mappableMemory); + vkUnmapMemory(vulkan->GetDevice(), mappableMemory); + + VkCommandBuffer cmd = vulkan->GetInitCommandBuffer(); if (!needStaging) { /* If we can use the linear tiled image as a texture, just do it */ image = mappableImage; mem = mappableMemory; imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - TransitionImageLayout(vulkan->GetInitCommandBuffer(), image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - imageLayout); + TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout); } else { VkImageCreateInfo image_create_info = {}; image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -1235,14 +1197,15 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) { image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.flags = 0; - /* The mappable image cannot be our texture, so create an optimally tiled image and blit to it */ + // The mappable image cannot be our texture, so create an optimally tiled image and blit to it image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkResult res = vkCreateImage(vulkan->device_, &image_create_info, NULL, &image); + VkResult res = vkCreateImage(vulkan->GetDevice(), &image_create_info, NULL, &image); assert(res == VK_SUCCESS); - vkGetImageMemoryRequirements(vulkan->device_, image, &mem_reqs); + vkGetImageMemoryRequirements(vulkan->GetDevice(), image, &mem_reqs); VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; @@ -1256,22 +1219,22 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) { assert(pass); /* allocate memory */ - res = vkAllocateMemory(vulkan->device_, &mem_alloc, NULL, &mem); + res = vkAllocateMemory(vulkan->GetDevice(), &mem_alloc, NULL, &mem); assert(res == VK_SUCCESS); /* bind memory */ - res = vkBindImageMemory(vulkan->device_, image, mem, 0); + res = vkBindImageMemory(vulkan->GetDevice(), image, mem, 0); assert(res == VK_SUCCESS); /* Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL */ /* Side effect is that this will create info.cmd */ - TransitionImageLayout(vulkan->GetInitCommandBuffer(), mappableImage, + TransitionImageLayout(cmd, mappableImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); /* Since we're going to blit to the texture image, set its layout to DESTINATION_OPTIMAL */ - TransitionImageLayout(vulkan->GetInitCommandBuffer(), image, + TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); @@ -1295,59 +1258,23 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) { copy_region.extent.height = tex_height; copy_region.extent.depth = 1; - VkCommandBufferBeginInfo cmd_buf_info = {}; - cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmd_buf_info.pNext = NULL; - cmd_buf_info.flags = 0; - cmd_buf_info.pInheritanceInfo = nullptr; - - res = vkBeginCommandBuffer(vulkan->cmd_, &cmd_buf_info); - assert(res == VK_SUCCESS); - - /* Put the copy command into the command buffer */ - vkCmdCopyImage(vulkan->cmd_, + // Put the copy command into the command buffer + vkCmdCopyImage(cmd, mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); - res = vkEndCommandBuffer(vulkan->cmd_); - assert(res == VK_SUCCESS); - const VkCommandBuffer cmd_bufs[] = { vulkan->cmd_ }; - VkFence nullFence = VK_NULL_HANDLE; - - VkSubmitInfo submit_info[1] = {}; - submit_info[0].pNext = NULL; - submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info[0].waitSemaphoreCount = 0; - submit_info[0].pWaitSemaphores = NULL; - submit_info[0].commandBufferCount = 1; - submit_info[0].pCommandBuffers = cmd_bufs; - submit_info[0].signalSemaphoreCount = 0; - submit_info[0].pSignalSemaphores = NULL; - - // Queue the command buffer for execution. We can't legally delete the staging source - // until this has completed, so let's throw in a fence. Note that this is a huge stall - // that could be avoided with smarter code - for example we can check the fence the next - // time the texture is used and discard the staging image then. - - VkFence fence = vulkan->CreateFence(false); - - res = vkQueueSubmit(vulkan->gfx_queue_, 1, submit_info, fence); assert(res == VK_SUCCESS); /* Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY */ imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - TransitionImageLayout(vulkan->GetInitCommandBuffer(), image, + TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout); - vulkan->WaitAndResetFence(fence); - - /* Release the resources for the staging image */ - vkFreeMemory(vulkan->device_, mappableMemory, NULL); - vkDestroyImage(vulkan->device_, mappableImage, NULL); - vkDestroyFence(vulkan->device_, fence, NULL); + vulkan->QueueDelete(mappableMemory); + vulkan->QueueDelete(mappableImage); } VkImageViewCreateInfo view_info = {}; @@ -1368,14 +1295,14 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) { /* create image view */ view_info.image = image; - VkResult res = vkCreateImageView(vulkan->device_, &view_info, NULL, &view); + VkResult res = vkCreateImageView(vulkan->GetDevice(), &view_info, NULL, &view); assert(res == VK_SUCCESS); } void VulkanTexture::Destroy(VulkanContext *vulkan) { - vkDestroyImageView(vulkan->device_, view, NULL); - vkDestroyImage(vulkan->device_, image, NULL); - vkFreeMemory(vulkan->device_, mem, NULL); + vkDestroyImageView(vulkan->GetDevice(), view, NULL); + vkDestroyImage(vulkan->GetDevice(), image, NULL); + vkFreeMemory(vulkan->GetDevice(), mem, NULL); view = NULL; image = NULL; mem = NULL; @@ -1392,16 +1319,8 @@ VkFence VulkanContext::CreateFence(bool presignalled) { } void VulkanContext::WaitAndResetFence(VkFence fence) { - VkResult res = vkWaitForFences(device_, 1, &fence, true, UINT64_MAX); - assert(!res); - res = vkResetFences(device_, 1, &fence); - assert(!res); -} - -void VulkanContext::DestroyCommandBuffer() { - VkCommandBuffer cmd_bufs[1] = { cmd_ }; - vkFreeCommandBuffers(device_, cmd_pool_, 1, cmd_bufs); - cmd_ = nullptr; + vkWaitForFences(device_, 1, &fence, true, UINT64_MAX); + vkResetFences(device_, 1, &fence); } void VulkanContext::DestroyCommandPool() { @@ -1473,9 +1392,13 @@ void TransitionImageLayout( image_memory_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { /* Make sure anything that was copying from this image has completed */ - image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT|VK_ACCESS_MEMORY_READ_BIT; } if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { diff --git a/ext/native/thin3d/VulkanContext.h b/ext/native/thin3d/VulkanContext.h index 7eaba87e59..fcf4e84862 100644 --- a/ext/native/thin3d/VulkanContext.h +++ b/ext/native/thin3d/VulkanContext.h @@ -64,6 +64,34 @@ struct layer_properties { std::vector extensions; }; +class VulkanDeleteList { +public: + void QueueDelete(VkImage image) { images_.push_back(image); } + void QueueDelete(VkDeviceMemory deviceMemory) { memory_.push_back(deviceMemory); } + + void Ingest(VulkanDeleteList &del) { + images_ = del.images_; + memory_ = del.memory_; + del.images_.clear(); + del.memory_.clear(); + } + + void PerformDeletes(VkDevice device) { + for (auto &mem : memory_) { + vkFreeMemory(device, mem, nullptr); + } + memory_.clear(); + for (auto &image : images_) { + vkDestroyImage(device, image, nullptr); + } + images_.clear(); + } + +private: + std::vector images_; + std::vector memory_; +}; + // 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. class VulkanContext { @@ -77,29 +105,28 @@ public: return device_; } + template + void QueueDelete(T mem) { + globalDeleteList_.QueueDelete(mem); + } + void InitSurfaceAndQueue(HINSTANCE conn, HWND wnd); - void InitSwapchain(); + void InitSwapchain(VkCommandBuffer cmd); void InitSurfaceRenderPass(bool include_depth, bool clear); void InitFramebuffers(bool include_depth); - void InitDepthStencilBuffer(); + void InitDepthStencilBuffer(VkCommandBuffer cmd); void InitCommandPool(); - void InitCommandBuffer(); void InitObjects(HINSTANCE hInstance, HWND hWnd, bool depthPresent); void DestroyObjects(); - void BeginInitCommandBuffer(); - void SubmitInitCommandBufferSync(); - VkCommandBuffer GetInitCommandBuffer() { - return cmd_; - } + VkCommandBuffer GetInitCommandBuffer(); void DestroySurfaceRenderPass(); void DestroyFramebuffers(); void DestroySwapChain(); void DestroyDepthStencilBuffer(); void DestroyCommandPool(); - void DestroyCommandBuffer(); void DestroyDevice(); void WaitUntilQueueIdle(); @@ -147,7 +174,7 @@ public: VkResult InitDeviceExtensionProperties(layer_properties &layer_props); VkResult InitDeviceLayerProperties(); - +private: VkSemaphore acquireSemaphore; #ifdef _WIN32 @@ -162,9 +189,8 @@ public: xcb_intern_atom_reply_t *atom_wm_delete_window; #endif // _WIN32 + // TODO: Move to frame data VkCommandPool cmd_pool_; - VkCommandBuffer cmd_; // Buffer for initialization commands - bool cmdInitActive_; VkInstance instance_; VkDevice device_; @@ -190,7 +216,6 @@ public: std::vector queue_props; VkPhysicalDeviceMemoryProperties memory_properties; -private: struct swap_chain_buffer { VkImage image; VkImageView view; @@ -208,12 +233,23 @@ private: // Manages flipping command buffers for the backbuffer render pass. // It is recommended to do the same for other rendering passes. struct FrameData { + FrameData() : fence(nullptr), hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {} + VkFence fence; + bool hasInitCommands; + VkCommandBuffer cmdInit; VkCommandBuffer cmdBuf; + + VulkanDeleteList deleteList; }; + FrameData frame_[2]; int curFrame_; + // At the end of the frame, this is copied into the frame's delete list, so it can be processed + // the next time the frame comes around again. + VulkanDeleteList globalDeleteList_; + // Simple loader for the WSI extension. PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; @@ -272,8 +308,6 @@ VkBool32 CheckLayers(const std::vector &layer_props, const std void VulkanBeginCommandBuffer(VkCommandBuffer cmd); -void vk_submit_sync(VkDevice device, VkSemaphore waitForSemaphore, VkQueue queue, VkCommandBuffer cmd); - void init_glslang(); void finalize_glslang(); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b421af57e4..8b55ce6bd3 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -351,6 +351,7 @@ public: info->pVertexAttributeDescriptions = attrDescs; info->vertexBindingDescriptionCount = 1; info->pVertexBindingDescriptions = bindDescs; + info->flags = 0; } bool RequiresBuffer() { @@ -512,8 +513,6 @@ private: void ApplyDynamicState(); void DirtyDynamicState(); - void BeginInitCommands(); - VulkanContext *vulkan_; // These are used to compose the pipeline cache key. @@ -554,15 +553,7 @@ private: Thin3DVKTexture *boundTextures_[MAX_BOUND_TEXTURES]; Thin3DVKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES]; - // Ephemeral command buffer used for initializing textures, etc. As there can only be one in flight, can cause annoying stalls until I do something better. - VkCommandBuffer initCmd_; - bool hasInitCommands_; - VkFence initFence_; - - // TODO: Transpose this into a struct FrameObject[2]. - VkCommandBuffer cmd_; // The current one - VulkanPushBuffer *pushBuffer_[2]; int frameNum_; VulkanPushBuffer *push_; @@ -753,22 +744,6 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan) res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_); assert(VK_SUCCESS == res); - VkCommandBufferAllocateInfo cb; - cb.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cb.pNext = nullptr; - cb.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cb.commandPool = cmdPool_; - cb.commandBufferCount = 1; - res = vkAllocateCommandBuffers(device_, &cb, &initCmd_); - assert(VK_SUCCESS == res); - hasInitCommands_ = false; - - VkFenceCreateInfo f; - f.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - f.pNext = nullptr; - f.flags = 0; - vkCreateFence(device_, &f, nullptr, &initFence_); - VkPipelineCacheCreateInfo pc; pc.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; pc.pNext = nullptr; @@ -811,42 +786,9 @@ void Thin3DVKContext::Begin(bool clear, uint32_t colorval, float depthVal, int s viewportDirty_ = true; } -void Thin3DVKContext::BeginInitCommands() { - assert(!hasInitCommands_); - - VkCommandBufferBeginInfo begin; - begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin.pNext = nullptr; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(initCmd_, &begin); - assert(VK_SUCCESS == res); - hasInitCommands_ = true; -} - void Thin3DVKContext::End() { // Stop collecting data in the frame data buffer. push_->End(device_); - - // IF something needs to be uploaded etc, sneak it in before we actually run the main command buffer. - if (hasInitCommands_) { - VkResult res = vkEndCommandBuffer(initCmd_); - assert(VK_SUCCESS == res); - - // Run the texture uploads etc _before_ we execute the ordinary command buffer - VkSubmitInfo submit = {}; - submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit.pCommandBuffers = &initCmd_; - submit.commandBufferCount = 1; - res = vkQueueSubmit(queue_, 1, &submit, initFence_); - assert(VK_SUCCESS == res); - // Before we can begin, we must be sure that the command buffer is no longer in use, as we only have a single one for init - // tasks (for now). - vulkan_->WaitAndResetFence(initFence_); - hasInitCommands_ = false; - // Init cmd buffer is again available for writing. - } - vulkan_->EndSurfaceRenderPass(); frameNum_++; @@ -899,6 +841,8 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() { writes[0].dstArrayElement = 0; writes[0].dstBinding = 0; writes[0].pBufferInfo = &bufferDesc; + writes[0].pImageInfo = nullptr; + writes[0].pTexelBufferView = nullptr; writes[0].descriptorCount = 1; writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; @@ -907,7 +851,9 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() { writes[1].dstSet = descSet; writes[1].dstArrayElement = 0; writes[1].dstBinding = 1; + writes[1].pBufferInfo = nullptr; writes[1].pImageInfo = &imageDesc; + writes[1].pTexelBufferView = nullptr; writes[1].descriptorCount = 1; writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1207,10 +1153,8 @@ void Thin3DVKContext::SetTextures(int start, int count, Thin3DTexture **textures boundTextures_[i] = static_cast(textures[i]); // Bind simply copies the texture to VRAM if needed. if (boundTextures_[i]->NeedsUpload()) { - if (!hasInitCommands_) { - BeginInitCommands(); - } - boundTextures_[i]->Upload(initCmd_); + VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); + boundTextures_[i]->Upload(cmd); } } }