diff options
author | Dmitry Osipenko <digetx@gmail.com> | 2017-12-25 04:30:21 +0300 |
---|---|---|
committer | Dmitry Osipenko <digetx@gmail.com> | 2017-12-26 22:14:28 +0300 |
commit | ce89b4943ac051ce5036930c11995d6f213368e9 (patch) | |
tree | dff1f46b03e5ff5e1939ae71af46671f7fe6b889 | |
parent | ed61456b0c468c12b63fbceecd519e99d2de9db8 (diff) |
Implement zero-copy output
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/decoder.c | 2 | ||||
-rw-r--r-- | src/presentation_queue.c | 43 | ||||
-rw-r--r-- | src/surface.c | 120 | ||||
-rw-r--r-- | src/surface_mixer.c | 145 | ||||
-rw-r--r-- | src/surface_output.c | 2 | ||||
-rw-r--r-- | src/surface_shared.c | 253 | ||||
-rw-r--r-- | src/surface_video.c | 32 | ||||
-rw-r--r-- | src/vdpau_tegra.c | 70 | ||||
-rw-r--r-- | src/vdpau_tegra.h | 61 |
10 files changed, 590 insertions, 139 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 4b86694..499313c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ libvdpau_tegra_la_SOURCES = vdpau_tegra.c \ surface_bitmap.c \ surface_video.c \ surface_mixer.c \ + surface_shared.c \ surface.c \ presentation_queue.c \ decoder.c \ diff --git a/src/decoder.c b/src/decoder.c index 63c0dbe..8b043df 100644 --- a/src/decoder.c +++ b/src/decoder.c @@ -569,6 +569,8 @@ VdpStatus vdp_decoder_render(VdpDecoder decoder, return ret; } + surf = shared_surface_swap_video(surf); + ret = tegra_decode_h264(dec, surf, picture_info, bitstream_data_fd, &bitstream_reader); diff --git a/src/presentation_queue.c b/src/presentation_queue.c index 7181e1a..9175594 100644 --- a/src/presentation_queue.c +++ b/src/presentation_queue.c @@ -175,15 +175,37 @@ static void * presentation_queue_thr(void *opaque) goto del_surface; } - XvPutImage(dev->display, dev->xv_port, - pqt->drawable, pqt->gc, - surf->xv_img, - 0, 0, - surf->disp_width, - surf->disp_height, - 0, 0, - surf->disp_width, - surf->disp_height); + if (surf->set_bg && surf->bg_color != pqt->bg_color) { + XSetWindowBackground(dev->display, pqt->drawable, + surf->bg_color); + XClearWindow(dev->display, pqt->drawable); + + pqt->bg_color = surf->bg_color; + } + + if (surf->shared) { + XvPutImage(dev->display, dev->xv_port, + pqt->drawable, pqt->gc, + surf->shared->xv_img, + surf->shared->src_x0, + surf->shared->src_y0, + surf->shared->src_width, + surf->shared->src_height, + surf->shared->dst_x0, + surf->shared->dst_y0, + surf->shared->dst_width, + surf->shared->dst_height); + } else { + XvPutImage(dev->display, dev->xv_port, + pqt->drawable, pqt->gc, + surf->xv_img, + 0, 0, + surf->disp_width, + surf->disp_height, + 0, 0, + surf->disp_width, + surf->disp_height); + } XSync(dev->display, 0); @@ -580,6 +602,9 @@ VdpStatus vdp_presentation_queue_target_create_x11( pqt->drawable = drawable; pqt->gc = XCreateGC(dev->display, drawable, 0, &values); + XSetWindowBackground(dev->display, drawable, pqt->bg_color); + XClearWindow(dev->display, drawable); + *target = i; return VDP_STATUS_OK; diff --git a/src/surface.c b/src/surface.c index bbd749d..11d8a58 100644 --- a/src/surface.c +++ b/src/surface.c @@ -36,40 +36,23 @@ static uint32_t get_unused_surface_id(void) return id; } -uint32_t create_surface(tegra_device *dev, - uint32_t width, - uint32_t height, - VdpRGBAFormat rgba_format, - int output, - int video) +tegra_surface *alloc_surface(tegra_device *dev, + uint32_t width, uint32_t height, + VdpRGBAFormat rgba_format, + int output, int video) { tegra_surface *surf = calloc(1, sizeof(tegra_surface)); pixman_image_t *pix = NULL; XvImage *xv_img = NULL; struct tegra_vde_h264_frame *frame = NULL; struct host1x_pixelbuffer *pixbuf = NULL; - uint32_t surface_id = VDP_INVALID_HANDLE; uint32_t stride = width * 4; uint32_t *bo_flinks = NULL; uint32_t *pitches = NULL; int ret; - if (surf == NULL) { - return VDP_INVALID_HANDLE; - } - - pthread_mutex_lock(&global_lock); - - surface_id = get_unused_surface_id(); - - if (surface_id != VDP_INVALID_HANDLE) { - set_surface(surface_id, surf); - } - - pthread_mutex_unlock(&global_lock); - - if (surface_id == VDP_INVALID_HANDLE) { - goto err_cleanup; + if (!surf) { + return NULL; } if (!video){ @@ -97,6 +80,7 @@ uint32_t create_surface(tegra_device *dev, pixbuf_fmt, PIX_BUF_LAYOUT_LINEAR); if (pixbuf == NULL) { + ret = -ENOMEM; goto err_cleanup; } @@ -116,6 +100,7 @@ uint32_t create_surface(tegra_device *dev, data, stride); if (pix == NULL) { + ret = -ENOMEM; goto err_cleanup; } } @@ -124,6 +109,7 @@ uint32_t create_surface(tegra_device *dev, frame = calloc(1, sizeof(struct tegra_vde_h264_frame)); if (frame == NULL) { + ret = -ENOMEM; goto err_cleanup; } @@ -139,6 +125,7 @@ uint32_t create_surface(tegra_device *dev, PIX_BUF_FMT_YV12, PIX_BUF_LAYOUT_LINEAR); if (pixbuf == NULL) { + ret = -ENOMEM; goto err_cleanup; } @@ -250,6 +237,7 @@ uint32_t create_surface(tegra_device *dev, format_id, NULL, width, height); if (xv_img == NULL) { ErrorMsg("XvCreateImage failed\n"); + ret = -ENOMEM; goto err_cleanup; } @@ -258,12 +246,14 @@ uint32_t create_surface(tegra_device *dev, xv_img->data = calloc(1, xv_img->data_size); if (xv_img->data == NULL) { + ret = -ENOMEM; goto err_cleanup; } bo_flinks = (uint32_t *) xv_img->data; - if (drm_tegra_bo_get_name(pixbuf->bo, &bo_flinks[0]) != 0) { + ret = drm_tegra_bo_get_name(pixbuf->bo, &bo_flinks[0]); + if (ret != 0) { ErrorMsg("drm_tegra_bo_get_name failed\n"); goto err_cleanup; } @@ -291,16 +281,17 @@ uint32_t create_surface(tegra_device *dev, surf->xv_img = xv_img; surf->frame = frame; surf->flags = video ? SURFACE_VIDEO : 0; + surf->flags |= output ? SURFACE_OUTPUT : 0; surf->pixbuf = pixbuf; surf->dev = dev; + surf->width = width; + surf->height = height; ref_device(dev); - return surface_id; + return surf; err_cleanup: - set_surface(surface_id, NULL); - if (xv_img != NULL) { free(xv_img->data); XFree(xv_img); @@ -325,7 +316,42 @@ err_cleanup: free(frame); free(surf); - return VDP_INVALID_HANDLE; + return NULL; +} + +uint32_t create_surface(tegra_device *dev, + uint32_t width, + uint32_t height, + VdpRGBAFormat rgba_format, + int output, + int video) +{ + tegra_surface *surf; + uint32_t surface_id; + + surf = alloc_surface(dev, width, height, rgba_format, output, video); + + if (surf == NULL) { + return VDP_INVALID_HANDLE; + } + + pthread_mutex_lock(&global_lock); + + surface_id = get_unused_surface_id(); + + if (surface_id != VDP_INVALID_HANDLE) { + set_surface(surface_id, surf); + } + + pthread_mutex_unlock(&global_lock); + + if (surface_id != VDP_INVALID_HANDLE) { + surf->surface_id = surface_id; + } else { + destroy_surface(surf); + } + + return surface_id; } void ref_surface(tegra_surface *surf) @@ -339,6 +365,26 @@ VdpStatus unref_surface(tegra_surface *surf) return VDP_STATUS_OK; } + if (surf->flags & SURFACE_OUTPUT) { + shared_surface_kill_disp(surf); + } + + if (surf->pixbuf != NULL) { + host1x_pixelbuffer_free(surf->pixbuf); + surf->pixbuf = NULL; + } + + if (surf->pix != NULL) { + pixman_image_unref(surf->pix); + surf->pix = NULL; + } + + if (surf->xv_img != NULL) { + free(surf->xv_img->data); + XFree(surf->xv_img); + surf->xv_img = NULL; + } + if (surf->frame != NULL) { drm_tegra_bo_unref(surf->aux_bo); close(surf->frame->y_fd); @@ -358,25 +404,7 @@ VdpStatus unref_surface(tegra_surface *surf) VdpStatus destroy_surface(tegra_surface *surf) { pthread_mutex_lock(&surf->lock); - surf->earliest_presentation_time = 0; - - if (surf->pixbuf != NULL) { - host1x_pixelbuffer_free(surf->pixbuf); - surf->pixbuf = NULL; - } - - if (surf->pix != NULL) { - pixman_image_unref(surf->pix); - surf->pix = NULL; - } - - if (surf->xv_img != NULL) { - free(surf->xv_img->data); - XFree(surf->xv_img); - surf->xv_img = NULL; - } - pthread_mutex_unlock(&surf->lock); return unref_surface(surf); diff --git a/src/surface_mixer.c b/src/surface_mixer.c index 6e96bf0..9c0cd96 100644 --- a/src/surface_mixer.c +++ b/src/surface_mixer.c @@ -204,6 +204,9 @@ VdpStatus vdp_video_mixer_create(VdpDevice device, return VDP_STATUS_RESOURCES; } + ref_device(dev); + mix->dev = dev; + *mixer = i; return VDP_STATUS_OK; @@ -328,6 +331,7 @@ VdpStatus vdp_video_mixer_destroy(VdpVideoMixer mixer) set_mixer(mixer, NULL); + unref_device(mix->dev); free(mix); return VDP_STATUS_OK; @@ -350,13 +354,14 @@ VdpStatus vdp_video_mixer_render( uint32_t layer_count, VdpLayer const *layers) { + tegra_surface *bg_surf = get_surface(background_surface); tegra_surface *dest_surf = get_surface(destination_surface); + tegra_surface *video_surf = get_surface(video_surface_current); tegra_mixer *mix = get_mixer(mixer); - tegra_device *dev; - uint32_t vid_width, vid_height; - uint32_t vid_x0, vid_y0; - uint32_t bg_width, bg_height; - uint32_t bg_x0, bg_y0; + tegra_shared_surface *shared = NULL; + uint32_t src_vid_width, src_vid_height, src_vid_x0, src_vid_y0; + uint32_t dst_vid_width, dst_vid_height, dst_vid_x0, dst_vid_y0; + uint32_t bg_width, bg_height, bg_x0, bg_y0; uint32_t bg_color; bool draw_background = false; @@ -364,21 +369,33 @@ VdpStatus vdp_video_mixer_render( return VDP_STATUS_INVALID_HANDLE; } - dev = dest_surf->dev; - assert(dest_surf->idle_hack || dest_surf->status == VDP_PRESENTATION_QUEUE_STATUS_IDLE); + shared_surface_kill_disp(dest_surf); + if (destination_video_rect != NULL) { - vid_width = destination_video_rect->x1 - destination_video_rect->x0; - vid_height = destination_video_rect->y1 - destination_video_rect->y0; - vid_x0 = destination_video_rect->x0; - vid_y0 = destination_video_rect->y0; + dst_vid_width = destination_video_rect->x1 - destination_video_rect->x0; + dst_vid_height = destination_video_rect->y1 - destination_video_rect->y0; + dst_vid_x0 = destination_video_rect->x0; + dst_vid_y0 = destination_video_rect->y0; } else { - vid_width = dest_surf->pixbuf->width; - vid_height = dest_surf->pixbuf->height; - vid_x0 = 0; - vid_y0 = 0; + dst_vid_width = video_surf->width; + dst_vid_height = video_surf->height; + dst_vid_x0 = 0; + dst_vid_y0 = 0; + } + + if (video_source_rect != NULL) { + src_vid_width = video_source_rect->x1 - video_source_rect->x0; + src_vid_height = video_source_rect->y1 - video_source_rect->y0; + src_vid_x0 = video_source_rect->x0; + src_vid_y0 = video_source_rect->y0; + } else { + src_vid_width = video_surf->width; + src_vid_height = video_surf->height; + src_vid_x0 = 0; + src_vid_y0 = 0; } if (background_source_rect != NULL) { @@ -387,25 +404,20 @@ VdpStatus vdp_video_mixer_render( bg_x0 = background_source_rect->x0; bg_y0 = background_source_rect->y0; } else { - bg_width = dest_surf->pixbuf->width; - bg_height = dest_surf->pixbuf->height; + bg_width = bg_surf ? bg_surf->width : dest_surf->width; + bg_height = bg_surf ? bg_surf->height : dest_surf->height; bg_x0 = 0; bg_y0 = 0; } - if (vid_height < bg_height) - draw_background = true; - - if (vid_width < bg_width) - draw_background = true; + draw_background = (dst_vid_y0 != bg_y0 || + dst_vid_x0 != bg_x0 || + dst_vid_height < bg_height || + dst_vid_width < bg_width); - if (vid_y0 != bg_y0) - draw_background = true; + dest_surf->set_bg = false; - if (vid_x0 != bg_x0) - draw_background = true; - - if (draw_background) { + if (draw_background && !bg_surf) { bg_color = (int)(mix->bg_color.alpha * 255) << 24; switch (dest_surf->pixbuf->format) { @@ -425,29 +437,64 @@ VdpStatus vdp_video_mixer_render( abort(); } - host1x_gr2d_clear_rect_clipped(dev->stream, - dest_surf->pixbuf, - bg_color, - bg_x0, bg_y0, - bg_width, bg_height, - vid_x0, vid_y0, - vid_x0 + vid_width, - vid_y0 + vid_height, - true); + if (background_source_rect) { + host1x_gr2d_clear_rect_clipped(mix->dev->stream, + dest_surf->pixbuf, + bg_color, + bg_x0, bg_y0, + bg_width, bg_height, + dst_vid_x0, dst_vid_y0, + dst_vid_x0 + dst_vid_width, + dst_vid_y0 + dst_vid_height, + true); + } else { + dest_surf->bg_color = bg_color; + dest_surf->set_bg = true; + draw_background = false; + } + } + + if (draw_background && bg_surf) { + host1x_gr2d_surface_blit(mix->dev->stream, + bg_surf->pixbuf, + dest_surf->pixbuf, + mix->csc_matrix, + bg_x0, bg_y0, + bg_width, bg_height, + 0, + 0, + dest_surf->width, + dest_surf->height); } - host1x_gr2d_surface_blit(dev->stream, - get_surface(video_surface_current)->pixbuf, - dest_surf->pixbuf, - mix->csc_matrix, - video_source_rect->x0, - video_source_rect->y0, - video_source_rect->x1 - video_source_rect->x0, - video_source_rect->y1 - video_source_rect->y0, - vid_x0, - vid_y0, - vid_width, - vid_height); + if (!draw_background) { + shared = create_shared_surface(dest_surf, + video_surf, + mix->csc_matrix, + src_vid_x0, + src_vid_y0, + src_vid_width, + src_vid_height, + dst_vid_x0, + dst_vid_y0, + dst_vid_width, + dst_vid_height); + } + + if (!shared) { + host1x_gr2d_surface_blit(mix->dev->stream, + video_surf->pixbuf, + dest_surf->pixbuf, + mix->csc_matrix, + src_vid_x0, + src_vid_y0, + src_vid_width, + src_vid_height, + dst_vid_x0, + dst_vid_y0, + dst_vid_width, + dst_vid_height); + } while (layer_count--) { if (layers[layer_count].struct_version != VDP_LAYER_VERSION) { diff --git a/src/surface_output.c b/src/surface_output.c index 8bd8927..1b65564 100644 --- a/src/surface_output.c +++ b/src/surface_output.c @@ -243,6 +243,8 @@ VdpStatus vdp_output_surface_render_bitmap_surface( dst_y0 = 0; } + shared_surface_transfer_video(dst_surf); + if (source_surface == VDP_INVALID_HANDLE) { ret = host1x_gr2d_clear_rect(dst_surf->dev->stream, dst_surf->pixbuf, diff --git a/src/surface_shared.c b/src/surface_shared.c new file mode 100644 index 0000000..8cd6f1e --- /dev/null +++ b/src/surface_shared.c @@ -0,0 +1,253 @@ + /* + * NVIDIA TEGRA 2 VDPAU backend + * + * Copyright (c) 2017 Dmitry Osipenko <digetx@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "vdpau_tegra.h" + +static pthread_mutex_t shared_lock = PTHREAD_MUTEX_INITIALIZER; + +static XvImage * create_video_xv(tegra_surface *video) +{ + XvImage *xv_img; + uint32_t *bo_flinks; + uint32_t *pitches; + uint32_t *offsets; + + xv_img = XvCreateImage(video->dev->display, video->dev->xv_port, + FOURCC_PASSTHROUGH_YV12, NULL, + video->width, video->height); + if (!xv_img) { + return NULL; + } + + xv_img->data = calloc(1, xv_img->data_size); + + if (!xv_img->data) { + XFree(xv_img); + return NULL; + } + + bo_flinks = (uint32_t*) (xv_img->data + 0); + pitches = (uint32_t*) (xv_img->data + 12); + offsets = (uint32_t*) (xv_img->data + 24); + + drm_tegra_bo_get_name(video->y_bo, &bo_flinks[0]); + drm_tegra_bo_get_name(video->cb_bo, &bo_flinks[1]); + drm_tegra_bo_get_name(video->cr_bo, &bo_flinks[2]); + + pitches[0] = video->pixbuf->pitch; + pitches[1] = video->pixbuf->pitch_uv; + pitches[2] = video->pixbuf->pitch_uv; + + offsets[0] = video->pixbuf->bo_offset[0]; + offsets[1] = video->pixbuf->bo_offset[1]; + offsets[2] = video->pixbuf->bo_offset[2]; + + return xv_img; +} + +tegra_shared_surface *create_shared_surface(tegra_surface *disp, + tegra_surface *video, + VdpCSCMatrix const csc_matrix, + uint32_t src_x0, + uint32_t src_y0, + uint32_t src_width, + uint32_t src_height, + uint32_t dst_x0, + uint32_t dst_y0, + uint32_t dst_width, + uint32_t dst_height) +{ + tegra_shared_surface *shared; + int i, k; + + for (i = 0; i < 3; i++) + for (k = 0; k < 3; k++) + if (fabs(csc_matrix[i][k] - CSC_BT_601[i][k]) > 0.01f) + goto check_709; + + goto shared_alloc; + + /* XXX: Tegra's CSC is hardcoded to BT601 in the kernel driver */ +check_709: + for (i = 0; i < 3; i++) + for (k = 0; k < 3; k++) + if (fabs(csc_matrix[i][k] - CSC_BT_709[i][k]) > 0.01f) + goto custom_csc; + + goto shared_alloc; + +custom_csc: + return NULL; + +shared_alloc: + shared = calloc(1, sizeof(tegra_shared_surface)); + if (!shared) { + return NULL; + } + + assert(disp->shared == NULL); + + atomic_set(&shared->refcnt, 1); + memcpy(&shared->csc_matrix, csc_matrix, sizeof(VdpCSCMatrix)); + + shared->xv_img = create_video_xv(video); + shared->video = video; + shared->disp = disp; + shared->src_x0 = src_x0; + shared->src_y0 = src_y0; + shared->src_width = src_width; + shared->src_height = src_height; + shared->dst_x0 = dst_x0; + shared->dst_y0 = dst_y0; + shared->dst_width = dst_width; + shared->dst_height = dst_height; + + if (!shared->xv_img) { + free(shared); + return NULL; + } + + ref_surface(video); + video->shared = shared; + disp->shared = shared; + + return shared; +} + +void ref_shared_surface(tegra_shared_surface *shared) +{ + atomic_inc(&shared->refcnt); +} + +void unref_shared_surface(tegra_shared_surface *shared) +{ + if (!atomic_dec_and_test(&shared->refcnt)) { + return; + } + + free(shared->xv_img->data); + XFree(shared->xv_img); + free(shared); +} + +tegra_surface * shared_surface_swap_video(tegra_surface *old) +{ + tegra_shared_surface *shared; + tegra_surface *new; + + assert(old->flags & SURFACE_VIDEO); + + pthread_mutex_lock(&shared_lock); + shared = old->shared; + pthread_mutex_unlock(&shared_lock); + + if (!shared) { + return old; + } + + new = alloc_surface(old->dev, old->width, old->height, ~0, 0, 1); + if (new) { + pthread_mutex_lock(&global_lock); + replace_surface(old, new); + pthread_mutex_unlock(&global_lock); + + unref_surface(old); + } else { + new = old; + } + + return new; +} + +void shared_surface_transfer_video(tegra_surface *disp) +{ + tegra_shared_surface *shared; + tegra_surface *video; + + assert(disp->flags & SURFACE_OUTPUT); + + pthread_mutex_lock(&shared_lock); + shared = disp->shared; + if (shared) { + ref_surface(disp); + disp->shared = NULL; + video = shared->video; + } + pthread_mutex_unlock(&shared_lock); + + if (!shared) { + return; + } + + if (disp->set_bg) { + host1x_gr2d_clear_rect_clipped(video->dev->stream, + disp->pixbuf, + disp->bg_color, + 0, 0, + disp->width, + disp->height, + shared->dst_x0, + shared->dst_y0, + shared->dst_x0 + shared->dst_width, + shared->dst_y0 + shared->dst_height, + true); + disp->set_bg = false; + } + + host1x_gr2d_surface_blit(video->dev->stream, + video->pixbuf, + disp->pixbuf, + shared->csc_matrix, + shared->src_x0, + shared->src_y0, + shared->src_width, + shared->src_height, + shared->dst_x0, + shared->dst_y0, + shared->dst_width, + shared->dst_height); + + unref_surface(disp); + unref_surface(video); + unref_shared_surface(shared); +} + +void shared_surface_kill_disp(tegra_surface *disp) +{ + tegra_shared_surface *shared = NULL; + + assert(disp->flags & SURFACE_OUTPUT); + + pthread_mutex_lock(&shared_lock); + + shared = disp->shared; + if (!shared) { + goto out; + } + + if (shared->video) { + unref_surface(shared->video); + } + + disp->shared = NULL; + unref_shared_surface(shared); + +out: + pthread_mutex_unlock(&shared_lock); +} diff --git a/src/surface_video.c b/src/surface_video.c index b2dc8db..9fc424a 100644 --- a/src/surface_video.c +++ b/src/surface_video.c @@ -71,7 +71,7 @@ VdpStatus vdp_video_surface_create(VdpDevice device, return VDP_STATUS_INVALID_CHROMA_TYPE; } - *surface = create_surface(dev, width, height, PIXMAN_x8r8g8b8, 0, 1); + *surface = create_surface(dev, width, height, ~0, 0, 1); if (*surface == VDP_INVALID_HANDLE) { return VDP_STATUS_RESOURCES; @@ -89,7 +89,7 @@ VdpStatus vdp_video_surface_get_parameters(VdpVideoSurface surface, VdpChromaType *chroma_type, uint32_t *width, uint32_t *height) { - tegra_surface *surf = get_surface(surface); + tegra_surface *surf = get_surface_ref(surface); if (surf == NULL) { return VDP_STATUS_INVALID_HANDLE; @@ -102,13 +102,15 @@ VdpStatus vdp_video_surface_get_parameters(VdpVideoSurface surface, } if (width != NULL) { - *width = surf->pixbuf->width; + *width = surf->width; } if (width != NULL) { - *height = surf->pixbuf->height; + *height = surf->height; } + unref_surface(surf); + return VDP_STATUS_OK; } @@ -118,7 +120,7 @@ VdpStatus vdp_video_surface_get_bits_y_cb_cr( void *const *destination_data, uint32_t const *destination_pitches) { - tegra_surface *surf = get_surface(surface); + tegra_surface *surf = get_surface_ref(surface); void *dst_y = destination_data[0]; void *dst_cr = destination_data[1]; void *dst_cb = destination_data[2]; @@ -135,17 +137,19 @@ VdpStatus vdp_video_surface_get_bits_y_cb_cr( case VDP_YCBCR_FORMAT_YV12: break; default: + unref_surface(surf); return VDP_STATUS_NO_IMPLEMENTATION; } ret = sync_video_frame_dmabufs(surf, READ_START); if (ret) { + unref_surface(surf); return ret; } - width = surf->pixbuf->width; - height = surf->pixbuf->height; + width = surf->width; + height = surf->height; /* Copy luma plane. */ ret = pixman_blt(surf->y_data, dst_y, @@ -177,11 +181,14 @@ VdpStatus vdp_video_surface_get_bits_y_cb_cr( ret = sync_video_frame_dmabufs(surf, READ_END); if (ret) { + unref_surface(surf); return ret; } surf->flags &= ~SURFACE_DATA_NEEDS_SYNC; + unref_surface(surf); + return VDP_STATUS_OK; } @@ -191,7 +198,7 @@ VdpStatus vdp_video_surface_put_bits_y_cb_cr( void const *const *source_data, uint32_t const *source_pitches) { - tegra_surface *surf = get_surface(surface); + tegra_surface *surf = get_surface_ref(surface); void *src_y = (void *)source_data[0]; void *src_cr = (void *)source_data[1]; void *src_cb = (void *)source_data[2]; @@ -208,17 +215,19 @@ VdpStatus vdp_video_surface_put_bits_y_cb_cr( case VDP_YCBCR_FORMAT_YV12: break; default: + unref_surface(surf); return VDP_STATUS_NO_IMPLEMENTATION; } ret = sync_video_frame_dmabufs(surf, WRITE_START); if (ret) { + unref_surface(surf); return ret; } - width = surf->pixbuf->width; - height = surf->pixbuf->height; + width = surf->width; + height = surf->height; /* Copy luma plane. */ ret = pixman_blt(src_y, surf->y_data, @@ -252,11 +261,14 @@ VdpStatus vdp_video_surface_put_bits_y_cb_cr( ret = sync_video_frame_dmabufs(surf, WRITE_END); if (ret) { + unref_surface(surf); return ret; } surf->flags &= ~SURFACE_DATA_NEEDS_SYNC; surf->flags |= SURFACE_YUV_UNCONVERTED; + unref_surface(surf); + return VDP_STATUS_OK; } diff --git a/src/vdpau_tegra.c b/src/vdpau_tegra.c index db9023f..1f3352a 100644 --- a/src/vdpau_tegra.c +++ b/src/vdpau_tegra.c @@ -28,6 +28,18 @@ static tegra_surface * tegra_surfaces[MAX_SURFACES_NB]; static tegra_pqt * tegra_pqts[MAX_PRESENTATION_QUEUE_TARGETS_NB]; static tegra_pq * tegra_pqs[MAX_PRESENTATION_QUEUES_NB]; +VdpCSCMatrix CSC_BT_601 = { + { 1.164384f, 0.000000f, 1.596027f }, + { 1.164384f,-0.391762f,-0.812968f }, + { 1.164384f, 2.017232f, 0.000000f }, +}; + +VdpCSCMatrix CSC_BT_709 = { + { 1.164384f, 0.000000f, 1.792741f }, + { 1.164384f,-0.213249f,-0.532909f }, + { 1.164384f, 2.112402f, 0.000000f }, +}; + tegra_device * get_device(VdpDevice device) { if (device >= MAX_DEVICES_NB ) { @@ -91,6 +103,22 @@ tegra_surface * get_surface(VdpBitmapSurface surface) return tegra_surfaces[surface]; } +tegra_surface * get_surface_ref(VdpBitmapSurface surface) +{ + tegra_surface *surf; + + pthread_mutex_lock(&global_lock); + + surf = get_surface(surface); + if (surf) { + ref_surface(surf); + } + + pthread_mutex_unlock(&global_lock); + + return surf; +} + void set_surface(VdpBitmapSurface surface, tegra_surface *surf) { if (surface >= MAX_SURFACES_NB) { @@ -104,6 +132,22 @@ void set_surface(VdpBitmapSurface surface, tegra_surface *surf) tegra_surfaces[surface] = surf; } +void replace_surface(tegra_surface *old_surf, tegra_surface *new_surf) +{ + if (old_surf->surface_id >= MAX_SURFACES_NB) { + return; + } + + if (old_surf != new_surf) { + assert(tegra_surfaces[old_surf->surface_id] == old_surf); + + new_surf->surface_id = old_surf->surface_id; + old_surf->surface_id = MAX_SURFACES_NB; + + tegra_surfaces[new_surf->surface_id] = new_surf; + } +} + tegra_pqt * get_presentation_queue_target(VdpPresentationQueueTarget target) { if (target >= MAX_PRESENTATION_QUEUE_TARGETS_NB) { @@ -227,33 +271,13 @@ VdpStatus vdp_generate_csc_matrix(VdpProcamp *procamp, switch (standard) { case VDP_COLOR_STANDARD_ITUR_BT_601: - (*csc_matrix)[0][0] = 1.164f; - (*csc_matrix)[0][1] = 0.000f; - (*csc_matrix)[0][2] = 1.596f; - - (*csc_matrix)[1][0] = 1.164f; - (*csc_matrix)[1][1] = -0.392f; - (*csc_matrix)[1][2] = -0.813f; - - (*csc_matrix)[2][0] = 1.164f; - (*csc_matrix)[2][1] = 2.017f; - (*csc_matrix)[2][2] = 0.000f; + memcpy(csc_matrix, CSC_BT_601, sizeof(VdpCSCMatrix)); break; case VDP_COLOR_STANDARD_ITUR_BT_709: - (*csc_matrix)[0][0] = 1.164f; - (*csc_matrix)[0][1] = 0.000f; - (*csc_matrix)[0][2] = 1.793f; - - (*csc_matrix)[1][0] = 1.164f; - (*csc_matrix)[1][1] = -0.213f; - (*csc_matrix)[1][2] = -0.533f; - - (*csc_matrix)[2][0] = 1.164f; - (*csc_matrix)[2][1] = 2.112f; - (*csc_matrix)[2][2] = 0.000f; + memcpy(csc_matrix, CSC_BT_709, sizeof(VdpCSCMatrix)); break; default: - abort(); + return VDP_STATUS_NO_IMPLEMENTATION; } if (procamp == NULL) { diff --git a/src/vdpau_tegra.h b/src/vdpau_tegra.h index 099c54f..cd67b0d 100644 --- a/src/vdpau_tegra.h +++ b/src/vdpau_tegra.h @@ -86,6 +86,7 @@ #define SURFACE_VIDEO (1 << 0) #define SURFACE_YUV_UNCONVERTED (1 << 1) #define SURFACE_DATA_NEEDS_SYNC (1 << 2) +#define SURFACE_OUTPUT (1 << 3) #define PASSTHROUGH_DATA_SIZE 36 @@ -103,6 +104,9 @@ #define UNIFIED_BUFFER 0 +extern VdpCSCMatrix CSC_BT_601; +extern VdpCSCMatrix CSC_BT_709; + typedef struct { int atomic; } atomic_t; @@ -121,6 +125,18 @@ typedef struct tegra_device { int drm_fd; } tegra_device; +struct tegra_surface; + +typedef struct tegra_shared_surface { + atomic_t refcnt; + struct tegra_surface *video; + struct tegra_surface *disp; + VdpCSCMatrix csc_matrix; + uint32_t src_x0, src_y0, src_width, src_height; + uint32_t dst_x0, dst_y0, dst_width, dst_height; + XvImage *xv_img; +} tegra_shared_surface; + typedef struct tegra_surface { tegra_device *dev; @@ -144,6 +160,9 @@ typedef struct tegra_surface { uint32_t disp_width; uint32_t disp_height; + uint32_t width; + uint32_t height; + VdpPresentationQueueStatus status; VdpTime first_presentation_time; VdpTime earliest_presentation_time; @@ -154,6 +173,13 @@ typedef struct tegra_surface { pthread_mutex_t lock; bool idle_hack; + + uint32_t surface_id; + + tegra_shared_surface *shared; + + uint32_t bg_color; + bool set_bg; } tegra_surface; typedef struct tegra_decoder { @@ -166,6 +192,7 @@ typedef struct tegra_decoder { typedef struct tegra_mixer { VdpCSCMatrix csc_matrix; VdpColor bg_color; + tegra_device *dev; } tegra_mixer; typedef struct tegra_pqt { @@ -174,12 +201,11 @@ typedef struct tegra_pqt { Drawable drawable; GC gc; atomic_t refcnt; + uint32_t bg_color; } tegra_pqt; typedef struct tegra_pq { tegra_pqt *pqt; - VdpColor background_color; - struct list_head surf_list; pthread_mutex_t lock; pthread_cond_t cond; @@ -204,8 +230,12 @@ void set_mixer(VdpVideoMixer mixer, tegra_mixer *mix); tegra_surface * get_surface(VdpBitmapSurface surface); +tegra_surface * get_surface_ref(VdpBitmapSurface surface); + void set_surface(VdpBitmapSurface surface, tegra_surface *surf); +void replace_surface(tegra_surface *old_surf, tegra_surface *new_surf); + tegra_pqt * get_presentation_queue_target(VdpPresentationQueueTarget target); void set_presentation_queue_target(VdpPresentationQueueTarget target, @@ -223,6 +253,11 @@ uint32_t create_surface(tegra_device *dev, int output, int video); +tegra_surface *alloc_surface(tegra_device *dev, + uint32_t width, uint32_t height, + VdpRGBAFormat rgba_format, + int output, int video); + VdpStatus destroy_surface(tegra_surface *surf); void ref_surface(tegra_surface *surf); @@ -246,6 +281,28 @@ enum frame_sync { int sync_video_frame_dmabufs(tegra_surface *surf, enum frame_sync type); +tegra_shared_surface *create_shared_surface(tegra_surface *disp, + tegra_surface *video, + VdpCSCMatrix const csc_matrix, + uint32_t src_x0, + uint32_t src_y0, + uint32_t src_width, + uint32_t src_height, + uint32_t dst_x0, + uint32_t dst_y0, + uint32_t dst_width, + uint32_t dst_height); + +void ref_shared_surface(tegra_shared_surface *shared); + +void unref_shared_surface(tegra_shared_surface *shared); + +tegra_surface * shared_surface_swap_video(tegra_surface *old); + +void shared_surface_transfer_video(tegra_surface *disp); + +void shared_surface_kill_disp(tegra_surface *disp); + VdpGetErrorString vdp_get_error_string; VdpGetProcAddress vdp_get_proc_address; VdpGetApiVersion vdp_get_api_version; |