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