1 | /* gskglcommandqueueprivate.h |
2 | * |
3 | * Copyright 2020 Christian Hergert <chergert@redhat.com> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * SPDX-License-Identifier: LGPL-2.1-or-later |
19 | */ |
20 | |
21 | #ifndef __GSK_GL_COMMAND_QUEUE_PRIVATE_H__ |
22 | #define __GSK_GL_COMMAND_QUEUE_PRIVATE_H__ |
23 | |
24 | #include <gsk/gskprofilerprivate.h> |
25 | |
26 | #include "gskgltypesprivate.h" |
27 | #include "gskglbufferprivate.h" |
28 | #include "gskglattachmentstateprivate.h" |
29 | #include "gskgluniformstateprivate.h" |
30 | |
31 | #include "inlinearray.h" |
32 | |
33 | #include "gskglprofilerprivate.h" |
34 | |
35 | G_BEGIN_DECLS |
36 | |
37 | #define GSK_TYPE_GL_COMMAND_QUEUE (gsk_gl_command_queue_get_type()) |
38 | |
39 | G_DECLARE_FINAL_TYPE (GskGLCommandQueue, gsk_gl_command_queue, GSK, GL_COMMAND_QUEUE, GObject) |
40 | |
41 | typedef enum _GskGLCommandKind |
42 | { |
43 | /* The batch will perform a glClear() */ |
44 | GSK_GL_COMMAND_KIND_CLEAR, |
45 | |
46 | /* The batch will perform a glDrawArrays() */ |
47 | GSK_GL_COMMAND_KIND_DRAW, |
48 | } GskGLCommandKind; |
49 | |
50 | typedef struct _GskGLCommandBind |
51 | { |
52 | /* @texture is the value passed to glActiveTexture(), the "slot" the |
53 | * texture will be placed into. We always use GL_TEXTURE_2D so we don't |
54 | * waste any bits here to indicate that. |
55 | */ |
56 | guint texture : 5; |
57 | |
58 | /* The identifier for the texture created with glGenTextures(). */ |
59 | guint id : 27; |
60 | } GskGLCommandBind; |
61 | |
62 | G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4); |
63 | |
64 | typedef struct _GskGLCommandBatchAny |
65 | { |
66 | /* A GskGLCommandKind indicating what the batch will do */ |
67 | guint kind : 8; |
68 | |
69 | /* The program's identifier to use for determining if we can merge two |
70 | * batches together into a single set of draw operations. We put this |
71 | * here instead of the GskGLCommandDraw so that we can use the extra |
72 | * bits here without making the structure larger. |
73 | */ |
74 | guint program : 24; |
75 | |
76 | /* The index of the next batch following this one. This is used |
77 | * as a sort of integer-based linked list to simplify out-of-order |
78 | * batching without moving memory around. -1 indicates last batch. |
79 | */ |
80 | gint16 next_batch_index; |
81 | |
82 | /* Same but for reverse direction as we sort in reverse to get the |
83 | * batches ordered by framebuffer. |
84 | */ |
85 | gint16 prev_batch_index; |
86 | |
87 | /* The viewport size of the batch. We check this as we process |
88 | * batches to determine if we need to resize the viewport. |
89 | */ |
90 | struct { |
91 | guint16 width; |
92 | guint16 height; |
93 | } viewport; |
94 | } GskGLCommandBatchAny; |
95 | |
96 | G_STATIC_ASSERT (sizeof (GskGLCommandBatchAny) == 12); |
97 | |
98 | typedef struct _GskGLCommandDraw |
99 | { |
100 | GskGLCommandBatchAny head; |
101 | |
102 | /* There doesn't seem to be a limit on the framebuffer identifier that |
103 | * can be returned, so we have to use a whole unsigned for the framebuffer |
104 | * we are drawing to. When processing batches, we check to see if this |
105 | * changes and adjust the render target accordingly. Some sorting is |
106 | * performed to reduce the amount we change framebuffers. |
107 | */ |
108 | guint framebuffer; |
109 | |
110 | /* The number of uniforms to change. This must be less than or equal to |
111 | * GL_MAX_UNIFORM_LOCATIONS but only guaranteed up to 1024 by any OpenGL |
112 | * implementation to be conformant. |
113 | */ |
114 | guint uniform_count : 11; |
115 | |
116 | /* The number of textures to bind, which is only guaranteed up to 16 |
117 | * by the OpenGL specification to be conformant. |
118 | */ |
119 | guint bind_count : 5; |
120 | |
121 | /* GL_MAX_ELEMENTS_VERTICES specifies 33000 for this which requires 16-bit |
122 | * to address all possible counts <= GL_MAX_ELEMENTS_VERTICES. |
123 | */ |
124 | guint vbo_count : 16; |
125 | |
126 | /* The offset within the VBO containing @vbo_count vertices to send with |
127 | * glDrawArrays(). |
128 | */ |
129 | guint vbo_offset; |
130 | |
131 | /* The offset within the array of uniform changes to be made containing |
132 | * @uniform_count `GskGLCommandUniform` elements to apply. |
133 | */ |
134 | guint uniform_offset; |
135 | |
136 | /* The offset within the array of bind changes to be made containing |
137 | * @bind_count `GskGLCommandBind` elements to apply. |
138 | */ |
139 | guint bind_offset; |
140 | } GskGLCommandDraw; |
141 | |
142 | G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 32); |
143 | |
144 | typedef struct _GskGLCommandClear |
145 | { |
146 | GskGLCommandBatchAny any; |
147 | guint bits; |
148 | guint framebuffer; |
149 | } GskGLCommandClear; |
150 | |
151 | G_STATIC_ASSERT (sizeof (GskGLCommandClear) == 20); |
152 | |
153 | typedef struct _GskGLCommandUniform |
154 | { |
155 | GskGLUniformInfo info; |
156 | guint location; |
157 | } GskGLCommandUniform; |
158 | |
159 | G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8); |
160 | |
161 | typedef union _GskGLCommandBatch |
162 | { |
163 | GskGLCommandBatchAny any; |
164 | GskGLCommandDraw draw; |
165 | GskGLCommandClear clear; |
166 | } GskGLCommandBatch; |
167 | |
168 | G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32); |
169 | |
170 | DEFINE_INLINE_ARRAY (GskGLCommandBatches, gsk_gl_command_batches, GskGLCommandBatch) |
171 | DEFINE_INLINE_ARRAY (GskGLCommandBinds, gsk_gl_command_binds, GskGLCommandBind) |
172 | DEFINE_INLINE_ARRAY (GskGLCommandUniforms, gsk_gl_command_uniforms, GskGLCommandUniform) |
173 | |
174 | struct _GskGLCommandQueue |
175 | { |
176 | GObject parent_instance; |
177 | |
178 | /* The GdkGLContext we make current before executing GL commands. */ |
179 | GdkGLContext *context; |
180 | |
181 | /* Array of GskGLCommandBatch which is a fixed size structure that will |
182 | * point into offsets of other arrays so that all similar data is stored |
183 | * together. The idea here is that we reduce the need for pointers so that |
184 | * using g_realloc()'d arrays is fine. |
185 | */ |
186 | GskGLCommandBatches batches; |
187 | |
188 | /* Contains array of vertices and some wrapper code to help upload them |
189 | * to the GL driver. We can also tweak this to use double buffered arrays |
190 | * if we find that to be faster on some hardware and/or drivers. |
191 | */ |
192 | GskGLBuffer vertices; |
193 | |
194 | /* The GskGLAttachmentState contains information about our FBO and texture |
195 | * attachments as we process incoming operations. We snapshot them into |
196 | * various batches so that we can compare differences between merge |
197 | * candidates. |
198 | */ |
199 | GskGLAttachmentState *attachments; |
200 | |
201 | /* The uniform state across all programs. We snapshot this into batches so |
202 | * that we can compare uniform state between batches to give us more |
203 | * chances at merging draw commands. |
204 | */ |
205 | GskGLUniformState *uniforms; |
206 | |
207 | /* Current program if we are in a draw so that we can send commands |
208 | * to the uniform state as needed. |
209 | */ |
210 | GskGLUniformProgram *program_info; |
211 | |
212 | /* The profiler instance to deliver timing/etc data */ |
213 | GskProfiler *profiler; |
214 | GskGLProfiler *gl_profiler; |
215 | |
216 | /* Array of GskGLCommandBind which denote what textures need to be attached |
217 | * to which slot. GskGLCommandDraw.bind_offset and bind_count reference this |
218 | * array to determine what to attach. |
219 | */ |
220 | GskGLCommandBinds batch_binds; |
221 | |
222 | /* Array of GskGLCommandUniform denoting which uniforms must be updated |
223 | * before the glDrawArrays() may be called. These are referenced from the |
224 | * GskGLCommandDraw.uniform_offset and uniform_count fields. |
225 | */ |
226 | GskGLCommandUniforms batch_uniforms; |
227 | |
228 | /* Discovered max texture size when loading the command queue so that we |
229 | * can either scale down or slice textures to fit within this size. Assumed |
230 | * to be both height and width. |
231 | */ |
232 | int max_texture_size; |
233 | |
234 | /* The index of the last batch in @batches, which may not be the element |
235 | * at the end of the array, as batches can be reordered. This is used to |
236 | * update the "next" index when adding a new batch. |
237 | */ |
238 | gint16 tail_batch_index; |
239 | gint16 head_batch_index; |
240 | |
241 | /* Max framebuffer we used, so we can sort items faster */ |
242 | guint fbo_max; |
243 | |
244 | /* Various GSK and GDK metric counter ids */ |
245 | struct { |
246 | GQuark n_frames; |
247 | GQuark cpu_time; |
248 | GQuark gpu_time; |
249 | guint n_binds; |
250 | guint n_fbos; |
251 | guint n_uniforms; |
252 | guint n_uploads; |
253 | guint n_programs; |
254 | guint queue_depth; |
255 | } metrics; |
256 | |
257 | /* Counter for uploads on the frame */ |
258 | guint n_uploads; |
259 | |
260 | /* If we're inside a begin/end_frame pair */ |
261 | guint in_frame : 1; |
262 | |
263 | /* If we're inside of a begin_draw()/end_draw() pair. */ |
264 | guint in_draw : 1; |
265 | |
266 | /* If we've warned about truncating batches */ |
267 | guint have_truncated : 1; |
268 | }; |
269 | |
270 | GskGLCommandQueue *gsk_gl_command_queue_new (GdkGLContext *context, |
271 | GskGLUniformState *uniforms); |
272 | void gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self, |
273 | GskProfiler *profiler); |
274 | GdkGLContext *gsk_gl_command_queue_get_context (GskGLCommandQueue *self); |
275 | void gsk_gl_command_queue_make_current (GskGLCommandQueue *self); |
276 | void gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self); |
277 | void gsk_gl_command_queue_end_frame (GskGLCommandQueue *self); |
278 | void gsk_gl_command_queue_execute (GskGLCommandQueue *self, |
279 | guint surface_height, |
280 | guint scale_factor, |
281 | const cairo_region_t *scissor, |
282 | guint default_framebuffer); |
283 | int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self, |
284 | GdkTexture *texture, |
285 | int min_filter, |
286 | int mag_filter); |
287 | int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self, |
288 | int width, |
289 | int height, |
290 | int format, |
291 | int min_filter, |
292 | int mag_filter); |
293 | guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self); |
294 | gboolean gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self, |
295 | int width, |
296 | int height, |
297 | int format, |
298 | int min_filter, |
299 | int mag_filter, |
300 | guint *out_fbo_id, |
301 | guint *out_texture_id); |
302 | void gsk_gl_command_queue_delete_program (GskGLCommandQueue *self, |
303 | guint program_id); |
304 | void gsk_gl_command_queue_clear (GskGLCommandQueue *self, |
305 | guint clear_bits, |
306 | const graphene_rect_t *viewport); |
307 | void gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self, |
308 | GskGLUniformProgram *program_info, |
309 | guint width, |
310 | guint height); |
311 | void gsk_gl_command_queue_end_draw (GskGLCommandQueue *self); |
312 | void gsk_gl_command_queue_split_draw (GskGLCommandQueue *self); |
313 | |
314 | static inline GskGLCommandBatch * |
315 | gsk_gl_command_queue_get_batch (GskGLCommandQueue *self) |
316 | { |
317 | return gsk_gl_command_batches_tail (ar: &self->batches); |
318 | } |
319 | |
320 | static inline GskGLDrawVertex * |
321 | gsk_gl_command_queue_add_vertices (GskGLCommandQueue *self) |
322 | { |
323 | gsk_gl_command_queue_get_batch (self)->draw.vbo_count += GSK_GL_N_VERTICES; |
324 | return gsk_gl_buffer_advance (buffer: &self->vertices, GSK_GL_N_VERTICES); |
325 | } |
326 | |
327 | static inline GskGLDrawVertex * |
328 | gsk_gl_command_queue_add_n_vertices (GskGLCommandQueue *self, |
329 | guint count) |
330 | { |
331 | /* This is a batch form of gsk_gl_command_queue_add_vertices(). Note that |
332 | * it does *not* add the count to .draw.vbo_count as the caller is responsible |
333 | * for that. |
334 | */ |
335 | return gsk_gl_buffer_advance (buffer: &self->vertices, GSK_GL_N_VERTICES * count); |
336 | } |
337 | |
338 | static inline void |
339 | gsk_gl_command_queue_retract_n_vertices (GskGLCommandQueue *self, |
340 | guint count) |
341 | { |
342 | /* Like gsk_gl_command_queue_add_n_vertices(), this does not tweak |
343 | * the draw vbo_count. |
344 | */ |
345 | gsk_gl_buffer_retract (buffer: &self->vertices, GSK_GL_N_VERTICES * count); |
346 | } |
347 | |
348 | static inline guint |
349 | gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self, |
350 | guint framebuffer) |
351 | { |
352 | guint ret = self->attachments->fbo.id; |
353 | gsk_gl_attachment_state_bind_framebuffer (self: self->attachments, id: framebuffer); |
354 | return ret; |
355 | } |
356 | |
357 | G_END_DECLS |
358 | |
359 | #endif /* __GSK_GL_COMMAND_QUEUE_PRIVATE_H__ */ |
360 | |