1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
/**
* @file ral.h
* @author your name (you@domain.com)
* @brief Render Abstraction Layer
* @details API that a graphics backend *must* implement
* @version 0.1
* @date 2024-03-31
*
* @copyright Copyright (c) 2024
*
*/
#pragma once
#include "buf.h"
#include "defines.h"
#include "mem.h"
#include "ral_types.h"
#include "str.h"
// Unrelated forward declares
struct GLFWwindow;
// Forward declare structs - these must be defined in the backend implementation
typedef struct gpu_swapchain gpu_swapchain;
typedef struct gpu_device gpu_device;
typedef struct gpu_pipeline_layout gpu_pipeline_layout;
typedef struct gpu_pipeline gpu_pipeline;
typedef struct gpu_renderpass gpu_renderpass;
typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording
typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission
typedef struct gpu_buffer gpu_buffer;
typedef struct gpu_texture gpu_texture;
#define MAX_SHADER_DATA_LAYOUTS 5
#define MAX_BUFFERS 256
#define MAX_TEXTURES 256
#define MAX_PIPELINES 128
#define MAX_RENDERPASSES 128
TYPED_POOL(gpu_buffer, buffer);
TYPED_POOL(gpu_texture, texture);
TYPED_POOL(gpu_pipeline_layout, pipeline_layout);
TYPED_POOL(gpu_pipeline, pipeline);
TYPED_POOL(gpu_renderpass, renderpass);
// --- Handy macros
#define BUFFER_GET(h) (buffer_pool_get(&context.resource_pools->buffers, h))
#define TEXTURE_GET(h) (texture_pool_get(&context.resource_pools->textures, h))
// --- Pools
typedef struct gpu_backend_pools {
pipeline_pool pipelines;
pipeline_layout_pool pipeline_layouts;
renderpass_pool renderpasses;
} gpu_backend_pools;
void backend_pools_init(arena* a, gpu_backend_pools* backend_pools);
struct resource_pools {
buffer_pool buffers;
texture_pool textures;
};
void resource_pools_init(arena* a, struct resource_pools* res_pools);
// --- Pipeline description
typedef enum pipeline_kind {
PIPELINE_GRAPHICS,
PIPELINE_COMPUTE,
} pipeline_kind;
typedef struct shader_desc {
const char* debug_name;
str8 filepath; // Where it came from
str8 code; // Either GLSL or SPIRV bytecode
bool is_spirv;
bool is_combined_vert_frag; // Contains both vertex and fragment stages
} shader_desc;
shader_desc shader_quick_load(const char* filepath);
struct graphics_pipeline_desc {
const char* debug_name;
vertex_description vertex_desc;
shader_desc vs; /** @brief Vertex shader stage */
shader_desc fs; /** @brief Fragment shader stage */
// Roughly equivalent to a descriptor set layout each. each layout can have multiple bindings
// examples:
// - uniform buffer reprensenting view projection matrix
// - texture for shadow map
shader_data data_layouts[MAX_SHADER_DATA_LAYOUTS];
u32 data_layouts_count;
// gpu_pipeline_layout* layout;
gpu_renderpass* renderpass;
bool wireframe;
bool depth_test;
};
typedef struct gpu_renderpass_desc {
bool default_framebuffer;
bool has_color_target;
texture_handle color_target; // for now only support one
bool has_depth_stencil;
texture_handle depth_stencil;
} gpu_renderpass_desc;
// --- Lifecycle functions
bool gpu_backend_init(const char* window_name, struct GLFWwindow* window);
void gpu_backend_shutdown();
void resource_pools_init(arena* a, struct resource_pools* res_pools);
bool gpu_device_create(gpu_device* out_device);
void gpu_device_destroy();
// --- Render Pipeline
gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description);
void gpu_pipeline_destroy(gpu_pipeline* pipeline);
// --- Renderpass
gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description);
void gpu_renderpass_destroy(gpu_renderpass* pass);
// --- Swapchain
bool gpu_swapchain_create(gpu_swapchain* out_swapchain);
void gpu_swapchain_destroy(gpu_swapchain* swapchain);
// --- Command buffer
gpu_cmd_encoder gpu_cmd_encoder_create();
void gpu_cmd_encoder_destroy(gpu_cmd_encoder* encoder);
void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder);
void gpu_cmd_encoder_begin_render(gpu_cmd_encoder* encoder, gpu_renderpass* renderpass);
void gpu_cmd_encoder_end_render(gpu_cmd_encoder* encoder);
void gpu_cmd_encoder_begin_compute();
gpu_cmd_encoder* gpu_get_default_cmd_encoder();
/** @brief Finish recording and return a command buffer that can be submitted to a queue */
gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder);
void gpu_queue_submit(gpu_cmd_buffer* buffer);
// --- Data copy commands
/** @brief Copy data from one buffer to another */
void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset,
buffer_handle dst, u64 dst_offset, u64 copy_size);
/** @brief Upload CPU-side data as array of bytes to a GPU buffer */
void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size);
/** @brief Copy data from buffer to buffer using a one time submit command buffer and a wait */
void copy_buffer_to_buffer_oneshot(buffer_handle src, u64 src_offset, buffer_handle dst,
u64 dst_offset, u64 copy_size);
/** @brief Copy data from buffer to an image using a one time submit command buffer */
void copy_buffer_to_image_oneshot(buffer_handle src, texture_handle dst);
// --- Render commands
void encode_bind_pipeline(gpu_cmd_encoder* encoder, pipeline_kind kind, gpu_pipeline* pipeline);
void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* data);
void encode_set_default_settings(gpu_cmd_encoder* encoder);
void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
void encode_set_bind_group(); // TODO
void encode_draw(gpu_cmd_encoder* encoder, u64 count);
void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count);
void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
// --- Buffers
buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_flags flags,
const void* data);
void gpu_buffer_destroy(buffer_handle buffer);
void gpu_buffer_upload(const void* data);
// Textures
/** @brief Create a new GPU texture resource.
* @param create_view creates a texture view (with same dimensions) at the same time
* @param data if not NULL then the data stored at the pointer will be uploaded to the GPU texture
* @note automatically creates a sampler for you */
texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data);
void gpu_texture_destroy(texture_handle);
void gpu_texture_upload(texture_handle texture, const void* data);
// --- Vertex formats
bytebuffer vertices_as_bytebuffer(arena* a, vertex_format format, vertex_darray* vertices);
void vertex_desc_add(vertex_description* builder, const char* name, vertex_attrib_type type);
// --- TEMP
bool gpu_backend_begin_frame();
void gpu_backend_end_frame();
void gpu_temp_draw(size_t n_verts);
// TODO: --- Compute
// --- Helpers
vertex_description static_3d_vertex_description();
size_t vertex_attrib_size(vertex_attrib_type attr);
|