1 #include "config.h"
2
3 #include "gskglrendererprivate.h"
4
5 #include "gskdebugprivate.h"
6 #include "gskenums.h"
7 #include "gskgldriverprivate.h"
8 #include "gskglprofilerprivate.h"
9 #include "gskprofilerprivate.h"
10 #include "gskrendererprivate.h"
11 #include "gskrendernodeprivate.h"
12 #include "gsktransformprivate.h"
13 #include "gskglshaderbuilderprivate.h"
14 #include "gskglglyphcacheprivate.h"
15 #include "gskgliconcacheprivate.h"
16 #include "gskglrenderopsprivate.h"
17 #include "gskcairoblurprivate.h"
18 #include "gskglshadowcacheprivate.h"
19 #include "gskglnodesampleprivate.h"
20 #include "gsktransform.h"
21 #include "glutilsprivate.h"
22 #include "gskglshaderprivate.h"
23
24 #include "gskprivate.h"
25
26 #include "gdk/gdkgltextureprivate.h"
27 #include "gdk/gdkglcontextprivate.h"
28 #include "gdk/gdkprofilerprivate.h"
29 #include "gdk/gdkrgbaprivate.h"
30
31 #include <epoxy/gl.h>
32
33 #define SHADER_VERSION_GLES 100
34 #define SHADER_VERSION_GL2_LEGACY 110
35 #define SHADER_VERSION_GL3_LEGACY 130
36 #define SHADER_VERSION_GL3 150
37
38 #define ORTHO_NEAR_PLANE -10000
39 #define ORTHO_FAR_PLANE 10000
40
41 #define DEBUG_OPS 0
42
43 #define SHADOW_EXTRA_SIZE 4
44
45 #if DEBUG_OPS
46 #define OP_PRINT(format, ...) g_print(format, ## __VA_ARGS__)
47 #else
48 #define OP_PRINT(format, ...)
49 #endif
50
51 #define INIT_PROGRAM_UNIFORM_LOCATION(program_name, uniform_basename) \
52 G_STMT_START{\
53 programs->program_name ## _program.program_name.uniform_basename ## _location = \
54 glGetUniformLocation(programs->program_name ## _program.id, "u_" #uniform_basename);\
55 if (programs->program_name ## _program.program_name.uniform_basename ## _location == -1) \
56 { \
57 g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED, \
58 "Failed to find variable \"u_%s\" in shader program \"%s\"", #uniform_basename, #program_name); \
59 g_clear_pointer (&programs, gsk_gl_renderer_programs_unref); \
60 goto out; \
61 } \
62 }G_STMT_END
63
64 #define INIT_COMMON_UNIFORM_LOCATION(program_ptr, uniform_basename) \
65 G_STMT_START{\
66 program_ptr->uniform_basename ## _location = \
67 glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\
68 }G_STMT_END
69
70 static Program *gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self,
71 GskGLShader *shader);
72 static Program *gsk_gl_renderer_create_custom_program (GskGLRenderer *self,
73 GskGLShader *shader);
74
75 typedef enum
76 {
77 FORCE_OFFSCREEN = 1 << 0,
78 RESET_CLIP = 1 << 1,
79 DUMP_FRAMEBUFFER = 1 << 3,
80 NO_CACHE_PLZ = 1 << 5,
81 LINEAR_FILTER = 1 << 6,
82 } OffscreenFlags;
83
84 static inline void
init_full_texture_region(TextureRegion * r,int texture_id)85 init_full_texture_region (TextureRegion *r,
86 int texture_id)
87 {
88 r->texture_id = texture_id;
89 r->x = 0;
90 r->y = 0;
91 r->x2 = 1;
92 r->y2 = 1;
93 }
94
95 static void G_GNUC_UNUSED
print_render_node_tree(GskRenderNode * root,int level)96 print_render_node_tree (GskRenderNode *root, int level)
97 {
98 #define INDENT 4
99 const guint type = gsk_render_node_get_node_type (root);
100 guint i;
101
102 switch (type)
103 {
104 case GSK_CONTAINER_NODE:
105 g_print ("%*s Container\n", level * INDENT, " ");
106 for (i = 0; i < gsk_container_node_get_n_children (root); i++)
107 print_render_node_tree (gsk_container_node_get_child (root, i), level + 1);
108 break;
109
110 case GSK_TRANSFORM_NODE:
111 g_print ("%*s Transform\n", level * INDENT, " ");
112 print_render_node_tree (gsk_transform_node_get_child (root), level + 1);
113 break;
114
115 case GSK_COLOR_MATRIX_NODE:
116 g_print ("%*s Color Matrix\n", level * INDENT, " ");
117 print_render_node_tree (gsk_color_matrix_node_get_child (root), level + 1);
118 break;
119
120 case GSK_CROSS_FADE_NODE:
121 g_print ("%*s Crossfade(%.2f)\n", level * INDENT, " ",
122 gsk_cross_fade_node_get_progress (root));
123 print_render_node_tree (gsk_cross_fade_node_get_start_child (root), level + 1);
124 print_render_node_tree (gsk_cross_fade_node_get_end_child (root), level + 1);
125 break;
126
127 case GSK_TEXT_NODE:
128 g_print ("%*s Text\n", level * INDENT, " ");
129 break;
130
131 case GSK_COLOR_NODE:
132 g_print ("%*s Color %s\n", level * INDENT, " ", gdk_rgba_to_string (gsk_color_node_get_color (root)));
133 break;
134
135 case GSK_SHADOW_NODE:
136 g_print ("%*s Shadow\n", level * INDENT, " ");
137 print_render_node_tree (gsk_shadow_node_get_child (root), level + 1);
138 break;
139
140 case GSK_GL_SHADER_NODE:
141 g_print ("%*s GL Shader\n", level * INDENT, " ");
142 for (i = 0; i < gsk_gl_shader_node_get_n_children (root); i++)
143 print_render_node_tree (gsk_gl_shader_node_get_child (root, i), level + 1);
144 break;
145
146 case GSK_TEXTURE_NODE:
147 g_print ("%*s Texture %p\n", level * INDENT, " ", gsk_texture_node_get_texture (root));
148 break;
149
150 case GSK_DEBUG_NODE:
151 g_print ("%*s Debug: %s\n", level * INDENT, " ", gsk_debug_node_get_message (root));
152 print_render_node_tree (gsk_debug_node_get_child (root), level + 1);
153 break;
154
155 case GSK_CLIP_NODE:
156 g_print ("%*s Clip (%f, %f, %f, %f):\n", level * INDENT, " ",
157 root->bounds.origin.x, root->bounds.origin.y, root->bounds.size.width, root->bounds.size.height);
158 print_render_node_tree (gsk_clip_node_get_child (root), level + 1);
159 break;
160
161 default:
162 g_print ("%*s %s\n", level * INDENT, " ", g_type_name_from_instance ((GTypeInstance *) root));
163 }
164
165 #undef INDENT
166 }
167
168
169 static void G_GNUC_UNUSED
dump_framebuffer(const char * filename,int w,int h)170 dump_framebuffer (const char *filename, int w, int h)
171 {
172 int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w);
173 guchar *data = g_malloc (h * stride);
174 cairo_surface_t *s;
175
176 glReadPixels (0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, data);
177 s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, w, h, stride);
178 cairo_surface_write_to_png (s, filename);
179
180 cairo_surface_destroy (s);
181 g_free (data);
182 }
183
184 static void G_GNUC_UNUSED
dump_node(GskRenderNode * node,const char * filename)185 dump_node (GskRenderNode *node,
186 const char *filename)
187 {
188 const int surface_width = ceilf (node->bounds.size.width);
189 const int surface_height = ceilf (node->bounds.size.height);
190 cairo_surface_t *surface;
191 cairo_t *cr;
192
193 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
194 surface_width,
195 surface_height);
196
197 cr = cairo_create (surface);
198 cairo_save (cr);
199 cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y);
200 gsk_render_node_draw (node, cr);
201 cairo_restore (cr);
202 cairo_destroy (cr);
203
204 cairo_surface_write_to_png (surface, filename);
205 cairo_surface_destroy (surface);
206 }
207
208 static inline bool G_GNUC_PURE
node_is_invisible(const GskRenderNode * node)209 node_is_invisible (const GskRenderNode *node)
210 {
211 return node->bounds.size.width == 0.0f ||
212 node->bounds.size.height == 0.0f ||
213 isnan (node->bounds.size.width) ||
214 isnan (node->bounds.size.height);
215 }
216
217 static inline bool G_GNUC_PURE
graphene_rect_intersects(const graphene_rect_t * r1,const graphene_rect_t * r2)218 graphene_rect_intersects (const graphene_rect_t *r1,
219 const graphene_rect_t *r2)
220 {
221 /* Assume both rects are already normalized, as they usually are */
222 if (r1->origin.x > (r2->origin.x + r2->size.width) ||
223 (r1->origin.x + r1->size.width) < r2->origin.x)
224 return false;
225
226 if (r1->origin.y > (r2->origin.y + r2->size.height) ||
227 (r1->origin.y + r1->size.height) < r2->origin.y)
228 return false;
229
230 return true;
231 }
232
233 static inline bool G_GNUC_PURE
_graphene_rect_contains_rect(const graphene_rect_t * r1,const graphene_rect_t * r2)234 _graphene_rect_contains_rect (const graphene_rect_t *r1,
235 const graphene_rect_t *r2)
236 {
237 if (r2->origin.x >= r1->origin.x &&
238 (r2->origin.x + r2->size.width) <= (r1->origin.x + r1->size.width) &&
239 r2->origin.y >= r1->origin.y &&
240 (r2->origin.y + r2->size.height) <= (r1->origin.y + r1->size.height))
241 return true;
242
243 return false;
244 }
245
246 static inline bool G_GNUC_PURE
equal_texture_nodes(GskRenderNode * node1,GskRenderNode * node2)247 equal_texture_nodes (GskRenderNode *node1,
248 GskRenderNode *node2)
249 {
250 if (gsk_render_node_get_node_type (node1) != GSK_TEXTURE_NODE ||
251 gsk_render_node_get_node_type (node2) != GSK_TEXTURE_NODE)
252 return false;
253
254 if (gsk_texture_node_get_texture (node1) !=
255 gsk_texture_node_get_texture (node2))
256 return false;
257
258 return graphene_rect_equal (&node1->bounds, &node2->bounds);
259 }
260
261 static inline void
sort_border_sides(const GdkRGBA * colors,int * indices)262 sort_border_sides (const GdkRGBA *colors,
263 int *indices)
264 {
265 gboolean done[4] = {0, 0, 0, 0};
266 int i, k;
267 int cur = 0;
268
269 for (i = 0; i < 3; i ++)
270 {
271 if (done[i])
272 continue;
273
274 indices[cur] = i;
275 done[i] = TRUE;
276 cur ++;
277
278 for (k = i + 1; k < 4; k ++)
279 {
280 if (gdk_rgba_equal (&colors[k], &colors[i]))
281 {
282 indices[cur] = k;
283 done[k] = TRUE;
284 cur ++;
285 }
286 }
287
288 if (cur >= 4)
289 break;
290 }
291 }
292
293 static inline void
init_projection_matrix(graphene_matrix_t * out_proj,const graphene_rect_t * viewport)294 init_projection_matrix (graphene_matrix_t *out_proj,
295 const graphene_rect_t *viewport)
296 {
297 graphene_matrix_init_ortho (out_proj,
298 viewport->origin.x,
299 viewport->origin.x + viewport->size.width,
300 viewport->origin.y,
301 viewport->origin.y + viewport->size.height,
302 ORTHO_NEAR_PLANE,
303 ORTHO_FAR_PLANE);
304 graphene_matrix_scale (out_proj, 1, -1, 1);
305 }
306
307 static inline gboolean G_GNUC_PURE
color_matrix_modifies_alpha(GskRenderNode * node)308 color_matrix_modifies_alpha (GskRenderNode *node)
309 {
310 const graphene_matrix_t *matrix = gsk_color_matrix_node_get_color_matrix (node);
311 const graphene_vec4_t *offset = gsk_color_matrix_node_get_color_offset (node);
312 graphene_vec4_t row3;
313
314 if (graphene_vec4_get_w (offset) != 0.0f)
315 return TRUE;
316
317 graphene_matrix_get_row (matrix, 3, &row3);
318
319 return !graphene_vec4_equal (graphene_vec4_w_axis (), &row3);
320 }
321
322 static inline void
gsk_rounded_rect_shrink_to_minimum(GskRoundedRect * self)323 gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
324 {
325 self->bounds.size.width = MAX (self->corner[0].width + self->corner[1].width,
326 self->corner[3].width + self->corner[2].width);
327 self->bounds.size.height = MAX (self->corner[0].height + self->corner[3].height,
328 self->corner[1].height + self->corner[2].height);
329 }
330
331 static inline gboolean G_GNUC_PURE
node_supports_transform(GskRenderNode * node)332 node_supports_transform (GskRenderNode *node)
333 {
334 /* Some nodes can't handle non-trivial transforms without being
335 * rendered to a texture (e.g. rotated clips, etc.). Some however
336 * work just fine, mostly because they already draw their child
337 * to a texture and just render the texture manipulated in some
338 * way, think opacity or color matrix. */
339 const guint node_type = gsk_render_node_get_node_type (node);
340
341 switch (node_type)
342 {
343 case GSK_COLOR_NODE:
344 case GSK_OPACITY_NODE:
345 case GSK_COLOR_MATRIX_NODE:
346 case GSK_TEXTURE_NODE:
347 case GSK_CROSS_FADE_NODE:
348 case GSK_LINEAR_GRADIENT_NODE:
349 case GSK_DEBUG_NODE:
350 case GSK_TEXT_NODE:
351 return TRUE;
352
353 case GSK_TRANSFORM_NODE:
354 return node_supports_transform (gsk_transform_node_get_child (node));
355
356 default:
357 return FALSE;
358 }
359 return FALSE;
360 }
361
362 static inline void
load_vertex_data_with_region(GskQuadVertex vertex_data[GL_N_VERTICES],const graphene_rect_t * bounds,RenderOpBuilder * builder,const TextureRegion * r,gboolean flip_y)363 load_vertex_data_with_region (GskQuadVertex vertex_data[GL_N_VERTICES],
364 const graphene_rect_t *bounds,
365 RenderOpBuilder *builder,
366 const TextureRegion *r,
367 gboolean flip_y)
368 {
369 const float min_x = builder->dx + bounds->origin.x;
370 const float min_y = builder->dy + bounds->origin.y;
371 const float max_x = min_x + bounds->size.width;
372 const float max_y = min_y + bounds->size.height;
373 const float y1 = flip_y ? r->y2 : r->y;
374 const float y2 = flip_y ? r->y : r->y2;
375
376 vertex_data[0].position[0] = min_x;
377 vertex_data[0].position[1] = min_y;
378 vertex_data[0].uv[0] = r->x;
379 vertex_data[0].uv[1] = y1;
380
381 vertex_data[1].position[0] = min_x;
382 vertex_data[1].position[1] = max_y;
383 vertex_data[1].uv[0] = r->x;
384 vertex_data[1].uv[1] = y2;
385
386 vertex_data[2].position[0] = max_x;
387 vertex_data[2].position[1] = min_y;
388 vertex_data[2].uv[0] = r->x2;
389 vertex_data[2].uv[1] = y1;
390
391 vertex_data[3].position[0] = max_x;
392 vertex_data[3].position[1] = max_y;
393 vertex_data[3].uv[0] = r->x2;
394 vertex_data[3].uv[1] = y2;
395
396 vertex_data[4].position[0] = min_x;
397 vertex_data[4].position[1] = max_y;
398 vertex_data[4].uv[0] = r->x;
399 vertex_data[4].uv[1] = y2;
400
401 vertex_data[5].position[0] = max_x;
402 vertex_data[5].position[1] = min_y;
403 vertex_data[5].uv[0] = r->x2;
404 vertex_data[5].uv[1] = y1;
405 }
406
407 static inline void
load_float_vertex_data(GskQuadVertex vertex_data[GL_N_VERTICES],RenderOpBuilder * builder,float x,float y,float width,float height)408 load_float_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES],
409 RenderOpBuilder *builder,
410 float x,
411 float y,
412 float width,
413 float height)
414 {
415 const float min_x = builder->dx + x;
416 const float min_y = builder->dy + y;
417 const float max_x = min_x + width;
418 const float max_y = min_y + height;
419
420 vertex_data[0].position[0] = min_x;
421 vertex_data[0].position[1] = min_y;
422 vertex_data[0].uv[0] = 0;
423 vertex_data[0].uv[1] = 0;
424
425 vertex_data[1].position[0] = min_x;
426 vertex_data[1].position[1] = max_y;
427 vertex_data[1].uv[0] = 0;
428 vertex_data[1].uv[1] = 1;
429
430 vertex_data[2].position[0] = max_x;
431 vertex_data[2].position[1] = min_y;
432 vertex_data[2].uv[0] = 1;
433 vertex_data[2].uv[1] = 0;
434
435 vertex_data[3].position[0] = max_x;
436 vertex_data[3].position[1] = max_y;
437 vertex_data[3].uv[0] = 1;
438 vertex_data[3].uv[1] = 1;
439
440 vertex_data[4].position[0] = min_x;
441 vertex_data[4].position[1] = max_y;
442 vertex_data[4].uv[0] = 0;
443 vertex_data[4].uv[1] = 1;
444
445 vertex_data[5].position[0] = max_x;
446 vertex_data[5].position[1] = min_y;
447 vertex_data[5].uv[0] = 1;
448 vertex_data[5].uv[1] = 0;
449 }
450
451 static void
load_vertex_data(GskQuadVertex vertex_data[GL_N_VERTICES],const graphene_rect_t * bounds,RenderOpBuilder * builder)452 load_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES],
453 const graphene_rect_t *bounds,
454 RenderOpBuilder *builder)
455 {
456 load_float_vertex_data (vertex_data, builder,
457 bounds->origin.x, bounds->origin.y,
458 bounds->size.width, bounds->size.height);
459 }
460
461 static void
fill_vertex_data(GskQuadVertex vertex_data[GL_N_VERTICES],const float min_x,const float min_y,const float max_x,const float max_y)462 fill_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES],
463 const float min_x,
464 const float min_y,
465 const float max_x,
466 const float max_y)
467 {
468 vertex_data[0].position[0] = min_x;
469 vertex_data[0].position[1] = min_y;
470 vertex_data[0].uv[0] = 0;
471 vertex_data[0].uv[1] = 1;
472
473 vertex_data[1].position[0] = min_x;
474 vertex_data[1].position[1] = max_y;
475 vertex_data[1].uv[0] = 0;
476 vertex_data[1].uv[1] = 0;
477
478 vertex_data[2].position[0] = max_x;
479 vertex_data[2].position[1] = min_y;
480 vertex_data[2].uv[0] = 1;
481 vertex_data[2].uv[1] = 1;
482
483 vertex_data[3].position[0] = max_x;
484 vertex_data[3].position[1] = max_y;
485 vertex_data[3].uv[0] = 1;
486 vertex_data[3].uv[1] = 0;
487
488 vertex_data[4].position[0] = min_x;
489 vertex_data[4].position[1] = max_y;
490 vertex_data[4].uv[0] = 0;
491 vertex_data[4].uv[1] = 0;
492
493 vertex_data[5].position[0] = max_x;
494 vertex_data[5].position[1] = min_y;
495 vertex_data[5].uv[0] = 1;
496 vertex_data[5].uv[1] = 1;
497 }
498
499 static void
load_offscreen_vertex_data(GskQuadVertex vertex_data[GL_N_VERTICES],GskRenderNode * node,RenderOpBuilder * builder)500 load_offscreen_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES],
501 GskRenderNode *node,
502 RenderOpBuilder *builder)
503 {
504 const float min_x = builder->dx + node->bounds.origin.x;
505 const float min_y = builder->dy + node->bounds.origin.y;
506 const float max_x = min_x + node->bounds.size.width;
507 const float max_y = min_y + node->bounds.size.height;
508
509 fill_vertex_data (vertex_data,
510 min_x, min_y,
511 max_x, max_y);
512 }
513
514
515 static void gsk_gl_renderer_setup_render_mode (GskGLRenderer *self);
516 static gboolean add_offscreen_ops (GskGLRenderer *self,
517 RenderOpBuilder *builder,
518 const graphene_rect_t *bounds,
519 GskRenderNode *child_node,
520 TextureRegion *region_out,
521 gboolean *is_offscreen,
522 guint flags) G_GNUC_WARN_UNUSED_RESULT;
523 static void gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
524 GskRenderNode *node,
525 RenderOpBuilder *builder);
526
527 struct _GskGLRenderer
528 {
529 GskRenderer parent_instance;
530
531 int scale_factor;
532
533 GdkGLContext *gl_context;
534 GskGLDriver *gl_driver;
535 GskGLProfiler *gl_profiler;
536
537 GskGLRendererPrograms *programs;
538
539 RenderOpBuilder op_builder;
540
541 GskGLTextureAtlases *atlases;
542 GskGLGlyphCache *glyph_cache;
543 GskGLIconCache *icon_cache;
544 GskGLShadowCache shadow_cache;
545
546 #ifdef G_ENABLE_DEBUG
547 struct {
548 GQuark frames;
549 } profile_counters;
550 struct {
551 GQuark cpu_time;
552 GQuark gpu_time;
553 } profile_timers;
554 #endif
555
556 cairo_region_t *render_region;
557 };
558
559 struct _GskGLRendererClass
560 {
561 GskRendererClass parent_class;
562 };
563
G_DEFINE_TYPE(GskGLRenderer,gsk_gl_renderer,GSK_TYPE_RENDERER)564 G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
565
566 static void
567 init_shader_builder (GskGLRenderer *self,
568 GskGLShaderBuilder *shader_builder)
569 {
570 #ifdef G_ENABLE_DEBUG
571 if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
572 shader_builder->debugging = TRUE;
573 #endif
574
575 if (gdk_gl_context_get_use_es (self->gl_context))
576 {
577 gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GLES);
578 shader_builder->gles = TRUE;
579 }
580 else if (gdk_gl_context_is_legacy (self->gl_context))
581 {
582 int maj, min;
583
584 gdk_gl_context_get_version (self->gl_context, &maj, &min);
585
586 if (maj == 3)
587 gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3_LEGACY);
588 else
589 gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL2_LEGACY);
590
591 shader_builder->legacy = TRUE;
592 }
593 else
594 {
595 gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3);
596 shader_builder->gl3 = TRUE;
597 }
598 }
599
600 static GdkRGBA BLACK = {0, 0, 0, 1};
601 static void G_GNUC_UNUSED
add_rect_outline_ops(GskGLRenderer * self,RenderOpBuilder * builder,const graphene_rect_t * rect)602 add_rect_outline_ops (GskGLRenderer *self,
603 RenderOpBuilder *builder,
604 const graphene_rect_t *rect)
605 {
606 ops_set_program (builder, &self->programs->color_program);
607 ops_set_color (builder, &BLACK);
608
609 load_vertex_data (ops_draw (builder, NULL),
610 &GRAPHENE_RECT_INIT (rect->origin.x, rect->origin.y, 1, rect->size.height),
611 builder);
612 load_vertex_data (ops_draw (builder, NULL),
613 &GRAPHENE_RECT_INIT (rect->origin.x, rect->origin.y, rect->size.width, 1),
614 builder);
615 load_vertex_data (ops_draw (builder, NULL),
616 &GRAPHENE_RECT_INIT (rect->origin.x + rect->size.width - 1,rect->origin.y,
617 1, rect->size.height),
618 builder);
619 load_vertex_data (ops_draw (builder, NULL),
620 &GRAPHENE_RECT_INIT (rect->origin.x, rect->origin.y + rect->size.height - 1,
621 rect->size.width, 1),
622 builder);
623 }
624
625 static inline GskRoundedRect
transform_rect(GskGLRenderer * self,RenderOpBuilder * builder,const GskRoundedRect * rect)626 transform_rect (GskGLRenderer *self,
627 RenderOpBuilder *builder,
628 const GskRoundedRect *rect)
629 {
630 GskRoundedRect r;
631
632 r.bounds.origin.x = builder->dx + rect->bounds.origin.x;
633 r.bounds.origin.y = builder->dy + rect->bounds.origin.y;
634 r.bounds.size = rect->bounds.size;
635
636 r.corner[0] = rect->corner[0];
637 r.corner[1] = rect->corner[1];
638 r.corner[2] = rect->corner[2];
639 r.corner[3] = rect->corner[3];
640
641 return r;
642 }
643
644 static inline void
render_fallback_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)645 render_fallback_node (GskGLRenderer *self,
646 GskRenderNode *node,
647 RenderOpBuilder *builder)
648 {
649 const float scale_x = builder->scale_x;
650 const float scale_y = builder->scale_y;
651 const int surface_width = ceilf (node->bounds.size.width * scale_x);
652 const int surface_height = ceilf (node->bounds.size.height * scale_y);
653 GdkTexture *texture;
654 cairo_surface_t *surface;
655 cairo_surface_t *rendered_surface;
656 cairo_t *cr;
657 int cached_id;
658 int texture_id;
659 GskTextureKey key;
660
661 if (surface_width <= 0 ||
662 surface_height <= 0)
663 return;
664
665 key.pointer = node;
666 key.pointer_is_child = FALSE;
667 key.scale_x = scale_x;
668 key.scale_y = scale_y;
669 key.filter = GL_NEAREST;
670
671 cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
672
673 if (cached_id != 0)
674 {
675 ops_set_program (builder, &self->programs->blit_program);
676 ops_set_texture (builder, cached_id);
677 load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
678 return;
679 }
680
681
682 /* We first draw the recording surface on an image surface,
683 * just because the scaleY(-1) later otherwise screws up the
684 * rendering... */
685 {
686 rendered_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
687 surface_width,
688 surface_height);
689
690 cairo_surface_set_device_scale (rendered_surface, scale_x, scale_y);
691 cr = cairo_create (rendered_surface);
692
693 cairo_save (cr);
694 cairo_translate (cr, - floorf (node->bounds.origin.x), - floorf (node->bounds.origin.y));
695 gsk_render_node_draw (node, cr);
696 cairo_restore (cr);
697 cairo_destroy (cr);
698 }
699
700 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
701 surface_width,
702 surface_height);
703 cairo_surface_set_device_scale (surface, scale_x, scale_y);
704 cr = cairo_create (surface);
705
706 /* We draw upside down here, so it matches what GL does. */
707 cairo_save (cr);
708 cairo_scale (cr, 1, -1);
709 cairo_translate (cr, 0, - surface_height / scale_y);
710 cairo_set_source_surface (cr, rendered_surface, 0, 0);
711 cairo_rectangle (cr, 0, 0, surface_width / scale_x, surface_height / scale_y);
712 cairo_fill (cr);
713 cairo_restore (cr);
714
715 #ifdef G_ENABLE_DEBUG
716 if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
717 {
718 cairo_move_to (cr, 0, 0);
719 cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
720 if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE)
721 cairo_set_source_rgba (cr, 0.3, 0, 1, 0.25);
722 else
723 cairo_set_source_rgba (cr, 1, 0, 0, 0.25);
724 cairo_fill_preserve (cr);
725 if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE)
726 cairo_set_source_rgba (cr, 0.3, 0, 1, 1);
727 else
728 cairo_set_source_rgba (cr, 1, 0, 0, 1);
729 cairo_stroke (cr);
730 }
731 #endif
732 cairo_destroy (cr);
733
734
735 /* Upload the Cairo surface to a GL texture */
736 texture_id = gsk_gl_driver_create_texture (self->gl_driver,
737 surface_width,
738 surface_height);
739 gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
740
741 texture = gdk_texture_new_for_surface (surface);
742 gsk_gl_driver_init_texture (self->gl_driver,
743 texture_id,
744 texture,
745 GL_NEAREST, GL_NEAREST);
746
747 if (gdk_gl_context_has_debug (self->gl_context))
748 gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
749 "Fallback %s %d",
750 g_type_name_from_instance ((GTypeInstance *) node),
751 texture_id);
752
753 g_object_unref (texture);
754 cairo_surface_destroy (surface);
755 cairo_surface_destroy (rendered_surface);
756
757 gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, texture_id);
758
759 ops_set_program (builder, &self->programs->blit_program);
760 ops_set_texture (builder, texture_id);
761 load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
762 }
763
764 static inline void
render_text_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder,const GdkRGBA * color,gboolean force_color)765 render_text_node (GskGLRenderer *self,
766 GskRenderNode *node,
767 RenderOpBuilder *builder,
768 const GdkRGBA *color,
769 gboolean force_color)
770 {
771 const PangoFont *font = gsk_text_node_get_font (node);
772 const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
773 const float text_scale = MAX (builder->scale_x, builder->scale_y); /* TODO: Fix for uneven scales? */
774 const graphene_point_t *offset = gsk_text_node_get_offset (node);
775 const guint num_glyphs = gsk_text_node_get_num_glyphs (node);
776 const float x = offset->x + builder->dx;
777 const float y = offset->y + builder->dy;
778 int i;
779 int x_position = 0;
780 GlyphCacheKey lookup;
781
782 /* If the font has color glyphs, we don't need to recolor anything */
783 if (!force_color && gsk_text_node_has_color_glyphs (node))
784 {
785 ops_set_program (builder, &self->programs->blit_program);
786 }
787 else
788 {
789 ops_set_program (builder, &self->programs->coloring_program);
790 ops_set_color (builder, color);
791 }
792
793 memset (&lookup, 0, sizeof (CacheKeyData));
794 lookup.data.font = (PangoFont *)font;
795 lookup.data.scale = (guint) (text_scale * 1024);
796
797 /* We use one quad per character */
798 for (i = 0; i < num_glyphs; i++)
799 {
800 const PangoGlyphInfo *gi = &glyphs[i];
801 const GskGLCachedGlyph *glyph;
802 float glyph_x, glyph_y, glyph_x2, glyph_y2;
803 float tx, ty, tx2, ty2;
804 float cx;
805 float cy;
806
807 if (gi->glyph == PANGO_GLYPH_EMPTY)
808 continue;
809
810 cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
811 cy = (float)(gi->geometry.y_offset) / PANGO_SCALE;
812
813 glyph_cache_key_set_glyph_and_shift (&lookup, gi->glyph, x + cx, y + cy);
814
815 gsk_gl_glyph_cache_lookup_or_add (self->glyph_cache,
816 &lookup,
817 self->gl_driver,
818 &glyph);
819
820 if (glyph->texture_id == 0)
821 goto next;
822
823 ops_set_texture (builder, glyph->texture_id);
824
825 tx = glyph->tx;
826 ty = glyph->ty;
827 tx2 = tx + glyph->tw;
828 ty2 = ty + glyph->th;
829
830 glyph_x = floor (x + cx + 0.125) + glyph->draw_x;
831 glyph_y = floor (y + cy + 0.125) + glyph->draw_y;
832 glyph_x2 = glyph_x + glyph->draw_width;
833 glyph_y2 = glyph_y + glyph->draw_height;
834
835 ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
836 { { glyph_x, glyph_y }, { tx, ty }, },
837 { { glyph_x, glyph_y2 }, { tx, ty2 }, },
838 { { glyph_x2, glyph_y }, { tx2, ty }, },
839
840 { { glyph_x2, glyph_y2 }, { tx2, ty2 }, },
841 { { glyph_x, glyph_y2 }, { tx, ty2 }, },
842 { { glyph_x2, glyph_y }, { tx2, ty }, },
843 });
844
845 next:
846 x_position += gi->geometry.width;
847 }
848 }
849
850 static inline void
render_border_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)851 render_border_node (GskGLRenderer *self,
852 GskRenderNode *node,
853 RenderOpBuilder *builder)
854 {
855 const GdkRGBA *colors = gsk_border_node_get_colors (node);
856 const GskRoundedRect *rounded_outline = gsk_border_node_get_outline (node);
857 const float *widths = gsk_border_node_get_widths (node);
858 int i;
859 struct {
860 float w;
861 float h;
862 } sizes[4];
863
864 if (gsk_border_node_get_uniform (node))
865 {
866 ops_set_program (builder, &self->programs->inset_shadow_program);
867 ops_set_inset_shadow (builder, transform_rect (self, builder, rounded_outline),
868 widths[0], &colors[0], 0, 0);
869
870 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
871 return;
872 }
873
874 /* Top left */
875 if (widths[3] > 0)
876 sizes[0].w = MAX (widths[3], rounded_outline->corner[0].width);
877 else
878 sizes[0].w = 0;
879
880 if (widths[0] > 0)
881 sizes[0].h = MAX (widths[0], rounded_outline->corner[0].height);
882 else
883 sizes[0].h = 0;
884
885 /* Top right */
886 if (widths[1] > 0)
887 sizes[1].w = MAX (widths[1], rounded_outline->corner[1].width);
888 else
889 sizes[1].w = 0;
890
891 if (widths[0] > 0)
892 sizes[1].h = MAX (widths[0], rounded_outline->corner[1].height);
893 else
894 sizes[1].h = 0;
895
896 /* Bottom right */
897 if (widths[1] > 0)
898 sizes[2].w = MAX (widths[1], rounded_outline->corner[2].width);
899 else
900 sizes[2].w = 0;
901
902 if (widths[2] > 0)
903 sizes[2].h = MAX (widths[2], rounded_outline->corner[2].height);
904 else
905 sizes[2].h = 0;
906
907
908 /* Bottom left */
909 if (widths[3] > 0)
910 sizes[3].w = MAX (widths[3], rounded_outline->corner[3].width);
911 else
912 sizes[3].w = 0;
913
914 if (widths[2] > 0)
915 sizes[3].h = MAX (widths[2], rounded_outline->corner[3].height);
916 else
917 sizes[3].h = 0;
918
919 {
920 const float min_x = builder->dx + node->bounds.origin.x;
921 const float min_y = builder->dy + node->bounds.origin.y;
922 const float max_x = min_x + node->bounds.size.width;
923 const float max_y = min_y + node->bounds.size.height;
924 const GskQuadVertex side_data[4][6] = {
925 /* Top */
926 {
927 { { min_x, min_y }, { 0, 1 }, }, /* Upper left */
928 { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */
929 { { max_x, min_y }, { 1, 1 }, }, /* Upper right */
930
931 { { max_x - sizes[1].w, min_y + sizes[1].h }, { 1, 0 }, }, /* Lower right */
932 { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */
933 { { max_x, min_y }, { 1, 1 }, }, /* Upper right */
934 },
935 /* Right */
936 {
937 { { max_x - sizes[1].w, min_y + sizes[1].h }, { 0, 1 }, }, /* Upper left */
938 { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */
939 { { max_x, min_y }, { 1, 1 }, }, /* Upper right */
940
941 { { max_x, max_y }, { 1, 0 }, }, /* Lower right */
942 { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */
943 { { max_x, min_y }, { 1, 1 }, }, /* Upper right */
944 },
945 /* Bottom */
946 {
947 { { min_x + sizes[3].w, max_y - sizes[3].h }, { 0, 1 }, }, /* Upper left */
948 { { min_x, max_y }, { 0, 0 }, }, /* Lower left */
949 { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */
950
951 { { max_x, max_y }, { 1, 0 }, }, /* Lower right */
952 { { min_x , max_y }, { 0, 0 }, }, /* Lower left */
953 { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */
954 },
955 /* Left */
956 {
957 { { min_x, min_y }, { 0, 1 }, }, /* Upper left */
958 { { min_x, max_y }, { 0, 0 }, }, /* Lower left */
959 { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */
960
961 { { min_x + sizes[3].w, max_y - sizes[3].h }, { 1, 0 }, }, /* Lower right */
962 { { min_x, max_y }, { 0, 0 }, }, /* Lower left */
963 { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */
964 }
965 };
966 int indices[4] = { 0, 1, 2, 3 };
967 GskRoundedRect outline;
968
969 /* We sort them by color */
970 sort_border_sides (colors, indices);
971
972 /* Prepare outline */
973 outline = transform_rect (self, builder, rounded_outline);
974
975 ops_set_program (builder, &self->programs->border_program);
976 ops_set_border_width (builder, widths);
977 ops_set_border (builder, &outline);
978
979 for (i = 0; i < 4; i ++)
980 {
981 if (widths[indices[i]] > 0)
982 {
983 ops_set_border_color (builder, &colors[indices[i]]);
984 ops_draw (builder, side_data[indices[i]]);
985 }
986 }
987 }
988 }
989
990 static inline void
render_color_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)991 render_color_node (GskGLRenderer *self,
992 GskRenderNode *node,
993 RenderOpBuilder *builder)
994 {
995 ops_set_program (builder, &self->programs->color_program);
996 ops_set_color (builder, gsk_color_node_get_color (node));
997 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
998 }
999
1000 static inline void
upload_texture(GskGLRenderer * self,GdkTexture * texture,TextureRegion * out_region)1001 upload_texture (GskGLRenderer *self,
1002 GdkTexture *texture,
1003 TextureRegion *out_region)
1004 {
1005 if (texture->width <= 128 &&
1006 texture->height <= 128 &&
1007 !GDK_IS_GL_TEXTURE (texture))
1008 {
1009 const IconData *icon_data;
1010
1011 gsk_gl_icon_cache_lookup_or_add (self->icon_cache,
1012 texture,
1013 &icon_data);
1014
1015 out_region->texture_id = icon_data->texture_id;
1016 out_region->x = icon_data->x;
1017 out_region->y = icon_data->y;
1018 out_region->x2 = icon_data->x2;
1019 out_region->y2 = icon_data->y2;
1020 }
1021 else
1022 {
1023
1024 out_region->texture_id =
1025 gsk_gl_driver_get_texture_for_texture (self->gl_driver,
1026 texture,
1027 GL_LINEAR,
1028 GL_LINEAR);
1029
1030 out_region->x = 0;
1031 out_region->y = 0;
1032 out_region->x2 = 1;
1033 out_region->y2 = 1;
1034 }
1035 }
1036
1037 static inline void
render_texture_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1038 render_texture_node (GskGLRenderer *self,
1039 GskRenderNode *node,
1040 RenderOpBuilder *builder)
1041 {
1042 GdkTexture *texture = gsk_texture_node_get_texture (node);
1043 const int max_texture_size = gsk_gl_driver_get_max_texture_size (self->gl_driver);
1044
1045 if (texture->width > max_texture_size || texture->height > max_texture_size)
1046 {
1047 const float min_x = builder->dx + node->bounds.origin.x;
1048 const float min_y = builder->dy + node->bounds.origin.y;
1049 const float max_x = min_x + node->bounds.size.width;
1050 const float max_y = min_y + node->bounds.size.height;
1051 const float scale_x = (max_x - min_x) / texture->width;
1052 const float scale_y = (max_y - min_y) / texture->height;
1053 TextureSlice *slices;
1054 guint n_slices;
1055 guint i;
1056
1057 gsk_gl_driver_slice_texture (self->gl_driver, texture, &slices, &n_slices);
1058
1059 ops_set_program (builder, &self->programs->blit_program);
1060 for (i = 0; i < n_slices; i ++)
1061 {
1062 const TextureSlice *slice = &slices[i];
1063 float x1, x2, y1, y2;
1064
1065 x1 = min_x + (scale_x * slice->rect.x);
1066 x2 = x1 + (slice->rect.width * scale_x);
1067 y1 = min_y + (scale_y * slice->rect.y);
1068 y2 = y1 + (slice->rect.height * scale_y);
1069
1070 ops_set_texture (builder, slice->texture_id);
1071 ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
1072 { { x1, y1 }, { 0, 0 }, },
1073 { { x1, y2 }, { 0, 1 }, },
1074 { { x2, y1 }, { 1, 0 }, },
1075
1076 { { x2, y2 }, { 1, 1 }, },
1077 { { x1, y2 }, { 0, 1 }, },
1078 { { x2, y1 }, { 1, 0 }, },
1079 });
1080 }
1081 }
1082 else
1083 {
1084 TextureRegion r;
1085
1086 upload_texture (self, texture, &r);
1087
1088 ops_set_program (builder, &self->programs->blit_program);
1089 ops_set_texture (builder, r.texture_id);
1090
1091 load_vertex_data_with_region (ops_draw (builder, NULL),
1092 &node->bounds, builder,
1093 &r,
1094 FALSE);
1095 }
1096 }
1097
1098 static Program *
compile_glshader(GskGLRenderer * self,GskGLShader * shader,GError ** error)1099 compile_glshader (GskGLRenderer *self,
1100 GskGLShader *shader,
1101 GError **error)
1102 {
1103 GskGLShaderBuilder shader_builder;
1104 const char *shader_source;
1105 gsize shader_source_len;
1106 int n_uniforms;
1107 const GskGLUniform *uniforms;
1108 GBytes *bytes;
1109 int n_required_textures = gsk_gl_shader_get_n_textures (shader);
1110 int program_id;
1111 Program *program;
1112
1113 bytes = gsk_gl_shader_get_source (shader);
1114 shader_source = g_bytes_get_data (bytes, &shader_source_len);
1115 uniforms = gsk_gl_shader_get_uniforms (shader, &n_uniforms);
1116
1117 if (n_uniforms > G_N_ELEMENTS (program->glshader.args_locations))
1118 {
1119 g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT,
1120 "GLShaderNode supports max %d custom uniforms", (int)G_N_ELEMENTS (program->glshader.args_locations));
1121 return NULL;
1122 }
1123
1124 if (n_required_textures > G_N_ELEMENTS (program->glshader.texture_locations))
1125 {
1126 g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT,
1127 "GLShaderNode supports max %d texture sources", (int)(G_N_ELEMENTS (program->glshader.texture_locations)));
1128 return NULL;
1129 }
1130
1131 gsk_gl_shader_builder_init (&shader_builder,
1132 "/org/gtk/libgsk/gl/preamble.glsl",
1133 "/org/gtk/libgsk/gl/preamble.vs.glsl",
1134 "/org/gtk/libgsk/gl/preamble.fs.glsl");
1135
1136 init_shader_builder (self, &shader_builder);
1137 program_id = gsk_gl_shader_builder_create_program (&shader_builder,
1138 "/org/gtk/libgsk/gl/custom.glsl",
1139 shader_source, shader_source_len,
1140 error);
1141 gsk_gl_shader_builder_finish (&shader_builder);
1142
1143 if (program_id < 0)
1144 return NULL;
1145
1146 program = gsk_gl_renderer_create_custom_program (self, shader);
1147
1148 program->id = program_id;
1149 INIT_COMMON_UNIFORM_LOCATION (program, alpha);
1150 INIT_COMMON_UNIFORM_LOCATION (program, clip_rect);
1151 INIT_COMMON_UNIFORM_LOCATION (program, viewport);
1152 INIT_COMMON_UNIFORM_LOCATION (program, projection);
1153 INIT_COMMON_UNIFORM_LOCATION (program, modelview);
1154 program->glshader.size_location = glGetUniformLocation(program->id, "u_size");
1155 program->glshader.texture_locations[0] = glGetUniformLocation(program->id, "u_texture1");
1156 program->glshader.texture_locations[1] = glGetUniformLocation(program->id, "u_texture2");
1157 program->glshader.texture_locations[2] = glGetUniformLocation(program->id, "u_texture3");
1158 program->glshader.texture_locations[3] = glGetUniformLocation(program->id, "u_texture4");
1159
1160 /* We use u_texture1 for the texture 0 in the glshaders, so alias it here so we can use the regular setters */
1161 program->source_location = program->glshader.texture_locations[0];
1162
1163 for (int i = 0; i < G_N_ELEMENTS (program->glshader.args_locations); i++)
1164 {
1165 if (i < n_uniforms)
1166 {
1167 program->glshader.args_locations[i] = glGetUniformLocation(program->id, uniforms[i].name);
1168 /* This isn't necessary a hard error, you might declare uniforms that are not actually
1169 always used, for instance if you have an "API" in uniforms for multiple shaders. */
1170 if (program->glshader.args_locations[i] == -1)
1171 g_debug ("Declared uniform `%s` not found in GskGLShader", uniforms[i].name);
1172 }
1173 else
1174 program->glshader.args_locations[i] = -1;
1175 }
1176
1177 return program;
1178 }
1179
1180 gboolean
gsk_gl_renderer_try_compile_gl_shader(GskGLRenderer * self,GskGLShader * shader,GError ** error)1181 gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *self,
1182 GskGLShader *shader,
1183 GError **error)
1184 {
1185 Program *program;
1186
1187 gdk_gl_context_make_current (self->gl_context);
1188
1189 /* Maybe we tried to compile it already? */
1190 program = gsk_gl_renderer_lookup_custom_program (self, shader);
1191 if (program != NULL)
1192 {
1193 if (program->id > 0)
1194 return TRUE;
1195 else
1196 {
1197 g_propagate_error (error, g_error_copy (program->glshader.compile_error));
1198 return FALSE;
1199 }
1200 }
1201
1202 program = compile_glshader (self, shader, error);
1203 return program != NULL;
1204 }
1205
1206 static inline void
render_gl_shader_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1207 render_gl_shader_node (GskGLRenderer *self,
1208 GskRenderNode *node,
1209 RenderOpBuilder *builder)
1210 {
1211 GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
1212 Program *program = gsk_gl_renderer_lookup_custom_program (self, shader);
1213 int n_children = gsk_gl_shader_node_get_n_children (node);
1214
1215 if (program == NULL)
1216 {
1217 GError *error = NULL;
1218
1219 program = compile_glshader (self, shader, &error);
1220 if (program == NULL)
1221 {
1222 /* We create the program anyway (in a failed state), so that any compiler warnings or other are only reported once */
1223 program = gsk_gl_renderer_create_custom_program (self, shader);
1224 program->id = -1;
1225 program->glshader.compile_error = error;
1226
1227 g_warning ("Failed to compile gl shader: %s", error->message);
1228 }
1229 }
1230
1231 if (program->id >= 0 && n_children <= G_N_ELEMENTS (program->glshader.texture_locations))
1232 {
1233 GBytes *args;
1234 TextureRegion regions[4];
1235 gboolean is_offscreen[4];
1236 for (guint i = 0; i < n_children; i++)
1237 {
1238 GskRenderNode *child = gsk_gl_shader_node_get_child (node, i);
1239 if (!add_offscreen_ops (self, builder,
1240 &node->bounds,
1241 child,
1242 ®ions[i], &is_offscreen[i],
1243 FORCE_OFFSCREEN | RESET_CLIP))
1244 return;
1245 }
1246
1247 args = gsk_gl_shader_node_get_args (node);
1248 ops_set_program (builder, program);
1249
1250 ops_set_gl_shader_args (builder, shader, node->bounds.size.width, node->bounds.size.height, g_bytes_get_data (args, NULL));
1251 for (guint i = 0; i < n_children; i++)
1252 {
1253 if (i == 0)
1254 ops_set_texture (builder, regions[i].texture_id);
1255 else
1256 ops_set_extra_texture (builder, regions[i].texture_id, i);
1257 }
1258
1259 load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
1260 }
1261 else
1262 {
1263 static GdkRGBA pink = { 255 / 255., 105 / 255., 180 / 255., 1.0 };
1264 ops_set_program (builder, &self->programs->color_program);
1265 ops_set_color (builder, &pink);
1266 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
1267 }
1268 }
1269
1270 /* Returns TRUE if applying @transform to @bounds
1271 * yields an axis-aligned rectangle
1272 */
1273 static gboolean
result_is_axis_aligned(GskTransform * transform,const graphene_rect_t * bounds)1274 result_is_axis_aligned (GskTransform *transform,
1275 const graphene_rect_t *bounds)
1276 {
1277 graphene_matrix_t m;
1278 graphene_quad_t q;
1279 graphene_rect_t b;
1280 graphene_point_t b1, b2;
1281 const graphene_point_t *p;
1282 int i;
1283
1284 gsk_transform_to_matrix (transform, &m);
1285 gsk_matrix_transform_rect (&m, bounds, &q);
1286 graphene_quad_bounds (&q, &b);
1287 graphene_rect_get_top_left (&b, &b1);
1288 graphene_rect_get_bottom_right (&b, &b2);
1289
1290 for (i = 0; i < 4; i++)
1291 {
1292 p = graphene_quad_get_point (&q, i);
1293 if (fabs (p->x - b1.x) > FLT_EPSILON && fabs (p->x - b2.x) > FLT_EPSILON)
1294 return FALSE;
1295 if (fabs (p->y - b1.y) > FLT_EPSILON && fabs (p->y - b2.y) > FLT_EPSILON)
1296 return FALSE;
1297 }
1298
1299 return TRUE;
1300 }
1301
1302 static inline void
render_transform_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1303 render_transform_node (GskGLRenderer *self,
1304 GskRenderNode *node,
1305 RenderOpBuilder *builder)
1306 {
1307 GskTransform *node_transform = gsk_transform_node_get_transform (node);
1308 const GskTransformCategory category = gsk_transform_get_category (node_transform);
1309 GskRenderNode *child = gsk_transform_node_get_child (node);
1310
1311 switch (category)
1312 {
1313 case GSK_TRANSFORM_CATEGORY_IDENTITY:
1314 gsk_gl_renderer_add_render_ops (self, child, builder);
1315 break;
1316
1317 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
1318 {
1319 float dx, dy;
1320
1321 gsk_transform_to_translate (node_transform, &dx, &dy);
1322
1323 ops_offset (builder, dx, dy);
1324 gsk_gl_renderer_add_render_ops (self, child, builder);
1325 ops_offset (builder, -dx, -dy);
1326 }
1327 break;
1328
1329 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
1330 {
1331 ops_push_modelview (builder, node_transform);
1332 gsk_gl_renderer_add_render_ops (self, child, builder);
1333 ops_pop_modelview (builder);
1334 }
1335 break;
1336
1337 case GSK_TRANSFORM_CATEGORY_2D:
1338 case GSK_TRANSFORM_CATEGORY_3D:
1339 case GSK_TRANSFORM_CATEGORY_ANY:
1340 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
1341 {
1342 TextureRegion region;
1343 gboolean is_offscreen;
1344
1345 if (node_supports_transform (child))
1346 {
1347 ops_push_modelview (builder, node_transform);
1348 gsk_gl_renderer_add_render_ops (self, child, builder);
1349 ops_pop_modelview (builder);
1350 }
1351 else
1352 {
1353 int filter_flag = 0;
1354
1355 if (!result_is_axis_aligned (node_transform, &child->bounds))
1356 filter_flag = LINEAR_FILTER;
1357
1358 if (add_offscreen_ops (self, builder,
1359 &child->bounds,
1360 child,
1361 ®ion, &is_offscreen,
1362 RESET_CLIP | filter_flag))
1363 {
1364 /* For non-trivial transforms, we draw everything on a texture and then
1365 * draw the texture transformed. */
1366 /* TODO: We should compute a modelview containing only the "non-trivial"
1367 * part (e.g. the rotation) and use that. We want to keep the scale
1368 * for the texture.
1369 */
1370 ops_push_modelview (builder, node_transform);
1371 ops_set_texture (builder, region.texture_id);
1372 ops_set_program (builder, &self->programs->blit_program);
1373
1374 load_vertex_data_with_region (ops_draw (builder, NULL),
1375 &child->bounds, builder,
1376 ®ion,
1377 is_offscreen);
1378 ops_pop_modelview (builder);
1379 }
1380 }
1381 }
1382 break;
1383
1384 default:
1385 g_assert_not_reached ();
1386 break;
1387 }
1388 }
1389
1390 static inline void
render_opacity_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1391 render_opacity_node (GskGLRenderer *self,
1392 GskRenderNode *node,
1393 RenderOpBuilder *builder)
1394 {
1395 GskRenderNode *child = gsk_opacity_node_get_child (node);
1396 const float opacity = gsk_opacity_node_get_opacity (node);
1397 float prev_opacity;
1398
1399 if (gsk_render_node_get_node_type (child) == GSK_CONTAINER_NODE)
1400 {
1401 gboolean is_offscreen;
1402 TextureRegion region;
1403
1404 /* The semantics of an opacity node mandate that when, e.g., two color nodes overlap,
1405 * there may not be any blending between them */
1406 if (!add_offscreen_ops (self, builder, &child->bounds,
1407 child,
1408 ®ion, &is_offscreen,
1409 FORCE_OFFSCREEN | RESET_CLIP))
1410 return;
1411
1412 prev_opacity = ops_set_opacity (builder,
1413 builder->current_opacity * opacity);
1414
1415 if (builder->current_opacity >= ((float) 0x00ff / (float) 0xffff))
1416 {
1417 ops_set_program (builder, &self->programs->blit_program);
1418 ops_set_texture (builder, region.texture_id);
1419
1420 load_vertex_data_with_region (ops_draw (builder, NULL),
1421 &node->bounds, builder,
1422 ®ion,
1423 is_offscreen);
1424 }
1425 }
1426 else
1427 {
1428 prev_opacity = ops_set_opacity (builder,
1429 builder->current_opacity * opacity);
1430
1431 if (builder->current_opacity >= ((float) 0x00ff / (float) 0xffff))
1432 gsk_gl_renderer_add_render_ops (self, child, builder);
1433 }
1434
1435 ops_set_opacity (builder, prev_opacity);
1436 }
1437
1438 static inline void
render_linear_gradient_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1439 render_linear_gradient_node (GskGLRenderer *self,
1440 GskRenderNode *node,
1441 RenderOpBuilder *builder)
1442 {
1443 const int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node);
1444
1445 if (n_color_stops < GL_MAX_GRADIENT_STOPS)
1446 {
1447 const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL);
1448 const graphene_point_t *start = gsk_linear_gradient_node_get_start (node);
1449 const graphene_point_t *end = gsk_linear_gradient_node_get_end (node);
1450
1451 ops_set_program (builder, &self->programs->linear_gradient_program);
1452 ops_set_linear_gradient (builder,
1453 n_color_stops,
1454 stops,
1455 gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
1456 builder->dx + start->x,
1457 builder->dy + start->y,
1458 builder->dx + end->x,
1459 builder->dy + end->y);
1460
1461 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
1462 }
1463 else
1464 {
1465 render_fallback_node (self, node, builder);
1466 }
1467 }
1468
1469 static inline void
render_radial_gradient_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1470 render_radial_gradient_node (GskGLRenderer *self,
1471 GskRenderNode *node,
1472 RenderOpBuilder *builder)
1473 {
1474 const int n_color_stops = gsk_radial_gradient_node_get_n_color_stops (node);
1475
1476 if (n_color_stops < GL_MAX_GRADIENT_STOPS)
1477 {
1478 const GskColorStop *stops = gsk_radial_gradient_node_get_color_stops (node, NULL);
1479 const graphene_point_t *center = gsk_radial_gradient_node_get_center (node);
1480 const float start = gsk_radial_gradient_node_get_start (node);
1481 const float end = gsk_radial_gradient_node_get_end (node);
1482 const float hradius = gsk_radial_gradient_node_get_hradius (node);
1483 const float vradius = gsk_radial_gradient_node_get_vradius (node);
1484
1485 ops_set_program (builder, &self->programs->radial_gradient_program);
1486 ops_set_radial_gradient (builder,
1487 n_color_stops,
1488 stops,
1489 gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE,
1490 builder->dx + center->x,
1491 builder->dy + center->y,
1492 start, end,
1493 hradius * builder->scale_x,
1494 vradius * builder->scale_y);
1495
1496 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
1497 }
1498 else
1499 {
1500 render_fallback_node (self, node, builder);
1501 }
1502 }
1503
1504 static inline void
render_conic_gradient_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1505 render_conic_gradient_node (GskGLRenderer *self,
1506 GskRenderNode *node,
1507 RenderOpBuilder *builder)
1508 {
1509 const int n_color_stops = gsk_conic_gradient_node_get_n_color_stops (node);
1510
1511 if (n_color_stops < GL_MAX_GRADIENT_STOPS)
1512 {
1513 const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
1514 const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
1515 const float angle = gsk_conic_gradient_node_get_angle (node);
1516
1517 ops_set_program (builder, &self->programs->conic_gradient_program);
1518 ops_set_conic_gradient (builder,
1519 n_color_stops,
1520 stops,
1521 builder->dx + center->x,
1522 builder->dy + center->y,
1523 angle);
1524
1525 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
1526 }
1527 else
1528 {
1529 render_fallback_node (self, node, builder);
1530 }
1531 }
1532
1533 static inline gboolean
rounded_inner_rect_contains_rect(const GskRoundedRect * rounded,const graphene_rect_t * rect)1534 rounded_inner_rect_contains_rect (const GskRoundedRect *rounded,
1535 const graphene_rect_t *rect)
1536 {
1537 const graphene_rect_t *rounded_bounds = &rounded->bounds;
1538 graphene_rect_t inner;
1539 float offset_x, offset_y;
1540
1541 /* TODO: This is pretty conservative and we could to further, more
1542 * fine-grained checks to avoid offscreen drawing. */
1543
1544 offset_x = MAX (rounded->corner[GSK_CORNER_TOP_LEFT].width,
1545 rounded->corner[GSK_CORNER_BOTTOM_LEFT].width);
1546 offset_y = MAX (rounded->corner[GSK_CORNER_TOP_LEFT].height,
1547 rounded->corner[GSK_CORNER_TOP_RIGHT].height);
1548
1549
1550 inner.origin.x = rounded_bounds->origin.x + offset_x;
1551 inner.origin.y = rounded_bounds->origin.y + offset_y;
1552 inner.size.width = rounded_bounds->size.width - offset_x -
1553 MAX (rounded->corner[GSK_CORNER_TOP_RIGHT].width,
1554 rounded->corner[GSK_CORNER_BOTTOM_RIGHT].width);
1555 inner.size.height = rounded_bounds->size.height - offset_y -
1556 MAX (rounded->corner[GSK_CORNER_BOTTOM_LEFT].height,
1557 rounded->corner[GSK_CORNER_BOTTOM_RIGHT].height);
1558
1559 return _graphene_rect_contains_rect (&inner, rect);
1560 }
1561
1562 /* Current clip is NOT rounded but new one is definitely! */
1563 static inline bool
intersect_rounded_rectilinear(const graphene_rect_t * non_rounded,const GskRoundedRect * rounded,GskRoundedRect * result)1564 intersect_rounded_rectilinear (const graphene_rect_t *non_rounded,
1565 const GskRoundedRect *rounded,
1566 GskRoundedRect *result)
1567 {
1568 bool corners[4];
1569
1570 /* Intersects with top left corner? */
1571 corners[0] = rounded_rect_has_corner (rounded, 0) &&
1572 graphene_rect_intersects (non_rounded,
1573 &rounded_rect_corner (rounded, 0));
1574 /* top right? */
1575 corners[1] = rounded_rect_has_corner (rounded, 1) &&
1576 graphene_rect_intersects (non_rounded,
1577 &rounded_rect_corner (rounded, 1));
1578 /* bottom right? */
1579 corners[2] = rounded_rect_has_corner (rounded, 2) &&
1580 graphene_rect_intersects (non_rounded,
1581 &rounded_rect_corner (rounded, 2));
1582 /* bottom left */
1583 corners[3] = rounded_rect_has_corner (rounded, 3) &&
1584 graphene_rect_intersects (non_rounded,
1585 &rounded_rect_corner (rounded, 3));
1586
1587 if (corners[0] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 0)))
1588 return false;
1589 if (corners[1] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 1)))
1590 return false;
1591 if (corners[2] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 2)))
1592 return false;
1593 if (corners[3] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 3)))
1594 return false;
1595
1596 /* We do intersect with at least one of the corners, but in such a way that the
1597 * intersection between the two clips can still be represented by a single rounded
1598 * rect in a trivial way. do that. */
1599 graphene_rect_intersection (non_rounded, &rounded->bounds, &result->bounds);
1600
1601 for (int i = 0; i < 4; i++)
1602 {
1603 if (corners[i])
1604 result->corner[i] = rounded->corner[i];
1605 else
1606 result->corner[i].width = result->corner[i].height = 0;
1607 }
1608
1609 return true;
1610 }
1611
1612 /* This code intersects the current (maybe rounded) clip with the new
1613 * non-rounded clip */
1614 static inline void
render_clipped_child(GskGLRenderer * self,RenderOpBuilder * builder,const graphene_rect_t * clip,GskRenderNode * child)1615 render_clipped_child (GskGLRenderer *self,
1616 RenderOpBuilder *builder,
1617 const graphene_rect_t *clip,
1618 GskRenderNode *child)
1619 {
1620 graphene_rect_t transformed_clip;
1621 GskRoundedRect intersection;
1622
1623 ops_transform_bounds_modelview (builder, clip, &transformed_clip);
1624
1625 if (builder->clip_is_rectilinear)
1626 {
1627 memset (&intersection, 0, sizeof (GskRoundedRect));
1628 graphene_rect_intersection (&transformed_clip,
1629 &builder->current_clip->bounds,
1630 &intersection.bounds);
1631
1632 ops_push_clip (builder, &intersection);
1633 gsk_gl_renderer_add_render_ops (self, child, builder);
1634 ops_pop_clip (builder);
1635 }
1636 else if (intersect_rounded_rectilinear (&transformed_clip,
1637 builder->current_clip,
1638 &intersection))
1639 {
1640 ops_push_clip (builder, &intersection);
1641 gsk_gl_renderer_add_render_ops (self, child, builder);
1642 ops_pop_clip (builder);
1643 }
1644 else
1645 {
1646 /* well fuck */
1647 const float scale_x = builder->scale_x;
1648 const float scale_y = builder->scale_y;
1649 const GskRoundedRect scaled_clip = GSK_ROUNDED_RECT_INIT ((builder->dx + clip->origin.x) * scale_x,
1650 (builder->dy + clip->origin.y) * scale_y,
1651 clip->size.width * scale_x,
1652 clip->size.height * scale_y);
1653 gboolean is_offscreen;
1654 TextureRegion region;
1655
1656 ops_push_clip (builder, &scaled_clip);
1657 if (!add_offscreen_ops (self, builder, &child->bounds,
1658 child,
1659 ®ion, &is_offscreen,
1660 FORCE_OFFSCREEN))
1661 g_assert_not_reached ();
1662 ops_pop_clip (builder);
1663
1664 ops_set_program (builder, &self->programs->blit_program);
1665 ops_set_texture (builder, region.texture_id);
1666
1667 load_offscreen_vertex_data (ops_draw (builder, NULL), child, builder);
1668 }
1669 }
1670
1671 static inline void
render_clip_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1672 render_clip_node (GskGLRenderer *self,
1673 GskRenderNode *node,
1674 RenderOpBuilder *builder)
1675 {
1676 const graphene_rect_t *clip = gsk_clip_node_get_clip (node);
1677 GskRenderNode *child = gsk_clip_node_get_child (node);
1678
1679 render_clipped_child (self, builder, clip, child);
1680 }
1681
1682 static inline void
render_rounded_clip_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1683 render_rounded_clip_node (GskGLRenderer *self,
1684 GskRenderNode *node,
1685 RenderOpBuilder *builder)
1686 {
1687 const float scale_x = builder->scale_x;
1688 const float scale_y = builder->scale_y;
1689 const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node);
1690 GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
1691 GskRoundedRect transformed_clip;
1692 gboolean need_offscreen;
1693 int i;
1694
1695 if (node_is_invisible (child))
1696 return;
1697
1698 ops_transform_bounds_modelview (builder, &clip->bounds, &transformed_clip.bounds);
1699 for (i = 0; i < 4; i ++)
1700 {
1701 transformed_clip.corner[i].width = clip->corner[i].width * scale_x;
1702 transformed_clip.corner[i].height = clip->corner[i].height * scale_y;
1703 }
1704
1705 if (builder->clip_is_rectilinear)
1706 {
1707 GskRoundedRect intersected_clip;
1708
1709 if (intersect_rounded_rectilinear (&builder->current_clip->bounds,
1710 &transformed_clip,
1711 &intersected_clip))
1712 {
1713 ops_push_clip (builder, &intersected_clip);
1714 gsk_gl_renderer_add_render_ops (self, child, builder);
1715 ops_pop_clip (builder);
1716 return;
1717 }
1718 }
1719
1720 /* After this point we are really working with a new and a current clip
1721 * which both have rounded corners. */
1722
1723 if (!ops_has_clip (builder))
1724 need_offscreen = FALSE;
1725 else if (rounded_inner_rect_contains_rect (builder->current_clip,
1726 &transformed_clip.bounds))
1727 need_offscreen = FALSE;
1728 else
1729 need_offscreen = TRUE;
1730
1731 if (!need_offscreen)
1732 {
1733 /* If they don't intersect at all, we can simply set
1734 * the new clip and add the render ops */
1735
1736 /* If the new clip entirely contains the current clip, the intersection is simply
1737 * the current clip, so we can ignore the new one */
1738 if (rounded_inner_rect_contains_rect (&transformed_clip, &builder->current_clip->bounds))
1739 {
1740 gsk_gl_renderer_add_render_ops (self, child, builder);
1741 return;
1742 }
1743
1744 ops_push_clip (builder, &transformed_clip);
1745 gsk_gl_renderer_add_render_ops (self, child, builder);
1746 ops_pop_clip (builder);
1747 }
1748 else
1749 {
1750 gboolean is_offscreen;
1751 TextureRegion region;
1752
1753 ops_push_clip (builder, &transformed_clip);
1754 if (!add_offscreen_ops (self, builder, &node->bounds,
1755 child,
1756 ®ion, &is_offscreen,
1757 FORCE_OFFSCREEN))
1758 g_assert_not_reached ();
1759 ops_pop_clip (builder);
1760
1761 ops_set_program (builder, &self->programs->blit_program);
1762 ops_set_texture (builder, region.texture_id);
1763
1764 load_vertex_data_with_region (ops_draw (builder, NULL), &node->bounds, builder,
1765 ®ion, is_offscreen);
1766 }
1767 }
1768
1769 static inline void
render_color_matrix_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1770 render_color_matrix_node (GskGLRenderer *self,
1771 GskRenderNode *node,
1772 RenderOpBuilder *builder)
1773 {
1774 GskRenderNode *child = gsk_color_matrix_node_get_child (node);
1775 TextureRegion region;
1776 gboolean is_offscreen;
1777
1778 if (node_is_invisible (child))
1779 return;
1780
1781 if (!add_offscreen_ops (self, builder,
1782 &node->bounds,
1783 child,
1784 ®ion, &is_offscreen,
1785 RESET_CLIP))
1786 g_assert_not_reached ();
1787
1788 ops_set_program (builder, &self->programs->color_matrix_program);
1789 ops_set_color_matrix (builder,
1790 gsk_color_matrix_node_get_color_matrix (node),
1791 gsk_color_matrix_node_get_color_offset (node));
1792
1793 ops_set_texture (builder, region.texture_id);
1794
1795 load_vertex_data_with_region (ops_draw (builder, NULL),
1796 &node->bounds, builder,
1797 ®ion,
1798 is_offscreen);
1799 }
1800
1801 static inline int
blur_texture(GskGLRenderer * self,RenderOpBuilder * builder,const TextureRegion * region,const int texture_to_blur_width,const int texture_to_blur_height,float blur_radius_x,float blur_radius_y)1802 blur_texture (GskGLRenderer *self,
1803 RenderOpBuilder *builder,
1804 const TextureRegion *region,
1805 const int texture_to_blur_width,
1806 const int texture_to_blur_height,
1807 float blur_radius_x,
1808 float blur_radius_y)
1809 {
1810 const GskRoundedRect new_clip = GSK_ROUNDED_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height);
1811 int pass1_texture_id, pass1_render_target;
1812 int pass2_texture_id, pass2_render_target;
1813 int prev_render_target;
1814 graphene_matrix_t prev_projection;
1815 graphene_rect_t prev_viewport;
1816 graphene_matrix_t item_proj;
1817 OpBlur *op;
1818
1819 g_assert (blur_radius_x > 0);
1820 g_assert (blur_radius_y > 0);
1821
1822 gsk_gl_driver_create_render_target (self->gl_driver,
1823 MAX (texture_to_blur_width, 1), MAX (texture_to_blur_height, 1),
1824 GL_NEAREST, GL_NEAREST,
1825 &pass1_texture_id, &pass1_render_target);
1826
1827 if (texture_to_blur_width <= 0 || texture_to_blur_height <= 0)
1828 {
1829 return pass1_texture_id;
1830 }
1831
1832 gsk_gl_driver_create_render_target (self->gl_driver,
1833 texture_to_blur_width, texture_to_blur_height,
1834 GL_NEAREST, GL_NEAREST,
1835 &pass2_texture_id, &pass2_render_target);
1836
1837 init_projection_matrix (&item_proj, &new_clip.bounds);
1838
1839 ops_set_program (builder, &self->programs->blur_program);
1840 prev_projection = ops_set_projection (builder, &item_proj);
1841 ops_set_modelview (builder, NULL);
1842 prev_viewport = ops_set_viewport (builder, &new_clip.bounds);
1843 ops_push_clip (builder, &new_clip);
1844
1845 prev_render_target = ops_set_render_target (builder, pass1_render_target);
1846 ops_begin (builder, OP_CLEAR);
1847
1848 op = ops_begin (builder, OP_CHANGE_BLUR);
1849 op->size.width = texture_to_blur_width;
1850 op->size.height = texture_to_blur_height;
1851 op->radius = blur_radius_x;
1852 op->dir[0] = 1;
1853 op->dir[1] = 0;
1854 ops_set_texture (builder, region->texture_id);
1855
1856 load_vertex_data_with_region (ops_draw (builder, NULL),
1857 &new_clip.bounds,
1858 builder, region,
1859 FALSE);
1860 #if 0
1861 {
1862 static int k;
1863 ops_dump_framebuffer (builder,
1864 g_strdup_printf ("pass1_%d.png", k++),
1865 texture_to_blur_width,
1866 texture_to_blur_height);
1867 }
1868 #endif
1869 op = ops_begin (builder, OP_CHANGE_BLUR);
1870 op->size.width = texture_to_blur_width;
1871 op->size.height = texture_to_blur_height;
1872 op->radius = blur_radius_y;
1873 op->dir[0] = 0;
1874 op->dir[1] = 1;
1875 ops_set_texture (builder, pass1_texture_id);
1876 ops_set_render_target (builder, pass2_render_target);
1877 ops_begin (builder, OP_CLEAR);
1878 load_vertex_data_with_region (ops_draw (builder, NULL), /* render pass 2 */
1879 &new_clip.bounds,
1880 builder, region,
1881 FALSE);
1882
1883 #if 0
1884 {
1885 static int k;
1886 ops_dump_framebuffer (builder,
1887 g_strdup_printf ("blurred%d.png", k++),
1888 texture_to_blur_width,
1889 texture_to_blur_height);
1890 }
1891 #endif
1892
1893 ops_set_render_target (builder, prev_render_target);
1894 ops_set_viewport (builder, &prev_viewport);
1895 ops_set_projection (builder, &prev_projection);
1896 ops_pop_modelview (builder);
1897 ops_pop_clip (builder);
1898
1899 return pass2_texture_id;
1900 }
1901
1902 static inline void
blur_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder,float blur_radius,guint extra_flags,TextureRegion * out_region,float * out_vertex_data[4])1903 blur_node (GskGLRenderer *self,
1904 GskRenderNode *node,
1905 RenderOpBuilder *builder,
1906 float blur_radius,
1907 guint extra_flags,
1908 TextureRegion *out_region,
1909 float *out_vertex_data[4]) /* min, max, min, max */
1910 {
1911 const float scale_x = builder->scale_x;
1912 const float scale_y = builder->scale_y;
1913 const float blur_extra = blur_radius * 2.0; /* 2.0 = shader radius_multiplier */
1914 float texture_width, texture_height;
1915 gboolean is_offscreen;
1916 TextureRegion region;
1917 int blurred_texture_id;
1918
1919 g_assert (blur_radius > 0);
1920
1921 /* Increase texture size for the given blur radius and scale it */
1922 texture_width = ceilf ((node->bounds.size.width + blur_extra));
1923 texture_height = ceilf ((node->bounds.size.height + blur_extra));
1924
1925 /* Only blur this if the out region has no texture id yet */
1926 if (out_region->texture_id == 0)
1927 {
1928 if (!add_offscreen_ops (self, builder,
1929 &GRAPHENE_RECT_INIT (node->bounds.origin.x - (blur_extra / 2.0),
1930 node->bounds.origin.y - (blur_extra / 2.0),
1931 texture_width, texture_height),
1932 node,
1933 ®ion, &is_offscreen,
1934 RESET_CLIP | FORCE_OFFSCREEN | extra_flags))
1935 g_assert_not_reached ();
1936
1937 blurred_texture_id = blur_texture (self, builder,
1938 ®ion,
1939 texture_width * scale_x, texture_height * scale_y,
1940 blur_radius * scale_x,
1941 blur_radius * scale_y);
1942 init_full_texture_region (out_region, blurred_texture_id);
1943 }
1944
1945 if (out_vertex_data)
1946 {
1947 *out_vertex_data[0] = builder->dx + node->bounds.origin.x - (blur_extra / 2.0);
1948 *out_vertex_data[1] = builder->dx + node->bounds.origin.x + node->bounds.size.width + (blur_extra / 2.0);
1949 *out_vertex_data[2] = builder->dy + node->bounds.origin.y - (blur_extra / 2.0);
1950 *out_vertex_data[3] = builder->dy + node->bounds.origin.y + node->bounds.size.height + (blur_extra / 2.0);
1951 }
1952 }
1953
1954 static inline void
render_blur_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1955 render_blur_node (GskGLRenderer *self,
1956 GskRenderNode *node,
1957 RenderOpBuilder *builder)
1958 {
1959 const float blur_radius = gsk_blur_node_get_radius (node);
1960 GskRenderNode *child = gsk_blur_node_get_child (node);
1961 TextureRegion blurred_region;
1962 GskTextureKey key;
1963 float min_x, max_x, min_y, max_y;
1964
1965 if (node_is_invisible (child))
1966 return;
1967
1968 if (blur_radius <= 0)
1969 {
1970 gsk_gl_renderer_add_render_ops (self, child, builder);
1971 return;
1972 }
1973
1974 key.pointer = node;
1975 key.pointer_is_child = FALSE;
1976 key.scale_x = builder->scale_x;
1977 key.scale_y = builder->scale_y;
1978 key.filter = GL_NEAREST;
1979 blurred_region.texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
1980 blur_node (self, child, builder, blur_radius, 0, &blurred_region,
1981 (float*[4]){&min_x, &max_x, &min_y, &max_y});
1982
1983 g_assert (blurred_region.texture_id != 0);
1984
1985 /* Draw the result */
1986 ops_set_program (builder, &self->programs->blit_program);
1987 ops_set_texture (builder, blurred_region.texture_id);
1988 fill_vertex_data (ops_draw (builder, NULL), min_x, min_y, max_x, max_y);
1989
1990
1991 /* Add to cache for the blur node */
1992 gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, blurred_region.texture_id);
1993 }
1994
1995 static inline void
render_unblurred_inset_shadow_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)1996 render_unblurred_inset_shadow_node (GskGLRenderer *self,
1997 GskRenderNode *node,
1998 RenderOpBuilder *builder)
1999 {
2000 const float dx = gsk_inset_shadow_node_get_dx (node);
2001 const float dy = gsk_inset_shadow_node_get_dy (node);
2002 const float spread = gsk_inset_shadow_node_get_spread (node);
2003
2004 g_assert (gsk_inset_shadow_node_get_blur_radius (node) == 0);
2005
2006 ops_set_program (builder, &self->programs->inset_shadow_program);
2007 ops_set_inset_shadow (builder, transform_rect (self, builder, gsk_inset_shadow_node_get_outline (node)),
2008 spread,
2009 gsk_inset_shadow_node_get_color (node),
2010 dx, dy);
2011
2012 load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
2013 }
2014
2015 static inline void
render_inset_shadow_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2016 render_inset_shadow_node (GskGLRenderer *self,
2017 GskRenderNode *node,
2018 RenderOpBuilder *builder)
2019 {
2020 const float scale_x = builder->scale_x;
2021 const float scale_y = builder->scale_y;
2022 const float blur_radius = gsk_inset_shadow_node_get_blur_radius (node);
2023 const float blur_extra = blur_radius * 2.0; /* 2.0 = shader radius_multiplier */
2024 const float dx = gsk_inset_shadow_node_get_dx (node);
2025 const float dy = gsk_inset_shadow_node_get_dy (node);
2026 const GskRoundedRect *node_outline = gsk_inset_shadow_node_get_outline (node);
2027 float texture_width;
2028 float texture_height;
2029 int blurred_texture_id;
2030 GskTextureKey key;
2031
2032 g_assert (blur_radius > 0);
2033
2034 texture_width = ceilf ((node_outline->bounds.size.width + blur_extra) * scale_x);
2035 texture_height = ceilf ((node_outline->bounds.size.height + blur_extra) * scale_y);
2036
2037 key.pointer = node;
2038 key.pointer_is_child = FALSE;
2039 key.scale_x = scale_x;
2040 key.scale_y = scale_y;
2041 key.filter = GL_NEAREST;
2042 blurred_texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
2043 if (blurred_texture_id == 0)
2044 {
2045 const float spread = gsk_inset_shadow_node_get_spread (node) + (blur_extra / 2.0);
2046 GskRoundedRect outline_to_blur;
2047 int render_target, texture_id;
2048 int prev_render_target;
2049 graphene_matrix_t prev_projection;
2050 graphene_rect_t prev_viewport;
2051 graphene_matrix_t item_proj;
2052 int i;
2053
2054 /* TODO: In the following code, we have to be careful about where we apply the scale.
2055 * We're manually scaling stuff (e.g. the outline) so we can later use texture_width
2056 * and texture_height (which are already scaled) as the geometry and keep the modelview
2057 * at a scale of 1. That's kinda complicated though... */
2058
2059 /* Outline of what we actually want to blur later.
2060 * Spread grows inside, so we don't need to account for that. But the blur will need
2061 * to read outside of the inset shadow, so we need to draw some color in there. */
2062 outline_to_blur = *node_outline;
2063 gsk_rounded_rect_shrink (&outline_to_blur,
2064 - blur_extra / 2.0, - blur_extra / 2.0,
2065 - blur_extra / 2.0, - blur_extra / 2.0);
2066
2067 /* Fit to our texture */
2068 outline_to_blur.bounds.origin.x = 0;
2069 outline_to_blur.bounds.origin.y = 0;
2070 outline_to_blur.bounds.size.width *= scale_x;
2071 outline_to_blur.bounds.size.height *= scale_y;
2072
2073 for (i = 0; i < 4; i ++)
2074 {
2075 outline_to_blur.corner[i].width *= scale_x;
2076 outline_to_blur.corner[i].height *= scale_y;
2077 }
2078
2079 gsk_gl_driver_create_render_target (self->gl_driver,
2080 texture_width, texture_height,
2081 GL_NEAREST, GL_NEAREST,
2082 &texture_id, &render_target);
2083
2084 init_projection_matrix (&item_proj,
2085 &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
2086
2087 prev_projection = ops_set_projection (builder, &item_proj);
2088 ops_set_modelview (builder, NULL);
2089 prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
2090 ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT (0, 0, texture_width, texture_height));
2091
2092 prev_render_target = ops_set_render_target (builder, render_target);
2093 ops_begin (builder, OP_CLEAR);
2094
2095 /* Actual inset shadow outline drawing */
2096 ops_set_program (builder, &self->programs->inset_shadow_program);
2097 ops_set_inset_shadow (builder, transform_rect (self, builder, &outline_to_blur),
2098 spread * MAX (scale_x, scale_y),
2099 gsk_inset_shadow_node_get_color (node),
2100 dx * scale_x, dy * scale_y);
2101
2102 load_float_vertex_data (ops_draw (builder, NULL), builder,
2103 0, 0, texture_width, texture_height);
2104
2105 ops_set_render_target (builder, prev_render_target);
2106 ops_set_viewport (builder, &prev_viewport);
2107 ops_set_projection (builder, &prev_projection);
2108 ops_pop_modelview (builder);
2109 ops_pop_clip (builder);
2110
2111 blurred_texture_id = blur_texture (self, builder,
2112 &(TextureRegion) { texture_id, 0, 0, 1, 1 },
2113 texture_width,
2114 texture_height,
2115 blur_radius * scale_x,
2116 blur_radius * scale_y);
2117 }
2118
2119 g_assert (blurred_texture_id != 0);
2120
2121 /* Blur the rendered unblurred inset shadow */
2122 /* Use a clip to cut away the unwanted parts outside of the original outline */
2123 {
2124 const gboolean needs_clip = !gsk_rounded_rect_is_rectilinear (node_outline);
2125 const float tx1 = blur_extra / 2.0 * scale_x / texture_width;
2126 const float tx2 = 1.0 - tx1;
2127 const float ty1 = blur_extra / 2.0 * scale_y / texture_height;
2128 const float ty2 = 1.0 - ty1;
2129
2130 gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, blurred_texture_id);
2131
2132 if (needs_clip)
2133 {
2134 GskRoundedRect node_clip;
2135
2136 ops_transform_bounds_modelview (builder, &node_outline->bounds, &node_clip.bounds);
2137 for (int i = 0; i < 4; i ++)
2138 {
2139 node_clip.corner[i].width = node_outline->corner[i].width * scale_x;
2140 node_clip.corner[i].height = node_outline->corner[i].height * scale_y;
2141 }
2142
2143 ops_push_clip (builder, &node_clip);
2144 }
2145
2146 ops_set_program (builder, &self->programs->blit_program);
2147 ops_set_texture (builder, blurred_texture_id);
2148
2149 load_vertex_data_with_region (ops_draw (builder, NULL),
2150 &node->bounds, builder,
2151 &(TextureRegion) { 0, tx1, ty1, tx2, ty2 },
2152 TRUE);
2153
2154 if (needs_clip)
2155 ops_pop_clip (builder);
2156 }
2157
2158 }
2159
2160 /* Spread *grows* the outline. The offset moves the shadow and leaves the
2161 * inner rect where it was */
2162 static inline void
render_unblurred_outset_shadow_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2163 render_unblurred_outset_shadow_node (GskGLRenderer *self,
2164 GskRenderNode *node,
2165 RenderOpBuilder *builder)
2166 {
2167 const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node);
2168 const float x = node->bounds.origin.x;
2169 const float y = node->bounds.origin.y;
2170 const float w = node->bounds.size.width;
2171 const float h = node->bounds.size.height;
2172 const float spread = gsk_outset_shadow_node_get_spread (node);
2173 const float dx = gsk_outset_shadow_node_get_dx (node);
2174 const float dy = gsk_outset_shadow_node_get_dy (node);
2175 const float edge_sizes[] = { // Top, right, bottom, left
2176 spread - dy, spread + dx, spread + dy, spread - dx
2177 };
2178 const float corner_sizes[][2] = { // top left, top right, bottom right, bottom left
2179 { outline->corner[0].width + spread - dx, outline->corner[0].height + spread - dy },
2180 { outline->corner[1].width + spread + dx, outline->corner[1].height + spread - dy },
2181 { outline->corner[2].width + spread + dx, outline->corner[2].height + spread + dy },
2182 { outline->corner[3].width + spread - dx, outline->corner[3].height + spread + dy },
2183 };
2184
2185 ops_set_program (builder, &self->programs->unblurred_outset_shadow_program);
2186 ops_set_unblurred_outset_shadow (builder, transform_rect (self, builder, outline),
2187 spread,
2188 gsk_outset_shadow_node_get_color (node),
2189 dx, dy);
2190
2191 /* Corners... */
2192 if (corner_sizes[0][0] > 0 && corner_sizes[0][1] > 0) /* Top left */
2193 load_float_vertex_data (ops_draw (builder, NULL), builder,
2194 x, y,
2195 corner_sizes[0][0], corner_sizes[0][1]);
2196 if (corner_sizes[1][0] > 0 && corner_sizes[1][1] > 0) /* Top right */
2197 load_float_vertex_data (ops_draw (builder, NULL), builder,
2198 x + w - corner_sizes[1][0], y,
2199 corner_sizes[1][0], corner_sizes[1][1]);
2200 if (corner_sizes[2][0] > 0 && corner_sizes[2][1] > 0) /* Bottom right */
2201 load_float_vertex_data (ops_draw (builder, NULL), builder,
2202 x + w - corner_sizes[2][0], y + h - corner_sizes[2][1],
2203 corner_sizes[2][0], corner_sizes[2][1]);
2204 if (corner_sizes[3][0] > 0 && corner_sizes[3][1] > 0) /* Bottom left */
2205 load_float_vertex_data (ops_draw (builder, NULL), builder,
2206 x, y + h - corner_sizes[3][1],
2207 corner_sizes[3][0], corner_sizes[3][1]);
2208 /* Edges... */;
2209 if (edge_sizes[0] > 0) /* Top */
2210 load_float_vertex_data (ops_draw (builder, NULL), builder,
2211 x + corner_sizes[0][0], y,
2212 w - corner_sizes[0][0] - corner_sizes[1][0], edge_sizes[0]);
2213 if (edge_sizes[1] > 0) /* Right */
2214 load_float_vertex_data (ops_draw (builder, NULL), builder,
2215 x + w - edge_sizes[1], y + corner_sizes[1][1],
2216 edge_sizes[1], h - corner_sizes[1][1] - corner_sizes[2][1]);
2217 if (edge_sizes[2] > 0) /* Bottom */
2218 load_float_vertex_data (ops_draw (builder, NULL), builder,
2219 x + corner_sizes[3][0], y + h - edge_sizes[2],
2220 w - corner_sizes[3][0] - corner_sizes[2][0], edge_sizes[2]);
2221 if (edge_sizes[3] > 0) /* Left */
2222 load_float_vertex_data (ops_draw (builder, NULL), builder,
2223 x, y + corner_sizes[0][1],
2224 edge_sizes[3], h - corner_sizes[0][1] - corner_sizes[3][1]);
2225 }
2226
2227
2228 static GdkRGBA COLOR_WHITE = { 1, 1, 1, 1 };
2229 static inline void
render_outset_shadow_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2230 render_outset_shadow_node (GskGLRenderer *self,
2231 GskRenderNode *node,
2232 RenderOpBuilder *builder)
2233 {
2234 const float scale_x = builder->scale_x;
2235 const float scale_y = builder->scale_y;
2236 const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node);
2237 const GdkRGBA *color = gsk_outset_shadow_node_get_color (node);
2238 const float blur_radius = gsk_outset_shadow_node_get_blur_radius (node);
2239 const float blur_extra = blur_radius * 2.0f; /* 2.0 = shader radius_multiplier */
2240 const int extra_blur_pixels = (int) ceilf(blur_extra / 2.0 * MAX (scale_x, scale_y)); /* TODO: No need to MAX() her actually */
2241 const float spread = gsk_outset_shadow_node_get_spread (node);
2242 const float dx = gsk_outset_shadow_node_get_dx (node);
2243 const float dy = gsk_outset_shadow_node_get_dy (node);
2244 GskRoundedRect scaled_outline;
2245 int texture_width, texture_height;
2246 OpOutsetShadow *shadow;
2247 int blurred_texture_id;
2248 int cached_tid;
2249 bool do_slicing;
2250
2251 /* scaled_outline is the minimal outline we need to draw the given drop shadow,
2252 * enlarged by the spread and offset by the blur radius. */
2253 scaled_outline = *outline;
2254
2255 if (outline->bounds.size.width < blur_extra ||
2256 outline->bounds.size.height < blur_extra)
2257 {
2258 do_slicing = false;
2259 gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread);
2260 }
2261 else
2262 {
2263 /* Shrink our outline to the minimum size that can still hold all the border radii */
2264 gsk_rounded_rect_shrink_to_minimum (&scaled_outline);
2265 /* Increase by the spread */
2266 gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread);
2267 /* Grow bounds but don't grow corners */
2268 graphene_rect_inset (&scaled_outline.bounds, - blur_extra / 2.0, - blur_extra / 2.0);
2269 /* For the center part, we add a few pixels */
2270 scaled_outline.bounds.size.width += SHADOW_EXTRA_SIZE;
2271 scaled_outline.bounds.size.height += SHADOW_EXTRA_SIZE;
2272
2273 do_slicing = true;
2274 }
2275
2276 texture_width = (int)ceil ((scaled_outline.bounds.size.width + blur_extra) * scale_x);
2277 texture_height = (int)ceil ((scaled_outline.bounds.size.height + blur_extra) * scale_y);
2278
2279 scaled_outline.bounds.origin.x = extra_blur_pixels;
2280 scaled_outline.bounds.origin.y = extra_blur_pixels;
2281 scaled_outline.bounds.size.width = texture_width - (extra_blur_pixels * 2);
2282 scaled_outline.bounds.size.height = texture_height - (extra_blur_pixels * 2);
2283
2284 for (int i = 0; i < 4; i ++)
2285 {
2286 scaled_outline.corner[i].width *= scale_x;
2287 scaled_outline.corner[i].height *= scale_y;
2288 }
2289
2290 cached_tid = gsk_gl_shadow_cache_get_texture_id (&self->shadow_cache,
2291 self->gl_driver,
2292 &scaled_outline,
2293 blur_radius);
2294
2295 if (cached_tid == 0)
2296 {
2297 int texture_id, render_target;
2298 int prev_render_target;
2299 graphene_matrix_t prev_projection;
2300 graphene_rect_t prev_viewport;
2301 graphene_matrix_t item_proj;
2302
2303 gsk_gl_driver_create_render_target (self->gl_driver,
2304 texture_width, texture_height,
2305 GL_NEAREST, GL_NEAREST,
2306 &texture_id, &render_target);
2307 if (gdk_gl_context_has_debug (self->gl_context))
2308 {
2309 gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
2310 "Outset Shadow Temp %d", texture_id);
2311 gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target,
2312 "Outset Shadow FB Temp %d", render_target);
2313 }
2314
2315 ops_set_program (builder, &self->programs->color_program);
2316 init_projection_matrix (&item_proj,
2317 &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
2318
2319 prev_render_target = ops_set_render_target (builder, render_target);
2320 ops_begin (builder, OP_CLEAR);
2321 prev_projection = ops_set_projection (builder, &item_proj);
2322 ops_set_modelview (builder, NULL);
2323 prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
2324
2325 /* Draw outline */
2326 ops_push_clip (builder, &scaled_outline);
2327 ops_set_color (builder, &COLOR_WHITE);
2328 load_float_vertex_data (ops_draw (builder, NULL), builder,
2329 0, 0, texture_width, texture_height);
2330
2331 ops_pop_clip (builder);
2332 ops_set_viewport (builder, &prev_viewport);
2333 ops_pop_modelview (builder);
2334 ops_set_projection (builder, &prev_projection);
2335 ops_set_render_target (builder, prev_render_target);
2336
2337 /* Now blur the outline */
2338 blurred_texture_id = blur_texture (self, builder,
2339 &(TextureRegion) { texture_id, 0, 0, 1, 1 },
2340 texture_width,
2341 texture_height,
2342 blur_radius * scale_x,
2343 blur_radius * scale_y);
2344
2345 gsk_gl_driver_mark_texture_permanent (self->gl_driver, blurred_texture_id);
2346 gsk_gl_shadow_cache_commit (&self->shadow_cache,
2347 &scaled_outline,
2348 blur_radius,
2349 blurred_texture_id);
2350 }
2351 else
2352 {
2353 blurred_texture_id = cached_tid;
2354 }
2355
2356
2357 if (!do_slicing)
2358 {
2359 const float min_x = floorf (outline->bounds.origin.x - spread - (blur_extra / 2.0) + dx);
2360 const float min_y = floorf (outline->bounds.origin.y - spread - (blur_extra / 2.0) + dy);
2361
2362 ops_set_program (builder, &self->programs->outset_shadow_program);
2363 ops_set_color (builder, color);
2364 ops_set_texture (builder, blurred_texture_id);
2365
2366 shadow = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW);
2367 shadow->outline.value = transform_rect (self, builder, outline);
2368 shadow->outline.send = TRUE;
2369
2370 load_vertex_data_with_region (ops_draw (builder, NULL),
2371 &GRAPHENE_RECT_INIT (
2372 min_x, min_y,
2373 texture_width / scale_x, texture_height / scale_y
2374 ), builder,
2375 &(TextureRegion) { 0, 0, 0, 1, 1 },
2376 FALSE);
2377 return;
2378 }
2379
2380
2381 ops_set_program (builder, &self->programs->outset_shadow_program);
2382 ops_set_color (builder, color);
2383 ops_set_texture (builder, blurred_texture_id);
2384
2385 shadow = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW);
2386 shadow->outline.value = transform_rect (self, builder, outline);
2387 shadow->outline.send = TRUE;
2388
2389 {
2390 const float min_x = floorf (outline->bounds.origin.x - spread - (blur_extra / 2.0) + dx);
2391 const float min_y = floorf (outline->bounds.origin.y - spread - (blur_extra / 2.0) + dy);
2392 const float max_x = ceilf (outline->bounds.origin.x + outline->bounds.size.width +
2393 (blur_extra / 2.0) + dx + spread);
2394 const float max_y = ceilf (outline->bounds.origin.y + outline->bounds.size.height +
2395 (blur_extra / 2.0) + dy + spread);
2396 cairo_rectangle_int_t slices[9];
2397 TextureRegion tregs[9];
2398
2399 /* TODO: The slicing never changes and could just go into the cache */
2400 nine_slice_rounded_rect (&scaled_outline, slices);
2401 nine_slice_grow (slices, extra_blur_pixels);
2402 nine_slice_to_texture_coords (slices, texture_width, texture_height, tregs);
2403
2404 /* Our texture coordinates MUST be scaled, while the actual vertex coords
2405 * MUST NOT be scaled. */
2406
2407 /* Top left */
2408 if (slice_is_visible (&slices[NINE_SLICE_TOP_LEFT]))
2409 {
2410 load_vertex_data_with_region (ops_draw (builder, NULL),
2411 &GRAPHENE_RECT_INIT (
2412 min_x, min_y,
2413 slices[NINE_SLICE_TOP_LEFT].width / scale_x,
2414 slices[NINE_SLICE_TOP_LEFT].height / scale_y
2415 ),
2416 builder,
2417 &tregs[NINE_SLICE_TOP_LEFT], TRUE);
2418 }
2419
2420 /* Top center */
2421 if (slice_is_visible (&slices[NINE_SLICE_TOP_CENTER]))
2422 {
2423 const float width = (max_x - min_x) - (slices[NINE_SLICE_TOP_LEFT].width / scale_x +
2424 slices[NINE_SLICE_TOP_RIGHT].width / scale_x);
2425 load_vertex_data_with_region (ops_draw (builder, NULL),
2426 &GRAPHENE_RECT_INIT (
2427 min_x + (slices[NINE_SLICE_TOP_LEFT].width / scale_x),
2428 min_y,
2429 width,
2430 slices[NINE_SLICE_TOP_CENTER].height / scale_y
2431 ),
2432 builder,
2433 &tregs[NINE_SLICE_TOP_CENTER], TRUE);
2434 }
2435 /* Top right */
2436 if (slice_is_visible (&slices[NINE_SLICE_TOP_RIGHT]))
2437 {
2438 load_vertex_data_with_region (ops_draw (builder, NULL),
2439 &GRAPHENE_RECT_INIT (
2440 max_x - (slices[NINE_SLICE_TOP_RIGHT].width / scale_x),
2441 min_y,
2442 slices[NINE_SLICE_TOP_RIGHT].width / scale_x,
2443 slices[NINE_SLICE_TOP_RIGHT].height / scale_y
2444 ),
2445 builder,
2446 &tregs[NINE_SLICE_TOP_RIGHT], TRUE);
2447 }
2448
2449 /* Bottom right */
2450 if (slice_is_visible (&slices[NINE_SLICE_BOTTOM_RIGHT]))
2451 {
2452 load_vertex_data_with_region (ops_draw (builder, NULL),
2453 &GRAPHENE_RECT_INIT (
2454 max_x - (slices[NINE_SLICE_BOTTOM_RIGHT].width / scale_x),
2455 max_y - (slices[NINE_SLICE_BOTTOM_RIGHT].height / scale_y),
2456 slices[NINE_SLICE_BOTTOM_RIGHT].width / scale_x,
2457 slices[NINE_SLICE_BOTTOM_RIGHT].height / scale_y
2458 ),
2459 builder,
2460 &tregs[NINE_SLICE_BOTTOM_RIGHT], TRUE);
2461 }
2462
2463 /* Bottom left */
2464 if (slice_is_visible (&slices[NINE_SLICE_BOTTOM_LEFT]))
2465 {
2466 load_vertex_data_with_region (ops_draw (builder, NULL),
2467 &GRAPHENE_RECT_INIT (
2468 min_x,
2469 max_y - (slices[NINE_SLICE_BOTTOM_LEFT].height / scale_y),
2470 slices[NINE_SLICE_BOTTOM_LEFT].width / scale_x,
2471 slices[NINE_SLICE_BOTTOM_LEFT].height / scale_y
2472 ),
2473 builder,
2474 &tregs[NINE_SLICE_BOTTOM_LEFT], TRUE);
2475 }
2476
2477 /* Left side */
2478 if (slice_is_visible (&slices[NINE_SLICE_LEFT_CENTER]))
2479 {
2480 const float height = (max_y - min_y) - (slices[NINE_SLICE_TOP_LEFT].height / scale_y +
2481 slices[NINE_SLICE_BOTTOM_LEFT].height / scale_y);
2482 load_vertex_data_with_region (ops_draw (builder, NULL),
2483 &GRAPHENE_RECT_INIT (
2484 min_x,
2485 min_y + (slices[NINE_SLICE_TOP_LEFT].height / scale_y),
2486 slices[NINE_SLICE_LEFT_CENTER].width / scale_x,
2487 height
2488 ),
2489 builder,
2490 &tregs[NINE_SLICE_LEFT_CENTER], TRUE);
2491 }
2492
2493 /* Right side */
2494 if (slice_is_visible (&slices[NINE_SLICE_RIGHT_CENTER]))
2495 {
2496 const float height = (max_y - min_y) - (slices[NINE_SLICE_TOP_RIGHT].height / scale_y +
2497 slices[NINE_SLICE_BOTTOM_RIGHT].height / scale_y);
2498 load_vertex_data_with_region (ops_draw (builder, NULL),
2499 &GRAPHENE_RECT_INIT (
2500 max_x - (slices[NINE_SLICE_RIGHT_CENTER].width / scale_x),
2501 min_y + (slices[NINE_SLICE_TOP_LEFT].height / scale_y),
2502 slices[NINE_SLICE_RIGHT_CENTER].width / scale_x,
2503 height
2504 ),
2505 builder,
2506 &tregs[NINE_SLICE_RIGHT_CENTER], TRUE);
2507 }
2508
2509 /* Bottom side */
2510 if (slice_is_visible (&slices[NINE_SLICE_BOTTOM_CENTER]))
2511 {
2512 const float width = (max_x - min_x) - (slices[NINE_SLICE_BOTTOM_LEFT].width / scale_x +
2513 slices[NINE_SLICE_BOTTOM_RIGHT].width / scale_x);
2514 load_vertex_data_with_region (ops_draw (builder, NULL),
2515 &GRAPHENE_RECT_INIT (
2516 min_x + (slices[NINE_SLICE_BOTTOM_LEFT].width / scale_x),
2517 max_y - (slices[NINE_SLICE_BOTTOM_CENTER].height / scale_y),
2518 width,
2519 slices[NINE_SLICE_BOTTOM_CENTER].height / scale_y
2520 ),
2521 builder,
2522 &tregs[NINE_SLICE_BOTTOM_CENTER], TRUE);
2523 }
2524
2525 /* Middle */
2526 if (slice_is_visible (&slices[NINE_SLICE_CENTER]))
2527 {
2528 const float width = (max_x - min_x) - (slices[NINE_SLICE_LEFT_CENTER].width / scale_x +
2529 slices[NINE_SLICE_RIGHT_CENTER].width / scale_x);
2530 const float height = (max_y - min_y) - (slices[NINE_SLICE_TOP_CENTER].height / scale_y +
2531 slices[NINE_SLICE_BOTTOM_CENTER].height / scale_y);
2532
2533 load_vertex_data_with_region (ops_draw (builder, NULL),
2534 &GRAPHENE_RECT_INIT (
2535 min_x + (slices[NINE_SLICE_LEFT_CENTER].width / scale_x),
2536 min_y + (slices[NINE_SLICE_TOP_CENTER].height / scale_y),
2537 width, height
2538 ),
2539 builder,
2540 &tregs[NINE_SLICE_CENTER], TRUE);
2541 }
2542 }
2543 }
2544
2545 static inline void
render_shadow_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2546 render_shadow_node (GskGLRenderer *self,
2547 GskRenderNode *node,
2548 RenderOpBuilder *builder)
2549 {
2550 const gsize n_shadows = gsk_shadow_node_get_n_shadows (node);
2551 GskRenderNode *original_child = gsk_shadow_node_get_child (node);
2552 GskRenderNode *shadow_child = original_child;
2553 guint i;
2554
2555 /* Shadow nodes recolor every pixel of the source texture, but leave the alpha in tact.
2556 * If the child is a color matrix node that doesn't touch the alpha, we can throw that away. */
2557 if (gsk_render_node_get_node_type (shadow_child) == GSK_COLOR_MATRIX_NODE &&
2558 !color_matrix_modifies_alpha (shadow_child))
2559 {
2560 shadow_child = gsk_color_matrix_node_get_child (shadow_child);
2561 }
2562
2563 for (i = 0; i < n_shadows; i ++)
2564 {
2565 const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i);
2566 const float dx = shadow->dx;
2567 const float dy = shadow->dy;
2568 TextureRegion region;
2569 gboolean is_offscreen;
2570 graphene_rect_t bounds;
2571
2572 if (shadow->radius == 0 &&
2573 gsk_render_node_get_node_type (shadow_child) == GSK_TEXT_NODE)
2574 {
2575 ops_offset (builder, dx, dy);
2576 render_text_node (self, shadow_child, builder, &shadow->color, TRUE);
2577 ops_offset (builder, - dx, - dy);
2578 continue;
2579 }
2580
2581 if (gdk_rgba_is_clear (&shadow->color))
2582 continue;
2583
2584 if (node_is_invisible (shadow_child))
2585 continue;
2586
2587 if (shadow->radius > 0)
2588 {
2589 float min_x;
2590 float min_y;
2591 float max_x;
2592 float max_y;
2593
2594 region.texture_id = 0;
2595 blur_node (self, shadow_child, builder, shadow->radius, NO_CACHE_PLZ, ®ion,
2596 (float*[4]){&min_x, &max_x, &min_y, &max_y});
2597 bounds.origin.x = min_x - builder->dx;
2598 bounds.origin.y = min_y - builder->dy;
2599 bounds.size.width = max_x - min_x;
2600 bounds.size.height = max_y - min_y;
2601 is_offscreen = TRUE;
2602 }
2603 else if (dx == 0 && dy == 0)
2604 {
2605 continue; /* Invisible anyway */
2606 }
2607 else
2608 {
2609 if (!add_offscreen_ops (self, builder,
2610 &shadow_child->bounds,
2611 shadow_child, ®ion, &is_offscreen,
2612 RESET_CLIP | NO_CACHE_PLZ))
2613 g_assert_not_reached ();
2614
2615 bounds = shadow_child->bounds;
2616 }
2617
2618 ops_set_program (builder, &self->programs->coloring_program);
2619 ops_set_color (builder, &shadow->color);
2620 ops_set_texture (builder, region.texture_id);
2621
2622 ops_offset (builder, dx, dy);
2623 load_vertex_data_with_region (ops_draw (builder, NULL),
2624 &bounds, builder,
2625 ®ion,
2626 is_offscreen);
2627 ops_offset (builder, -dx, -dy);
2628 }
2629
2630 /* Now draw the child normally */
2631 gsk_gl_renderer_add_render_ops (self, original_child, builder);
2632 }
2633
2634 static inline void
render_cross_fade_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2635 render_cross_fade_node (GskGLRenderer *self,
2636 GskRenderNode *node,
2637 RenderOpBuilder *builder)
2638 {
2639 GskRenderNode *start_node = gsk_cross_fade_node_get_start_child (node);
2640 GskRenderNode *end_node = gsk_cross_fade_node_get_end_child (node);
2641 const float progress = gsk_cross_fade_node_get_progress (node);
2642 TextureRegion start_region;
2643 TextureRegion end_region;
2644 gboolean is_offscreen1, is_offscreen2;
2645 OpCrossFade *op;
2646
2647 if (progress <= 0)
2648 {
2649 gsk_gl_renderer_add_render_ops (self, start_node, builder);
2650 return;
2651 }
2652 else if (progress >= 1)
2653 {
2654 gsk_gl_renderer_add_render_ops (self, end_node, builder);
2655 return;
2656 }
2657
2658 if (equal_texture_nodes (start_node, end_node))
2659 {
2660 gsk_gl_renderer_add_render_ops (self, end_node, builder);
2661 return;
2662 }
2663
2664 /* TODO: We create 2 textures here as big as the cross-fade node, but both the
2665 * start and the end node might be a lot smaller than that. */
2666
2667 if (!add_offscreen_ops (self, builder,
2668 &node->bounds,
2669 start_node,
2670 &start_region, &is_offscreen1,
2671 FORCE_OFFSCREEN | RESET_CLIP))
2672 {
2673 gsk_gl_renderer_add_render_ops (self, end_node, builder);
2674 return;
2675 }
2676
2677 if (!add_offscreen_ops (self, builder,
2678 &node->bounds,
2679 end_node,
2680 &end_region, &is_offscreen2,
2681 FORCE_OFFSCREEN | RESET_CLIP))
2682 {
2683 const float prev_opacity = ops_set_opacity (builder, builder->current_opacity * progress);
2684 gsk_gl_renderer_add_render_ops (self, start_node, builder);
2685 ops_set_opacity (builder, prev_opacity);
2686
2687 return;
2688 }
2689
2690 ops_set_program (builder, &self->programs->cross_fade_program);
2691
2692 op = ops_begin (builder, OP_CHANGE_CROSS_FADE);
2693 op->progress = progress;
2694 op->source2 = end_region.texture_id;
2695
2696 ops_set_texture (builder, start_region.texture_id);
2697
2698 load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
2699 }
2700
2701 static inline void
render_blend_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2702 render_blend_node (GskGLRenderer *self,
2703 GskRenderNode *node,
2704 RenderOpBuilder *builder)
2705 {
2706 GskRenderNode *top_child = gsk_blend_node_get_top_child (node);
2707 GskRenderNode *bottom_child = gsk_blend_node_get_bottom_child (node);
2708 TextureRegion top_region;
2709 TextureRegion bottom_region;
2710 gboolean is_offscreen1, is_offscreen2;
2711 OpBlend *op;
2712
2713 /* TODO: We create 2 textures here as big as the blend node, but both the
2714 * start and the end node might be a lot smaller than that. */
2715 if (!add_offscreen_ops (self, builder,
2716 &node->bounds,
2717 bottom_child,
2718 &bottom_region, &is_offscreen1,
2719 FORCE_OFFSCREEN | RESET_CLIP))
2720 {
2721 gsk_gl_renderer_add_render_ops (self, top_child, builder);
2722 return;
2723 }
2724
2725 if (!add_offscreen_ops (self, builder,
2726 &node->bounds,
2727 top_child,
2728 &top_region, &is_offscreen2,
2729 FORCE_OFFSCREEN | RESET_CLIP))
2730 {
2731 load_vertex_data_with_region (ops_draw (builder, NULL),
2732 &node->bounds,
2733 builder,
2734 &bottom_region,
2735 TRUE);
2736 return;
2737 }
2738
2739 ops_set_program (builder, &self->programs->blend_program);
2740 ops_set_texture (builder, bottom_region.texture_id);
2741
2742 op = ops_begin (builder, OP_CHANGE_BLEND);
2743 op->source2 = top_region.texture_id;
2744 op->mode = gsk_blend_node_get_blend_mode (node);
2745
2746 load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
2747 }
2748
2749 static inline void
render_repeat_node(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)2750 render_repeat_node (GskGLRenderer *self,
2751 GskRenderNode *node,
2752 RenderOpBuilder *builder)
2753 {
2754 GskRenderNode *child = gsk_repeat_node_get_child (node);
2755 const graphene_rect_t *child_bounds = gsk_repeat_node_get_child_bounds (node);
2756 TextureRegion region;
2757 gboolean is_offscreen;
2758 OpRepeat *op;
2759
2760 if (node_is_invisible (child))
2761 return;
2762
2763 if (!graphene_rect_equal (child_bounds, &child->bounds))
2764 {
2765 /* TODO: Implement these repeat nodes. */
2766 render_fallback_node (self, node, builder);
2767 return;
2768 }
2769
2770 /* If the size of the repeat node is smaller than the size of the
2771 * child node, we don't repeat at all and can just draw that part
2772 * of the child texture... */
2773 if (graphene_rect_contains_rect (child_bounds, &node->bounds))
2774 {
2775 render_clipped_child (self, builder, &node->bounds, child);
2776 return;
2777 }
2778
2779 /* Draw the entire child on a texture */
2780 if (!add_offscreen_ops (self, builder,
2781 &child->bounds,
2782 child,
2783 ®ion, &is_offscreen,
2784 RESET_CLIP))
2785 g_assert_not_reached ();
2786
2787 ops_set_program (builder, &self->programs->repeat_program);
2788 ops_set_texture (builder, region.texture_id);
2789
2790 op = ops_begin (builder, OP_CHANGE_REPEAT);
2791 op->child_bounds[0] = (node->bounds.origin.x - child_bounds->origin.x) / child_bounds->size.width;
2792 op->child_bounds[1] = (node->bounds.origin.y - child_bounds->origin.y) / child_bounds->size.height;
2793 op->child_bounds[2] = node->bounds.size.width / child_bounds->size.width;
2794 op->child_bounds[3] = node->bounds.size.height / child_bounds->size.height;
2795
2796 op->texture_rect[0] = region.x;
2797 op->texture_rect[2] = region.x2;
2798
2799 if (is_offscreen)
2800 {
2801 op->texture_rect[1] = region.y2;
2802 op->texture_rect[3] = region.y;
2803 }
2804 else
2805 {
2806 op->texture_rect[1] = region.y;
2807 op->texture_rect[3] = region.y2;
2808 }
2809
2810 load_vertex_data_with_region (ops_draw (builder, NULL),
2811 &node->bounds, builder,
2812 ®ion,
2813 is_offscreen);
2814 }
2815
2816 static inline void
apply_viewport_op(const Program * program,const OpViewport * op)2817 apply_viewport_op (const Program *program,
2818 const OpViewport *op)
2819 {
2820 OP_PRINT (" -> New Viewport: %f, %f, %f, %f",
2821 op->viewport.origin.x, op->viewport.origin.y,
2822 op->viewport.size.width, op->viewport.size.height);
2823 glUniform4f (program->viewport_location,
2824 op->viewport.origin.x, op->viewport.origin.y,
2825 op->viewport.size.width, op->viewport.size.height);
2826 glViewport (0, 0, op->viewport.size.width, op->viewport.size.height);
2827 }
2828
2829 static inline void
apply_modelview_op(const Program * program,const OpMatrix * op)2830 apply_modelview_op (const Program *program,
2831 const OpMatrix *op)
2832 {
2833 float mat[16];
2834
2835 graphene_matrix_to_float (&op->matrix, mat);
2836 OP_PRINT (" -> Modelview { { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }",
2837 mat[0], mat[1], mat[2], mat[3],
2838 mat[4], mat[5], mat[6], mat[7],
2839 mat[8], mat[9], mat[10], mat[11],
2840 mat[12], mat[13], mat[14], mat[15]);
2841 glUniformMatrix4fv (program->modelview_location, 1, GL_FALSE, mat);
2842 }
2843
2844 static inline void
apply_projection_op(const Program * program,const OpMatrix * op)2845 apply_projection_op (const Program *program,
2846 const OpMatrix *op)
2847 {
2848 float mat[16];
2849
2850 graphene_matrix_to_float (&op->matrix, mat);
2851 OP_PRINT (" -> Projection { { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }",
2852 mat[0], mat[1], mat[2], mat[3],
2853 mat[4], mat[5], mat[6], mat[7],
2854 mat[8], mat[9], mat[10], mat[11],
2855 mat[12], mat[13], mat[14], mat[15]);
2856 glUniformMatrix4fv (program->projection_location, 1, GL_FALSE, mat);
2857 }
2858
2859 static inline void
apply_program_op(const Program * program,const OpProgram * op)2860 apply_program_op (const Program *program,
2861 const OpProgram *op)
2862 {
2863 OP_PRINT (" -> Program: %d", op->program->index);
2864 glUseProgram (op->program->id);
2865 }
2866
2867 static inline void
apply_render_target_op(GskGLRenderer * self,const OpRenderTarget * op)2868 apply_render_target_op (GskGLRenderer *self,
2869 const OpRenderTarget *op)
2870 {
2871 OP_PRINT (" -> Render Target: %d", op->render_target_id);
2872
2873 glBindFramebuffer (GL_FRAMEBUFFER, op->render_target_id);
2874
2875 if (op->render_target_id != 0)
2876 glDisable (GL_SCISSOR_TEST);
2877 else
2878 gsk_gl_renderer_setup_render_mode (self); /* Reset glScissor etc. */
2879 }
2880
2881 static inline void
apply_color_op(const Program * program,const OpColor * op)2882 apply_color_op (const Program *program,
2883 const OpColor *op)
2884 {
2885 OP_PRINT (" -> Color: (%f, %f, %f, %f)",
2886 op->rgba->red, op->rgba->green, op->rgba->blue, op->rgba->alpha);
2887 glUniform4fv (program->color.color_location, 1, (float *)op->rgba);
2888 }
2889
2890 static inline void
apply_opacity_op(const Program * program,const OpOpacity * op)2891 apply_opacity_op (const Program *program,
2892 const OpOpacity *op)
2893 {
2894 OP_PRINT (" -> Opacity %f", op->opacity);
2895 glUniform1f (program->alpha_location, op->opacity);
2896 }
2897
2898 static inline void
apply_source_texture_op(const Program * program,const OpTexture * op)2899 apply_source_texture_op (const Program *program,
2900 const OpTexture *op)
2901 {
2902 g_assert(op->texture_id != 0);
2903 OP_PRINT (" -> New texture: %d", op->texture_id);
2904 /* Use texture unit 0 for the source */
2905 glUniform1i (program->source_location, 0);
2906 glActiveTexture (GL_TEXTURE0);
2907 glBindTexture (GL_TEXTURE_2D, op->texture_id);
2908 }
2909
2910 static inline void
apply_source_extra_texture_op(const Program * program,const OpExtraTexture * op)2911 apply_source_extra_texture_op (const Program *program,
2912 const OpExtraTexture *op)
2913 {
2914 g_assert(op->texture_id != 0);
2915 OP_PRINT (" -> New extra texture %d: %d", op->idx, op->texture_id);
2916 glUniform1i (program->glshader.texture_locations[op->idx], op->idx);
2917 glActiveTexture (GL_TEXTURE0 + op->idx);
2918 glBindTexture (GL_TEXTURE_2D, op->texture_id);
2919 }
2920
2921 static inline void
apply_color_matrix_op(const Program * program,const OpColorMatrix * op)2922 apply_color_matrix_op (const Program *program,
2923 const OpColorMatrix *op)
2924 {
2925 OP_PRINT (" -> Color matrix. Send matrix: %d. Send offset: %d.",
2926 op->matrix.send, op->offset.send);
2927
2928 if (op->matrix.send)
2929 {
2930 float mat[16];
2931 graphene_matrix_to_float (op->matrix.value, mat);
2932 glUniformMatrix4fv (program->color_matrix.color_matrix_location, 1, GL_FALSE, mat);
2933 }
2934
2935 if (op->offset.send)
2936 {
2937 float vec[4];
2938 graphene_vec4_to_float (op->offset.value, vec);
2939 glUniform4fv (program->color_matrix.color_offset_location, 1, vec);
2940 }
2941 }
2942
2943 static inline void
apply_clip_op(const Program * program,const OpClip * op)2944 apply_clip_op (const Program *program,
2945 const OpClip *op)
2946 {
2947 int count;
2948
2949 if (op->send_corners)
2950 {
2951 OP_PRINT (" -> Clip: %s", gsk_rounded_rect_to_string (&op->clip));
2952 count = 3;
2953 }
2954 else
2955 {
2956 OP_PRINT (" -> clip: %f, %f, %f, %f",
2957 op->clip.bounds.origin.x, op->clip.bounds.origin.y,
2958 op->clip.bounds.size.width, op->clip.bounds.size.height);
2959 count = 1;
2960 }
2961
2962 glUniform4fv (program->clip_rect_location, count, (float *)&op->clip.bounds);
2963 }
2964
2965 static inline void
apply_inset_shadow_op(const Program * program,const OpShadow * op)2966 apply_inset_shadow_op (const Program *program,
2967 const OpShadow *op)
2968 {
2969 OP_PRINT (" -> inset shadow. Color: %s, Offset: (%f, %f), Spread: %f, Outline: %s",
2970 op->color.send ? gdk_rgba_to_string (op->color.value) : "don't send",
2971 op->offset.send ? op->offset.value[0] : -1337.0,
2972 op->offset.send ? op->offset.value[1] : -1337.0,
2973 op->spread.send ? op->spread.value : -1337.0,
2974 op->outline.send ? gsk_rounded_rect_to_string (&op->outline.value) : "don't send");
2975 if (op->outline.send)
2976 {
2977 if (op->outline.send_corners)
2978 glUniform4fv (program->inset_shadow.outline_rect_location, 3, (float *)&op->outline.value);
2979 else
2980 glUniform4fv (program->inset_shadow.outline_rect_location, 1, (float *)&op->outline.value);
2981 }
2982
2983 if (op->color.send)
2984 glUniform4fv (program->inset_shadow.color_location, 1, (float *)op->color.value);
2985
2986 if (op->spread.send)
2987 glUniform1f (program->inset_shadow.spread_location, op->spread.value);
2988
2989 if (op->offset.send)
2990 glUniform2fv (program->inset_shadow.offset_location, 1, op->offset.value);
2991 }
2992
2993 static inline void
apply_unblurred_outset_shadow_op(const Program * program,const OpShadow * op)2994 apply_unblurred_outset_shadow_op (const Program *program,
2995 const OpShadow *op)
2996 {
2997 OP_PRINT (" -> unblurred outset shadow");
2998
2999 if (op->outline.send)
3000 {
3001 if (op->outline.send_corners)
3002 glUniform4fv (program->unblurred_outset_shadow.outline_rect_location, 3, (float *)&op->outline.value);
3003 else
3004 glUniform4fv (program->unblurred_outset_shadow.outline_rect_location, 1, (float *)&op->outline.value);
3005 }
3006
3007 if (op->color.send)
3008 glUniform4fv (program->unblurred_outset_shadow.color_location, 1, (float *)op->color.value);
3009
3010 if (op->spread.send)
3011 glUniform1f (program->unblurred_outset_shadow.spread_location, op->spread.value);
3012
3013 if (op->offset.send)
3014 glUniform2fv (program->unblurred_outset_shadow.offset_location, 1, op->offset.value);
3015 }
3016
3017 static inline void
apply_outset_shadow_op(const Program * program,const OpOutsetShadow * op)3018 apply_outset_shadow_op (const Program *program,
3019 const OpOutsetShadow *op)
3020 {
3021 OP_PRINT (" -> outset shadow");
3022 glUniform4fv (program->outset_shadow.outline_rect_location, 3, (float *)&op->outline.value.bounds);
3023 }
3024
3025 static inline void
apply_linear_gradient_op(const Program * program,const OpLinearGradient * op)3026 apply_linear_gradient_op (const Program *program,
3027 const OpLinearGradient *op)
3028 {
3029 OP_PRINT (" -> Linear gradient");
3030 if (op->n_color_stops.send)
3031 glUniform1i (program->linear_gradient.num_color_stops_location, op->n_color_stops.value);
3032
3033 if (op->color_stops.send)
3034 glUniform1fv (program->linear_gradient.color_stops_location,
3035 op->n_color_stops.value * 5,
3036 (float *)op->color_stops.value);
3037
3038 glUniform4f (program->linear_gradient.points_location,
3039 op->start_point[0], op->start_point[1],
3040 op->end_point[0] - op->start_point[0], op->end_point[1] - op->start_point[1]);
3041 glUniform1i (program->linear_gradient.repeat_location, op->repeat);
3042 }
3043
3044 static inline void
apply_radial_gradient_op(const Program * program,const OpRadialGradient * op)3045 apply_radial_gradient_op (const Program *program,
3046 const OpRadialGradient *op)
3047 {
3048 float scale;
3049 float bias;
3050
3051 OP_PRINT (" -> Radial gradient");
3052 if (op->n_color_stops.send)
3053 glUniform1i (program->radial_gradient.num_color_stops_location, op->n_color_stops.value);
3054
3055 if (op->color_stops.send)
3056 glUniform1fv (program->radial_gradient.color_stops_location,
3057 op->n_color_stops.value * 5,
3058 (float *)op->color_stops.value);
3059
3060 scale = 1.0f / (op->end - op->start);
3061 bias = -op->start * scale;
3062
3063 glUniform1i (program->radial_gradient.repeat_location, op->repeat);
3064 glUniform2f (program->radial_gradient.range_location, scale, bias);
3065 glUniform4f (program->radial_gradient.geometry_location,
3066 op->center[0], op->center[1],
3067 1.0f / op->radius[0], 1.0f / op->radius[1]);
3068 }
3069
3070 static inline void
apply_conic_gradient_op(const Program * program,const OpConicGradient * op)3071 apply_conic_gradient_op (const Program *program,
3072 const OpConicGradient *op)
3073 {
3074 float bias;
3075 float scale;
3076
3077 OP_PRINT (" -> Conic gradient");
3078 if (op->n_color_stops.send)
3079 glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value);
3080
3081 if (op->color_stops.send)
3082 glUniform1fv (program->conic_gradient.color_stops_location,
3083 op->n_color_stops.value * 5,
3084 (float *)op->color_stops.value);
3085
3086 scale = 0.5f * M_1_PI;
3087 bias = op->angle * scale + 2.0f;
3088 glUniform4f (program->conic_gradient.geometry_location, op->center[0], op->center[1], scale, bias);
3089 }
3090
3091 static inline void
apply_border_op(const Program * program,const OpBorder * op)3092 apply_border_op (const Program *program,
3093 const OpBorder *op)
3094 {
3095 OP_PRINT (" -> Border Outline");
3096
3097 glUniform4fv (program->border.outline_rect_location, 3, (float *)&op->outline.bounds);
3098 }
3099
3100 static inline void
apply_gl_shader_args_op(const Program * program,const OpGLShader * op)3101 apply_gl_shader_args_op (const Program *program,
3102 const OpGLShader *op)
3103 {
3104 int n_uniforms, i;
3105 const GskGLUniform *uniforms;
3106
3107 OP_PRINT (" -> GL Shader Args");
3108
3109 glUniform2fv (program->glshader.size_location, 1, op->size);
3110
3111 uniforms = gsk_gl_shader_get_uniforms (op->shader, &n_uniforms);
3112 for (i = 0; i < n_uniforms; i++)
3113 {
3114 const GskGLUniform *u = &uniforms[i];
3115 const guchar *data = op->uniform_data + u->offset;
3116
3117 switch (u->type)
3118 {
3119 default:
3120 case GSK_GL_UNIFORM_TYPE_NONE:
3121 break;
3122 case GSK_GL_UNIFORM_TYPE_FLOAT:
3123 glUniform1fv (program->glshader.args_locations[i], 1, (const float *)data);
3124 break;
3125 case GSK_GL_UNIFORM_TYPE_INT:
3126 glUniform1iv (program->glshader.args_locations[i], 1, (const gint32 *)data);
3127 break;
3128 case GSK_GL_UNIFORM_TYPE_UINT:
3129 case GSK_GL_UNIFORM_TYPE_BOOL:
3130 glUniform1uiv (program->glshader.args_locations[i], 1, (const guint32 *) data);
3131 break;
3132 case GSK_GL_UNIFORM_TYPE_VEC2:
3133 glUniform2fv (program->glshader.args_locations[i], 1, (const float *)data);
3134 break;
3135 case GSK_GL_UNIFORM_TYPE_VEC3:
3136 glUniform3fv (program->glshader.args_locations[i], 1, (const float *)data);
3137 break;
3138 case GSK_GL_UNIFORM_TYPE_VEC4:
3139 glUniform4fv (program->glshader.args_locations[i], 1, (const float *)data);
3140 break;
3141 }
3142 }
3143 }
3144
3145 static inline void
apply_border_width_op(const Program * program,const OpBorder * op)3146 apply_border_width_op (const Program *program,
3147 const OpBorder *op)
3148 {
3149 OP_PRINT (" -> Border width (%f, %f, %f, %f)",
3150 op->widths[0], op->widths[1], op->widths[2], op->widths[3]);
3151
3152 glUniform4fv (program->border.widths_location, 1, op->widths);
3153 }
3154
3155 static inline void
apply_border_color_op(const Program * program,const OpBorder * op)3156 apply_border_color_op (const Program *program,
3157 const OpBorder *op)
3158 {
3159 OP_PRINT (" -> Border color: %s", gdk_rgba_to_string (op->color));
3160 glUniform4fv (program->border.color_location, 1, (float *)op->color);
3161 }
3162
3163 static inline void
apply_blur_op(const Program * program,const OpBlur * op)3164 apply_blur_op (const Program *program,
3165 const OpBlur *op)
3166 {
3167 OP_PRINT (" -> Blur");
3168 glUniform1f (program->blur.blur_radius_location, op->radius);
3169 glUniform2f (program->blur.blur_size_location, op->size.width, op->size.height);
3170 glUniform2f (program->blur.blur_dir_location, op->dir[0], op->dir[1]);
3171 }
3172
3173 static inline void
apply_cross_fade_op(const Program * program,const OpCrossFade * op)3174 apply_cross_fade_op (const Program *program,
3175 const OpCrossFade *op)
3176 {
3177 OP_PRINT (" -> Cross fade");
3178 /* End texture id */
3179 glUniform1i (program->cross_fade.source2_location, 1);
3180 glActiveTexture (GL_TEXTURE0 + 1);
3181 glBindTexture (GL_TEXTURE_2D, op->source2);
3182 /* progress */
3183 glUniform1f (program->cross_fade.progress_location, op->progress);
3184 }
3185
3186 static inline void
apply_blend_op(const Program * program,const OpBlend * op)3187 apply_blend_op (const Program *program,
3188 const OpBlend *op)
3189 {
3190 /* End texture id */
3191 glUniform1i (program->blend.source2_location, 1);
3192 glActiveTexture (GL_TEXTURE0 + 1);
3193 glBindTexture (GL_TEXTURE_2D, op->source2);
3194 /* progress */
3195 glUniform1i (program->blend.mode_location, op->mode);
3196 }
3197
3198 static inline void
apply_repeat_op(const Program * program,const OpRepeat * op)3199 apply_repeat_op (const Program *program,
3200 const OpRepeat *op)
3201 {
3202 OP_PRINT (" -> Repeat");
3203 glUniform4fv (program->repeat.child_bounds_location, 1, op->child_bounds);
3204 glUniform4fv (program->repeat.texture_rect_location, 1, op->texture_rect);
3205 }
3206
3207 static void
gsk_gl_renderer_dispose(GObject * gobject)3208 gsk_gl_renderer_dispose (GObject *gobject)
3209 {
3210 GskGLRenderer *self = GSK_GL_RENDERER (gobject);
3211
3212 ops_free (&self->op_builder);
3213
3214 G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
3215 }
3216
3217 static void
program_init(Program * program)3218 program_init (Program *program)
3219 {
3220 program->index = -1;
3221 program->state.opacity = 1.0f;
3222 }
3223
3224 static void
program_finalize(Program * program)3225 program_finalize (Program *program)
3226 {
3227 if (program->id > 0)
3228 glDeleteProgram (program->id);
3229 if (program->index == -1 &&
3230 program->glshader.compile_error != NULL)
3231 g_error_free (program->glshader.compile_error);
3232
3233 gsk_transform_unref (program->state.modelview);
3234 }
3235
3236 static void
program_free(Program * program)3237 program_free (Program *program)
3238 {
3239 program_finalize (program);
3240 g_free (program);
3241 }
3242
3243 static GskGLRendererPrograms *
gsk_gl_renderer_programs_new(void)3244 gsk_gl_renderer_programs_new (void)
3245 {
3246 GskGLRendererPrograms *programs;
3247 int i;
3248
3249 programs = g_new0 (GskGLRendererPrograms, 1);
3250 programs->ref_count = 1;
3251 for (i = 0; i < GL_N_PROGRAMS; i ++)
3252 program_init (&programs->programs[i]);
3253
3254 /* We use direct hash for performance, not string hash on the source, because we assume each caller
3255 * reuses a single GskGLShader for all uses and different callers will use different source content. */
3256 programs->custom_programs = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify)g_object_unref, (GDestroyNotify)program_free);
3257
3258 return programs;
3259 }
3260
3261 static GskGLRendererPrograms *
gsk_gl_renderer_programs_ref(GskGLRendererPrograms * programs)3262 gsk_gl_renderer_programs_ref (GskGLRendererPrograms *programs)
3263 {
3264 programs->ref_count++;
3265 return programs;
3266 }
3267
3268 /* Must be called with the context current */
3269 static void
gsk_gl_renderer_programs_unref(GskGLRendererPrograms * programs)3270 gsk_gl_renderer_programs_unref (GskGLRendererPrograms *programs)
3271 {
3272 int i;
3273 programs->ref_count--;
3274 if (programs->ref_count == 0)
3275 {
3276 for (i = 0; i < GL_N_PROGRAMS; i ++)
3277 program_finalize (&programs->programs[i]);
3278
3279 g_hash_table_destroy (programs->custom_programs);
3280 g_free (programs);
3281 }
3282 }
3283
3284 static Program *
gsk_gl_renderer_lookup_custom_program(GskGLRenderer * self,GskGLShader * shader)3285 gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self,
3286 GskGLShader *shader)
3287 {
3288 return g_hash_table_lookup (self->programs->custom_programs, shader);
3289 }
3290
3291 static Program *
gsk_gl_renderer_create_custom_program(GskGLRenderer * self,GskGLShader * shader)3292 gsk_gl_renderer_create_custom_program (GskGLRenderer *self,
3293 GskGLShader *shader)
3294 {
3295 Program *program = g_new0 (Program, 1);
3296
3297 program_init (program);
3298
3299 g_hash_table_insert (self->programs->custom_programs, g_object_ref (shader), program);
3300
3301 return program;
3302 }
3303
3304 static GskGLRendererPrograms *
gsk_gl_renderer_create_programs(GskGLRenderer * self,GError ** error)3305 gsk_gl_renderer_create_programs (GskGLRenderer *self,
3306 GError **error)
3307 {
3308 GskGLShaderBuilder shader_builder;
3309 GskGLRendererPrograms *programs = NULL;
3310 int i;
3311 static const struct {
3312 const char *resource_path;
3313 const char *name;
3314 } program_definitions[] = {
3315 { "/org/gtk/libgsk/gl/blend.glsl", "blend" },
3316 { "/org/gtk/libgsk/gl/blit.glsl", "blit" },
3317 { "/org/gtk/libgsk/gl/blur.glsl", "blur" },
3318 { "/org/gtk/libgsk/gl/border.glsl", "border" },
3319 { "/org/gtk/libgsk/gl/color_matrix.glsl", "color matrix" },
3320 { "/org/gtk/libgsk/gl/color.glsl", "color" },
3321 { "/org/gtk/libgsk/gl/coloring.glsl", "coloring" },
3322 { "/org/gtk/libgsk/gl/cross_fade.glsl", "cross fade" },
3323 { "/org/gtk/libgsk/gl/inset_shadow.glsl", "inset shadow" },
3324 { "/org/gtk/libgsk/gl/linear_gradient.glsl", "linear gradient" },
3325 { "/org/gtk/libgsk/gl/radial_gradient.glsl", "radial gradient" },
3326 { "/org/gtk/libgsk/gl/conic_gradient.glsl", "conic gradient" },
3327 { "/org/gtk/libgsk/gl/outset_shadow.glsl", "outset shadow" },
3328 { "/org/gtk/libgsk/gl/repeat.glsl", "repeat" },
3329 { "/org/gtk/libgsk/gl/unblurred_outset_shadow.glsl", "unblurred_outset shadow" },
3330 };
3331
3332 gsk_gl_shader_builder_init (&shader_builder,
3333 "/org/gtk/libgsk/gl/preamble.glsl",
3334 "/org/gtk/libgsk/gl/preamble.vs.glsl",
3335 "/org/gtk/libgsk/gl/preamble.fs.glsl");
3336
3337 g_assert (G_N_ELEMENTS (program_definitions) == GL_N_PROGRAMS);
3338
3339 init_shader_builder (self, &shader_builder);
3340
3341 programs = gsk_gl_renderer_programs_new ();
3342
3343 for (i = 0; i < GL_N_PROGRAMS; i ++)
3344 {
3345 Program *prog = &programs->programs[i];
3346
3347 prog->name = program_definitions[i].name;
3348 prog->index = i;
3349 prog->id = gsk_gl_shader_builder_create_program (&shader_builder,
3350 program_definitions[i].resource_path,
3351 NULL, 0, error);
3352 if (prog->id < 0)
3353 {
3354 g_clear_pointer (&programs, gsk_gl_renderer_programs_unref);
3355 goto out;
3356 }
3357
3358 INIT_COMMON_UNIFORM_LOCATION (prog, alpha);
3359 INIT_COMMON_UNIFORM_LOCATION (prog, source);
3360 INIT_COMMON_UNIFORM_LOCATION (prog, clip_rect);
3361 INIT_COMMON_UNIFORM_LOCATION (prog, viewport);
3362 INIT_COMMON_UNIFORM_LOCATION (prog, projection);
3363 INIT_COMMON_UNIFORM_LOCATION (prog, modelview);
3364 }
3365 /* color */
3366 INIT_PROGRAM_UNIFORM_LOCATION (color, color);
3367
3368 /* coloring */
3369 INIT_PROGRAM_UNIFORM_LOCATION (coloring, color);
3370
3371 /* color matrix */
3372 INIT_PROGRAM_UNIFORM_LOCATION (color_matrix, color_matrix);
3373 INIT_PROGRAM_UNIFORM_LOCATION (color_matrix, color_offset);
3374
3375 /* linear gradient */
3376 INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, color_stops);
3377 INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, num_color_stops);
3378 INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, repeat);
3379 INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, points);
3380
3381 /* radial gradient */
3382 INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops);
3383 INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, num_color_stops);
3384 INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, repeat);
3385 INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, geometry);
3386 INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, range);
3387
3388 /* conic gradient */
3389 INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops);
3390 INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops);
3391 INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, geometry);
3392
3393 /* blur */
3394 INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius);
3395 INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size);
3396 INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_dir);
3397
3398 /* inset shadow */
3399 INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, color);
3400 INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, spread);
3401 INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, offset);
3402 INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, outline_rect);
3403
3404 /* outset shadow */
3405 INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, color);
3406 INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, outline_rect);
3407
3408 /* unblurred outset shadow */
3409 INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, color);
3410 INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, spread);
3411 INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, offset);
3412 INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, outline_rect);
3413
3414 /* border */
3415 INIT_PROGRAM_UNIFORM_LOCATION (border, color);
3416 INIT_PROGRAM_UNIFORM_LOCATION (border, widths);
3417 INIT_PROGRAM_UNIFORM_LOCATION (border, outline_rect);
3418
3419 /* cross fade */
3420 INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, progress);
3421 INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, source2);
3422
3423 /* blend */
3424 INIT_PROGRAM_UNIFORM_LOCATION (blend, source2);
3425 INIT_PROGRAM_UNIFORM_LOCATION (blend, mode);
3426
3427 /* repeat */
3428 INIT_PROGRAM_UNIFORM_LOCATION (repeat, child_bounds);
3429 INIT_PROGRAM_UNIFORM_LOCATION (repeat, texture_rect);
3430
3431
3432 /* We initialize the alpha uniform here, since the default value is important.
3433 * We can't do it in the shader like a reasonable person would because that doesn't
3434 * work in gles. */
3435 for (i = 0; i < GL_N_PROGRAMS; i++)
3436 {
3437 glUseProgram(programs->programs[i].id);
3438 glUniform1f (programs->programs[i].alpha_location, 1.0);
3439 }
3440
3441 out:
3442 gsk_gl_shader_builder_finish (&shader_builder);
3443
3444 /* Check we indeed emitted an error if there was one */
3445 g_assert (programs || !error || *error);
3446
3447 return programs;
3448 }
3449
3450 static GskGLRendererPrograms *
get_programs_for_display(GskGLRenderer * self,GdkDisplay * display,GError ** error)3451 get_programs_for_display (GskGLRenderer *self,
3452 GdkDisplay *display,
3453 GError **error)
3454 {
3455 GskGLRendererPrograms *programs;
3456
3457 if (g_getenv ("GSK_NO_SHARED_PROGRAMS"))
3458 return gsk_gl_renderer_create_programs (self, error);
3459
3460 programs = (GskGLRendererPrograms *)g_object_get_data (G_OBJECT (display), "gsk-gl-programs");
3461 if (programs == NULL)
3462 {
3463 programs = gsk_gl_renderer_create_programs (self, error);
3464 if (programs)
3465 g_object_set_data_full (G_OBJECT (display), "gsk-gl-programs",
3466 programs,
3467 (GDestroyNotify) gsk_gl_renderer_programs_unref);
3468 }
3469
3470 if (programs)
3471 return gsk_gl_renderer_programs_ref (programs);
3472 return NULL;
3473 }
3474
3475
3476 static GskGLTextureAtlases *
get_texture_atlases_for_display(GdkDisplay * display)3477 get_texture_atlases_for_display (GdkDisplay *display)
3478 {
3479 GskGLTextureAtlases *atlases;
3480
3481 if (g_getenv ("GSK_NO_SHARED_CACHES"))
3482 return gsk_gl_texture_atlases_new ();
3483
3484 atlases = (GskGLTextureAtlases*)g_object_get_data (G_OBJECT (display), "gsk-gl-texture-atlases");
3485 if (atlases == NULL)
3486 {
3487 atlases = gsk_gl_texture_atlases_new ();
3488 g_object_set_data_full (G_OBJECT (display), "gsk-gl-texture-atlases",
3489 atlases,
3490 (GDestroyNotify) gsk_gl_texture_atlases_unref);
3491 }
3492
3493 return gsk_gl_texture_atlases_ref (atlases);
3494 }
3495
3496 static GskGLGlyphCache *
get_glyph_cache_for_display(GdkDisplay * display,GskGLTextureAtlases * atlases)3497 get_glyph_cache_for_display (GdkDisplay *display,
3498 GskGLTextureAtlases *atlases)
3499 {
3500 GskGLGlyphCache *glyph_cache;
3501
3502 if (g_getenv ("GSK_NO_SHARED_CACHES"))
3503 return gsk_gl_glyph_cache_new (display, atlases);
3504
3505 glyph_cache = (GskGLGlyphCache*)g_object_get_data (G_OBJECT (display), "gsk-gl-glyph-cache");
3506 if (glyph_cache == NULL)
3507 {
3508 glyph_cache = gsk_gl_glyph_cache_new (display, atlases);
3509 g_object_set_data_full (G_OBJECT (display), "gsk-gl-glyph-cache",
3510 glyph_cache,
3511 (GDestroyNotify) gsk_gl_glyph_cache_unref);
3512 }
3513
3514 return gsk_gl_glyph_cache_ref (glyph_cache);
3515 }
3516
3517 static GskGLIconCache *
get_icon_cache_for_display(GdkDisplay * display,GskGLTextureAtlases * atlases)3518 get_icon_cache_for_display (GdkDisplay *display,
3519 GskGLTextureAtlases *atlases)
3520 {
3521 GskGLIconCache *icon_cache;
3522
3523 if (g_getenv ("GSK_NO_SHARED_CACHES"))
3524 return gsk_gl_icon_cache_new (display, atlases);
3525
3526 icon_cache = (GskGLIconCache*)g_object_get_data (G_OBJECT (display), "gsk-gl-icon-cache");
3527 if (icon_cache == NULL)
3528 {
3529 icon_cache = gsk_gl_icon_cache_new (display, atlases);
3530 g_object_set_data_full (G_OBJECT (display), "gsk-gl-icon-cache",
3531 icon_cache,
3532 (GDestroyNotify) gsk_gl_icon_cache_unref);
3533 }
3534
3535 return gsk_gl_icon_cache_ref (icon_cache);
3536 }
3537
3538 static gboolean
gsk_gl_renderer_realize(GskRenderer * renderer,GdkSurface * surface,GError ** error)3539 gsk_gl_renderer_realize (GskRenderer *renderer,
3540 GdkSurface *surface,
3541 GError **error)
3542 {
3543 GskGLRenderer *self = GSK_GL_RENDERER (renderer);
3544 gint64 before G_GNUC_UNUSED;
3545
3546 before = GDK_PROFILER_CURRENT_TIME;
3547 /* If we didn't get a GdkGLContext before realization, try creating
3548 * one now, for our exclusive use.
3549 */
3550 if (self->gl_context == NULL)
3551 {
3552 self->gl_context = gdk_surface_create_gl_context (surface, error);
3553 if (self->gl_context == NULL)
3554 return FALSE;
3555 }
3556
3557 if (!gdk_gl_context_realize (self->gl_context, error))
3558 return FALSE;
3559
3560 gdk_gl_context_make_current (self->gl_context);
3561
3562 g_assert (self->gl_driver == NULL);
3563 self->gl_profiler = gsk_gl_profiler_new (self->gl_context);
3564 self->gl_driver = gsk_gl_driver_new (self->gl_context);
3565
3566 GSK_RENDERER_NOTE (renderer, OPENGL, g_message ("Creating buffers and programs"));
3567 self->programs = get_programs_for_display (self, gdk_surface_get_display (surface), error);
3568 if (self->programs == NULL)
3569 return FALSE;
3570 self->op_builder.programs = self->programs;
3571
3572 self->atlases = get_texture_atlases_for_display (gdk_surface_get_display (surface));
3573 self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface), self->atlases);
3574 self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface), self->atlases);
3575 gsk_gl_shadow_cache_init (&self->shadow_cache);
3576
3577 gdk_profiler_end_mark (before, "gl renderer realize", NULL);
3578
3579 return TRUE;
3580 }
3581
3582 static void
gsk_gl_renderer_unrealize(GskRenderer * renderer)3583 gsk_gl_renderer_unrealize (GskRenderer *renderer)
3584 {
3585 GskGLRenderer *self = GSK_GL_RENDERER (renderer);
3586
3587 if (self->gl_context == NULL)
3588 return;
3589
3590 gdk_gl_context_make_current (self->gl_context);
3591
3592 /* We don't need to iterate to destroy the associated GL resources,
3593 * as they will be dropped when we finalize the GskGLDriver
3594 */
3595 ops_reset (&self->op_builder);
3596 self->op_builder.programs = NULL;
3597
3598 g_clear_pointer (&self->programs, gsk_gl_renderer_programs_unref);
3599 g_clear_pointer (&self->glyph_cache, gsk_gl_glyph_cache_unref);
3600 g_clear_pointer (&self->icon_cache, gsk_gl_icon_cache_unref);
3601 g_clear_pointer (&self->atlases, gsk_gl_texture_atlases_unref);
3602 gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver);
3603
3604 g_clear_object (&self->gl_profiler);
3605 g_clear_object (&self->gl_driver);
3606
3607 if (self->gl_context == gdk_gl_context_get_current ())
3608 gdk_gl_context_clear_current ();
3609
3610 g_clear_object (&self->gl_context);
3611 }
3612
3613 static void
gsk_gl_renderer_clear_tree(GskGLRenderer * self)3614 gsk_gl_renderer_clear_tree (GskGLRenderer *self)
3615 {
3616 if (self->gl_context == NULL)
3617 return;
3618
3619 gdk_gl_context_make_current (self->gl_context);
3620
3621 ops_reset (&self->op_builder);
3622
3623 #ifdef G_ENABLE_DEBUG
3624 int removed_textures = gsk_gl_driver_collect_textures (self->gl_driver);
3625 GSK_RENDERER_NOTE (GSK_RENDERER (self), OPENGL, g_message ("Collected: %d textures", removed_textures));
3626 #else
3627 gsk_gl_driver_collect_textures (self->gl_driver);
3628 #endif
3629 }
3630
3631 static void
gsk_gl_renderer_clear(GskGLRenderer * self)3632 gsk_gl_renderer_clear (GskGLRenderer *self)
3633 {
3634 GSK_RENDERER_NOTE (GSK_RENDERER (self), OPENGL, g_message ("Clearing viewport"));
3635 glClearColor (0, 0, 0, 0);
3636 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
3637 }
3638
3639 static void
gsk_gl_renderer_setup_render_mode(GskGLRenderer * self)3640 gsk_gl_renderer_setup_render_mode (GskGLRenderer *self)
3641 {
3642 if (self->render_region == NULL)
3643 {
3644 glDisable (GL_SCISSOR_TEST);
3645 }
3646 else
3647 {
3648 GdkSurface *surface = gsk_renderer_get_surface (GSK_RENDERER (self));
3649 cairo_rectangle_int_t extents;
3650 int surface_height;
3651
3652 g_assert (cairo_region_num_rectangles (self->render_region) == 1);
3653
3654 surface_height = gdk_surface_get_height (surface) * self->scale_factor;
3655 cairo_region_get_rectangle (self->render_region, 0, &extents);
3656
3657 glEnable (GL_SCISSOR_TEST);
3658 glScissor (extents.x * self->scale_factor,
3659 surface_height - (extents.height * self->scale_factor) - (extents.y * self->scale_factor),
3660 extents.width * self->scale_factor,
3661 extents.height * self->scale_factor);
3662 }
3663 }
3664
3665 static void
gsk_gl_renderer_add_render_ops(GskGLRenderer * self,GskRenderNode * node,RenderOpBuilder * builder)3666 gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
3667 GskRenderNode *node,
3668 RenderOpBuilder *builder)
3669 {
3670 /* This can still happen, even if the render nodes are created using
3671 * GtkSnapshot, so let's just be safe. */
3672 if (node_is_invisible (node))
3673 return;
3674
3675 /* Check whether the render node is entirely out of the current
3676 * already transformed clip region */
3677 {
3678 graphene_rect_t transformed_node_bounds;
3679
3680 ops_transform_bounds_modelview (builder,
3681 &node->bounds,
3682 &transformed_node_bounds);
3683
3684 if (!graphene_rect_intersects (&builder->current_clip->bounds,
3685 &transformed_node_bounds))
3686 return;
3687 }
3688
3689 switch (gsk_render_node_get_node_type (node))
3690 {
3691 case GSK_NOT_A_RENDER_NODE:
3692 g_assert_not_reached ();
3693
3694 case GSK_CONTAINER_NODE:
3695 {
3696 guint i, p;
3697
3698 for (i = 0, p = gsk_container_node_get_n_children (node); i < p; i ++)
3699 {
3700 GskRenderNode *child = gsk_container_node_get_child (node, i);
3701
3702 gsk_gl_renderer_add_render_ops (self, child, builder);
3703 }
3704 }
3705 break;
3706
3707 case GSK_DEBUG_NODE:
3708 {
3709 const char *message = gsk_debug_node_get_message (node);
3710 if (message)
3711 ops_push_debug_group (builder, message);
3712 gsk_gl_renderer_add_render_ops (self,
3713 gsk_debug_node_get_child (node),
3714 builder);
3715 if (message)
3716 ops_pop_debug_group (builder);
3717 }
3718 break;
3719
3720 case GSK_COLOR_NODE:
3721 render_color_node (self, node, builder);
3722 break;
3723
3724 case GSK_TEXTURE_NODE:
3725 render_texture_node (self, node, builder);
3726 break;
3727
3728 case GSK_TRANSFORM_NODE:
3729 render_transform_node (self, node, builder);
3730 break;
3731
3732 case GSK_OPACITY_NODE:
3733 render_opacity_node (self, node, builder);
3734 break;
3735
3736 case GSK_LINEAR_GRADIENT_NODE:
3737 case GSK_REPEATING_LINEAR_GRADIENT_NODE:
3738 render_linear_gradient_node (self, node, builder);
3739 break;
3740
3741 case GSK_RADIAL_GRADIENT_NODE:
3742 case GSK_REPEATING_RADIAL_GRADIENT_NODE:
3743 render_radial_gradient_node (self, node, builder);
3744 break;
3745
3746 case GSK_CONIC_GRADIENT_NODE:
3747 render_conic_gradient_node (self, node, builder);
3748 break;
3749
3750 case GSK_CLIP_NODE:
3751 render_clip_node (self, node, builder);
3752 break;
3753
3754 case GSK_ROUNDED_CLIP_NODE:
3755 render_rounded_clip_node (self, node, builder);
3756 break;
3757
3758 case GSK_TEXT_NODE:
3759 render_text_node (self, node, builder,
3760 gsk_text_node_get_color (node), FALSE);
3761 break;
3762
3763 case GSK_COLOR_MATRIX_NODE:
3764 render_color_matrix_node (self, node, builder);
3765 break;
3766
3767 case GSK_BLUR_NODE:
3768 render_blur_node (self, node, builder);
3769 break;
3770
3771 case GSK_INSET_SHADOW_NODE:
3772 if (gsk_inset_shadow_node_get_blur_radius (node) > 0)
3773 render_inset_shadow_node (self, node, builder);
3774 else
3775 render_unblurred_inset_shadow_node (self, node, builder);
3776 break;
3777
3778 case GSK_OUTSET_SHADOW_NODE:
3779 if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
3780 render_outset_shadow_node (self, node, builder);
3781 else
3782 render_unblurred_outset_shadow_node (self, node, builder);
3783 break;
3784
3785 case GSK_SHADOW_NODE:
3786 render_shadow_node (self, node, builder);
3787 break;
3788
3789 case GSK_BORDER_NODE:
3790 render_border_node (self, node, builder);
3791 break;
3792
3793 case GSK_CROSS_FADE_NODE:
3794 render_cross_fade_node (self, node, builder);
3795 break;
3796
3797 case GSK_BLEND_NODE:
3798 render_blend_node (self, node, builder);
3799 break;
3800
3801 case GSK_REPEAT_NODE:
3802 render_repeat_node (self, node, builder);
3803 break;
3804
3805 case GSK_GL_SHADER_NODE:
3806 render_gl_shader_node (self, node, builder);
3807 break;
3808
3809 case GSK_CAIRO_NODE:
3810 default:
3811 {
3812 render_fallback_node (self, node, builder);
3813 }
3814 }
3815 }
3816
3817 static gboolean
add_offscreen_ops(GskGLRenderer * self,RenderOpBuilder * builder,const graphene_rect_t * bounds,GskRenderNode * child_node,TextureRegion * texture_region_out,gboolean * is_offscreen,guint flags)3818 add_offscreen_ops (GskGLRenderer *self,
3819 RenderOpBuilder *builder,
3820 const graphene_rect_t *bounds,
3821 GskRenderNode *child_node,
3822 TextureRegion *texture_region_out,
3823 gboolean *is_offscreen,
3824 guint flags)
3825 {
3826 const float dx = builder->dx;
3827 const float dy = builder->dy;
3828 float scaled_width, scaled_height;
3829 float scale_x;
3830 float scale_y;
3831 int render_target;
3832 int prev_render_target;
3833 graphene_matrix_t prev_projection;
3834 graphene_rect_t prev_viewport;
3835 graphene_matrix_t item_proj;
3836 float prev_opacity = 1.0;
3837 int texture_id = 0;
3838 int filter;
3839 GskTextureKey key;
3840 int cached_id;
3841 graphene_rect_t viewport;
3842
3843 if (node_is_invisible (child_node))
3844 {
3845 /* Just to be safe */
3846 *is_offscreen = FALSE;
3847 init_full_texture_region (texture_region_out, 0);
3848 return FALSE;
3849 }
3850
3851 /* We need the child node as a texture. If it already is one, we don't need to draw
3852 * it on a framebuffer of course. */
3853 if (gsk_render_node_get_node_type (child_node) == GSK_TEXTURE_NODE &&
3854 (flags & FORCE_OFFSCREEN) == 0)
3855 {
3856 GdkTexture *texture = gsk_texture_node_get_texture (child_node);
3857 upload_texture (self, texture, texture_region_out);
3858 *is_offscreen = FALSE;
3859 return TRUE;
3860 }
3861
3862 if (flags & LINEAR_FILTER)
3863 filter = GL_LINEAR;
3864 else
3865 filter = GL_NEAREST;
3866
3867 /* Check if we've already cached the drawn texture. */
3868 key.pointer = child_node;
3869 key.pointer_is_child = TRUE; /* Don't conflict with the child using the cache too */
3870 key.parent_rect = *bounds;
3871 key.scale_x = builder->scale_x;
3872 key.scale_y = builder->scale_y;
3873 key.filter = filter;
3874 cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
3875
3876 if (cached_id != 0)
3877 {
3878 init_full_texture_region (texture_region_out, cached_id);
3879 /* We didn't render it offscreen, but hand out an offscreen texture id */
3880 *is_offscreen = TRUE;
3881 return TRUE;
3882 }
3883
3884 scale_x = builder->scale_x;
3885 scale_y = builder->scale_y;
3886
3887 /* Tweak the scale factor so that the required texture doesn't
3888 * exceed the max texture limit. This will render with a lower
3889 * resolution, but this is better than clipping.
3890 */
3891 {
3892 const int max_texture_size = gsk_gl_driver_get_max_texture_size (self->gl_driver);
3893
3894 scaled_width = ceilf (bounds->size.width * scale_x);
3895 if (scaled_width > max_texture_size)
3896 {
3897 scale_x *= (float)max_texture_size / scaled_width;
3898 scaled_width = max_texture_size;
3899 }
3900
3901 scaled_height = ceilf (bounds->size.height * scale_y);
3902 if (scaled_height > max_texture_size)
3903 {
3904 scale_y *= (float)max_texture_size / scaled_height;
3905 scaled_height = max_texture_size;
3906 }
3907 }
3908
3909 gsk_gl_driver_create_render_target (self->gl_driver,
3910 scaled_width, scaled_height,
3911 filter, filter,
3912 &texture_id, &render_target);
3913 if (gdk_gl_context_has_debug (self->gl_context))
3914 {
3915 gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
3916 "Offscreen<%s> %d",
3917 g_type_name_from_instance ((GTypeInstance *) child_node),
3918 texture_id);
3919 gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target,
3920 "Offscreen<%s> FB %d",
3921 g_type_name_from_instance ((GTypeInstance *) child_node),
3922 render_target);
3923 }
3924
3925 ops_transform_bounds_modelview (builder, bounds, &viewport);
3926 /* Code above will scale the size with the scale we use in the render ops,
3927 * but for the viewport size, we need our own size limited by the texture size */
3928 viewport.size.width = scaled_width;
3929 viewport.size.height = scaled_height;
3930
3931 init_projection_matrix (&item_proj, &viewport);
3932 prev_render_target = ops_set_render_target (builder, render_target);
3933 /* Clear since we use this rendertarget for the first time */
3934 ops_begin (builder, OP_CLEAR);
3935 prev_projection = ops_set_projection (builder, &item_proj);
3936 ops_set_modelview (builder, gsk_transform_scale (NULL, scale_x, scale_y));
3937 prev_viewport = ops_set_viewport (builder, &viewport);
3938 if (flags & RESET_CLIP)
3939 ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
3940
3941 builder->dx = dx;
3942 builder->dy = dy;
3943
3944 prev_opacity = ops_set_opacity (builder, 1.0);
3945
3946 gsk_gl_renderer_add_render_ops (self, child_node, builder);
3947
3948 #ifdef G_ENABLE_DEBUG
3949 if (G_UNLIKELY (flags & DUMP_FRAMEBUFFER))
3950 {
3951 static int k;
3952 ops_dump_framebuffer (builder,
3953 g_strdup_printf ("%s_%p_%d.png",
3954 g_type_name_from_instance ((GTypeInstance *) child_node),
3955 child_node,
3956 k ++),
3957 scaled_width, scaled_height);
3958 }
3959 #endif
3960
3961 ops_set_opacity (builder, prev_opacity);
3962
3963 builder->dx = dx;
3964 builder->dy = dy;
3965
3966 if (flags & RESET_CLIP)
3967 ops_pop_clip (builder);
3968
3969 ops_set_viewport (builder, &prev_viewport);
3970 ops_pop_modelview (builder);
3971 ops_set_projection (builder, &prev_projection);
3972 ops_set_render_target (builder, prev_render_target);
3973
3974 *is_offscreen = TRUE;
3975 init_full_texture_region (texture_region_out, texture_id);
3976
3977 if ((flags & NO_CACHE_PLZ) == 0)
3978 gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, texture_id);
3979
3980 return TRUE;
3981 }
3982
3983 static void
gsk_gl_renderer_render_ops(GskGLRenderer * self)3984 gsk_gl_renderer_render_ops (GskGLRenderer *self)
3985 {
3986 const Program *program = NULL;
3987 const gsize vertex_data_size = self->op_builder.vertices->len * sizeof (GskQuadVertex);
3988 const float *vertex_data = (float *)self->op_builder.vertices->data;
3989 OpBufferIter iter;
3990 OpKind kind;
3991 gpointer ptr;
3992 GLuint buffer_id, vao_id;
3993
3994 #if DEBUG_OPS
3995 g_print ("============================================\n");
3996 #endif
3997
3998 glGenVertexArrays (1, &vao_id);
3999 glBindVertexArray (vao_id);
4000
4001 glGenBuffers (1, &buffer_id);
4002 glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
4003
4004 glBufferData (GL_ARRAY_BUFFER, vertex_data_size, vertex_data, GL_STATIC_DRAW);
4005
4006 /* 0 = position location */
4007 glEnableVertexAttribArray (0);
4008 glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE,
4009 sizeof (GskQuadVertex),
4010 (void *) G_STRUCT_OFFSET (GskQuadVertex, position));
4011 /* 1 = texture coord location */
4012 glEnableVertexAttribArray (1);
4013 glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE,
4014 sizeof (GskQuadVertex),
4015 (void *) G_STRUCT_OFFSET (GskQuadVertex, uv));
4016
4017 op_buffer_iter_init (&iter, ops_get_buffer (&self->op_builder));
4018 while ((ptr = op_buffer_iter_next (&iter, &kind)))
4019 {
4020 if (kind == OP_NONE)
4021 continue;
4022
4023 if (program == NULL &&
4024 kind != OP_PUSH_DEBUG_GROUP &&
4025 kind != OP_POP_DEBUG_GROUP &&
4026 kind != OP_CHANGE_PROGRAM &&
4027 kind != OP_CHANGE_RENDER_TARGET &&
4028 kind != OP_CLEAR)
4029 continue;
4030
4031 OP_PRINT ("Op %u: %u", iter.pos - 2, kind);
4032
4033 switch (kind)
4034 {
4035 case OP_CHANGE_PROJECTION:
4036 apply_projection_op (program, ptr);
4037 break;
4038
4039 case OP_CHANGE_MODELVIEW:
4040 apply_modelview_op (program, ptr);
4041 break;
4042
4043 case OP_CHANGE_PROGRAM:
4044 {
4045 const OpProgram *op = ptr;
4046 apply_program_op (program, op);
4047 program = op->program;
4048 break;
4049 }
4050
4051 case OP_CHANGE_RENDER_TARGET:
4052 apply_render_target_op (self, ptr);
4053 break;
4054
4055 case OP_CLEAR:
4056 OP_PRINT ("-> CLEAR");
4057 glClearColor (0, 0, 0, 0);
4058 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
4059 break;
4060
4061 case OP_CHANGE_VIEWPORT:
4062 apply_viewport_op (program, ptr);
4063 break;
4064
4065 case OP_CHANGE_OPACITY:
4066 apply_opacity_op (program, ptr);
4067 break;
4068
4069 case OP_CHANGE_COLOR_MATRIX:
4070 apply_color_matrix_op (program, ptr);
4071 break;
4072
4073 case OP_CHANGE_COLOR:
4074 /*g_assert (program == &self->color_program || program == &self->coloring_program ||*/
4075 /*program == &self->shadow_program);*/
4076 apply_color_op (program, ptr);
4077 break;
4078
4079 case OP_CHANGE_BORDER_COLOR:
4080 apply_border_color_op (program, ptr);
4081 break;
4082
4083 case OP_CHANGE_CLIP:
4084 apply_clip_op (program, ptr);
4085 break;
4086
4087 case OP_CHANGE_SOURCE_TEXTURE:
4088 apply_source_texture_op (program, ptr);
4089 break;
4090
4091 case OP_CHANGE_EXTRA_SOURCE_TEXTURE:
4092 apply_source_extra_texture_op (program, ptr);
4093 break;
4094
4095 case OP_CHANGE_CROSS_FADE:
4096 g_assert (program == &self->programs->cross_fade_program);
4097 apply_cross_fade_op (program, ptr);
4098 break;
4099
4100 case OP_CHANGE_BLEND:
4101 g_assert (program == &self->programs->blend_program);
4102 apply_blend_op (program, ptr);
4103 break;
4104
4105 case OP_CHANGE_LINEAR_GRADIENT:
4106 apply_linear_gradient_op (program, ptr);
4107 break;
4108
4109 case OP_CHANGE_RADIAL_GRADIENT:
4110 apply_radial_gradient_op (program, ptr);
4111 break;
4112
4113 case OP_CHANGE_CONIC_GRADIENT:
4114 apply_conic_gradient_op (program, ptr);
4115 break;
4116
4117 case OP_CHANGE_BLUR:
4118 apply_blur_op (program, ptr);
4119 break;
4120
4121 case OP_CHANGE_INSET_SHADOW:
4122 apply_inset_shadow_op (program, ptr);
4123 break;
4124
4125 case OP_CHANGE_OUTSET_SHADOW:
4126 apply_outset_shadow_op (program, ptr);
4127 break;
4128
4129 case OP_CHANGE_BORDER:
4130 apply_border_op (program, ptr);
4131 break;
4132
4133 case OP_CHANGE_BORDER_WIDTH:
4134 apply_border_width_op (program, ptr);
4135 break;
4136
4137 case OP_CHANGE_UNBLURRED_OUTSET_SHADOW:
4138 apply_unblurred_outset_shadow_op (program, ptr);
4139 break;
4140
4141 case OP_CHANGE_REPEAT:
4142 apply_repeat_op (program, ptr);
4143 break;
4144
4145 case OP_CHANGE_GL_SHADER_ARGS:
4146 apply_gl_shader_args_op (program, ptr);
4147 break;
4148
4149 case OP_DRAW:
4150 {
4151 const OpDraw *op = ptr;
4152
4153 OP_PRINT (" -> draw %ld, size %ld and program %d: %s",
4154 op->vao_offset, op->vao_size, program->index,
4155 program->name ?: "");
4156 glDrawArrays (GL_TRIANGLES, op->vao_offset, op->vao_size);
4157 break;
4158 }
4159
4160 case OP_DUMP_FRAMEBUFFER:
4161 {
4162 const OpDumpFrameBuffer *op = ptr;
4163
4164 dump_framebuffer (op->filename, op->width, op->height);
4165 break;
4166 }
4167
4168 case OP_PUSH_DEBUG_GROUP:
4169 {
4170 const OpDebugGroup *op = ptr;
4171 gdk_gl_context_push_debug_group (self->gl_context, op->text);
4172 OP_PRINT (" Debug: %s", op->text);
4173 break;
4174 }
4175
4176 case OP_POP_DEBUG_GROUP:
4177 gdk_gl_context_pop_debug_group (self->gl_context);
4178 break;
4179
4180 case OP_NONE:
4181 case OP_LAST:
4182 default:
4183 g_warn_if_reached ();
4184 }
4185
4186 OP_PRINT ("\n");
4187 }
4188
4189 glDeleteVertexArrays (1, &vao_id);
4190 glDeleteBuffers (1, &buffer_id);
4191 }
4192
4193 static void
gsk_gl_renderer_do_render(GskRenderer * renderer,GskRenderNode * root,const graphene_rect_t * viewport,int fbo_id,int scale_factor)4194 gsk_gl_renderer_do_render (GskRenderer *renderer,
4195 GskRenderNode *root,
4196 const graphene_rect_t *viewport,
4197 int fbo_id,
4198 int scale_factor)
4199 {
4200 GskGLRenderer *self = GSK_GL_RENDERER (renderer);
4201 graphene_matrix_t projection;
4202 #ifdef G_ENABLE_DEBUG
4203 GskProfiler *profiler;
4204 gint64 gpu_time, cpu_time;
4205 gint64 start_time G_GNUC_UNUSED;
4206 #endif
4207 GPtrArray *removed;
4208
4209 #ifdef G_ENABLE_DEBUG
4210 profiler = gsk_renderer_get_profiler (renderer);
4211 #endif
4212
4213 if (self->gl_context == NULL)
4214 {
4215 GSK_RENDERER_NOTE (renderer, OPENGL, g_message ("No valid GL context associated to the renderer"));
4216 return;
4217 }
4218
4219 g_assert (gsk_gl_driver_in_frame (self->gl_driver));
4220
4221 removed = g_ptr_array_new ();
4222 gsk_gl_texture_atlases_begin_frame (self->atlases, removed);
4223 gsk_gl_glyph_cache_begin_frame (self->glyph_cache, self->gl_driver, removed);
4224 gsk_gl_icon_cache_begin_frame (self->icon_cache, removed);
4225 gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver);
4226 g_ptr_array_unref (removed);
4227
4228 /* Set up the modelview and projection matrices to fit our viewport */
4229 init_projection_matrix (&projection, viewport);
4230 ops_set_projection (&self->op_builder, &projection);
4231 ops_set_viewport (&self->op_builder, viewport);
4232 ops_set_modelview (&self->op_builder, gsk_transform_scale (NULL, scale_factor, scale_factor));
4233
4234 /* Initial clip is self->render_region! */
4235 if (self->render_region != NULL)
4236 {
4237 graphene_rect_t transformed_render_region;
4238 cairo_rectangle_int_t render_extents;
4239
4240 cairo_region_get_extents (self->render_region, &render_extents);
4241
4242 ops_transform_bounds_modelview (&self->op_builder,
4243 &GRAPHENE_RECT_INIT (render_extents.x,
4244 render_extents.y,
4245 render_extents.width,
4246 render_extents.height),
4247 &transformed_render_region);
4248 ops_push_clip (&self->op_builder,
4249 &GSK_ROUNDED_RECT_INIT (transformed_render_region.origin.x,
4250 transformed_render_region.origin.y,
4251 transformed_render_region.size.width,
4252 transformed_render_region.size.height));
4253 }
4254 else
4255 {
4256 ops_push_clip (&self->op_builder,
4257 &GSK_ROUNDED_RECT_INIT (viewport->origin.x,
4258 viewport->origin.y,
4259 viewport->size.width,
4260 viewport->size.height));
4261 }
4262
4263 if (fbo_id != 0)
4264 ops_set_render_target (&self->op_builder, fbo_id);
4265
4266 gdk_gl_context_push_debug_group (self->gl_context, "Adding render ops");
4267 gsk_gl_renderer_add_render_ops (self, root, &self->op_builder);
4268 gdk_gl_context_pop_debug_group (self->gl_context);
4269
4270 /* We correctly reset the state everywhere */
4271 g_assert_cmpint (self->op_builder.current_render_target, ==, fbo_id);
4272 ops_pop_modelview (&self->op_builder);
4273 ops_pop_clip (&self->op_builder);
4274 ops_finish (&self->op_builder);
4275
4276 /*g_message ("Ops: %u", self->render_ops->len);*/
4277
4278 /* Now actually draw things... */
4279 #ifdef G_ENABLE_DEBUG
4280 gsk_gl_profiler_begin_gpu_region (self->gl_profiler);
4281 gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
4282 #endif
4283
4284 /* Actually do the rendering */
4285 if (fbo_id != 0)
4286 glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
4287
4288 glViewport (0, 0, ceilf (viewport->size.width), ceilf (viewport->size.height));
4289 gsk_gl_renderer_setup_render_mode (self);
4290 gsk_gl_renderer_clear (self);
4291
4292 glEnable (GL_DEPTH_TEST);
4293 glDepthFunc (GL_LEQUAL);
4294
4295 /* Pre-multiplied alpha! */
4296 glEnable (GL_BLEND);
4297 glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
4298 glBlendEquation (GL_FUNC_ADD);
4299
4300 gdk_gl_context_push_debug_group (self->gl_context, "Rendering ops");
4301 gsk_gl_renderer_render_ops (self);
4302 gdk_gl_context_pop_debug_group (self->gl_context);
4303
4304 #ifdef G_ENABLE_DEBUG
4305 gsk_profiler_counter_inc (profiler, self->profile_counters.frames);
4306
4307 start_time = gsk_profiler_timer_get_start (profiler, self->profile_timers.cpu_time);
4308 cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
4309 gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
4310
4311 gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler);
4312 gsk_profiler_timer_set (profiler, self->profile_timers.gpu_time, gpu_time);
4313
4314 gsk_profiler_push_samples (profiler);
4315
4316 gdk_profiler_add_mark (start_time * 1000, cpu_time * 1000, "GL render", "");
4317 #endif
4318 }
4319
4320 static GdkTexture *
gsk_gl_renderer_render_texture(GskRenderer * renderer,GskRenderNode * root,const graphene_rect_t * viewport)4321 gsk_gl_renderer_render_texture (GskRenderer *renderer,
4322 GskRenderNode *root,
4323 const graphene_rect_t *viewport)
4324 {
4325 GskGLRenderer *self = GSK_GL_RENDERER (renderer);
4326 GdkTexture *texture;
4327 int width, height;
4328 guint texture_id;
4329 guint fbo_id;
4330
4331 g_return_val_if_fail (self->gl_context != NULL, NULL);
4332
4333 gdk_gl_context_make_current (self->gl_context);
4334 gdk_gl_context_push_debug_group_printf (self->gl_context,
4335 "Render %s<%p> to texture",
4336 g_type_name_from_instance ((GTypeInstance *) root),
4337 root);
4338
4339 width = ceilf (viewport->size.width);
4340 height = ceilf (viewport->size.height);
4341
4342 self->scale_factor = gdk_surface_get_scale_factor (gsk_renderer_get_surface (renderer));
4343
4344 /* Prepare our framebuffer */
4345 gsk_gl_driver_begin_frame (self->gl_driver);
4346 glGenTextures (1, &texture_id);
4347 glBindTexture (GL_TEXTURE_2D, texture_id);
4348
4349 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4350 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4351 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
4352 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
4353
4354 if (gdk_gl_context_has_debug (self->gl_context))
4355 gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
4356 "Texture %s<%p> %d",
4357 g_type_name_from_instance ((GTypeInstance *) root),
4358 root,
4359 texture_id);
4360
4361 if (gdk_gl_context_get_use_es (self->gl_context))
4362 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4363 else
4364 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
4365
4366 glGenFramebuffers (1, &fbo_id);
4367 glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
4368
4369 if (gdk_gl_context_has_debug (self->gl_context))
4370 gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, fbo_id,
4371 "FB %s<%p> %d",
4372 g_type_name_from_instance ((GTypeInstance *) root),
4373 root,
4374 fbo_id);
4375 glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
4376
4377 /* Render the actual scene */
4378 gsk_gl_renderer_do_render (renderer, root, viewport, fbo_id, 1);
4379
4380 glDeleteFramebuffers (1, &fbo_id);
4381
4382
4383 /* Render the now drawn framebuffer y-flipped so it's as GdkGLTexture expects it to be */
4384 {
4385 guint final_texture_id, final_fbo_id;
4386 graphene_matrix_t m;
4387
4388 ops_reset (&self->op_builder);
4389
4390 glGenFramebuffers (1, &final_fbo_id);
4391 glBindFramebuffer (GL_FRAMEBUFFER, final_fbo_id);
4392 glGenTextures (1, &final_texture_id);
4393 glBindTexture (GL_TEXTURE_2D, final_texture_id);
4394
4395 if (gdk_gl_context_get_use_es (self->gl_context))
4396 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4397 else
4398 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
4399
4400 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4401 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4402 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
4403 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
4404
4405 glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, final_texture_id, 0);
4406 g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
4407
4408 ops_set_render_target (&self->op_builder, final_fbo_id);
4409 ops_push_clip (&self->op_builder, &GSK_ROUNDED_RECT_INIT (0, 0, width, height));
4410 ops_set_program (&self->op_builder, &self->programs->blit_program);
4411
4412 ops_begin (&self->op_builder, OP_CLEAR);
4413 ops_set_texture (&self->op_builder, texture_id);
4414 ops_set_modelview (&self->op_builder, NULL);
4415 ops_set_viewport (&self->op_builder, &GRAPHENE_RECT_INIT (0, 0, width, height));
4416 init_projection_matrix (&m, &GRAPHENE_RECT_INIT (0, 0, width, height));
4417 graphene_matrix_scale (&m, 1, -1, 1); /* Undo the scale init_projection_matrix() does again */
4418 ops_set_projection (&self->op_builder, &m);
4419
4420 fill_vertex_data (ops_draw (&self->op_builder, NULL),
4421 0, 0, width, height);
4422 ops_pop_clip (&self->op_builder);
4423 gsk_gl_renderer_render_ops (self);
4424
4425 ops_finish (&self->op_builder);
4426
4427 glDeleteTextures (1, &texture_id);
4428
4429 texture_id = final_texture_id;
4430 }
4431
4432 texture = gdk_gl_texture_new (self->gl_context,
4433 texture_id,
4434 width, height,
4435 NULL, NULL);
4436
4437 gsk_gl_driver_end_frame (self->gl_driver);
4438
4439 gdk_gl_context_pop_debug_group (self->gl_context);
4440
4441 gsk_gl_renderer_clear_tree (self);
4442 return texture;
4443 }
4444
4445 static void
gsk_gl_renderer_render(GskRenderer * renderer,GskRenderNode * root,const cairo_region_t * update_area)4446 gsk_gl_renderer_render (GskRenderer *renderer,
4447 GskRenderNode *root,
4448 const cairo_region_t *update_area)
4449 {
4450 GskGLRenderer *self = GSK_GL_RENDERER (renderer);
4451 graphene_rect_t viewport;
4452 const cairo_region_t *damage;
4453 GdkRectangle whole_surface;
4454 GdkSurface *surface;
4455
4456 if (self->gl_context == NULL)
4457 return;
4458
4459 surface = gsk_renderer_get_surface (renderer);
4460 self->scale_factor = gdk_surface_get_scale_factor (surface);
4461
4462 gdk_gl_context_make_current (self->gl_context);
4463 gdk_gl_context_push_debug_group_printf (self->gl_context,
4464 "Render root node %p", root);
4465
4466 whole_surface = (GdkRectangle) {
4467 0, 0,
4468 gdk_surface_get_width (surface) * self->scale_factor,
4469 gdk_surface_get_height (surface) * self->scale_factor
4470 };
4471
4472 gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->gl_context),
4473 update_area);
4474
4475 damage = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->gl_context));
4476
4477 if (cairo_region_contains_rectangle (damage, &whole_surface) == CAIRO_REGION_OVERLAP_IN)
4478 {
4479 self->render_region = NULL;
4480 }
4481 else
4482 {
4483 GdkRectangle extents;
4484
4485 cairo_region_get_extents (damage, &extents);
4486
4487 if (gdk_rectangle_equal (&extents, &whole_surface))
4488 self->render_region = NULL;
4489 else
4490 self->render_region = cairo_region_create_rectangle (&extents);
4491 }
4492
4493 gdk_gl_context_make_current (self->gl_context);
4494
4495 viewport.origin.x = 0;
4496 viewport.origin.y = 0;
4497 viewport.size.width = whole_surface.width;
4498 viewport.size.height = whole_surface.height;
4499
4500 gsk_gl_driver_begin_frame (self->gl_driver);
4501 gsk_gl_renderer_do_render (renderer, root, &viewport, 0, self->scale_factor);
4502 gsk_gl_driver_end_frame (self->gl_driver);
4503
4504 gsk_gl_renderer_clear_tree (self);
4505
4506 gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->gl_context));
4507 gdk_gl_context_make_current (self->gl_context);
4508
4509 gdk_gl_context_pop_debug_group (self->gl_context);
4510
4511 g_clear_pointer (&self->render_region, cairo_region_destroy);
4512 }
4513
4514 static void
gsk_gl_renderer_class_init(GskGLRendererClass * klass)4515 gsk_gl_renderer_class_init (GskGLRendererClass *klass)
4516 {
4517 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
4518 GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
4519
4520 gobject_class->dispose = gsk_gl_renderer_dispose;
4521
4522 renderer_class->realize = gsk_gl_renderer_realize;
4523 renderer_class->unrealize = gsk_gl_renderer_unrealize;
4524 renderer_class->render = gsk_gl_renderer_render;
4525 renderer_class->render_texture = gsk_gl_renderer_render_texture;
4526 }
4527
4528 static void
gsk_gl_renderer_init(GskGLRenderer * self)4529 gsk_gl_renderer_init (GskGLRenderer *self)
4530 {
4531 gsk_ensure_resources ();
4532
4533 ops_init (&self->op_builder);
4534 self->op_builder.renderer = self;
4535
4536 #ifdef G_ENABLE_DEBUG
4537 {
4538 GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
4539
4540 self->profile_counters.frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE);
4541
4542 self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU time", FALSE, TRUE);
4543 self->profile_timers.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU time", FALSE, TRUE);
4544 }
4545 #endif
4546 }
4547
4548 /**
4549 * gsk_gl_renderer_new:
4550 *
4551 * Creates a new `GskRenderer` using OpenGL.
4552 *
4553 * Returns: a new GL renderer
4554 */
4555 GskRenderer *
gsk_gl_renderer_new(void)4556 gsk_gl_renderer_new (void)
4557 {
4558 return g_object_new (GSK_TYPE_GL_RENDERER, NULL);
4559 }
4560