[Vulkan v2] Present loop

This commit is contained in:
Triang3l 2019-08-11 17:49:01 +03:00
parent 675034670e
commit be6fa2b577
2 changed files with 180 additions and 6 deletions

View File

@ -123,6 +123,7 @@ bool VulkanContext::Initialize() {
surface_min_image_count_ = std::min(surface_min_image_count_,
surface_capabilities.maxImageCount);
}
surface_transform_ = surface_capabilities.currentTransform;
if (surface_capabilities.supportedCompositeAlpha &
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) {
surface_composite_alpha_ = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
@ -192,12 +193,36 @@ bool VulkanContext::Initialize() {
}
}
// Create presentation semaphores.
VkSemaphoreCreateInfo semaphore_create_info;
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_create_info.pNext = nullptr;
semaphore_create_info.flags = 0;
if (vkCreateSemaphore(device, &semaphore_create_info, nullptr,
&semaphore_present_complete_) != VK_SUCCESS) {
XELOGE(
"Failed to create a Vulkan semaphone for awaiting swapchain image "
"acquisition");
Shutdown();
return false;
}
if (vkCreateSemaphore(device, &semaphore_create_info, nullptr,
&semaphore_draw_complete_) != VK_SUCCESS) {
XELOGE(
"Failed to create a Vulkan semaphone for awaiting drawing before "
"presentation");
Shutdown();
return false;
}
// Initialize the immediate mode drawer if not offscreen.
immediate_drawer_ = std::make_unique<VulkanImmediateDrawer>(this);
if (!immediate_drawer_->Initialize()) {
Shutdown();
return false;
}
swapchain_ = VK_NULL_HANDLE;
}
return true;
@ -214,9 +239,18 @@ void VulkanContext::Shutdown() {
initialized_fully_ = false;
immediate_drawer_.reset();
if (target_window_) {
util::DestroyAndNullHandle(vkDestroySwapchainKHR, device, swapchain_);
util::DestroyAndNullHandle(vkDestroySurfaceKHR, instance, surface_);
immediate_drawer_.reset();
util::DestroyAndNullHandle(vkDestroySemaphore, device,
semaphore_draw_complete_);
util::DestroyAndNullHandle(vkDestroySemaphore, device,
semaphore_present_complete_);
util::DestroyAndNullHandle(vkDestroySurfaceKHR, instance, surface_);
}
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
util::DestroyAndNullHandle(vkDestroyFence, device, fences_[i]);
@ -238,7 +272,8 @@ void VulkanContext::BeginSwap() {
return;
}
auto device = GetVulkanProvider()->GetDevice();
auto provider = GetVulkanProvider();
auto device = provider->GetDevice();
// Await the availability of transient objects for the new frame.
// The frame number is incremented in EndSwap so it can be treated the same
@ -253,6 +288,95 @@ void VulkanContext::BeginSwap() {
if (last_completed_frame_ + kQueuedFrames < current_frame_) {
last_completed_frame_ = current_frame_ - kQueuedFrames;
}
if (target_window_) {
VkExtent2D target_window_extent = {
uint32_t(target_window_->scaled_width()),
uint32_t(target_window_->scaled_height())};
// On certain platforms (like Windows), swapchain size must match the window
// size.
VkSurfaceTransformFlagBitsKHR current_transform = surface_transform_;
VkSurfaceCapabilitiesKHR surface_capabilities;
if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
provider->GetPhysicalDevice(), surface_, &surface_capabilities) ==
VK_SUCCESS) {
current_transform = surface_capabilities.currentTransform;
}
bool create_swapchain =
swapchain_ == VK_NULL_HANDLE ||
swapchain_extent_.width != target_window_extent.width ||
swapchain_extent_.height != target_window_extent.height ||
surface_transform_ != current_transform;
if (!create_swapchain) {
// Try to acquire the image for the existing swapchain or check if out of
// date.
VkResult acquire_result = vkAcquireNextImageKHR(
device, swapchain_, UINT64_MAX, semaphore_present_complete_,
VK_NULL_HANDLE, &swapchain_acquired_image_index_);
if (acquire_result == VK_ERROR_OUT_OF_DATE_KHR) {
create_swapchain = true;
} else if (acquire_result != VK_SUCCESS &&
acquire_result != VK_SUBOPTIMAL_KHR) {
// Checking for resizing externally. For proper suboptimal handling,
// either the swapchain needs to be recreated in the next frame, or the
// semaphore must be awaited right now (since suboptimal is a successful
// result). Assume all other errors are fatal.
context_lost_ = true;
return;
}
}
if (create_swapchain) {
if (swapchain_ != VK_NULL_HANDLE) {
AwaitAllFramesCompletion();
if (context_lost_) {
return;
}
}
// TODO(Triang3l): Destroy the old pass, framebuffer and image views.
// Destroying the old swapchain after creating the new one because
// swapchain creation needs the old one.
VkSwapchainKHR swapchain;
VkSwapchainCreateInfoKHR swapchain_create_info;
swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_create_info.pNext = nullptr;
swapchain_create_info.flags = 0;
swapchain_create_info.surface = surface_;
swapchain_create_info.minImageCount = surface_min_image_count_;
swapchain_create_info.imageFormat = surface_format_.format;
swapchain_create_info.imageColorSpace = surface_format_.colorSpace;
swapchain_create_info.imageExtent = target_window_extent;
swapchain_create_info.imageArrayLayers = 1;
swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain_create_info.queueFamilyIndexCount = 0;
swapchain_create_info.pQueueFamilyIndices = nullptr;
swapchain_create_info.preTransform = current_transform;
swapchain_create_info.compositeAlpha = surface_composite_alpha_;
swapchain_create_info.presentMode = surface_present_mode_;
swapchain_create_info.clipped = VK_TRUE;
swapchain_create_info.oldSwapchain = swapchain_;
if (vkCreateSwapchainKHR(device, &swapchain_create_info, nullptr,
&swapchain) != VK_SUCCESS) {
context_lost_ = true;
return;
}
if (swapchain_ != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(device, swapchain_, nullptr);
}
swapchain_ = swapchain;
swapchain_extent_ = target_window_extent;
surface_transform_ = current_transform;
// TODO(Triang3l): Get images, create the framebuffer and the passes.
VkResult acquire_result = vkAcquireNextImageKHR(
device, swapchain_, UINT64_MAX, semaphore_present_complete_,
VK_NULL_HANDLE, &swapchain_acquired_image_index_);
if (acquire_result != VK_SUCCESS && acquire_result != VK_SUBOPTIMAL_KHR) {
context_lost_ = true;
return;
}
}
// TODO(Triang3l): Insert a barrier and clear.
}
}
void VulkanContext::EndSwap() {
@ -260,8 +384,49 @@ void VulkanContext::EndSwap() {
return;
}
auto provider = GetVulkanProvider();
auto queue = provider->GetGraphicsQueue();
if (target_window_ != nullptr) {
// Present.
// TODO(Triang3l): Insert a barrier.
VkSubmitInfo submit_info;
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = nullptr;
submit_info.waitSemaphoreCount = 0;
submit_info.pWaitSemaphores = nullptr;
submit_info.pWaitDstStageMask = nullptr;
submit_info.commandBufferCount = 0;
submit_info.pCommandBuffers = nullptr;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &semaphore_draw_complete_;
if (vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
context_lost_ = true;
return;
}
VkPresentInfoKHR present_info;
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.pNext = nullptr;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &semaphore_draw_complete_;
present_info.swapchainCount = 1;
present_info.pSwapchains = &swapchain_;
present_info.pImageIndices = &swapchain_acquired_image_index_;
present_info.pResults = nullptr;
VkResult present_result = vkQueuePresentKHR(queue, &present_info);
if (present_result != VK_SUCCESS && present_result != VK_SUBOPTIMAL_KHR &&
present_result != VK_ERROR_OUT_OF_DATE_KHR) {
// VK_ERROR_OUT_OF_DATE_KHR will be handled in the next BeginSwap. In case
// of it, the semaphore will be unsignaled anyway:
// https://github.com/KhronosGroup/Vulkan-Docs/issues/572
context_lost_ = true;
return;
}
}
// Go to the next transient object frame.
auto queue = GetVulkanProvider()->GetGraphicsQueue();
VkFence fence = fences_[current_queue_frame_];
vkResetFences(provider->GetDevice(), 1, &fence);
if (vkQueueSubmit(queue, 0, nullptr, fences_[current_queue_frame_]) !=
VK_SUCCESS) {
context_lost_ = true;
@ -275,7 +440,7 @@ void VulkanContext::EndSwap() {
}
std::unique_ptr<RawImage> VulkanContext::Capture() {
// TODO(Triang3l): Read back swap chain front buffer.
// TODO(Triang3l): Read back swapchain front buffer.
return nullptr;
}

View File

@ -75,11 +75,20 @@ class VulkanContext : public GraphicsContext {
uint32_t surface_min_image_count_ = 3;
VkSurfaceFormatKHR surface_format_ = {VK_FORMAT_R8G8B8A8_UNORM,
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
VkPresentModeKHR surface_present_mode_ = VK_PRESENT_MODE_FIFO_KHR;
VkSurfaceTransformFlagBitsKHR surface_transform_ =
VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
VkCompositeAlphaFlagBitsKHR surface_composite_alpha_ =
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkPresentModeKHR surface_present_mode_ = VK_PRESENT_MODE_FIFO_KHR;
VkSemaphore semaphore_present_complete_ = VK_NULL_HANDLE;
VkSemaphore semaphore_draw_complete_ = VK_NULL_HANDLE;
std::unique_ptr<VulkanImmediateDrawer> immediate_drawer_ = nullptr;
VkSwapchainKHR swapchain_ = VK_NULL_HANDLE;
VkExtent2D swapchain_extent_ = {};
uint32_t swapchain_acquired_image_index_ = 0;
};
} // namespace vk