Added resize + fullscreen support

This commit is contained in:
Pasha Bibko
2025-11-16 22:03:14 +00:00
parent edcf13d62a
commit cfce4b3069
5 changed files with 142 additions and 43 deletions

View File

@@ -36,7 +36,7 @@ int main()
glfwPollEvents(); glfwPollEvents();
GLFWManager::UpdateWindowTitleFPSInfo(); GLFWManager::UpdateWindowTitleFPSInfo();
if (!VulkanManager::RenderPass()) if (!VulkanManager::RenderPass(window))
CleanupAllAndExit(EXIT_FAILURE); CleanupAllAndExit(EXIT_FAILURE);
} }

View File

@@ -12,6 +12,13 @@ namespace PB::Renderer
std::string GLFWManager::s_BaseWindowTitle = {}; std::string GLFWManager::s_BaseWindowTitle = {};
bool GLFWManager::s_Fullscreen = false;
int GLFWManager::s_WindowedWidth = 800;
int GLFWManager::s_WindowedHeight = 600;
int GLFWManager::s_WindowedPosX = 50;
int GLFWManager::s_WindowedPosY = 50;
GLFWwindow* GLFWManager::Init(const int width, const int height, const char* title) GLFWwindow* GLFWManager::Init(const int width, const int height, const char* title)
{ {
const bool success = glfwInit(); const bool success = glfwInit();
@@ -31,10 +38,13 @@ namespace PB::Renderer
return nullptr; return nullptr;
} }
/* Creates the window */
s_Window = glfwCreateWindow(width, height, title, nullptr, nullptr);
s_BaseWindowTitle = title; s_BaseWindowTitle = title;
/* Stores the window and returns it to the user */ /* Adds callbacks to the window and returns it */
s_Window = glfwCreateWindow(width, height, title, nullptr, nullptr); glfwSetKeyCallback(s_Window, KeyCallback);
return s_Window; return s_Window;
} }
@@ -59,13 +69,44 @@ namespace PB::Renderer
s_LastFPSUpdateTime = currentTime; s_LastFPSUpdateTime = currentTime;
} }
double deltaTime = currentTime - s_LastDeltaUpdateTime; const double deltaTime = currentTime - s_LastDeltaUpdateTime;
s_LastDeltaUpdateTime = currentTime; s_LastDeltaUpdateTime = currentTime;
std::string fullTitle = s_BaseWindowTitle + '(' const std::string fullTitle = s_BaseWindowTitle + '('
+ std::to_string(s_FpsCounter) + " FPS | " + std::to_string(s_FpsCounter) + " FPS | "
+ std::to_string(deltaTime) + " ms)"; + std::to_string(deltaTime) + " ms)";
glfwSetWindowTitle(s_Window, fullTitle.c_str()); glfwSetWindowTitle(s_Window, fullTitle.c_str());
} }
void GLFWManager::KeyCallback(GLFWwindow* window, const int key, const int scancode, const int action, const int mode)
{
if (action != GLFW_PRESS)
return;
switch (key)
{
case GLFW_KEY_F11:
{
s_Fullscreen = !s_Fullscreen;
if (s_Fullscreen)
{
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* videoMode = glfwGetVideoMode(monitor);
glfwGetWindowPos(window, &s_WindowedPosX, &s_WindowedPosY);
glfwGetWindowSize(window, &s_WindowedWidth, &s_WindowedHeight);
glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate);
return;
}
glfwSetWindowMonitor(window, nullptr, s_WindowedPosX, s_WindowedPosY, s_WindowedWidth, s_WindowedHeight, 0);
return;
}
default:
return;
}
}
} }

View File

@@ -6,6 +6,15 @@ namespace PB::Renderer
{ {
class GLFWManager class GLFWManager
{ {
public:
static GLFWwindow* Init(int width, int height, const char* title = "Unnamed window");
__forceinline static GLFWwindow* GetWindow() { return s_Window; }
static bool Cleanup();
static __forceinline void PollEvents() { glfwPollEvents(); }
static void UpdateWindowTitleFPSInfo();
private: private:
static GLFWwindow* s_Window; static GLFWwindow* s_Window;
@@ -16,13 +25,12 @@ namespace PB::Renderer
static std::string s_BaseWindowTitle; static std::string s_BaseWindowTitle;
public: static bool s_Fullscreen;
static GLFWwindow* Init(int width, int height, const char* title = "Unnamed window"); static int s_WindowedWidth;
__forceinline static GLFWwindow* GetWindow() { return s_Window; } static int s_WindowedHeight;
static int s_WindowedPosX;
static int s_WindowedPosY;
static bool Cleanup(); static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode);
static __forceinline void PollEvents() { glfwPollEvents(); }
static void UpdateWindowTitleFPSInfo();
}; };
} }

View File

@@ -89,7 +89,7 @@ namespace PB::Renderer
/* /*
Assembles shaders, fixed function state and creates the render pipeline. Assembles shaders, fixed function state and creates the render pipeline.
Also checks that the render pass is compatiable. Also checks that the render pass is compatible.
*/ */
static bool CreateGraphicsPipeline(); static bool CreateGraphicsPipeline();
@@ -108,9 +108,11 @@ namespace PB::Renderer
/* /*
Draws a frame to the screen using the command buffers and sync objects. Draws a frame to the screen using the command buffers and sync objects.
*/ */
static bool RenderPass(); static bool RenderPass(GLFWwindow* window);
private: private:
// === Vulkan init helpers === //
static bool IsDeviceSuitable(const VkPhysicalDevice& device); static bool IsDeviceSuitable(const VkPhysicalDevice& device);
static QueueFamilyIndices FindQueueFamilies(const VkPhysicalDevice& device); static QueueFamilyIndices FindQueueFamilies(const VkPhysicalDevice& device);
static bool CheckDeviceExtensionSupport(const VkPhysicalDevice& device); static bool CheckDeviceExtensionSupport(const VkPhysicalDevice& device);
@@ -122,6 +124,15 @@ namespace PB::Renderer
static VkShaderModule CreateShaderModule(const std::string& filename); static VkShaderModule CreateShaderModule(const std::string& filename);
// === Vulkan render helpers === //
static VkResult RenderPassInternal();
static void RecreateSwapChain(GLFWwindow* window);
static void CleanupSwapChain();
// === Vulkan resources === //
static VkInstance s_Instance; static VkInstance s_Instance;
static VkSurfaceKHR s_Surface; static VkSurfaceKHR s_Surface;

View File

@@ -1,8 +1,70 @@
#include "VulkanManager.h" #include "VulkanManager.h"
#define CHECK_RESULT(res) if (res != VK_SUCCESS) { return res; }
namespace PB::Renderer namespace PB::Renderer
{ {
bool VulkanManager::RenderPass() bool VulkanManager::RenderPass(GLFWwindow* window)
{
switch (const VkResult result = RenderPassInternal())
{
case VK_ERROR_OUT_OF_DATE_KHR:
case VK_SUBOPTIMAL_KHR:
RecreateSwapChain(window);
return true;
default:
return result == VK_SUCCESS;
}
}
void VulkanManager::RecreateSwapChain(GLFWwindow* window)
{
int width, height;
glfwGetFramebufferSize(window, &width, &height);
/* Pauses if minimised */
while (width == 0 || height == 0)
{
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
/* Waits until device is idle before recreating swap chain */
vkDeviceWaitIdle(s_Device);
CleanupSwapChain();
CreateSwapChain(window);
CreateImageViews();
CreateRenderPass();
CreateFramebuffer();
CreateGraphicsPipeline();
CreateCommandBuffers();
CreateSemaphores();
}
void VulkanManager::CleanupSwapChain()
{
for (const VkFramebuffer framebuffer : s_Framebuffers)
{
vkDestroyFramebuffer(s_Device, framebuffer, nullptr);
}
s_Framebuffers.clear();
vkFreeCommandBuffers(s_Device, s_CommandPool, static_cast<uint32_t>(s_CommandBuffers.size()), s_CommandBuffers.data());
vkDestroyPipeline(s_Device, s_RenderPipeline, nullptr);
vkDestroyPipelineLayout(s_Device, s_PipelineLayout, nullptr);
vkDestroyRenderPass(s_Device, s_RenderPass, nullptr);
for (const VkImageView view : s_SwapChainImageViews)
{
vkDestroyImageView(s_Device, view, nullptr);
}
vkDestroySwapchainKHR(s_Device, s_SwapChain, nullptr);
}
VkResult VulkanManager::RenderPassInternal()
{ {
uint32_t imageIndex; uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR( VkResult result = vkAcquireNextImageKHR(
@@ -13,17 +75,7 @@ namespace PB::Renderer
VK_NULL_HANDLE, VK_NULL_HANDLE,
&imageIndex &imageIndex
); );
CHECK_RESULT(result);
if (result == VK_ERROR_OUT_OF_DATE_KHR)
{
return false;
}
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
{
std::cout << "PB::Renderer::VulkanManager::RenderPass(): Failed to acquire swap chain image!" << std::endl;
return false;
}
VkSubmitInfo submitInfo{}; VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@@ -41,11 +93,8 @@ namespace PB::Renderer
submitInfo.signalSemaphoreCount = 1; submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores; submitInfo.pSignalSemaphores = signalSemaphores;
if (vkQueueSubmit(s_GraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) result = vkQueueSubmit(s_GraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
{ CHECK_RESULT(result);
std::cout << "PB::Renderer::VulkanManager::RenderPass(): Failed to submit draw command buffer!" << std::endl;
return false;
}
VkPresentInfoKHR presentInfo{}; VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
@@ -58,19 +107,9 @@ namespace PB::Renderer
presentInfo.pImageIndices = &imageIndex; presentInfo.pImageIndices = &imageIndex;
result = vkQueuePresentKHR(s_PresentQueue, &presentInfo); result = vkQueuePresentKHR(s_PresentQueue, &presentInfo);
CHECK_RESULT(result);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
{
return false;
}
if (result != VK_SUCCESS)
{
std::cout << "PB::Renderer::VulkanManager::RenderPass(): Failed to present swap chain image!" << std::endl;
return false;
}
vkQueueWaitIdle(s_PresentQueue); vkQueueWaitIdle(s_PresentQueue);
return true; return VK_SUCCESS;
} }
} }