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

View File

@ -3,15 +3,19 @@
#include "detail/graphics.h"
namespace deerith {
namespace graphics {
void frame_buffer_resize_callback(GLFWwindow* window, int width, int height);
}
void graphics::init_glfw() {
deerith_graphics_trace("Initializing glfw");
glfwInit();
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);
glfwSetFramebufferSizeCallback(window, frame_buffer_resize_callback);
if (!window) {
deerith_graphics_error("failed to init glfw window");
@ -24,4 +28,13 @@ namespace deerith {
glfwDestroyWindow(window);
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

View File

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

View File

@ -7,7 +7,7 @@ namespace deerith {
create_vulkan_validation_layer();
create_vulkan_instance();
create_vulkan_surface();
create_vulkan_phisical_device();
pick_vulkan_phisical_device();
create_vulkan_logical_device();
create_vulkan_swap_chain();
create_vulkan_image_view();
@ -20,25 +20,48 @@ namespace deerith {
}
void graphics::shutdown_vulkan() {
deerith_graphics_trace("Shutting Vulkan");
deerith_graphics_trace("Waiting for Vulkan device to finish");
vkDeviceWaitIdle(device);
vkDestroyCommandPool(device, command_pool, nullptr);
for (auto framebuffer : swap_chain_frame_buffers) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
deerith_graphics_trace("Shutting Vulkan");
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);
vkDestroyRenderPass(device, render_pass, 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);
vkDestroyDevice(device, 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

View File

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

View File

@ -2,7 +2,7 @@
namespace deerith {
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) {
deerith_graphics_error("failed to create window surface!");

View File

@ -13,13 +13,15 @@ namespace deerith {
deerith_graphics_error("failed to create command pool!");
}
command_buffers.resize(MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.commandPool = command_pool;
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!");
}
}

View File

@ -15,22 +15,22 @@ namespace deerith {
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachmentRef{};
color_attachmentRef.attachment = 0;
color_attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_attachment_ref{};
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
VkSubpassDescription sub_pass{};
sub_pass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachmentRef;
sub_pass.colorAttachmentCount = 1;
sub_pass.pColorAttachments = &color_attachment_ref;
VkRenderPassCreateInfo render_pass_info{};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &color_attachment;
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) {
deerith_graphics_error("failed to create render pass!");

View File

@ -13,6 +13,10 @@ namespace deerith {
}
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{};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@ -20,41 +24,52 @@ namespace deerith {
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &image_available_semaphore) != VK_SUCCESS ||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &render_finished_semaphore) != VK_SUCCESS ||
vkCreateFence(device, &fenceInfo, nullptr, &in_flight_fence) != VK_SUCCESS) {
deerith_graphics_error("failed to create semaphores!");
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &image_available_semaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &render_finished_semaphores[i]) != VK_SUCCESS ||
vkCreateFence(device, &fenceInfo, nullptr, &in_flight_fences[i]) != VK_SUCCESS) {
deerith_graphics_error("failed to create synchronization objects for a frame!");
}
}
}
void graphics::drawFrame() {
vkWaitForFences(device, 1, &in_flight_fence, VK_TRUE, UINT64_MAX);
vkResetFences(device, 1, &in_flight_fence);
vkWaitForFences(device, 1, &in_flight_fences[current_frame], VK_TRUE, UINT64_MAX);
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{};
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};
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = waitSemaphores;
submit_info.pWaitDstStageMask = waitStages;
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.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!");
}
@ -78,8 +93,17 @@ namespace deerith {
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
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

View File

@ -15,10 +15,10 @@ namespace deerith {
SwapChainSupportDetails swap_chain_support = query_swap_chain_support(physical_device);
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);
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) {
image_count = swap_chain_support.capabilities.maxImageCount;
}
@ -48,7 +48,7 @@ namespace deerith {
create_info.preTransform = swap_chain_support.capabilities.currentTransform;
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.oldSwapchain = VK_NULL_HANDLE;

View File

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