Browse Source

Move swapchain-related code to Vulkan::Swapchain.

master
Draklaw 4 years ago
parent
commit
7fdb6c8aab
  1. 1
      CMakeLists.txt
  2. 341
      src/Vulkan/Context.cpp
  3. 62
      src/Vulkan/Context.h
  4. 393
      src/Vulkan/Swapchain.cpp
  5. 109
      src/Vulkan/Swapchain.h
  6. 43
      src/VulkanTutorial.cpp
  7. 2
      src/VulkanTutorial.h
  8. 36
      src/utils.h

1
CMakeLists.txt

@ -31,6 +31,7 @@ endfunction()
add_executable(vk_expe
src/Vulkan/Context.cpp
src/Vulkan/Swapchain.cpp
src/main.cpp
src/utils.cpp
src/Logger.cpp

341
src/Vulkan/Context.cpp

@ -86,16 +86,24 @@ uint32_t Context::queue_family(size_t queue_index) const {
return m_queue_families[queue_index];
}
uint32_t Context::presentation_queue_family() const {
return m_presentation_queue_family;
}
VkQueue Context::queue(size_t queue_index) {
return m_queues[queue_index];
}
VkSurfaceKHR Context::surface() {
return m_surface;
VkQueue Context::presentation_queue() {
return m_presentation_queue;
}
VkSwapchainKHR Context::swapchain() {
return m_swapchain;
SDL_Window* Context::window() {
return m_window;
}
VkSurfaceKHR Context::surface() {
return m_surface;
}
VkSurfaceFormatKHR Context::surface_format() const {
@ -106,44 +114,6 @@ VkPresentModeKHR Context::present_mode() const {
return m_present_mode;
}
VkExtent2D Context::swapchain_extent() const {
return m_swapchain_extent;
}
size_t Context::swapchain_image_count() const {
return m_image_resources.size();
}
uint32_t Context::current_image_index() const {
return m_current_image_index;
}
VkImage Context::swapchain_image() {
assert(m_current_image_index != CURRENT_IMAGE_INDEX);
return m_image_resources[m_current_image_index].image;
}
VkImage Context::swapchain_image(size_t image_index) {
return m_image_resources[image_index].image;
}
VkImageView Context::swapchain_image_view() {
assert(m_current_image_index != CURRENT_IMAGE_INDEX);
return m_image_resources[m_current_image_index].view;
}
VkImageView Context::swapchain_image_view(size_t image_index) {
return m_image_resources[image_index].view;
}
VkSemaphore Context::ready_to_render() {
return m_frame_resources[m_frame_resources_index].ready_to_render;
}
VkFence Context::render_done() {
return m_frame_resources[m_frame_resources_index].render_done;
}
void Context::initialize(const ContextSettings& settings) {
m_window = settings.window();
@ -151,7 +121,6 @@ void Context::initialize(const ContextSettings& settings) {
create_surface(settings);
choose_physical_device(settings);
create_device(settings);
create_swapchain(settings);
}
void Context::shutdown() {
@ -163,120 +132,12 @@ void Context::shutdown() {
for(auto& callback: m_context_destruction_callbacks)
callback();
destroy_swapchain();
destroy_device(m_device);
destroy_surface(m_surface);
destroy_debug_messenger(m_debug_messenger);
destroy_instance(m_instance);
}
void Context::begin_frame() {
assert(m_current_image_index == CURRENT_IMAGE_INDEX);
auto& frame_resources = m_frame_resources[m_frame_resources_index];
// frame_resources are used cyclically. We wait for the current one to be
// done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at
// the same time.
if(frame_resources.render_done != VK_NULL_HANDLE) {
vkWaitForFences(
m_device,
1, &frame_resources.render_done,
VK_TRUE,
UINT64_MAX
);
}
bool acquired_image = false;
while(!acquired_image) {
VkResult const result = vkAcquireNextImageKHR(
m_device,
m_swapchain,
UINT64_MAX,
frame_resources.ready_to_render,
VK_NULL_HANDLE,
&m_current_image_index
);
if(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) {
acquired_image = true;
}
else if(result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate_swapchain();
}
else {
throw std::runtime_error("failed to acquire swapchain image");
}
}
logger.info() << "begin frame " << m_frame_index
<< ": image " << m_current_image_index
<< ", frame: " << m_frame_resources_index;
auto& image_resources = m_image_resources[m_current_image_index];
// In case vkAcquireNextImageKHR doesn't return images in-order, we wait
// again to be sure.
if(image_resources.render_done != VK_NULL_HANDLE) {
vkWaitForFences(
m_device,
1, &image_resources.render_done,
VK_TRUE,
UINT64_MAX
);
}
// Remember the right frame to wait for.
frame_resources.render_done = image_resources.render_done;
vkResetFences(m_device, 1, &frame_resources.render_done);
}
void Context::swap_buffers(uint32_t semaphore_count, VkSemaphore* wait_semaphores) {
assert(m_current_image_index != CURRENT_IMAGE_INDEX);
VkPresentInfoKHR present_info {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = semaphore_count,
.pWaitSemaphores = wait_semaphores,
.swapchainCount = 1,
.pSwapchains = &m_swapchain,
.pImageIndices = &m_current_image_index,
.pResults = nullptr,
};
VkResult const result = vkQueuePresentKHR(m_presentation_queue, &present_info);
if(result == VK_SUCCESS) {
// Nothing to see here !
}
else if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
m_invalid_swapchain = true;
}
else {
throw std::runtime_error("failed to present swapchain image");
}
if(m_invalid_swapchain)
recreate_swapchain();
m_frame_index += 1;
m_frame_resources_index = m_frame_index % MAX_FRAMES_IN_FLIGHT;
m_current_image_index = CURRENT_IMAGE_INDEX;
}
void Context::invalidate_swapchain() {
m_invalid_swapchain = true;
}
void Context::register_swapchain_creation_callback(SwapchainCreationCallback callback) {
m_swapchain_creation_callbacks.emplace_back(std::move(callback));
}
void Context::register_swapchain_destruction_callback(SwapchainDestructionCallback callback) {
m_swapchain_destruction_callbacks.emplace_back(std::move(callback));
}
void Context::register_context_destruction_callback(ContextDestructionCallback callback) {
m_context_destruction_callbacks.emplace_back(std::move(callback));
}
@ -778,184 +639,6 @@ void Context::create_device(const ContextSettings& settings) {
vkGetDeviceQueue(m_device, m_presentation_queue_family, 0, &m_presentation_queue);
}
void Context::create_swapchain(const ContextSettings& settings) {
m_swapchain_queue_families = {
m_presentation_queue_family,
};
for(size_t index = 0; index < m_queue_families.size(); index += 1) {
if(settings.queues()[index].use_swapchain_images)
m_swapchain_queue_families.push_back(m_queue_families[index]);
}
std::sort(m_swapchain_queue_families.begin(), m_swapchain_queue_families.end());
m_swapchain_queue_families.erase(
std::unique(m_swapchain_queue_families.begin(), m_swapchain_queue_families.end()),
m_swapchain_queue_families.end()
);
create_swapchain();
}
void Context::create_swapchain() {
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physical_device, m_surface, &capabilities);
m_swapchain_extent = capabilities.currentExtent;
if(m_swapchain_extent.width == UINT32_MAX) {
int width;
int height;
SDL_Vulkan_GetDrawableSize(m_window, &width, &height);
m_swapchain_extent.width = std::clamp(
uint32_t(width),
capabilities.minImageExtent.width,
capabilities.maxImageExtent.width
);
m_swapchain_extent.height = std::clamp(
uint32_t(height),
capabilities.minImageExtent.height,
capabilities.maxImageExtent.height
);
}
logger.debug() << "swapchain extent: " << m_swapchain_extent.width
<< ", " << m_swapchain_extent.height;
uint32_t image_count = capabilities.minImageCount;
if(capabilities.maxImageCount != 0)
image_count = std::min(image_count, capabilities.maxImageCount);
logger.debug() << "swapchain image count: " << image_count;
bool share_image = m_swapchain_queue_families.size() > 1;
VkSwapchainCreateInfoKHR swapchain_info {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = m_surface,
.minImageCount = image_count,
.imageFormat = m_surface_format.format,
.imageColorSpace = m_surface_format.colorSpace,
.imageExtent = m_swapchain_extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = share_image?
VK_SHARING_MODE_CONCURRENT:
VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = share_image?
uint32_t(m_swapchain_queue_families.size()):
0u,
.pQueueFamilyIndices = share_image?
m_swapchain_queue_families.data():
nullptr,
.preTransform = capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = m_present_mode,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
if(vkCreateSwapchainKHR(m_device, &swapchain_info, nullptr, &m_swapchain) != VK_SUCCESS)
throw std::runtime_error("failed to create swapchain");
vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, nullptr);
VkImage swapchain_images[16];
vkGetSwapchainImagesKHR(
m_device,
m_swapchain,
&image_count,
swapchain_images
);
m_image_resources.resize(image_count, {});
for(size_t index = 0; index < image_count; index += 1) {
auto& image_resources = m_image_resources[index];
image_resources.image = swapchain_images[index];
VkImageViewCreateInfo view_info {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image_resources.image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = m_surface_format.format,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
if(vkCreateImageView(
m_device,
&view_info,
nullptr,
&image_resources.view
) != VK_SUCCESS) {
throw std::runtime_error("failed to create swapchain image view");
}
VkFenceCreateInfo fence_info {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
if(vkCreateFence(
m_device,
&fence_info,
nullptr,
&image_resources.render_done
) != VK_SUCCESS)
throw std::runtime_error("failed to create fence");
}
m_frame_resources.resize(MAX_FRAMES_IN_FLIGHT, {});
int index = 0;
for(auto& frame_resources: m_frame_resources) {
VkSemaphoreCreateInfo semaphore_info {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
if(vkCreateSemaphore(
m_device,
&semaphore_info,
nullptr,
&frame_resources.ready_to_render
) != VK_SUCCESS)
throw std::runtime_error("failed to create semaphore");
index += 1;
}
for(auto& callback: m_swapchain_creation_callbacks)
callback(image_count);
}
void Context::destroy_swapchain() {
for(auto& callback: m_swapchain_destruction_callbacks)
callback();
for(auto& frame_resources: m_frame_resources) {
destroy_semaphore(frame_resources.ready_to_render);
// This is a reference to an ImageResources.render_done
frame_resources.render_done = VK_NULL_HANDLE;
}
for(auto& image_resources: m_image_resources) {
destroy_fence(image_resources.render_done);
destroy_image_view(image_resources.view);
}
destroy_swapchain(m_swapchain);
}
void Context::recreate_swapchain() {
destroy_swapchain();
create_swapchain();
}
void Context::initialize_extension_functions() {
uint32_t errors_count = 0;

62
src/Vulkan/Context.h

@ -52,11 +52,6 @@ constexpr uint32_t INVALID_QUEUE_FAMILY = UINT32_MAX;
class Context {
public:
static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX;
static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
using SwapchainCreationCallback = std::function<void(uint32_t)>;
using SwapchainDestructionCallback = std::function<void()>;
using ContextDestructionCallback = std::function<void()>;
public:
@ -69,36 +64,21 @@ public:
VkInstance instance();
VkPhysicalDevice physical_device();
VkDevice device();
size_t queue_family_count() const;
uint32_t queue_family(size_t queue_index) const;
uint32_t presentation_queue_family() const;
VkQueue queue(size_t queue_index);
VkQueue presentation_queue();
SDL_Window* window();
VkSurfaceKHR surface();
VkSwapchainKHR swapchain();
VkSurfaceFormatKHR surface_format() const;
VkPresentModeKHR present_mode() const;
VkExtent2D swapchain_extent() const;
size_t swapchain_image_count() const;
uint32_t current_image_index() const;
VkImage swapchain_image();
VkImage swapchain_image(size_t image_index);
VkImageView swapchain_image_view();
VkImageView swapchain_image_view(size_t image_index);
// VkFramebuffer framebuffer();
VkSemaphore ready_to_render();
VkFence render_done();
void initialize(const ContextSettings& settings);
void shutdown();
void begin_frame();
void swap_buffers(uint32_t semaphore_count, VkSemaphore* wait_semaphores);
void invalidate_swapchain();
void register_swapchain_creation_callback(SwapchainCreationCallback callback);
void register_swapchain_destruction_callback(SwapchainDestructionCallback callback);
void register_context_destruction_callback(ContextDestructionCallback callback);
@ -145,18 +125,6 @@ public:
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr;
PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = nullptr;
private:
struct ImageResources {
VkImage image = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
VkFence render_done = VK_NULL_HANDLE;
};
struct FrameResources {
VkSemaphore ready_to_render = VK_NULL_HANDLE;
VkFence render_done = VK_NULL_HANDLE;
};
private:
void create_instance(const ContextSettings& settings);
void create_surface(const ContextSettings& settings);
@ -166,11 +134,6 @@ private:
const ContextSettings& settings
);
void create_device(const ContextSettings& settings);
void create_swapchain(const ContextSettings& settings);
void create_swapchain();
void destroy_swapchain();
void recreate_swapchain();
void initialize_extension_functions();
@ -197,25 +160,10 @@ private:
VkQueue m_presentation_queue = nullptr;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
VkSurfaceFormatKHR m_surface_format;
VkPresentModeKHR m_present_mode;
VkExtent2D m_swapchain_extent;
std::vector<uint32_t> m_swapchain_queue_families;
std::vector<ImageResources> m_image_resources;
std::vector<FrameResources> m_frame_resources;
bool m_invalid_swapchain = false;
std::vector<SwapchainCreationCallback> m_swapchain_creation_callbacks;
std::vector<SwapchainDestructionCallback> m_swapchain_destruction_callbacks;
std::vector<ContextDestructionCallback> m_context_destruction_callbacks;
uint32_t m_current_image_index = CURRENT_IMAGE_INDEX;
size_t m_frame_index = 0;
size_t m_frame_resources_index = 0;
friend class ContextSettings;
std::vector<ContextDestructionCallback> m_context_destruction_callbacks;
};

393
src/Vulkan/Swapchain.cpp

@ -0,0 +1,393 @@
#include <Vulkan/Swapchain.h>
#include <Logger.h>
#include <SDL2/SDL_vulkan.h>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <tuple>
namespace Vulkan {
SwapchainSettings::SwapchainSettings(Context* context)
: m_context(context)
{}
SwapchainSettings::~SwapchainSettings() {
}
Context* SwapchainSettings::context() const {
return m_context;
}
std::vector<uint32_t> SwapchainSettings::queue_families() const {
return m_queue_families;
}
SwapchainSettings& SwapchainSettings::with_queue(uint32_t queue_family) {
m_queue_families.push_back(queue_family);
return *this;
}
//////////////////////////////////////////////////////////////////////////////
// Swapchain
Swapchain::Swapchain() {
}
Swapchain::~Swapchain() {
shutdown();
}
VkSwapchainKHR Swapchain::swapchain() {
return m_swapchain;
}
VkExtent2D Swapchain::extent() const {
return m_extent;
}
size_t Swapchain::image_count() const {
return m_image_resources.size();
}
uint32_t Swapchain::current_image_index() const {
return m_current_image_index;
}
VkImage Swapchain::image() {
assert(m_current_image_index != CURRENT_IMAGE_INDEX);
return m_image_resources[m_current_image_index].image;
}
VkImage Swapchain::image(size_t image_index) {
return m_image_resources[image_index].image;
}
VkImageView Swapchain::image_view() {
assert(m_current_image_index != CURRENT_IMAGE_INDEX);
return m_image_resources[m_current_image_index].view;
}
VkImageView Swapchain::image_view(size_t image_index) {
return m_image_resources[image_index].view;
}
VkSemaphore Swapchain::ready_to_render() {
return m_frame_resources[m_frame_resources_index].ready_to_render;
}
VkFence Swapchain::render_done() {
return m_frame_resources[m_frame_resources_index].render_done;
}
void Swapchain::initialize(const SwapchainSettings& settings) {
assert(m_context == nullptr);
m_context = settings.context();
create(settings);
}
void Swapchain::shutdown() {
if(!m_swapchain)
return;
destroy();
m_context = nullptr;
}
void Swapchain::begin_frame() {
assert(m_current_image_index == CURRENT_IMAGE_INDEX);
auto& frame_resources = m_frame_resources[m_frame_resources_index];
// frame_resources are used cyclically. We wait for the current one to be
// done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at
// the same time.
if(frame_resources.render_done != VK_NULL_HANDLE) {
vkWaitForFences(
m_context->device(),
1, &frame_resources.render_done,
VK_TRUE,
UINT64_MAX
);
}
bool acquired_image = false;
while(!acquired_image) {
VkResult const result = vkAcquireNextImageKHR(
m_context->device(),
m_swapchain,
UINT64_MAX,
frame_resources.ready_to_render,
VK_NULL_HANDLE,
&m_current_image_index
);
if(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) {
acquired_image = true;
}
else if(result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate();
}
else {
throw std::runtime_error("failed to acquire swapchain image");
}
}
logger.info() << "begin frame " << m_frame_index
<< ": image " << m_current_image_index
<< ", frame: " << m_frame_resources_index;
auto& image_resources = m_image_resources[m_current_image_index];
// In case vkAcquireNextImageKHR doesn't return images in-order, we wait
// again to be sure.
if(image_resources.render_done != VK_NULL_HANDLE) {
vkWaitForFences(
m_context->device(),
1, &image_resources.render_done,
VK_TRUE,
UINT64_MAX
);
}
// Remember the right frame to wait for.
frame_resources.render_done = image_resources.render_done;
vkResetFences(m_context->device(), 1, &frame_resources.render_done);
}
void Swapchain::swap_buffers(Array<VkSemaphore> wait_semaphores) {
assert(m_current_image_index != CURRENT_IMAGE_INDEX);
VkPresentInfoKHR present_info {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = uint32_t(wait_semaphores.size()),
.pWaitSemaphores = wait_semaphores.data(),
.swapchainCount = 1,
.pSwapchains = &m_swapchain,
.pImageIndices = &m_current_image_index,
.pResults = nullptr,
};
VkResult const result = vkQueuePresentKHR(
m_context->presentation_queue(),
&present_info
);
if(result == VK_SUCCESS) {
// Nothing to see here !
}
else if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
m_invalid = true;
}
else {
throw std::runtime_error("failed to present swapchain image");
}
if(m_invalid)
recreate();
m_frame_index += 1;
m_frame_resources_index = m_frame_index % MAX_FRAMES_IN_FLIGHT;
m_current_image_index = CURRENT_IMAGE_INDEX;
}
void Swapchain::invalidate() {
m_invalid = true;
}
void Swapchain::register_creation_callback(CreationCallback callback) {
m_creation_callbacks.emplace_back(std::move(callback));
}
void Swapchain::register_destruction_callback(DestructionCallback callback) {
m_destruction_callbacks.emplace_back(std::move(callback));
}
void Swapchain::create(const SwapchainSettings& settings) {
m_queue_families = settings.queue_families();
m_queue_families.push_back(m_context->presentation_queue_family());
std::sort(m_queue_families.begin(), m_queue_families.end());
m_queue_families.erase(
std::unique(m_queue_families.begin(), m_queue_families.end()),
m_queue_families.end()
);
create();
}
void Swapchain::create() {
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
m_context->physical_device(),
m_context->surface(),
&capabilities
);
m_extent = capabilities.currentExtent;
if(m_extent.width == UINT32_MAX) {
int width;
int height;
SDL_Vulkan_GetDrawableSize(m_context->window(), &width, &height);
m_extent.width = std::clamp(
uint32_t(width),
capabilities.minImageExtent.width,
capabilities.maxImageExtent.width
);
m_extent.height = std::clamp(
uint32_t(height),
capabilities.minImageExtent.height,
capabilities.maxImageExtent.height
);
}
logger.debug() << "swapchain extent: " << m_extent.width
<< ", " << m_extent.height;
uint32_t image_count = capabilities.minImageCount;
if(capabilities.maxImageCount != 0)
image_count = std::min(image_count, capabilities.maxImageCount);
logger.debug() << "swapchain image count: " << image_count;
bool share_image = m_queue_families.size() > 1;
VkSwapchainCreateInfoKHR swapchain_info {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = m_context->surface(),
.minImageCount = image_count,
.imageFormat = m_context->surface_format().format,
.imageColorSpace = m_context->surface_format().colorSpace,
.imageExtent = m_extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = share_image?
VK_SHARING_MODE_CONCURRENT:
VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = share_image?
uint32_t(m_queue_families.size()):
0u,
.pQueueFamilyIndices = share_image?
m_queue_families.data():
nullptr,
.preTransform = capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = m_context->present_mode(),
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
if(vkCreateSwapchainKHR(m_context->device(), &swapchain_info, nullptr, &m_swapchain) != VK_SUCCESS)
throw std::runtime_error("failed to create swapchain");
vkGetSwapchainImagesKHR(m_context->device(), m_swapchain, &image_count, nullptr);
VkImage swapchain_images[16];
vkGetSwapchainImagesKHR(
m_context->device(),
m_swapchain,
&image_count,
swapchain_images
);
m_image_resources.resize(image_count, {});
for(size_t index = 0; index < image_count; index += 1) {
auto& image_resources = m_image_resources[index];
image_resources.image = swapchain_images[index];
VkImageViewCreateInfo view_info {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image_resources.image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = m_context->surface_format().format,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
if(vkCreateImageView(
m_context->device(),
&view_info,
nullptr,
&image_resources.view
) != VK_SUCCESS) {
throw std::runtime_error("failed to create swapchain image view");
}
VkFenceCreateInfo fence_info {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
if(vkCreateFence(
m_context->device(),
&fence_info,
nullptr,
&image_resources.render_done
) != VK_SUCCESS)
throw std::runtime_error("failed to create fence");
}
m_frame_resources.resize(MAX_FRAMES_IN_FLIGHT, {});
int index = 0;
for(auto& frame_resources: m_frame_resources) {
VkSemaphoreCreateInfo semaphore_info {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
if(vkCreateSemaphore(
m_context->device(),
&semaphore_info,
nullptr,
&frame_resources.ready_to_render
) != VK_SUCCESS)
throw std::runtime_error("failed to create semaphore");
index += 1;
}
for(auto& callback: m_creation_callbacks)
callback(image_count);
}
void Swapchain::destroy() {
for(auto& callback: m_destruction_callbacks)
callback();
for(auto& frame_resources: m_frame_resources) {
m_context->destroy_semaphore(frame_resources.ready_to_render);
// This is a reference to an ImageResources.render_done
frame_resources.render_done = VK_NULL_HANDLE;
}
for(auto& image_resources: m_image_resources) {
m_context->destroy_fence(image_resources.render_done);
m_context->destroy_image_view(image_resources.view);
}
m_context->destroy_swapchain(m_swapchain);
}
void Swapchain::recreate() {
destroy();
create();
}
}

109
src/Vulkan/Swapchain.h

@ -0,0 +1,109 @@
#pragma once
#include <Vulkan/Context.h>
#include <utils.h>
#include <SDL2/SDL.h>
#include <vulkan/vulkan.h>
#include <optional>
#include <initializer_list>
#include <vector>
#include <functional>
namespace Vulkan {
class Swapchain;
class SwapchainSettings {
public:
SwapchainSettings(Context* context);
~SwapchainSettings();
Context* context() const;
std::vector<uint32_t> queue_families() const;
SwapchainSettings& with_queue(uint32_t queue_family);
private:
Context* m_context;
std::vector<uint32_t> m_queue_families;
};
class Swapchain {
public:
static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX;
static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
using CreationCallback = std::function<void(uint32_t)>;
using DestructionCallback = std::function<void()>;
public:
Swapchain();
Swapchain(const Swapchain&) = delete;
~Swapchain();
Swapchain& operator=(const Swapchain&) = delete;
VkSwapchainKHR swapchain();
VkExtent2D extent() const;
size_t image_count() const;
uint32_t current_image_index() const;
VkImage image();
VkImage image(size_t image_index);
VkImageView image_view();
VkImageView image_view(size_t image_index);
VkSemaphore ready_to_render();
VkFence render_done();
void initialize(const SwapchainSettings& settings);
void shutdown();
void begin_frame();
void swap_buffers(Array<VkSemaphore> wait_semaphores);
void invalidate();
void register_creation_callback(CreationCallback callback);
void register_destruction_callback(DestructionCallback callback);
private:
struct ImageResources {
VkImage image = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
VkFence render_done = VK_NULL_HANDLE;
};
struct FrameResources {
VkSemaphore ready_to_render = VK_NULL_HANDLE;
VkFence render_done = VK_NULL_HANDLE;
};
private:
void create(const SwapchainSettings& settings);
void create();
void destroy();
void recreate();
private:
Context* m_context = nullptr;
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
VkExtent2D m_extent;
std::vector<uint32_t> m_queue_families;
std::vector<ImageResources> m_image_resources;
std::vector<FrameResources> m_frame_resources;
bool m_invalid = false;
std::vector<CreationCallback> m_creation_callbacks;
std::vector<DestructionCallback> m_destruction_callbacks;
uint32_t m_current_image_index = CURRENT_IMAGE_INDEX;
size_t m_frame_index = 0;
size_t m_frame_resources_index = 0;
};
}

43
src/VulkanTutorial.cpp

@ -47,9 +47,9 @@ const std::vector<Vertex> vertices = {
VulkanTutorial::VulkanTutorial() {
m_context.register_swapchain_creation_callback(
m_swapchain.register_creation_callback(
std::bind(&VulkanTutorial::create_swapchain_objects, this, std::placeholders::_1));
m_context.register_swapchain_destruction_callback(
m_swapchain.register_destruction_callback(
std::bind(&VulkanTutorial::destroy_swapchain_objects, this));
}
@ -58,13 +58,17 @@ VulkanTutorial::~VulkanTutorial() {
}
void VulkanTutorial::initialize(SDL_Window* window) {
auto const settings = Vulkan::ContextSettings()
auto const context_settings = Vulkan::ContextSettings()
#if defined(VKEXPE_ENABLE_VALIDATION) || !defined(NDEBUG)
.with_debug(true)
#endif
.with_queue(GRAPHIC_QUEUE, VK_QUEUE_GRAPHICS_BIT)
.with_window(window);
m_context.initialize(settings);
m_context.initialize(context_settings);
auto const swapchain_settings = Vulkan::SwapchainSettings(&m_context)
.with_queue(m_context.queue_family(GRAPHIC_QUEUE));
m_swapchain.initialize(swapchain_settings);
create_command_pool();
create_vertex_buffer();
@ -87,15 +91,16 @@ void VulkanTutorial::shutdown() {
m_context.destroy_semaphore(semaphore);
m_render_done.clear();
m_swapchain.shutdown();
m_context.shutdown();
}
void VulkanTutorial::draw_frame() {
m_context.begin_frame();
auto const image_index = m_context.current_image_index();
m_swapchain.begin_frame();
auto const image_index = m_swapchain.current_image_index();
VkSemaphore wait_semaphores[] = {
m_context.ready_to_render(),
m_swapchain.ready_to_render(),
};
VkPipelineStageFlags stages[] = {
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
@ -114,15 +119,15 @@ void VulkanTutorial::draw_frame() {
if(vkQueueSubmit(
m_context.queue(GRAPHIC_QUEUE),
1, &submit_info,
m_context.render_done()
m_swapchain.render_done()
))
throw std::runtime_error("failed to submit draw command buffer");
m_context.swap_buffers(1, &m_render_done[image_index]);
m_swapchain.swap_buffers({1, &m_render_done[image_index]});
}
void VulkanTutorial::invalidate_swapchain() {
m_context.invalidate_swapchain();
m_swapchain.invalidate();
}
void VulkanTutorial::create_swapchain_objects(uint32_t image_count) {
@ -131,7 +136,7 @@ void VulkanTutorial::create_swapchain_objects(uint32_t image_count) {
create_graphic_pipeline();
create_command_buffers();
m_render_done.resize(m_context.swapchain_image_count());
m_render_done.resize(m_swapchain.image_count());
VkSemaphoreCreateInfo semaphore_info {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
@ -268,15 +273,15 @@ void VulkanTutorial::create_graphic_pipeline() {
VkViewport viewport {
.x = 0.0f,
.y = 0.0f,
.width = float(m_context.swapchain_extent().width),
.height = float(m_context.swapchain_extent().height),
.width = float(m_swapchain.extent().width),
.height = float(m_swapchain.extent().height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
VkRect2D scissor = {
.offset = { 0, 0 },
.extent = m_context.swapchain_extent(),
.extent = m_swapchain.extent(),
};
VkPipelineViewportStateCreateInfo viewport_info {
@ -381,18 +386,18 @@ void VulkanTutorial::create_graphic_pipeline() {
}
void VulkanTutorial::create_framebuffers() {
m_framebuffers.resize(m_context.swapchain_image_count());
m_framebuffers.resize(m_swapchain.image_count());
for(size_t index = 0; index < m_framebuffers.size(); index += 1) {
VkImageView view = m_context.swapchain_image_view(index);
VkImageView view = m_swapchain.image_view(index);
VkFramebufferCreateInfo framebuffer_info {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = m_render_pass,
.attachmentCount = 1,
.pAttachments = &view,
.width = m_context.swapchain_extent().width,
.height = m_context.swapchain_extent().height,
.width = m_swapchain.extent().width,
.height = m_swapchain.extent().height,
.layers = 1,
};
@ -547,7 +552,7 @@ void VulkanTutorial::create_command_buffers() {
.framebuffer = m_framebuffers[index],
.renderArea = {
.offset = { 0, 0 },
.extent = m_context.swapchain_extent(),
.extent = m_swapchain.extent(),
},
.clearValueCount = 1,
.pClearValues = &clear_color,

2
src/VulkanTutorial.h

@ -1,6 +1,7 @@
#pragma once
#include <Vulkan/Context.h>
#include <Vulkan/Swapchain.h>
#include <SDL2/SDL.h>
#include <vulkan/vulkan.h>
@ -54,6 +55,7 @@ private:
private:
Vulkan::Context m_context;
Vulkan::Swapchain m_swapchain;
VkQueue m_graphic_queue = nullptr;
VkQueue m_presentation_queue = nullptr;

36
src/utils.h

@ -1,5 +1,6 @@
#pragma once
#include <cassert>
#include <vector>
#include <string>
#include <sstream>
@ -38,3 +39,38 @@ std::string cat(Args&&... args) {
}
std::vector<char> read_binary_file(const char* path);
template<typename T>
class Array {
public:
Array() {
}
Array(size_t size, T* data)
: m_size(size)
, m_data(data)
{
assert((size == 0 && m_data == nullptr) || (size != 0 && m_data != nullptr));
}
Array(const std::vector<T>& vector)
: m_size(vector.size())
, m_data(vector.data())
{}
explicit operator bool() const {
return m_size != 0 && m_data != nullptr;
}
size_t size() const {
return m_size;
}
T* data() const {
return m_data;
}
private:
size_t m_size = 0;
T* m_data = nullptr;
};

Loading…
Cancel
Save