1/* gskgldriver.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 "gskgldriverprivate.h"
27
28#include <gsk/gskdebugprivate.h>
29#include <gsk/gskglshaderprivate.h>
30#include <gsk/gskrendererprivate.h>
31
32#include "gskglcommandqueueprivate.h"
33#include "gskglcompilerprivate.h"
34#include "gskglglyphlibraryprivate.h"
35#include "gskgliconlibraryprivate.h"
36#include "gskglprogramprivate.h"
37#include "gskglshadowlibraryprivate.h"
38#include "gskgltextureprivate.h"
39#include "fp16private.h"
40
41#include <gdk/gdkglcontextprivate.h>
42#include <gdk/gdkdisplayprivate.h>
43#include <gdk/gdkmemorytextureprivate.h>
44#include <gdk/gdkprofilerprivate.h>
45#include <gdk/gdktextureprivate.h>
46
47#define ATLAS_SIZE 512
48#define MAX_OLD_RATIO 0.5
49
50G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
51
52static guint
53texture_key_hash (gconstpointer v)
54{
55 const GskTextureKey *k = (const GskTextureKey *)v;
56
57 /* Optimize for 0..3 where 0 is the scaled out case. Usually
58 * we'll be squarely on 1 or 2 for standard vs HiDPI. When rendering
59 * to a texture scaled out like in node-editor, we might be < 1.
60 */
61 guint scale_x = floorf (x: k->scale_x);
62 guint scale_y = floorf (x: k->scale_y);
63
64 return GPOINTER_TO_SIZE (k->pointer) ^
65 ((scale_x << 8) |
66 (scale_y << 6) |
67 (k->filter << 1) |
68 k->pointer_is_child);
69}
70
71static gboolean
72texture_key_equal (gconstpointer v1,
73 gconstpointer v2)
74{
75 const GskTextureKey *k1 = (const GskTextureKey *)v1;
76 const GskTextureKey *k2 = (const GskTextureKey *)v2;
77
78 return k1->pointer == k2->pointer &&
79 k1->scale_x == k2->scale_x &&
80 k1->scale_y == k2->scale_y &&
81 k1->filter == k2->filter &&
82 k1->pointer_is_child == k2->pointer_is_child &&
83 (!k1->pointer_is_child || memcmp (s1: &k1->parent_rect, s2: &k2->parent_rect, n: sizeof k1->parent_rect) == 0);
84}
85
86static void
87remove_texture_key_for_id (GskGLDriver *self,
88 guint texture_id)
89{
90 GskTextureKey *key;
91
92 g_assert (GSK_IS_GL_DRIVER (self));
93 g_assert (texture_id > 0);
94
95 /* g_hash_table_remove() will cause @key to be freed */
96 if (g_hash_table_steal_extended (hash_table: self->texture_id_to_key,
97 GUINT_TO_POINTER (texture_id),
98 NULL,
99 stolen_value: (gpointer *)&key))
100 g_hash_table_remove (hash_table: self->key_to_texture_id, key);
101}
102
103static void
104gsk_gl_texture_destroyed (gpointer data)
105{
106 ((GskGLTexture *)data)->user = NULL;
107}
108
109static void
110gsk_gl_driver_autorelease_texture (GskGLDriver *self,
111 guint texture_id)
112{
113 g_assert (GSK_IS_GL_DRIVER (self));
114
115 g_array_append_val (self->texture_pool, texture_id);
116}
117
118static guint
119gsk_gl_driver_collect_unused_textures (GskGLDriver *self,
120 gint64 watermark)
121{
122 GHashTableIter iter;
123 gpointer k, v;
124 guint old_size;
125 guint collected;
126
127 g_assert (GSK_IS_GL_DRIVER (self));
128
129 old_size = g_hash_table_size (hash_table: self->textures);
130
131 g_hash_table_iter_init (iter: &iter, hash_table: self->textures);
132 while (g_hash_table_iter_next (iter: &iter, key: &k, value: &v))
133 {
134 GskGLTexture *t = v;
135
136 if (t->user || t->permanent)
137 continue;
138
139 if (t->last_used_in_frame <= watermark)
140 {
141 g_hash_table_iter_steal (iter: &iter);
142
143 g_assert (t->link.prev == NULL);
144 g_assert (t->link.next == NULL);
145 g_assert (t->link.data == t);
146
147 remove_texture_key_for_id (self, texture_id: t->texture_id);
148 gsk_gl_driver_autorelease_texture (self, texture_id: t->texture_id);
149 t->texture_id = 0;
150 gsk_gl_texture_free (texture: t);
151 }
152 }
153
154 collected = old_size - g_hash_table_size (hash_table: self->textures);
155
156 return collected;
157}
158
159static void
160gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
161{
162 if (atlas->texture_id != 0)
163 {
164 glDeleteTextures (1, &atlas->texture_id);
165 atlas->texture_id = 0;
166 }
167
168 g_clear_pointer (&atlas->nodes, g_free);
169 g_slice_free (GskGLTextureAtlas, atlas);
170}
171
172GskGLTextureAtlas *
173gsk_gl_driver_create_atlas (GskGLDriver *self)
174{
175 GskGLTextureAtlas *atlas;
176
177 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
178
179 atlas = g_slice_new0 (GskGLTextureAtlas);
180 atlas->width = ATLAS_SIZE;
181 atlas->height = ATLAS_SIZE;
182 /* TODO: We might want to change the strategy about the amount of
183 * nodes here? stb_rect_pack.h says width is optimal. */
184 atlas->nodes = g_malloc0_n (n_blocks: atlas->width, n_block_bytes: sizeof (struct stbrp_node));
185 stbrp_init_target (context: &atlas->context, width: atlas->width, height: atlas->height, nodes: atlas->nodes, num_nodes: atlas->width);
186 atlas->texture_id = gsk_gl_command_queue_create_texture (self: self->command_queue,
187 width: atlas->width,
188 height: atlas->height,
189 GL_RGBA8,
190 GL_LINEAR,
191 GL_LINEAR);
192
193 gdk_gl_context_label_object_printf (context: gdk_gl_context_get_current (),
194 GL_TEXTURE, name: atlas->texture_id,
195 format: "Texture atlas %d",
196 atlas->texture_id);
197
198 g_ptr_array_add (array: self->atlases, data: atlas);
199
200 return atlas;
201}
202
203static void
204remove_program (gpointer data)
205{
206 GskGLProgram *program = data;
207
208 g_assert (!program || GSK_IS_GL_PROGRAM (program));
209
210 if (program != NULL)
211 {
212 gsk_gl_program_delete (self: program);
213 g_object_unref (object: program);
214 }
215}
216
217static void
218gsk_gl_driver_shader_weak_cb (gpointer data,
219 GObject *where_object_was)
220{
221 GskGLDriver *self = data;
222
223 g_assert (GSK_IS_GL_DRIVER (self));
224
225 if (self->shader_cache != NULL)
226 g_hash_table_remove (hash_table: self->shader_cache, key: where_object_was);
227}
228
229static void
230gsk_gl_driver_dispose (GObject *object)
231{
232 GskGLDriver *self = (GskGLDriver *)object;
233
234 g_assert (GSK_IS_GL_DRIVER (self));
235 g_assert (self->in_frame == FALSE);
236
237#define GSK_GL_NO_UNIFORMS
238#define GSK_GL_ADD_UNIFORM(pos, KEY, name)
239#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
240 GSK_GL_DELETE_PROGRAM(name); \
241 GSK_GL_DELETE_PROGRAM(name ## _no_clip); \
242 GSK_GL_DELETE_PROGRAM(name ## _rect_clip);
243#define GSK_GL_DELETE_PROGRAM(name) \
244 G_STMT_START { \
245 if (self->name) \
246 gsk_gl_program_delete (self->name); \
247 g_clear_object (&self->name); \
248 } G_STMT_END;
249# include "gskglprograms.defs"
250#undef GSK_GL_NO_UNIFORMS
251#undef GSK_GL_ADD_UNIFORM
252#undef GSK_GL_DEFINE_PROGRAM
253
254 if (self->shader_cache != NULL)
255 {
256 GHashTableIter iter;
257 gpointer k, v;
258
259 g_hash_table_iter_init (iter: &iter, hash_table: self->shader_cache);
260 while (g_hash_table_iter_next (iter: &iter, key: &k, value: &v))
261 {
262 GskGLShader *shader = k;
263 g_object_weak_unref (G_OBJECT (shader),
264 notify: gsk_gl_driver_shader_weak_cb,
265 data: self);
266 g_hash_table_iter_remove (iter: &iter);
267 }
268
269 g_clear_pointer (&self->shader_cache, g_hash_table_unref);
270 }
271
272 if (self->command_queue != NULL)
273 {
274 gsk_gl_command_queue_make_current (self: self->command_queue);
275 gsk_gl_driver_collect_unused_textures (self, watermark: 0);
276 g_clear_object (&self->command_queue);
277 }
278
279 if (self->autorelease_framebuffers->len > 0)
280 {
281 glDeleteFramebuffers (self->autorelease_framebuffers->len,
282 (GLuint *)(gpointer)self->autorelease_framebuffers->data);
283 self->autorelease_framebuffers->len = 0;
284 }
285
286 g_clear_pointer (&self->texture_pool, g_array_unref);
287
288 g_assert (!self->textures || g_hash_table_size (self->textures) == 0);
289 g_assert (!self->texture_id_to_key || g_hash_table_size (self->texture_id_to_key) == 0);
290 g_assert (!self->key_to_texture_id|| g_hash_table_size (self->key_to_texture_id) == 0);
291
292 g_clear_object (&self->glyphs);
293 g_clear_object (&self->icons);
294 g_clear_object (&self->shadows);
295
296 g_clear_pointer (&self->atlases, g_ptr_array_unref);
297 g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
298 g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
299 g_clear_pointer (&self->textures, g_hash_table_unref);
300 g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
301 g_clear_pointer (&self->texture_id_to_key, g_hash_table_unref);
302 g_clear_pointer (&self->render_targets, g_ptr_array_unref);
303 g_clear_pointer (&self->shader_cache, g_hash_table_unref);
304
305 g_clear_object (&self->command_queue);
306 g_clear_object (&self->shared_command_queue);
307
308 G_OBJECT_CLASS (gsk_gl_driver_parent_class)->dispose (object);
309}
310
311static void
312gsk_gl_driver_class_init (GskGLDriverClass *klass)
313{
314 GObjectClass *object_class = G_OBJECT_CLASS (klass);
315
316 object_class->dispose = gsk_gl_driver_dispose;
317}
318
319static void
320gsk_gl_driver_init (GskGLDriver *self)
321{
322 self->autorelease_framebuffers = g_array_new (FALSE, FALSE, element_size: sizeof (guint));
323 self->textures = g_hash_table_new_full (NULL, NULL, NULL,
324 value_destroy_func: (GDestroyNotify)gsk_gl_texture_free);
325 self->texture_id_to_key = g_hash_table_new (NULL, NULL);
326 self->key_to_texture_id = g_hash_table_new_full (hash_func: texture_key_hash,
327 key_equal_func: texture_key_equal,
328 key_destroy_func: g_free,
329 NULL);
330 self->shader_cache = g_hash_table_new_full (NULL, NULL, NULL, value_destroy_func: remove_program);
331 self->texture_pool = g_array_new (FALSE, FALSE, element_size: sizeof (guint));
332 self->render_targets = g_ptr_array_new ();
333 self->atlases = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify)gsk_gl_texture_atlas_free);
334}
335
336static gboolean
337gsk_gl_driver_load_programs (GskGLDriver *self,
338 GError **error)
339{
340 GskGLCompiler *compiler;
341 gboolean ret = FALSE;
342 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
343
344 g_assert (GSK_IS_GL_DRIVER (self));
345 g_assert (GSK_IS_GL_COMMAND_QUEUE (self->command_queue));
346
347 compiler = gsk_gl_compiler_new (driver: self, debug: self->debug);
348
349 /* Setup preambles that are shared by all shaders */
350 gsk_gl_compiler_set_preamble_from_resource (self: compiler,
351 kind: GSK_GL_COMPILER_ALL,
352 resource_path: "/org/gtk/libgsk/gl/preamble.glsl");
353 gsk_gl_compiler_set_preamble_from_resource (self: compiler,
354 kind: GSK_GL_COMPILER_VERTEX,
355 resource_path: "/org/gtk/libgsk/gl/preamble.vs.glsl");
356 gsk_gl_compiler_set_preamble_from_resource (self: compiler,
357 kind: GSK_GL_COMPILER_FRAGMENT,
358 resource_path: "/org/gtk/libgsk/gl/preamble.fs.glsl");
359
360 /* Setup attributes that are provided via VBO */
361 gsk_gl_compiler_bind_attribute (self: compiler, name: "aPosition", location: 0);
362 gsk_gl_compiler_bind_attribute (self: compiler, name: "aUv", location: 1);
363 gsk_gl_compiler_bind_attribute (self: compiler, name: "aColor", location: 2);
364 gsk_gl_compiler_bind_attribute (self: compiler, name: "aColor2", location: 3);
365
366 /* Use XMacros to register all of our programs and their uniforms */
367#define GSK_GL_NO_UNIFORMS
368#define GSK_GL_ADD_UNIFORM(pos, KEY, name) \
369 gsk_gl_program_add_uniform (program, #name, UNIFORM_##KEY);
370#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
371 gsk_gl_compiler_set_source_from_resource (compiler, GSK_GL_COMPILER_ALL, resource); \
372 GSK_GL_COMPILE_PROGRAM(name ## _no_clip, uniforms, "#define NO_CLIP 1\n"); \
373 GSK_GL_COMPILE_PROGRAM(name ## _rect_clip, uniforms, "#define RECT_CLIP 1\n"); \
374 GSK_GL_COMPILE_PROGRAM(name, uniforms, "");
375#define GSK_GL_COMPILE_PROGRAM(name, uniforms, clip) \
376 G_STMT_START { \
377 GskGLProgram *program; \
378 gboolean have_alpha; \
379 gboolean have_source; \
380 \
381 if (!(program = gsk_gl_compiler_compile (compiler, #name, clip, error))) \
382 goto failure; \
383 \
384 have_alpha = gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_ALPHA); \
385 have_source = gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SHARED_SOURCE); \
386 gsk_gl_program_add_uniform (program, "u_clip_rect", UNIFORM_SHARED_CLIP_RECT); \
387 gsk_gl_program_add_uniform (program, "u_viewport", UNIFORM_SHARED_VIEWPORT); \
388 gsk_gl_program_add_uniform (program, "u_projection", UNIFORM_SHARED_PROJECTION); \
389 gsk_gl_program_add_uniform (program, "u_modelview", UNIFORM_SHARED_MODELVIEW); \
390 \
391 uniforms \
392 \
393 gsk_gl_program_uniforms_added (program, have_source); \
394 if (have_alpha) \
395 gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 0, 1.0f); \
396 \
397 *(GskGLProgram **)(((guint8 *)self) + G_STRUCT_OFFSET (GskGLDriver, name)) = \
398 g_steal_pointer (&program); \
399 } G_STMT_END;
400# include "gskglprograms.defs"
401#undef GSK_GL_DEFINE_PROGRAM_CLIP
402#undef GSK_GL_DEFINE_PROGRAM
403#undef GSK_GL_ADD_UNIFORM
404
405 ret = TRUE;
406
407failure:
408 g_clear_object (&compiler);
409
410 gdk_profiler_end_mark (start_time, "load programs", NULL);
411
412 return ret;
413}
414
415/**
416 * gsk_gl_driver_autorelease_framebuffer:
417 * @self: a `GskGLDriver`
418 * @framebuffer_id: the id of the OpenGL framebuffer
419 *
420 * Marks @framebuffer_id to be deleted when the current frame has cmopleted.
421 */
422static void
423gsk_gl_driver_autorelease_framebuffer (GskGLDriver *self,
424 guint framebuffer_id)
425{
426 g_assert (GSK_IS_GL_DRIVER (self));
427
428 g_array_append_val (self->autorelease_framebuffers, framebuffer_id);
429}
430
431static GskGLDriver *
432gsk_gl_driver_new (GskGLCommandQueue *command_queue,
433 gboolean debug_shaders,
434 GError **error)
435{
436 GskGLDriver *self;
437 GdkGLContext *context;
438 gint64 before G_GNUC_UNUSED;
439
440 g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue), NULL);
441
442 before = GDK_PROFILER_CURRENT_TIME;
443
444 context = gsk_gl_command_queue_get_context (self: command_queue);
445
446 gdk_gl_context_make_current (context);
447
448 self = g_object_new (GSK_TYPE_GL_DRIVER, NULL);
449 self->command_queue = g_object_ref (command_queue);
450 self->shared_command_queue = g_object_ref (command_queue);
451 self->debug = !!debug_shaders;
452
453 if (!gsk_gl_driver_load_programs (self, error))
454 {
455 g_object_unref (object: self);
456 return NULL;
457 }
458
459 self->glyphs = gsk_gl_glyph_library_new (driver: self);
460 self->icons = gsk_gl_icon_library_new (driver: self);
461 self->shadows = gsk_gl_shadow_library_new (driver: self);
462
463 gdk_profiler_end_mark (before, "create GskGLDriver", NULL);
464
465 return g_steal_pointer (&self);
466}
467
468/**
469 * gsk_gl_driver_for_display:
470 * @display: A #GdkDisplay that is known to support GL
471 * @debug_shaders: if debug information for shaders should be displayed
472 * @error: location for error information
473 *
474 * Retrieves a driver for a shared display. Generally this is shared across all GL
475 * contexts for a display so that fewer programs are necessary for driving output.
476 *
477 * Returns: (transfer full): a `GskGLDriver` if successful; otherwise %NULL and
478 * @error is set.
479 */
480GskGLDriver *
481gsk_gl_driver_for_display (GdkDisplay *display,
482 gboolean debug_shaders,
483 GError **error)
484{
485 GdkGLContext *context;
486 GskGLCommandQueue *command_queue = NULL;
487 GskGLDriver *driver;
488
489 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
490
491 if ((driver = g_object_get_data (G_OBJECT (display), key: "GSK_GL_DRIVER")))
492 return g_object_ref (driver);
493
494 context = gdk_display_get_gl_context (display);
495 g_assert (context);
496
497 gdk_gl_context_make_current (context);
498
499 /* Initially we create a command queue using the shared context. However,
500 * as frames are processed this will be replaced with the command queue
501 * for a given renderer. But since the programs are compiled into the
502 * shared context, all other contexts sharing with it will have access
503 * to those programs.
504 */
505 command_queue = gsk_gl_command_queue_new (context, NULL);
506
507 if (!(driver = gsk_gl_driver_new (command_queue, debug_shaders, error)))
508 goto failure;
509
510 g_object_set_data_full (G_OBJECT (display),
511 key: "GSK_GL_DRIVER",
512 g_object_ref (driver),
513 destroy: g_object_unref);
514
515failure:
516 g_clear_object (&command_queue);
517
518 return g_steal_pointer (&driver);
519}
520
521static GPtrArray *
522gsk_gl_driver_compact_atlases (GskGLDriver *self)
523{
524 GPtrArray *removed = NULL;
525
526 g_assert (GSK_IS_GL_DRIVER (self));
527
528 for (guint i = self->atlases->len; i > 0; i--)
529 {
530 GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
531
532 if (gsk_gl_texture_atlas_get_unused_ratio (self: atlas) > MAX_OLD_RATIO)
533 {
534 GSK_NOTE (GLYPH_CACHE,
535 g_message ("Dropping atlas %d (%g.2%% old)", i,
536 100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
537 if (removed == NULL)
538 removed = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify)gsk_gl_texture_atlas_free);
539 g_ptr_array_add (array: removed, data: g_ptr_array_steal_index (array: self->atlases, index_: i - 1));
540 }
541 }
542
543 GSK_NOTE (GLYPH_CACHE, {
544 static guint timestamp;
545 if (timestamp++ % 60 == 0)
546 g_message ("%d atlases", self->atlases->len);
547 });
548
549 return removed;
550}
551
552/**
553 * gsk_gl_driver_begin_frame:
554 * @self: a `GskGLDriver`
555 * @command_queue: A `GskGLCommandQueue` from the renderer
556 *
557 * Begin a new frame.
558 *
559 * Texture atlases, pools, and other resources will be prepared to draw the
560 * next frame. The command queue should be one that was created for the
561 * target context to be drawn into (the context of the renderer's surface).
562 */
563void
564gsk_gl_driver_begin_frame (GskGLDriver *self,
565 GskGLCommandQueue *command_queue)
566{
567 gint64 last_frame_id;
568 GPtrArray *removed;
569
570 g_return_if_fail (GSK_IS_GL_DRIVER (self));
571 g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue));
572 g_return_if_fail (self->in_frame == FALSE);
573
574 last_frame_id = self->current_frame_id;
575
576 self->in_frame = TRUE;
577 self->current_frame_id++;
578
579 g_set_object (&self->command_queue, command_queue);
580
581 gsk_gl_command_queue_begin_frame (self: self->command_queue);
582
583 /* Compact atlases with too many freed pixels */
584 removed = gsk_gl_driver_compact_atlases (self);
585
586 /* Mark unused pixel regions of the atlases */
587 gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons),
588 frame_id: self->current_frame_id,
589 removed_atlases: removed);
590 gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs),
591 frame_id: self->current_frame_id,
592 removed_atlases: removed);
593
594 /* Cleanup old shadows */
595 gsk_gl_shadow_library_begin_frame (self: self->shadows);
596
597 /* Remove all textures that are from a previous frame or are no
598 * longer used by linked GdkTexture. We do this at the beginning
599 * of the following frame instead of the end so that we reduce chances
600 * we block on any resources while delivering our frames.
601 */
602 gsk_gl_driver_collect_unused_textures (self, watermark: last_frame_id - 1);
603
604 /* Now free atlas textures */
605 g_clear_pointer (&removed, g_ptr_array_unref);
606}
607
608/**
609 * gsk_gl_driver_end_frame:
610 * @self: a `GskGLDriver`
611 *
612 * Clean up resources from drawing the current frame.
613 *
614 * Temporary resources used while drawing will be released.
615 */
616void
617gsk_gl_driver_end_frame (GskGLDriver *self)
618{
619 g_return_if_fail (GSK_IS_GL_DRIVER (self));
620 g_return_if_fail (self->in_frame == TRUE);
621
622 gsk_gl_command_queue_make_current (self: self->command_queue);
623 gsk_gl_command_queue_end_frame (self: self->command_queue);
624
625 self->in_frame = FALSE;
626}
627
628/**
629 * gsk_gl_driver_after_frame:
630 * @self: a `GskGLDriver`
631 *
632 * This function does post-frame cleanup operations.
633 *
634 * To reduce the chances of blocking on the driver it is performed
635 * after the frame has swapped buffers.
636 */
637void
638gsk_gl_driver_after_frame (GskGLDriver *self)
639{
640 g_return_if_fail (GSK_IS_GL_DRIVER (self));
641 g_return_if_fail (self->in_frame == FALSE);
642
643 /* Release any render targets (possibly adding them to
644 * self->autorelease_framebuffers) so we can release the FBOs immediately
645 * afterwards.
646 */
647 while (self->render_targets->len > 0)
648 {
649 GskGLRenderTarget *render_target = g_ptr_array_index (self->render_targets, self->render_targets->len - 1);
650
651 gsk_gl_driver_autorelease_framebuffer (self, framebuffer_id: render_target->framebuffer_id);
652 gsk_gl_driver_autorelease_texture (self, texture_id: render_target->texture_id);
653 g_slice_free (GskGLRenderTarget, render_target);
654
655 self->render_targets->len--;
656 }
657
658 /* Now that we have collected render targets, release all the FBOs */
659 if (self->autorelease_framebuffers->len > 0)
660 {
661 glDeleteFramebuffers (self->autorelease_framebuffers->len,
662 (GLuint *)(gpointer)self->autorelease_framebuffers->data);
663 self->autorelease_framebuffers->len = 0;
664 }
665
666 /* Release any cached textures we used during the frame */
667 if (self->texture_pool->len > 0)
668 {
669 glDeleteTextures (self->texture_pool->len,
670 (GLuint *)(gpointer)self->texture_pool->data);
671 self->texture_pool->len = 0;
672 }
673
674 /* Reset command queue to our shared queue incase we have operations
675 * that need to be processed outside of a frame (such as callbacks
676 * from external systems such as GDK).
677 */
678 g_set_object (&self->command_queue, self->shared_command_queue);
679}
680
681GdkGLContext *
682gsk_gl_driver_get_context (GskGLDriver *self)
683{
684 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
685 g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), NULL);
686
687 return gsk_gl_command_queue_get_context (self: self->command_queue);
688}
689
690/**
691 * gsk_gl_driver_cache_texture:
692 * @self: a `GskGLDriver`
693 * @key: the key for the texture
694 * @texture_id: the id of the texture to be cached
695 *
696 * Inserts @texture_id into the texture cache using @key.
697 *
698 * Textures can be looked up by @key after calling this function using
699 * gsk_gl_driver_lookup_texture().
700 *
701 * Textures that have not been used within a number of frames will be
702 * purged from the texture cache automatically.
703 */
704void
705gsk_gl_driver_cache_texture (GskGLDriver *self,
706 const GskTextureKey *key,
707 guint texture_id)
708{
709 GskTextureKey *k;
710
711 g_assert (GSK_IS_GL_DRIVER (self));
712 g_assert (key != NULL);
713 g_assert (texture_id > 0);
714 g_assert (g_hash_table_contains (self->textures, GUINT_TO_POINTER (texture_id)));
715
716 k = g_memdup (mem: key, byte_size: sizeof *key);
717
718 g_hash_table_insert (hash_table: self->key_to_texture_id, key: k, GUINT_TO_POINTER (texture_id));
719 g_hash_table_insert (hash_table: self->texture_id_to_key, GUINT_TO_POINTER (texture_id), value: k);
720}
721
722/**
723 * gsk_gl_driver_load_texture:
724 * @self: a `GdkTexture`
725 * @texture: a `GdkTexture`
726 * @min_filter: GL_NEAREST or GL_LINEAR
727 * @mag_filter: GL_NEAREST or GL_LINEAR
728 *
729 * Loads a `GdkTexture` by uploading the contents to the GPU when
730 * necessary. If @texture is a `GdkGLTexture`, it can be used without
731 * uploading contents to the GPU.
732 *
733 * If the texture has already been uploaded and not yet released
734 * from cache, this function returns that texture id without further
735 * work.
736 *
737 * If the texture has not been used for a number of frames, it will
738 * be removed from cache.
739 *
740 * There is no need to release the resulting texture identifier after
741 * using it. It will be released automatically.
742 *
743 * Returns: a texture identifier
744 */
745guint
746gsk_gl_driver_load_texture (GskGLDriver *self,
747 GdkTexture *texture,
748 int min_filter,
749 int mag_filter)
750{
751 GdkGLContext *context;
752 GdkMemoryTexture *downloaded_texture;
753 GskGLTexture *t;
754 guint texture_id;
755 int height;
756 int width;
757 int format;
758
759 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
760 g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
761 g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), 0);
762
763 context = self->command_queue->context;
764
765 format = GL_RGBA8;
766
767 if (GDK_IS_GL_TEXTURE (texture))
768 {
769 GdkGLTexture *gl_texture = (GdkGLTexture *) texture;
770 GdkGLContext *texture_context = gdk_gl_texture_get_context (self: gl_texture);
771
772 if (gdk_gl_context_is_shared (self: context, other: texture_context))
773 {
774 /* A GL texture from the same GL context is a simple task... */
775 return gdk_gl_texture_get_id (self: gl_texture);
776 }
777 else
778 {
779 downloaded_texture = gdk_memory_texture_from_texture (texture, format: gdk_texture_get_format (self: texture));
780 }
781 }
782 else
783 {
784 if ((t = gdk_texture_get_render_data (self: texture, key: self)))
785 {
786 if (t->min_filter == min_filter && t->mag_filter == mag_filter)
787 return t->texture_id;
788 }
789
790 downloaded_texture = gdk_memory_texture_from_texture (texture, format: gdk_texture_get_format (self: texture));
791 }
792
793 /* The download_texture() call may have switched the GL context. Make sure
794 * the right context is at work again. */
795 gdk_gl_context_make_current (context);
796
797 width = gdk_texture_get_width (texture);
798 height = gdk_texture_get_height (texture);
799 texture_id = gsk_gl_command_queue_upload_texture (self: self->command_queue,
800 GDK_TEXTURE (downloaded_texture),
801 min_filter,
802 mag_filter);
803
804 t = gsk_gl_texture_new (texture_id,
805 width, height, format, min_filter, mag_filter,
806 frame_id: self->current_frame_id);
807
808 g_hash_table_insert (hash_table: self->textures, GUINT_TO_POINTER (texture_id), value: t);
809
810 if (gdk_texture_set_render_data (self: texture, key: self, data: t, notify: gsk_gl_texture_destroyed))
811 t->user = texture;
812
813 gdk_gl_context_label_object_printf (context, GL_TEXTURE, name: t->texture_id,
814 format: "GdkTexture<%p> %d", texture, t->texture_id);
815
816 g_clear_object (&downloaded_texture);
817
818 return texture_id;
819}
820
821/**
822 * gsk_gl_driver_create_texture:
823 * @self: a `GskGLDriver`
824 * @width: the width of the texture
825 * @height: the height of the texture
826 * @format: format for the texture
827 * @min_filter: GL_NEAREST or GL_LINEAR
828 * @mag_filter: GL_NEAREST or GL_FILTER
829 *
830 * Creates a new texture immediately that can be used by the caller
831 * to upload data, map to a framebuffer, or other uses which may
832 * modify the texture immediately.
833 *
834 * Typical examples for @format are GL_RGBA8, GL_RGBA16F or GL_RGBA32F.
835 *
836 * Use gsk_gl_driver_release_texture() to release this texture back into
837 * the pool so it may be reused later in the pipeline.
838 *
839 * Returns: a `GskGLTexture` which can be returned to the pool with
840 * gsk_gl_driver_release_texture().
841 */
842GskGLTexture *
843gsk_gl_driver_create_texture (GskGLDriver *self,
844 float width,
845 float height,
846 int format,
847 int min_filter,
848 int mag_filter)
849{
850 GskGLTexture *texture;
851 guint texture_id;
852
853 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
854
855 texture_id = gsk_gl_command_queue_create_texture (self: self->command_queue,
856 width, height,
857 format,
858 min_filter, mag_filter);
859 texture = gsk_gl_texture_new (texture_id,
860 width, height,
861 format,
862 min_filter, mag_filter,
863 frame_id: self->current_frame_id);
864 g_hash_table_insert (hash_table: self->textures,
865 GUINT_TO_POINTER (texture->texture_id),
866 value: texture);
867
868 return texture;
869}
870
871/**
872 * gsk_gl_driver_release_texture:
873 * @self: a `GskGLDriver`
874 * @texture: a `GskGLTexture`
875 *
876 * Releases @texture back into the pool so that it can be used later
877 * in the command stream by future batches. This helps reduce VRAM
878 * usage on the GPU.
879 *
880 * When the frame has completed, pooled textures will be released
881 * to free additional VRAM back to the system.
882 */
883void
884gsk_gl_driver_release_texture (GskGLDriver *self,
885 GskGLTexture *texture)
886{
887 guint texture_id;
888
889 g_assert (GSK_IS_GL_DRIVER (self));
890 g_assert (texture != NULL);
891
892 texture_id = texture->texture_id;
893 texture->texture_id = 0;
894 gsk_gl_texture_free (texture);
895
896 if (texture_id > 0)
897 remove_texture_key_for_id (self, texture_id);
898
899 g_hash_table_steal (hash_table: self->textures, GUINT_TO_POINTER (texture_id));
900 gsk_gl_driver_autorelease_texture (self, texture_id);
901}
902
903/**
904 * gsk_gl_driver_create_render_target:
905 * @self: a `GskGLDriver`
906 * @width: the width for the render target
907 * @height: the height for the render target
908 * @format: the format to use
909 * @min_filter: the min filter to use for the texture
910 * @mag_filter: the mag filter to use for the texture
911 * @out_render_target: (out): a location for the render target
912 *
913 * Creates a new render target which contains a framebuffer and a texture
914 * bound to that framebuffer of the size @width x @height and using the
915 * appropriate filters.
916 *
917 * Typical examples for @format are GK_RGBA8, GL_RGBA16F or GL_RGBA32F.
918 *
919 * Use gsk_gl_driver_release_render_target() when you are finished with
920 * the render target to release it. You may steal the texture from the
921 * render target when releasing it.
922 *
923 * Returns: %TRUE if successful; otherwise %FALSE and @out_fbo_id and
924 * @out_texture_id are undefined.
925 */
926gboolean
927gsk_gl_driver_create_render_target (GskGLDriver *self,
928 int width,
929 int height,
930 int format,
931 int min_filter,
932 int mag_filter,
933 GskGLRenderTarget **out_render_target)
934{
935 guint framebuffer_id;
936 guint texture_id;
937
938 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), FALSE);
939 g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), FALSE);
940 g_return_val_if_fail (out_render_target != NULL, FALSE);
941
942#if 0
943 if (self->render_targets->len > 0)
944 {
945 for (guint i = self->render_targets->len; i > 0; i--)
946 {
947 GskGLRenderTarget *render_target = g_ptr_array_index (self->render_targets, i-1);
948
949 if (render_target->width == width &&
950 render_target->height == height &&
951 render_target->min_filter == min_filter &&
952 render_target->mag_filter == mag_filter)
953 {
954 *out_render_target = g_ptr_array_steal_index_fast (self->render_targets, i-1);
955 return TRUE;
956 }
957 }
958 }
959#endif
960
961 if (gsk_gl_command_queue_create_render_target (self: self->command_queue,
962 width, height,
963 format,
964 min_filter, mag_filter,
965 out_fbo_id: &framebuffer_id, out_texture_id: &texture_id))
966 {
967 GskGLRenderTarget *render_target;
968
969 render_target = g_slice_new0 (GskGLRenderTarget);
970 render_target->min_filter = min_filter;
971 render_target->mag_filter = mag_filter;
972 render_target->format = format;
973 render_target->width = width;
974 render_target->height = height;
975 render_target->framebuffer_id = framebuffer_id;
976 render_target->texture_id = texture_id;
977
978 *out_render_target = render_target;
979
980 return TRUE;
981 }
982
983 *out_render_target = NULL;
984
985 return FALSE;
986}
987
988/**
989 * gsk_gl_driver_release_render_target:
990 * @self: a `GskGLDriver`
991 * @render_target: a `GskGLRenderTarget` created with
992 * gsk_gl_driver_create_render_target().
993 * @release_texture: if the texture should also be released
994 *
995 * Releases a render target that was previously created. An attempt may
996 * be made to cache the render target so that future creations of render
997 * targets are performed faster.
998 *
999 * If @release_texture is %FALSE, the backing texture id is returned and
1000 * the framebuffer is released. Otherwise, both the texture and framebuffer
1001 * are released or cached until the end of the frame.
1002 *
1003 * This may be called when building the render job as the texture or
1004 * framebuffer will not be removed immediately.
1005 *
1006 * Returns: a texture id if @release_texture is %FALSE, otherwise zero.
1007 */
1008guint
1009gsk_gl_driver_release_render_target (GskGLDriver *self,
1010 GskGLRenderTarget *render_target,
1011 gboolean release_texture)
1012{
1013 guint texture_id;
1014
1015 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
1016 g_return_val_if_fail (render_target != NULL, 0);
1017
1018 if (release_texture)
1019 {
1020 texture_id = 0;
1021 g_ptr_array_add (array: self->render_targets, data: render_target);
1022 }
1023 else
1024 {
1025 GskGLTexture *texture;
1026
1027 texture_id = render_target->texture_id;
1028
1029 texture = gsk_gl_texture_new (texture_id: render_target->texture_id,
1030 width: render_target->width,
1031 height: render_target->height,
1032 format: render_target->format,
1033 min_filter: render_target->min_filter,
1034 mag_filter: render_target->mag_filter,
1035 frame_id: self->current_frame_id);
1036 g_hash_table_insert (hash_table: self->textures,
1037 GUINT_TO_POINTER (texture_id),
1038 g_steal_pointer (&texture));
1039
1040 gsk_gl_driver_autorelease_framebuffer (self, framebuffer_id: render_target->framebuffer_id);
1041 g_slice_free (GskGLRenderTarget, render_target);
1042
1043 }
1044
1045 return texture_id;
1046}
1047
1048/**
1049 * gsk_gl_driver_lookup_shader:
1050 * @self: a `GskGLDriver`
1051 * @shader: the shader to lookup or load
1052 * @error: a location for a `GError`
1053 *
1054 * Attepts to load @shader from the shader cache.
1055 *
1056 * If it has not been loaded, then it will compile the shader on demand.
1057 *
1058 * Returns: (nullable) (transfer none): a `GskGLShader`
1059 */
1060GskGLProgram *
1061gsk_gl_driver_lookup_shader (GskGLDriver *self,
1062 GskGLShader *shader,
1063 GError **error)
1064{
1065 GskGLProgram *program;
1066
1067 g_return_val_if_fail (self != NULL, NULL);
1068 g_return_val_if_fail (shader != NULL, NULL);
1069
1070 program = g_hash_table_lookup (hash_table: self->shader_cache, key: shader);
1071
1072 if (program == NULL)
1073 {
1074 const GskGLUniform *uniforms;
1075 GskGLCompiler *compiler;
1076 GBytes *suffix;
1077 int n_required_textures;
1078 int n_uniforms;
1079
1080 uniforms = gsk_gl_shader_get_uniforms (shader, n_uniforms: &n_uniforms);
1081 if (n_uniforms > GSK_GL_PROGRAM_MAX_CUSTOM_ARGS)
1082 {
1083 g_set_error (err: error,
1084 GDK_GL_ERROR,
1085 code: GDK_GL_ERROR_UNSUPPORTED_FORMAT,
1086 format: "Tried to use %d uniforms, while only %d is supported",
1087 n_uniforms,
1088 GSK_GL_PROGRAM_MAX_CUSTOM_ARGS);
1089 return NULL;
1090 }
1091
1092 n_required_textures = gsk_gl_shader_get_n_textures (shader);
1093 if (n_required_textures > GSK_GL_PROGRAM_MAX_CUSTOM_TEXTURES)
1094 {
1095 g_set_error (err: error,
1096 GDK_GL_ERROR,
1097 code: GDK_GL_ERROR_UNSUPPORTED_FORMAT,
1098 format: "Tried to use %d textures, while only %d is supported",
1099 n_required_textures,
1100 GSK_GL_PROGRAM_MAX_CUSTOM_TEXTURES);
1101 return NULL;
1102 }
1103
1104 compiler = gsk_gl_compiler_new (driver: self, FALSE);
1105 suffix = gsk_gl_shader_get_source (shader);
1106
1107 gsk_gl_compiler_set_preamble_from_resource (self: compiler,
1108 kind: GSK_GL_COMPILER_ALL,
1109 resource_path: "/org/gtk/libgsk/gl/preamble.glsl");
1110 gsk_gl_compiler_set_preamble_from_resource (self: compiler,
1111 kind: GSK_GL_COMPILER_VERTEX,
1112 resource_path: "/org/gtk/libgsk/gl/preamble.vs.glsl");
1113 gsk_gl_compiler_set_preamble_from_resource (self: compiler,
1114 kind: GSK_GL_COMPILER_FRAGMENT,
1115 resource_path: "/org/gtk/libgsk/gl/preamble.fs.glsl");
1116 gsk_gl_compiler_set_source_from_resource (self: compiler,
1117 kind: GSK_GL_COMPILER_ALL,
1118 resource_path: "/org/gtk/libgsk/gl/custom.glsl");
1119 gsk_gl_compiler_set_suffix (self: compiler, kind: GSK_GL_COMPILER_FRAGMENT, suffix_bytes: suffix);
1120
1121 /* Setup attributes that are provided via VBO */
1122 gsk_gl_compiler_bind_attribute (self: compiler, name: "aPosition", location: 0);
1123 gsk_gl_compiler_bind_attribute (self: compiler, name: "aUv", location: 1);
1124 gsk_gl_compiler_bind_attribute (self: compiler, name: "aColor", location: 2);
1125 gsk_gl_compiler_bind_attribute (self: compiler, name: "aColor2", location: 3);
1126
1127 if ((program = gsk_gl_compiler_compile (self: compiler, NULL, clip: "", error)))
1128 {
1129 gboolean have_alpha;
1130
1131 gsk_gl_program_add_uniform (self: program, name: "u_source", key: UNIFORM_SHARED_SOURCE);
1132 gsk_gl_program_add_uniform (self: program, name: "u_clip_rect", key: UNIFORM_SHARED_CLIP_RECT);
1133 gsk_gl_program_add_uniform (self: program, name: "u_viewport", key: UNIFORM_SHARED_VIEWPORT);
1134 gsk_gl_program_add_uniform (self: program, name: "u_projection", key: UNIFORM_SHARED_PROJECTION);
1135 gsk_gl_program_add_uniform (self: program, name: "u_modelview", key: UNIFORM_SHARED_MODELVIEW);
1136 have_alpha = gsk_gl_program_add_uniform (self: program, name: "u_alpha", key: UNIFORM_SHARED_ALPHA);
1137
1138 gsk_gl_program_add_uniform (self: program, name: "u_size", key: UNIFORM_CUSTOM_SIZE);
1139 gsk_gl_program_add_uniform (self: program, name: "u_texture1", key: UNIFORM_CUSTOM_TEXTURE1);
1140 gsk_gl_program_add_uniform (self: program, name: "u_texture2", key: UNIFORM_CUSTOM_TEXTURE2);
1141 gsk_gl_program_add_uniform (self: program, name: "u_texture3", key: UNIFORM_CUSTOM_TEXTURE3);
1142 gsk_gl_program_add_uniform (self: program, name: "u_texture4", key: UNIFORM_CUSTOM_TEXTURE4);
1143
1144 /* Custom arguments (max is 8) */
1145 for (guint i = 0; i < n_uniforms; i++)
1146 gsk_gl_program_add_uniform (self: program, name: uniforms[i].name, key: UNIFORM_CUSTOM_ARG0+i);
1147
1148 gsk_gl_program_uniforms_added (self: program, TRUE);
1149
1150 if (have_alpha)
1151 gsk_gl_program_set_uniform1f (self: program, key: UNIFORM_SHARED_ALPHA, stamp: 0, value0: 1.0f);
1152
1153 g_hash_table_insert (hash_table: self->shader_cache, key: shader, value: program);
1154 g_object_weak_ref (G_OBJECT (shader),
1155 notify: gsk_gl_driver_shader_weak_cb,
1156 data: self);
1157 }
1158
1159 g_object_unref (object: compiler);
1160 }
1161
1162 return program;
1163}
1164
1165#ifdef G_ENABLE_DEBUG
1166static void
1167write_atlas_to_png (GskGLDriver *driver,
1168 GskGLTextureAtlas *atlas,
1169 const char *filename)
1170{
1171 GdkTexture *texture;
1172
1173 texture = gdk_gl_texture_new (context: gsk_gl_driver_get_context (self: driver),
1174 id: atlas->texture_id,
1175 width: atlas->width, height: atlas->height,
1176 NULL, NULL);
1177 gdk_texture_save_to_png (texture, filename);
1178 g_object_unref (object: texture);
1179}
1180
1181void
1182gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
1183 const char *directory)
1184{
1185 g_return_if_fail (GSK_IS_GL_DRIVER (self));
1186
1187 if (directory == NULL)
1188 directory = ".";
1189
1190 for (guint i = 0; i < self->atlases->len; i++)
1191 {
1192 GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
1193 char *filename = g_strdup_printf (format: "%s%sframe-%d-atlas-%d.png",
1194 directory,
1195 G_DIR_SEPARATOR_S,
1196 (int)self->current_frame_id,
1197 atlas->texture_id);
1198 write_atlas_to_png (driver: self, atlas, filename);
1199 g_free (mem: filename);
1200 }
1201}
1202#endif
1203
1204GskGLCommandQueue *
1205gsk_gl_driver_create_command_queue (GskGLDriver *self,
1206 GdkGLContext *context)
1207{
1208 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
1209 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1210
1211 return gsk_gl_command_queue_new (context, uniforms: self->shared_command_queue->uniforms);
1212}
1213
1214void
1215gsk_gl_driver_add_texture_slices (GskGLDriver *self,
1216 GdkTexture *texture,
1217 GskGLTextureSlice **out_slices,
1218 guint *out_n_slices)
1219{
1220 int max_texture_size;
1221 GskGLTextureSlice *slices;
1222 GskGLTexture *t;
1223 guint n_slices;
1224 guint cols;
1225 guint rows;
1226 int tex_width;
1227 int tex_height;
1228 int x = 0, y = 0;
1229 GdkMemoryTexture *memtex;
1230
1231 g_assert (GSK_IS_GL_DRIVER (self));
1232 g_assert (GDK_IS_TEXTURE (texture));
1233 g_assert (out_slices != NULL);
1234 g_assert (out_n_slices != NULL);
1235
1236 /* XXX: Too much? */
1237 max_texture_size = self->command_queue->max_texture_size / 4;
1238
1239 tex_width = texture->width;
1240 tex_height = texture->height;
1241 cols = (texture->width / max_texture_size) + 1;
1242 rows = (texture->height / max_texture_size) + 1;
1243
1244 if ((t = gdk_texture_get_render_data (self: texture, key: self)))
1245 {
1246 *out_slices = t->slices;
1247 *out_n_slices = t->n_slices;
1248 return;
1249 }
1250
1251 n_slices = cols * rows;
1252 slices = g_new0 (GskGLTextureSlice, n_slices);
1253 memtex = gdk_memory_texture_from_texture (texture,
1254 format: gdk_texture_get_format (self: texture));
1255
1256 for (guint col = 0; col < cols; col ++)
1257 {
1258 int slice_width = MIN (max_texture_size, texture->width - x);
1259
1260 for (guint row = 0; row < rows; row ++)
1261 {
1262 int slice_height = MIN (max_texture_size, texture->height - y);
1263 int slice_index = (col * rows) + row;
1264 GdkTexture *subtex;
1265 guint texture_id;
1266
1267 subtex = gdk_memory_texture_new_subtexture (texture: memtex,
1268 x, y,
1269 width: slice_width, height: slice_height);
1270 texture_id = gsk_gl_command_queue_upload_texture (self: self->command_queue,
1271 texture: subtex,
1272 GL_NEAREST, GL_NEAREST);
1273 g_object_unref (object: subtex);
1274
1275 slices[slice_index].rect.x = x;
1276 slices[slice_index].rect.y = y;
1277 slices[slice_index].rect.width = slice_width;
1278 slices[slice_index].rect.height = slice_height;
1279 slices[slice_index].texture_id = texture_id;
1280
1281 y += slice_height;
1282 }
1283
1284 y = 0;
1285 x += slice_width;
1286 }
1287
1288 g_object_unref (object: memtex);
1289
1290 /* Allocate one Texture for the entire thing. */
1291 t = gsk_gl_texture_new (texture_id: 0,
1292 width: tex_width, height: tex_height,
1293 GL_RGBA8,
1294 GL_NEAREST, GL_NEAREST,
1295 frame_id: self->current_frame_id);
1296
1297 /* Use gsk_gl_texture_free() as destroy notify here since we are
1298 * not inserting this GskGLTexture into self->textures!
1299 */
1300 gdk_texture_set_render_data (self: texture, key: self, data: t,
1301 notify: (GDestroyNotify)gsk_gl_texture_free);
1302
1303 t->slices = *out_slices = slices;
1304 t->n_slices = *out_n_slices = n_slices;
1305}
1306
1307GskGLTexture *
1308gsk_gl_driver_mark_texture_permanent (GskGLDriver *self,
1309 guint texture_id)
1310{
1311 GskGLTexture *t;
1312
1313 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
1314 g_return_val_if_fail (texture_id > 0, NULL);
1315
1316 if ((t = g_hash_table_lookup (hash_table: self->textures, GUINT_TO_POINTER (texture_id))))
1317 t->permanent = TRUE;
1318
1319 return t;
1320}
1321
1322void
1323gsk_gl_driver_release_texture_by_id (GskGLDriver *self,
1324 guint texture_id)
1325{
1326 GskGLTexture *texture;
1327
1328 g_return_if_fail (GSK_IS_GL_DRIVER (self));
1329 g_return_if_fail (texture_id > 0);
1330
1331 remove_texture_key_for_id (self, texture_id);
1332
1333 if ((texture = g_hash_table_lookup (hash_table: self->textures, GUINT_TO_POINTER (texture_id))))
1334 gsk_gl_driver_release_texture (self, texture);
1335}
1336
1337typedef struct _GskGLTextureState
1338{
1339 GdkGLContext *context;
1340 GLuint texture_id;
1341} GskGLTextureState;
1342
1343static void
1344create_texture_from_texture_destroy (gpointer data)
1345{
1346 GskGLTextureState *state = data;
1347
1348 g_assert (state != NULL);
1349 g_assert (GDK_IS_GL_CONTEXT (state->context));
1350
1351 gdk_gl_context_make_current (context: state->context);
1352 glDeleteTextures (1, &state->texture_id);
1353 g_clear_object (&state->context);
1354 g_slice_free (GskGLTextureState, state);
1355}
1356
1357GdkTexture *
1358gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
1359 guint texture_id)
1360{
1361 GskGLTextureState *state;
1362 GskGLTexture *texture;
1363 int width, height;
1364
1365 g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
1366 g_return_val_if_fail (self->command_queue != NULL, NULL);
1367 g_return_val_if_fail (GDK_IS_GL_CONTEXT (self->command_queue->context), NULL);
1368 g_return_val_if_fail (texture_id > 0, NULL);
1369 g_return_val_if_fail (!g_hash_table_contains (self->texture_id_to_key, GUINT_TO_POINTER (texture_id)), NULL);
1370
1371 /* We must be tracking this texture_id already to use it */
1372 if (!(texture = g_hash_table_lookup (hash_table: self->textures, GUINT_TO_POINTER (texture_id))))
1373 g_return_val_if_reached (NULL);
1374
1375 state = g_slice_new0 (GskGLTextureState);
1376 state->texture_id = texture_id;
1377 state->context = g_object_ref (self->command_queue->context);
1378
1379 g_hash_table_steal (hash_table: self->textures, GUINT_TO_POINTER (texture_id));
1380
1381 width = texture->width;
1382 height = texture->height;
1383
1384 texture->texture_id = 0;
1385 gsk_gl_texture_free (texture);
1386
1387 return gdk_gl_texture_new (context: self->command_queue->context,
1388 id: texture_id,
1389 width,
1390 height,
1391 destroy: create_texture_from_texture_destroy,
1392 data: state);
1393}
1394

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