From 1db14cd1c3e060501dedb7040b1c4c829495e952 Mon Sep 17 00:00:00 2001 From: Stanislav Vorobiov Date: Mon, 10 Feb 2014 20:50:35 +0400 Subject: [PATCH] VIGS: Implemented plane support We now support up to 2 hardware planes with z-ordering and scaling. This patch also adds surface scanout flag support. Surface scanout flag is used as a hint that helps the host to decide how to process the surface - either upload it to texture or continously scanout data out of surface's VRAM Change-Id: Ia5ac6014cfd0b49f80c4593763ee86966cf23c2a --- hw/vigs/vigs_backend.h | 24 +++-- hw/vigs/vigs_comm.c | 32 ++++++- hw/vigs/vigs_comm.h | 10 ++ hw/vigs/vigs_gl_backend.c | 232 ++++++++++++++++++++++++++++++++++++++++++---- hw/vigs/vigs_plane.h | 55 +++++++++++ hw/vigs/vigs_protocol.h | 35 ++++++- hw/vigs/vigs_server.c | 213 +++++++++++++++++++++++++++++++++--------- hw/vigs/vigs_server.h | 4 +- hw/vigs/vigs_surface.c | 6 ++ hw/vigs/vigs_surface.h | 7 ++ hw/vigs/vigs_sw_backend.c | 34 +++++-- 11 files changed, 568 insertions(+), 84 deletions(-) create mode 100644 hw/vigs/vigs_plane.h diff --git a/hw/vigs/vigs_backend.h b/hw/vigs/vigs_backend.h index c918c7d..126cc5d 100644 --- a/hw/vigs/vigs_backend.h +++ b/hw/vigs/vigs_backend.h @@ -34,13 +34,16 @@ struct winsys_info; struct vigs_surface; +struct vigs_plane; -typedef void (*vigs_read_pixels_cb)(void */*user_data*/, - uint8_t */*pixels*/, - uint32_t /*width*/, - uint32_t /*height*/, - uint32_t /*stride*/, - vigsp_surface_format /*format*/); +typedef uint8_t *(*vigs_composite_start_cb)(void */*user_data*/, + uint32_t /*width*/, + uint32_t /*height*/, + uint32_t /*stride*/, + vigsp_surface_format /*format*/); + +typedef void (*vigs_composite_end_cb)(void */*user_data*/, + bool /*was_started*/); struct vigs_backend { @@ -55,10 +58,11 @@ struct vigs_backend vigsp_surface_format /*format*/, vigsp_surface_id /*id*/); - void (*read_pixels)(struct vigs_surface */*surface*/, - uint8_t */*pixels*/, - vigs_read_pixels_cb /*cb*/, - void */*user_data*/); + void (*composite)(struct vigs_surface */*surface*/, + const struct vigs_plane */*planes*/, + vigs_composite_start_cb /*start_cb*/, + vigs_composite_end_cb /*end_cb*/, + void */*user_data*/); void (*batch_end)(struct vigs_backend */*backend*/); diff --git a/hw/vigs/vigs_comm.c b/hw/vigs/vigs_comm.c index 54577fe..6e08517 100644 --- a/hw/vigs/vigs_comm.c +++ b/hw/vigs/vigs_comm.c @@ -74,12 +74,14 @@ static void vigs_comm_dispatch_set_root_surface(struct vigs_comm_ops *ops, struct vigsp_cmd_set_root_surface_request *request, vigsp_fence_seq fence_seq) { - VIGS_LOG_TRACE("id = %u, offset = %u", + VIGS_LOG_TRACE("id = %u, scanout = %d, offset = %u", request->id, + request->scanout, request->offset); ops->set_root_surface(user_data, request->id, + request->scanout, request->offset, fence_seq); } @@ -189,6 +191,28 @@ static void vigs_comm_dispatch_solid_fill(struct vigs_comm_batch_ops *ops, request->num_entries); } +static void vigs_comm_dispatch_set_plane(struct vigs_comm_batch_ops *ops, + void *user_data, + struct vigsp_cmd_set_plane_request *request) +{ + VIGS_LOG_TRACE("plane = %u, sfc_id = %u, src_rect = {%u, %u, %u, %u}, dst_x = %d, dst_y = %d, dst_size = {%u, %u}, z_pos = %d", + request->plane, + request->sfc_id, + request->src_rect.pos.x, + request->src_rect.pos.y, + request->src_rect.size.w, + request->src_rect.size.h, + request->dst_x, + request->dst_y, + request->dst_size.w, + request->dst_size.h, + request->z_pos); + + ops->set_plane(user_data, request->plane, request->sfc_id, + &request->src_rect, request->dst_x, request->dst_y, + &request->dst_size, request->z_pos); +} + /* * @} */ @@ -210,11 +234,13 @@ static const vigs_dispatch_func vigs_dispatch_table[] = VIGS_DISPATCH_ENTRY(vigsp_cmd_copy, vigs_comm_dispatch_copy), VIGS_DISPATCH_ENTRY(vigsp_cmd_solid_fill, - vigs_comm_dispatch_solid_fill) + vigs_comm_dispatch_solid_fill), + VIGS_DISPATCH_ENTRY(vigsp_cmd_set_plane, + vigs_comm_dispatch_set_plane) }; #define VIGS_MIN_BATCH_CMD_ID vigsp_cmd_create_surface -#define VIGS_MAX_BATCH_CMD_ID vigsp_cmd_solid_fill +#define VIGS_MAX_BATCH_CMD_ID vigsp_cmd_set_plane struct vigs_comm *vigs_comm_create(uint8_t *ram_ptr) { diff --git a/hw/vigs/vigs_comm.h b/hw/vigs/vigs_comm.h index 40514e7..b07c71b 100644 --- a/hw/vigs/vigs_comm.h +++ b/hw/vigs/vigs_comm.h @@ -42,6 +42,7 @@ struct vigs_comm_ops void (*set_root_surface)(void */*user_data*/, vigsp_surface_id /*id*/, + bool /*scanout*/, vigsp_offset /*offset*/, vigsp_fence_seq /*fence_seq*/); @@ -86,6 +87,15 @@ struct vigs_comm_batch_ops const struct vigsp_rect */*entries*/, uint32_t /*num_entries*/); + void (*set_plane)(void */*user_data*/, + vigsp_u32 /*plane*/, + vigsp_surface_id /*sfc_id*/, + const struct vigsp_rect */*src_rect*/, + int /*dst_x*/, + int /*dst_y*/, + const struct vigsp_size */*dst_size*/, + int /*z_pos*/); + void (*end)(void */*user_data*/, vigsp_fence_seq /*fence_seq*/); }; diff --git a/hw/vigs/vigs_gl_backend.c b/hw/vigs/vigs_gl_backend.c index 2cf7a94..1824bf2 100644 --- a/hw/vigs/vigs_gl_backend.c +++ b/hw/vigs/vigs_gl_backend.c @@ -29,6 +29,7 @@ #include "vigs_gl_backend.h" #include "vigs_surface.h" +#include "vigs_plane.h" #include "vigs_log.h" #include "vigs_utils.h" #include "vigs_ref.h" @@ -43,9 +44,9 @@ struct vigs_gl_backend_read_pixels_work_item struct vigs_gl_backend *backend; - vigs_read_pixels_cb cb; + vigs_composite_start_cb start_cb; + vigs_composite_end_cb end_cb; void *user_data; - uint8_t *pixels; uint32_t width; uint32_t height; uint32_t stride; @@ -886,18 +887,23 @@ static void vigs_gl_backend_read_pixels_work(struct work_queue_item *wq_item) { struct vigs_gl_backend_read_pixels_work_item *item = (struct vigs_gl_backend_read_pixels_work_item*)wq_item; struct vigs_gl_backend *backend = item->backend; + uint8_t *dst = NULL; VIGS_LOG_TRACE("enter"); if (backend->read_pixels_make_current(backend, true)) { - uint8_t *pixels; + uint8_t *src; backend->BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, backend->pbo); - pixels = backend->MapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); + src = backend->MapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); - if (pixels) { - memcpy(item->pixels, pixels, item->stride * item->height); + if (src) { + dst = item->start_cb(item->user_data, + item->width, item->height, + item->stride, item->format); + + memcpy(dst, src, item->stride * item->height); if (!backend->UnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB)) { VIGS_LOG_CRITICAL("glUnmapBuffer failed"); @@ -911,24 +917,209 @@ static void vigs_gl_backend_read_pixels_work(struct work_queue_item *wq_item) backend->read_pixels_make_current(backend, false); } - item->cb(item->user_data, - item->pixels, item->width, item->height, - item->stride, item->format); + item->end_cb(item->user_data, (dst != NULL)); g_free(item); } -static void vigs_gl_backend_read_pixels(struct vigs_surface *surface, - uint8_t *pixels, - vigs_read_pixels_cb cb, - void *user_data) +static void vigs_gl_backend_composite(struct vigs_surface *surface, + const struct vigs_plane *planes, + vigs_composite_start_cb start_cb, + vigs_composite_end_cb end_cb, + void *user_data) { struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)surface->backend; + struct vigs_gl_surface *gl_root_sfc = (struct vigs_gl_surface*)surface; + struct vigs_winsys_gl_surface *ws_root_sfc = get_ws_sfc(gl_root_sfc); + uint32_t i; + GLfloat *vert_coords; + GLfloat *tex_coords; + const struct vigs_plane *sorted_planes[VIGS_MAX_PLANES]; uint32_t size = surface->stride * surface->ws_sfc->height; struct vigs_gl_backend_read_pixels_work_item *item; VIGS_LOG_TRACE("enter"); + if (!surface->ptr) { + if (!ws_root_sfc->tex) { + VIGS_LOG_WARN("compositing garbage (root surface) ???"); + } + + if (!vigs_winsys_gl_surface_create_texture(ws_root_sfc, &ws_root_sfc->tex)) { + goto out; + } + } + + if (!vigs_winsys_gl_surface_create_texture(ws_root_sfc, &gl_root_sfc->tmp_tex)) { + goto out; + } + + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + struct vigs_gl_surface *gl_sfc; + struct vigs_winsys_gl_surface *ws_sfc; + + if (!planes[i].sfc) { + continue; + } + + gl_sfc = (struct vigs_gl_surface*)planes[i].sfc; + ws_sfc = get_ws_sfc(gl_sfc); + + if (!ws_sfc->tex) { + VIGS_LOG_WARN("compositing garbage (plane %u) ???", i); + } + + if (!vigs_winsys_gl_surface_create_texture(ws_sfc, &ws_sfc->tex)) { + goto out; + } + } + + if (!vigs_gl_surface_create_framebuffer(gl_root_sfc)) { + goto out; + } + + vigs_vector_resize(&gl_backend->v1, 0); + vigs_vector_resize(&gl_backend->v2, 0); + + vert_coords = vigs_vector_append(&gl_backend->v1, + (8 * sizeof(GLfloat))); + tex_coords = vigs_vector_append(&gl_backend->v2, + (8 * sizeof(GLfloat))); + + if (surface->ptr) { + /* + * Root surface is scanout, upload it to texture. + * Slow path. + */ + + gl_backend->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl_backend->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl_backend->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + gl_backend->PixelStorei(GL_UNPACK_SKIP_ROWS, 0); + + gl_backend->BindTexture(GL_TEXTURE_2D, gl_root_sfc->tmp_tex); + + gl_backend->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + ws_root_sfc->base.base.width, + ws_root_sfc->base.base.height, + ws_root_sfc->tex_format, + ws_root_sfc->tex_type, + surface->ptr); + } + + gl_backend->BindFramebuffer(GL_FRAMEBUFFER, gl_root_sfc->fb); + + vigs_gl_surface_setup_framebuffer(gl_root_sfc); + + gl_backend->Enable(GL_TEXTURE_2D); + + gl_backend->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gl_root_sfc->tmp_tex, 0); + + gl_backend->EnableClientState(GL_VERTEX_ARRAY); + gl_backend->EnableClientState(GL_TEXTURE_COORD_ARRAY); + + gl_backend->Color4f(1.0f, 1.0f, 1.0f, 1.0f); + + if (!surface->ptr) { + /* + * If root surface is not scanout then we must render + * it. + */ + + vert_coords[0] = 0; + vert_coords[1] = ws_root_sfc->base.base.height; + vert_coords[2] = ws_root_sfc->base.base.width; + vert_coords[3] = ws_root_sfc->base.base.height; + vert_coords[4] = ws_root_sfc->base.base.width; + vert_coords[5] = 0; + vert_coords[6] = 0; + vert_coords[7] = 0; + + tex_coords[0] = 0; + tex_coords[1] = 0; + tex_coords[2] = 1; + tex_coords[3] = 0; + tex_coords[4] = 1; + tex_coords[5] = 1; + tex_coords[6] = 0; + tex_coords[7] = 1; + + gl_backend->BindTexture(GL_TEXTURE_2D, ws_root_sfc->tex); + + gl_backend->VertexPointer(2, GL_FLOAT, 0, vert_coords); + gl_backend->TexCoordPointer(2, GL_FLOAT, 0, tex_coords); + + gl_backend->DrawArrays(GL_QUADS, 0, 4); + } + + /* + * Sort planes, only 2 of them now, don't bother... + */ + + assert(VIGS_MAX_PLANES == 2); + + if (planes[0].z_pos <= planes[1].z_pos) { + sorted_planes[0] = &planes[0]; + sorted_planes[1] = &planes[1]; + } else { + sorted_planes[0] = &planes[1]; + sorted_planes[1] = &planes[0]; + } + + /* + * Now render planes, respect z-order. + */ + + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + const struct vigs_plane *plane = sorted_planes[i]; + struct vigs_gl_surface *gl_sfc; + struct vigs_winsys_gl_surface *ws_sfc; + GLfloat src_w, src_h; + + if (!plane->sfc) { + continue; + } + + gl_sfc = (struct vigs_gl_surface*)plane->sfc; + ws_sfc = get_ws_sfc(gl_sfc); + + src_w = ws_sfc->base.base.width; + src_h = ws_sfc->base.base.height; + + vert_coords[0] = plane->dst_x; + vert_coords[1] = plane->dst_y; + vert_coords[2] = plane->dst_x + (int)plane->dst_size.w; + vert_coords[3] = plane->dst_y; + vert_coords[4] = plane->dst_x + (int)plane->dst_size.w; + vert_coords[5] = plane->dst_y + (int)plane->dst_size.h; + vert_coords[6] = plane->dst_x; + vert_coords[7] = plane->dst_y + (int)plane->dst_size.h; + + tex_coords[0] = (GLfloat)plane->src_rect.pos.x / src_w; + tex_coords[1] = (GLfloat)(src_h - plane->src_rect.pos.y) / src_h; + tex_coords[2] = (GLfloat)(plane->src_rect.pos.x + plane->src_rect.size.w) / src_w; + tex_coords[3] = (GLfloat)(src_h - plane->src_rect.pos.y) / src_h; + tex_coords[4] = (GLfloat)(plane->src_rect.pos.x + plane->src_rect.size.w) / src_w; + tex_coords[5] = (GLfloat)(src_h - (plane->src_rect.pos.y + plane->src_rect.size.h)) / src_h; + tex_coords[6] = (GLfloat)plane->src_rect.pos.x / src_w; + tex_coords[7] = (GLfloat)(src_h - (plane->src_rect.pos.y + plane->src_rect.size.h)) / src_h; + + gl_backend->BindTexture(GL_TEXTURE_2D, ws_sfc->tex); + + gl_backend->VertexPointer(2, GL_FLOAT, 0, vert_coords); + gl_backend->TexCoordPointer(2, GL_FLOAT, 0, tex_coords); + + gl_backend->DrawArrays(GL_QUADS, 0, 4); + } + + gl_backend->DisableClientState(GL_TEXTURE_COORD_ARRAY); + gl_backend->DisableClientState(GL_VERTEX_ARRAY); + + /* + * Now schedule asynchronous glReadPixels. + */ + gl_backend->BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, gl_backend->pbo); if (size > gl_backend->pbo_size) { @@ -939,7 +1130,11 @@ static void vigs_gl_backend_read_pixels(struct vigs_surface *surface, GL_STREAM_READ); } - surface->read_pixels(surface, NULL); + gl_backend->PixelStorei(GL_PACK_ALIGNMENT, 1); + gl_backend->ReadPixels(0, 0, + surface->ws_sfc->width, surface->ws_sfc->height, + ws_root_sfc->tex_format, ws_root_sfc->tex_type, + NULL); gl_backend->BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); @@ -958,15 +1153,18 @@ static void vigs_gl_backend_read_pixels(struct vigs_surface *surface, item->backend = gl_backend; - item->cb = cb; + item->start_cb = start_cb; + item->end_cb = end_cb; item->user_data = user_data; - item->pixels = pixels; item->width = surface->ws_sfc->width; item->height = surface->ws_sfc->height; item->stride = surface->stride; item->format = surface->format; work_queue_add_item(gl_backend->read_pixels_queue, &item->base); + +out: + gl_backend->BindFramebuffer(GL_FRAMEBUFFER, 0); } static void vigs_gl_backend_batch_end(struct vigs_backend *backend) @@ -1011,7 +1209,7 @@ bool vigs_gl_backend_init(struct vigs_gl_backend *gl_backend) gl_backend->base.batch_start = &vigs_gl_backend_batch_start; gl_backend->base.create_surface = &vigs_gl_backend_create_surface; - gl_backend->base.read_pixels = &vigs_gl_backend_read_pixels; + gl_backend->base.composite = &vigs_gl_backend_composite; gl_backend->base.batch_end = &vigs_gl_backend_batch_end; gl_backend->make_current(gl_backend, false); diff --git a/hw/vigs/vigs_plane.h b/hw/vigs/vigs_plane.h new file mode 100644 index 0000000..de53671 --- /dev/null +++ b/hw/vigs/vigs_plane.h @@ -0,0 +1,55 @@ +/* + * vigs + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Stanislav Vorobiov + * Jinhyung Jo + * YeongKyoon Lee + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#ifndef _QEMU_VIGS_PLANE_H +#define _QEMU_VIGS_PLANE_H + +#include "vigs_types.h" + +struct vigs_surface; + +struct vigs_plane +{ + struct vigs_surface *sfc; + + struct vigsp_rect src_rect; + + int dst_x; + int dst_y; + struct vigsp_size dst_size; + + int z_pos; + + /* + * Plane moved/resized, need to recomposite. + */ + bool is_dirty; +}; + +#endif diff --git a/hw/vigs/vigs_protocol.h b/hw/vigs/vigs_protocol.h index ba853e4..4ab1b14 100644 --- a/hw/vigs/vigs_protocol.h +++ b/hw/vigs/vigs_protocol.h @@ -37,7 +37,9 @@ /* * Bump this whenever protocol changes. */ -#define VIGS_PROTOCOL_VERSION 15 +#define VIGS_PROTOCOL_VERSION 16 + +#define VIGS_MAX_PLANES 2 typedef signed char vigsp_s8; typedef signed short vigsp_s16; @@ -78,6 +80,7 @@ typedef enum vigsp_cmd_update_gpu = 0x7, vigsp_cmd_copy = 0x8, vigsp_cmd_solid_fill = 0x9, + vigsp_cmd_set_plane = 0xA, /* * @} */ @@ -228,8 +231,8 @@ struct vigsp_cmd_destroy_surface_request * cmd_set_root_surface * * Sets surface identified by 'id' as new root surface. Root surface is the - * one that's displayed on screen. Root surface must reside in VRAM - * all the time, pass 'offset' in VRAM here. + * one that's displayed on screen. Root surface resides in VRAM + * all the time if 'scanout' is true. * * Pass 0 as id in order to reset the root surface. * @@ -239,6 +242,7 @@ struct vigsp_cmd_destroy_surface_request struct vigsp_cmd_set_root_surface_request { vigsp_surface_id id; + vigsp_bool scanout; vigsp_offset offset; }; @@ -325,6 +329,31 @@ struct vigsp_cmd_solid_fill_request * @} */ +/* + * cmd_set_plane + * + * Assigns surface 'sfc_id' to plane identified by 'plane'. + * + * Pass 0 as sfc_id in order to disable the plane. + * + * @{ + */ + +struct vigsp_cmd_set_plane_request +{ + vigsp_u32 plane; + vigsp_surface_id sfc_id; + struct vigsp_rect src_rect; + vigsp_s32 dst_x; + vigsp_s32 dst_y; + struct vigsp_size dst_size; + vigsp_s32 z_pos; +}; + +/* + * @} + */ + #pragma pack() #endif diff --git a/hw/vigs/vigs_server.c b/hw/vigs/vigs_server.c index 7438c21..a710a6c 100644 --- a/hw/vigs/vigs_server.c +++ b/hw/vigs/vigs_server.c @@ -49,6 +49,7 @@ struct vigs_server_set_root_surface_work_item struct vigs_server *server; vigsp_surface_id id; + bool scanout; vigsp_offset offset; }; @@ -62,13 +63,25 @@ static void vigs_server_surface_destroy_func(gpointer data) static void vigs_server_unuse_surface(struct vigs_server *server, struct vigs_surface *sfc) { + int i; + /* * If it was root surface then root surface is now NULL. */ if (server->root_sfc == sfc) { + vigs_surface_set_scanout(server->root_sfc, NULL); server->root_sfc = NULL; - server->root_sfc_ptr = NULL; + } + + /* + * If it was attached to a plane then detach it. + */ + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + if (server->planes[i].sfc == sfc) { + server->planes[i].sfc = NULL; + server->planes[i].is_dirty = true; + } } } @@ -194,7 +207,9 @@ static void vigs_server_dispatch_update_vram(void *user_data, vigs_sfc->read_pixels(vigs_sfc, server->vram_ptr + offset); - vigs_sfc->is_dirty = false; + if (vigs_sfc->ptr) { + vigs_sfc->is_dirty = false; + } } static void vigs_server_dispatch_update_gpu(void *user_data, @@ -290,6 +305,46 @@ static void vigs_server_dispatch_solid_fill(void *user_data, sfc->is_dirty = true; } +static void vigs_server_dispatch_set_plane(void *user_data, + vigsp_u32 plane, + vigsp_surface_id sfc_id, + const struct vigsp_rect *src_rect, + int dst_x, + int dst_y, + const struct vigsp_size *dst_size, + int z_pos) +{ + struct vigs_server *server = user_data; + struct vigs_surface *sfc = NULL; + + if (!server->initialized) { + VIGS_LOG_ERROR("not initialized"); + return; + } + + if (sfc_id) { + sfc = g_hash_table_lookup(server->surfaces, GUINT_TO_POINTER(sfc_id)); + + if (!sfc) { + VIGS_LOG_ERROR("surface %u not found", sfc_id); + return; + } + } + + if (plane >= VIGS_MAX_PLANES) { + VIGS_LOG_ERROR("bad plane %u", plane); + return; + } + + server->planes[plane].sfc = sfc; + server->planes[plane].src_rect = *src_rect; + server->planes[plane].dst_x = dst_x; + server->planes[plane].dst_y = dst_y; + server->planes[plane].dst_size = *dst_size; + server->planes[plane].z_pos = z_pos; + server->planes[plane].is_dirty = true; +} + static void vigs_server_dispatch_batch_end(void *user_data, vigsp_fence_seq fence_seq) { @@ -311,6 +366,7 @@ static struct vigs_comm_batch_ops vigs_server_dispatch_batch_ops = .update_gpu = &vigs_server_dispatch_update_gpu, .copy = &vigs_server_dispatch_copy, .solid_fill = &vigs_server_dispatch_solid_fill, + .set_plane = &vigs_server_dispatch_set_plane, .end = &vigs_server_dispatch_batch_end }; @@ -339,8 +395,10 @@ static void vigs_server_set_root_surface_work(struct work_queue_item *wq_item) } if (item->id == 0) { + if (server->root_sfc) { + vigs_surface_set_scanout(server->root_sfc, NULL); + } server->root_sfc = NULL; - server->root_sfc_ptr = NULL; VIGS_LOG_TRACE("root surface reset"); @@ -355,39 +413,55 @@ static void vigs_server_set_root_surface_work(struct work_queue_item *wq_item) } server->root_sfc = sfc; - server->root_sfc_ptr = server->vram_ptr + item->offset; + + if (item->scanout) { + vigs_surface_set_scanout(server->root_sfc, + server->vram_ptr + item->offset); + } else { + vigs_surface_set_scanout(server->root_sfc, NULL); + + /* + * We want to display it on next display update. + */ + server->root_sfc->is_dirty = true; + } out: g_free(item); } -static void vigs_server_update_display_cb(void *user_data, - uint8_t *pixels, - uint32_t width, - uint32_t height, - uint32_t stride, - vigsp_surface_format format) +static uint8_t *vigs_server_update_display_start_cb(void *user_data, + uint32_t width, + uint32_t height, + uint32_t stride, + vigsp_surface_format format) { struct vigs_server *server = user_data; - uint32_t capture_fence_seq; qemu_mutex_lock(&server->capture_mutex); - if (pixels) { - if ((server->captured.stride != stride) || - (server->captured.height != height)) { - g_free(server->captured.data); - server->captured.data = g_malloc(stride * height); - } + if ((server->captured.stride != stride) || + (server->captured.height != height)) { + g_free(server->captured.data); + server->captured.data = g_malloc(stride * height); + } + + server->captured.width = width; + server->captured.height = height; + server->captured.stride = stride; + server->captured.format = format; - memcpy(server->captured.data, - pixels, - stride * height); + return server->captured.data; +} - server->captured.width = width; - server->captured.height = height; - server->captured.stride = stride; - server->captured.format = format; +static void vigs_server_update_display_end_cb(void *user_data, + bool was_started) +{ + struct vigs_server *server = user_data; + uint32_t capture_fence_seq; + + if (!was_started) { + qemu_mutex_lock(&server->capture_mutex); } server->is_capturing = false; @@ -407,32 +481,85 @@ static void vigs_server_update_display_work(struct work_queue_item *wq_item) struct vigs_server_work_item *item = (struct vigs_server_work_item*)wq_item; struct vigs_server *server = item->server; struct vigs_surface *root_sfc = server->root_sfc; + int i; + bool planes_on = false; + bool planes_dirty = false; if (!root_sfc) { - vigs_server_update_display_cb(server, - NULL, - 0, - 0, - 0, - vigsp_surface_bgrx8888); + /* + * If no root surface then this is a no-op. + * TODO: Can planes be enabled without a root surface ? + */ + vigs_server_update_display_end_cb(server, false); goto out; } - if (root_sfc->is_dirty) { - root_sfc->is_dirty = false; + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + /* + * If plane was moved/resized or turned on/off + * then we're dirty. + */ + if (server->planes[i].is_dirty) { + planes_dirty = true; + } + + if (server->planes[i].sfc) { + planes_on = true; + + /* + * If plane's surface is dirty then we're dirty. + */ + if (server->planes[i].sfc->is_dirty) { + planes_dirty = true; + } + } + } + + if (root_sfc->ptr && !root_sfc->is_dirty && !planes_on) { + /* + * Root surface is scanout, it's not dirty and planes not on, + * finish immediately. + */ + uint8_t *buff = vigs_server_update_display_start_cb(server, + root_sfc->ws_sfc->width, + root_sfc->ws_sfc->height, + root_sfc->stride, + root_sfc->format); + + memcpy(buff, + root_sfc->ptr, + root_sfc->stride * root_sfc->ws_sfc->height); + + vigs_server_update_display_end_cb(server, true); + } else if (root_sfc->ptr || root_sfc->is_dirty || planes_dirty) { + /* + * Composite root surface and planes. + */ server->backend->batch_start(server->backend); - server->backend->read_pixels(root_sfc, - server->root_sfc_ptr, - &vigs_server_update_display_cb, - server); + server->backend->composite(root_sfc, + &server->planes[0], + &vigs_server_update_display_start_cb, + &vigs_server_update_display_end_cb, + server); server->backend->batch_end(server->backend); + + root_sfc->is_dirty = false; + + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + if (server->planes[i].is_dirty) { + server->planes[i].is_dirty = false; + } + + if (server->planes[i].sfc && + server->planes[i].sfc->is_dirty) { + server->planes[i].sfc->is_dirty = false; + } + } } else { - vigs_server_update_display_cb(server, - server->root_sfc_ptr, - root_sfc->ws_sfc->width, - root_sfc->ws_sfc->height, - root_sfc->stride, - root_sfc->format); + /* + * No changes, no-op. + */ + vigs_server_update_display_end_cb(server, false); } out: @@ -493,6 +620,7 @@ static void vigs_server_dispatch_exit(void *user_data) static void vigs_server_dispatch_set_root_surface(void *user_data, vigsp_surface_id id, + bool scanout, vigsp_offset offset, vigsp_fence_seq fence_seq) { @@ -506,6 +634,7 @@ static void vigs_server_dispatch_set_root_surface(void *user_data, item->server = server; item->id = id; + item->scanout = scanout; item->offset = offset; work_queue_add_item(server->render_queue, &item->base); diff --git a/hw/vigs/vigs_server.h b/hw/vigs/vigs_server.h index fa56e42..9ced88d 100644 --- a/hw/vigs/vigs_server.h +++ b/hw/vigs/vigs_server.h @@ -31,6 +31,7 @@ #define _QEMU_VIGS_SERVER_H #include "vigs_types.h" +#include "vigs_plane.h" #include "winsys.h" #include @@ -92,7 +93,8 @@ struct vigs_server GHashTable *surfaces; struct vigs_surface *root_sfc; - uint8_t *root_sfc_ptr; + + struct vigs_plane planes[VIGS_MAX_PLANES]; QemuMutex capture_mutex; diff --git a/hw/vigs/vigs_surface.c b/hw/vigs/vigs_surface.c index cc3969b..d44d724 100644 --- a/hw/vigs/vigs_surface.c +++ b/hw/vigs/vigs_surface.c @@ -40,6 +40,7 @@ void vigs_surface_init(struct vigs_surface *sfc, ws_sfc->acquire(ws_sfc); sfc->ws_sfc = ws_sfc; sfc->backend = backend; + sfc->ptr = NULL; sfc->stride = stride; sfc->format = format; sfc->id = id; @@ -49,3 +50,8 @@ void vigs_surface_cleanup(struct vigs_surface *sfc) { sfc->ws_sfc->release(sfc->ws_sfc); } + +void vigs_surface_set_scanout(struct vigs_surface *sfc, uint8_t *ptr) +{ + sfc->ptr = ptr; +} diff --git a/hw/vigs/vigs_surface.h b/hw/vigs/vigs_surface.h index 95daa92..9867b0f 100644 --- a/hw/vigs/vigs_surface.h +++ b/hw/vigs/vigs_surface.h @@ -41,6 +41,11 @@ struct vigs_surface struct vigs_backend *backend; + /* + * Can be non-NULL only for root surface. + */ + uint8_t *ptr; + uint32_t stride; vigsp_surface_format format; vigsp_surface_id id; @@ -77,4 +82,6 @@ void vigs_surface_init(struct vigs_surface *sfc, void vigs_surface_cleanup(struct vigs_surface *sfc); +void vigs_surface_set_scanout(struct vigs_surface *sfc, uint8_t *ptr); + #endif diff --git a/hw/vigs/vigs_sw_backend.c b/hw/vigs/vigs_sw_backend.c index 4489e7d..53886b7 100644 --- a/hw/vigs/vigs_sw_backend.c +++ b/hw/vigs/vigs_sw_backend.c @@ -310,15 +310,33 @@ static struct vigs_surface *vigs_sw_backend_create_surface(struct vigs_backend * return &sw_sfc->base; } -static void vigs_sw_backend_read_pixels(struct vigs_surface *surface, - uint8_t *pixels, - vigs_read_pixels_cb cb, - void *user_data) +static void vigs_sw_backend_composite(struct vigs_surface *surface, + const struct vigs_plane *planes, + vigs_composite_start_cb start_cb, + vigs_composite_end_cb end_cb, + void *user_data) { - surface->read_pixels(surface, pixels); + struct vigs_sw_surface *sw_sfc = (struct vigs_sw_surface*)surface; + uint8_t *buff; + + /* + * TODO: Render planes. + */ + + buff = start_cb(user_data, surface->ws_sfc->width, surface->ws_sfc->height, + surface->stride, surface->format); + + if (surface->ptr) { + memcpy(buff, + surface->ptr, + surface->stride * surface->ws_sfc->height); + } else if (surface->is_dirty) { + memcpy(buff, + sw_sfc->data, + surface->stride * surface->ws_sfc->height); + } - cb(user_data, pixels, surface->ws_sfc->width, surface->ws_sfc->height, - surface->stride, surface->format); + end_cb(user_data, true); } static void vigs_sw_backend_batch_end(struct vigs_backend *backend) @@ -341,7 +359,7 @@ struct vigs_backend *vigs_sw_backend_create(void) backend->batch_start = &vigs_sw_backend_batch_start; backend->create_surface = &vigs_sw_backend_create_surface; - backend->read_pixels = &vigs_sw_backend_read_pixels; + backend->composite = &vigs_sw_backend_composite; backend->batch_end = &vigs_sw_backend_batch_end; backend->destroy = &vigs_sw_backend_destroy; -- 2.7.4