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
dobj->dpy = (Display*)vigs_qt5_display();
+ if (!dobj->dpy) {
+ dobj->dpy = XOpenDisplay(0);
+ }
+
if (!dobj->dpy) {
error_setg(errp, "Cannot open X display");
}
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*/);
};
*/
#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"
}
}
+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;
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 =
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;
{
VIGSState *s = DO_UPCAST(VIGSState, dev, dev);
- vigs_server_destroy(s->server);
+ s->server->destroy(s->server);
qemu_bh_delete(s->fence_ack_bh);
#include "vigs_log.h"
#include "vigs_utils.h"
#include "vigs_ref.h"
+#include "vigs_qt5.h"
#include "winsys_gl.h"
#include <math.h>
* 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
*/
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,
*/
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,
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;
+}
+
/*
* @}
*/
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;
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;
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) {
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,
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;
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);
}
/*
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)
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);
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);
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.
* @{
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;
+
/*
* @}
*/
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,
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;
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 =
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);
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,
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)) {
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);
--- /dev/null
+/*
+ * vigs
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Stanislav Vorobiov <s.vorobiov@samsung.com>
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * YeongKyoon Lee <yeongkyoon.lee@samsung.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, 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;
+}
--- /dev/null
+/*
+ * vigs
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Stanislav Vorobiov <s.vorobiov@samsung.com>
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * YeongKyoon Lee <yeongkyoon.lee@samsung.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, 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
--- /dev/null
+/*
+ * vigs
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Stanislav Vorobiov <s.vorobiov@samsung.com>
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * YeongKyoon Lee <yeongkyoon.lee@samsung.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, 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;
+}
--- /dev/null
+/*
+ * vigs
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Stanislav Vorobiov <s.vorobiov@samsung.com>
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * YeongKyoon Lee <yeongkyoon.lee@samsung.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, 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
#include <qpa/qplatformnativeinterface.h>
#include <stdio.h>
+extern QApplication *qt5App;
extern QOpenGLContext *qt5GLContext;
extern QSurfaceFormat qt5GLFormat;
void *vigs_qt5_display(void)
{
+ if (!qt5App) {
+ return NULL;
+ }
+
QPlatformNativeInterface *native =
QGuiApplication::platformNativeInterface();
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;
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));
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;
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,
goto out;
}
- qemu_mutex_lock(&server->dpy_mutex);
-
if (item->id == 0) {
if (server->root_sfc) {
vigs_surface_set_scanout(server->root_sfc, NULL);
}
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);
}
{
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));
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);
}
}
.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;
server->comm = vigs_comm_create(ram_ptr);
if (!server->comm) {
- goto fail;
+ return false;
}
server->surfaces = g_hash_table_new_full(g_direct_hash,
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)
}
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;
}
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);
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
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;
{
}
+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;
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;
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)
{
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,
void loadSkinFormFromXML(QFile *, UIInformation *);
void loadControllerFormFromXML(QFile *, UIInformation *);
-static QApplication *app;
+QApplication *qt5App = NULL;
static int argc = 0;
static char *argv[0];
Q_INIT_RESOURCE(resource);
qInstallMessageHandler(qMessageOutput);
- app = new QApplication(argc, argv);
+ qt5App = new QApplication(argc, argv);
}
void qt5_prepare(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)
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
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 */
#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)
{
/* 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()
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;
adjustSize();
}
-void MainWindow::updateSkin() // TODO: temp
-{
-}
-
void MainWindow::setRegion(QImage baseImage)
{
//qDebug("set region");
{
qDebug("destory main window");
+ swapper->setTerminating();
+ qt5_graphic_hw_invalidate();
+
swapperThread->quit();
swapperThread->wait();
}
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
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;