vl_compositor.c revision 3d40d4f391e2fc319a03d8f171a2cfb9daf250c8
1/**************************************************************************
2 *
3 * Copyright 2009 Younes Manton.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "vl_compositor.h"
29#include "util/u_draw.h"
30#include <assert.h>
31#include <pipe/p_context.h>
32#include <util/u_inlines.h>
33#include <util/u_memory.h>
34#include <util/u_keymap.h>
35#include <util/u_draw.h>
36#include <util/u_sampler.h>
37#include <tgsi/tgsi_ureg.h>
38#include "vl_csc.h"
39
40struct vertex_shader_consts
41{
42   struct vertex4f dst_scale;
43   struct vertex4f dst_trans;
44   struct vertex4f src_scale;
45   struct vertex4f src_trans;
46};
47
48struct fragment_shader_consts
49{
50   float matrix[16];
51};
52
53static bool
54u_video_rects_equal(struct pipe_video_rect *a, struct pipe_video_rect *b)
55{
56   assert(a && b);
57
58   if (a->x != b->x)
59      return false;
60   if (a->y != b->y)
61      return false;
62   if (a->w != b->w)
63      return false;
64   if (a->h != b->h)
65      return false;
66
67   return true;
68}
69
70static bool
71create_vert_shader(struct vl_compositor *c)
72{
73   struct ureg_program *shader;
74   struct ureg_src vpos, vtex;
75   struct ureg_dst o_vpos, o_vtex;
76
77   shader = ureg_create(TGSI_PROCESSOR_VERTEX);
78   if (!shader)
79      return false;
80
81   vpos = ureg_DECL_vs_input(shader, 0);
82   vtex = ureg_DECL_vs_input(shader, 1);
83   o_vpos = ureg_DECL_output(shader, TGSI_SEMANTIC_POSITION, 0);
84   o_vtex = ureg_DECL_output(shader, TGSI_SEMANTIC_GENERIC, 1);
85
86   /*
87    * o_vpos = vpos
88    * o_vtex = vtex
89    */
90   ureg_MOV(shader, o_vpos, vpos);
91   ureg_MOV(shader, o_vtex, vtex);
92
93   ureg_END(shader);
94
95   c->vertex_shader = ureg_create_shader_and_destroy(shader, c->pipe);
96   if (!c->vertex_shader)
97      return false;
98
99   return true;
100}
101
102static bool
103create_frag_shader_ycbcr_2_rgb(struct vl_compositor *c)
104{
105   struct ureg_program *shader;
106   struct ureg_src tc;
107   struct ureg_src csc[4];
108   struct ureg_src sampler;
109   struct ureg_dst texel;
110   struct ureg_dst fragment;
111   unsigned i;
112
113   shader = ureg_create(TGSI_PROCESSOR_FRAGMENT);
114   if (!shader)
115      return false;
116
117   tc = ureg_DECL_fs_input(shader, TGSI_SEMANTIC_GENERIC, 1, TGSI_INTERPOLATE_LINEAR);
118   for (i = 0; i < 4; ++i)
119      csc[i] = ureg_DECL_constant(shader, i);
120   sampler = ureg_DECL_sampler(shader, 0);
121   texel = ureg_DECL_temporary(shader);
122   fragment = ureg_DECL_output(shader, TGSI_SEMANTIC_COLOR, 0);
123
124   /*
125    * texel = tex(tc, sampler)
126    * fragment = csc * texel
127    */
128   ureg_TEX(shader, texel, TGSI_TEXTURE_2D, tc, sampler);
129   for (i = 0; i < 4; ++i)
130      ureg_DP4(shader, ureg_writemask(fragment, TGSI_WRITEMASK_X << i), csc[i], ureg_src(texel));
131
132   ureg_release_temporary(shader, texel);
133   ureg_END(shader);
134
135   c->fragment_shader.ycbcr_2_rgb = ureg_create_shader_and_destroy(shader, c->pipe);
136   if (!c->fragment_shader.ycbcr_2_rgb)
137      return false;
138
139   return true;
140}
141
142static bool
143create_frag_shader_rgb_2_rgb(struct vl_compositor *c)
144{
145   struct ureg_program *shader;
146   struct ureg_src tc;
147   struct ureg_src sampler;
148   struct ureg_dst fragment;
149
150   shader = ureg_create(TGSI_PROCESSOR_FRAGMENT);
151   if (!shader)
152      return false;
153
154   tc = ureg_DECL_fs_input(shader, TGSI_SEMANTIC_GENERIC, 1, TGSI_INTERPOLATE_LINEAR);
155   sampler = ureg_DECL_sampler(shader, 0);
156   fragment = ureg_DECL_output(shader, TGSI_SEMANTIC_COLOR, 0);
157
158   /*
159    * fragment = tex(tc, sampler)
160    */
161   ureg_TEX(shader, fragment, TGSI_TEXTURE_2D, tc, sampler);
162   ureg_END(shader);
163
164   c->fragment_shader.rgb_2_rgb = ureg_create_shader_and_destroy(shader, c->pipe);
165   if (!c->fragment_shader.rgb_2_rgb)
166      return false;
167
168   return true;
169}
170
171static bool
172init_pipe_state(struct vl_compositor *c)
173{
174   struct pipe_sampler_state sampler;
175
176   assert(c);
177
178   c->fb_state.nr_cbufs = 1;
179   c->fb_state.zsbuf = NULL;
180
181   memset(&sampler, 0, sizeof(sampler));
182   sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
183   sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
184   sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
185   sampler.min_img_filter = PIPE_TEX_FILTER_LINEAR;
186   sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
187   sampler.mag_img_filter = PIPE_TEX_FILTER_LINEAR;
188   sampler.compare_mode = PIPE_TEX_COMPARE_NONE;
189   sampler.compare_func = PIPE_FUNC_ALWAYS;
190   sampler.normalized_coords = 1;
191   /*sampler.lod_bias = ;*/
192   /*sampler.min_lod = ;*/
193   /*sampler.max_lod = ;*/
194   /*sampler.border_color[i] = ;*/
195   /*sampler.max_anisotropy = ;*/
196   c->sampler = c->pipe->create_sampler_state(c->pipe, &sampler);
197
198   return true;
199}
200
201static void cleanup_pipe_state(struct vl_compositor *c)
202{
203   assert(c);
204
205   c->pipe->delete_sampler_state(c->pipe, c->sampler);
206}
207
208static bool
209init_shaders(struct vl_compositor *c)
210{
211   assert(c);
212
213   if (!create_vert_shader(c)) {
214      debug_printf("Unable to create vertex shader.\n");
215      return false;
216   }
217   if (!create_frag_shader_ycbcr_2_rgb(c)) {
218      debug_printf("Unable to create YCbCr-to-RGB fragment shader.\n");
219      return false;
220   }
221   if (!create_frag_shader_rgb_2_rgb(c)) {
222      debug_printf("Unable to create RGB-to-RGB fragment shader.\n");
223      return false;
224   }
225
226   return true;
227}
228
229static void cleanup_shaders(struct vl_compositor *c)
230{
231   assert(c);
232
233   c->pipe->delete_vs_state(c->pipe, c->vertex_shader);
234   c->pipe->delete_fs_state(c->pipe, c->fragment_shader.ycbcr_2_rgb);
235   c->pipe->delete_fs_state(c->pipe, c->fragment_shader.rgb_2_rgb);
236}
237
238static bool
239init_buffers(struct vl_compositor *c)
240{
241   struct fragment_shader_consts fsc;
242   struct pipe_vertex_element vertex_elems[2];
243
244   assert(c);
245
246   /*
247    * Create our vertex buffer and vertex buffer elements
248    */
249   c->vertex_buf.stride = sizeof(struct vertex4f);
250   c->vertex_buf.buffer_offset = 0;
251   /* XXX: Create with DYNAMIC or STREAM */
252   c->vertex_buf.buffer = pipe_buffer_create
253   (
254      c->pipe->screen,
255      PIPE_BIND_VERTEX_BUFFER,
256      PIPE_USAGE_STATIC,
257      sizeof(struct vertex4f) * (VL_COMPOSITOR_MAX_LAYERS + 2) * 6
258   );
259
260   vertex_elems[0].src_offset = 0;
261   vertex_elems[0].instance_divisor = 0;
262   vertex_elems[0].vertex_buffer_index = 0;
263   vertex_elems[0].src_format = PIPE_FORMAT_R32G32_FLOAT;
264   vertex_elems[1].src_offset = sizeof(struct vertex2f);
265   vertex_elems[1].instance_divisor = 0;
266   vertex_elems[1].vertex_buffer_index = 0;
267   vertex_elems[1].src_format = PIPE_FORMAT_R32G32_FLOAT;
268   c->vertex_elems_state = c->pipe->create_vertex_elements_state(c->pipe, 2, vertex_elems);
269
270   /*
271    * Create our fragment shader's constant buffer
272    * Const buffer contains the color conversion matrix and bias vectors
273    */
274   /* XXX: Create with IMMUTABLE/STATIC... although it does change every once in a long while... */
275   c->fs_const_buf = pipe_buffer_create
276   (
277      c->pipe->screen,
278      PIPE_BIND_CONSTANT_BUFFER,
279      PIPE_USAGE_STATIC,
280      sizeof(struct fragment_shader_consts)
281   );
282
283   vl_csc_get_matrix(VL_CSC_COLOR_STANDARD_IDENTITY, NULL, true, fsc.matrix);
284
285   vl_compositor_set_csc_matrix(c, fsc.matrix);
286
287   return true;
288}
289
290static void
291cleanup_buffers(struct vl_compositor *c)
292{
293   assert(c);
294
295   c->pipe->delete_vertex_elements_state(c->pipe, c->vertex_elems_state);
296   pipe_resource_reference(&c->vertex_buf.buffer, NULL);
297   pipe_resource_reference(&c->fs_const_buf, NULL);
298}
299
300static void
301texview_map_delete(const struct keymap *map,
302                   const void *key, void *data,
303                   void *user)
304{
305   struct pipe_sampler_view *sv = (struct pipe_sampler_view*)data;
306
307   assert(map);
308   assert(key);
309   assert(data);
310   assert(user);
311
312   pipe_sampler_view_reference(&sv, NULL);
313}
314
315bool vl_compositor_init(struct vl_compositor *compositor, struct pipe_context *pipe)
316{
317   unsigned i;
318
319   assert(compositor);
320
321   memset(compositor, 0, sizeof(struct vl_compositor));
322
323   compositor->pipe = pipe;
324
325   compositor->texview_map = util_new_keymap(sizeof(struct pipe_surface*), -1,
326                                             texview_map_delete);
327   if (!compositor->texview_map)
328      return false;
329
330   if (!init_pipe_state(compositor)) {
331      util_delete_keymap(compositor->texview_map, compositor->pipe);
332      return false;
333   }
334   if (!init_shaders(compositor)) {
335      util_delete_keymap(compositor->texview_map, compositor->pipe);
336      cleanup_pipe_state(compositor);
337      return false;
338   }
339   if (!init_buffers(compositor)) {
340      util_delete_keymap(compositor->texview_map, compositor->pipe);
341      cleanup_shaders(compositor);
342      cleanup_pipe_state(compositor);
343      return false;
344   }
345
346   compositor->fb_state.width = 0;
347   compositor->fb_state.height = 0;
348   for (i = 0; i < VL_COMPOSITOR_MAX_LAYERS; ++i)
349      compositor->layers[i] = NULL;
350   compositor->dirty_layers = 0;
351
352   return true;
353}
354
355void vl_compositor_cleanup(struct vl_compositor *compositor)
356{
357   assert(compositor);
358
359   util_delete_keymap(compositor->texview_map, compositor->pipe);
360   cleanup_buffers(compositor);
361   cleanup_shaders(compositor);
362   cleanup_pipe_state(compositor);
363}
364
365#if 0
366void vl_compositor_set_background(struct vl_compositor *compositor,
367                                 struct pipe_surface *bg, struct pipe_video_rect *bg_src_rect)
368{
369   assert(compositor);
370   assert((bg && bg_src_rect) || (!bg && !bg_src_rect));
371
372   if (compositor->bg != bg ||
373       !u_video_rects_equal(&compositor->bg_src_rect, bg_src_rect)) {
374      pipe_surface_reference(&compositor->bg, bg);
375      /*if (!u_video_rects_equal(&compositor->bg_src_rect, bg_src_rect))*/
376         compositor->bg_src_rect = *bg_src_rect;
377      compositor->dirty_bg = true;
378   }
379}
380#endif
381
382void vl_compositor_set_layers(struct vl_compositor *compositor,
383                              struct pipe_sampler_view *layers[],
384                              struct pipe_video_rect *src_rects[],
385                              struct pipe_video_rect *dst_rects[],
386                              unsigned num_layers)
387{
388   unsigned i;
389
390   assert(compositor);
391   assert(num_layers <= VL_COMPOSITOR_MAX_LAYERS);
392
393   for (i = 0; i < num_layers; ++i)
394   {
395      assert((layers[i] && src_rects[i] && dst_rects[i]) ||
396             (!layers[i] && !src_rects[i] && !dst_rects[i]));
397
398      if (compositor->layers[i] != layers[i] ||
399          !u_video_rects_equal(&compositor->layer_src_rects[i], src_rects[i]) ||
400          !u_video_rects_equal(&compositor->layer_dst_rects[i], dst_rects[i]))
401      {
402         pipe_sampler_view_reference(&compositor->layers[i], layers[i]);
403         compositor->layer_src_rects[i] = *src_rects[i];
404         compositor->layer_dst_rects[i] = *dst_rects[i];
405         compositor->dirty_layers |= 1 << i;
406      }
407
408      if (layers[i])
409         compositor->dirty_layers |= 1 << i;
410   }
411
412   for (; i < VL_COMPOSITOR_MAX_LAYERS; ++i)
413      pipe_sampler_view_reference(&compositor->layers[i], NULL);
414}
415
416static void gen_rect_verts(unsigned pos,
417                           struct pipe_video_rect *src_rect,
418                           struct vertex2f *src_inv_size,
419                           struct pipe_video_rect *dst_rect,
420                           struct vertex2f *dst_inv_size,
421                           struct vertex4f *vb)
422{
423   assert(pos < VL_COMPOSITOR_MAX_LAYERS + 2);
424   assert(src_rect);
425   assert(src_inv_size);
426   assert((dst_rect && dst_inv_size) /*|| (!dst_rect && !dst_inv_size)*/);
427   assert(vb);
428
429   vb[pos * 6 + 0].x = dst_rect->x * dst_inv_size->x;
430   vb[pos * 6 + 0].y = dst_rect->y * dst_inv_size->y;
431   vb[pos * 6 + 0].z = src_rect->x * src_inv_size->x;
432   vb[pos * 6 + 0].w = src_rect->y * src_inv_size->y;
433
434   vb[pos * 6 + 1].x = dst_rect->x * dst_inv_size->x;
435   vb[pos * 6 + 1].y = (dst_rect->y + dst_rect->h) * dst_inv_size->y;
436   vb[pos * 6 + 1].z = src_rect->x * src_inv_size->x;
437   vb[pos * 6 + 1].w = (src_rect->y + src_rect->h) * src_inv_size->y;
438
439   vb[pos * 6 + 2].x = (dst_rect->x + dst_rect->w) * dst_inv_size->x;
440   vb[pos * 6 + 2].y = dst_rect->y * dst_inv_size->y;
441   vb[pos * 6 + 2].z = (src_rect->x + src_rect->w) * src_inv_size->x;
442   vb[pos * 6 + 2].w = src_rect->y * src_inv_size->y;
443
444   vb[pos * 6 + 3].x = (dst_rect->x + dst_rect->w) * dst_inv_size->x;
445   vb[pos * 6 + 3].y = dst_rect->y * dst_inv_size->y;
446   vb[pos * 6 + 3].z = (src_rect->x + src_rect->w) * src_inv_size->x;
447   vb[pos * 6 + 3].w = src_rect->y * src_inv_size->y;
448
449   vb[pos * 6 + 4].x = dst_rect->x * dst_inv_size->x;
450   vb[pos * 6 + 4].y = (dst_rect->y + dst_rect->h) * dst_inv_size->y;
451   vb[pos * 6 + 4].z = src_rect->x * src_inv_size->x;
452   vb[pos * 6 + 4].w = (src_rect->y + src_rect->h) * src_inv_size->y;
453
454   vb[pos * 6 + 5].x = (dst_rect->x + dst_rect->w) * dst_inv_size->x;
455   vb[pos * 6 + 5].y = (dst_rect->y + dst_rect->h) * dst_inv_size->y;
456   vb[pos * 6 + 5].z = (src_rect->x + src_rect->w) * src_inv_size->x;
457   vb[pos * 6 + 5].w = (src_rect->y + src_rect->h) * src_inv_size->y;
458}
459
460static unsigned gen_data(struct vl_compositor *c,
461                         struct pipe_sampler_view *src_surface,
462                         struct pipe_video_rect *src_rect,
463                         struct pipe_video_rect *dst_rect,
464                         struct pipe_sampler_view **textures,
465                         void **frag_shaders)
466{
467   void *vb;
468   struct pipe_transfer *buf_transfer;
469   unsigned num_rects = 0;
470   unsigned i;
471
472   assert(c);
473   assert(src_surface);
474   assert(src_rect);
475   assert(dst_rect);
476   assert(textures);
477
478   vb = pipe_buffer_map(c->pipe, c->vertex_buf.buffer,
479                        PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD,
480                        &buf_transfer);
481
482   if (!vb)
483      return 0;
484
485   {
486      struct vertex2f src_inv_size = { 1.0f / src_surface->texture->width0, 1.0f / src_surface->texture->height0};
487      gen_rect_verts(num_rects, src_rect, &src_inv_size, dst_rect, &c->fb_inv_size, vb);
488      textures[num_rects] = src_surface;
489      /* XXX: Hack, sort of */
490      frag_shaders[num_rects] = c->fragment_shader.ycbcr_2_rgb;
491      ++num_rects;
492   }
493
494   for (i = 0; c->dirty_layers > 0; i++) {
495      assert(i < VL_COMPOSITOR_MAX_LAYERS);
496
497      if (c->dirty_layers & (1 << i)) {
498         struct vertex2f layer_inv_size = {1.0f / c->layers[i]->texture->width0, 1.0f / c->layers[i]->texture->height0};
499         gen_rect_verts(num_rects, &c->layer_src_rects[i], &layer_inv_size,
500                        &c->layer_dst_rects[i], &c->fb_inv_size, vb);
501         textures[num_rects] = c->layers[i];
502         /* XXX: Hack */
503         frag_shaders[num_rects] = c->fragment_shader.rgb_2_rgb;
504         ++num_rects;
505         c->dirty_layers &= ~(1 << i);
506      }
507   }
508
509   pipe_buffer_unmap(c->pipe, buf_transfer);
510
511   return num_rects;
512}
513
514static void draw_layers(struct vl_compositor *c,
515                        struct pipe_sampler_view *src_surface,
516                        struct pipe_video_rect *src_rect,
517                        struct pipe_video_rect *dst_rect)
518{
519   unsigned num_rects;
520   struct pipe_sampler_view *src_surfaces[VL_COMPOSITOR_MAX_LAYERS + 2];
521   void *frag_shaders[VL_COMPOSITOR_MAX_LAYERS + 2];
522   unsigned i;
523
524   assert(c);
525   assert(src_surface);
526   assert(src_rect);
527   assert(dst_rect);
528
529   num_rects = gen_data(c, src_surface, src_rect, dst_rect, src_surfaces, frag_shaders);
530
531   for (i = 0; i < num_rects; ++i) {
532      boolean delete_view = FALSE;
533      struct pipe_sampler_view *surface_view = (struct pipe_sampler_view*)util_keymap_lookup(c->texview_map,
534                                                                                             &src_surfaces[i]);
535      if (!surface_view) {
536         struct pipe_sampler_view templat;
537         u_sampler_view_default_template(&templat, src_surfaces[i]->texture,
538                                         src_surfaces[i]->texture->format);
539         surface_view = c->pipe->create_sampler_view(c->pipe, src_surfaces[i]->texture,
540                                                     &templat);
541         if (!surface_view)
542            return;
543
544         delete_view = !util_keymap_insert(c->texview_map, &src_surfaces[i],
545                                           surface_view, c->pipe);
546      }
547
548      c->pipe->bind_fs_state(c->pipe, frag_shaders[i]);
549      c->pipe->set_fragment_sampler_views(c->pipe, 1, &surface_view);
550
551      util_draw_arrays(c->pipe, PIPE_PRIM_TRIANGLES, i * 6, 6);
552
553      if (delete_view) {
554         pipe_sampler_view_reference(&surface_view, NULL);
555      }
556   }
557}
558
559void vl_compositor_render(struct vl_compositor          *compositor,
560                          struct pipe_sampler_view      *src_surface,
561                          enum pipe_mpeg12_picture_type picture_type,
562                          struct pipe_video_rect        *src_area,
563                          struct pipe_surface           *dst_surface,
564                          struct pipe_video_rect        *dst_area,
565                          struct pipe_fence_handle      **fence)
566{
567   assert(compositor);
568   assert(src_surface);
569   assert(src_area);
570   assert(dst_surface);
571   assert(dst_area);
572   assert(picture_type == PIPE_MPEG12_PICTURE_TYPE_FRAME);
573
574   if (compositor->fb_state.width != dst_surface->width) {
575      compositor->fb_inv_size.x = 1.0f / dst_surface->width;
576      compositor->fb_state.width = dst_surface->width;
577   }
578   if (compositor->fb_state.height != dst_surface->height) {
579      compositor->fb_inv_size.y = 1.0f / dst_surface->height;
580      compositor->fb_state.height = dst_surface->height;
581   }
582
583   compositor->fb_state.cbufs[0] = dst_surface;
584
585   compositor->viewport.scale[0] = compositor->fb_state.width;
586   compositor->viewport.scale[1] = compositor->fb_state.height;
587   compositor->viewport.scale[2] = 1;
588   compositor->viewport.scale[3] = 1;
589   compositor->viewport.translate[0] = 0;
590   compositor->viewport.translate[1] = 0;
591   compositor->viewport.translate[2] = 0;
592   compositor->viewport.translate[3] = 0;
593
594   compositor->pipe->set_framebuffer_state(compositor->pipe, &compositor->fb_state);
595   compositor->pipe->set_viewport_state(compositor->pipe, &compositor->viewport);
596   compositor->pipe->bind_fragment_sampler_states(compositor->pipe, 1, &compositor->sampler);
597   compositor->pipe->bind_vs_state(compositor->pipe, compositor->vertex_shader);
598   compositor->pipe->set_vertex_buffers(compositor->pipe, 1, &compositor->vertex_buf);
599   compositor->pipe->bind_vertex_elements_state(compositor->pipe, compositor->vertex_elems_state);
600   compositor->pipe->set_constant_buffer(compositor->pipe, PIPE_SHADER_FRAGMENT, 0, compositor->fs_const_buf);
601
602   draw_layers(compositor, src_surface, src_area, dst_area);
603
604   assert(!compositor->dirty_layers);
605   compositor->pipe->flush(compositor->pipe, fence);
606}
607
608void vl_compositor_set_csc_matrix(struct vl_compositor *compositor, const float *mat)
609{
610   struct pipe_transfer *buf_transfer;
611
612   assert(compositor);
613
614   memcpy
615   (
616      pipe_buffer_map(compositor->pipe, compositor->fs_const_buf,
617                      PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD,
618                      &buf_transfer),
619		mat,
620		sizeof(struct fragment_shader_consts)
621   );
622
623   pipe_buffer_unmap(compositor->pipe, buf_transfer);
624}
625