[Vulkan] Propagate swap chain initialization errors

This commit is contained in:
DrChat 2017-12-18 20:35:04 -06:00
parent 7ae5795cb8
commit 4ca8cafc1b
3 changed files with 110 additions and 56 deletions

View File

@ -94,7 +94,7 @@ bool VulkanContext::Initialize() {
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
swap_chain_ = std::make_unique<VulkanSwapChain>(provider->instance(), swap_chain_ = std::make_unique<VulkanSwapChain>(provider->instance(),
provider->device()); provider->device());
if (!swap_chain_->Initialize(surface)) { if (swap_chain_->Initialize(surface) != VK_SUCCESS) {
XELOGE("Unable to initialize swap chain"); XELOGE("Unable to initialize swap chain");
vkDestroySurfaceKHR(*provider->instance(), surface, nullptr); vkDestroySurfaceKHR(*provider->instance(), surface, nullptr);
return false; return false;

View File

@ -34,25 +34,31 @@ VulkanSwapChain::VulkanSwapChain(VulkanInstance* instance, VulkanDevice* device)
VulkanSwapChain::~VulkanSwapChain() { Shutdown(); } VulkanSwapChain::~VulkanSwapChain() { Shutdown(); }
bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) { VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
surface_ = surface; surface_ = surface;
VkBool32 surface_supported = false; VkBool32 surface_supported = false;
auto err = vkGetPhysicalDeviceSurfaceSupportKHR( auto status = vkGetPhysicalDeviceSurfaceSupportKHR(
*device_, device_->queue_family_index(), surface, &surface_supported); *device_, device_->queue_family_index(), surface, &surface_supported);
assert_true(surface_supported); assert_true(surface_supported);
CheckResult(err, "vkGetPhysicalDeviceSurfaceSupportKHR"); CheckResult(status, "vkGetPhysicalDeviceSurfaceSupportKHR");
if (status != VK_SUCCESS) {
return status;
}
// Query supported target formats. // Query supported target formats.
uint32_t count = 0; uint32_t count = 0;
err = status =
vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, nullptr); vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, nullptr);
CheckResult(err, "vkGetPhysicalDeviceSurfaceFormatsKHR"); CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR");
std::vector<VkSurfaceFormatKHR> surface_formats; std::vector<VkSurfaceFormatKHR> surface_formats;
surface_formats.resize(count); surface_formats.resize(count);
err = vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, status = vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count,
surface_formats.data()); surface_formats.data());
CheckResult(err, "vkGetPhysicalDeviceSurfaceFormatsKHR"); CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR");
if (status != VK_SUCCESS) {
return status;
}
// If the format list includes just one entry of VK_FORMAT_UNDEFINED the // If the format list includes just one entry of VK_FORMAT_UNDEFINED the
// surface has no preferred format. // surface has no preferred format.
@ -69,19 +75,29 @@ bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
// Query surface min/max/caps. // Query surface min/max/caps.
VkSurfaceCapabilitiesKHR surface_caps; VkSurfaceCapabilitiesKHR surface_caps;
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_, status = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_,
&surface_caps); &surface_caps);
CheckResult(err, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); CheckResult(status, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
if (status != VK_SUCCESS) {
return status;
}
// Query surface properties so we can configure ourselves within bounds. // Query surface properties so we can configure ourselves within bounds.
std::vector<VkPresentModeKHR> present_modes; std::vector<VkPresentModeKHR> present_modes;
err = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count, status = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count,
nullptr); nullptr);
CheckResult(err, "vkGetPhysicalDeviceSurfacePresentModesKHR"); CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR");
if (status != VK_SUCCESS) {
return status;
}
present_modes.resize(count); present_modes.resize(count);
err = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count, status = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count,
present_modes.data()); present_modes.data());
CheckResult(err, "vkGetPhysicalDeviceSurfacePresentModesKHR"); CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR");
if (status != VK_SUCCESS) {
return status;
}
// Calculate swapchain target dimensions. // Calculate swapchain target dimensions.
VkExtent2D extent = surface_caps.currentExtent; VkExtent2D extent = surface_caps.currentExtent;
@ -162,10 +178,10 @@ bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
XELOGVK(" imageSharingMode = %s", to_string(create_info.imageSharingMode)); XELOGVK(" imageSharingMode = %s", to_string(create_info.imageSharingMode));
XELOGVK(" queueFamilyCount = %u", create_info.queueFamilyIndexCount); XELOGVK(" queueFamilyCount = %u", create_info.queueFamilyIndexCount);
err = vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle); status = vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle);
if (err) { if (status != VK_SUCCESS) {
XELOGE("Failed to create swapchain: %s", to_string(err)); XELOGE("Failed to create swapchain: %s", to_string(status));
return false; return status;
} }
// Create the pool used for transient buffers, so we can reset them all at // Create the pool used for transient buffers, so we can reset them all at
@ -175,8 +191,11 @@ bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
cmd_pool_info.pNext = nullptr; cmd_pool_info.pNext = nullptr;
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
cmd_pool_info.queueFamilyIndex = device_->queue_family_index(); cmd_pool_info.queueFamilyIndex = device_->queue_family_index();
err = vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_); status = vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_);
CheckResult(err, "vkCreateCommandPool"); CheckResult(status, "vkCreateCommandPool");
if (status != VK_SUCCESS) {
return status;
}
// Primary command buffer // Primary command buffer
VkCommandBufferAllocateInfo cmd_buffer_info; VkCommandBufferAllocateInfo cmd_buffer_info;
@ -185,15 +204,22 @@ bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
cmd_buffer_info.commandPool = cmd_pool_; cmd_buffer_info.commandPool = cmd_pool_;
cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd_buffer_info.commandBufferCount = 2; cmd_buffer_info.commandBufferCount = 2;
err = vkAllocateCommandBuffers(*device_, &cmd_buffer_info, &cmd_buffer_); status = vkAllocateCommandBuffers(*device_, &cmd_buffer_info, &cmd_buffer_);
CheckResult(err, "vkCreateCommandBuffer"); CheckResult(status, "vkCreateCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// Make two command buffers we'll do all our primary rendering from. // Make two command buffers we'll do all our primary rendering from.
VkCommandBuffer command_buffers[2]; VkCommandBuffer command_buffers[2];
cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
cmd_buffer_info.commandBufferCount = 2; cmd_buffer_info.commandBufferCount = 2;
err = vkAllocateCommandBuffers(*device_, &cmd_buffer_info, command_buffers); status =
CheckResult(err, "vkCreateCommandBuffer"); vkAllocateCommandBuffers(*device_, &cmd_buffer_info, command_buffers);
CheckResult(status, "vkCreateCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
render_cmd_buffer_ = command_buffers[0]; render_cmd_buffer_ = command_buffers[0];
copy_cmd_buffer_ = command_buffers[1]; copy_cmd_buffer_ = command_buffers[1];
@ -238,53 +264,75 @@ bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
render_pass_info.pSubpasses = &render_subpass; render_pass_info.pSubpasses = &render_subpass;
render_pass_info.dependencyCount = 0; render_pass_info.dependencyCount = 0;
render_pass_info.pDependencies = nullptr; render_pass_info.pDependencies = nullptr;
err = vkCreateRenderPass(*device_, &render_pass_info, nullptr, &render_pass_); status =
CheckResult(err, "vkCreateRenderPass"); vkCreateRenderPass(*device_, &render_pass_info, nullptr, &render_pass_);
CheckResult(status, "vkCreateRenderPass");
if (status != VK_SUCCESS) {
return status;
}
// Create a semaphore we'll use to synchronize with the swapchain. // Create a semaphore we'll use to synchronize with the swapchain.
VkSemaphoreCreateInfo semaphore_info; VkSemaphoreCreateInfo semaphore_info;
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_info.pNext = nullptr; semaphore_info.pNext = nullptr;
semaphore_info.flags = 0; semaphore_info.flags = 0;
err = vkCreateSemaphore(*device_, &semaphore_info, nullptr, status = vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_available_semaphore_); &image_available_semaphore_);
CheckResult(err, "vkCreateSemaphore"); CheckResult(status, "vkCreateSemaphore");
if (status != VK_SUCCESS) {
return status;
}
// Create another semaphore used to synchronize writes to the swap image. // Create another semaphore used to synchronize writes to the swap image.
err = vkCreateSemaphore(*device_, &semaphore_info, nullptr, status = vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_usage_semaphore_); &image_usage_semaphore_);
CheckResult(err, "vkCreateSemaphore"); CheckResult(status, "vkCreateSemaphore");
if (status != VK_SUCCESS) {
return status;
}
// Get images we will be presenting to. // Get images we will be presenting to.
// Note that this may differ from our requested amount. // Note that this may differ from our requested amount.
uint32_t actual_image_count = 0; uint32_t actual_image_count = 0;
std::vector<VkImage> images; std::vector<VkImage> images;
err = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, nullptr); status =
CheckResult(err, "vkGetSwapchainImagesKHR"); vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, nullptr);
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
}
images.resize(actual_image_count); images.resize(actual_image_count);
err = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, status = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count,
images.data()); images.data());
CheckResult(err, "vkGetSwapchainImagesKHR"); CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
}
// Create all buffers. // Create all buffers.
buffers_.resize(images.size()); buffers_.resize(images.size());
for (size_t i = 0; i < buffers_.size(); ++i) { for (size_t i = 0; i < buffers_.size(); ++i) {
if (!InitializeBuffer(&buffers_[i], images[i])) { status = InitializeBuffer(&buffers_[i], images[i]);
if (status != VK_SUCCESS) {
XELOGE("Failed to initialize a swapchain buffer"); XELOGE("Failed to initialize a swapchain buffer");
return false; return status;
} }
buffers_[i].image_layout = VK_IMAGE_LAYOUT_UNDEFINED; buffers_[i].image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
} }
XELOGVK("Swap chain initialized successfully!"); XELOGVK("Swap chain initialized successfully!");
return true; return VK_SUCCESS;
} }
bool VulkanSwapChain::InitializeBuffer(Buffer* buffer, VkImage target_image) { VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer,
VkImage target_image) {
DestroyBuffer(buffer); DestroyBuffer(buffer);
buffer->image = target_image; buffer->image = target_image;
VkResult status;
// Create an image view for the presentation image. // Create an image view for the presentation image.
// This will be used as a framebuffer attachment. // This will be used as a framebuffer attachment.
VkImageViewCreateInfo image_view_info; VkImageViewCreateInfo image_view_info;
@ -303,9 +351,12 @@ bool VulkanSwapChain::InitializeBuffer(Buffer* buffer, VkImage target_image) {
image_view_info.subresourceRange.levelCount = 1; image_view_info.subresourceRange.levelCount = 1;
image_view_info.subresourceRange.baseArrayLayer = 0; image_view_info.subresourceRange.baseArrayLayer = 0;
image_view_info.subresourceRange.layerCount = 1; image_view_info.subresourceRange.layerCount = 1;
auto err = vkCreateImageView(*device_, &image_view_info, nullptr, status = vkCreateImageView(*device_, &image_view_info, nullptr,
&buffer->image_view); &buffer->image_view);
CheckResult(err, "vkCreateImageView"); CheckResult(status, "vkCreateImageView");
if (status != VK_SUCCESS) {
return status;
}
// Create the framebuffer used to render into this image. // Create the framebuffer used to render into this image.
VkImageView attachments[] = {buffer->image_view}; VkImageView attachments[] = {buffer->image_view};
@ -320,11 +371,14 @@ bool VulkanSwapChain::InitializeBuffer(Buffer* buffer, VkImage target_image) {
framebuffer_info.width = surface_width_; framebuffer_info.width = surface_width_;
framebuffer_info.height = surface_height_; framebuffer_info.height = surface_height_;
framebuffer_info.layers = 1; framebuffer_info.layers = 1;
err = vkCreateFramebuffer(*device_, &framebuffer_info, nullptr, status = vkCreateFramebuffer(*device_, &framebuffer_info, nullptr,
&buffer->framebuffer); &buffer->framebuffer);
CheckResult(err, "vkCreateFramebuffer"); CheckResult(status, "vkCreateFramebuffer");
if (status != VK_SUCCESS) {
return status;
}
return true; return VK_SUCCESS;
} }
void VulkanSwapChain::DestroyBuffer(Buffer* buffer) { void VulkanSwapChain::DestroyBuffer(Buffer* buffer) {
@ -340,7 +394,7 @@ void VulkanSwapChain::DestroyBuffer(Buffer* buffer) {
buffer->image = nullptr; buffer->image = nullptr;
} }
bool VulkanSwapChain::Reinitialize() { VkResult VulkanSwapChain::Reinitialize() {
// Hacky, but stash the surface so we can reuse it. // Hacky, but stash the surface so we can reuse it.
auto surface = surface_; auto surface = surface_;
surface_ = nullptr; surface_ = nullptr;

View File

@ -47,11 +47,11 @@ class VulkanSwapChain {
VkCommandBuffer copy_cmd_buffer() const { return copy_cmd_buffer_; } VkCommandBuffer copy_cmd_buffer() const { return copy_cmd_buffer_; }
// Initializes the swap chain with the given WSI surface. // Initializes the swap chain with the given WSI surface.
bool Initialize(VkSurfaceKHR surface); VkResult Initialize(VkSurfaceKHR surface);
// Reinitializes the swap chain with the initial surface. // Reinitializes the swap chain with the initial surface.
// The surface will be retained but all other swap chain resources will be // The surface will be retained but all other swap chain resources will be
// torn down and recreated with the new surface properties (size/etc). // torn down and recreated with the new surface properties (size/etc).
bool Reinitialize(); VkResult Reinitialize();
// Waits on and signals a semaphore in this operation. // Waits on and signals a semaphore in this operation.
void WaitAndSignalSemaphore(VkSemaphore sem); void WaitAndSignalSemaphore(VkSemaphore sem);
@ -69,7 +69,7 @@ class VulkanSwapChain {
VkFramebuffer framebuffer = nullptr; VkFramebuffer framebuffer = nullptr;
}; };
bool InitializeBuffer(Buffer* buffer, VkImage target_image); VkResult InitializeBuffer(Buffer* buffer, VkImage target_image);
void DestroyBuffer(Buffer* buffer); void DestroyBuffer(Buffer* buffer);
// Safely releases all swap chain resources. // Safely releases all swap chain resources.