1 #include "gskglrenderopsprivate.h"
2 #include "gsktransform.h"
3 
4 typedef struct
5 {
6   GskRoundedRect rect;
7   bool is_rectilinear;
8 } ClipStackEntry;
9 
10 static inline gboolean
rect_equal(const graphene_rect_t * a,const graphene_rect_t * b)11 rect_equal (const graphene_rect_t *a,
12             const graphene_rect_t *b)
13 {
14   return memcmp (a, b, sizeof (graphene_rect_t)) == 0;
15 }
16 
17 static inline bool G_GNUC_PURE
rounded_rect_equal(const GskRoundedRect * r1,const GskRoundedRect * r2)18 rounded_rect_equal (const GskRoundedRect *r1,
19                     const GskRoundedRect *r2)
20 {
21   if (r1 == r2)
22       return true;
23 
24   if (!r1)
25     return false;
26 
27   if (r1->bounds.origin.x != r2->bounds.origin.x ||
28       r1->bounds.origin.y != r2->bounds.origin.y ||
29       r1->bounds.size.width != r2->bounds.size.width ||
30       r1->bounds.size.height != r2->bounds.size.height)
31     return false;
32 
33   for (int i = 0; i < 4; i ++)
34     if (r1->corner[i].width != r2->corner[i].width ||
35         r1->corner[i].height != r2->corner[i].height)
36       return false;
37 
38   return true;
39 }
40 
41 static inline gboolean G_GNUC_PURE
rounded_rect_corners_equal(const GskRoundedRect * r1,const GskRoundedRect * r2)42 rounded_rect_corners_equal (const GskRoundedRect *r1,
43                             const GskRoundedRect *r2)
44 {
45   int i;
46 
47   if (!r1)
48     return FALSE;
49 
50   for (i = 0; i < 4; i ++)
51     if (r1->corner[i].width != r2->corner[i].width ||
52         r1->corner[i].height != r2->corner[i].height)
53       return FALSE;
54 
55   return TRUE;
56 }
57 
58 static inline ProgramState *
get_current_program_state(RenderOpBuilder * builder)59 get_current_program_state (RenderOpBuilder *builder)
60 {
61   if (!builder->current_program)
62     return NULL;
63 
64   return &builder->current_program->state;
65 }
66 
67 void
ops_finish(RenderOpBuilder * builder)68 ops_finish (RenderOpBuilder *builder)
69 {
70   if (builder->mv_stack)
71     g_array_free (builder->mv_stack, TRUE);
72   builder->mv_stack = NULL;
73 
74   if (builder->clip_stack)
75     g_array_free (builder->clip_stack, TRUE);
76   builder->clip_stack = NULL;
77 
78   builder->dx = 0;
79   builder->dy = 0;
80   builder->scale_x = 1;
81   builder->scale_y = 1;
82   builder->current_modelview = NULL;
83   builder->current_clip = NULL;
84   builder->clip_is_rectilinear = TRUE;
85   builder->current_render_target = 0;
86   builder->current_texture = 0;
87   builder->current_program = NULL;
88   graphene_matrix_init_identity (&builder->current_projection);
89   builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0);
90 }
91 
92 /* Debugging only! */
93 void
ops_dump_framebuffer(RenderOpBuilder * builder,const char * filename,int width,int height)94 ops_dump_framebuffer (RenderOpBuilder *builder,
95                       const char      *filename,
96                       int              width,
97                       int              height)
98 {
99   OpDumpFrameBuffer *op;
100 
101   op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
102   op->filename = g_strdup (filename);
103   op->width = width;
104   op->height = height;
105 }
106 
107 void
ops_push_debug_group(RenderOpBuilder * builder,const char * text)108 ops_push_debug_group (RenderOpBuilder *builder,
109                       const char      *text)
110 {
111   OpDebugGroup *op;
112 
113   op = ops_begin (builder, OP_PUSH_DEBUG_GROUP);
114   strncpy (op->text, text, sizeof(op->text) - 1);
115   op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
116 }
117 
118 void
ops_pop_debug_group(RenderOpBuilder * builder)119 ops_pop_debug_group (RenderOpBuilder *builder)
120 {
121   ops_begin (builder, OP_POP_DEBUG_GROUP);
122 }
123 
124 static void
extract_matrix_metadata(GskTransform * transform,OpsMatrixMetadata * md)125 extract_matrix_metadata (GskTransform      *transform,
126                          OpsMatrixMetadata *md)
127 {
128   float dummy;
129 
130   switch (gsk_transform_get_category (transform))
131     {
132     case GSK_TRANSFORM_CATEGORY_IDENTITY:
133     case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
134       md->scale_x = 1;
135       md->scale_y = 1;
136     break;
137 
138     case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
139       gsk_transform_to_affine (transform,
140                                &md->scale_x, &md->scale_y,
141                                &dummy, &dummy);
142     break;
143 
144     case GSK_TRANSFORM_CATEGORY_UNKNOWN:
145     case GSK_TRANSFORM_CATEGORY_ANY:
146     case GSK_TRANSFORM_CATEGORY_3D:
147     case GSK_TRANSFORM_CATEGORY_2D:
148       {
149         graphene_vec3_t col1;
150         graphene_vec3_t col2;
151         graphene_matrix_t m;
152 
153         gsk_transform_to_matrix (transform, &m);
154 
155         /* TODO: 90% sure this is incorrect. But we should never hit this code
156          * path anyway. */
157         graphene_vec3_init (&col1,
158                             graphene_matrix_get_value (&m, 0, 0),
159                             graphene_matrix_get_value (&m, 1, 0),
160                             graphene_matrix_get_value (&m, 2, 0));
161 
162         graphene_vec3_init (&col2,
163                             graphene_matrix_get_value (&m, 0, 1),
164                             graphene_matrix_get_value (&m, 1, 1),
165                             graphene_matrix_get_value (&m, 2, 1));
166 
167         md->scale_x = graphene_vec3_length (&col1);
168         md->scale_y = graphene_vec3_length (&col2);
169       }
170     break;
171     default:
172       {}
173     }
174 }
175 
176 void
ops_transform_bounds_modelview(const RenderOpBuilder * builder,const graphene_rect_t * src,graphene_rect_t * dst)177 ops_transform_bounds_modelview (const RenderOpBuilder *builder,
178                                 const graphene_rect_t *src,
179                                 graphene_rect_t       *dst)
180 {
181   graphene_rect_t r = *src;
182 
183   g_assert (builder->mv_stack != NULL);
184   g_assert (builder->mv_stack->len >= 1);
185 
186   r.origin.x += builder->dx;
187   r.origin.y += builder->dy;
188 
189   gsk_transform_transform_bounds (builder->current_modelview, &r, dst);
190 }
191 
192 void
ops_init(RenderOpBuilder * builder)193 ops_init (RenderOpBuilder *builder)
194 {
195   memset (builder, 0, sizeof (*builder));
196 
197   builder->current_opacity = 1.0f;
198 
199   op_buffer_init (&builder->render_ops);
200   builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
201 }
202 
203 void
ops_free(RenderOpBuilder * builder)204 ops_free (RenderOpBuilder *builder)
205 {
206   g_array_unref (builder->vertices);
207   op_buffer_destroy (&builder->render_ops);
208 }
209 
210 void
ops_set_program(RenderOpBuilder * builder,Program * program)211 ops_set_program (RenderOpBuilder *builder,
212                  Program         *program)
213 {
214   OpProgram *op;
215 
216   if (builder->current_program == program)
217     return;
218 
219   op = ops_begin (builder, OP_CHANGE_PROGRAM);
220   op->program = program;
221 
222   builder->current_program = program;
223 }
224 
225 void
ops_push_clip(RenderOpBuilder * self,const GskRoundedRect * clip)226 ops_push_clip (RenderOpBuilder      *self,
227                const GskRoundedRect *clip)
228 {
229   ClipStackEntry entry;
230 
231   if (G_UNLIKELY (self->clip_stack == NULL))
232     self->clip_stack = g_array_new (FALSE, TRUE, sizeof (ClipStackEntry));
233 
234   g_assert (self->clip_stack != NULL);
235 
236   entry.rect = *clip;
237   entry.is_rectilinear = gsk_rounded_rect_is_rectilinear (clip);
238   g_array_append_val (self->clip_stack, entry);
239   self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect;
240   self->clip_is_rectilinear = entry.is_rectilinear;
241 }
242 
243 void
ops_pop_clip(RenderOpBuilder * self)244 ops_pop_clip (RenderOpBuilder *self)
245 {
246   const ClipStackEntry *head;
247 
248   g_assert (self->clip_stack);
249   g_assert (self->clip_stack->len >= 1);
250 
251   self->clip_stack->len --;
252   head = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1);
253 
254   if (self->clip_stack->len >= 1)
255     {
256       self->current_clip = &head->rect;
257       self->clip_is_rectilinear = head->is_rectilinear;
258     }
259   else
260     {
261       self->current_clip = NULL;
262       self->clip_is_rectilinear = TRUE;
263     }
264 }
265 
266 gboolean
ops_has_clip(RenderOpBuilder * self)267 ops_has_clip (RenderOpBuilder *self)
268 {
269   return self->clip_stack != NULL &&
270          self->clip_stack->len > 1;
271 }
272 
273 /**
274  * ops_set_modelview:
275  * @builder
276  * @transform: (transfer full): The new modelview transform
277  *
278  * This sets the modelview to the given one without looking at the
279  * one that's currently set */
280 void
ops_set_modelview(RenderOpBuilder * builder,GskTransform * transform)281 ops_set_modelview (RenderOpBuilder *builder,
282                    GskTransform    *transform)
283 {
284   MatrixStackEntry *entry;
285 
286   if (G_UNLIKELY (builder->mv_stack == NULL))
287     builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
288 
289   g_assert (builder->mv_stack != NULL);
290 
291   g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
292   entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
293 
294   entry->transform = transform;
295 
296   entry->metadata.dx_before = builder->dx;
297   entry->metadata.dy_before = builder->dy;
298   extract_matrix_metadata (entry->transform, &entry->metadata);
299 
300   builder->dx = 0;
301   builder->dy = 0;
302   builder->current_modelview = entry->transform;
303   builder->scale_x = entry->metadata.scale_x;
304   builder->scale_y = entry->metadata.scale_y;
305 }
306 
307 /* This sets the given modelview to the one we get when multiplying
308  * the given modelview with the current one. */
309 void
ops_push_modelview(RenderOpBuilder * builder,GskTransform * transform)310 ops_push_modelview (RenderOpBuilder *builder,
311                     GskTransform    *transform)
312 {
313   MatrixStackEntry *entry;
314 
315   if (G_UNLIKELY (builder->mv_stack == NULL))
316     builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
317 
318   g_assert (builder->mv_stack != NULL);
319 
320   g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
321   entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
322 
323   if (G_LIKELY (builder->mv_stack->len >= 2))
324     {
325       const MatrixStackEntry *cur;
326       GskTransform *t = NULL;
327 
328       cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2);
329       /* Multiply given matrix with current modelview */
330 
331       t = gsk_transform_translate (gsk_transform_ref (cur->transform),
332                                    &(graphene_point_t) { builder->dx, builder->dy});
333       t = gsk_transform_transform (t, transform);
334       entry->transform = t;
335     }
336   else
337     {
338       entry->transform = gsk_transform_ref (transform);
339     }
340 
341   entry->metadata.dx_before = builder->dx;
342   entry->metadata.dy_before = builder->dy;
343   extract_matrix_metadata (entry->transform, &entry->metadata);
344 
345   builder->dx = 0;
346   builder->dy = 0;
347   builder->scale_x = entry->metadata.scale_x;
348   builder->scale_y = entry->metadata.scale_y;
349   builder->current_modelview = entry->transform;
350 }
351 
352 void
ops_pop_modelview(RenderOpBuilder * builder)353 ops_pop_modelview (RenderOpBuilder *builder)
354 {
355   const MatrixStackEntry *head;
356 
357   g_assert (builder->mv_stack);
358   g_assert (builder->mv_stack->len >= 1);
359 
360   head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
361   builder->dx = head->metadata.dx_before;
362   builder->dy = head->metadata.dy_before;
363   gsk_transform_unref (head->transform);
364 
365   builder->mv_stack->len --;
366   head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
367 
368   if (builder->mv_stack->len >= 1)
369     {
370       builder->scale_x = head->metadata.scale_x;
371       builder->scale_y = head->metadata.scale_y;
372       builder->current_modelview = head->transform;
373     }
374   else
375     {
376       builder->current_modelview = NULL;
377     }
378 }
379 
380 graphene_matrix_t
ops_set_projection(RenderOpBuilder * builder,const graphene_matrix_t * projection)381 ops_set_projection (RenderOpBuilder         *builder,
382                     const graphene_matrix_t *projection)
383 {
384   graphene_matrix_t prev_mv;
385 
386   prev_mv = builder->current_projection;
387   builder->current_projection = *projection;
388 
389   return prev_mv;
390 }
391 
392 graphene_rect_t
ops_set_viewport(RenderOpBuilder * builder,const graphene_rect_t * viewport)393 ops_set_viewport (RenderOpBuilder       *builder,
394                   const graphene_rect_t *viewport)
395 {
396   ProgramState *current_program_state = get_current_program_state (builder);
397   OpViewport *op;
398   graphene_rect_t prev_viewport;
399 
400   if (rect_equal (&builder->current_viewport, viewport))
401     return *viewport;
402 
403   op = ops_begin (builder, OP_CHANGE_VIEWPORT);
404   op->viewport = *viewport;
405 
406   if (current_program_state != NULL)
407     current_program_state->viewport = *viewport;
408 
409   prev_viewport = builder->current_viewport;
410   builder->current_viewport = *viewport;
411 
412   return prev_viewport;
413 }
414 
415 void
ops_set_texture(RenderOpBuilder * builder,int texture_id)416 ops_set_texture (RenderOpBuilder *builder,
417                  int              texture_id)
418 {
419   OpTexture *op;
420 
421   if (builder->current_texture == texture_id)
422     return;
423 
424   op = ops_begin (builder, OP_CHANGE_SOURCE_TEXTURE);
425   op->texture_id = texture_id;
426   builder->current_texture = texture_id;
427 }
428 
429 void
ops_set_extra_texture(RenderOpBuilder * builder,int texture_id,int idx)430 ops_set_extra_texture (RenderOpBuilder *builder,
431                        int              texture_id,
432                        int              idx)
433 {
434   OpExtraTexture *op;
435 
436   op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE);
437   op->texture_id = texture_id;
438   op->idx = idx;
439 }
440 
441 int
ops_set_render_target(RenderOpBuilder * builder,int render_target_id)442 ops_set_render_target (RenderOpBuilder *builder,
443                        int              render_target_id)
444 {
445   OpRenderTarget *op;
446   int prev_render_target;
447 
448   if (builder->current_render_target == render_target_id)
449     return render_target_id;
450 
451   prev_render_target = builder->current_render_target;
452 
453   if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET)))
454     op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET);
455 
456   op->render_target_id = render_target_id;
457 
458   builder->current_render_target = render_target_id;
459 
460   return prev_render_target;
461 }
462 
463 float
ops_set_opacity(RenderOpBuilder * builder,float opacity)464 ops_set_opacity (RenderOpBuilder *builder,
465                  float            opacity)
466 {
467   float prev_opacity;
468 
469   if (builder->current_opacity == opacity)
470     return opacity;
471 
472   prev_opacity = builder->current_opacity;
473   builder->current_opacity = opacity;
474 
475   return prev_opacity;
476 }
477 
478 void
ops_set_color(RenderOpBuilder * builder,const GdkRGBA * color)479 ops_set_color (RenderOpBuilder *builder,
480                const GdkRGBA   *color)
481 {
482   ProgramState *current_program_state = get_current_program_state (builder);
483   OpColor *op;
484 
485   if (gdk_rgba_equal (color, &current_program_state->color))
486     return;
487 
488   current_program_state->color = *color;
489 
490   op = ops_begin (builder, OP_CHANGE_COLOR);
491   op->rgba = color;
492 }
493 
494 void
ops_set_gl_shader_args(RenderOpBuilder * builder,GskGLShader * shader,float width,float height,const guchar * uniform_data)495 ops_set_gl_shader_args (RenderOpBuilder       *builder,
496                         GskGLShader           *shader,
497                         float                  width,
498                         float                  height,
499                         const guchar          *uniform_data)
500 {
501   ProgramState *current_program_state = get_current_program_state (builder);
502   OpGLShader *op;
503   gsize args_size = gsk_gl_shader_get_args_size (shader);
504 
505   if (current_program_state)
506     {
507       if (current_program_state->gl_shader.width == width &&
508           current_program_state->gl_shader.height == height &&
509           current_program_state->gl_shader.uniform_data_len == args_size &&
510           memcmp (current_program_state->gl_shader.uniform_data, uniform_data, args_size) == 0)
511         return;
512 
513       current_program_state->gl_shader.width = width;
514       current_program_state->gl_shader.height = height;
515       if (args_size > sizeof (current_program_state->gl_shader.uniform_data))
516         current_program_state->gl_shader.uniform_data_len = 0;
517       else
518         {
519           current_program_state->gl_shader.uniform_data_len = args_size;
520           memcpy (current_program_state->gl_shader.uniform_data, uniform_data, args_size);
521         }
522     }
523 
524   op = ops_begin (builder, OP_CHANGE_GL_SHADER_ARGS);
525   op->shader = shader;
526   op->size[0] = width;
527   op->size[1] = height;
528   op->uniform_data = uniform_data;
529 }
530 
531 void
ops_set_color_matrix(RenderOpBuilder * builder,const graphene_matrix_t * matrix,const graphene_vec4_t * offset)532 ops_set_color_matrix (RenderOpBuilder         *builder,
533                       const graphene_matrix_t *matrix,
534                       const graphene_vec4_t   *offset)
535 {
536   ProgramState *current_program_state = get_current_program_state (builder);
537   const bool offset_equal = graphene_vec4_equal (offset, &current_program_state->color_matrix.offset);
538   const bool matrix_equal = graphene_matrix_equal_fast (matrix,
539                                                         &current_program_state->color_matrix.matrix);
540   OpColorMatrix *op;
541 
542   if (offset_equal && matrix_equal)
543     return;
544 
545   op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
546 
547   if (!matrix_equal)
548     {
549       current_program_state->color_matrix.matrix = *matrix;
550       op->matrix.value = matrix;
551       op->matrix.send = TRUE;
552     }
553   else
554     op->matrix.send = FALSE;
555 
556   if (!offset_equal)
557     {
558       current_program_state->color_matrix.offset = *offset;
559       op->offset.value = offset;
560       op->offset.send = TRUE;
561     }
562   else
563     op->offset.send = FALSE;
564 }
565 
566 void
ops_set_border(RenderOpBuilder * builder,const GskRoundedRect * outline)567 ops_set_border (RenderOpBuilder      *builder,
568                 const GskRoundedRect *outline)
569 {
570   ProgramState *current_program_state = get_current_program_state (builder);
571   OpBorder *op;
572 
573   if (memcmp (&current_program_state->border.outline,
574               outline, sizeof (GskRoundedRect)) == 0)
575     return;
576 
577   current_program_state->border.outline = *outline;
578 
579   op = ops_begin (builder, OP_CHANGE_BORDER);
580   op->outline = *outline;
581 }
582 
583 void
ops_set_border_width(RenderOpBuilder * builder,const float * widths)584 ops_set_border_width (RenderOpBuilder *builder,
585                       const float     *widths)
586 {
587   ProgramState *current_program_state = get_current_program_state (builder);
588   OpBorder *op;
589 
590   g_assert (current_program_state);
591 
592   if (memcmp (current_program_state->border.widths,
593               widths, sizeof (float) * 4) == 0)
594     return;
595 
596   memcpy (&current_program_state->border.widths,
597           widths, sizeof (float) * 4);
598 
599   op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH);
600   op->widths[0] = widths[0];
601   op->widths[1] = widths[1];
602   op->widths[2] = widths[2];
603   op->widths[3] = widths[3];
604 }
605 
606 void
ops_set_border_color(RenderOpBuilder * builder,const GdkRGBA * color)607 ops_set_border_color (RenderOpBuilder *builder,
608                       const GdkRGBA   *color)
609 {
610   ProgramState *current_program_state = get_current_program_state (builder);
611   OpBorder *op;
612 
613   if (gdk_rgba_equal (color, &current_program_state->border.color))
614     return;
615 
616   op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
617   op->color = color;
618 
619   current_program_state->border.color = *color;
620 }
621 
622 GskQuadVertex *
ops_draw(RenderOpBuilder * builder,const GskQuadVertex vertex_data[GL_N_VERTICES])623 ops_draw (RenderOpBuilder     *builder,
624           const GskQuadVertex  vertex_data[GL_N_VERTICES])
625 {
626   ProgramState *program_state = get_current_program_state (builder);
627   OpDraw *op;
628 
629   if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
630     {
631       OpMatrix *opm;
632 
633       opm = ops_begin (builder, OP_CHANGE_PROJECTION);
634       opm->matrix = builder->current_projection;
635       program_state->projection = builder->current_projection;
636     }
637 
638   if (program_state->modelview == NULL ||
639       !gsk_transform_equal (builder->current_modelview, program_state->modelview))
640     {
641       OpMatrix *opm;
642 
643       opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
644       gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
645       gsk_transform_unref (program_state->modelview);
646       program_state->modelview = gsk_transform_ref (builder->current_modelview);
647     }
648 
649   if (!rect_equal (&builder->current_viewport, &program_state->viewport))
650     {
651       OpViewport *opv;
652 
653       opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
654       opv->viewport = builder->current_viewport;
655       program_state->viewport = builder->current_viewport;
656     }
657 
658   if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
659     {
660       OpClip *opc;
661 
662       opc = ops_begin (builder, OP_CHANGE_CLIP);
663       opc->clip = *builder->current_clip;
664       opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
665       program_state->clip = *builder->current_clip;
666     }
667 
668   if (program_state->opacity != builder->current_opacity)
669     {
670       OpOpacity *opo;
671 
672       opo = ops_begin (builder, OP_CHANGE_OPACITY);
673       opo->opacity = builder->current_opacity;
674       program_state->opacity = builder->current_opacity;
675     }
676 
677   /* TODO: Did the additions above break the following optimization? */
678   if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
679     {
680       op->vao_size += GL_N_VERTICES;
681     }
682   else
683     {
684       op = op_buffer_add (&builder->render_ops, OP_DRAW);
685       op->vao_offset = builder->vertices->len;
686       op->vao_size = GL_N_VERTICES;
687     }
688 
689   if (vertex_data)
690     {
691       g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES);
692       return NULL; /* Better not use this on the caller side */
693     }
694 
695   g_array_set_size (builder->vertices, builder->vertices->len + GL_N_VERTICES);
696   return &g_array_index (builder->vertices, GskQuadVertex, builder->vertices->len - GL_N_VERTICES);
697 }
698 
699 /* The offset is only valid for the current modelview.
700  * Setting a new modelview will add the offset to that matrix
701  * and reset the internal offset to 0. */
702 void
ops_offset(RenderOpBuilder * builder,float x,float y)703 ops_offset (RenderOpBuilder *builder,
704             float            x,
705             float            y)
706 {
707   builder->dx += x;
708   builder->dy += y;
709 }
710 
711 gpointer
ops_begin(RenderOpBuilder * builder,OpKind kind)712 ops_begin (RenderOpBuilder *builder,
713            OpKind           kind)
714 {
715   return op_buffer_add (&builder->render_ops, kind);
716 }
717 
718 void
ops_reset(RenderOpBuilder * builder)719 ops_reset (RenderOpBuilder *builder)
720 {
721   op_buffer_clear (&builder->render_ops);
722   g_array_set_size (builder->vertices, 0);
723 }
724 
725 OpBuffer *
ops_get_buffer(RenderOpBuilder * builder)726 ops_get_buffer (RenderOpBuilder *builder)
727 {
728   return &builder->render_ops;
729 }
730 
731 void
ops_set_inset_shadow(RenderOpBuilder * self,const GskRoundedRect outline,float spread,const GdkRGBA * color,float dx,float dy)732 ops_set_inset_shadow (RenderOpBuilder      *self,
733                       const GskRoundedRect  outline,
734                       float                 spread,
735                       const GdkRGBA        *color,
736                       float                 dx,
737                       float                 dy)
738 {
739   ProgramState *current_program_state = get_current_program_state (self);
740   OpShadow *op;
741 
742   g_assert (current_program_state);
743 
744   op = ops_begin (self, OP_CHANGE_INSET_SHADOW);
745 
746   if (!rounded_rect_equal (&outline, &current_program_state->inset_shadow.outline))
747     {
748       op->outline.value = outline;
749       op->outline.send = TRUE;
750       op->outline.send_corners = !rounded_rect_corners_equal (&current_program_state->inset_shadow.outline,
751                                                               &outline);
752       current_program_state->inset_shadow.outline = outline;
753     }
754   else
755     op->outline.send = FALSE;
756 
757   if (spread != current_program_state->inset_shadow.spread)
758     {
759       op->spread.value = spread;
760       op->spread.send = TRUE;
761 
762       current_program_state->inset_shadow.spread = spread;
763     }
764   else
765     op->spread.send = FALSE;
766 
767   if (!gdk_rgba_equal (color, &current_program_state->inset_shadow.color))
768     {
769       op->color.value = color;
770       op->color.send = TRUE;
771 
772       current_program_state->inset_shadow.color = *color;
773     }
774   else
775     op->color.send = FALSE;
776 
777   if (dx != current_program_state->inset_shadow.dx ||
778       dy != current_program_state->inset_shadow.dy)
779     {
780       op->offset.value[0] = dx;
781       op->offset.value[1] = dy;
782       op->offset.send = TRUE;
783 
784       current_program_state->inset_shadow.dx = dx;
785       current_program_state->inset_shadow.dy = dy;
786     }
787   else
788     op->offset.send = FALSE;
789 
790   if (!op->outline.send &&
791       !op->spread.send &&
792       !op->offset.send &&
793       !op->color.send)
794     {
795       op_buffer_pop_tail (&self->render_ops);
796     }
797 }
798 void
ops_set_unblurred_outset_shadow(RenderOpBuilder * self,const GskRoundedRect outline,float spread,const GdkRGBA * color,float dx,float dy)799 ops_set_unblurred_outset_shadow (RenderOpBuilder      *self,
800                                  const GskRoundedRect  outline,
801                                  float                 spread,
802                                  const GdkRGBA        *color,
803                                  float                 dx,
804                                  float                 dy)
805 {
806   ProgramState *current_program_state = get_current_program_state (self);
807   OpShadow *op;
808 
809   g_assert (current_program_state);
810 
811   op = ops_begin (self, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
812 
813   if (!rounded_rect_equal (&outline, &current_program_state->unblurred_outset_shadow.outline))
814     {
815       op->outline.value = outline;
816       op->outline.send = TRUE;
817       op->outline.send_corners = !rounded_rect_corners_equal (&current_program_state->unblurred_outset_shadow.outline,
818                                                               &outline);
819       current_program_state->unblurred_outset_shadow.outline = outline;
820     }
821   else
822     op->outline.send = FALSE;
823 
824   if (spread != current_program_state->unblurred_outset_shadow.spread)
825     {
826       op->spread.value = spread;
827       op->spread.send = TRUE;
828 
829       current_program_state->unblurred_outset_shadow.spread = spread;
830     }
831   else
832     op->spread.send = FALSE;
833 
834   if (!gdk_rgba_equal (color, &current_program_state->unblurred_outset_shadow.color))
835     {
836       op->color.value = color;
837       op->color.send = TRUE;
838 
839       current_program_state->unblurred_outset_shadow.color = *color;
840     }
841   else
842     op->color.send = FALSE;
843 
844   if (dx != current_program_state->unblurred_outset_shadow.dx ||
845       dy != current_program_state->unblurred_outset_shadow.dy)
846     {
847       op->offset.value[0] = dx;
848       op->offset.value[1] = dy;
849       op->offset.send = TRUE;
850 
851       current_program_state->unblurred_outset_shadow.dx = dx;
852       current_program_state->unblurred_outset_shadow.dy = dy;
853     }
854   else
855     op->offset.send = FALSE;
856 }
857 
858 void
ops_set_linear_gradient(RenderOpBuilder * self,guint n_color_stops,const GskColorStop * color_stops,gboolean repeat,float start_x,float start_y,float end_x,float end_y)859 ops_set_linear_gradient (RenderOpBuilder     *self,
860                          guint                n_color_stops,
861                          const GskColorStop  *color_stops,
862                          gboolean             repeat,
863                          float                start_x,
864                          float                start_y,
865                          float                end_x,
866                          float                end_y)
867 {
868   ProgramState *current_program_state = get_current_program_state (self);
869   OpLinearGradient *op;
870   const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
871 
872   g_assert (current_program_state);
873 
874   op = ops_begin (self, OP_CHANGE_LINEAR_GRADIENT);
875 
876   /* We always save the n_color_stops value in the op so the renderer can use it in
877    * cases where we send the color stops, but not n_color_stops */
878   op->n_color_stops.value = real_n_color_stops;
879   if (current_program_state->linear_gradient.n_color_stops != real_n_color_stops)
880     {
881       op->n_color_stops.send = TRUE;
882       current_program_state->linear_gradient.n_color_stops = real_n_color_stops;
883     }
884   else
885     op->n_color_stops.send = FALSE;
886 
887   op->color_stops.send = FALSE;
888   if (!op->n_color_stops.send)
889     {
890       g_assert (current_program_state->linear_gradient.n_color_stops == real_n_color_stops);
891 
892       for (guint i = 0; i < real_n_color_stops; i ++)
893         {
894           const GskColorStop *s1 = &color_stops[i];
895           const GskColorStop *s2 = &current_program_state->linear_gradient.color_stops[i];
896 
897           if (s1->offset != s2->offset ||
898               !gdk_rgba_equal (&s1->color, &s2->color))
899             {
900               op->color_stops.send = TRUE;
901               break;
902             }
903         }
904     }
905   else
906     op->color_stops.send = TRUE;
907 
908   if (op->color_stops.send)
909     {
910       op->color_stops.value = color_stops;
911       memcpy (&current_program_state->linear_gradient.color_stops,
912               color_stops,
913               sizeof (GskColorStop) * real_n_color_stops);
914     }
915 
916   op->repeat = repeat;
917   op->start_point[0] = start_x;
918   op->start_point[1] = start_y;
919   op->end_point[0] = end_x;
920   op->end_point[1] = end_y;
921 }
922 
923 void
ops_set_radial_gradient(RenderOpBuilder * self,guint n_color_stops,const GskColorStop * color_stops,gboolean repeat,float center_x,float center_y,float start,float end,float hradius,float vradius)924 ops_set_radial_gradient (RenderOpBuilder    *self,
925                          guint               n_color_stops,
926                          const GskColorStop *color_stops,
927                          gboolean            repeat,
928                          float               center_x,
929                          float               center_y,
930                          float               start,
931                          float               end,
932                          float               hradius,
933                          float               vradius)
934 {
935   const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
936   OpRadialGradient *op;
937 
938   /* TODO: State tracking? */
939 
940   op = ops_begin (self, OP_CHANGE_RADIAL_GRADIENT);
941   op->n_color_stops.value = real_n_color_stops;
942   op->n_color_stops.send = true;
943   op->color_stops.value = color_stops;
944   op->color_stops.send = true;
945   op->center[0] = center_x;
946   op->center[1] = center_y;
947   op->radius[0] = hradius;
948   op->radius[1] = vradius;
949   op->start = start;
950   op->end = end;
951   op->repeat = repeat;
952 }
953 
954 void
ops_set_conic_gradient(RenderOpBuilder * self,guint n_color_stops,const GskColorStop * color_stops,float center_x,float center_y,float angle)955 ops_set_conic_gradient (RenderOpBuilder    *self,
956                         guint               n_color_stops,
957                         const GskColorStop *color_stops,
958                         float               center_x,
959                         float               center_y,
960                         float               angle)
961 {
962   const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
963   OpConicGradient *op;
964 
965   /* TODO: State tracking? */
966 
967   op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT);
968   op->n_color_stops.value = real_n_color_stops;
969   op->n_color_stops.send = true;
970   op->color_stops.value = color_stops;
971   op->color_stops.send = true;
972   op->center[0] = center_x;
973   op->center[1] = center_y;
974   op->angle = angle;
975 }
976 
977