1 /**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen & Orasanu Lucian.
4 * Copyright 2014 Advanced Micro Devices, Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 **************************************************************************/
28
29 #include "pipe/p_screen.h"
30
31 #include "util/u_memory.h"
32 #include "util/u_handle_table.h"
33 #include "util/u_surface.h"
34 #include "util/u_video.h"
35
36 #include "vl/vl_winsys.h"
37 #include "vl/vl_video_buffer.h"
38
39 #include "va_private.h"
40
41 static const VAImageFormat formats[] =
42 {
43 {VA_FOURCC('N','V','1','2')},
44 {VA_FOURCC('P','0','1','0')},
45 {VA_FOURCC('P','0','1','6')},
46 {VA_FOURCC('I','4','2','0')},
47 {VA_FOURCC('Y','V','1','2')},
48 {VA_FOURCC('Y','U','Y','V')},
49 {VA_FOURCC('U','Y','V','Y')},
50 {.fourcc = VA_FOURCC('B','G','R','A'), .byte_order = VA_LSB_FIRST, 32, 32,
51 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
52 {.fourcc = VA_FOURCC('R','G','B','A'), .byte_order = VA_LSB_FIRST, 32, 32,
53 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
54 {.fourcc = VA_FOURCC('B','G','R','X'), .byte_order = VA_LSB_FIRST, 32, 24,
55 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
56 {.fourcc = VA_FOURCC('R','G','B','X'), .byte_order = VA_LSB_FIRST, 32, 24,
57 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000}
58 };
59
60 static void
vlVaVideoSurfaceSize(vlVaSurface * p_surf,int component,unsigned * width,unsigned * height)61 vlVaVideoSurfaceSize(vlVaSurface *p_surf, int component,
62 unsigned *width, unsigned *height)
63 {
64 *width = p_surf->templat.width;
65 *height = p_surf->templat.height;
66
67 vl_video_buffer_adjust_size(width, height, component,
68 p_surf->templat.chroma_format,
69 p_surf->templat.interlaced);
70 }
71
72 VAStatus
vlVaQueryImageFormats(VADriverContextP ctx,VAImageFormat * format_list,int * num_formats)73 vlVaQueryImageFormats(VADriverContextP ctx, VAImageFormat *format_list, int *num_formats)
74 {
75 struct pipe_screen *pscreen;
76 enum pipe_format format;
77 int i;
78
79 STATIC_ASSERT(ARRAY_SIZE(formats) == VL_VA_MAX_IMAGE_FORMATS);
80
81 if (!ctx)
82 return VA_STATUS_ERROR_INVALID_CONTEXT;
83
84 if (!(format_list && num_formats))
85 return VA_STATUS_ERROR_INVALID_PARAMETER;
86
87 *num_formats = 0;
88 pscreen = VL_VA_PSCREEN(ctx);
89 for (i = 0; i < ARRAY_SIZE(formats); ++i) {
90 format = VaFourccToPipeFormat(formats[i].fourcc);
91 if (pscreen->is_video_format_supported(pscreen, format,
92 PIPE_VIDEO_PROFILE_UNKNOWN,
93 PIPE_VIDEO_ENTRYPOINT_BITSTREAM))
94 format_list[(*num_formats)++] = formats[i];
95 }
96
97 return VA_STATUS_SUCCESS;
98 }
99
100 VAStatus
vlVaCreateImage(VADriverContextP ctx,VAImageFormat * format,int width,int height,VAImage * image)101 vlVaCreateImage(VADriverContextP ctx, VAImageFormat *format, int width, int height, VAImage *image)
102 {
103 VAStatus status;
104 vlVaDriver *drv;
105 VAImage *img;
106 int w, h;
107
108 if (!ctx)
109 return VA_STATUS_ERROR_INVALID_CONTEXT;
110
111 if (!(format && image && width && height))
112 return VA_STATUS_ERROR_INVALID_PARAMETER;
113
114 drv = VL_VA_DRIVER(ctx);
115
116 img = CALLOC(1, sizeof(VAImage));
117 if (!img)
118 return VA_STATUS_ERROR_ALLOCATION_FAILED;
119 mtx_lock(&drv->mutex);
120 img->image_id = handle_table_add(drv->htab, img);
121 mtx_unlock(&drv->mutex);
122
123 img->format = *format;
124 img->width = width;
125 img->height = height;
126 w = align(width, 2);
127 h = align(height, 2);
128
129 switch (format->fourcc) {
130 case VA_FOURCC('N','V','1','2'):
131 img->num_planes = 2;
132 img->pitches[0] = w;
133 img->offsets[0] = 0;
134 img->pitches[1] = w;
135 img->offsets[1] = w * h;
136 img->data_size = w * h * 3 / 2;
137 break;
138
139 case VA_FOURCC('P','0','1','0'):
140 case VA_FOURCC('P','0','1','6'):
141 img->num_planes = 2;
142 img->pitches[0] = w * 2;
143 img->offsets[0] = 0;
144 img->pitches[1] = w * 2;
145 img->offsets[1] = w * h * 2;
146 img->data_size = w * h * 3;
147 break;
148
149 case VA_FOURCC('I','4','2','0'):
150 case VA_FOURCC('Y','V','1','2'):
151 img->num_planes = 3;
152 img->pitches[0] = w;
153 img->offsets[0] = 0;
154 img->pitches[1] = w / 2;
155 img->offsets[1] = w * h;
156 img->pitches[2] = w / 2;
157 img->offsets[2] = w * h * 5 / 4;
158 img->data_size = w * h * 3 / 2;
159 break;
160
161 case VA_FOURCC('U','Y','V','Y'):
162 case VA_FOURCC('Y','U','Y','V'):
163 img->num_planes = 1;
164 img->pitches[0] = w * 2;
165 img->offsets[0] = 0;
166 img->data_size = w * h * 2;
167 break;
168
169 case VA_FOURCC('B','G','R','A'):
170 case VA_FOURCC('R','G','B','A'):
171 case VA_FOURCC('B','G','R','X'):
172 case VA_FOURCC('R','G','B','X'):
173 img->num_planes = 1;
174 img->pitches[0] = w * 4;
175 img->offsets[0] = 0;
176 img->data_size = w * h * 4;
177 break;
178
179 default:
180 return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
181 }
182
183 status = vlVaCreateBuffer(ctx, 0, VAImageBufferType,
184 align(img->data_size, 16),
185 1, NULL, &img->buf);
186 if (status != VA_STATUS_SUCCESS)
187 return status;
188 *image = *img;
189
190 return status;
191 }
192
193 VAStatus
vlVaDeriveImage(VADriverContextP ctx,VASurfaceID surface,VAImage * image)194 vlVaDeriveImage(VADriverContextP ctx, VASurfaceID surface, VAImage *image)
195 {
196 vlVaDriver *drv;
197 vlVaSurface *surf;
198 vlVaBuffer *img_buf;
199 VAImage *img;
200 struct pipe_surface **surfaces;
201 int w;
202 int h;
203 int i;
204
205 if (!ctx)
206 return VA_STATUS_ERROR_INVALID_CONTEXT;
207
208 drv = VL_VA_DRIVER(ctx);
209
210 if (!drv)
211 return VA_STATUS_ERROR_INVALID_CONTEXT;
212
213 surf = handle_table_get(drv->htab, surface);
214
215 if (!surf || !surf->buffer || surf->buffer->interlaced)
216 return VA_STATUS_ERROR_INVALID_SURFACE;
217
218 surfaces = surf->buffer->get_surfaces(surf->buffer);
219 if (!surfaces || !surfaces[0]->texture)
220 return VA_STATUS_ERROR_ALLOCATION_FAILED;
221
222 img = CALLOC(1, sizeof(VAImage));
223 if (!img)
224 return VA_STATUS_ERROR_ALLOCATION_FAILED;
225
226 img->format.fourcc = PipeFormatToVaFourcc(surf->buffer->buffer_format);
227 img->buf = VA_INVALID_ID;
228 img->width = surf->buffer->width;
229 img->height = surf->buffer->height;
230 img->num_palette_entries = 0;
231 img->entry_bytes = 0;
232 w = align(surf->buffer->width, 2);
233 h = align(surf->buffer->height, 2);
234
235 for (i = 0; i < ARRAY_SIZE(formats); ++i) {
236 if (img->format.fourcc == formats[i].fourcc) {
237 img->format = formats[i];
238 break;
239 }
240 }
241
242 switch (img->format.fourcc) {
243 case VA_FOURCC('U','Y','V','Y'):
244 case VA_FOURCC('Y','U','Y','V'):
245 img->num_planes = 1;
246 img->pitches[0] = w * 2;
247 img->offsets[0] = 0;
248 img->data_size = w * h * 2;
249 break;
250
251 case VA_FOURCC('B','G','R','A'):
252 case VA_FOURCC('R','G','B','A'):
253 case VA_FOURCC('B','G','R','X'):
254 case VA_FOURCC('R','G','B','X'):
255 img->num_planes = 1;
256 img->pitches[0] = w * 4;
257 img->offsets[0] = 0;
258 img->data_size = w * h * 4;
259 break;
260
261 default:
262 /* VaDeriveImage is designed for contiguous planes. */
263 FREE(img);
264 return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
265 }
266
267 img_buf = CALLOC(1, sizeof(vlVaBuffer));
268 if (!img_buf) {
269 FREE(img);
270 return VA_STATUS_ERROR_ALLOCATION_FAILED;
271 }
272
273 mtx_lock(&drv->mutex);
274 img->image_id = handle_table_add(drv->htab, img);
275
276 img_buf->type = VAImageBufferType;
277 img_buf->size = img->data_size;
278 img_buf->num_elements = 1;
279
280 pipe_resource_reference(&img_buf->derived_surface.resource, surfaces[0]->texture);
281
282 img->buf = handle_table_add(VL_VA_DRIVER(ctx)->htab, img_buf);
283 mtx_unlock(&drv->mutex);
284
285 *image = *img;
286
287 return VA_STATUS_SUCCESS;
288 }
289
290 VAStatus
vlVaDestroyImage(VADriverContextP ctx,VAImageID image)291 vlVaDestroyImage(VADriverContextP ctx, VAImageID image)
292 {
293 vlVaDriver *drv;
294 VAImage *vaimage;
295 VAStatus status;
296
297 if (!ctx)
298 return VA_STATUS_ERROR_INVALID_CONTEXT;
299
300 drv = VL_VA_DRIVER(ctx);
301 mtx_lock(&drv->mutex);
302 vaimage = handle_table_get(drv->htab, image);
303 if (!vaimage) {
304 mtx_unlock(&drv->mutex);
305 return VA_STATUS_ERROR_INVALID_IMAGE;
306 }
307
308 handle_table_remove(VL_VA_DRIVER(ctx)->htab, image);
309 mtx_unlock(&drv->mutex);
310 status = vlVaDestroyBuffer(ctx, vaimage->buf);
311 FREE(vaimage);
312 return status;
313 }
314
315 VAStatus
vlVaSetImagePalette(VADriverContextP ctx,VAImageID image,unsigned char * palette)316 vlVaSetImagePalette(VADriverContextP ctx, VAImageID image, unsigned char *palette)
317 {
318 if (!ctx)
319 return VA_STATUS_ERROR_INVALID_CONTEXT;
320
321 return VA_STATUS_ERROR_UNIMPLEMENTED;
322 }
323
324 VAStatus
vlVaGetImage(VADriverContextP ctx,VASurfaceID surface,int x,int y,unsigned int width,unsigned int height,VAImageID image)325 vlVaGetImage(VADriverContextP ctx, VASurfaceID surface, int x, int y,
326 unsigned int width, unsigned int height, VAImageID image)
327 {
328 vlVaDriver *drv;
329 vlVaSurface *surf;
330 vlVaBuffer *img_buf;
331 VAImage *vaimage;
332 struct pipe_sampler_view **views;
333 enum pipe_format format;
334 bool convert = false;
335 void *data[3];
336 unsigned pitches[3], i, j;
337
338 if (!ctx)
339 return VA_STATUS_ERROR_INVALID_CONTEXT;
340
341 drv = VL_VA_DRIVER(ctx);
342
343 mtx_lock(&drv->mutex);
344 surf = handle_table_get(drv->htab, surface);
345 if (!surf || !surf->buffer) {
346 mtx_unlock(&drv->mutex);
347 return VA_STATUS_ERROR_INVALID_SURFACE;
348 }
349
350 vaimage = handle_table_get(drv->htab, image);
351 if (!vaimage) {
352 mtx_unlock(&drv->mutex);
353 return VA_STATUS_ERROR_INVALID_IMAGE;
354 }
355
356 img_buf = handle_table_get(drv->htab, vaimage->buf);
357 if (!img_buf) {
358 mtx_unlock(&drv->mutex);
359 return VA_STATUS_ERROR_INVALID_BUFFER;
360 }
361
362 format = VaFourccToPipeFormat(vaimage->format.fourcc);
363 if (format == PIPE_FORMAT_NONE) {
364 mtx_unlock(&drv->mutex);
365 return VA_STATUS_ERROR_OPERATION_FAILED;
366 }
367
368 if (format != surf->buffer->buffer_format) {
369 /* support NV12 to YV12 and IYUV conversion now only */
370 if ((format == PIPE_FORMAT_YV12 &&
371 surf->buffer->buffer_format == PIPE_FORMAT_NV12) ||
372 (format == PIPE_FORMAT_IYUV &&
373 surf->buffer->buffer_format == PIPE_FORMAT_NV12))
374 convert = true;
375 else {
376 mtx_unlock(&drv->mutex);
377 return VA_STATUS_ERROR_OPERATION_FAILED;
378 }
379 }
380
381 views = surf->buffer->get_sampler_view_planes(surf->buffer);
382 if (!views) {
383 mtx_unlock(&drv->mutex);
384 return VA_STATUS_ERROR_OPERATION_FAILED;
385 }
386
387 for (i = 0; i < vaimage->num_planes; i++) {
388 data[i] = img_buf->data + vaimage->offsets[i];
389 pitches[i] = vaimage->pitches[i];
390 }
391 if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
392 void *tmp_d;
393 unsigned tmp_p;
394 tmp_d = data[1];
395 data[1] = data[2];
396 data[2] = tmp_d;
397 tmp_p = pitches[1];
398 pitches[1] = pitches[2];
399 pitches[2] = tmp_p;
400 }
401
402 for (i = 0; i < vaimage->num_planes; i++) {
403 unsigned width, height;
404 if (!views[i]) continue;
405 vlVaVideoSurfaceSize(surf, i, &width, &height);
406 for (j = 0; j < views[i]->texture->array_size; ++j) {
407 struct pipe_box box = {0, 0, j, width, height, 1};
408 struct pipe_transfer *transfer;
409 uint8_t *map;
410 map = drv->pipe->transfer_map(drv->pipe, views[i]->texture, 0,
411 PIPE_TRANSFER_READ, &box, &transfer);
412 if (!map) {
413 mtx_unlock(&drv->mutex);
414 return VA_STATUS_ERROR_OPERATION_FAILED;
415 }
416
417 if (i == 1 && convert) {
418 u_copy_nv12_to_yv12(data, pitches, i, j,
419 transfer->stride, views[i]->texture->array_size,
420 map, box.width, box.height);
421 } else {
422 util_copy_rect(data[i] + pitches[i] * j,
423 views[i]->texture->format,
424 pitches[i] * views[i]->texture->array_size, 0, 0,
425 box.width, box.height, map, transfer->stride, 0, 0);
426 }
427 pipe_transfer_unmap(drv->pipe, transfer);
428 }
429 }
430 mtx_unlock(&drv->mutex);
431
432 return VA_STATUS_SUCCESS;
433 }
434
435 VAStatus
vlVaPutImage(VADriverContextP ctx,VASurfaceID surface,VAImageID image,int src_x,int src_y,unsigned int src_width,unsigned int src_height,int dest_x,int dest_y,unsigned int dest_width,unsigned int dest_height)436 vlVaPutImage(VADriverContextP ctx, VASurfaceID surface, VAImageID image,
437 int src_x, int src_y, unsigned int src_width, unsigned int src_height,
438 int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height)
439 {
440 vlVaDriver *drv;
441 vlVaSurface *surf;
442 vlVaBuffer *img_buf;
443 VAImage *vaimage;
444 struct pipe_sampler_view **views;
445 enum pipe_format format;
446 void *data[3];
447 unsigned pitches[3], i, j;
448
449 if (!ctx)
450 return VA_STATUS_ERROR_INVALID_CONTEXT;
451
452 drv = VL_VA_DRIVER(ctx);
453 mtx_lock(&drv->mutex);
454
455 surf = handle_table_get(drv->htab, surface);
456 if (!surf || !surf->buffer) {
457 mtx_unlock(&drv->mutex);
458 return VA_STATUS_ERROR_INVALID_SURFACE;
459 }
460
461 vaimage = handle_table_get(drv->htab, image);
462 if (!vaimage) {
463 mtx_unlock(&drv->mutex);
464 return VA_STATUS_ERROR_INVALID_IMAGE;
465 }
466
467 img_buf = handle_table_get(drv->htab, vaimage->buf);
468 if (!img_buf) {
469 mtx_unlock(&drv->mutex);
470 return VA_STATUS_ERROR_INVALID_BUFFER;
471 }
472
473 if (img_buf->derived_surface.resource) {
474 /* Attempting to transfer derived image to surface */
475 mtx_unlock(&drv->mutex);
476 return VA_STATUS_ERROR_UNIMPLEMENTED;
477 }
478
479 format = VaFourccToPipeFormat(vaimage->format.fourcc);
480
481 if (format == PIPE_FORMAT_NONE) {
482 mtx_unlock(&drv->mutex);
483 return VA_STATUS_ERROR_OPERATION_FAILED;
484 }
485
486 if ((format != surf->buffer->buffer_format) &&
487 ((format != PIPE_FORMAT_YV12) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12)) &&
488 ((format != PIPE_FORMAT_IYUV) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12))) {
489 struct pipe_video_buffer *tmp_buf;
490
491 surf->templat.buffer_format = format;
492 if (format == PIPE_FORMAT_YUYV || format == PIPE_FORMAT_UYVY ||
493 format == PIPE_FORMAT_B8G8R8A8_UNORM || format == PIPE_FORMAT_B8G8R8X8_UNORM ||
494 format == PIPE_FORMAT_R8G8B8A8_UNORM || format == PIPE_FORMAT_R8G8B8X8_UNORM)
495 surf->templat.interlaced = false;
496 tmp_buf = drv->pipe->create_video_buffer(drv->pipe, &surf->templat);
497
498 if (!tmp_buf) {
499 mtx_unlock(&drv->mutex);
500 return VA_STATUS_ERROR_ALLOCATION_FAILED;
501 }
502
503 surf->buffer->destroy(surf->buffer);
504 surf->buffer = tmp_buf;
505 }
506
507 views = surf->buffer->get_sampler_view_planes(surf->buffer);
508 if (!views) {
509 mtx_unlock(&drv->mutex);
510 return VA_STATUS_ERROR_OPERATION_FAILED;
511 }
512
513 for (i = 0; i < vaimage->num_planes; i++) {
514 data[i] = img_buf->data + vaimage->offsets[i];
515 pitches[i] = vaimage->pitches[i];
516 }
517 if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
518 void *tmp_d;
519 unsigned tmp_p;
520 tmp_d = data[1];
521 data[1] = data[2];
522 data[2] = tmp_d;
523 tmp_p = pitches[1];
524 pitches[1] = pitches[2];
525 pitches[2] = tmp_p;
526 }
527
528 for (i = 0; i < vaimage->num_planes; ++i) {
529 unsigned width, height;
530 struct pipe_resource *tex;
531
532 if (!views[i]) continue;
533 tex = views[i]->texture;
534
535 vlVaVideoSurfaceSize(surf, i, &width, &height);
536 for (j = 0; j < tex->array_size; ++j) {
537 struct pipe_box dst_box = {0, 0, j, width, height, 1};
538
539 if (((format == PIPE_FORMAT_YV12) || (format == PIPE_FORMAT_IYUV))
540 && (surf->buffer->buffer_format == PIPE_FORMAT_NV12)
541 && i == 1) {
542 struct pipe_transfer *transfer = NULL;
543 uint8_t *map = NULL;
544
545 map = drv->pipe->transfer_map(drv->pipe,
546 tex,
547 0,
548 PIPE_TRANSFER_WRITE |
549 PIPE_TRANSFER_DISCARD_RANGE,
550 &dst_box, &transfer);
551 if (map == NULL) {
552 mtx_unlock(&drv->mutex);
553 return VA_STATUS_ERROR_OPERATION_FAILED;
554 }
555
556 u_copy_nv12_from_yv12((const void * const*) data, pitches, i, j,
557 transfer->stride, tex->array_size,
558 map, dst_box.width, dst_box.height);
559 pipe_transfer_unmap(drv->pipe, transfer);
560 } else {
561 drv->pipe->texture_subdata(drv->pipe, tex, 0,
562 PIPE_TRANSFER_WRITE, &dst_box,
563 data[i] + pitches[i] * j,
564 pitches[i] * views[i]->texture->array_size, 0);
565 }
566 }
567 }
568 mtx_unlock(&drv->mutex);
569
570 return VA_STATUS_SUCCESS;
571 }
572