Format and resize callback

This commit is contained in:
Chewico 2026-03-23 13:21:28 +01:00
parent 8b1aa63327
commit 09d92231e2
12 changed files with 131 additions and 63 deletions

View File

@ -10,6 +10,7 @@
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#define ACTIVE_VALIDATION_LAYER true #define ACTIVE_VALIDATION_LAYER true
#define MAX_FRAMES_IN_FLIGHT 2
class GLFWwindow; class GLFWwindow;
@ -32,33 +33,34 @@ namespace deerith {
extern GraphicsConfig graphics_config; extern GraphicsConfig graphics_config;
extern GLFWwindow* window; extern GLFWwindow* window;
extern bool frame_buffer_resize;
// Vulkan Specific // Vulkan Specific
extern VkInstance instance; extern VkInstance instance;
extern const std::vector<const char*> validation_layers; extern const std::vector<const char*> validation_layers;
extern const std::vector<const char*> device_extensions; extern VkSurfaceKHR surface;
extern VkPhysicalDevice physical_device; extern VkPhysicalDevice physical_device;
extern QueueFamilyIndices queue_family_indices; extern QueueFamilyIndices queue_family_indices;
extern SwapChainSupportDetails swap_chain_support_details; extern SwapChainSupportDetails swap_chain_support_details;
extern const std::vector<const char*> device_extensions;
extern VkDevice device; extern VkDevice device;
extern VkQueue graphics_queue; extern VkQueue graphics_queue;
extern VkQueue pressent_queue; extern VkQueue pressent_queue;
extern VkSurfaceKHR surface;
extern VkSwapchainKHR swap_chain; extern VkSwapchainKHR swap_chain;
extern std::vector<VkImage> swap_chain_images; extern std::vector<VkImage> swap_chain_images;
extern VkFormat swap_chain_image_format; extern VkFormat swap_chain_image_format;
extern VkExtent2D swap_chain_extent; extern VkExtent2D swap_chain_extent;
extern std::vector<VkImageView> swap_chain_image_views; extern std::vector<VkImageView> swap_chain_image_views;
extern VkPipelineLayout pipeline_layout;
extern VkRenderPass render_pass; extern VkRenderPass render_pass;
extern VkPipelineLayout pipeline_layout;
extern VkPipeline graphics_pipeline; extern VkPipeline graphics_pipeline;
extern std::vector<VkFramebuffer> swap_chain_frame_buffers; extern std::vector<VkFramebuffer> swap_chain_frame_buffers;
extern VkCommandPool command_pool; extern VkCommandPool command_pool;
extern VkCommandBuffer command_buffer; extern std::vector<VkCommandBuffer> command_buffers;
extern std::vector<VkSemaphore> image_available_semaphores;
extern VkSemaphore image_available_semaphore; extern std::vector<VkSemaphore> render_finished_semaphores;
extern VkSemaphore render_finished_semaphore; extern std::vector<VkFence> in_flight_fences;
extern VkFence in_flight_fence; extern uint32_t current_frame;
void init_glfw(); void init_glfw();
void shutdown_glfw(); void shutdown_glfw();
@ -69,7 +71,7 @@ namespace deerith {
void create_vulkan_instance(); void create_vulkan_instance();
void create_vulkan_validation_layer(); void create_vulkan_validation_layer();
void create_vulkan_surface(); void create_vulkan_surface();
void create_vulkan_phisical_device(); void pick_vulkan_phisical_device();
void create_vulkan_logical_device(); void create_vulkan_logical_device();
void create_vulkan_swap_chain(); void create_vulkan_swap_chain();
void create_vulkan_image_view(); void create_vulkan_image_view();
@ -79,6 +81,8 @@ namespace deerith {
void create_vulkan_command_buffer(); void create_vulkan_command_buffer();
void create_vulkan_sync_objects(); void create_vulkan_sync_objects();
void vulkan_clean_swap_chain();
void vulkan_recreate_swap_chain();
void vulkan_test_loop(); void vulkan_test_loop();
QueueFamilyIndices find_queue_families(VkPhysicalDevice device); QueueFamilyIndices find_queue_families(VkPhysicalDevice device);

View File

@ -3,15 +3,19 @@
#include "detail/graphics.h" #include "detail/graphics.h"
namespace deerith { namespace deerith {
namespace graphics {
void frame_buffer_resize_callback(GLFWwindow* window, int width, int height);
}
void graphics::init_glfw() { void graphics::init_glfw() {
deerith_graphics_trace("Initializing glfw"); deerith_graphics_trace("Initializing glfw");
glfwInit(); glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(graphics_config.window_width, graphics_config.window_height, graphics_config.window_title, nullptr, nullptr); window = glfwCreateWindow(graphics_config.window_width, graphics_config.window_height, graphics_config.window_title, nullptr, nullptr);
glfwSetFramebufferSizeCallback(window, frame_buffer_resize_callback);
if (!window) { if (!window) {
deerith_graphics_error("failed to init glfw window"); deerith_graphics_error("failed to init glfw window");
@ -24,4 +28,13 @@ namespace deerith {
glfwDestroyWindow(window); glfwDestroyWindow(window);
glfwTerminate(); glfwTerminate();
} }
void graphics::frame_buffer_resize_callback(GLFWwindow* window, int width, int height) {
deerith_graphics_trace("called frame buffer resize");
graphics_config.window_height = height;
graphics_config.window_width = width;
frame_buffer_resize = true;
}
} // namespace deerith } // namespace deerith

View File

@ -27,8 +27,10 @@ namespace deerith {
VkPipeline graphics::graphics_pipeline; VkPipeline graphics::graphics_pipeline;
std::vector<VkFramebuffer> graphics::swap_chain_frame_buffers; std::vector<VkFramebuffer> graphics::swap_chain_frame_buffers;
VkCommandPool graphics::command_pool; VkCommandPool graphics::command_pool;
VkCommandBuffer graphics::command_buffer; std::vector<VkCommandBuffer> graphics::command_buffers;
VkSemaphore graphics::image_available_semaphore; std::vector<VkSemaphore> graphics::image_available_semaphores;
VkSemaphore graphics::render_finished_semaphore; std::vector<VkSemaphore> graphics::render_finished_semaphores;
VkFence graphics::in_flight_fence; std::vector<VkFence> graphics::in_flight_fences;
uint32_t graphics::current_frame = 0;
bool graphics::frame_buffer_resize = false;
} // namespace deerith } // namespace deerith

View File

@ -7,7 +7,7 @@ namespace deerith {
create_vulkan_validation_layer(); create_vulkan_validation_layer();
create_vulkan_instance(); create_vulkan_instance();
create_vulkan_surface(); create_vulkan_surface();
create_vulkan_phisical_device(); pick_vulkan_phisical_device();
create_vulkan_logical_device(); create_vulkan_logical_device();
create_vulkan_swap_chain(); create_vulkan_swap_chain();
create_vulkan_image_view(); create_vulkan_image_view();
@ -20,25 +20,48 @@ namespace deerith {
} }
void graphics::shutdown_vulkan() { void graphics::shutdown_vulkan() {
deerith_graphics_trace("Shutting Vulkan"); deerith_graphics_trace("Waiting for Vulkan device to finish");
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
vkDestroyCommandPool(device, command_pool, nullptr);
for (auto framebuffer : swap_chain_frame_buffers) { deerith_graphics_trace("Shutting Vulkan");
vkDestroyFramebuffer(device, framebuffer, nullptr); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(device, render_finished_semaphores[i], nullptr);
vkDestroySemaphore(device, image_available_semaphores[i], nullptr);
vkDestroyFence(device, in_flight_fences[i], nullptr);
} }
vkDestroyCommandPool(device, command_pool, nullptr);
vulkan_clean_swap_chain();
vkDestroyPipeline(device, graphics_pipeline, nullptr); vkDestroyPipeline(device, graphics_pipeline, nullptr);
vkDestroyRenderPass(device, render_pass, nullptr); vkDestroyRenderPass(device, render_pass, nullptr);
vkDestroyPipelineLayout(device, pipeline_layout, nullptr); vkDestroyPipelineLayout(device, pipeline_layout, nullptr);
for (auto image_view : swap_chain_image_views) {
vkDestroyImageView(device, image_view, nullptr);
}
vkDestroySwapchainKHR(device, swap_chain, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyDevice(device, nullptr); vkDestroyDevice(device, nullptr);
vkDestroyInstance(instance, nullptr); vkDestroyInstance(instance, nullptr);
} }
void graphics::vulkan_clean_swap_chain() {
for (auto framebuffer : swap_chain_frame_buffers) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
for (auto imageView : swap_chain_image_views) {
vkDestroyImageView(device, imageView, nullptr);
}
vkDestroySwapchainKHR(device, swap_chain, nullptr);
}
void graphics::vulkan_recreate_swap_chain() {
vkDeviceWaitIdle(device);
vulkan_clean_swap_chain();
create_vulkan_swap_chain();
create_vulkan_image_view();
create_vulkan_frame_buffer();
}
} // namespace deerith } // namespace deerith

View File

@ -17,7 +17,7 @@ namespace deerith {
frame_buffer_info.layers = 1; frame_buffer_info.layers = 1;
if (vkCreateFramebuffer(device, &frame_buffer_info, nullptr, &swap_chain_frame_buffers[i]) != VK_SUCCESS) { if (vkCreateFramebuffer(device, &frame_buffer_info, nullptr, &swap_chain_frame_buffers[i]) != VK_SUCCESS) {
deerith_graphics_error("failed to create framebuffer!"); deerith_graphics_error("failed to create frame_buffer!");
} }
} }
} }

View File

@ -8,19 +8,19 @@ namespace deerith {
bool check_device_extension_support(VkPhysicalDevice device); bool check_device_extension_support(VkPhysicalDevice device);
} // namespace graphics } // namespace graphics
void graphics::create_vulkan_phisical_device() { void graphics::pick_vulkan_phisical_device() {
deerith_graphics_trace("Initializing Vulkan phisical device"); deerith_graphics_trace("Choosing Vulkan phisical device");
uint32_t deviceCount = 0; uint32_t device_count = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
if (deviceCount == 0) { if (device_count == 0) {
deerith_graphics_error("failed to find GPUs with Vulkan support!"); deerith_graphics_error("failed to find GPUs with Vulkan support!");
return; return;
} }
std::vector<VkPhysicalDevice> devices(deviceCount); std::vector<VkPhysicalDevice> devices(device_count);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
for (const auto& device : devices) { for (const auto& device : devices) {
if (is_device_suitable(device)) { if (is_device_suitable(device)) {

View File

@ -2,7 +2,7 @@
namespace deerith { namespace deerith {
void graphics::create_vulkan_surface() { void graphics::create_vulkan_surface() {
deerith_graphics_trace("Initializing Vulkan pressentation/surface"); deerith_graphics_trace("Initializing Vulkan surface");
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
deerith_graphics_error("failed to create window surface!"); deerith_graphics_error("failed to create window surface!");

View File

@ -13,13 +13,15 @@ namespace deerith {
deerith_graphics_error("failed to create command pool!"); deerith_graphics_error("failed to create command pool!");
} }
command_buffers.resize(MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo alloc_info{}; VkCommandBufferAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.commandPool = command_pool; alloc_info.commandPool = command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = 1; alloc_info.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
if (vkAllocateCommandBuffers(device, &alloc_info, &command_buffer) != VK_SUCCESS) { if (vkAllocateCommandBuffers(device, &alloc_info, command_buffers.data()) != VK_SUCCESS) {
deerith_graphics_error("failed to allocate command buffers!"); deerith_graphics_error("failed to allocate command buffers!");
} }
} }

View File

@ -15,22 +15,22 @@ namespace deerith {
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachmentRef{}; VkAttachmentReference color_attachment_ref{};
color_attachmentRef.attachment = 0; color_attachment_ref.attachment = 0;
color_attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{}; VkSubpassDescription sub_pass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; sub_pass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1; sub_pass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachmentRef; sub_pass.pColorAttachments = &color_attachment_ref;
VkRenderPassCreateInfo render_pass_info{}; VkRenderPassCreateInfo render_pass_info{};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = 1; render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &color_attachment; render_pass_info.pAttachments = &color_attachment;
render_pass_info.subpassCount = 1; render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass; render_pass_info.pSubpasses = &sub_pass;
if (vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass) != VK_SUCCESS) { if (vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass) != VK_SUCCESS) {
deerith_graphics_error("failed to create render pass!"); deerith_graphics_error("failed to create render pass!");

View File

@ -13,6 +13,10 @@ namespace deerith {
} }
void graphics::create_vulkan_sync_objects() { void graphics::create_vulkan_sync_objects() {
image_available_semaphores.resize(MAX_FRAMES_IN_FLIGHT);
render_finished_semaphores.resize(MAX_FRAMES_IN_FLIGHT);
in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT);
VkSemaphoreCreateInfo semaphoreInfo{}; VkSemaphoreCreateInfo semaphoreInfo{};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@ -20,41 +24,52 @@ namespace deerith {
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &image_available_semaphore) != VK_SUCCESS || for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &render_finished_semaphore) != VK_SUCCESS || if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &image_available_semaphores[i]) != VK_SUCCESS ||
vkCreateFence(device, &fenceInfo, nullptr, &in_flight_fence) != VK_SUCCESS) { vkCreateSemaphore(device, &semaphoreInfo, nullptr, &render_finished_semaphores[i]) != VK_SUCCESS ||
deerith_graphics_error("failed to create semaphores!"); vkCreateFence(device, &fenceInfo, nullptr, &in_flight_fences[i]) != VK_SUCCESS) {
deerith_graphics_error("failed to create synchronization objects for a frame!");
}
} }
} }
void graphics::drawFrame() { void graphics::drawFrame() {
vkWaitForFences(device, 1, &in_flight_fence, VK_TRUE, UINT64_MAX); vkWaitForFences(device, 1, &in_flight_fences[current_frame], VK_TRUE, UINT64_MAX);
vkResetFences(device, 1, &in_flight_fence);
uint32_t image_index; uint32_t image_index;
vkAcquireNextImageKHR(device, swap_chain, UINT64_MAX, image_available_semaphore, VK_NULL_HANDLE, &image_index); VkResult result = vkAcquireNextImageKHR(device, swap_chain, UINT64_MAX, image_available_semaphores[current_frame], VK_NULL_HANDLE, &image_index);
vkResetCommandBuffer(command_buffer, 0); if (result == VK_ERROR_OUT_OF_DATE_KHR) {
frame_buffer_resize = false;
vulkan_recreate_swap_chain();
return;
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
deerith_graphics_error("failed to acquire swap chain image!");
}
record_command_buffer(command_buffer, image_index); vkResetFences(device, 1, &in_flight_fences[current_frame]);
vkResetCommandBuffer(command_buffers[current_frame], 0);
record_command_buffer(command_buffers[current_frame], image_index);
VkSubmitInfo submit_info{}; VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {image_available_semaphore}; VkSemaphore waitSemaphores[] = {image_available_semaphores[current_frame]};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submit_info.waitSemaphoreCount = 1; submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = waitSemaphores; submit_info.pWaitSemaphores = waitSemaphores;
submit_info.pWaitDstStageMask = waitStages; submit_info.pWaitDstStageMask = waitStages;
submit_info.commandBufferCount = 1; submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer; submit_info.pCommandBuffers = &command_buffers[current_frame];
VkSemaphore signalSemaphores[] = {render_finished_semaphore}; VkSemaphore signalSemaphores[] = {render_finished_semaphores[current_frame]};
submit_info.signalSemaphoreCount = 1; submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signalSemaphores; submit_info.pSignalSemaphores = signalSemaphores;
if (vkQueueSubmit(graphics_queue, 1, &submit_info, in_flight_fence) != VK_SUCCESS) { if (vkQueueSubmit(graphics_queue, 1, &submit_info, in_flight_fences[current_frame]) != VK_SUCCESS) {
deerith_graphics_error("failed to submit draw command buffer!"); deerith_graphics_error("failed to submit draw command buffer!");
} }
@ -78,8 +93,17 @@ namespace deerith {
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains; presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &image_index; presentInfo.pImageIndices = &image_index;
presentInfo.pResults = nullptr; // Optional presentInfo.pResults = nullptr;
vkQueuePresentKHR(pressent_queue, &presentInfo); result = vkQueuePresentKHR(pressent_queue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || frame_buffer_resize) {
vulkan_recreate_swap_chain();
frame_buffer_resize = false;
} else if (result != VK_SUCCESS) {
deerith_graphics_error("failed to present swap chain image!");
}
current_frame = (current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
} }
} // namespace deerith } // namespace deerith

View File

@ -15,10 +15,10 @@ namespace deerith {
SwapChainSupportDetails swap_chain_support = query_swap_chain_support(physical_device); SwapChainSupportDetails swap_chain_support = query_swap_chain_support(physical_device);
VkSurfaceFormatKHR surface_format = choose_swap_surface_format(swap_chain_support.formats); VkSurfaceFormatKHR surface_format = choose_swap_surface_format(swap_chain_support.formats);
VkPresentModeKHR presentMode = choose_swap_present_mode(swap_chain_support.present_modes); VkPresentModeKHR present_mode = choose_swap_present_mode(swap_chain_support.present_modes);
VkExtent2D extent = choose_swap_extent(swap_chain_support.capabilities); VkExtent2D extent = choose_swap_extent(swap_chain_support.capabilities);
uint32_t image_count = swap_chain_support.capabilities.minImageCount; uint32_t image_count = swap_chain_support.capabilities.minImageCount + 1;
if (swap_chain_support.capabilities.maxImageCount > 0 && image_count > swap_chain_support.capabilities.maxImageCount) { if (swap_chain_support.capabilities.maxImageCount > 0 && image_count > swap_chain_support.capabilities.maxImageCount) {
image_count = swap_chain_support.capabilities.maxImageCount; image_count = swap_chain_support.capabilities.maxImageCount;
} }
@ -48,7 +48,7 @@ namespace deerith {
create_info.preTransform = swap_chain_support.capabilities.currentTransform; create_info.preTransform = swap_chain_support.capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = presentMode; create_info.presentMode = present_mode;
create_info.clipped = VK_TRUE; create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE; create_info.oldSwapchain = VK_NULL_HANDLE;

View File

@ -10,8 +10,8 @@ int main() {
graphics_config.window_title = "Deerith Studio"; graphics_config.window_title = "Deerith Studio";
deerith::graphics::init(graphics_config); deerith::graphics::init(graphics_config);
deerith::graphics::shutdown(); deerith::graphics::shutdown();
deerith::engine::shutdown(); deerith::engine::shutdown();
return 0; return 0;
} }