You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
6
6
---
7
7
8
-
## Description
8
+
## Introduction
9
9
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.
11
11
12
-
## Creating a header file
12
+
---
13
+
14
+
## 1. Creating the Header File
13
15
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.
15
17
16
18
```cpp
19
+
// window.hpp
17
20
#pragma once
18
21
19
-
structAppWindow{
22
+
structAppWindow {
23
+
// Window-related properties and methods will go here
20
24
};
21
25
```
22
26
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>
24
29
25
-
```cpp
26
-
#include<daxa/daxa.hpp>
27
-
// For things like `u32`. Not necessary of course.
28
-
usingnamespacedaxa::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.
29
32
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`
// Window-related properties and methods will go here
61
+
};
40
62
```
41
63
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>
43
66
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
+
};
49
85
```
50
86
51
-
We can now create a constructor and a destructor for the window.
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.
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.
78
155
156
+
```cpp
157
+
// window.hpp
79
158
~AppWindow() {
80
-
glfwDestroyWindow(glfw_window_ptr);
81
-
glfwTerminate();
159
+
glfwDestroyWindow(glfw_window_ptr); // Destroy the GLFW window
160
+
glfwTerminate(); // Terminate GLFW
82
161
}
83
162
```
84
163
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.
86
179
87
180
```cpp
88
-
auto get_native_handle() const -> daxa::NativeWindowHandle
@@ -100,11 +192,27 @@ auto get_native_handle() const -> daxa::NativeWindowHandle
100
192
}
101
193
#endif
102
194
}
195
+
```
196
+
197
+
<details>
198
+
<summary>Key Notes</summary>
103
199
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.
case GLFW_PLATFORM_WIN32: return daxa::NativeWindowPlatform::WIN32_API;
109
217
case GLFW_PLATFORM_X11: return daxa::NativeWindowPlatform::XLIB_API;
110
218
case GLFW_PLATFORM_WAYLAND: return daxa::NativeWindowPlatform::WAYLAND_API;
@@ -113,55 +221,102 @@ static auto get_native_platform() -> daxa::NativeWindowPlatform
113
221
}
114
222
```
115
223
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.
- 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.
- `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
147
281
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`.
-`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