1 /* gskngltexturepool.c
2  *
3  * Copyright 2020 Christian Hergert <chergert@redhat.com>
4  *
5  * This file is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; either version 2.1 of the License, or (at your option)
8  * any later version.
9  *
10  * This file is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
13  * License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: LGPL-2.1-or-later
19  */
20 
21 #include "config.h"
22 
23 #include <gdk/gdktextureprivate.h>
24 
25 #include "gskngltexturepoolprivate.h"
26 #include "ninesliceprivate.h"
27 
28 void
gsk_ngl_texture_free(GskNglTexture * texture)29 gsk_ngl_texture_free (GskNglTexture *texture)
30 {
31   if (texture != NULL)
32     {
33       g_assert (texture->link.prev == NULL);
34       g_assert (texture->link.next == NULL);
35 
36       if (texture->user)
37         g_clear_pointer (&texture->user, gdk_texture_clear_render_data);
38 
39       if (texture->texture_id != 0)
40         {
41           glDeleteTextures (1, &texture->texture_id);
42           texture->texture_id = 0;
43         }
44 
45       for (guint i = 0; i < texture->n_slices; i++)
46         {
47           glDeleteTextures (1, &texture->slices[i].texture_id);
48           texture->slices[i].texture_id = 0;
49         }
50 
51       g_clear_pointer (&texture->slices, g_free);
52       g_clear_pointer (&texture->nine_slice, g_free);
53 
54       g_slice_free (GskNglTexture, texture);
55     }
56 }
57 
58 void
gsk_ngl_texture_pool_init(GskNglTexturePool * self)59 gsk_ngl_texture_pool_init (GskNglTexturePool *self)
60 {
61   g_queue_init (&self->queue);
62 }
63 
64 void
gsk_ngl_texture_pool_clear(GskNglTexturePool * self)65 gsk_ngl_texture_pool_clear (GskNglTexturePool *self)
66 {
67   guint *free_me = NULL;
68   guint *texture_ids;
69   guint i = 0;
70 
71   if G_LIKELY (self->queue.length <= 1024)
72     texture_ids = g_newa (guint, self->queue.length);
73   else
74     texture_ids = free_me = g_new (guint, self->queue.length);
75 
76   while (self->queue.length > 0)
77     {
78       GskNglTexture *head = g_queue_peek_head (&self->queue);
79 
80       g_queue_unlink (&self->queue, &head->link);
81 
82       texture_ids[i++] = head->texture_id;
83       head->texture_id = 0;
84 
85       gsk_ngl_texture_free (head);
86     }
87 
88   g_assert (self->queue.length == 0);
89 
90   if (i > 0)
91     glDeleteTextures (i, texture_ids);
92 
93   g_free (free_me);
94 }
95 
96 void
gsk_ngl_texture_pool_put(GskNglTexturePool * self,GskNglTexture * texture)97 gsk_ngl_texture_pool_put (GskNglTexturePool *self,
98                           GskNglTexture     *texture)
99 {
100   g_assert (self != NULL);
101   g_assert (texture != NULL);
102   g_assert (texture->user == NULL);
103   g_assert (texture->link.prev == NULL);
104   g_assert (texture->link.next == NULL);
105   g_assert (texture->link.data == texture);
106 
107   if (texture->permanent)
108     gsk_ngl_texture_free (texture);
109   else
110     g_queue_push_tail_link (&self->queue, &texture->link);
111 }
112 
113 GskNglTexture *
gsk_ngl_texture_pool_get(GskNglTexturePool * self,int width,int height,int min_filter,int mag_filter)114 gsk_ngl_texture_pool_get (GskNglTexturePool *self,
115                           int                width,
116                           int                height,
117                           int                min_filter,
118                           int                mag_filter)
119 {
120   GskNglTexture *texture;
121 
122   g_assert (self != NULL);
123 
124   texture = g_slice_new0 (GskNglTexture);
125   texture->link.data = texture;
126   texture->min_filter = min_filter;
127   texture->mag_filter = mag_filter;
128 
129   glGenTextures (1, &texture->texture_id);
130 
131   glActiveTexture (GL_TEXTURE0);
132   glBindTexture (GL_TEXTURE_2D, texture->texture_id);
133   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
134   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
135   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
136   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
137 
138   if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
139     glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
140   else
141     glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
142 
143   glBindTexture (GL_TEXTURE_2D, 0);
144 
145   return texture;
146 }
147 
148 GskNglTexture *
gsk_ngl_texture_new(guint texture_id,int width,int height,int min_filter,int mag_filter,gint64 frame_id)149 gsk_ngl_texture_new (guint  texture_id,
150                      int    width,
151                      int    height,
152                      int    min_filter,
153                      int    mag_filter,
154                      gint64 frame_id)
155 {
156   GskNglTexture *texture;
157 
158   texture = g_slice_new0 (GskNglTexture);
159   texture->texture_id = texture_id;
160   texture->link.data = texture;
161   texture->min_filter = min_filter;
162   texture->mag_filter = mag_filter;
163   texture->width = width;
164   texture->height = height;
165   texture->last_used_in_frame = frame_id;
166 
167   return texture;
168 }
169 
170 const GskNglTextureNineSlice *
gsk_ngl_texture_get_nine_slice(GskNglTexture * texture,const GskRoundedRect * outline,float extra_pixels_x,float extra_pixels_y)171 gsk_ngl_texture_get_nine_slice (GskNglTexture        *texture,
172                                 const GskRoundedRect *outline,
173                                 float                 extra_pixels_x,
174                                 float                 extra_pixels_y)
175 {
176   g_assert (texture != NULL);
177   g_assert (outline != NULL);
178 
179   if G_UNLIKELY (texture->nine_slice == NULL)
180     {
181       texture->nine_slice = g_new0 (GskNglTextureNineSlice, 9);
182 
183       nine_slice_rounded_rect (texture->nine_slice, outline);
184       nine_slice_grow (texture->nine_slice, extra_pixels_x, extra_pixels_y);
185       nine_slice_to_texture_coords (texture->nine_slice, texture->width, texture->height);
186     }
187 
188   return texture->nine_slice;
189 }
190