Skip to content

Commit 6208a42

Browse files
feat: rework 00_Creating_a_window.md
1 parent 68ba50b commit 6208a42

File tree

1 file changed

+229
-74
lines changed

1 file changed

+229
-74
lines changed
+229-74
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,189 @@
11
---
2-
title: Creating a window
3-
description: Creating a window
4-
slug: "getting-started/creating-a-window"
5-
editUrl: https://github.com/learndaxa/Tutorial/edit/main/docs/03 Getting started/00_Creating_a_window.md
2+
title: Creating a Window
3+
description: Learn how to set up a blank window for your graphics application using GLFW and Daxa.
4+
slug: "drawing-a-triangle/creating-a-window"
5+
editUrl: https://github.com/learndaxa/Tutorial/edit/main/docs/02 Drawing a triangle/00_Creating_a_window.md
66
---
77

8-
## Description
8+
## Introduction
99

10-
The first thing when developing a graphics application is to open a blank window.
10+
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.
1111

12-
## Creating a header file
12+
---
13+
14+
## 1. Creating the Header File
1315

14-
In this tutorial, we will create a new header file `src/window.hpp` that is responsible for interfacing and abstracting the windowing library of our choice GLFW.
16+
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.
1517

1618
```cpp
19+
// window.hpp
1720
#pragma once
1821

19-
struct AppWindow{
22+
struct AppWindow {
23+
// Window-related properties and methods will go here
2024
};
2125
```
2226
23-
Next, we need to include the libraries. Since we need the native window handle later, we also need the native GLFW headers. Since those libraries are platform dependant though, we need some extra preprocessor magic.
27+
<details>
28+
<summary>Explanation</summary>
2429
25-
```cpp
26-
#include <daxa/daxa.hpp>
27-
// For things like `u32`. Not necessary of course.
28-
using namespace daxa::types;
30+
- `#pragma once`: Ensures the file is included only once during compilation, preventing duplicate definitions.
31+
- `struct AppWindow`: Declares a placeholder structure to encapsulate window-related functionality. This allows us to modularly add methods and properties later.
2932
30-
#include <GLFW/glfw3.h>
31-
#if defined(_WIN32)
32-
#define GLFW_EXPOSE_NATIVE_WIN32
33-
#define GLFW_NATIVE_INCLUDE_NONE
34-
using HWND = void *;
35-
#elif defined(__linux__)
36-
#define GLFW_EXPOSE_NATIVE_X11
37-
#define GLFW_EXPOSE_NATIVE_WAYLAND
38-
#endif
39-
#include <GLFW/glfw3native.h>
33+
</details>
34+
35+
## 2. Including Required Libraries
36+
37+
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.
38+
39+
```diff lang="cpp"
40+
// window.hpp
41+
#pragma once
42+
43+
+#include <daxa/daxa.hpp>
44+
+using namespace daxa::types; // For types like `u32`
45+
+
46+
+#include <GLFW/glfw3.h>
47+
+
48+
+#if defined(_WIN32)
49+
+ #define GLFW_EXPOSE_NATIVE_WIN32
50+
+ #define GLFW_NATIVE_INCLUDE_NONE
51+
+ using HWND = void*;
52+
+#elif defined(__linux__)
53+
+ #define GLFW_EXPOSE_NATIVE_X11
54+
+ #define GLFW_EXPOSE_NATIVE_WAYLAND
55+
+#endif
56+
+
57+
+#include <GLFW/glfw3native.h> // Platform-specific GLFW functions
58+
59+
struct AppWindow {
60+
// Window-related properties and methods will go here
61+
};
4062
```
4163

42-
We now need to add some properties to our window struct. This includes the GLFW window pointer, the current width and height, whether the window is minimized and whether the swapchain is out of date due to the resizing of the window.
64+
<details>
65+
<summary>Why These Includes?</summary>
4366

44-
```cpp
45-
GLFWwindow * glfw_window_ptr;
46-
u32 width, height;
47-
bool minimized = false;
48-
bool swapchain_out_of_date = false;
67+
- **Daxa**: Provides rendering capabilities and essential types (like `u32` for unsigned 32-bit integers).
68+
- **GLFW**: A cross-platform library for creating windows and handling input.
69+
- **GLFW Native Headers**: Expose low-level platform-specific APIs (e.g., Windows HWND or Linux X11). :::
70+
71+
</details>
72+
73+
## 3. Defining Window Properties
74+
75+
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.
76+
77+
```diff lang="cpp"
78+
// window.hpp
79+
struct AppWindow {
80+
+ GLFWwindow* glfw_window_ptr; // Pointer to the GLFW window object
81+
+ u32 width, height; // Dimensions of the window
82+
+ bool minimized = false; // Tracks if the window is minimized
83+
+ bool swapchain_out_of_date = false; // Tracks if the swapchain needs updating
84+
};
4985
```
5086

51-
We can now create a constructor and a destructor for the window.
87+
<details>
88+
<summary>Explanation</summary>
5289

53-
```cpp
54-
explicit AppWindow(char const * window_name, u32 sx = 800, u32 sy = 600) : width{sx}, height{sy} {
55-
// Initialize GLFW
56-
glfwInit();
90+
1. `GLFWwindow* glfw_window_ptr`: Stores the GLFW window object created during initialization. This is the primary handle for interacting with the window.
91+
2. `u32 width, height`: Tracks the window's current width and height, useful for managing rendering surfaces and responding to resize events.
92+
3. `bool minimized`: A flag to check if the window is minimized. Rendering can often be paused when minimized to save resources.
93+
4. `bool swapchain_out_of_date`: Indicates when the swapchain (used for rendering) needs to be recreated, often after window resizing.
94+
95+
</details>
96+
97+
:::tip[Managing Window State]
98+
Always validate the window's state before performing rendering operations to avoid undefined behavior.
99+
Use the swapchain_out_of_date flag to trigger re-creation of Vulkan/Daxa rendering resources during resize events.
100+
:::
101+
102+
## 4. Constructor and Destructor
103+
104+
In this step, you'll define the **constructor** to initialize the window, configure callbacks for resizing, and handle cleanup in the **destructor**.
105+
106+
### Constructor
107+
108+
The constructor initializes the GLFW window, sets its properties, and registers callbacks for resizing events.
57109

58-
// Tell GLFW to not include any other API
59-
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
110+
```cpp
111+
// window.hpp
112+
explicit AppWindow(char const* window_name, u32 sx = 800, u32 sy = 600)
113+
: width{sx}, height{sy} {
114+
glfwInit(); // Initialize GLFW
60115

61-
// Tell GLFW to make the window resizable
62-
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
116+
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // No graphics API
117+
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Make the window resizable
63118

64-
// Create the window
65-
glfw_window_ptr = glfwCreateWindow(static_cast<i32>(width), static_cast<i32>(height), window_name, nullptr, nullptr);
119+
// Create the GLFW window
120+
glfw_window_ptr = glfwCreateWindow(
121+
static_cast<i32>(width), static_cast<i32>(height),
122+
window_name, nullptr, nullptr
123+
);
66124

67-
// Set the user pointer to this window
125+
// Associate the AppWindow object with the GLFW window
68126
glfwSetWindowUserPointer(glfw_window_ptr, this);
69127

70-
// When the window is resized, update the width and height and mark the swapchain as out of date
71-
glfwSetWindowSizeCallback(glfw_window_ptr, [](GLFWwindow *window, int size_x, int size_y) {
128+
// Set a callback to handle resizing events
129+
glfwSetWindowSizeCallback(glfw_window_ptr, [](GLFWwindow* window, int size_x, int size_y) {
72130
auto* win = static_cast<AppWindow*>(glfwGetWindowUserPointer(window));
73131
win->width = static_cast<u32>(size_x);
74132
win->height = static_cast<u32>(size_y);
75133
win->swapchain_out_of_date = true;
76134
});
77135
}
136+
```
137+
138+
<details>
139+
<summary>Key Points</summary>
140+
141+
1. `glfwInit()`: Initializes the GLFW library. Always call this before using GLFW functions.
142+
2. `glfwWindowHint()`: Configures the window. Setting `GLFW_CLIENT_API` to `GLFW_NO_API` means the window won't automatically use a graphics API like OpenGL.
143+
3. `glfwSetWindowUserPointer()`: Associates a user-defined pointer (our `AppWindow` instance) with the GLFW window, allowing us to reference the `AppWindow` object in callbacks.
144+
4. **Resize Callback**: Updates window dimensions and marks the swapchain as out-of-date whenever the window size changes.
145+
146+
</details>
147+
148+
:::tip[Resize Callback Use Case]
149+
This callback is crucial for Vulkan-based rendering. Resizing a window invalidates the swapchain, and marking it as out-of-date helps ensure rendering resources are re-created correctly.
150+
:::
151+
152+
### Destructor
153+
154+
The destructor ensures proper cleanup of GLFW resources to avoid memory leaks or dangling pointers.
78155
156+
```cpp
157+
// window.hpp
79158
~AppWindow() {
80-
glfwDestroyWindow(glfw_window_ptr);
81-
glfwTerminate();
159+
glfwDestroyWindow(glfw_window_ptr); // Destroy the GLFW window
160+
glfwTerminate(); // Terminate GLFW
82161
}
83162
```
84163

85-
Next, we need to create functions to obtain the native handle of the window and the platform identifier.
164+
<details>
165+
<summary>Why This is Important</summary>
166+
167+
- `glfwDestroyWindow()`: Releases memory and handles associated with the window.
168+
- `glfwTerminate()`: Cleans up GLFW internals. Failing to call this can lead to resource leaks.
169+
170+
</details>
171+
172+
## 5. Native Handles and Platform Identification
173+
174+
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.
175+
176+
### Native Handle Retrieval
177+
178+
This function returns a native window handle compatible with Daxa's `NativeWindowHandle` type.
86179

87180
```cpp
88-
auto get_native_handle() const -> daxa::NativeWindowHandle
89-
{
181+
// window.hpp
182+
auto get_native_handle() const -> daxa::NativeWindowHandle {
90183
#if defined(_WIN32)
91184
return glfwGetWin32Window(glfw_window_ptr);
92185
#elif defined(__linux__)
93-
switch (get_native_platform())
94-
{
186+
switch (get_native_platform()) {
95187
case daxa::NativeWindowPlatform::WAYLAND_API:
96188
return reinterpret_cast<daxa::NativeWindowHandle>(glfwGetWaylandWindow(glfw_window_ptr));
97189
case daxa::NativeWindowPlatform::XLIB_API:
@@ -100,11 +192,27 @@ auto get_native_handle() const -> daxa::NativeWindowHandle
100192
}
101193
#endif
102194
}
195+
```
196+
197+
<details>
198+
<summary>Key Notes</summary>
103199

104-
static auto get_native_platform() -> daxa::NativeWindowPlatform
105-
{
106-
switch(glfwGetPlatform())
107-
{
200+
1. **Platform-Specific Retrieval**:
201+
- On Windows, `glfwGetWin32Window()` retrieves an `HWND` handle.
202+
- On Linux, either `glfwGetX11Window()` or `glfwGetWaylandWindow()` is used, depending on the platform.
203+
204+
2. `daxa::NativeWindowHandle`: A Daxa-specific abstraction for handling different windowing systems. By returning this type, the function ensures compatibility with Daxa's rendering APIs.
205+
206+
</details>
207+
208+
### Platform Identification
209+
210+
This static function identifies the native platform and maps it to Daxa's NativeWindowPlatform enumeration.
211+
212+
```cpp
213+
// window.hpp
214+
static auto get_native_platform() -> daxa::NativeWindowPlatform {
215+
switch (glfwGetPlatform()) {
108216
case GLFW_PLATFORM_WIN32: return daxa::NativeWindowPlatform::WIN32_API;
109217
case GLFW_PLATFORM_X11: return daxa::NativeWindowPlatform::XLIB_API;
110218
case GLFW_PLATFORM_WAYLAND: return daxa::NativeWindowPlatform::WAYLAND_API;
@@ -113,55 +221,102 @@ static auto get_native_platform() -> daxa::NativeWindowPlatform
113221
}
114222
```
115223

116-
While we are at it, we can also create some utility functions.
224+
<details>
225+
<summary>Key Notes</summary>
226+
227+
1. `glfwGetPlatform()`: Returns the current platform (e.g., Win32, X11, Wayland).
228+
2. ***Mapping to `daxa::NativeWindowPlatform`**: Provides an abstraction layer to decouple GLFW-specific platform identifiers from Daxa's API.
229+
230+
</details>
231+
232+
## 6. Utility Functions
233+
234+
To simplify window management, implement utility methods for common tasks like mouse control, checking if the window should close, and handling updates.
117235

118236
```cpp
119-
inline void set_mouse_capture(bool should_capture) const
120-
{
237+
// window.hpp
238+
inline void set_mouse_capture(bool should_capture) const {
121239
glfwSetCursorPos(glfw_window_ptr, static_cast<f64>(width / 2.), static_cast<f64>(height / 2.));
122240
glfwSetInputMode(glfw_window_ptr, GLFW_CURSOR, should_capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
123241
glfwSetInputMode(glfw_window_ptr, GLFW_RAW_MOUSE_MOTION, should_capture);
124242
}
125243

126-
inline bool should_close() const
127-
{
244+
inline bool should_close() const {
128245
return glfwWindowShouldClose(glfw_window_ptr);
129246
}
130247

131-
inline void update() const
132-
{
248+
inline void update() const {
133249
glfwPollEvents();
134250
glfwSwapBuffers(glfw_window_ptr);
135251
}
136252

137-
inline GLFWwindow* get_glfw_window() const{
253+
inline GLFWwindow* get_glfw_window() const {
138254
return glfw_window_ptr;
139255
}
140-
141-
inline bool should_close() {
142-
return glfwWindowShouldClose(glfw_window_ptr);
143-
}
144256
```
145257
146-
## Opening our window
258+
<details>
259+
<summary>Function Breakdown</summary>
260+
261+
1. `set_mouse_capture(bool should_capture)`:
262+
- Centers the mouse cursor within the window and adjusts its behavior.
263+
- `GLFW_CURSOR_DISABLED`: Locks the cursor to the window, often used in first-person or immersive applications.
264+
- `GLFW_CURSOR_NORMAL`: Frees the cursor for standard interaction.
265+
- `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.
266+
2. `should_close()`:
267+
- Returns whether the window should close, typically triggered by the user clicking the close button or pressing Alt+F4.
268+
:::info[Why Check for Closure?]
269+
This is a standard condition for exiting the main application loop and ensures proper cleanup before termination.
270+
:::
271+
3. `update()`:
272+
- Handles event polling and buffer swapping.
273+
- `glfwPollEvents()`: Processes all pending input events (e.g., mouse movement, keyboard presses).
274+
- `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.
275+
4. `get_glfw_window()`:
276+
- Provides direct access to the raw GLFWwindow* pointer for advanced interactions or integrations.
277+
278+
</details>
279+
280+
## 7. Opening the Window
147281
148-
We can now go ahead and open our window for the first time. We therefore need to extend our main.cpp file.
282+
Integrate the `AppWindow` class into your application by creating and managing the window in `main.cpp`.
283+
284+
### Example Usage in main.cpp
149285
150286
```cpp
287+
// main.cpp
151288
#include "window.hpp"
152289
153-
int main(int argc, char const *argv[])
154-
{
290+
int main(int argc, char const* argv[]) {
155291
// Create a window
156292
auto window = AppWindow("Learn Daxa", 860, 640);
157293
158-
// Daxa code goes here...
294+
// Daxa rendering initialization code goes here...
159295
160-
while (!window.should_close())
161-
{
296+
// Main loop
297+
while (!window.should_close()) {
162298
window.update();
163299
}
164300
165301
return 0;
166302
}
167303
```
304+
305+
<details>
306+
<summary>Step-by-Step Explanation</summary>
307+
308+
1. Creating the Window:
309+
- `AppWindow("Learn Daxa", 860, 640)` initializes a window with the title `"Learn Daxa"` and dimensions 860x640.
310+
311+
2. Main Application Loop:
312+
- The `while (!window.should_close())` loop keeps the application running until the window is closed.
313+
- `window.update()` ensures the application processes events and renders frames.
314+
315+
3. Integration with Daxa:
316+
- Add Daxa-specific rendering code (e.g., setting up pipelines, rendering frames) in place of the `// Daxa rendering initialization code goes here...` comment.
317+
318+
</details>
319+
320+
## Summary
321+
322+
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.

0 commit comments

Comments
 (0)