Move swapchain-related code to Vulkan::Swapchain.
This commit is contained in:
@@ -31,6 +31,7 @@ endfunction()
|
|||||||
|
|
||||||
add_executable(vk_expe
|
add_executable(vk_expe
|
||||||
src/Vulkan/Context.cpp
|
src/Vulkan/Context.cpp
|
||||||
|
src/Vulkan/Swapchain.cpp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/utils.cpp
|
src/utils.cpp
|
||||||
src/Logger.cpp
|
src/Logger.cpp
|
||||||
|
|||||||
@@ -86,16 +86,24 @@ uint32_t Context::queue_family(size_t queue_index) const {
|
|||||||
return m_queue_families[queue_index];
|
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) {
|
VkQueue Context::queue(size_t queue_index) {
|
||||||
return m_queues[queue_index];
|
return m_queues[queue_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceKHR Context::surface() {
|
VkQueue Context::presentation_queue() {
|
||||||
return m_surface;
|
return m_presentation_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSwapchainKHR Context::swapchain() {
|
SDL_Window* Context::window() {
|
||||||
return m_swapchain;
|
return m_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSurfaceKHR Context::surface() {
|
||||||
|
return m_surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceFormatKHR Context::surface_format() const {
|
VkSurfaceFormatKHR Context::surface_format() const {
|
||||||
@@ -106,44 +114,6 @@ VkPresentModeKHR Context::present_mode() const {
|
|||||||
return m_present_mode;
|
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) {
|
void Context::initialize(const ContextSettings& settings) {
|
||||||
m_window = settings.window();
|
m_window = settings.window();
|
||||||
|
|
||||||
@@ -151,7 +121,6 @@ void Context::initialize(const ContextSettings& settings) {
|
|||||||
create_surface(settings);
|
create_surface(settings);
|
||||||
choose_physical_device(settings);
|
choose_physical_device(settings);
|
||||||
create_device(settings);
|
create_device(settings);
|
||||||
create_swapchain(settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::shutdown() {
|
void Context::shutdown() {
|
||||||
@@ -163,120 +132,12 @@ void Context::shutdown() {
|
|||||||
for(auto& callback: m_context_destruction_callbacks)
|
for(auto& callback: m_context_destruction_callbacks)
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
destroy_swapchain();
|
|
||||||
|
|
||||||
destroy_device(m_device);
|
destroy_device(m_device);
|
||||||
destroy_surface(m_surface);
|
destroy_surface(m_surface);
|
||||||
destroy_debug_messenger(m_debug_messenger);
|
destroy_debug_messenger(m_debug_messenger);
|
||||||
destroy_instance(m_instance);
|
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) {
|
void Context::register_context_destruction_callback(ContextDestructionCallback callback) {
|
||||||
m_context_destruction_callbacks.emplace_back(std::move(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);
|
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() {
|
void Context::initialize_extension_functions() {
|
||||||
uint32_t errors_count = 0;
|
uint32_t errors_count = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -52,11 +52,6 @@ constexpr uint32_t INVALID_QUEUE_FAMILY = UINT32_MAX;
|
|||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
public:
|
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()>;
|
using ContextDestructionCallback = std::function<void()>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -69,36 +64,21 @@ public:
|
|||||||
VkInstance instance();
|
VkInstance instance();
|
||||||
VkPhysicalDevice physical_device();
|
VkPhysicalDevice physical_device();
|
||||||
VkDevice device();
|
VkDevice device();
|
||||||
|
size_t queue_family_count() const;
|
||||||
uint32_t queue_family(size_t queue_index) const;
|
uint32_t queue_family(size_t queue_index) const;
|
||||||
|
uint32_t presentation_queue_family() const;
|
||||||
VkQueue queue(size_t queue_index);
|
VkQueue queue(size_t queue_index);
|
||||||
|
VkQueue presentation_queue();
|
||||||
|
|
||||||
|
SDL_Window* window();
|
||||||
VkSurfaceKHR surface();
|
VkSurfaceKHR surface();
|
||||||
VkSwapchainKHR swapchain();
|
|
||||||
|
|
||||||
VkSurfaceFormatKHR surface_format() const;
|
VkSurfaceFormatKHR surface_format() const;
|
||||||
VkPresentModeKHR present_mode() 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 initialize(const ContextSettings& settings);
|
||||||
void shutdown();
|
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);
|
void register_context_destruction_callback(ContextDestructionCallback callback);
|
||||||
|
|
||||||
|
|
||||||
@@ -145,18 +125,6 @@ public:
|
|||||||
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr;
|
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr;
|
||||||
PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = 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:
|
private:
|
||||||
void create_instance(const ContextSettings& settings);
|
void create_instance(const ContextSettings& settings);
|
||||||
void create_surface(const ContextSettings& settings);
|
void create_surface(const ContextSettings& settings);
|
||||||
@@ -166,11 +134,6 @@ private:
|
|||||||
const ContextSettings& settings
|
const ContextSettings& settings
|
||||||
);
|
);
|
||||||
void create_device(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();
|
void initialize_extension_functions();
|
||||||
|
|
||||||
@@ -197,25 +160,10 @@ private:
|
|||||||
VkQueue m_presentation_queue = nullptr;
|
VkQueue m_presentation_queue = nullptr;
|
||||||
|
|
||||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||||
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
VkSurfaceFormatKHR m_surface_format;
|
VkSurfaceFormatKHR m_surface_format;
|
||||||
VkPresentModeKHR m_present_mode;
|
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;
|
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
393
src/Vulkan/Swapchain.cpp
Normal file
393
src/Vulkan/Swapchain.cpp
Normal file
@@ -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
Normal file
109
src/Vulkan/Swapchain.h
Normal file
@@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -47,9 +47,9 @@ const std::vector<Vertex> vertices = {
|
|||||||
|
|
||||||
|
|
||||||
VulkanTutorial::VulkanTutorial() {
|
VulkanTutorial::VulkanTutorial() {
|
||||||
m_context.register_swapchain_creation_callback(
|
m_swapchain.register_creation_callback(
|
||||||
std::bind(&VulkanTutorial::create_swapchain_objects, this, std::placeholders::_1));
|
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));
|
std::bind(&VulkanTutorial::destroy_swapchain_objects, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,13 +58,17 @@ VulkanTutorial::~VulkanTutorial() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanTutorial::initialize(SDL_Window* window) {
|
void VulkanTutorial::initialize(SDL_Window* window) {
|
||||||
auto const settings = Vulkan::ContextSettings()
|
auto const context_settings = Vulkan::ContextSettings()
|
||||||
#if defined(VKEXPE_ENABLE_VALIDATION) || !defined(NDEBUG)
|
#if defined(VKEXPE_ENABLE_VALIDATION) || !defined(NDEBUG)
|
||||||
.with_debug(true)
|
.with_debug(true)
|
||||||
#endif
|
#endif
|
||||||
.with_queue(GRAPHIC_QUEUE, VK_QUEUE_GRAPHICS_BIT)
|
.with_queue(GRAPHIC_QUEUE, VK_QUEUE_GRAPHICS_BIT)
|
||||||
.with_window(window);
|
.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_command_pool();
|
||||||
create_vertex_buffer();
|
create_vertex_buffer();
|
||||||
@@ -87,15 +91,16 @@ void VulkanTutorial::shutdown() {
|
|||||||
m_context.destroy_semaphore(semaphore);
|
m_context.destroy_semaphore(semaphore);
|
||||||
m_render_done.clear();
|
m_render_done.clear();
|
||||||
|
|
||||||
|
m_swapchain.shutdown();
|
||||||
m_context.shutdown();
|
m_context.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanTutorial::draw_frame() {
|
void VulkanTutorial::draw_frame() {
|
||||||
m_context.begin_frame();
|
m_swapchain.begin_frame();
|
||||||
auto const image_index = m_context.current_image_index();
|
auto const image_index = m_swapchain.current_image_index();
|
||||||
|
|
||||||
VkSemaphore wait_semaphores[] = {
|
VkSemaphore wait_semaphores[] = {
|
||||||
m_context.ready_to_render(),
|
m_swapchain.ready_to_render(),
|
||||||
};
|
};
|
||||||
VkPipelineStageFlags stages[] = {
|
VkPipelineStageFlags stages[] = {
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
@@ -114,15 +119,15 @@ void VulkanTutorial::draw_frame() {
|
|||||||
if(vkQueueSubmit(
|
if(vkQueueSubmit(
|
||||||
m_context.queue(GRAPHIC_QUEUE),
|
m_context.queue(GRAPHIC_QUEUE),
|
||||||
1, &submit_info,
|
1, &submit_info,
|
||||||
m_context.render_done()
|
m_swapchain.render_done()
|
||||||
))
|
))
|
||||||
throw std::runtime_error("failed to submit draw command buffer");
|
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() {
|
void VulkanTutorial::invalidate_swapchain() {
|
||||||
m_context.invalidate_swapchain();
|
m_swapchain.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanTutorial::create_swapchain_objects(uint32_t image_count) {
|
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_graphic_pipeline();
|
||||||
create_command_buffers();
|
create_command_buffers();
|
||||||
|
|
||||||
m_render_done.resize(m_context.swapchain_image_count());
|
m_render_done.resize(m_swapchain.image_count());
|
||||||
|
|
||||||
VkSemaphoreCreateInfo semaphore_info {
|
VkSemaphoreCreateInfo semaphore_info {
|
||||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||||
@@ -268,15 +273,15 @@ void VulkanTutorial::create_graphic_pipeline() {
|
|||||||
VkViewport viewport {
|
VkViewport viewport {
|
||||||
.x = 0.0f,
|
.x = 0.0f,
|
||||||
.y = 0.0f,
|
.y = 0.0f,
|
||||||
.width = float(m_context.swapchain_extent().width),
|
.width = float(m_swapchain.extent().width),
|
||||||
.height = float(m_context.swapchain_extent().height),
|
.height = float(m_swapchain.extent().height),
|
||||||
.minDepth = 0.0f,
|
.minDepth = 0.0f,
|
||||||
.maxDepth = 1.0f,
|
.maxDepth = 1.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
VkRect2D scissor = {
|
VkRect2D scissor = {
|
||||||
.offset = { 0, 0 },
|
.offset = { 0, 0 },
|
||||||
.extent = m_context.swapchain_extent(),
|
.extent = m_swapchain.extent(),
|
||||||
};
|
};
|
||||||
|
|
||||||
VkPipelineViewportStateCreateInfo viewport_info {
|
VkPipelineViewportStateCreateInfo viewport_info {
|
||||||
@@ -381,18 +386,18 @@ void VulkanTutorial::create_graphic_pipeline() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanTutorial::create_framebuffers() {
|
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) {
|
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 {
|
VkFramebufferCreateInfo framebuffer_info {
|
||||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||||
.renderPass = m_render_pass,
|
.renderPass = m_render_pass,
|
||||||
.attachmentCount = 1,
|
.attachmentCount = 1,
|
||||||
.pAttachments = &view,
|
.pAttachments = &view,
|
||||||
.width = m_context.swapchain_extent().width,
|
.width = m_swapchain.extent().width,
|
||||||
.height = m_context.swapchain_extent().height,
|
.height = m_swapchain.extent().height,
|
||||||
.layers = 1,
|
.layers = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -547,7 +552,7 @@ void VulkanTutorial::create_command_buffers() {
|
|||||||
.framebuffer = m_framebuffers[index],
|
.framebuffer = m_framebuffers[index],
|
||||||
.renderArea = {
|
.renderArea = {
|
||||||
.offset = { 0, 0 },
|
.offset = { 0, 0 },
|
||||||
.extent = m_context.swapchain_extent(),
|
.extent = m_swapchain.extent(),
|
||||||
},
|
},
|
||||||
.clearValueCount = 1,
|
.clearValueCount = 1,
|
||||||
.pClearValues = &clear_color,
|
.pClearValues = &clear_color,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Vulkan/Context.h>
|
#include <Vulkan/Context.h>
|
||||||
|
#include <Vulkan/Swapchain.h>
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
@@ -54,6 +55,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Vulkan::Context m_context;
|
Vulkan::Context m_context;
|
||||||
|
Vulkan::Swapchain m_swapchain;
|
||||||
VkQueue m_graphic_queue = nullptr;
|
VkQueue m_graphic_queue = nullptr;
|
||||||
VkQueue m_presentation_queue = nullptr;
|
VkQueue m_presentation_queue = nullptr;
|
||||||
|
|
||||||
|
|||||||
36
src/utils.h
36
src/utils.h
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -38,3 +39,38 @@ std::string cat(Args&&... args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> read_binary_file(const char* path);
|
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;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user