st/vdpau: add device reference counting
[mesa.git] / src / gallium / state_trackers / vdpau / surface.c
1 /**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen.
4 * Copyright 2011 Christian König.
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 VMWARE AND/OR ITS SUPPLIERS 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 <assert.h>
30
31 #include "pipe/p_state.h"
32
33 #include "util/u_memory.h"
34 #include "util/u_debug.h"
35 #include "util/u_rect.h"
36 #include "util/u_surface.h"
37 #include "vl/vl_defines.h"
38
39 #include "vdpau_private.h"
40
41 enum getbits_conversion {
42 CONVERSION_NONE,
43 CONVERSION_NV12_TO_YV12,
44 CONVERSION_YV12_TO_NV12,
45 CONVERSION_SWAP_YUYV_UYVY,
46 };
47
48 /**
49 * Create a VdpVideoSurface.
50 */
51 VdpStatus
52 vlVdpVideoSurfaceCreate(VdpDevice device, VdpChromaType chroma_type,
53 uint32_t width, uint32_t height,
54 VdpVideoSurface *surface)
55 {
56 struct pipe_context *pipe;
57 vlVdpSurface *p_surf;
58 VdpStatus ret;
59
60 if (!(width && height)) {
61 ret = VDP_STATUS_INVALID_SIZE;
62 goto inv_size;
63 }
64
65 p_surf = CALLOC(1, sizeof(vlVdpSurface));
66 if (!p_surf) {
67 ret = VDP_STATUS_RESOURCES;
68 goto no_res;
69 }
70
71 vlVdpDevice *dev = vlGetDataHTAB(device);
72 if (!dev) {
73 ret = VDP_STATUS_INVALID_HANDLE;
74 goto inv_device;
75 }
76
77 DeviceReference(&p_surf->device, dev);
78 pipe = dev->context;
79
80 pipe_mutex_lock(dev->mutex);
81 memset(&p_surf->templat, 0, sizeof(p_surf->templat));
82 p_surf->templat.buffer_format = pipe->screen->get_video_param
83 (
84 pipe->screen,
85 PIPE_VIDEO_PROFILE_UNKNOWN,
86 PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
87 PIPE_VIDEO_CAP_PREFERED_FORMAT
88 );
89 p_surf->templat.chroma_format = ChromaToPipe(chroma_type);
90 p_surf->templat.width = width;
91 p_surf->templat.height = height;
92 p_surf->templat.interlaced = pipe->screen->get_video_param
93 (
94 pipe->screen,
95 PIPE_VIDEO_PROFILE_UNKNOWN,
96 PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
97 PIPE_VIDEO_CAP_PREFERS_INTERLACED
98 );
99 if (p_surf->templat.buffer_format != PIPE_FORMAT_NONE)
100 p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
101
102 /* do not mandate early allocation of a video buffer */
103 vlVdpVideoSurfaceClear(p_surf);
104 pipe_mutex_unlock(dev->mutex);
105
106 *surface = vlAddDataHTAB(p_surf);
107 if (*surface == 0) {
108 ret = VDP_STATUS_ERROR;
109 goto no_handle;
110 }
111
112 return VDP_STATUS_OK;
113
114 no_handle:
115 p_surf->video_buffer->destroy(p_surf->video_buffer);
116
117 inv_device:
118 DeviceReference(&p_surf->device, NULL);
119 FREE(p_surf);
120
121 no_res:
122 inv_size:
123 return ret;
124 }
125
126 /**
127 * Destroy a VdpVideoSurface.
128 */
129 VdpStatus
130 vlVdpVideoSurfaceDestroy(VdpVideoSurface surface)
131 {
132 vlVdpSurface *p_surf;
133
134 p_surf = (vlVdpSurface *)vlGetDataHTAB((vlHandle)surface);
135 if (!p_surf)
136 return VDP_STATUS_INVALID_HANDLE;
137
138 pipe_mutex_lock(p_surf->device->mutex);
139 if (p_surf->video_buffer)
140 p_surf->video_buffer->destroy(p_surf->video_buffer);
141 pipe_mutex_unlock(p_surf->device->mutex);
142
143 vlRemoveDataHTAB(surface);
144 DeviceReference(&p_surf->device, NULL);
145 FREE(p_surf);
146
147 return VDP_STATUS_OK;
148 }
149
150 /**
151 * Retrieve the parameters used to create a VdpVideoSurface.
152 */
153 VdpStatus
154 vlVdpVideoSurfaceGetParameters(VdpVideoSurface surface,
155 VdpChromaType *chroma_type,
156 uint32_t *width, uint32_t *height)
157 {
158 if (!(width && height && chroma_type))
159 return VDP_STATUS_INVALID_POINTER;
160
161 vlVdpSurface *p_surf = vlGetDataHTAB(surface);
162 if (!p_surf)
163 return VDP_STATUS_INVALID_HANDLE;
164
165 if (p_surf->video_buffer) {
166 *width = p_surf->video_buffer->width;
167 *height = p_surf->video_buffer->height;
168 *chroma_type = PipeToChroma(p_surf->video_buffer->chroma_format);
169 } else {
170 *width = p_surf->templat.width;
171 *height = p_surf->templat.height;
172 *chroma_type = PipeToChroma(p_surf->templat.chroma_format);
173 }
174
175 return VDP_STATUS_OK;
176 }
177
178 static void
179 vlVdpVideoSurfaceSize(vlVdpSurface *p_surf, int component,
180 unsigned *width, unsigned *height)
181 {
182 *width = p_surf->templat.width;
183 *height = p_surf->templat.height;
184
185 if (component > 0) {
186 if (p_surf->templat.chroma_format == PIPE_VIDEO_CHROMA_FORMAT_420) {
187 *width /= 2;
188 *height /= 2;
189 } else if (p_surf->templat.chroma_format == PIPE_VIDEO_CHROMA_FORMAT_422) {
190 *width /= 2;
191 }
192 }
193 if (p_surf->templat.interlaced)
194 *height /= 2;
195 }
196
197 static void
198 vlVdpCopyNV12ToYV12(void *const *destination_data,
199 uint32_t const *destination_pitches,
200 int src_plane, int src_field,
201 int src_stride, int num_fields,
202 uint8_t const *src,
203 int width, int height)
204 {
205 int x, y;
206 unsigned u_stride = destination_pitches[2] * num_fields;
207 unsigned v_stride = destination_pitches[1] * num_fields;
208 uint8_t *u_dst = (uint8_t *)destination_data[2] + destination_pitches[2] * src_field;
209 uint8_t *v_dst = (uint8_t *)destination_data[1] + destination_pitches[1] * src_field;
210
211 /* TODO: SIMD */
212 for (y = 0; y < height; y++) {
213 for (x = 0; x < width; x++) {
214 u_dst[x] = src[2*x];
215 v_dst[x] = src[2*x+1];
216 }
217 u_dst += u_stride;
218 v_dst += v_stride;
219 src += src_stride;
220 }
221 }
222
223 static void
224 vlVdpCopyYV12ToNV12(void *const *destination_data,
225 uint32_t const *destination_pitches,
226 int src_plane, int src_field,
227 int src_stride, int num_fields,
228 uint8_t const *src,
229 int width, int height)
230 {
231 int x, y;
232 unsigned offset = 2 - src_plane;
233 unsigned stride = destination_pitches[1] * num_fields;
234 uint8_t *dst = (uint8_t *)destination_data[1] + destination_pitches[1] * src_field;
235
236 /* TODO: SIMD */
237 for (y = 0; y < height; y++) {
238 for (x = 0; x < 2 * width; x += 2) {
239 dst[x+offset] = src[x>>1];
240 }
241 dst += stride;
242 src += src_stride;
243 }
244 }
245
246 static void
247 vlVdpCopySwap422Packed(void *const *destination_data,
248 uint32_t const *destination_pitches,
249 int src_plane, int src_field,
250 int src_stride, int num_fields,
251 uint8_t const *src,
252 int width, int height)
253 {
254 int x, y;
255 unsigned stride = destination_pitches[0] * num_fields;
256 uint8_t *dst = (uint8_t *)destination_data[0] + destination_pitches[0] * src_field;
257
258 /* TODO: SIMD */
259 for (y = 0; y < height; y++) {
260 for (x = 0; x < 4 * width; x += 4) {
261 dst[x+0] = src[x+1];
262 dst[x+1] = src[x+0];
263 dst[x+2] = src[x+3];
264 dst[x+3] = src[x+2];
265 }
266 dst += stride;
267 src += src_stride;
268 }
269 }
270
271 /**
272 * Copy image data from a VdpVideoSurface to application memory in a specified
273 * YCbCr format.
274 */
275 VdpStatus
276 vlVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface,
277 VdpYCbCrFormat destination_ycbcr_format,
278 void *const *destination_data,
279 uint32_t const *destination_pitches)
280 {
281 vlVdpSurface *vlsurface;
282 struct pipe_context *pipe;
283 enum pipe_format format, buffer_format;
284 struct pipe_sampler_view **sampler_views;
285 enum getbits_conversion conversion = CONVERSION_NONE;
286 unsigned i, j;
287
288 vlsurface = vlGetDataHTAB(surface);
289 if (!vlsurface)
290 return VDP_STATUS_INVALID_HANDLE;
291
292 pipe = vlsurface->device->context;
293 if (!pipe)
294 return VDP_STATUS_INVALID_HANDLE;
295
296 format = FormatYCBCRToPipe(destination_ycbcr_format);
297 if (format == PIPE_FORMAT_NONE)
298 return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;
299
300 if (vlsurface->video_buffer == NULL)
301 return VDP_STATUS_INVALID_VALUE;
302
303 buffer_format = vlsurface->video_buffer->buffer_format;
304 if (format != buffer_format) {
305 if (format == PIPE_FORMAT_YV12 && buffer_format == PIPE_FORMAT_NV12)
306 conversion = CONVERSION_NV12_TO_YV12;
307 else if (format == PIPE_FORMAT_NV12 && buffer_format == PIPE_FORMAT_YV12)
308 conversion = CONVERSION_YV12_TO_NV12;
309 else if ((format == PIPE_FORMAT_YUYV && buffer_format == PIPE_FORMAT_UYVY) ||
310 (format == PIPE_FORMAT_UYVY && buffer_format == PIPE_FORMAT_YUYV))
311 conversion = CONVERSION_SWAP_YUYV_UYVY;
312 else
313 return VDP_STATUS_NO_IMPLEMENTATION;
314 }
315
316 pipe_mutex_lock(vlsurface->device->mutex);
317 sampler_views = vlsurface->video_buffer->get_sampler_view_planes(vlsurface->video_buffer);
318 if (!sampler_views) {
319 pipe_mutex_unlock(vlsurface->device->mutex);
320 return VDP_STATUS_RESOURCES;
321 }
322
323 for (i = 0; i < 3; ++i) {
324 unsigned width, height;
325 struct pipe_sampler_view *sv = sampler_views[i];
326 if (!sv) continue;
327
328 vlVdpVideoSurfaceSize(vlsurface, i, &width, &height);
329
330 for (j = 0; j < sv->texture->array_size; ++j) {
331 struct pipe_box box = {
332 0, 0, j,
333 width, height, 1
334 };
335 struct pipe_transfer *transfer;
336 uint8_t *map;
337
338 map = pipe->transfer_map(pipe, sv->texture, 0,
339 PIPE_TRANSFER_READ, &box, &transfer);
340 if (!map) {
341 pipe_mutex_unlock(vlsurface->device->mutex);
342 return VDP_STATUS_RESOURCES;
343 }
344
345 if (conversion == CONVERSION_NV12_TO_YV12 && i == 1) {
346 vlVdpCopyNV12ToYV12(destination_data, destination_pitches,
347 i, j, transfer->stride, sv->texture->array_size,
348 map, box.width, box.height);
349 } else if (conversion == CONVERSION_YV12_TO_NV12 && i > 0) {
350 vlVdpCopyYV12ToNV12(destination_data, destination_pitches,
351 i, j, transfer->stride, sv->texture->array_size,
352 map, box.width, box.height);
353 } else if (conversion == CONVERSION_SWAP_YUYV_UYVY) {
354 vlVdpCopySwap422Packed(destination_data, destination_pitches,
355 i, j, transfer->stride, sv->texture->array_size,
356 map, box.width, box.height);
357 } else {
358 util_copy_rect(destination_data[i] + destination_pitches[i] * j, sv->texture->format,
359 destination_pitches[i] * sv->texture->array_size, 0, 0,
360 box.width, box.height, map, transfer->stride, 0, 0);
361 }
362
363 pipe_transfer_unmap(pipe, transfer);
364 }
365 }
366 pipe_mutex_unlock(vlsurface->device->mutex);
367
368 return VDP_STATUS_OK;
369 }
370
371 /**
372 * Copy image data from application memory in a specific YCbCr format to
373 * a VdpVideoSurface.
374 */
375 VdpStatus
376 vlVdpVideoSurfacePutBitsYCbCr(VdpVideoSurface surface,
377 VdpYCbCrFormat source_ycbcr_format,
378 void const *const *source_data,
379 uint32_t const *source_pitches)
380 {
381 enum pipe_format pformat = FormatYCBCRToPipe(source_ycbcr_format);
382 struct pipe_context *pipe;
383 struct pipe_sampler_view **sampler_views;
384 unsigned i, j;
385
386 vlVdpSurface *p_surf = vlGetDataHTAB(surface);
387 if (!p_surf)
388 return VDP_STATUS_INVALID_HANDLE;
389
390 pipe = p_surf->device->context;
391 if (!pipe)
392 return VDP_STATUS_INVALID_HANDLE;
393
394 pipe_mutex_lock(p_surf->device->mutex);
395 if (p_surf->video_buffer == NULL || pformat != p_surf->video_buffer->buffer_format) {
396
397 /* destroy the old one */
398 if (p_surf->video_buffer)
399 p_surf->video_buffer->destroy(p_surf->video_buffer);
400
401 /* adjust the template parameters */
402 p_surf->templat.buffer_format = pformat;
403
404 /* and try to create the video buffer with the new format */
405 p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
406
407 /* stil no luck? ok forget it we don't support it */
408 if (!p_surf->video_buffer) {
409 pipe_mutex_unlock(p_surf->device->mutex);
410 return VDP_STATUS_NO_IMPLEMENTATION;
411 }
412 vlVdpVideoSurfaceClear(p_surf);
413 }
414
415 sampler_views = p_surf->video_buffer->get_sampler_view_planes(p_surf->video_buffer);
416 if (!sampler_views) {
417 pipe_mutex_unlock(p_surf->device->mutex);
418 return VDP_STATUS_RESOURCES;
419 }
420
421 for (i = 0; i < 3; ++i) {
422 unsigned width, height;
423 struct pipe_sampler_view *sv = sampler_views[i];
424 if (!sv || !source_pitches[i]) continue;
425
426 vlVdpVideoSurfaceSize(p_surf, i, &width, &height);
427
428 for (j = 0; j < sv->texture->array_size; ++j) {
429 struct pipe_box dst_box = {
430 0, 0, j,
431 width, height, 1
432 };
433
434 pipe->transfer_inline_write(pipe, sv->texture, 0,
435 PIPE_TRANSFER_WRITE, &dst_box,
436 source_data[i] + source_pitches[i] * j,
437 source_pitches[i] * sv->texture->array_size,
438 0);
439 }
440 }
441 pipe_mutex_unlock(p_surf->device->mutex);
442
443 return VDP_STATUS_OK;
444 }
445
446 /**
447 * Helper function to initially clear the VideoSurface after (re-)creation
448 */
449 void
450 vlVdpVideoSurfaceClear(vlVdpSurface *vlsurf)
451 {
452 struct pipe_context *pipe = vlsurf->device->context;
453 struct pipe_surface **surfaces;
454 unsigned i;
455
456 if (!vlsurf->video_buffer)
457 return;
458
459 surfaces = vlsurf->video_buffer->get_surfaces(vlsurf->video_buffer);
460 for (i = 0; i < VL_MAX_SURFACES; ++i) {
461 union pipe_color_union c = {};
462
463 if (!surfaces[i])
464 continue;
465
466 if (i > !!vlsurf->templat.interlaced)
467 c.f[0] = c.f[1] = c.f[2] = c.f[3] = 0.5f;
468
469 pipe->clear_render_target(pipe, surfaces[i], &c, 0, 0,
470 surfaces[i]->width, surfaces[i]->height);
471 }
472 pipe->flush(pipe, NULL, 0);
473 }
474
475 /**
476 * Interop to mesa state tracker
477 */
478 struct pipe_video_buffer *vlVdpVideoSurfaceGallium(VdpVideoSurface surface)
479 {
480 vlVdpSurface *p_surf = vlGetDataHTAB(surface);
481 if (!p_surf)
482 return NULL;
483
484 pipe_mutex_lock(p_surf->device->mutex);
485 if (p_surf->video_buffer == NULL) {
486 struct pipe_context *pipe = p_surf->device->context;
487
488 /* try to create a video buffer if we don't already have one */
489 p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
490 }
491 pipe_mutex_unlock(p_surf->device->mutex);
492
493 return p_surf->video_buffer;
494 }