From 805cd22786fc0b337d9e9f7e9009da8196cfaa92 Mon Sep 17 00:00:00 2001 From: Stanislav Vorobiov Date: Mon, 11 Aug 2014 21:32:31 +0400 Subject: [PATCH] VIGS: introduce frontends We need to be able to use both old offscreen approach and new qt5-based onscreen approach. For that to work we separate vigs_server into 2 subclasses: onscreen and offscreen: onscreen subclass has almsot no-op update method, it schedules composite work on rendering thread and expects a client (qt5) to call 'graphic_hw_text_update' to trigger rendering. 'graphic_hw_text_update' is used just because it's the only custom GraphicHwOp available, it's basically a hack, but we have no choice, QEMU graphics console is not "direct rendering aware" offscreen subclass behaves as old code behaved, i.e. update method is a memcpy to QEMU surface and it also schedules composite work on rendering thread. offscreen subclass manages its own work_queue, once composite work is done it schedules another work on own work_queue that does glReadPixels, that's done in order not to stall the rendering thread. 'graphic_hw_text_update' is a no-op in this case Change-Id: I412cba41a123b2e8946112efa75b9ed21368ca84 Signed-off-by: Stanislav Vorobiov --- hw/vigs/Makefile.objs | 2 + hw/vigs/display.c | 4 + hw/vigs/vigs_backend.h | 8 +- hw/vigs/vigs_device.c | 58 ++++- hw/vigs/vigs_gl_backend.c | 345 +++++++++++++++++++-------- hw/vigs/vigs_gl_backend.h | 9 + hw/vigs/vigs_gl_backend_glx.c | 183 ++++++++++++-- hw/vigs/vigs_offscreen_server.c | 130 ++++++++++ hw/vigs/vigs_offscreen_server.h | 49 ++++ hw/vigs/vigs_onscreen_server.c | 179 ++++++++++++++ hw/vigs/vigs_onscreen_server.h | 55 +++++ hw/vigs/vigs_qt5.cpp | 10 + hw/vigs/vigs_server.c | 266 +++++++++++++-------- hw/vigs/vigs_server.h | 58 ++++- hw/vigs/vigs_sw_backend.c | 8 + tizen/src/display/qt5.c | 10 +- tizen/src/display/qt5_supplement.cpp | 14 +- tizen/src/display/qt5_supplement.h | 4 +- tizen/src/ui/displaywidget.cpp | 11 + tizen/src/ui/mainwindow.cpp | 68 ++---- tizen/src/ui/mainwindow.h | 12 +- 21 files changed, 1172 insertions(+), 311 deletions(-) create mode 100644 hw/vigs/vigs_offscreen_server.c create mode 100644 hw/vigs/vigs_offscreen_server.h create mode 100644 hw/vigs/vigs_onscreen_server.c create mode 100644 hw/vigs/vigs_onscreen_server.h diff --git a/hw/vigs/Makefile.objs b/hw/vigs/Makefile.objs index 572de90946..f4a364d63d 100644 --- a/hw/vigs/Makefile.objs +++ b/hw/vigs/Makefile.objs @@ -4,6 +4,8 @@ obj-y += vigs_device.o libs_softmmu += $(GLX_LIBS) obj-y += vigs_comm.o obj-y += vigs_server.o +obj-y += vigs_onscreen_server.o +obj-y += vigs_offscreen_server.o obj-y += vigs_backend.o obj-y += vigs_surface.o obj-y += vigs_utils.o diff --git a/hw/vigs/display.c b/hw/vigs/display.c index 1c55fed879..65dae3e45c 100644 --- a/hw/vigs/display.c +++ b/hw/vigs/display.c @@ -27,6 +27,10 @@ static void displayobject_complete(UserCreatable *obj, Error **errp) dobj->dpy = (Display*)vigs_qt5_display(); + if (!dobj->dpy) { + dobj->dpy = XOpenDisplay(0); + } + if (!dobj->dpy) { error_setg(errp, "Cannot open X display"); } diff --git a/hw/vigs/vigs_backend.h b/hw/vigs/vigs_backend.h index 0e0830a494..c300195f35 100644 --- a/hw/vigs/vigs_backend.h +++ b/hw/vigs/vigs_backend.h @@ -49,12 +49,16 @@ struct vigs_backend vigsp_surface_format /*format*/, vigsp_surface_id /*id*/); - void (*batch_end)(struct vigs_backend */*backend*/); - bool (*composite)(struct vigs_surface */*surface*/, const struct vigs_plane */*planes*/, + bool /*planes_dirty*/, uint8_t */*display_data*/); + void (*batch_end)(struct vigs_backend */*backend*/); + + bool (*display)(struct vigs_backend */*backend*/, + uint8_t */*display_data*/); + void (*destroy)(struct vigs_backend */*backend*/); }; diff --git a/hw/vigs/vigs_device.c b/hw/vigs/vigs_device.c index 464bd10479..61dbb3709f 100644 --- a/hw/vigs/vigs_device.c +++ b/hw/vigs/vigs_device.c @@ -28,10 +28,12 @@ */ #include "vigs_log.h" -#include "vigs_server.h" +#include "vigs_onscreen_server.h" +#include "vigs_offscreen_server.h" #include "vigs_backend.h" #include "vigs_regs.h" #include "vigs_fenceman.h" +#include "vigs_qt5.h" #include "display.h" #include "work_queue.h" #include "winsys.h" @@ -145,6 +147,29 @@ static void vigs_hw_update(void *opaque) } } +static void vigs_hw_display(void *opaque, console_ch_t *text) +{ + VIGSState *s = opaque; + DisplaySurface *ds = qemu_console_surface(s->con); + bool displayed; + + if (!surface_data(ds)) { + return; + } + + if (vigs_server_display(s->server, &displayed)) { + /* + * 'vigs_server_display' could have updated the surface, + * so fetch it again. + */ + ds = qemu_console_surface(s->con); + + dpy_gfx_update(s->con, 0, 0, surface_width(ds), surface_height(ds)); + } + + *text = displayed; +} + static void vigs_hw_invalidate(void *opaque) { VIGSState *s = opaque; @@ -269,7 +294,13 @@ static void vigs_io_write(void *opaque, hwaddr offset, static struct GraphicHwOps vigs_hw_ops = { .invalidate = vigs_hw_invalidate, - .gfx_update = vigs_hw_update + .gfx_update = vigs_hw_update, + /* + * Hack. use 'graphic_hw_text_update' to do displaying, but + * we have no choice, that's the only way to direct-to-window + * rendering from Qt5. + */ + .text_update = vigs_hw_display, }; static const MemoryRegionOps vigs_io_ops = @@ -421,12 +452,21 @@ static int vigs_device_init(PCIDevice *dev) goto fail; } - s->server = vigs_server_create(memory_region_get_ram_ptr(&s->vram_bar), - memory_region_get_ram_ptr(&s->ram_bar), - &vigs_dpy_ops, - s, - backend, - wqobj->wq); + if (vigs_qt5_display()) { + s->server = vigs_onscreen_server_create(memory_region_get_ram_ptr(&s->vram_bar), + memory_region_get_ram_ptr(&s->ram_bar), + &vigs_dpy_ops, + s, + backend, + wqobj->wq); + } else { + s->server = vigs_offscreen_server_create(memory_region_get_ram_ptr(&s->vram_bar), + memory_region_get_ram_ptr(&s->ram_bar), + &vigs_dpy_ops, + s, + backend, + wqobj->wq); + } if (!s->server) { goto fail; @@ -488,7 +528,7 @@ static void vigs_device_exit(PCIDevice *dev) { VIGSState *s = DO_UPCAST(VIGSState, dev, dev); - vigs_server_destroy(s->server); + s->server->destroy(s->server); qemu_bh_delete(s->fence_ack_bh); diff --git a/hw/vigs/vigs_gl_backend.c b/hw/vigs/vigs_gl_backend.c index 8b527b9975..32e7df7629 100644 --- a/hw/vigs/vigs_gl_backend.c +++ b/hw/vigs/vigs_gl_backend.c @@ -34,6 +34,7 @@ #include "vigs_log.h" #include "vigs_utils.h" #include "vigs_ref.h" +#include "vigs_qt5.h" #include "winsys_gl.h" #include @@ -101,13 +102,6 @@ struct vigs_gl_surface * Ortho matrix for this surface. */ GLfloat ortho[16]; - - /* - * Rotation angle and corresponding ortho, - * for display. - */ - int dpy_angle; - GLfloat dpy_ortho[16]; }; static __inline struct vigs_winsys_gl_surface @@ -345,8 +339,6 @@ static GLuint vigs_gl_backend_alloc_tmp_texture(void *user_data, */ backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); backend->TexImage2D(GL_TEXTURE_2D, 0, tex_internalformat, width, height, 0, @@ -734,8 +726,6 @@ static bool vigs_winsys_gl_surface_create_texture(struct vigs_winsys_gl_surface */ ws_sfc->backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ws_sfc->backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - ws_sfc->backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ws_sfc->backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ws_sfc->backend->TexImage2D(GL_TEXTURE_2D, 0, ws_sfc->tex_internalformat, ws_sfc->base.base.width, ws_sfc->base.base.height, 0, @@ -797,6 +787,55 @@ static bool vigs_gl_surface_setup_framebuffer(struct vigs_gl_surface *gl_sfc, return true; } +static bool vigs_gl_update_dpy_texture(struct vigs_gl_backend *gl_backend, + uint32_t width, uint32_t height, + GLfloat ortho[16]) +{ + GLuint cur_tex = 0; + + gl_backend->GetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&cur_tex); + + if (gl_backend->dpy_tex) { + if ((gl_backend->dpy_tex_width == width) && + (gl_backend->dpy_tex_height == height)) { + return true; + } + gl_backend->BindTexture(GL_TEXTURE_2D, gl_backend->dpy_tex); + } else { + GLuint tmp_tex = 0; + + gl_backend->GenTextures(1, &tmp_tex); + + if (!tmp_tex) { + return false; + } + + gl_backend->dpy_tex = tmp_tex; + + gl_backend->BindTexture(GL_TEXTURE_2D, tmp_tex); + + gl_backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + gl_backend->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + width, height, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + NULL); + + gl_backend->BindTexture(GL_TEXTURE_2D, cur_tex); + + memcpy(gl_backend->dpy_tex_ortho, + ortho, + sizeof(gl_backend->dpy_tex_ortho)); + gl_backend->dpy_tex_width = width; + gl_backend->dpy_tex_height = height; + + return true; +} + /* * @} */ @@ -1420,6 +1459,7 @@ static struct vigs_surface *vigs_gl_backend_create_surface(struct vigs_backend * static bool vigs_gl_backend_composite(struct vigs_surface *surface, const struct vigs_plane *planes, + bool planes_dirty, uint8_t *display_data) { struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)surface->backend; @@ -1429,30 +1469,32 @@ static bool vigs_gl_backend_composite(struct vigs_surface *surface, GLfloat *vert_coords; GLfloat *tex_coords; const struct vigs_plane *sorted_planes[VIGS_MAX_PLANES]; - bool scale; - GLfloat *ortho; VIGS_LOG_TRACE("enter"); - if (!ws_root_sfc->tex) { - VIGS_LOG_WARN("compositing garbage (root surface) ???"); - return false; - } - - if (!gl_backend->is_gl_2 && !gl_backend->dpy_vao) { - gl_backend->GenVertexArrays(1, &gl_backend->dpy_vao); - - if (!gl_backend->dpy_vao) { - VIGS_LOG_CRITICAL("cannot create VAO"); - exit(1); + if (surface->ptr) { + if (!vigs_qt5_display() && !planes_dirty) { + memcpy(display_data, + surface->ptr, + surface->stride * surface->ws_sfc->height); + return true; + } + } else { + if (!ws_root_sfc->tex) { + VIGS_LOG_WARN("compositing garbage (root surface) ???"); } - gl_backend->BindVertexArray(gl_backend->dpy_vao); + if (!vigs_winsys_gl_surface_create_texture(ws_root_sfc)) { + goto out; + } } - gl_backend->BindBuffer(GL_ARRAY_BUFFER, gl_backend->dpy_vbo); - gl_backend->Disable(GL_DEPTH_TEST); - gl_backend->Disable(GL_BLEND); + if (!vigs_gl_update_dpy_texture(gl_backend, + surface->ws_sfc->width, + surface->ws_sfc->height, + gl_root_sfc->ortho)) { + goto out; + } for (i = 0; i < VIGS_MAX_PLANES; ++i) { struct vigs_gl_surface *gl_sfc; @@ -1467,54 +1509,25 @@ static bool vigs_gl_backend_composite(struct vigs_surface *surface, if (!ws_sfc->tex) { VIGS_LOG_WARN("compositing garbage (plane %u) ???", i); - return false; } - } - if (qt5_window_angle == 0) { - ortho = gl_root_sfc->ortho; - } else { - if (gl_root_sfc->dpy_angle != qt5_window_angle) { - vigs_gl_rotate_ortho(gl_root_sfc->ortho, - (float)(360.0f - qt5_window_angle) * M_PI / 180.0f, - gl_root_sfc->dpy_ortho); - gl_root_sfc->dpy_angle = qt5_window_angle; + if (!vigs_winsys_gl_surface_create_texture(ws_sfc)) { + goto out; } - ortho = gl_root_sfc->dpy_ortho; } - scale = (qt5_window_width != ws_root_sfc->base.base.width) || - (qt5_window_height != ws_root_sfc->base.base.height); - - gl_backend->Viewport(0, 0, - qt5_window_width, - qt5_window_height); - - if (scale) { - float texSize[2]; - - gl_backend->UseProgram(gl_backend->dpy_scale_prog_id); - gl_backend->UniformMatrix4fv(gl_backend->dpy_scale_prog_proj_loc, 1, GL_FALSE, - ortho); - - texSize[0] = ws_root_sfc->base.base.width; - texSize[1] = ws_root_sfc->base.base.height; - - gl_backend->Uniform2fv(gl_backend->dpy_scale_prog_texSize_loc, 1, texSize); - } else { - gl_backend->UseProgram(gl_backend->dpy_tex_prog_id); - gl_backend->UniformMatrix4fv(gl_backend->dpy_tex_prog_proj_loc, 1, GL_FALSE, - ortho); + if (!vigs_gl_surface_setup_framebuffer(gl_root_sfc, + gl_backend->tex_prog_id, + gl_backend->tex_prog_proj_loc)) { + goto out; } - gl_backend->BindTexture(GL_TEXTURE_2D, ws_root_sfc->tex); - - vigs_vector_resize(&gl_backend->dpy_v1, 0); - vigs_vector_resize(&gl_backend->dpy_v2, 0); + vigs_vector_resize(&gl_backend->v1, 0); + vigs_vector_resize(&gl_backend->v2, 0); - vert_coords = vigs_vector_append(&gl_backend->dpy_v1, + vert_coords = vigs_vector_append(&gl_backend->v1, (12 * sizeof(GLfloat))); - tex_coords = vigs_vector_append(&gl_backend->dpy_v2, + tex_coords = vigs_vector_append(&gl_backend->v2, (12 * sizeof(GLfloat))); if (surface->ptr) { @@ -1528,6 +1541,8 @@ static bool vigs_gl_backend_composite(struct vigs_surface *surface, gl_backend->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0); gl_backend->PixelStorei(GL_UNPACK_SKIP_ROWS, 0); + gl_backend->BindTexture(GL_TEXTURE_2D, gl_backend->dpy_tex); + gl_backend->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ws_root_sfc->base.base.width, ws_root_sfc->base.base.height, @@ -1536,16 +1551,24 @@ static bool vigs_gl_backend_composite(struct vigs_surface *surface, surface->ptr); } - vert_coords[6] = vert_coords[0] = 0; - vert_coords[7] = 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[8] = vert_coords[4] = ws_root_sfc->base.base.width; - vert_coords[9] = vert_coords[5] = 0; - vert_coords[10] = 0; - vert_coords[11] = 0; + gl_backend->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gl_backend->dpy_tex, 0); + + if (!surface->ptr) { + /* + * If root surface is not scanout then we must render + * it. + */ + + vert_coords[6] = vert_coords[0] = 0; + vert_coords[7] = 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[8] = vert_coords[4] = ws_root_sfc->base.base.width; + vert_coords[9] = vert_coords[5] = 0; + vert_coords[10] = 0; + vert_coords[11] = 0; - if (surface->ptr) { tex_coords[6] = tex_coords[0] = 0; tex_coords[7] = tex_coords[1] = 0; tex_coords[2] = 1; @@ -1554,21 +1577,10 @@ static bool vigs_gl_backend_composite(struct vigs_surface *surface, tex_coords[9] = tex_coords[5] = 1; tex_coords[10] = 0; tex_coords[11] = 1; - } else { - tex_coords[6] = tex_coords[0] = 0; - tex_coords[7] = tex_coords[1] = 1; - tex_coords[2] = 1; - tex_coords[3] = 1; - tex_coords[8] = tex_coords[4] = 1; - tex_coords[9] = tex_coords[5] = 0; - tex_coords[10] = 0; - tex_coords[11] = 0; - } - if (scale) { - vigs_gl_draw_dpy_scale_prog(gl_backend, 6); - } else { - vigs_gl_draw_dpy_tex_prog(gl_backend, 6); + gl_backend->BindTexture(GL_TEXTURE_2D, ws_root_sfc->tex); + + vigs_gl_draw_tex_prog(gl_backend, 6); } /* @@ -1615,39 +1627,160 @@ static bool vigs_gl_backend_composite(struct vigs_surface *surface, vert_coords[11] = plane->dst_y + (int)plane->dst_size.h; tex_coords[6] = tex_coords[0] = (GLfloat)plane->src_rect.pos.x / src_w; - tex_coords[7] = tex_coords[1] = (GLfloat)(src_h - (plane->src_rect.pos.y + plane->src_rect.size.h)) / src_h; + tex_coords[7] = 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 + plane->src_rect.size.h)) / src_h; + tex_coords[3] = (GLfloat)(src_h - plane->src_rect.pos.y) / src_h; tex_coords[8] = tex_coords[4] = (GLfloat)(plane->src_rect.pos.x + plane->src_rect.size.w) / src_w; - tex_coords[9] = tex_coords[5] = (GLfloat)(src_h - plane->src_rect.pos.y) / src_h; + tex_coords[9] = tex_coords[5] = (GLfloat)(src_h - (plane->src_rect.pos.y + plane->src_rect.size.h)) / src_h; tex_coords[10] = (GLfloat)plane->src_rect.pos.x / src_w; - tex_coords[11] = (GLfloat)(src_h - plane->src_rect.pos.y) / src_h; + tex_coords[11] = (GLfloat)(src_h - (plane->src_rect.pos.y + plane->src_rect.size.h)) / src_h; gl_backend->BindTexture(GL_TEXTURE_2D, ws_sfc->tex); + vigs_gl_draw_tex_prog(gl_backend, 6); + } + +out: + gl_backend->BindFramebuffer(GL_FRAMEBUFFER, 0); + + return false; +} + +static void vigs_gl_backend_batch_end(struct vigs_backend *backend) +{ + struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)backend; + + gl_backend->Finish(); + gl_backend->make_current(gl_backend, false); +} + +static bool vigs_gl_backend_display(struct vigs_backend *backend, + uint8_t *display_data) +{ + struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)backend; + + VIGS_LOG_TRACE("enter"); + + if (vigs_qt5_display()) { + GLfloat *vert_coords; + GLfloat *tex_coords; + bool scale; + GLfloat rotated_ortho[16]; + GLfloat *ortho; + + if (!gl_backend->is_gl_2 && !gl_backend->dpy_vao) { + gl_backend->GenVertexArrays(1, &gl_backend->dpy_vao); + + if (!gl_backend->dpy_vao) { + VIGS_LOG_CRITICAL("cannot create VAO"); + exit(1); + } + + gl_backend->BindVertexArray(gl_backend->dpy_vao); + } + + gl_backend->BindBuffer(GL_ARRAY_BUFFER, gl_backend->dpy_vbo); + gl_backend->Disable(GL_DEPTH_TEST); + gl_backend->Disable(GL_BLEND); + + if (qt5_window_angle == 0) { + ortho = gl_backend->dpy_tex_ortho; + } else { + vigs_gl_rotate_ortho(gl_backend->dpy_tex_ortho, + (float)(360.0f - qt5_window_angle) * M_PI / 180.0f, + rotated_ortho); + ortho = rotated_ortho; + } + + scale = (qt5_window_width != gl_backend->dpy_tex_width) || + (qt5_window_height != gl_backend->dpy_tex_height); + + gl_backend->Viewport(0, 0, + qt5_window_width, + qt5_window_height); + if (scale) { float texSize[2]; - texSize[0] = src_w; - texSize[1] = src_h; + gl_backend->UseProgram(gl_backend->dpy_scale_prog_id); + gl_backend->UniformMatrix4fv(gl_backend->dpy_scale_prog_proj_loc, 1, GL_FALSE, + ortho); + + texSize[0] = gl_backend->dpy_tex_width; + texSize[1] = gl_backend->dpy_tex_height; gl_backend->Uniform2fv(gl_backend->dpy_scale_prog_texSize_loc, 1, texSize); + } else { + gl_backend->UseProgram(gl_backend->dpy_tex_prog_id); + gl_backend->UniformMatrix4fv(gl_backend->dpy_tex_prog_proj_loc, 1, GL_FALSE, + ortho); + } + gl_backend->BindTexture(GL_TEXTURE_2D, gl_backend->dpy_tex); + + vigs_vector_resize(&gl_backend->dpy_v1, 0); + vigs_vector_resize(&gl_backend->dpy_v2, 0); + + vert_coords = vigs_vector_append(&gl_backend->dpy_v1, + (12 * sizeof(GLfloat))); + tex_coords = vigs_vector_append(&gl_backend->dpy_v2, + (12 * sizeof(GLfloat))); + + vert_coords[6] = vert_coords[0] = 0; + vert_coords[7] = vert_coords[1] = gl_backend->dpy_tex_height; + vert_coords[2] = gl_backend->dpy_tex_width; + vert_coords[3] = gl_backend->dpy_tex_height; + vert_coords[8] = vert_coords[4] = gl_backend->dpy_tex_width; + vert_coords[9] = vert_coords[5] = 0; + vert_coords[10] = 0; + vert_coords[11] = 0; + + tex_coords[6] = tex_coords[0] = 0; + tex_coords[7] = tex_coords[1] = 0; + tex_coords[2] = 1; + tex_coords[3] = 0; + tex_coords[8] = tex_coords[4] = 1; + tex_coords[9] = tex_coords[5] = 1; + tex_coords[10] = 0; + tex_coords[11] = 1; + + if (scale) { vigs_gl_draw_dpy_scale_prog(gl_backend, 6); } else { vigs_gl_draw_dpy_tex_prog(gl_backend, 6); } - } - return false; -} + gl_backend->Finish(); -static void vigs_gl_backend_batch_end(struct vigs_backend *backend) -{ - struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)backend; + return true; + } else { + if (gl_backend->read_pixels_make_current(gl_backend, true)) { + if (!gl_backend->dpy_fb) { + gl_backend->GenFramebuffers(1, &gl_backend->dpy_fb); - gl_backend->Finish(); - gl_backend->make_current(gl_backend, false); + if (!gl_backend->dpy_fb) { + VIGS_LOG_CRITICAL("cannot create FB"); + exit(1); + } + + gl_backend->BindFramebuffer(GL_FRAMEBUFFER, gl_backend->dpy_fb); + } + + gl_backend->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gl_backend->dpy_tex, 0); + + gl_backend->ReadPixels(0, 0, gl_backend->dpy_tex_width, + gl_backend->dpy_tex_height, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + display_data); + + gl_backend->read_pixels_make_current(gl_backend, false); + + return true; + } else { + return false; + } + } } bool vigs_gl_backend_init(struct vigs_gl_backend *gl_backend) @@ -1817,6 +1950,7 @@ bool vigs_gl_backend_init(struct vigs_gl_backend *gl_backend) gl_backend->base.create_surface = &vigs_gl_backend_create_surface; gl_backend->base.composite = &vigs_gl_backend_composite; gl_backend->base.batch_end = &vigs_gl_backend_batch_end; + gl_backend->base.display = &vigs_gl_backend_display; gl_backend->make_current(gl_backend, false); @@ -1836,6 +1970,9 @@ fail: void vigs_gl_backend_cleanup(struct vigs_gl_backend *gl_backend) { if (gl_backend->make_current(gl_backend, true)) { + if (gl_backend->dpy_tex) { + gl_backend->DeleteTextures(1, &gl_backend->dpy_tex); + } gl_backend->DeleteBuffers(1, &gl_backend->dpy_vbo); gl_backend->DetachShader(gl_backend->dpy_scale_prog_id, gl_backend->dpy_scale_prog_vs_id); diff --git a/hw/vigs/vigs_gl_backend.h b/hw/vigs/vigs_gl_backend.h index 7f900f9e7a..450d1a22df 100644 --- a/hw/vigs/vigs_gl_backend.h +++ b/hw/vigs/vigs_gl_backend.h @@ -52,6 +52,9 @@ struct vigs_gl_backend bool (*make_current)(struct vigs_gl_backend */*gl_backend*/, bool /*enable*/); + bool (*read_pixels_make_current)(struct vigs_gl_backend */*gl_backend*/, + bool /*enable*/); + /* * Mandatory GL functions and extensions. * @{ @@ -210,6 +213,12 @@ struct vigs_gl_backend GLuint dpy_vbo; uint32_t dpy_vbo_size; + GLuint dpy_tex; + GLuint dpy_fb; + GLfloat dpy_tex_ortho[16]; + uint32_t dpy_tex_width; + uint32_t dpy_tex_height; + /* * @} */ diff --git a/hw/vigs/vigs_gl_backend_glx.c b/hw/vigs/vigs_gl_backend_glx.c index 64d8324ed4..23a4857956 100644 --- a/hw/vigs/vigs_gl_backend_glx.c +++ b/hw/vigs/vigs_gl_backend_glx.c @@ -94,6 +94,8 @@ struct vigs_gl_backend_glx Display *dpy; GLXPbuffer sfc; GLXContext ctx; + GLXPbuffer read_pixels_sfc; + GLXContext read_pixels_ctx; }; static bool vigs_gl_backend_glx_check_gl_version(struct vigs_gl_backend_glx *gl_backend_glx, @@ -192,27 +194,50 @@ out: static GLXFBConfig vigs_gl_backend_glx_get_config(struct vigs_gl_backend_glx *gl_backend_glx) { - int config_attribs[] = - { - GLX_FBCONFIG_ID, 0, - None - }; int n = 0; GLXFBConfig *glx_configs; GLXFBConfig best_config = NULL; - if (gl_backend_glx->glXQueryContext(gl_backend_glx->dpy, - gl_backend_glx->ctx, - GLX_FBCONFIG_ID, - &config_attribs[1]) != Success) { - VIGS_LOG_CRITICAL("Unable to get context's GLX config"); - return NULL; - } + if (vigs_qt5_display()) { + int config_attribs[] = + { + GLX_FBCONFIG_ID, 0, + None + }; + + if (gl_backend_glx->glXQueryContext(gl_backend_glx->dpy, + gl_backend_glx->ctx, + GLX_FBCONFIG_ID, + &config_attribs[1]) != Success) { + VIGS_LOG_CRITICAL("Unable to get context's GLX config"); + return NULL; + } - glx_configs = gl_backend_glx->glXChooseFBConfig(gl_backend_glx->dpy, - DefaultScreen(gl_backend_glx->dpy), - config_attribs, - &n); + glx_configs = gl_backend_glx->glXChooseFBConfig(gl_backend_glx->dpy, + DefaultScreen(gl_backend_glx->dpy), + config_attribs, + &n); + } else { + int config_attribs[] = + { + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_BUFFER_SIZE, 32, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, + None + }; + + glx_configs = gl_backend_glx->glXChooseFBConfig(gl_backend_glx->dpy, + DefaultScreen(gl_backend_glx->dpy), + config_attribs, + &n); + } if (n > 0) { int tmp; @@ -266,6 +291,42 @@ static bool vigs_gl_backend_glx_create_surface(struct vigs_gl_backend_glx *gl_ba return true; } +static bool vigs_gl_backend_glx_create_context(struct vigs_gl_backend_glx *gl_backend_glx, + GLXFBConfig config, + GLXContext share_ctx, + GLXContext *ctx) +{ + int attribs[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 1, + GLX_RENDER_TYPE, GLX_RGBA_TYPE, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + None + }; + + if (gl_backend_glx->base.is_gl_2) { + *ctx = gl_backend_glx->glXCreateNewContext(gl_backend_glx->dpy, + config, + GLX_RGBA_TYPE, + share_ctx, + True); + } else { + *ctx = gl_backend_glx->glXCreateContextAttribsARB(gl_backend_glx->dpy, + config, + share_ctx, + True, + attribs); + } + + if (!*ctx) { + VIGS_LOG_CRITICAL("glXCreateContextAttribsARB/glXCreateNewContext failed"); + return false; + } + + return true; +} + static bool vigs_gl_backend_glx_has_current(struct vigs_gl_backend *gl_backend) { struct vigs_gl_backend_glx *gl_backend_glx = @@ -294,12 +355,45 @@ static bool vigs_gl_backend_glx_make_current(struct vigs_gl_backend *gl_backend, return true; } +static bool vigs_gl_backend_glx_read_pixels_make_current(struct vigs_gl_backend *gl_backend, + bool enable) +{ + struct vigs_gl_backend_glx *gl_backend_glx = + (struct vigs_gl_backend_glx*)gl_backend; + Bool ret; + + ret = gl_backend_glx->glXMakeContextCurrent(gl_backend_glx->dpy, + (enable ? gl_backend_glx->read_pixels_sfc : None), + (enable ? gl_backend_glx->read_pixels_sfc : None), + (enable ? gl_backend_glx->read_pixels_ctx : NULL)); + + if (!ret) { + VIGS_LOG_CRITICAL("glXMakeContextCurrent failed"); + return false; + } + + return true; +} + static void vigs_gl_backend_glx_destroy(struct vigs_backend *backend) { struct vigs_gl_backend_glx *gl_backend_glx = (struct vigs_gl_backend_glx*)backend; vigs_gl_backend_cleanup(&gl_backend_glx->base); + if (gl_backend_glx->read_pixels_sfc) { + gl_backend_glx->glXDestroyPbuffer(gl_backend_glx->dpy, + gl_backend_glx->read_pixels_sfc); + } + if (!vigs_qt5_display() && gl_backend_glx->ctx) { + gl_backend_glx->glXDestroyContext(gl_backend_glx->dpy, + gl_backend_glx->ctx); + } + if (gl_backend_glx->read_pixels_ctx) { + gl_backend_glx->glXDestroyContext(gl_backend_glx->dpy, + gl_backend_glx->read_pixels_ctx); + } + gl_backend_glx->glXDestroyPbuffer(gl_backend_glx->dpy, gl_backend_glx->sfc); @@ -440,17 +534,45 @@ struct vigs_backend *vigs_gl_backend_create(void *display) VIGS_GL_GET_PROC(DeleteVertexArrays, glDeleteVertexArrays); } - gl_backend_glx->ctx = - (GLXContext)vigs_qt5_gl_context_create(gl_backend_glx->base.is_gl_2); + if (vigs_qt5_display()) { + gl_backend_glx->ctx = + (GLXContext)vigs_qt5_gl_context_create(gl_backend_glx->base.is_gl_2); - if (!gl_backend_glx->ctx) { - goto fail2; - } + if (!gl_backend_glx->ctx) { + goto fail2; + } - config = vigs_gl_backend_glx_get_config(gl_backend_glx); + config = vigs_gl_backend_glx_get_config(gl_backend_glx); - if (!config) { - goto fail2; + if (!config) { + goto fail2; + } + } else { + config = vigs_gl_backend_glx_get_config(gl_backend_glx); + + if (!config) { + goto fail2; + } + + if (!vigs_gl_backend_glx_create_surface(gl_backend_glx, + config, + &gl_backend_glx->read_pixels_sfc)) { + goto fail2; + } + + if (!vigs_gl_backend_glx_create_context(gl_backend_glx, + config, + NULL, + &gl_backend_glx->ctx)) { + goto fail2; + } + + if (!vigs_gl_backend_glx_create_context(gl_backend_glx, + config, + gl_backend_glx->ctx, + &gl_backend_glx->read_pixels_ctx)) { + goto fail2; + } } if (!vigs_gl_backend_glx_create_surface(gl_backend_glx, @@ -462,6 +584,7 @@ struct vigs_backend *vigs_gl_backend_create(void *display) gl_backend_glx->base.base.destroy = &vigs_gl_backend_glx_destroy; gl_backend_glx->base.has_current = &vigs_gl_backend_glx_has_current; gl_backend_glx->base.make_current = &vigs_gl_backend_glx_make_current; + gl_backend_glx->base.read_pixels_make_current = &vigs_gl_backend_glx_read_pixels_make_current; gl_backend_glx->base.ws_info.context = gl_backend_glx->ctx; if (!vigs_gl_backend_init(&gl_backend_glx->base)) { @@ -476,6 +599,18 @@ fail3: gl_backend_glx->glXDestroyPbuffer(gl_backend_glx->dpy, gl_backend_glx->sfc); fail2: + if (gl_backend_glx->read_pixels_sfc) { + gl_backend_glx->glXDestroyPbuffer(gl_backend_glx->dpy, + gl_backend_glx->read_pixels_sfc); + } + if (!vigs_qt5_display() && gl_backend_glx->ctx) { + gl_backend_glx->glXDestroyContext(gl_backend_glx->dpy, + gl_backend_glx->ctx); + } + if (gl_backend_glx->read_pixels_ctx) { + gl_backend_glx->glXDestroyContext(gl_backend_glx->dpy, + gl_backend_glx->read_pixels_ctx); + } dlclose(gl_backend_glx->handle); fail1: vigs_backend_cleanup(&gl_backend_glx->base.base); diff --git a/hw/vigs/vigs_offscreen_server.c b/hw/vigs/vigs_offscreen_server.c new file mode 100644 index 0000000000..28b3ea96a3 --- /dev/null +++ b/hw/vigs/vigs_offscreen_server.c @@ -0,0 +1,130 @@ +/* + * 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 + * + */ + +#include "vigs_offscreen_server.h" +#include "vigs_backend.h" +#include "work_queue.h" + +struct vigs_display_work_item +{ + struct work_queue_item base; + + struct vigs_server *server; +}; + +static void vigs_offscreen_server_display_work(struct work_queue_item *wq_item) +{ + struct vigs_display_work_item *item = (struct vigs_display_work_item*)wq_item; + struct vigs_server *server = item->server; + int index = (&server->capture_buffers[0] == server->captured) ? 1 : 0; + + vigs_server_finish_update_display( + server, server->backend->display(server->backend, + server->capture_buffers[index].data)); + + g_free(item); +} + +static bool vigs_offscreen_server_begin_update(struct vigs_server *server, + bool is_capturing, + bool force) +{ + return vigs_server_process_captured(server, force); +} + +static void vigs_offscreen_server_finish_update(struct vigs_server *server, + bool composited, + bool dirty) +{ + if (dirty) { + vigs_server_finish_update_display(server, true); + } else if (composited) { + struct vigs_offscreen_server *offscreen_server = + (struct vigs_offscreen_server*)server; + + struct vigs_display_work_item *item; + + item = g_malloc(sizeof(*item)); + + work_queue_item_init(&item->base, &vigs_offscreen_server_display_work); + + item->server = server; + + work_queue_add_item(offscreen_server->display_queue, &item->base); + } else { + vigs_server_finish_update_display(server, false); + } +} + +static bool vigs_offscreen_server_display(struct vigs_server *server, + bool *displayed) +{ + *displayed = false; + return false; +} + +static void vigs_offscreen_server_destroy(struct vigs_server *server) +{ + struct vigs_offscreen_server *offscreen_server = + (struct vigs_offscreen_server*)server; + + work_queue_destroy(offscreen_server->display_queue); + + vigs_server_cleanup(server); + + g_free(server); +} + +struct vigs_server *vigs_offscreen_server_create(uint8_t *vram_ptr, + uint8_t *ram_ptr, + struct vigs_display_ops *display_ops, + void *display_user_data, + struct vigs_backend *backend, + struct work_queue *render_queue) +{ + struct vigs_offscreen_server *server = NULL; + + server = g_malloc0(sizeof(*server)); + + if (!vigs_server_init(&server->base, vram_ptr, ram_ptr, + display_ops, display_user_data, backend, + render_queue)) { + g_free(server); + return NULL; + } + + server->display_queue = work_queue_create("display_queue"); + + server->base.begin_update = &vigs_offscreen_server_begin_update; + server->base.finish_update = &vigs_offscreen_server_finish_update; + server->base.display = &vigs_offscreen_server_display; + server->base.destroy = &vigs_offscreen_server_destroy; + + return &server->base; +} diff --git a/hw/vigs/vigs_offscreen_server.h b/hw/vigs/vigs_offscreen_server.h new file mode 100644 index 0000000000..1cfa3fd1fc --- /dev/null +++ b/hw/vigs/vigs_offscreen_server.h @@ -0,0 +1,49 @@ +/* + * 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_OFFSCREEN_SERVER_H +#define _QEMU_VIGS_OFFSCREEN_SERVER_H + +#include "vigs_server.h" + +struct vigs_offscreen_server +{ + struct vigs_server base; + + struct work_queue *display_queue; +}; + +struct vigs_server *vigs_offscreen_server_create(uint8_t *vram_ptr, + uint8_t *ram_ptr, + struct vigs_display_ops *display_ops, + void *display_user_data, + struct vigs_backend *backend, + struct work_queue *render_queue); + +#endif diff --git a/hw/vigs/vigs_onscreen_server.c b/hw/vigs/vigs_onscreen_server.c new file mode 100644 index 0000000000..984fd1bed2 --- /dev/null +++ b/hw/vigs/vigs_onscreen_server.c @@ -0,0 +1,179 @@ +/* + * 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 + * + */ + +#include "vigs_onscreen_server.h" +#include "vigs_backend.h" + +static bool vigs_onscreen_server_begin_update(struct vigs_server *server, + bool is_capturing, + bool force) +{ + struct vigs_onscreen_server *onscreen_server = + (struct vigs_onscreen_server*)server; + + qemu_mutex_lock(&onscreen_server->mutex); + + if (force) { + ++onscreen_server->invalidate_cnt; + } + + if (is_capturing) { + /* + * If capturing is in progress then just skip this update. + */ + goto out; + } + + onscreen_server->updated = false; + onscreen_server->composited = false; + onscreen_server->dirty = false; + +out: + qemu_mutex_unlock(&onscreen_server->mutex); + + return false; +} + +static void vigs_onscreen_server_finish_update(struct vigs_server *server, + bool composited, + bool dirty) +{ + struct vigs_onscreen_server *onscreen_server = + (struct vigs_onscreen_server*)server; + + qemu_mutex_lock(&onscreen_server->mutex); + + onscreen_server->updated = true; + onscreen_server->composited = composited; + onscreen_server->dirty = dirty; + + qemu_mutex_unlock(&onscreen_server->mutex); + + qemu_cond_signal(&onscreen_server->cond); +} + +static bool vigs_onscreen_server_display(struct vigs_server *server, + bool *displayed) +{ + struct vigs_onscreen_server *onscreen_server = + (struct vigs_onscreen_server*)server; + bool force = false; + + qemu_mutex_lock(&onscreen_server->mutex); + + /* + * Wait until rendering is finished. + */ + while (!onscreen_server->updated) { + qemu_cond_wait(&onscreen_server->cond, &onscreen_server->mutex); + } + + if (onscreen_server->invalidate_cnt > 0) { + --onscreen_server->invalidate_cnt; + force = true; + } + + onscreen_server->updated = false; + + qemu_mutex_unlock(&onscreen_server->mutex); + + *displayed = true; + + if (onscreen_server->dirty) { + /* + * Software composition took place, finish ASAP and + * process captured data. + */ + vigs_server_finish_update_display(server, true); + return vigs_server_process_captured(server, force); + } else if (onscreen_server->composited) { + /* + * Hw composition took place, display the content. + */ + server->backend->display(server->backend, NULL); + } else if (force) { + /* + * Nothing happened, but if it's a forced display, then + * we should try to display hw stuff first, if there isn't any + * then display sw stuff. + */ + if (!server->backend->display(server->backend, NULL)) { + vigs_server_finish_update_display(server, false); + return vigs_server_process_captured(server, force); + } + } else { + *displayed = false; + } + + vigs_server_finish_update_display(server, false); + + return false; +} + +static void vigs_onscreen_server_destroy(struct vigs_server *server) +{ + struct vigs_onscreen_server *onscreen_server = + (struct vigs_onscreen_server*)server; + + qemu_cond_destroy(&onscreen_server->cond); + qemu_mutex_destroy(&onscreen_server->mutex); + + vigs_server_cleanup(server); + + g_free(server); +} + +struct vigs_server *vigs_onscreen_server_create(uint8_t *vram_ptr, + uint8_t *ram_ptr, + struct vigs_display_ops *display_ops, + void *display_user_data, + struct vigs_backend *backend, + struct work_queue *render_queue) +{ + struct vigs_onscreen_server *server = NULL; + + server = g_malloc0(sizeof(*server)); + + if (!vigs_server_init(&server->base, vram_ptr, ram_ptr, + display_ops, display_user_data, backend, + render_queue)) { + g_free(server); + return NULL; + } + + qemu_mutex_init(&server->mutex); + qemu_cond_init(&server->cond); + + server->base.begin_update = &vigs_onscreen_server_begin_update; + server->base.finish_update = &vigs_onscreen_server_finish_update; + server->base.display = &vigs_onscreen_server_display; + server->base.destroy = &vigs_onscreen_server_destroy; + + return &server->base; +} diff --git a/hw/vigs/vigs_onscreen_server.h b/hw/vigs/vigs_onscreen_server.h new file mode 100644 index 0000000000..a3b443332c --- /dev/null +++ b/hw/vigs/vigs_onscreen_server.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_ONSCREEN_SERVER_H +#define _QEMU_VIGS_ONSCREEN_SERVER_H + +#include "vigs_server.h" + +struct vigs_onscreen_server +{ + struct vigs_server base; + + QemuMutex mutex; + QemuCond cond; + bool updated; + bool composited; + bool dirty; + + int invalidate_cnt; +}; + +struct vigs_server *vigs_onscreen_server_create(uint8_t *vram_ptr, + uint8_t *ram_ptr, + struct vigs_display_ops *display_ops, + void *display_user_data, + struct vigs_backend *backend, + struct work_queue *render_queue); + +#endif diff --git a/hw/vigs/vigs_qt5.cpp b/hw/vigs/vigs_qt5.cpp index 6a05acb4e6..fefca0d7d7 100644 --- a/hw/vigs/vigs_qt5.cpp +++ b/hw/vigs/vigs_qt5.cpp @@ -34,11 +34,16 @@ #include #include +extern QApplication *qt5App; extern QOpenGLContext *qt5GLContext; extern QSurfaceFormat qt5GLFormat; void *vigs_qt5_display(void) { + if (!qt5App) { + return NULL; + } + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); @@ -48,6 +53,11 @@ void *vigs_qt5_display(void) void *vigs_qt5_gl_context_create(bool is_gl2) { + if (!qt5App) { + fprintf(stderr, "QT5 not enabled!\n"); + return NULL; + } + if (qt5GLContext) { fprintf(stderr, "QT5 GL context already created!\n"); return NULL; diff --git a/hw/vigs/vigs_server.c b/hw/vigs/vigs_server.c index 6299299ed4..082506c4d3 100644 --- a/hw/vigs/vigs_server.c +++ b/hw/vigs/vigs_server.c @@ -178,12 +178,8 @@ static void vigs_server_dispatch_destroy_surface(void *user_data, return; } - qemu_mutex_lock(&server->dpy_mutex); - vigs_server_unuse_surface(server, sfc); - qemu_mutex_unlock(&server->dpy_mutex); - g_hash_table_remove(server->surfaces, GUINT_TO_POINTER(id)); VIGS_LOG_TRACE("num_surfaces = %u", g_hash_table_size(server->surfaces)); @@ -326,20 +322,18 @@ static void vigs_server_dispatch_set_plane(void *user_data, return; } - qemu_mutex_lock(&server->dpy_mutex); - 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); - goto out; + return; } } if (plane >= VIGS_MAX_PLANES) { VIGS_LOG_ERROR("bad plane %u", plane); - goto out; + return; } server->planes[plane].sfc = sfc; @@ -349,9 +343,6 @@ static void vigs_server_dispatch_set_plane(void *user_data, server->planes[plane].dst_size = *dst_size; server->planes[plane].z_pos = z_pos; server->planes[plane].is_dirty = true; - -out: - qemu_mutex_unlock(&server->dpy_mutex); } static void vigs_server_dispatch_batch_end(void *user_data, @@ -403,8 +394,6 @@ static void vigs_server_set_root_surface_work(struct work_queue_item *wq_item) goto out; } - qemu_mutex_lock(&server->dpy_mutex); - if (item->id == 0) { if (server->root_sfc) { vigs_surface_set_scanout(server->root_sfc, NULL); @@ -438,7 +427,83 @@ static void vigs_server_set_root_surface_work(struct work_queue_item *wq_item) } out: - qemu_mutex_unlock(&server->dpy_mutex); + g_free(item); +} + +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; + bool planes_dirty = false; + bool dirty = false; + bool composited = false; + int i, index; + + if (!root_sfc) { + /* + * If no root surface then this is a no-op. + * TODO: Can planes be enabled without a root surface ? + */ + goto out; + } + + 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 && server->planes[i].sfc->is_dirty) { + /* + * If plane's surface is dirty then we're dirty. + */ + planes_dirty = true; + } + } + + if (root_sfc->ptr || root_sfc->is_dirty || planes_dirty) { + /* + * Composite root surface and planes. + */ + composited = true; + + index = (&server->capture_buffers[0] == server->captured) ? 1 : 0; + + if ((server->capture_buffers[index].stride != root_sfc->stride) || + (server->capture_buffers[index].height != root_sfc->ws_sfc->height)) { + g_free(server->capture_buffers[index].data); + server->capture_buffers[index].data = g_malloc(root_sfc->stride * root_sfc->ws_sfc->height); + } + + server->capture_buffers[index].width = root_sfc->ws_sfc->width; + server->capture_buffers[index].height = root_sfc->ws_sfc->height; + server->capture_buffers[index].stride = root_sfc->stride; + server->capture_buffers[index].format = root_sfc->format; + + server->backend->batch_start(server->backend); + dirty = server->backend->composite(root_sfc, + &server->planes[0], + planes_dirty, + server->capture_buffers[index].data); + server->backend->batch_end(server->backend); + + root_sfc->is_dirty = false; + + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + server->planes[i].is_dirty = false; + + if (server->planes[i].sfc) { + server->planes[i].sfc->is_dirty = false; + } + } + } + +out: + server->finish_update(server, composited, dirty); g_free(item); } @@ -503,6 +568,7 @@ static void vigs_server_dispatch_set_root_surface(void *user_data, { struct vigs_server *server = user_data; struct vigs_server_set_root_surface_work_item *item; + uint32_t capture_fence_seq = 0; item = g_malloc(sizeof(*item)); @@ -515,9 +581,20 @@ static void vigs_server_dispatch_set_root_surface(void *user_data, work_queue_add_item(server->render_queue, &item->base); - if (fence_seq) { + qemu_mutex_lock(&server->capture_mutex); + + if (server->is_capturing) { + capture_fence_seq = server->capture_fence_seq; + server->capture_fence_seq = fence_seq; + } else { + capture_fence_seq = fence_seq; + } + + qemu_mutex_unlock(&server->capture_mutex); + + if (capture_fence_seq) { server->display_ops->fence_ack(server->display_user_data, - fence_seq); + capture_fence_seq); } } @@ -547,17 +624,14 @@ static struct vigs_comm_ops vigs_server_dispatch_ops = .batch = &vigs_server_dispatch_batch }; -struct vigs_server *vigs_server_create(uint8_t *vram_ptr, - uint8_t *ram_ptr, - struct vigs_display_ops *display_ops, - void *display_user_data, - struct vigs_backend *backend, - struct work_queue *render_queue) +bool vigs_server_init(struct vigs_server *server, + uint8_t *vram_ptr, + uint8_t *ram_ptr, + struct vigs_display_ops *display_ops, + void *display_user_data, + struct vigs_backend *backend, + struct work_queue *render_queue) { - struct vigs_server *server = NULL; - - server = g_malloc0(sizeof(*server)); - server->wsi.ws_info = backend->ws_info; server->wsi.acquire_surface = &vigs_server_acquire_surface; server->wsi.fence_ack = &vigs_server_fence_ack; @@ -571,7 +645,7 @@ struct vigs_server *vigs_server_create(uint8_t *vram_ptr, server->comm = vigs_comm_create(ram_ptr); if (!server->comm) { - goto fail; + return false; } server->surfaces = g_hash_table_new_full(g_direct_hash, @@ -579,25 +653,23 @@ struct vigs_server *vigs_server_create(uint8_t *vram_ptr, NULL, vigs_server_surface_destroy_func); - qemu_mutex_init(&server->dpy_mutex); + qemu_mutex_init(&server->capture_mutex); - return server; + server->captured = &server->capture_buffers[0]; -fail: - g_free(server); - - return NULL; + return true; } -void vigs_server_destroy(struct vigs_server *server) +void vigs_server_cleanup(struct vigs_server *server) { vigs_server_reset(server); g_hash_table_destroy(server->surfaces); vigs_comm_destroy(server->comm); server->backend->destroy(server->backend); - qemu_mutex_destroy(&server->dpy_mutex); - g_free(server); + qemu_mutex_destroy(&server->capture_mutex); + g_free(server->capture_buffers[0].data); + g_free(server->capture_buffers[1].data); } void vigs_server_reset(struct vigs_server *server) @@ -633,94 +705,96 @@ void vigs_server_dispatch(struct vigs_server *server, } bool vigs_server_update_display(struct vigs_server *server, int invalidate_cnt) +{ + bool is_capturing = server->is_capturing; + + bool updated = server->begin_update(server, is_capturing, (invalidate_cnt > 0)); + + if (!is_capturing) { + struct vigs_server_work_item *item; + + item = g_malloc(sizeof(*item)); + + work_queue_item_init(&item->base, &vigs_server_update_display_work); + + item->server = server; + + server->is_capturing = true; + + work_queue_add_item(server->render_queue, &item->base); + } + + return updated; +} + +bool vigs_server_display(struct vigs_server *server, bool *displayed) +{ + return server->display(server, displayed); +} + +bool vigs_server_process_captured(struct vigs_server *server, bool force) { bool updated = false; - struct vigs_surface *root_sfc; uint32_t display_stride, display_bpp; uint8_t *display_data; - int i; - bool planes_on = false; - bool planes_dirty = false; - - qemu_mutex_lock(&server->dpy_mutex); - root_sfc = server->root_sfc; + qemu_mutex_lock(&server->capture_mutex); - if (!root_sfc) { + if (!server->captured->data || + (!server->captured->dirty && !force)) { goto out; } + server->captured->dirty = false; + updated = true; + server->display_ops->resize(server->display_user_data, - root_sfc->ws_sfc->width, - root_sfc->ws_sfc->height); + server->captured->width, + server->captured->height); display_stride = server->display_ops->get_stride(server->display_user_data); display_bpp = server->display_ops->get_bpp(server->display_user_data); display_data = server->display_ops->get_data(server->display_user_data); - if ((vigs_format_bpp(root_sfc->format) != display_bpp) || - (root_sfc->stride != display_stride)) { + if ((vigs_format_bpp(server->captured->format) != display_bpp) || + (server->captured->stride != display_stride)) { VIGS_LOG_CRITICAL("bpp/format mismatch"); assert(false); goto out; } - 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; - } + memcpy(display_data, + server->captured->data, + server->captured->stride * server->captured->height); - if (server->planes[i].sfc) { - planes_on = true; + updated = true; - /* - * If plane's surface is dirty then we're dirty. - */ - if (server->planes[i].sfc->is_dirty) { - planes_dirty = true; - } - } - } +out: + qemu_mutex_unlock(&server->capture_mutex); - if (root_sfc->ptr && !root_sfc->is_dirty && !planes_on) { - /* - * Root surface is scanout, it's not dirty and planes not on. - */ + return updated; +} - memcpy(display_data, - root_sfc->ptr, - root_sfc->stride * root_sfc->ws_sfc->height); +void vigs_server_finish_update_display(struct vigs_server *server, bool dirty) +{ + uint32_t capture_fence_seq = 0; - updated = true; - } else if (root_sfc->ptr || root_sfc->is_dirty || planes_dirty || (invalidate_cnt > 0)) { - /* - * Composite root surface and planes. - */ + qemu_mutex_lock(&server->capture_mutex); - updated = server->backend->composite(root_sfc, - &server->planes[0], - display_data); + if (dirty) { + int index = (&server->capture_buffers[0] == server->captured) ? 1 : 0; + server->capture_buffers[index].dirty = true; + server->captured = &server->capture_buffers[index]; + } - root_sfc->is_dirty = false; + server->is_capturing = false; + capture_fence_seq = server->capture_fence_seq; + server->capture_fence_seq = 0; - for (i = 0; i < VIGS_MAX_PLANES; ++i) { - if (server->planes[i].is_dirty) { - server->planes[i].is_dirty = false; - } + qemu_mutex_unlock(&server->capture_mutex); - if (server->planes[i].sfc && - server->planes[i].sfc->is_dirty) { - server->planes[i].sfc->is_dirty = false; - } - } + if (capture_fence_seq) { + server->display_ops->fence_ack(server->display_user_data, + capture_fence_seq); } - -out: - qemu_mutex_unlock(&server->dpy_mutex); - - return updated; } diff --git a/hw/vigs/vigs_server.h b/hw/vigs/vigs_server.h index 6614dce83c..5c2248510b 100644 --- a/hw/vigs/vigs_server.h +++ b/hw/vigs/vigs_server.h @@ -96,21 +96,48 @@ struct vigs_server struct vigs_plane planes[VIGS_MAX_PLANES]; - QemuMutex dpy_mutex; + QemuMutex capture_mutex; + + bool is_capturing; + + uint32_t capture_fence_seq; + + struct + { + uint8_t *data; + uint32_t width; + uint32_t height; + uint32_t stride; + vigsp_surface_format format; + bool dirty; + } capture_buffers[2], *captured; /* * @} */ + + bool (*begin_update)(struct vigs_server */*server*/, + bool /*is_capturing*/, + bool /*force*/); + + void (*finish_update)(struct vigs_server */*server*/, + bool /*composited*/, + bool /*dirty*/); + + bool (*display)(struct vigs_server */*server*/, bool */*displayed*/); + + void (*destroy)(struct vigs_server */*server*/); }; -struct vigs_server *vigs_server_create(uint8_t *vram_ptr, - uint8_t *ram_ptr, - struct vigs_display_ops *display_ops, - void *display_user_data, - struct vigs_backend *backend, - struct work_queue *render_queue); +bool vigs_server_init(struct vigs_server *server, + uint8_t *vram_ptr, + uint8_t *ram_ptr, + struct vigs_display_ops *display_ops, + void *display_user_data, + struct vigs_backend *backend, + struct work_queue *render_queue); -void vigs_server_destroy(struct vigs_server *server); +void vigs_server_cleanup(struct vigs_server *server); void vigs_server_reset(struct vigs_server *server); @@ -119,4 +146,19 @@ void vigs_server_dispatch(struct vigs_server *server, bool vigs_server_update_display(struct vigs_server *server, int invalidate_cnt); +bool vigs_server_display(struct vigs_server *server, bool *displayed); + +/* + * For internal use only. + * @{ + */ + +bool vigs_server_process_captured(struct vigs_server *server, bool force); + +void vigs_server_finish_update_display(struct vigs_server *server, bool dirty); + +/* + * @} + */ + #endif diff --git a/hw/vigs/vigs_sw_backend.c b/hw/vigs/vigs_sw_backend.c index 991661b430..35224c0628 100644 --- a/hw/vigs/vigs_sw_backend.c +++ b/hw/vigs/vigs_sw_backend.c @@ -366,6 +366,7 @@ static struct vigs_surface *vigs_sw_backend_create_surface(struct vigs_backend * static bool vigs_sw_backend_composite(struct vigs_surface *surface, const struct vigs_plane *planes, + bool planes_dirty, uint8_t *display_data) { struct vigs_sw_surface *sw_sfc = (struct vigs_sw_surface*)surface; @@ -393,6 +394,12 @@ static void vigs_sw_backend_batch_end(struct vigs_backend *backend) { } +static bool vigs_sw_backend_display(struct vigs_backend *backend, + uint8_t *display_data) +{ + return false; +} + static void vigs_sw_backend_destroy(struct vigs_backend *backend) { struct vigs_sw_backend *sw_backend = (struct vigs_sw_backend*)backend; @@ -413,6 +420,7 @@ struct vigs_backend *vigs_sw_backend_create(void) backend->base.create_surface = &vigs_sw_backend_create_surface; backend->base.composite = &vigs_sw_backend_composite; backend->base.batch_end = &vigs_sw_backend_batch_end; + backend->base.display = &vigs_sw_backend_display; backend->base.destroy = &vigs_sw_backend_destroy; return &backend->base; diff --git a/tizen/src/display/qt5.c b/tizen/src/display/qt5.c index 44fb785ccd..87648e3473 100644 --- a/tizen/src/display/qt5.c +++ b/tizen/src/display/qt5.c @@ -45,6 +45,13 @@ void qt5_graphic_hw_invalidate(void) graphic_hw_invalidate(NULL); } +int qt5_graphic_hw_display(void) +{ + console_ch_t displayed = 0; + graphic_hw_text_update(NULL, &displayed); + return displayed; +} + static void qt5_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { @@ -67,9 +74,8 @@ static void qt5_switch(DisplayChangeListener *dcl, static void qt5_refresh(DisplayChangeListener *dcl) { - qt5_pre_refresh_internal(); graphic_hw_update(dcl->con); - qt5_post_refresh_internal(); + qt5_refresh_internal(); } static void qt5_mouse_warp(DisplayChangeListener *dcl, diff --git a/tizen/src/display/qt5_supplement.cpp b/tizen/src/display/qt5_supplement.cpp index 7f4b2337fb..cb9b595642 100644 --- a/tizen/src/display/qt5_supplement.cpp +++ b/tizen/src/display/qt5_supplement.cpp @@ -51,7 +51,7 @@ void qMessageOutput(QtMsgType, const QMessageLogContext &, const QString &); void loadSkinFormFromXML(QFile *, UIInformation *); void loadControllerFormFromXML(QFile *, UIInformation *); -static QApplication *app; +QApplication *qt5App = NULL; static int argc = 0; static char *argv[0]; @@ -124,7 +124,7 @@ void qt5_early_prepare(void) Q_INIT_RESOURCE(resource); qInstallMessageHandler(qMessageOutput); - app = new QApplication(argc, argv); + qt5App = new QApplication(argc, argv); } void qt5_prepare(void) @@ -148,15 +148,9 @@ void qt5_switch_internal(void) { } -void qt5_pre_refresh_internal(void) +void qt5_refresh_internal(void) { - mainwindow->makeCurrent(true); -} - -void qt5_post_refresh_internal(void) -{ - mainwindow->makeCurrent(false); - app->processEvents(); + qt5App->processEvents(); } void qMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) diff --git a/tizen/src/display/qt5_supplement.h b/tizen/src/display/qt5_supplement.h index bb6619fed0..1c7af9e1f3 100644 --- a/tizen/src/display/qt5_supplement.h +++ b/tizen/src/display/qt5_supplement.h @@ -33,13 +33,13 @@ extern "C" { #endif void qt5_graphic_hw_invalidate(void); +int qt5_graphic_hw_display(void); void qt5_early_prepare(void); void qt5_prepare(void); void qt5_update_internal(void *data, int width, int height); void qt5_switch_internal(void); -void qt5_pre_refresh_internal(void); -void qt5_post_refresh_internal(void); +void qt5_refresh_internal(void); #ifdef __cplusplus } #endif diff --git a/tizen/src/ui/displaywidget.cpp b/tizen/src/ui/displaywidget.cpp index 6c95f117f0..42b060c325 100644 --- a/tizen/src/ui/displaywidget.cpp +++ b/tizen/src/ui/displaywidget.cpp @@ -56,10 +56,21 @@ void DisplayWidget::scale(int scale) void DisplayWidget::paintEvent(QPaintEvent *event) { + /* + * We offload rendering to separate thread, this must be + * a no-op, see: http://qt-project.org/doc/qt-5/QGLWidget.html: + * "3. Using QPainter to draw into a QGLWidget in a thread" + */ } void DisplayWidget::resizeEvent(QResizeEvent *event) { + /* + * We offload rendering to separate thread, this must be + * a no-op, see: http://qt-project.org/doc/qt-5/QGLWidget.html: + * "3. Using QPainter to draw into a QGLWidget in a thread" + */ + qDebug("resize display"); //QGLWidget::resizeEvent(event); /* initializeGL */ diff --git a/tizen/src/ui/mainwindow.cpp b/tizen/src/ui/mainwindow.cpp index 6c7fff83ff..7dbcc88e5a 100644 --- a/tizen/src/ui/mainwindow.cpp +++ b/tizen/src/ui/mainwindow.cpp @@ -3,48 +3,35 @@ #include "mainwindow.h" #include "skinform.h" +extern "C" { +int qt5_graphic_hw_display(void); +} + QOpenGLContext *qt5GLContext = NULL; QSurfaceFormat qt5GLFormat; DisplaySwapper::DisplaySwapper(QGLContext* context, QObject* parent) : QObject(parent), context(context), - swapping(false) -{ -} - -void DisplaySwapper::lock() + terminating(false) { - m.lock(); - - while (swapping) { - c.wait(&m); - } - - m.unlock(); } -void DisplaySwapper::unlock() -{ - swapping = true; -} - -void DisplaySwapper::swapBuffers() +void DisplaySwapper::display() { + /* + * TODO: Currently qt5 skin doesn't terminate properly, + * check this once proper termination is implemented. + */ context->makeCurrent(); - context->swapBuffers(); + while (!terminating) { + if (qt5_graphic_hw_display()) { + context->swapBuffers(); + } + } context->doneCurrent(); - - context->moveToThread(qApp->thread()); - - m.lock(); - swapping = false; - m.unlock(); - - c.wakeAll(); } - MainWindow::MainWindow(UIInformation *uiInfo, QWidget *parent) : QWidget(parent) { @@ -121,11 +108,16 @@ MainWindow::MainWindow(UIInformation *uiInfo, QWidget *parent) : /* swapper */ swapperThread = new QThread(this); + context->doneCurrent(); + context->moveToThread(swapperThread); + swapper = new DisplaySwapper(context); swapper->moveToThread(swapperThread); connect(swapperThread, &QThread::finished, swapper, &QObject::deleteLater); swapperThread->start(); + + QMetaObject::invokeMethod(swapper, "display", Qt::QueuedConnection); } QLabel *MainWindow::getLabel() @@ -133,19 +125,6 @@ QLabel *MainWindow::getLabel() return label; } -void MainWindow::makeCurrent(bool value) -{ - if (value) { - swapper->lock(); - display->makeCurrent(); - } else { - display->doneCurrent(); - swapper->unlock(); - display->context()->moveToThread(swapperThread); - QMetaObject::invokeMethod(swapper, "swapBuffers", Qt::QueuedConnection); - } -} - void MainWindow::showContextMenu(const QPoint& pos) { QAction* selectedItem = 0; @@ -233,10 +212,6 @@ void MainWindow::scale(int scale) adjustSize(); } -void MainWindow::updateSkin() // TODO: temp -{ -} - void MainWindow::setRegion(QImage baseImage) { //qDebug("set region"); @@ -256,6 +231,9 @@ MainWindow::~MainWindow() { qDebug("destory main window"); + swapper->setTerminating(); + qt5_graphic_hw_invalidate(); + swapperThread->quit(); swapperThread->wait(); } diff --git a/tizen/src/ui/mainwindow.h b/tizen/src/ui/mainwindow.h index 71cda0c7b3..281c99a277 100644 --- a/tizen/src/ui/mainwindow.h +++ b/tizen/src/ui/mainwindow.h @@ -26,17 +26,14 @@ class DisplaySwapper : public QObject public: DisplaySwapper(QGLContext* context, QObject* parent = 0); - void lock(); - void unlock(); + inline void setTerminating() { terminating = true; } public slots: - void swapBuffers(); + void display(); private: QGLContext *context; - QMutex m; - QWaitCondition c; - bool swapping; + bool terminating; }; class MainWindow : public QWidget @@ -57,15 +54,12 @@ public: UIInformation *uiInfo; QLabel *getLabel(); - void makeCurrent(bool value); - public slots: void showContextMenu(const QPoint& pos); protected: void resizeEvent(QResizeEvent *event); - void updateSkin(); void setRegion(QImage baseImage); void closeEvent(QCloseEvent *); QLabel *label; -- 2.34.1