Skip to content

Commit

Permalink
Merge pull request #9713 from hrydgard/vulkan-improvements
Browse files Browse the repository at this point in the history
Vulkan - initial implementation of buffered rendering, fixes for Mali/Android
  • Loading branch information
hrydgard committed May 30, 2017
2 parents 499b32e + 44423f3 commit c109f84
Show file tree
Hide file tree
Showing 49 changed files with 1,431 additions and 725 deletions.
50 changes: 28 additions & 22 deletions Common/Vulkan/VulkanContext.cpp
Expand Up @@ -86,6 +86,7 @@ VulkanContext::VulkanContext(const char *app_name, int app_ver, uint32_t flags)
instance_extension_names.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#endif
device_extension_names.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// device_extension_names.push_back(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME);

if (flags & VULKAN_FLAG_VALIDATE) {
for (size_t i = 0; i < ARRAY_SIZE(validationLayers); i++) {
Expand Down Expand Up @@ -215,13 +216,11 @@ void VulkanContext::QueueBeforeSurfaceRender(VkCommandBuffer cmd) {
cmdQueue_.push_back(cmd);
}

VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) {
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, &current_buffer);

// TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
// return codes
assert(res == VK_SUCCESS);
Expand All @@ -238,7 +237,11 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
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];
Expand All @@ -248,18 +251,18 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
rp_begin.renderArea.extent.height = height_;
rp_begin.clearValueCount = 2;
rp_begin.pClearValues = clear_values;

// We don't really need to record this at this point in time, but hey, at some point we'll start this
// pass anyway so might as well do it now (although you can imagine getting away with just a stretchblt and not
// even starting a final render pass if there's nothing to overlay... hm. Uncommon though on mobile).
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);
Expand All @@ -285,21 +288,21 @@ void VulkanContext::EndSurfaceRenderPass() {
VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &acquireSemaphore;
VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
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 = 0;
submit_info.pSignalSemaphores = NULL;
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 = &current_buffer;
present.pWaitSemaphores = NULL;
present.waitSemaphoreCount = 0;
present.pWaitSemaphores = &renderingCompleteSemaphore;
present.waitSemaphoreCount = 1;
present.pResults = NULL;

res = vkQueuePresentKHR(gfx_queue_, &present);
Expand Down Expand Up @@ -942,13 +945,11 @@ void VulkanContext::InitQueue() {
vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_);
ILOG("gfx_queue_: %p", gfx_queue_);

VkSemaphoreCreateInfo acquireSemaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
acquireSemaphoreCreateInfo.flags = 0;

res = vkCreateSemaphore(device_,
&acquireSemaphoreCreateInfo,
NULL,
&acquireSemaphore);
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
semaphoreCreateInfo.flags = 0;
res = vkCreateSemaphore(device_, &semaphoreCreateInfo, NULL, &acquireSemaphore);
assert(res == VK_SUCCESS);
res = vkCreateSemaphore(device_, &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore);
assert(res == VK_SUCCESS);
}

Expand Down Expand Up @@ -1063,7 +1064,9 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
&swapchainImageCount, NULL);
assert(res == VK_SUCCESS);

VkImage* swapchainImages = (VkImage*)malloc(swapchainImageCount * sizeof(VkImage));
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);
Expand Down Expand Up @@ -1101,8 +1104,7 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
swapChainBuffers.push_back(sc_buffer);
assert(res == VK_SUCCESS);
}
free(swapchainImages);

delete[] swapchainImages;
current_buffer = 0;

return true;
Expand Down Expand Up @@ -1242,6 +1244,7 @@ void VulkanContext::DestroySwapChain() {
swap_chain_ = VK_NULL_HANDLE;
swapChainBuffers.clear();
vkDestroySemaphore(device_, acquireSemaphore, NULL);
vkDestroySemaphore(device_, renderingCompleteSemaphore, NULL);
}

void VulkanContext::DestroyFramebuffers() {
Expand Down Expand Up @@ -1300,6 +1303,9 @@ void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlag
if (old_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
image_memory_barrier.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
}
if (old_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
image_memory_barrier.srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
}

if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
image_memory_barrier.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
Expand Down
6 changes: 6 additions & 0 deletions Common/Vulkan/VulkanContext.h
Expand Up @@ -258,17 +258,22 @@ class VulkanContext {

VkCommandBuffer GetInitCommandBuffer();

VkFramebuffer GetSurfaceFramebuffer() {
return framebuffers_[current_buffer];
}
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
VkCommandBuffer GetSurfaceCommandBuffer() {
return frame_[curFrame_ & 1].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 EndFrame();

void QueueBeforeSurfaceRender(VkCommandBuffer cmd);

Expand Down Expand Up @@ -312,6 +317,7 @@ class VulkanContext {

private:
VkSemaphore acquireSemaphore;
VkSemaphore renderingCompleteSemaphore;

#ifdef _WIN32
HINSTANCE connection; // hInstance - Windows Instance
Expand Down
8 changes: 8 additions & 0 deletions Common/Vulkan/VulkanImage.cpp
Expand Up @@ -362,6 +362,14 @@ void VulkanTexture::EndCreate() {
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}

void VulkanTexture::TransitionForUpload() {
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
TransitionImageLayout(cmd, image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
}

void VulkanTexture::Destroy() {
if (view != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteImageView(view);
Expand Down
3 changes: 3 additions & 0 deletions Common/Vulkan/VulkanImage.h
Expand Up @@ -34,6 +34,9 @@ class VulkanTexture {
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();

void TransitionForUpload();

int GetNumMips() const { return numMips_; }
void Destroy();

Expand Down
1 change: 1 addition & 0 deletions Common/Vulkan/VulkanMemory.h
Expand Up @@ -51,6 +51,7 @@ class VulkanPushBuffer {
void Unmap() {
assert(writePtr_);
/*
// Should not need this since we use coherent memory.
VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
range.offset = 0;
range.size = offset_;
Expand Down

0 comments on commit c109f84

Please sign in to comment.