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
35G_BEGIN_DECLS
36
37#define GSK_TYPE_GL_COMMAND_QUEUE (gsk_gl_command_queue_get_type())
38
39G_DECLARE_FINAL_TYPE (GskGLCommandQueue, gsk_gl_command_queue, GSK, GL_COMMAND_QUEUE, GObject)
40
41typedef 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
50typedef 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
62G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4);
63
64typedef 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
96G_STATIC_ASSERT (sizeof (GskGLCommandBatchAny) == 12);
97
98typedef 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
142G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 32);
143
144typedef struct _GskGLCommandClear
145{
146 GskGLCommandBatchAny any;
147 guint bits;
148 guint framebuffer;
149} GskGLCommandClear;
150
151G_STATIC_ASSERT (sizeof (GskGLCommandClear) == 20);
152
153typedef struct _GskGLCommandUniform
154{
155 GskGLUniformInfo info;
156 guint location;
157} GskGLCommandUniform;
158
159G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
160
161typedef union _GskGLCommandBatch
162{
163 GskGLCommandBatchAny any;
164 GskGLCommandDraw draw;
165 GskGLCommandClear clear;
166} GskGLCommandBatch;
167
168G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32);
169
170DEFINE_INLINE_ARRAY (GskGLCommandBatches, gsk_gl_command_batches, GskGLCommandBatch)
171DEFINE_INLINE_ARRAY (GskGLCommandBinds, gsk_gl_command_binds, GskGLCommandBind)
172DEFINE_INLINE_ARRAY (GskGLCommandUniforms, gsk_gl_command_uniforms, GskGLCommandUniform)
173
174struct _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
270GskGLCommandQueue *gsk_gl_command_queue_new (GdkGLContext *context,
271 GskGLUniformState *uniforms);
272void gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self,
273 GskProfiler *profiler);
274GdkGLContext *gsk_gl_command_queue_get_context (GskGLCommandQueue *self);
275void gsk_gl_command_queue_make_current (GskGLCommandQueue *self);
276void gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self);
277void gsk_gl_command_queue_end_frame (GskGLCommandQueue *self);
278void 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);
283int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
284 GdkTexture *texture,
285 int min_filter,
286 int mag_filter);
287int 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);
293guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self);
294gboolean 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);
302void gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
303 guint program_id);
304void gsk_gl_command_queue_clear (GskGLCommandQueue *self,
305 guint clear_bits,
306 const graphene_rect_t *viewport);
307void gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
308 GskGLUniformProgram *program_info,
309 guint width,
310 guint height);
311void gsk_gl_command_queue_end_draw (GskGLCommandQueue *self);
312void gsk_gl_command_queue_split_draw (GskGLCommandQueue *self);
313
314static inline GskGLCommandBatch *
315gsk_gl_command_queue_get_batch (GskGLCommandQueue *self)
316{
317 return gsk_gl_command_batches_tail (ar: &self->batches);
318}
319
320static inline GskGLDrawVertex *
321gsk_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
327static inline GskGLDrawVertex *
328gsk_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
338static inline void
339gsk_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
348static inline guint
349gsk_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
357G_END_DECLS
358
359#endif /* __GSK_GL_COMMAND_QUEUE_PRIVATE_H__ */
360

source code of gtk/gsk/gl/gskglcommandqueueprivate.h