1/* gskglcommandqueue.c
2 *
3 * Copyright 2017 Timm Bäder <mail@baedert.org>
4 * Copyright 2018 Matthias Clasen <mclasen@redhat.com>
5 * Copyright 2018 Alexander Larsson <alexl@redhat.com>
6 * Copyright 2020 Christian Hergert <chergert@redhat.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * SPDX-License-Identifier: LGPL-2.1-or-later
22 */
23
24#include "config.h"
25
26#include <string.h>
27
28#include <gdk/gdkglcontextprivate.h>
29#include <gdk/gdkmemoryformatprivate.h>
30#include <gdk/gdkmemorytextureprivate.h>
31#include <gdk/gdkprofilerprivate.h>
32#include <gsk/gskdebugprivate.h>
33#include <gsk/gskroundedrectprivate.h>
34
35#include "gskglattachmentstateprivate.h"
36#include "gskglbufferprivate.h"
37#include "gskglcommandqueueprivate.h"
38#include "gskgluniformstateprivate.h"
39
40#include "inlinearray.h"
41
42G_DEFINE_TYPE (GskGLCommandQueue, gsk_gl_command_queue, G_TYPE_OBJECT)
43
44G_GNUC_UNUSED static inline void
45print_uniform (GskGLUniformFormat format,
46 guint array_count,
47 gconstpointer valueptr)
48{
49 const union {
50 graphene_matrix_t matrix[0];
51 GskRoundedRect rounded_rect[0];
52 float fval[0];
53 int ival[0];
54 guint uval[0];
55 } *data = valueptr;
56
57 switch (format)
58 {
59 case GSK_GL_UNIFORM_FORMAT_1F:
60 g_printerr (format: "1f<%f>", data->fval[0]);
61 break;
62
63 case GSK_GL_UNIFORM_FORMAT_2F:
64 g_printerr (format: "2f<%f,%f>", data->fval[0], data->fval[1]);
65 break;
66
67 case GSK_GL_UNIFORM_FORMAT_3F:
68 g_printerr (format: "3f<%f,%f,%f>", data->fval[0], data->fval[1], data->fval[2]);
69 break;
70
71 case GSK_GL_UNIFORM_FORMAT_4F:
72 g_printerr (format: "4f<%f,%f,%f,%f>", data->fval[0], data->fval[1], data->fval[2], data->fval[3]);
73 break;
74
75 case GSK_GL_UNIFORM_FORMAT_1I:
76 case GSK_GL_UNIFORM_FORMAT_TEXTURE:
77 g_printerr (format: "1i<%d>", data->ival[0]);
78 break;
79
80 case GSK_GL_UNIFORM_FORMAT_1UI:
81 g_printerr (format: "1ui<%u>", data->uval[0]);
82 break;
83
84 case GSK_GL_UNIFORM_FORMAT_COLOR: {
85 char *str = gdk_rgba_to_string (rgba: valueptr);
86 g_printerr (format: "%s", str);
87 g_free (mem: str);
88 break;
89 }
90
91 case GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT: {
92 char *str = gsk_rounded_rect_to_string (self: valueptr);
93 g_printerr (format: "%s", str);
94 g_free (mem: str);
95 break;
96 }
97
98 case GSK_GL_UNIFORM_FORMAT_MATRIX: {
99 float mat[16];
100 graphene_matrix_to_float (m: &data->matrix[0], v: mat);
101 g_printerr (format: "matrix<");
102 for (guint i = 0; i < G_N_ELEMENTS (mat)-1; i++)
103 g_printerr (format: "%f,", mat[i]);
104 g_printerr (format: "%f>", mat[G_N_ELEMENTS (mat)-1]);
105 break;
106 }
107
108 case GSK_GL_UNIFORM_FORMAT_1FV:
109 case GSK_GL_UNIFORM_FORMAT_2FV:
110 case GSK_GL_UNIFORM_FORMAT_3FV:
111 case GSK_GL_UNIFORM_FORMAT_4FV:
112 /* non-V variants are -4 from V variants */
113 format -= 4;
114 g_printerr (format: "[");
115 for (guint i = 0; i < array_count; i++)
116 {
117 print_uniform (format, array_count: 0, valueptr);
118 if (i + 1 != array_count)
119 g_printerr (format: ",");
120 valueptr = ((guint8*)valueptr + gsk_gl_uniform_format_size (format));
121 }
122 g_printerr (format: "]");
123 break;
124
125 case GSK_GL_UNIFORM_FORMAT_2I:
126 g_printerr (format: "2i<%d,%d>", data->ival[0], data->ival[1]);
127 break;
128
129 case GSK_GL_UNIFORM_FORMAT_3I:
130 g_printerr (format: "3i<%d,%d,%d>", data->ival[0], data->ival[1], data->ival[2]);
131 break;
132
133 case GSK_GL_UNIFORM_FORMAT_4I:
134 g_printerr (format: "3i<%d,%d,%d,%d>", data->ival[0], data->ival[1], data->ival[2], data->ival[3]);
135 break;
136
137 case GSK_GL_UNIFORM_FORMAT_LAST:
138 default:
139 g_assert_not_reached ();
140 }
141}
142
143G_GNUC_UNUSED static inline void
144gsk_gl_command_queue_print_batch (GskGLCommandQueue *self,
145 const GskGLCommandBatch *batch)
146{
147 static const char *command_kinds[] = { "Clear", "Draw", };
148 guint framebuffer_id;
149
150 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
151 g_assert (batch != NULL);
152
153 if (batch->any.kind == GSK_GL_COMMAND_KIND_CLEAR)
154 framebuffer_id = batch->clear.framebuffer;
155 else if (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW)
156 framebuffer_id = batch->draw.framebuffer;
157 else
158 return;
159
160 g_printerr (format: "Batch {\n");
161 g_printerr (format: " Kind: %s\n", command_kinds[batch->any.kind]);
162 g_printerr (format: " Viewport: %dx%d\n", batch->any.viewport.width, batch->any.viewport.height);
163 g_printerr (format: " Framebuffer: %d\n", framebuffer_id);
164
165 if (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW)
166 {
167 g_printerr (format: " Program: %d\n", batch->any.program);
168 g_printerr (format: " Vertices: %d\n", batch->draw.vbo_count);
169
170 for (guint i = 0; i < batch->draw.bind_count; i++)
171 {
172 const GskGLCommandBind *bind = &self->batch_binds.items[batch->draw.bind_offset + i];
173 g_printerr (format: " Bind[%d]: %u\n", bind->texture, bind->id);
174 }
175
176 for (guint i = 0; i < batch->draw.uniform_count; i++)
177 {
178 const GskGLCommandUniform *uniform = &self->batch_uniforms.items[batch->draw.uniform_offset + i];
179 g_printerr (format: " Uniform[%02d]: ", uniform->location);
180 print_uniform (format: uniform->info.format,
181 array_count: uniform->info.array_count,
182 gsk_gl_uniform_state_get_uniform_data (self->uniforms, uniform->info.offset));
183 g_printerr (format: "\n");
184 }
185 }
186 else if (batch->any.kind == GSK_GL_COMMAND_KIND_CLEAR)
187 {
188 g_printerr (format: " Bits: 0x%x\n", batch->clear.bits);
189 }
190
191 g_printerr (format: "}\n");
192}
193
194G_GNUC_UNUSED static inline void
195gsk_gl_command_queue_capture_png (GskGLCommandQueue *self,
196 const char *filename,
197 guint width,
198 guint height,
199 gboolean flip_y)
200{
201 guint stride;
202 guint8 *data;
203 GBytes *bytes;
204 GdkTexture *texture;
205
206 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
207 g_assert (filename != NULL);
208
209 stride = cairo_format_stride_for_width (format: CAIRO_FORMAT_ARGB32, width);
210 data = g_malloc_n (n_blocks: height, n_block_bytes: stride);
211
212 glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, data);
213
214 if (flip_y)
215 {
216 guint8 *flipped = g_malloc_n (n_blocks: height, n_block_bytes: stride);
217
218 for (guint i = 0; i < height; i++)
219 memcpy (dest: flipped + (height * stride) - ((i + 1) * stride),
220 src: data + (stride * i),
221 n: stride);
222
223 g_free (mem: data);
224 data = flipped;
225 }
226
227 bytes = g_bytes_new_take (data, size: height * stride);
228 texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride);
229 g_bytes_unref (bytes);
230
231 gdk_texture_save_to_png (texture, filename);
232 g_object_unref (object: texture);
233}
234
235static inline gboolean
236will_ignore_batch (GskGLCommandQueue *self)
237{
238 if G_LIKELY (self->batches.len < G_MAXINT16)
239 return FALSE;
240
241 if (!self->have_truncated)
242 {
243 self->have_truncated = TRUE;
244 g_critical ("GL command queue too large, truncating further batches.");
245 }
246
247 return TRUE;
248}
249
250static inline guint
251snapshot_attachments (const GskGLAttachmentState *state,
252 GskGLCommandBinds *array)
253{
254 GskGLCommandBind *bind = gsk_gl_command_binds_append_n (ar: array, G_N_ELEMENTS (state->textures));
255 guint count = 0;
256
257 for (guint i = 0; i < G_N_ELEMENTS (state->textures); i++)
258 {
259 if (state->textures[i].id)
260 {
261 bind[count].id = state->textures[i].id;
262 bind[count].texture = state->textures[i].texture;
263 count++;
264 }
265 }
266
267 if (count != G_N_ELEMENTS (state->textures))
268 array->len -= G_N_ELEMENTS (state->textures) - count;
269
270 return count;
271}
272
273static inline guint
274snapshot_uniforms (GskGLUniformState *state,
275 GskGLUniformProgram *program,
276 GskGLCommandUniforms *array)
277{
278 GskGLCommandUniform *uniform = gsk_gl_command_uniforms_append_n (ar: array, n: program->n_mappings);
279 guint count = 0;
280
281 for (guint i = 0; i < program->n_mappings; i++)
282 {
283 const GskGLUniformMapping *mapping = &program->mappings[i];
284
285 if (!mapping->info.initial && mapping->location > -1)
286 {
287 uniform[count].location = mapping->location;
288 uniform[count].info = mapping->info;
289 count++;
290 }
291 }
292
293 if (count != program->n_mappings)
294 array->len -= program->n_mappings - count;
295
296 return count;
297}
298
299static inline gboolean
300snapshots_equal (GskGLCommandQueue *self,
301 GskGLCommandBatch *first,
302 GskGLCommandBatch *second)
303{
304 if (first->draw.bind_count != second->draw.bind_count ||
305 first->draw.uniform_count != second->draw.uniform_count)
306 return FALSE;
307
308 for (guint i = 0; i < first->draw.bind_count; i++)
309 {
310 const GskGLCommandBind *fb = &self->batch_binds.items[first->draw.bind_offset+i];
311 const GskGLCommandBind *sb = &self->batch_binds.items[second->draw.bind_offset+i];
312
313 if (fb->id != sb->id || fb->texture != sb->texture)
314 return FALSE;
315 }
316
317 for (guint i = 0; i < first->draw.uniform_count; i++)
318 {
319 const GskGLCommandUniform *fu = &self->batch_uniforms.items[first->draw.uniform_offset+i];
320 const GskGLCommandUniform *su = &self->batch_uniforms.items[second->draw.uniform_offset+i];
321 gconstpointer fdata;
322 gconstpointer sdata;
323 gsize len;
324
325 /* Short circuit if we'd end up with the same memory */
326 if (fu->info.offset == su->info.offset)
327 continue;
328
329 if (fu->info.format != su->info.format ||
330 fu->info.array_count != su->info.array_count)
331 return FALSE;
332
333 fdata = gsk_gl_uniform_state_get_uniform_data (self->uniforms, fu->info.offset);
334 sdata = gsk_gl_uniform_state_get_uniform_data (self->uniforms, su->info.offset);
335
336 switch (fu->info.format)
337 {
338 case GSK_GL_UNIFORM_FORMAT_1F:
339 case GSK_GL_UNIFORM_FORMAT_1FV:
340 case GSK_GL_UNIFORM_FORMAT_1I:
341 case GSK_GL_UNIFORM_FORMAT_TEXTURE:
342 case GSK_GL_UNIFORM_FORMAT_1UI:
343 len = 4;
344 break;
345
346 case GSK_GL_UNIFORM_FORMAT_2F:
347 case GSK_GL_UNIFORM_FORMAT_2FV:
348 case GSK_GL_UNIFORM_FORMAT_2I:
349 len = 8;
350 break;
351
352 case GSK_GL_UNIFORM_FORMAT_3F:
353 case GSK_GL_UNIFORM_FORMAT_3FV:
354 case GSK_GL_UNIFORM_FORMAT_3I:
355 len = 12;
356 break;
357
358 case GSK_GL_UNIFORM_FORMAT_4F:
359 case GSK_GL_UNIFORM_FORMAT_4FV:
360 case GSK_GL_UNIFORM_FORMAT_4I:
361 len = 16;
362 break;
363
364
365 case GSK_GL_UNIFORM_FORMAT_MATRIX:
366 len = sizeof (float) * 16;
367 break;
368
369 case GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT:
370 len = sizeof (float) * 12;
371 break;
372
373 case GSK_GL_UNIFORM_FORMAT_COLOR:
374 len = sizeof (float) * 4;
375 break;
376
377 default:
378 g_assert_not_reached ();
379 }
380
381 len *= fu->info.array_count;
382
383 if (memcmp (s1: fdata, s2: sdata, n: len) != 0)
384 return FALSE;
385 }
386
387 return TRUE;
388}
389
390static void
391gsk_gl_command_queue_dispose (GObject *object)
392{
393 GskGLCommandQueue *self = (GskGLCommandQueue *)object;
394
395 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
396
397 g_clear_object (&self->profiler);
398 g_clear_object (&self->gl_profiler);
399 g_clear_object (&self->context);
400 g_clear_pointer (&self->attachments, gsk_gl_attachment_state_unref);
401 g_clear_pointer (&self->uniforms, gsk_gl_uniform_state_unref);
402
403 gsk_gl_command_batches_clear (ar: &self->batches);
404 gsk_gl_command_binds_clear (ar: &self->batch_binds);
405 gsk_gl_command_uniforms_clear (ar: &self->batch_uniforms);
406
407 gsk_gl_buffer_destroy (buffer: &self->vertices);
408
409 G_OBJECT_CLASS (gsk_gl_command_queue_parent_class)->dispose (object);
410}
411
412static void
413gsk_gl_command_queue_class_init (GskGLCommandQueueClass *klass)
414{
415 GObjectClass *object_class = G_OBJECT_CLASS (klass);
416
417 object_class->dispose = gsk_gl_command_queue_dispose;
418}
419
420static void
421gsk_gl_command_queue_init (GskGLCommandQueue *self)
422{
423 self->max_texture_size = -1;
424
425 gsk_gl_command_batches_init (ar: &self->batches, initial_size: 128);
426 gsk_gl_command_binds_init (ar: &self->batch_binds, initial_size: 1024);
427 gsk_gl_command_uniforms_init (ar: &self->batch_uniforms, initial_size: 2048);
428
429 gsk_gl_buffer_init (self: &self->vertices, GL_ARRAY_BUFFER, element_size: sizeof (GskGLDrawVertex));
430}
431
432GskGLCommandQueue *
433gsk_gl_command_queue_new (GdkGLContext *context,
434 GskGLUniformState *uniforms)
435{
436 GskGLCommandQueue *self;
437
438 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
439
440 self = g_object_new (GSK_TYPE_GL_COMMAND_QUEUE, NULL);
441 self->context = g_object_ref (context);
442 self->attachments = gsk_gl_attachment_state_new ();
443
444 /* Use shared uniform state if we're provided one */
445 if (uniforms != NULL)
446 self->uniforms = gsk_gl_uniform_state_ref (state: uniforms);
447 else
448 self->uniforms = gsk_gl_uniform_state_new ();
449
450 /* Determine max texture size immediately and restore context */
451 gdk_gl_context_make_current (context);
452 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &self->max_texture_size);
453
454 return g_steal_pointer (&self);
455}
456
457static inline GskGLCommandBatch *
458begin_next_batch (GskGLCommandQueue *self)
459{
460 GskGLCommandBatch *batch;
461
462 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
463
464 /* GskGLCommandBatch contains an embedded linked list using integers into the
465 * self->batches array. We can't use pointer because the batches could be
466 * realloc()'d at runtime.
467 *
468 * Before we execute the command queue, we sort the batches by framebuffer but
469 * leave the batches in place as we can just tweak the links via prev/next.
470 *
471 * Generally we only traverse forwards, so we could ignore the previous field.
472 * But to optimize the reordering of batches by framebuffer we walk backwards
473 * so we sort by most-recently-seen framebuffer to ensure draws happen in the
474 * proper order.
475 */
476
477 batch = gsk_gl_command_batches_append (ar: &self->batches);
478 batch->any.next_batch_index = -1;
479 batch->any.prev_batch_index = self->tail_batch_index;
480
481 return batch;
482}
483
484static void
485enqueue_batch (GskGLCommandQueue *self)
486{
487 guint index;
488
489 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
490 g_assert (self->batches.len > 0);
491
492 /* Batches are linked lists but using indexes into the batches array instead
493 * of pointers. This is for two main reasons. First, 16-bit indexes allow us
494 * to store the information in 4 bytes, where as two pointers would take 16
495 * bytes. Furthermore, we have an array here so pointers would get
496 * invalidated if we realloc()'d (and that can happen from time to time).
497 */
498
499 index = self->batches.len - 1;
500
501 if (self->head_batch_index == -1)
502 self->head_batch_index = index;
503
504 if (self->tail_batch_index != -1)
505 {
506 GskGLCommandBatch *prev = &self->batches.items[self->tail_batch_index];
507
508 prev->any.next_batch_index = index;
509 }
510
511 self->tail_batch_index = index;
512}
513
514static void
515discard_batch (GskGLCommandQueue *self)
516{
517 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
518 g_assert (self->batches.len > 0);
519
520 self->batches.len--;
521}
522
523void
524gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
525 GskGLUniformProgram *program,
526 guint width,
527 guint height)
528{
529 GskGLCommandBatch *batch;
530
531 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
532 g_assert (self->in_draw == FALSE);
533 g_assert (width <= G_MAXUINT16);
534 g_assert (height <= G_MAXUINT16);
535
536 /* Our internal links use 16-bits, so that is our max number
537 * of batches we can have in one frame.
538 */
539 if (will_ignore_batch (self))
540 return;
541
542 self->program_info = program;
543
544 batch = begin_next_batch (self);
545 batch->any.kind = GSK_GL_COMMAND_KIND_DRAW;
546 batch->any.program = program->program_id;
547 batch->any.next_batch_index = -1;
548 batch->any.viewport.width = width;
549 batch->any.viewport.height = height;
550 batch->draw.framebuffer = 0;
551 batch->draw.uniform_count = 0;
552 batch->draw.uniform_offset = self->batch_uniforms.len;
553 batch->draw.bind_count = 0;
554 batch->draw.bind_offset = self->batch_binds.len;
555 batch->draw.vbo_count = 0;
556 batch->draw.vbo_offset = gsk_gl_buffer_get_offset (buffer: &self->vertices);
557
558 self->fbo_max = MAX (self->fbo_max, batch->draw.framebuffer);
559
560 self->in_draw = TRUE;
561}
562
563void
564gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
565{
566 GskGLCommandBatch *last_batch;
567 GskGLCommandBatch *batch;
568
569 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
570 g_assert (self->batches.len > 0);
571
572 if (will_ignore_batch (self))
573 return;
574
575 batch = gsk_gl_command_batches_tail (ar: &self->batches);
576
577 g_assert (self->in_draw == TRUE);
578 g_assert (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW);
579
580 if G_UNLIKELY (batch->draw.vbo_count == 0)
581 {
582 discard_batch (self);
583 self->in_draw = FALSE;
584 return;
585 }
586
587 /* Track the destination framebuffer in case it changed */
588 batch->draw.framebuffer = self->attachments->fbo.id;
589 self->attachments->fbo.changed = FALSE;
590 self->fbo_max = MAX (self->fbo_max, self->attachments->fbo.id);
591
592 /* Save our full uniform state for this draw so we can possibly
593 * reorder the draw later.
594 */
595 batch->draw.uniform_offset = self->batch_uniforms.len;
596 batch->draw.uniform_count = snapshot_uniforms (state: self->uniforms, program: self->program_info, array: &self->batch_uniforms);
597
598 /* Track the bind attachments that changed */
599 if (self->program_info->has_attachments)
600 {
601 batch->draw.bind_offset = self->batch_binds.len;
602 batch->draw.bind_count = snapshot_attachments (state: self->attachments, array: &self->batch_binds);
603 }
604 else
605 {
606 batch->draw.bind_offset = 0;
607 batch->draw.bind_count = 0;
608 }
609
610 if (self->batches.len > 1)
611 last_batch = &self->batches.items[self->batches.len - 2];
612 else
613 last_batch = NULL;
614
615 /* Do simple chaining of draw to last batch. */
616 if (last_batch != NULL &&
617 last_batch->any.kind == GSK_GL_COMMAND_KIND_DRAW &&
618 last_batch->any.program == batch->any.program &&
619 last_batch->any.viewport.width == batch->any.viewport.width &&
620 last_batch->any.viewport.height == batch->any.viewport.height &&
621 last_batch->draw.framebuffer == batch->draw.framebuffer &&
622 last_batch->draw.vbo_offset + last_batch->draw.vbo_count == batch->draw.vbo_offset &&
623 last_batch->draw.vbo_count + batch->draw.vbo_count <= 0xffff &&
624 snapshots_equal (self, first: last_batch, second: batch))
625 {
626 last_batch->draw.vbo_count += batch->draw.vbo_count;
627 discard_batch (self);
628 }
629 else
630 {
631 enqueue_batch (self);
632 }
633
634 self->in_draw = FALSE;
635 self->program_info = NULL;
636}
637
638/**
639 * gsk_gl_command_queue_split_draw:
640 * @self a `GskGLCommandQueue`
641 *
642 * This function is like calling gsk_gl_command_queue_end_draw() followed by
643 * a gsk_gl_command_queue_begin_draw() with the same parameters as a
644 * previous begin draw (if shared uniforms where not changed further).
645 *
646 * This is useful to avoid comparisons inside of loops where we know shared
647 * uniforms are not changing.
648 *
649 * This generally should just be called from gsk_gl_program_split_draw()
650 * as that is where the begin/end flow happens from the render job.
651 */
652void
653gsk_gl_command_queue_split_draw (GskGLCommandQueue *self)
654{
655 GskGLCommandBatch *batch;
656 GskGLUniformProgram *program;
657 guint width;
658 guint height;
659
660 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
661 g_assert (self->batches.len > 0);
662 g_assert (self->in_draw == TRUE);
663
664 program = self->program_info;
665
666 batch = gsk_gl_command_batches_tail (ar: &self->batches);
667
668 g_assert (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW);
669
670 width = batch->any.viewport.width;
671 height = batch->any.viewport.height;
672
673 gsk_gl_command_queue_end_draw (self);
674 gsk_gl_command_queue_begin_draw (self, program, width, height);
675}
676
677void
678gsk_gl_command_queue_clear (GskGLCommandQueue *self,
679 guint clear_bits,
680 const graphene_rect_t *viewport)
681{
682 GskGLCommandBatch *batch;
683
684 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
685 g_assert (self->in_draw == FALSE);
686
687 if (will_ignore_batch (self))
688 return;
689
690 if (clear_bits == 0)
691 clear_bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
692
693 batch = begin_next_batch (self);
694 batch->any.kind = GSK_GL_COMMAND_KIND_CLEAR;
695 batch->any.viewport.width = viewport->size.width;
696 batch->any.viewport.height = viewport->size.height;
697 batch->clear.bits = clear_bits;
698 batch->clear.framebuffer = self->attachments->fbo.id;
699 batch->any.next_batch_index = -1;
700 batch->any.program = 0;
701
702 self->fbo_max = MAX (self->fbo_max, batch->clear.framebuffer);
703
704 enqueue_batch (self);
705
706 self->attachments->fbo.changed = FALSE;
707}
708
709GdkGLContext *
710gsk_gl_command_queue_get_context (GskGLCommandQueue *self)
711{
712 g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), NULL);
713
714 return self->context;
715}
716
717void
718gsk_gl_command_queue_make_current (GskGLCommandQueue *self)
719{
720 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
721 g_assert (GDK_IS_GL_CONTEXT (self->context));
722
723 gdk_gl_context_make_current (context: self->context);
724}
725
726void
727gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
728 guint program)
729{
730 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
731
732 glDeleteProgram (program);
733}
734
735static inline void
736apply_viewport (guint *current_width,
737 guint *current_height,
738 guint width,
739 guint height)
740{
741 if G_UNLIKELY (*current_width != width || *current_height != height)
742 {
743 *current_width = width;
744 *current_height = height;
745 glViewport (0, 0, width, height);
746 }
747}
748
749static inline void
750apply_scissor (gboolean *state,
751 guint framebuffer,
752 const graphene_rect_t *scissor,
753 gboolean has_scissor,
754 guint default_framebuffer)
755{
756 g_assert (framebuffer != (guint)-1);
757
758 if (framebuffer != default_framebuffer || !has_scissor)
759 {
760 if (*state != FALSE)
761 {
762 glDisable (GL_SCISSOR_TEST);
763 *state = FALSE;
764 }
765 }
766 else
767 {
768 if (*state != TRUE)
769 {
770 glEnable (GL_SCISSOR_TEST);
771 glScissor (scissor->origin.x,
772 scissor->origin.y,
773 scissor->size.width,
774 scissor->size.height);
775 *state = TRUE;
776 }
777 }
778}
779
780static inline gboolean
781apply_framebuffer (int *framebuffer,
782 guint new_framebuffer)
783{
784 if G_UNLIKELY (new_framebuffer != *framebuffer)
785 {
786 *framebuffer = new_framebuffer;
787 glBindFramebuffer (GL_FRAMEBUFFER, new_framebuffer);
788 return TRUE;
789 }
790
791 return FALSE;
792}
793
794static inline void
795gsk_gl_command_queue_unlink (GskGLCommandQueue *self,
796 GskGLCommandBatch *batch)
797{
798 if (batch->any.prev_batch_index == -1)
799 self->head_batch_index = batch->any.next_batch_index;
800 else
801 self->batches.items[batch->any.prev_batch_index].any.next_batch_index = batch->any.next_batch_index;
802
803 if (batch->any.next_batch_index == -1)
804 self->tail_batch_index = batch->any.prev_batch_index;
805 else
806 self->batches.items[batch->any.next_batch_index].any.prev_batch_index = batch->any.prev_batch_index;
807
808 batch->any.prev_batch_index = -1;
809 batch->any.next_batch_index = -1;
810}
811
812static inline void
813gsk_gl_command_queue_insert_before (GskGLCommandQueue *self,
814 GskGLCommandBatch *batch,
815 GskGLCommandBatch *sibling)
816{
817 int sibling_index;
818 int index;
819
820 g_assert (batch >= self->batches.items);
821 g_assert (batch < &self->batches.items[self->batches.len]);
822 g_assert (sibling >= self->batches.items);
823 g_assert (sibling < &self->batches.items[self->batches.len]);
824
825 index = gsk_gl_command_batches_index_of (ar: &self->batches, element: batch);
826 sibling_index = gsk_gl_command_batches_index_of (ar: &self->batches, element: sibling);
827
828 batch->any.next_batch_index = sibling_index;
829 batch->any.prev_batch_index = sibling->any.prev_batch_index;
830
831 if (batch->any.prev_batch_index > -1)
832 self->batches.items[batch->any.prev_batch_index].any.next_batch_index = index;
833
834 sibling->any.prev_batch_index = index;
835
836 if (batch->any.prev_batch_index == -1)
837 self->head_batch_index = index;
838}
839
840static void
841gsk_gl_command_queue_sort_batches (GskGLCommandQueue *self)
842{
843 int *seen;
844 int *seen_free = NULL;
845 int index;
846
847 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
848 g_assert (self->tail_batch_index >= 0);
849 g_assert (self->fbo_max >= 0);
850
851 /* Create our seen list with most recent index set to -1,
852 * meaning we haven't yet seen that framebuffer.
853 */
854 if (self->fbo_max < 1024)
855 seen = g_alloca (sizeof (int) * (self->fbo_max + 1));
856 else
857 seen = seen_free = g_new0 (int, (self->fbo_max + 1));
858 for (int i = 0; i <= self->fbo_max; i++)
859 seen[i] = -1;
860
861 /* Walk in reverse, and if we've seen that framebuffer before, we want to
862 * delay this operation until right before the last batch we saw for that
863 * framebuffer.
864 *
865 * We can do this because we don't use a framebuffer's texture until it has
866 * been completely drawn.
867 */
868 index = self->tail_batch_index;
869
870 while (index >= 0)
871 {
872 GskGLCommandBatch *batch = &self->batches.items[index];
873 int cur_index = index;
874 int fbo = -1;
875
876 g_assert (index > -1);
877 g_assert (index < self->batches.len);
878
879 switch (batch->any.kind)
880 {
881 case GSK_GL_COMMAND_KIND_DRAW:
882 fbo = batch->draw.framebuffer;
883 break;
884
885 case GSK_GL_COMMAND_KIND_CLEAR:
886 fbo = batch->clear.framebuffer;
887 break;
888
889 default:
890 g_assert_not_reached ();
891 }
892
893 index = batch->any.prev_batch_index;
894
895 g_assert (index >= -1);
896 g_assert (index < (int)self->batches.len);
897 g_assert (fbo >= -1);
898
899 if (fbo == -1)
900 continue;
901
902 g_assert (fbo <= self->fbo_max);
903 g_assert (seen[fbo] >= -1);
904 g_assert (seen[fbo] < (int)self->batches.len);
905
906 if (seen[fbo] != -1 && seen[fbo] != batch->any.next_batch_index)
907 {
908 int mru_index = seen[fbo];
909 GskGLCommandBatch *mru = &self->batches.items[mru_index];
910
911 g_assert (mru_index > -1);
912
913 gsk_gl_command_queue_unlink (self, batch);
914
915 g_assert (batch->any.prev_batch_index == -1);
916 g_assert (batch->any.next_batch_index == -1);
917
918 gsk_gl_command_queue_insert_before (self, batch, sibling: mru);
919
920 g_assert (batch->any.prev_batch_index > -1 ||
921 self->head_batch_index == cur_index);
922 g_assert (batch->any.next_batch_index == seen[fbo]);
923 }
924
925 g_assert (cur_index > -1);
926 g_assert (seen[fbo] >= -1);
927
928 seen[fbo] = cur_index;
929 }
930
931 g_free (mem: seen_free);
932}
933
934/**
935 * gsk_gl_command_queue_execute:
936 * @self: a `GskGLCommandQueue`
937 * @surface_height: the height of the backing surface
938 * @scale_factor: the scale factor of the backing surface
939 * @scissor: (nullable): the scissor clip if any
940 * @default_framebuffer: the default framebuffer id if not zero
941 *
942 * Executes all of the batches in the command queue.
943 *
944 * Typically, the scissor rect is only applied when rendering to the default
945 * framebuffer (zero in most cases). However, if @default_framebuffer is not
946 * zero, it will be checked to see if the rendering target matches so that
947 * the scissor rect is applied. This should be used in cases where rendering
948 * to the backbuffer for display is not the default GL framebuffer of zero.
949 * Currently, this happens when rendering on macOS using IOSurface.
950 */
951void
952gsk_gl_command_queue_execute (GskGLCommandQueue *self,
953 guint surface_height,
954 guint scale_factor,
955 const cairo_region_t *scissor,
956 guint default_framebuffer)
957{
958 G_GNUC_UNUSED guint count = 0;
959 graphene_rect_t scissor_test;
960 gboolean has_scissor = scissor != NULL;
961 gboolean scissor_state = -1;
962 guint program = 0;
963 guint width = 0;
964 guint height = 0;
965 G_GNUC_UNUSED guint n_binds = 0;
966 guint n_fbos = 0;
967 G_GNUC_UNUSED guint n_uniforms = 0;
968 guint n_programs = 0;
969 guint vao_id;
970 guint vbo_id;
971 int textures[4];
972 int framebuffer = -1;
973 int next_batch_index;
974 int active = -1;
975
976 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
977 g_assert (self->in_draw == FALSE);
978
979 if (self->batches.len == 0)
980 return;
981
982 for (guint i = 0; i < G_N_ELEMENTS (textures); i++)
983 textures[i] = -1;
984
985 gsk_gl_command_queue_sort_batches (self);
986
987 gsk_gl_command_queue_make_current (self);
988
989#ifdef G_ENABLE_DEBUG
990 gsk_gl_profiler_begin_gpu_region (profiler: self->gl_profiler);
991 gsk_profiler_timer_begin (profiler: self->profiler, timer_id: self->metrics.cpu_time);
992#endif
993
994 glEnable (GL_DEPTH_TEST);
995 glDepthFunc (GL_LEQUAL);
996
997 /* Pre-multiplied alpha */
998 glEnable (GL_BLEND);
999 glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1000 glBlendEquation (GL_FUNC_ADD);
1001
1002 glGenVertexArrays (1, &vao_id);
1003 glBindVertexArray (vao_id);
1004
1005 vbo_id = gsk_gl_buffer_submit (buffer: &self->vertices);
1006
1007 /* 0 = position location */
1008 glEnableVertexAttribArray (0);
1009 glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE,
1010 sizeof (GskGLDrawVertex),
1011 (void *) G_STRUCT_OFFSET (GskGLDrawVertex, position));
1012
1013 /* 1 = texture coord location */
1014 glEnableVertexAttribArray (1);
1015 glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE,
1016 sizeof (GskGLDrawVertex),
1017 (void *) G_STRUCT_OFFSET (GskGLDrawVertex, uv));
1018
1019 /* 2 = color location */
1020 glEnableVertexAttribArray (2);
1021 glVertexAttribPointer (2, 4, GL_HALF_FLOAT, GL_FALSE,
1022 sizeof (GskGLDrawVertex),
1023 (void *) G_STRUCT_OFFSET (GskGLDrawVertex, color));
1024
1025 /* 3 = color2 location */
1026 glEnableVertexAttribArray (3);
1027 glVertexAttribPointer (3, 4, GL_HALF_FLOAT, GL_FALSE,
1028 sizeof (GskGLDrawVertex),
1029 (void *) G_STRUCT_OFFSET (GskGLDrawVertex, color2));
1030
1031 /* Setup initial scissor clip */
1032 if (scissor != NULL)
1033 {
1034 cairo_rectangle_int_t r;
1035
1036 g_assert (cairo_region_num_rectangles (scissor) == 1);
1037 cairo_region_get_rectangle (region: scissor, nth: 0, rectangle: &r);
1038
1039 scissor_test.origin.x = r.x * scale_factor;
1040 scissor_test.origin.y = surface_height - (r.height * scale_factor) - (r.y * scale_factor);
1041 scissor_test.size.width = r.width * scale_factor;
1042 scissor_test.size.height = r.height * scale_factor;
1043 }
1044
1045 next_batch_index = self->head_batch_index;
1046
1047 while (next_batch_index >= 0)
1048 {
1049 const GskGLCommandBatch *batch = &self->batches.items[next_batch_index];
1050
1051 g_assert (next_batch_index >= 0);
1052 g_assert (next_batch_index < self->batches.len);
1053 g_assert (batch->any.next_batch_index != next_batch_index);
1054
1055 count++;
1056
1057 switch (batch->any.kind)
1058 {
1059 case GSK_GL_COMMAND_KIND_CLEAR:
1060 if (apply_framebuffer (framebuffer: &framebuffer, new_framebuffer: batch->clear.framebuffer))
1061 {
1062 apply_scissor (state: &scissor_state, framebuffer, scissor: &scissor_test, has_scissor, default_framebuffer);
1063 n_fbos++;
1064 }
1065
1066 apply_viewport (current_width: &width,
1067 current_height: &height,
1068 width: batch->any.viewport.width,
1069 height: batch->any.viewport.height);
1070
1071 glClearColor (0, 0, 0, 0);
1072 glClear (batch->clear.bits);
1073 break;
1074
1075 case GSK_GL_COMMAND_KIND_DRAW:
1076 if (batch->any.program != program)
1077 {
1078 program = batch->any.program;
1079 glUseProgram (program);
1080
1081 n_programs++;
1082 }
1083
1084 if (apply_framebuffer (framebuffer: &framebuffer, new_framebuffer: batch->draw.framebuffer))
1085 {
1086 apply_scissor (state: &scissor_state, framebuffer, scissor: &scissor_test, has_scissor, default_framebuffer);
1087 n_fbos++;
1088 }
1089
1090 apply_viewport (current_width: &width,
1091 current_height: &height,
1092 width: batch->any.viewport.width,
1093 height: batch->any.viewport.height);
1094
1095 if G_UNLIKELY (batch->draw.bind_count > 0)
1096 {
1097 const GskGLCommandBind *bind = &self->batch_binds.items[batch->draw.bind_offset];
1098
1099 for (guint i = 0; i < batch->draw.bind_count; i++)
1100 {
1101 if (textures[bind->texture] != bind->id)
1102 {
1103 if (active != bind->texture)
1104 {
1105 active = bind->texture;
1106 glActiveTexture (GL_TEXTURE0 + bind->texture);
1107 }
1108
1109 glBindTexture (GL_TEXTURE_2D, bind->id);
1110 textures[bind->texture] = bind->id;
1111 }
1112
1113 bind++;
1114 }
1115
1116 n_binds += batch->draw.bind_count;
1117 }
1118
1119 if (batch->draw.uniform_count > 0)
1120 {
1121 const GskGLCommandUniform *u = &self->batch_uniforms.items[batch->draw.uniform_offset];
1122
1123 for (guint i = 0; i < batch->draw.uniform_count; i++, u++)
1124 gsk_gl_uniform_state_apply (state: self->uniforms, program, location: u->location, info: u->info);
1125
1126 n_uniforms += batch->draw.uniform_count;
1127 }
1128
1129 glDrawArrays (GL_TRIANGLES, batch->draw.vbo_offset, batch->draw.vbo_count);
1130
1131 break;
1132
1133 default:
1134 g_assert_not_reached ();
1135 }
1136
1137#if 0
1138 if (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW ||
1139 batch->any.kind == GSK_GL_COMMAND_KIND_CLEAR)
1140 {
1141 char filename[128];
1142 g_snprintf (filename, sizeof filename,
1143 "capture%03u_batch%03d_kind%u_program%u_u%u_b%u_fb%u_ctx%p.png",
1144 count, next_batch_index,
1145 batch->any.kind, batch->any.program,
1146 batch->any.kind == GSK_GL_COMMAND_KIND_DRAW ? batch->draw.uniform_count : 0,
1147 batch->any.kind == GSK_GL_COMMAND_KIND_DRAW ? batch->draw.bind_count : 0,
1148 framebuffer,
1149 gdk_gl_context_get_current ());
1150 gsk_gl_command_queue_capture_png (self, filename, width, height, TRUE);
1151 gsk_gl_command_queue_print_batch (self, batch);
1152 }
1153#endif
1154
1155 next_batch_index = batch->any.next_batch_index;
1156 }
1157
1158 glDeleteBuffers (1, &vbo_id);
1159 glDeleteVertexArrays (1, &vao_id);
1160
1161 gdk_profiler_set_int_counter (self->metrics.n_binds, n_binds);
1162 gdk_profiler_set_int_counter (self->metrics.n_uniforms, n_uniforms);
1163 gdk_profiler_set_int_counter (self->metrics.n_fbos, n_fbos);
1164 gdk_profiler_set_int_counter (self->metrics.n_programs, n_programs);
1165 gdk_profiler_set_int_counter (self->metrics.n_uploads, self->n_uploads);
1166 gdk_profiler_set_int_counter (self->metrics.queue_depth, self->batches.len);
1167
1168#ifdef G_ENABLE_DEBUG
1169 {
1170 gint64 start_time G_GNUC_UNUSED = gsk_profiler_timer_get_start (profiler: self->profiler, timer_id: self->metrics.cpu_time);
1171 gint64 cpu_time = gsk_profiler_timer_end (profiler: self->profiler, timer_id: self->metrics.cpu_time);
1172 gint64 gpu_time = gsk_gl_profiler_end_gpu_region (profiler: self->gl_profiler);
1173
1174 gsk_profiler_timer_set (profiler: self->profiler, timer_id: self->metrics.gpu_time, value: gpu_time);
1175 gsk_profiler_timer_set (profiler: self->profiler, timer_id: self->metrics.cpu_time, value: cpu_time);
1176 gsk_profiler_counter_inc (profiler: self->profiler, counter_id: self->metrics.n_frames);
1177
1178 gsk_profiler_push_samples (profiler: self->profiler);
1179 }
1180#endif
1181}
1182
1183void
1184gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self)
1185{
1186 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1187 g_assert (self->batches.len == 0);
1188
1189 gsk_gl_command_queue_make_current (self);
1190
1191 self->fbo_max = 0;
1192 self->tail_batch_index = -1;
1193 self->head_batch_index = -1;
1194 self->in_frame = TRUE;
1195}
1196
1197/**
1198 * gsk_gl_command_queue_end_frame:
1199 * @self: a `GskGLCommandQueue`
1200 *
1201 * This function performs cleanup steps that need to be done after
1202 * a frame has finished. This is not performed as part of the command
1203 * queue execution to allow for the frame to be submitted as soon
1204 * as possible.
1205 *
1206 * However, it should be executed after the draw contexts end_frame
1207 * has been called to swap the OpenGL framebuffers.
1208 */
1209void
1210gsk_gl_command_queue_end_frame (GskGLCommandQueue *self)
1211{
1212 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1213
1214 gsk_gl_command_queue_make_current (self);
1215 gsk_gl_uniform_state_end_frame (state: self->uniforms);
1216
1217 /* Reset attachments so we don't hold on to any textures
1218 * that might be released after the frame.
1219 */
1220 for (guint i = 0; i < G_N_ELEMENTS (self->attachments->textures); i++)
1221 {
1222 if (self->attachments->textures[i].id != 0)
1223 {
1224 glActiveTexture (GL_TEXTURE0 + i);
1225 glBindTexture (GL_TEXTURE_2D, 0);
1226
1227 self->attachments->textures[i].id = 0;
1228 self->attachments->textures[i].changed = FALSE;
1229 self->attachments->textures[i].initial = TRUE;
1230 }
1231 }
1232
1233 self->batches.len = 0;
1234 self->batch_binds.len = 0;
1235 self->batch_uniforms.len = 0;
1236 self->n_uploads = 0;
1237 self->tail_batch_index = -1;
1238 self->in_frame = FALSE;
1239}
1240
1241gboolean
1242gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
1243 int width,
1244 int height,
1245 int format,
1246 int min_filter,
1247 int mag_filter,
1248 guint *out_fbo_id,
1249 guint *out_texture_id)
1250{
1251 GLuint fbo_id = 0;
1252 GLint texture_id;
1253
1254 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1255 g_assert (width > 0);
1256 g_assert (height > 0);
1257 g_assert (out_fbo_id != NULL);
1258 g_assert (out_texture_id != NULL);
1259
1260 texture_id = gsk_gl_command_queue_create_texture (self,
1261 width, height,
1262 format,
1263 min_filter, mag_filter);
1264
1265 if (texture_id == -1)
1266 {
1267 *out_fbo_id = 0;
1268 *out_texture_id = 0;
1269 return FALSE;
1270 }
1271
1272 fbo_id = gsk_gl_command_queue_create_framebuffer (self);
1273
1274 glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
1275 glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
1276 g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
1277
1278 *out_fbo_id = fbo_id;
1279 *out_texture_id = texture_id;
1280
1281 return TRUE;
1282}
1283
1284int
1285gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
1286 int width,
1287 int height,
1288 int format,
1289 int min_filter,
1290 int mag_filter)
1291{
1292 GLuint texture_id = 0;
1293
1294 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1295
1296 if G_UNLIKELY (self->max_texture_size == -1)
1297 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &self->max_texture_size);
1298
1299 if (width > self->max_texture_size || height > self->max_texture_size)
1300 return -1;
1301
1302 glGenTextures (1, &texture_id);
1303
1304 glActiveTexture (GL_TEXTURE0);
1305 glBindTexture (GL_TEXTURE_2D, texture_id);
1306
1307 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
1308 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
1309 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1310 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1311
1312 switch (format)
1313 {
1314 case GL_RGBA8:
1315 glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1316 break;
1317 case GL_RGBA16F:
1318 glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_HALF_FLOAT, NULL);
1319 break;
1320 case GL_RGBA32F:
1321 glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
1322 break;
1323 default:
1324 /* If you add new formats, make sure to set the correct format and type here
1325 * so that GLES doesn't barf invalid operations at you.
1326 * Because it is very important that these 3 values match when data is set to
1327 * NULL, do you hear me?
1328 */
1329 g_assert_not_reached ();
1330 break;
1331 }
1332
1333 /* Restore the previous texture if it was set */
1334 if (self->attachments->textures[0].id != 0)
1335 glBindTexture (GL_TEXTURE_2D, self->attachments->textures[0].id);
1336
1337 return (int)texture_id;
1338}
1339
1340guint
1341gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
1342{
1343 GLuint fbo_id;
1344
1345 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1346
1347 glGenFramebuffers (1, &fbo_id);
1348
1349 return fbo_id;
1350}
1351
1352static void
1353gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
1354 GdkTexture *texture)
1355{
1356 GdkGLContext *context;
1357 const guchar *data;
1358 gsize stride;
1359 GdkMemoryTexture *memtex;
1360 GdkMemoryFormat data_format;
1361 int width, height;
1362 GLenum gl_internalformat;
1363 GLenum gl_format;
1364 GLenum gl_type;
1365 gsize bpp;
1366 gboolean use_es;
1367
1368 context = gdk_gl_context_get_current ();
1369 use_es = gdk_gl_context_get_use_es (context);
1370 data_format = gdk_texture_get_format (self: texture);
1371 width = gdk_texture_get_width (texture);
1372 height = gdk_texture_get_height (texture);
1373
1374 if (!gdk_memory_format_gl_format (format: data_format,
1375 gles: use_es,
1376 out_internal_format: &gl_internalformat,
1377 out_format: &gl_format,
1378 out_type: &gl_type))
1379 {
1380 if (gdk_memory_format_prefers_high_depth (format: data_format))
1381 data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
1382 else
1383 data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
1384 if (!gdk_memory_format_gl_format (format: data_format,
1385 gles: use_es,
1386 out_internal_format: &gl_internalformat,
1387 out_format: &gl_format,
1388 out_type: &gl_type))
1389 {
1390 g_assert_not_reached ();
1391 }
1392 }
1393
1394 memtex = gdk_memory_texture_from_texture (texture, format: data_format);
1395 data = gdk_memory_texture_get_data (self: memtex);
1396 stride = gdk_memory_texture_get_stride (self: memtex);
1397 bpp = gdk_memory_format_bytes_per_pixel (format: data_format);
1398
1399 glPixelStorei (GL_UNPACK_ALIGNMENT, gdk_memory_format_alignment (format: data_format));
1400
1401 /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
1402 * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
1403 */
1404 if (stride == width * bpp)
1405 {
1406 glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
1407 }
1408 else if (stride % bpp == 0 &&
1409 (gdk_gl_context_check_version (context, required_gl_major: 0, required_gl_minor: 0, required_gles_major: 3, required_gles_minor: 0) || gdk_gl_context_has_unpack_subimage (context)))
1410 {
1411 glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
1412
1413 glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
1414
1415 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
1416 }
1417 else
1418 {
1419 int i;
1420 glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
1421 for (i = 0; i < height; i++)
1422 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
1423 }
1424 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1425
1426 g_object_unref (object: memtex);
1427}
1428
1429int
1430gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
1431 GdkTexture *texture,
1432 int min_filter,
1433 int mag_filter)
1434{
1435 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
1436 cairo_surface_t *surface = NULL;
1437 int width, height;
1438 int texture_id;
1439
1440 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1441 g_assert (!GDK_IS_GL_TEXTURE (texture));
1442 g_assert (min_filter == GL_LINEAR || min_filter == GL_NEAREST);
1443 g_assert (mag_filter == GL_LINEAR || min_filter == GL_NEAREST);
1444
1445 width = gdk_texture_get_width (texture);
1446 height = gdk_texture_get_height (texture);
1447 if (width > self->max_texture_size || height > self->max_texture_size)
1448 {
1449 g_warning ("Attempt to create texture of size %ux%u but max size is %d. "
1450 "Clipping will occur.",
1451 width, height, self->max_texture_size);
1452 width = MAX (width, self->max_texture_size);
1453 height = MAX (height, self->max_texture_size);
1454 }
1455 texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8, min_filter, mag_filter);
1456 if (texture_id == -1)
1457 return texture_id;
1458
1459 self->n_uploads++;
1460
1461 /* Switch to texture0 as 2D. We'll restore it later. */
1462 glActiveTexture (GL_TEXTURE0);
1463 glBindTexture (GL_TEXTURE_2D, texture_id);
1464
1465 gsk_gl_command_queue_do_upload_texture (self, texture);
1466
1467 /* Restore previous texture state if any */
1468 if (self->attachments->textures[0].id > 0)
1469 glBindTexture (self->attachments->textures[0].target,
1470 self->attachments->textures[0].id);
1471
1472 g_clear_pointer (&surface, cairo_surface_destroy);
1473
1474 if (gdk_profiler_is_running ())
1475 gdk_profiler_add_markf (start_time, GDK_PROFILER_CURRENT_TIME-start_time,
1476 "Upload Texture",
1477 "Size %dx%d", width, height);
1478
1479 return texture_id;
1480}
1481
1482void
1483gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self,
1484 GskProfiler *profiler)
1485{
1486#ifdef G_ENABLE_DEBUG
1487 g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
1488 g_assert (GSK_IS_PROFILER (profiler));
1489
1490 if (g_set_object (&self->profiler, profiler))
1491 {
1492 self->gl_profiler = gsk_gl_profiler_new (context: self->context);
1493
1494 self->metrics.n_frames = gsk_profiler_add_counter (profiler, counter_name: "frames", description: "Frames", FALSE);
1495 self->metrics.cpu_time = gsk_profiler_add_timer (profiler, timer_name: "cpu-time", description: "CPU Time", FALSE, TRUE);
1496 self->metrics.gpu_time = gsk_profiler_add_timer (profiler, timer_name: "gpu-time", description: "GPU Time", FALSE, TRUE);
1497
1498 self->metrics.n_binds = gdk_profiler_define_int_counter ("attachments", "Number of texture attachments");
1499 self->metrics.n_fbos = gdk_profiler_define_int_counter ("fbos", "Number of framebuffers attached");
1500 self->metrics.n_uniforms = gdk_profiler_define_int_counter ("uniforms", "Number of uniforms changed");
1501 self->metrics.n_uploads = gdk_profiler_define_int_counter ("uploads", "Number of texture uploads");
1502 self->metrics.n_programs = gdk_profiler_define_int_counter ("programs", "Number of program changes");
1503 self->metrics.queue_depth = gdk_profiler_define_int_counter ("gl-queue-depth", "Depth of GL command batches");
1504 }
1505#endif
1506}
1507

source code of gtk/gsk/gl/gskglcommandqueue.c