Creating a Window
Introduction
Section titled “Introduction”Creating a window is the first step when developing a graphics application. This tutorial walks you through creating a window.hpp
file to manage a window using GLFW and integrate it with Daxa.
1. Creating the Header File
Section titled “1. Creating the Header File”Start by creating a new header file src/window.hpp
. This file will serve as the abstraction layer for the GLFW windowing library, encapsulating its functionalities within a clean and reusable interface.
#pragma once
struct AppWindow { // Window-related properties and methods will go here};
Explanation
#pragma once
: Ensures the file is included only once during compilation, preventing duplicate definitions.struct AppWindow
: Declares a placeholder structure to encapsulate window-related functionality. This allows us to modularly add methods and properties later.
2. Including Required Libraries
Section titled “2. Including Required Libraries”To integrate GLFW and Daxa, include the necessary headers. Since GLFW provides platform-specific APIs for window creation and management, use preprocessor directives to include platform-specific definitions.
#pragma once
#include <daxa/daxa.hpp>using namespace daxa::types; // For types like `u32`
#include <GLFW/glfw3.h>
#if defined(_WIN32) #define GLFW_EXPOSE_NATIVE_WIN32 #define GLFW_NATIVE_INCLUDE_NONE using HWND = void*;#elif defined(__linux__) #define GLFW_EXPOSE_NATIVE_X11 #define GLFW_EXPOSE_NATIVE_WAYLAND#endif
#include <GLFW/glfw3native.h> // Platform-specific GLFW functions
struct AppWindow { // Window-related properties and methods will go here};
Why These Includes?
- Daxa: Provides rendering capabilities and essential types (like
u32
for unsigned 32-bit integers). - GLFW: A cross-platform library for creating windows and handling input.
- GLFW Native Headers: Expose low-level platform-specific APIs (e.g., Windows HWND or Linux X11). :::
3. Defining Window Properties
Section titled “3. Defining Window Properties”Define the properties necessary to manage the window’s state, such as dimensions, a pointer to the GLFW window object, and flags to track the window’s status.
struct AppWindow { GLFWwindow* glfw_window_ptr; // Pointer to the GLFW window object u32 width, height; // Dimensions of the window bool minimized = false; // Tracks if the window is minimized bool swapchain_out_of_date = false; // Tracks if the swapchain needs updating};
Explanation
GLFWwindow* glfw_window_ptr
: Stores the GLFW window object created during initialization. This is the primary handle for interacting with the window.u32 width, height
: Tracks the window’s current width and height, useful for managing rendering surfaces and responding to resize events.bool minimized
: A flag to check if the window is minimized. Rendering can often be paused when minimized to save resources.bool swapchain_out_of_date
: Indicates when the swapchain (used for rendering) needs to be recreated, often after window resizing.
4. Constructor and Destructor
Section titled “4. Constructor and Destructor”In this step, you’ll define the constructor to initialize the window, configure callbacks for resizing, and handle cleanup in the destructor.
Constructor
Section titled “Constructor”The constructor initializes the GLFW window, sets its properties, and registers callbacks for resizing events.
explicit AppWindow(char const* window_name, u32 sx = 800, u32 sy = 600) : width{sx}, height{sy} { glfwInit(); // Initialize GLFW
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // No graphics API glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Make the window resizable
// Create the GLFW window glfw_window_ptr = glfwCreateWindow( static_cast<i32>(width), static_cast<i32>(height), window_name, nullptr, nullptr );
// Associate the AppWindow object with the GLFW window glfwSetWindowUserPointer(glfw_window_ptr, this);
// Set a callback to handle resizing events glfwSetWindowSizeCallback(glfw_window_ptr, [](GLFWwindow* window, int size_x, int size_y) { auto* win = static_cast<AppWindow*>(glfwGetWindowUserPointer(window)); win->width = static_cast<u32>(size_x); win->height = static_cast<u32>(size_y); win->swapchain_out_of_date = true; });}
Key Points
glfwInit()
: Initializes the GLFW library. Always call this before using GLFW functions.glfwWindowHint()
: Configures the window. SettingGLFW_CLIENT_API
toGLFW_NO_API
means the window won’t automatically use a graphics API like OpenGL.glfwSetWindowUserPointer()
: Associates a user-defined pointer (ourAppWindow
instance) with the GLFW window, allowing us to reference theAppWindow
object in callbacks.- Resize Callback: Updates window dimensions and marks the swapchain as out-of-date whenever the window size changes.
Destructor
Section titled “Destructor”The destructor ensures proper cleanup of GLFW resources to avoid memory leaks or dangling pointers.
~AppWindow() { glfwDestroyWindow(glfw_window_ptr); // Destroy the GLFW window glfwTerminate(); // Terminate GLFW}
Why This is Important
glfwDestroyWindow()
: Releases memory and handles associated with the window.glfwTerminate()
: Cleans up GLFW internals. Failing to call this can lead to resource leaks.
5. Native Handles and Platform Identification
Section titled “5. Native Handles and Platform Identification”To interface with platform-specific window systems (e.g., Windows’ HWND or Linux’s X11/Wayland), expose methods to retrieve native handles and identify the platform.
Native Handle Retrieval
Section titled “Native Handle Retrieval”This function returns a native window handle compatible with Daxa’s NativeWindowHandle
type.
auto get_native_handle() const -> daxa::NativeWindowHandle {#if defined(_WIN32) return glfwGetWin32Window(glfw_window_ptr);#elif defined(__linux__) switch (get_native_platform()) { case daxa::NativeWindowPlatform::WAYLAND_API: return reinterpret_cast<daxa::NativeWindowHandle>(glfwGetWaylandWindow(glfw_window_ptr)); case daxa::NativeWindowPlatform::XLIB_API: default: return reinterpret_cast<daxa::NativeWindowHandle>(glfwGetX11Window(glfw_window_ptr)); }#endif}
Key Notes
Platform-Specific Retrieval:
- On Windows,
glfwGetWin32Window()
retrieves anHWND
handle. - On Linux, either
glfwGetX11Window()
orglfwGetWaylandWindow()
is used, depending on the platform.
- On Windows,
daxa::NativeWindowHandle
: A Daxa-specific abstraction for handling different windowing systems. By returning this type, the function ensures compatibility with Daxa’s rendering APIs.
Platform Identification
Section titled “Platform Identification”This static function identifies the native platform and maps it to Daxa’s NativeWindowPlatform enumeration.
static auto get_native_platform() -> daxa::NativeWindowPlatform { switch (glfwGetPlatform()) { case GLFW_PLATFORM_WIN32: return daxa::NativeWindowPlatform::WIN32_API; case GLFW_PLATFORM_X11: return daxa::NativeWindowPlatform::XLIB_API; case GLFW_PLATFORM_WAYLAND: return daxa::NativeWindowPlatform::WAYLAND_API; default: return daxa::NativeWindowPlatform::UNKNOWN; }}
Key Notes
glfwGetPlatform()
: Returns the current platform (e.g., Win32, X11, Wayland).- *Mapping to
daxa::NativeWindowPlatform
: Provides an abstraction layer to decouple GLFW-specific platform identifiers from Daxa’s API.
6. Utility Functions
Section titled “6. Utility Functions”To simplify window management, implement utility methods for common tasks like mouse control, checking if the window should close, and handling updates.
inline void set_mouse_capture(bool should_capture) const { glfwSetCursorPos(glfw_window_ptr, static_cast<f64>(width / 2.), static_cast<f64>(height / 2.)); glfwSetInputMode(glfw_window_ptr, GLFW_CURSOR, should_capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); glfwSetInputMode(glfw_window_ptr, GLFW_RAW_MOUSE_MOTION, should_capture);}
inline bool should_close() const { return glfwWindowShouldClose(glfw_window_ptr);}
inline void update() const { glfwPollEvents(); glfwSwapBuffers(glfw_window_ptr);}
inline GLFWwindow* get_glfw_window() const { return glfw_window_ptr;}
Function Breakdown
set_mouse_capture(bool should_capture)
:- Centers the mouse cursor within the window and adjusts its behavior.
GLFW_CURSOR_DISABLED
: Locks the cursor to the window, often used in first-person or immersive applications.GLFW_CURSOR_NORMAL
: Frees the cursor for standard interaction.GLFW_RAW_MOUSE_MOTION
: Enables raw mouse motion, bypassing OS-level acceleration for precise control. This function is ideal for games or interactive simulations where precise control over the mouse is required.
should_close()
:- Returns whether the window should close, typically triggered by the user clicking the close button or pressing Alt+F4.
update()
:- Handles event polling and buffer swapping.
glfwPollEvents()
: Processes all pending input events (e.g., mouse movement, keyboard presses).glfwSwapBuffers()
: Swaps the front and back buffers to present the rendered frame. Always call this function once per frame to ensure smooth rendering and responsive input handling.
get_glfw_window()
:- Provides direct access to the raw GLFWwindow* pointer for advanced interactions or integrations.
7. Opening the Window
Section titled “7. Opening the Window”Integrate the AppWindow
class into your application by creating and managing the window in main.cpp
.
Example Usage in main.cpp
Section titled “Example Usage in main.cpp”#include "window.hpp"
int main(int argc, char const* argv[]) { // Create a window auto window = AppWindow("Learn Daxa", 860, 640);
// Daxa rendering initialization code goes here...
// Main loop while (!window.should_close()) { window.update(); }
return 0;}
Step-by-Step Explanation
Creating the Window:
AppWindow("Learn Daxa", 860, 640)
initializes a window with the title"Learn Daxa"
and dimensions 860x640.
Main Application Loop:
- The
while (!window.should_close())
loop keeps the application running until the window is closed. window.update()
ensures the application processes events and renders frames.
- The
Integration with Daxa:
- Add Daxa-specific rendering code (e.g., setting up pipelines, rendering frames) in place of the
// Daxa rendering initialization code goes here...
comment.
- Add Daxa-specific rendering code (e.g., setting up pipelines, rendering frames) in place of the
Summary
Section titled “Summary”You now have a fully functional GLFW window integrated with Daxa, complete with resizing support and utility functions. Use this as the foundation for your graphics applications.