1 /*
2 * Copyright © 2013 Red Hat
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Dave Airlie <airlied@redhat.com>
25 *
26 * some code is derived from the xf86-video-ati radeon driver, mainly
27 * the calculations.
28 */
29
30 /** @file glamor_xv.c
31 *
32 * Xv acceleration implementation
33 */
34
35 #ifdef HAVE_DIX_CONFIG_H
36 #include <dix-config.h>
37 #endif
38
39 #include "glamor_priv.h"
40 #include "glamor_transform.h"
41 #include "glamor_transfer.h"
42
43 #include <X11/extensions/Xv.h>
44 #include "../hw/xfree86/common/fourcc.h"
45 /* Reference color space transform data */
46 typedef struct tagREF_TRANSFORM {
47 float RefLuma;
48 float RefRCb;
49 float RefRCr;
50 float RefGCb;
51 float RefGCr;
52 float RefBCb;
53 float RefBCr;
54 } REF_TRANSFORM;
55
56 #define RTFSaturation(a) (1.0 + ((a)*1.0)/1000.0)
57 #define RTFBrightness(a) (((a)*1.0)/2000.0)
58 #define RTFIntensity(a) (((a)*1.0)/2000.0)
59 #define RTFContrast(a) (1.0 + ((a)*1.0)/1000.0)
60 #define RTFHue(a) (((a)*3.1416)/1000.0)
61
62 static const glamor_facet glamor_facet_xv_planar = {
63 .name = "xv_planar",
64
65 .version = 120,
66
67 .source_name = "v_texcoord0",
68 .vs_vars = ("attribute vec2 position;\n"
69 "attribute vec2 v_texcoord0;\n"
70 "varying vec2 tcs;\n"),
71 .vs_exec = (GLAMOR_POS(gl_Position, position)
72 " tcs = v_texcoord0;\n"),
73
74 .fs_vars = ("uniform sampler2D y_sampler;\n"
75 "uniform sampler2D u_sampler;\n"
76 "uniform sampler2D v_sampler;\n"
77 "uniform vec4 offsetyco;\n"
78 "uniform vec4 ucogamma;\n"
79 "uniform vec4 vco;\n"
80 "varying vec2 tcs;\n"),
81 .fs_exec = (
82 " float sample;\n"
83 " vec4 temp1;\n"
84 " sample = texture2D(y_sampler, tcs).w;\n"
85 " temp1.xyz = offsetyco.www * vec3(sample) + offsetyco.xyz;\n"
86 " sample = texture2D(u_sampler, tcs).w;\n"
87 " temp1.xyz = ucogamma.xyz * vec3(sample) + temp1.xyz;\n"
88 " sample = texture2D(v_sampler, tcs).w;\n"
89 " temp1.xyz = clamp(vco.xyz * vec3(sample) + temp1.xyz, 0.0, 1.0);\n"
90 " temp1.w = 1.0;\n"
91 " gl_FragColor = temp1;\n"
92 ),
93 };
94
95 #define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
96
97 XvAttributeRec glamor_xv_attributes[] = {
98 {XvSettable | XvGettable, -1000, 1000, (char *)"XV_BRIGHTNESS"},
99 {XvSettable | XvGettable, -1000, 1000, (char *)"XV_CONTRAST"},
100 {XvSettable | XvGettable, -1000, 1000, (char *)"XV_SATURATION"},
101 {XvSettable | XvGettable, -1000, 1000, (char *)"XV_HUE"},
102 {XvSettable | XvGettable, 0, 1, (char *)"XV_COLORSPACE"},
103 {0, 0, 0, NULL}
104 };
105 int glamor_xv_num_attributes = ARRAY_SIZE(glamor_xv_attributes) - 1;
106
107 Atom glamorBrightness, glamorContrast, glamorSaturation, glamorHue,
108 glamorColorspace, glamorGamma;
109
110 XvImageRec glamor_xv_images[] = {
111 XVIMAGE_YV12,
112 XVIMAGE_I420,
113 };
114 int glamor_xv_num_images = ARRAY_SIZE(glamor_xv_images);
115
116 static void
glamor_init_xv_shader(ScreenPtr screen)117 glamor_init_xv_shader(ScreenPtr screen)
118 {
119 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
120 GLint sampler_loc;
121
122 glamor_build_program(screen,
123 &glamor_priv->xv_prog,
124 &glamor_facet_xv_planar, NULL, NULL, NULL);
125
126 glUseProgram(glamor_priv->xv_prog.prog);
127 sampler_loc = glGetUniformLocation(glamor_priv->xv_prog.prog, "y_sampler");
128 glUniform1i(sampler_loc, 0);
129 sampler_loc = glGetUniformLocation(glamor_priv->xv_prog.prog, "u_sampler");
130 glUniform1i(sampler_loc, 1);
131 sampler_loc = glGetUniformLocation(glamor_priv->xv_prog.prog, "v_sampler");
132 glUniform1i(sampler_loc, 2);
133
134 }
135
136 #define ClipValue(v,min,max) ((v) < (min) ? (min) : (v) > (max) ? (max) : (v))
137
138 void
glamor_xv_stop_video(glamor_port_private * port_priv)139 glamor_xv_stop_video(glamor_port_private *port_priv)
140 {
141 }
142
143 static void
glamor_xv_free_port_data(glamor_port_private * port_priv)144 glamor_xv_free_port_data(glamor_port_private *port_priv)
145 {
146 int i;
147
148 for (i = 0; i < 3; i++) {
149 if (port_priv->src_pix[i]) {
150 glamor_destroy_pixmap(port_priv->src_pix[i]);
151 port_priv->src_pix[i] = NULL;
152 }
153 }
154 RegionUninit(&port_priv->clip);
155 RegionNull(&port_priv->clip);
156 }
157
158 int
glamor_xv_set_port_attribute(glamor_port_private * port_priv,Atom attribute,INT32 value)159 glamor_xv_set_port_attribute(glamor_port_private *port_priv,
160 Atom attribute, INT32 value)
161 {
162 if (attribute == glamorBrightness)
163 port_priv->brightness = ClipValue(value, -1000, 1000);
164 else if (attribute == glamorHue)
165 port_priv->hue = ClipValue(value, -1000, 1000);
166 else if (attribute == glamorContrast)
167 port_priv->contrast = ClipValue(value, -1000, 1000);
168 else if (attribute == glamorSaturation)
169 port_priv->saturation = ClipValue(value, -1000, 1000);
170 else if (attribute == glamorGamma)
171 port_priv->gamma = ClipValue(value, 100, 10000);
172 else if (attribute == glamorColorspace)
173 port_priv->transform_index = ClipValue(value, 0, 1);
174 else
175 return BadMatch;
176 return Success;
177 }
178
179 int
glamor_xv_get_port_attribute(glamor_port_private * port_priv,Atom attribute,INT32 * value)180 glamor_xv_get_port_attribute(glamor_port_private *port_priv,
181 Atom attribute, INT32 *value)
182 {
183 if (attribute == glamorBrightness)
184 *value = port_priv->brightness;
185 else if (attribute == glamorHue)
186 *value = port_priv->hue;
187 else if (attribute == glamorContrast)
188 *value = port_priv->contrast;
189 else if (attribute == glamorSaturation)
190 *value = port_priv->saturation;
191 else if (attribute == glamorGamma)
192 *value = port_priv->gamma;
193 else if (attribute == glamorColorspace)
194 *value = port_priv->transform_index;
195 else
196 return BadMatch;
197
198 return Success;
199 }
200
201 int
glamor_xv_query_image_attributes(int id,unsigned short * w,unsigned short * h,int * pitches,int * offsets)202 glamor_xv_query_image_attributes(int id,
203 unsigned short *w, unsigned short *h,
204 int *pitches, int *offsets)
205 {
206 int size = 0, tmp;
207
208 if (offsets)
209 offsets[0] = 0;
210 switch (id) {
211 case FOURCC_YV12:
212 case FOURCC_I420:
213 *w = ALIGN(*w, 2);
214 *h = ALIGN(*h, 2);
215 size = ALIGN(*w, 4);
216 if (pitches)
217 pitches[0] = size;
218 size *= *h;
219 if (offsets)
220 offsets[1] = size;
221 tmp = ALIGN(*w >> 1, 4);
222 if (pitches)
223 pitches[1] = pitches[2] = tmp;
224 tmp *= (*h >> 1);
225 size += tmp;
226 if (offsets)
227 offsets[2] = size;
228 size += tmp;
229 break;
230 }
231 return size;
232 }
233
234 /* Parameters for ITU-R BT.601 and ITU-R BT.709 colour spaces
235 note the difference to the parameters used in overlay are due
236 to 10bit vs. float calcs */
237 static REF_TRANSFORM trans[2] = {
238 {1.1643, 0.0, 1.5960, -0.3918, -0.8129, 2.0172, 0.0}, /* BT.601 */
239 {1.1643, 0.0, 1.7927, -0.2132, -0.5329, 2.1124, 0.0} /* BT.709 */
240 };
241
242 void
glamor_xv_render(glamor_port_private * port_priv)243 glamor_xv_render(glamor_port_private *port_priv)
244 {
245 ScreenPtr screen = port_priv->pPixmap->drawable.pScreen;
246 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
247 PixmapPtr pixmap = port_priv->pPixmap;
248 glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
249 glamor_pixmap_private *src_pixmap_priv[3];
250 BoxPtr box = REGION_RECTS(&port_priv->clip);
251 int nBox = REGION_NUM_RECTS(&port_priv->clip);
252 GLfloat src_xscale[3], src_yscale[3];
253 int i;
254 const float Loff = -0.0627;
255 const float Coff = -0.502;
256 float uvcosf, uvsinf;
257 float yco;
258 float uco[3], vco[3], off[3];
259 float bright, cont, gamma;
260 int ref = port_priv->transform_index;
261 GLint uloc;
262 GLfloat *v;
263 char *vbo_offset;
264 int dst_box_index;
265
266 if (!glamor_priv->xv_prog.prog)
267 glamor_init_xv_shader(screen);
268
269 cont = RTFContrast(port_priv->contrast);
270 bright = RTFBrightness(port_priv->brightness);
271 gamma = (float) port_priv->gamma / 1000.0;
272 uvcosf = RTFSaturation(port_priv->saturation) * cos(RTFHue(port_priv->hue));
273 uvsinf = RTFSaturation(port_priv->saturation) * sin(RTFHue(port_priv->hue));
274 /* overlay video also does pre-gamma contrast/sat adjust, should we? */
275
276 yco = trans[ref].RefLuma * cont;
277 uco[0] = -trans[ref].RefRCr * uvsinf;
278 uco[1] = trans[ref].RefGCb * uvcosf - trans[ref].RefGCr * uvsinf;
279 uco[2] = trans[ref].RefBCb * uvcosf;
280 vco[0] = trans[ref].RefRCr * uvcosf;
281 vco[1] = trans[ref].RefGCb * uvsinf + trans[ref].RefGCr * uvcosf;
282 vco[2] = trans[ref].RefBCb * uvsinf;
283 off[0] = Loff * yco + Coff * (uco[0] + vco[0]) + bright;
284 off[1] = Loff * yco + Coff * (uco[1] + vco[1]) + bright;
285 off[2] = Loff * yco + Coff * (uco[2] + vco[2]) + bright;
286 gamma = 1.0;
287
288 glamor_set_alu(screen, GXcopy);
289
290 for (i = 0; i < 3; i++) {
291 if (port_priv->src_pix[i]) {
292 src_pixmap_priv[i] =
293 glamor_get_pixmap_private(port_priv->src_pix[i]);
294 pixmap_priv_get_scale(src_pixmap_priv[i], &src_xscale[i],
295 &src_yscale[i]);
296 }
297 }
298 glamor_make_current(glamor_priv);
299 glUseProgram(glamor_priv->xv_prog.prog);
300
301 uloc = glGetUniformLocation(glamor_priv->xv_prog.prog, "offsetyco");
302 glUniform4f(uloc, off[0], off[1], off[2], yco);
303 uloc = glGetUniformLocation(glamor_priv->xv_prog.prog, "ucogamma");
304 glUniform4f(uloc, uco[0], uco[1], uco[2], gamma);
305 uloc = glGetUniformLocation(glamor_priv->xv_prog.prog, "vco");
306 glUniform4f(uloc, vco[0], vco[1], vco[2], 0);
307
308 glActiveTexture(GL_TEXTURE0);
309 glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[0]->fbo->tex);
310 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
311 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
312 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
313 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
314
315 glActiveTexture(GL_TEXTURE1);
316 glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[1]->fbo->tex);
317 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
318 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
319 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
320 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
321
322 glActiveTexture(GL_TEXTURE2);
323 glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[2]->fbo->tex);
324 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
326 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
327 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
328
329 glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
330 glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
331
332 glEnable(GL_SCISSOR_TEST);
333
334 v = glamor_get_vbo_space(screen, 3 * 4 * sizeof(GLfloat), &vbo_offset);
335
336 /* Set up a single primitive covering the area being drawn. We'll
337 * clip it to port_priv->clip using GL scissors instead of just
338 * emitting a GL_QUAD per box, because this way we hopefully avoid
339 * diagonal tearing between the two trangles used to rasterize a
340 * GL_QUAD.
341 */
342 i = 0;
343 v[i++] = port_priv->drw_x;
344 v[i++] = port_priv->drw_y;
345
346 v[i++] = port_priv->drw_x + port_priv->dst_w * 2;
347 v[i++] = port_priv->drw_y;
348
349 v[i++] = port_priv->drw_x;
350 v[i++] = port_priv->drw_y + port_priv->dst_h * 2;
351
352 v[i++] = t_from_x_coord_x(src_xscale[0], port_priv->src_x);
353 v[i++] = t_from_x_coord_y(src_yscale[0], port_priv->src_y);
354
355 v[i++] = t_from_x_coord_x(src_xscale[0], port_priv->src_x +
356 port_priv->src_w * 2);
357 v[i++] = t_from_x_coord_y(src_yscale[0], port_priv->src_y);
358
359 v[i++] = t_from_x_coord_x(src_xscale[0], port_priv->src_x);
360 v[i++] = t_from_x_coord_y(src_yscale[0], port_priv->src_y +
361 port_priv->src_h * 2);
362
363 glVertexAttribPointer(GLAMOR_VERTEX_POS, 2,
364 GL_FLOAT, GL_FALSE,
365 2 * sizeof(float), vbo_offset);
366
367 glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2,
368 GL_FLOAT, GL_FALSE,
369 2 * sizeof(float), vbo_offset + 6 * sizeof(GLfloat));
370
371 glamor_put_vbo_space(screen);
372
373 /* Now draw our big triangle, clipped to each of the clip boxes. */
374 glamor_pixmap_loop(pixmap_priv, dst_box_index) {
375 int dst_off_x, dst_off_y;
376
377 glamor_set_destination_drawable(port_priv->pDraw,
378 dst_box_index,
379 FALSE, FALSE,
380 glamor_priv->xv_prog.matrix_uniform,
381 &dst_off_x, &dst_off_y);
382
383 for (i = 0; i < nBox; i++) {
384 int dstx, dsty, dstw, dsth;
385
386 dstx = box[i].x1 + dst_off_x;
387 dsty = box[i].y1 + dst_off_y;
388 dstw = box[i].x2 - box[i].x1;
389 dsth = box[i].y2 - box[i].y1;
390
391 glScissor(dstx, dsty, dstw, dsth);
392 glDrawArrays(GL_TRIANGLE_FAN, 0, 3);
393 }
394 }
395 glDisable(GL_SCISSOR_TEST);
396
397 glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
398 glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
399
400 DamageDamageRegion(port_priv->pDraw, &port_priv->clip);
401
402 glamor_xv_free_port_data(port_priv);
403 }
404
405 int
glamor_xv_put_image(glamor_port_private * port_priv,DrawablePtr pDrawable,short src_x,short src_y,short drw_x,short drw_y,short src_w,short src_h,short drw_w,short drw_h,int id,unsigned char * buf,short width,short height,Bool sync,RegionPtr clipBoxes)406 glamor_xv_put_image(glamor_port_private *port_priv,
407 DrawablePtr pDrawable,
408 short src_x, short src_y,
409 short drw_x, short drw_y,
410 short src_w, short src_h,
411 short drw_w, short drw_h,
412 int id,
413 unsigned char *buf,
414 short width,
415 short height,
416 Bool sync,
417 RegionPtr clipBoxes)
418 {
419 ScreenPtr pScreen = pDrawable->pScreen;
420 int srcPitch, srcPitch2;
421 int top, nlines;
422 int s2offset, s3offset, tmp;
423 BoxRec full_box, half_box;
424
425 s2offset = s3offset = srcPitch2 = 0;
426
427 if (!port_priv->src_pix[0] ||
428 (width != port_priv->src_pix_w || height != port_priv->src_pix_h)) {
429 int i;
430
431 for (i = 0; i < 3; i++)
432 if (port_priv->src_pix[i])
433 glamor_destroy_pixmap(port_priv->src_pix[i]);
434
435 port_priv->src_pix[0] =
436 glamor_create_pixmap(pScreen, width, height, 8,
437 GLAMOR_CREATE_FBO_NO_FBO);
438 port_priv->src_pix[1] =
439 glamor_create_pixmap(pScreen, width >> 1, height >> 1, 8,
440 GLAMOR_CREATE_FBO_NO_FBO);
441 port_priv->src_pix[2] =
442 glamor_create_pixmap(pScreen, width >> 1, height >> 1, 8,
443 GLAMOR_CREATE_FBO_NO_FBO);
444 port_priv->src_pix_w = width;
445 port_priv->src_pix_h = height;
446
447 if (!port_priv->src_pix[0] || !port_priv->src_pix[1] ||
448 !port_priv->src_pix[2])
449 return BadAlloc;
450 }
451
452 top = (src_y) & ~1;
453 nlines = (src_y + src_h) - top;
454
455 switch (id) {
456 case FOURCC_YV12:
457 case FOURCC_I420:
458 srcPitch = ALIGN(width, 4);
459 srcPitch2 = ALIGN(width >> 1, 4);
460 s2offset = srcPitch * height;
461 s3offset = s2offset + (srcPitch2 * ((height + 1) >> 1));
462 s2offset += ((top >> 1) * srcPitch2);
463 s3offset += ((top >> 1) * srcPitch2);
464 if (id == FOURCC_YV12) {
465 tmp = s2offset;
466 s2offset = s3offset;
467 s3offset = tmp;
468 }
469
470 full_box.x1 = 0;
471 full_box.y1 = 0;
472 full_box.x2 = width;
473 full_box.y2 = nlines;
474
475 half_box.x1 = 0;
476 half_box.y1 = 0;
477 half_box.x2 = width >> 1;
478 half_box.y2 = (nlines + 1) >> 1;
479
480 glamor_upload_boxes(port_priv->src_pix[0], &full_box, 1,
481 0, 0, 0, 0,
482 buf + (top * srcPitch), srcPitch);
483
484 glamor_upload_boxes(port_priv->src_pix[1], &half_box, 1,
485 0, 0, 0, 0,
486 buf + s2offset, srcPitch2);
487
488 glamor_upload_boxes(port_priv->src_pix[2], &half_box, 1,
489 0, 0, 0, 0,
490 buf + s3offset, srcPitch2);
491 break;
492 default:
493 return BadMatch;
494 }
495
496 if (pDrawable->type == DRAWABLE_WINDOW)
497 port_priv->pPixmap = pScreen->GetWindowPixmap((WindowPtr) pDrawable);
498 else
499 port_priv->pPixmap = (PixmapPtr) pDrawable;
500
501 RegionCopy(&port_priv->clip, clipBoxes);
502
503 port_priv->src_x = src_x;
504 port_priv->src_y = src_y - top;
505 port_priv->src_w = src_w;
506 port_priv->src_h = src_h;
507 port_priv->dst_w = drw_w;
508 port_priv->dst_h = drw_h;
509 port_priv->drw_x = drw_x;
510 port_priv->drw_y = drw_y;
511 port_priv->w = width;
512 port_priv->h = height;
513 port_priv->pDraw = pDrawable;
514 glamor_xv_render(port_priv);
515 return Success;
516 }
517
518 void
glamor_xv_init_port(glamor_port_private * port_priv)519 glamor_xv_init_port(glamor_port_private *port_priv)
520 {
521 port_priv->brightness = 0;
522 port_priv->contrast = 0;
523 port_priv->saturation = 0;
524 port_priv->hue = 0;
525 port_priv->gamma = 1000;
526 port_priv->transform_index = 0;
527
528 REGION_NULL(pScreen, &port_priv->clip);
529 }
530
531 void
glamor_xv_core_init(ScreenPtr screen)532 glamor_xv_core_init(ScreenPtr screen)
533 {
534 glamorBrightness = MAKE_ATOM("XV_BRIGHTNESS");
535 glamorContrast = MAKE_ATOM("XV_CONTRAST");
536 glamorSaturation = MAKE_ATOM("XV_SATURATION");
537 glamorHue = MAKE_ATOM("XV_HUE");
538 glamorGamma = MAKE_ATOM("XV_GAMMA");
539 glamorColorspace = MAKE_ATOM("XV_COLORSPACE");
540 }
541