Update VulkanContext

This commit is contained in:
Henrik Rydgard 2016-01-03 00:46:41 +01:00
parent cfbecf5071
commit e89a7f0c67
8 changed files with 174 additions and 269 deletions

View file

@ -24,9 +24,6 @@
#include "GPU/Common/GPUDebugInterface.h"
#include "Common/MemoryUtil.h"
#include <map>
#include <vector>
enum TextureFiltering {
TEX_FILTER_AUTO = 1,
TEX_FILTER_NEAREST = 2,

View file

@ -338,6 +338,7 @@
<ClCompile Include="Vulkan\PipelineManagerVulkan.cpp" />
<ClCompile Include="Vulkan\ShaderManagerVulkan.cpp" />
<ClCompile Include="Vulkan\StateMappingVulkan.cpp" />
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp" />
<ClCompile Include="Vulkan\VertexShaderGeneratorVulkan.cpp" />
</ItemGroup>
<ItemGroup>

View file

@ -403,5 +403,6 @@
<ClCompile Include="Vulkan\FramebufferVulkan.cpp" />
<ClCompile Include="Vulkan\FragmentShaderGeneratorVulkan.cpp" />
<ClCompile Include="Vulkan\ShaderManagerVulkan.cpp" />
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp" />
</ItemGroup>
</Project>

View file

@ -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) {
}

View file

@ -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 {

View file

@ -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, &copy_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) {

View file

@ -64,6 +64,34 @@ struct layer_properties {
std::vector<VkExtensionProperties> 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<VkImage> images_;
std::vector<VkDeviceMemory> 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 <class T>
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<VkQueueFamilyProperties> 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_properties> &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();

View file

@ -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<Thin3DVKTexture *>(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);
}
}
}