summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2017-12-25 04:30:21 +0300
committerDmitry Osipenko <digetx@gmail.com>2017-12-26 22:14:28 +0300
commitce89b4943ac051ce5036930c11995d6f213368e9 (patch)
treedff1f46b03e5ff5e1939ae71af46671f7fe6b889
parented61456b0c468c12b63fbceecd519e99d2de9db8 (diff)
Implement zero-copy output
-rw-r--r--src/Makefile.am1
-rw-r--r--src/decoder.c2
-rw-r--r--src/presentation_queue.c43
-rw-r--r--src/surface.c120
-rw-r--r--src/surface_mixer.c145
-rw-r--r--src/surface_output.c2
-rw-r--r--src/surface_shared.c253
-rw-r--r--src/surface_video.c32
-rw-r--r--src/vdpau_tegra.c70
-rw-r--r--src/vdpau_tegra.h61
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;