From b42af265bee3f8ffd8c113a585c3d6a8f0460ca0 Mon Sep 17 00:00:00 2001 From: Chris Michael Date: Tue, 11 Feb 2014 07:09:39 +0000 Subject: [PATCH] evas-drm: Add Evas Drm Engine (software only currently) @feature: Add working Evas_Drm Engine (software only currently) Signed-off-by: Chris Michael --- .../evas/engines/drm/Evas_Engine_Drm.h | 11 + src/modules/evas/engines/drm/evas_drm.c | 630 ++++++++++++++++++ src/modules/evas/engines/drm/evas_engine.c | 312 ++++++++- src/modules/evas/engines/drm/evas_engine.h | 74 +- src/modules/evas/engines/drm/evas_outbuf.c | 553 ++++++++++++++- 5 files changed, 1550 insertions(+), 30 deletions(-) create mode 100644 src/modules/evas/engines/drm/evas_drm.c diff --git a/src/modules/evas/engines/drm/Evas_Engine_Drm.h b/src/modules/evas/engines/drm/Evas_Engine_Drm.h index d149e609ac..6b1a663f68 100644 --- a/src/modules/evas/engines/drm/Evas_Engine_Drm.h +++ b/src/modules/evas/engines/drm/Evas_Engine_Drm.h @@ -11,8 +11,19 @@ struct _Evas_Engine_Info_Drm struct { + Evas *evas; + unsigned int rotation, depth; Eina_Bool destination_alpha : 1; + + int fd; + Eina_Bool own_fd : 1; + + int tty; + Eina_Bool own_tty : 1; + + int output; + int plane; } info; /* non-blocking or blocking mode */ diff --git a/src/modules/evas/engines/drm/evas_drm.c b/src/modules/evas/engines/drm/evas_drm.c new file mode 100644 index 0000000000..77ca0f6a50 --- /dev/null +++ b/src/modules/evas/engines/drm/evas_drm.c @@ -0,0 +1,630 @@ +#include "evas_engine.h" +#include +#include +#include + +/* NB: REALLY hate to store this here, but sigaction signal handlers cannot + * pass any 'user data' to the signal handlers :( */ +static Evas_Engine_Info_Drm *siginfo; + +static int +_evas_drm_card_open(int card) +{ + char dev[32]; + int fd = -1; + uint64_t dumb; + + sprintf(dev, DRM_DEV_NAME, DRM_DIR_NAME, card); + + if ((fd = open(dev, (O_RDWR | O_CLOEXEC))) < 0) + { + CRI("Could not open drm device %s: %m", dev); + return -1; + } + + drmVersionPtr ver; + if ((ver = drmGetVersion(fd))) + { + DBG("Drm Driver Name: %s", ver->name); + drmFreeVersion(ver); + } + + /* check for dumb buffer support + * + * NB: This checks that we can at least support software rendering */ + if ((drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &dumb) < 0) || (!dumb)) + { + CRI("Drm Device %s does not support software rendering", dev); + + /* close the card */ + close(fd); + + /* return failure */ + return -1; + } + + DBG("Opened Drm Card %s: %d", dev, fd); + + /* return opened card */ + return fd; +} + +static int +_evas_drm_tty_open(Evas_Engine_Info_Drm *info) +{ + int tty = STDIN_FILENO; + + /* check if the current stdin is a valid tty */ + if (!isatty(tty)) + { + /* if not, try to open the curren tty */ + if ((tty = open("/dev/tty", (O_RDWR | O_CLOEXEC))) < 0) + { + int tty0 = -1, num = -1; + char name[16]; + + /* if that fails, try tty0 */ + if ((tty0 = open("/dev/tty0", (O_WRONLY | O_CLOEXEC))) < 0) + { + CRI("Could not open tty0: %m"); + return -1; + } + + /* try to find a non-opened tty */ + if ((ioctl(tty0, VT_OPENQRY, &num) < 0) || (num < 0)) + { + CRI("Could not find a non-opened tty"); + close(tty0); + return -1; + } + + snprintf(name, sizeof(name), "/dev/tty%d", num); + + /* try to open this tty */ + if ((tty = open(name, (O_RDWR | O_CLOEXEC))) < 0) + { + CRI("Could not open tty: %s", name); + close(tty0); + return -1; + } + + /* set flag that evas should close this tty */ + info->info.own_tty = EINA_TRUE; + + /* close /dev/tty0 */ + close(tty0); + } + } + else + { + /* set flag that evas should close this tty */ + info->info.own_tty = EINA_TRUE; + } + + DBG("Opened Tty %d", tty); + + return tty; +} + +static int +_evas_drm_crtc_find(int fd, drmModeRes *res, drmModeConnector *conn) +{ + int crtc = -1; + drmModeEncoder *enc = NULL; + + /* if this connector already has an encoder, get it */ + if (conn->encoder_id) enc = drmModeGetEncoder(fd, conn->encoder_id); + + /* if this encoder already has a crtc, lets try to use that */ + if ((enc) && (enc->crtc_id)) crtc = enc->crtc_id; + + if (crtc < 0) + { + int i = 0, c = 0; + + /* if this connector has no encoder, we need to find one */ + for (; i < conn->count_encoders; ++i) + { + /* try to get this encoder */ + if (!(enc = drmModeGetEncoder(fd, conn->encoders[i]))) + continue; + + /* loop global crtcs */ + for (; c < res->count_crtcs; ++c) + { + /* does this crtc work with this encoder ? */ + if (!(enc->possible_crtcs & (1 << c))) continue; + + /* FIXME: We could be more proactive here and check that + * nobody else is using this crtc */ + + /* if it works, let's use it */ + crtc = res->crtcs[c]; + break; + } + + if (crtc >= 0) break; + } + } + + /* free the encoder */ + if (enc) drmModeFreeEncoder(enc); + + return crtc; +} + +static void +_evas_drm_tty_sigusr1(int x EINA_UNUSED, siginfo_t *info, void *data EINA_UNUSED) +{ + Evas_Engine_Info_Drm *einfo; + + DBG("Caught SIGUSR1"); + + if (!(einfo = siginfo)) return; + + /* TODO: set canvas to not render */ + + DBG("\tDrop Master & Release VT"); + + /* drop drm master */ + if (einfo->info.own_fd) + { + if (drmDropMaster(einfo->info.fd) != 0) + WRN("Could not drop drm master: %m"); + } + + /* release vt */ + if (einfo->info.own_tty) + { + if (ioctl(einfo->info.tty, VT_RELDISP, 1) < 0) + WRN("Could not release vt: %m"); + } +} + +static void +_evas_drm_tty_sigusr2(int x EINA_UNUSED, siginfo_t *info, void *data EINA_UNUSED) +{ + Evas_Engine_Info_Drm *einfo; + + DBG("Caught SIGUSR2"); + + if (!(einfo = siginfo)) return; + + /* TODO: set canvas to render again */ + + DBG("\tAcquire VT & Set Master"); + + /* acquire vt */ + if (einfo->info.own_tty) + { + if (ioctl(einfo->info.tty, VT_RELDISP, VT_ACKACQ) < 0) + WRN("Could not acquire vt: %m"); + } + + /* set master */ + if (einfo->info.own_fd) + { + if (drmSetMaster(einfo->info.fd) != 0) + WRN("Could not set drm master: %m"); + } +} + +static Eina_Bool +_evas_drm_tty_setup(Evas_Engine_Info_Drm *info) +{ + struct vt_mode vtmode = { 0 }; + struct sigaction sig; + + /* check for valid tty */ + if (info->info.tty < 0) return EINA_FALSE; + +#if 0 + /* set vt to graphics mode */ + if (ioctl(info->info.tty, KDSETMODE, KD_GRAPHICS) < 0) + { + CRI("Could not set tty to graphics mode: %m"); + return EINA_FALSE; + } +#endif + + /* setup tty rel/acq signals */ + vtmode.mode = VT_PROCESS; + vtmode.waitv = 0; + vtmode.relsig = SIGUSR1; + vtmode.acqsig = SIGUSR2; + if (ioctl(info->info.tty, VT_SETMODE, &vtmode) < 0) + { + CRI("Could not set tty mode: %m"); + return EINA_FALSE; + } + + /* store info struct + * + * NB: REALLY hate to store this here, but sigaction signal handlers cannot + * pass any 'user data' to the signal handlers :( + */ + siginfo = info; + + /* setup signal handlers for above signals */ + sig.sa_sigaction = _evas_drm_tty_sigusr1; + sig.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESTART); + sigemptyset(&sig.sa_mask); + sigaction(SIGUSR1, &sig, NULL); + + sig.sa_sigaction = _evas_drm_tty_sigusr2; + sig.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESTART); + sigemptyset(&sig.sa_mask); + sigaction(SIGUSR2, &sig, NULL); + + return EINA_TRUE; +} + +static void +_evas_drm_outbuf_page_flip(int fd EINA_UNUSED, unsigned int seq EINA_UNUSED, unsigned int tv_sec EINA_UNUSED, unsigned int tv_usec EINA_UNUSED, void *data) +{ + Outbuf *ob; + + /* get the output buffer from data */ + if (!(ob = data)) return; + + /* DBG("Page Flip Event"); */ + + ob->priv.pending_flip = EINA_FALSE; + ob->priv.curr = (ob->priv.curr + 1) % ob->priv.num; +} + +static void +_evas_drm_outbuf_vblank(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data) +{ + Outbuf *ob; + + /* get the output buffer from data */ + if (!(ob = data)) return; + + DBG("VBlank Event"); +} + +Eina_Bool +evas_drm_init(Evas_Engine_Info_Drm *info, int card) +{ + /* check for valid engine info */ + if (!info) return EINA_FALSE; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* check if we already opened the card */ + if (info->info.fd < 0) + { + /* try to open the drm card */ + if ((info->info.fd = _evas_drm_card_open(card)) < 0) + return EINA_FALSE; + + /* set flag to indicate that evas opened the card and we should + * be the one to close it */ + info->info.own_fd = EINA_TRUE; + } + + /* check if we already opened the tty */ + if (info->info.tty < 0) + { + /* try to open the current tty */ + if ((info->info.tty = _evas_drm_tty_open(info)) < 0) + { + /* check if we already opened the card. if so, close it */ + if ((info->info.fd >= 0) && (info->info.own_fd)) + { + close(info->info.fd); + info->info.fd = -1; + } + + return EINA_FALSE; + } + } + + /* with the tty opened, we need to set it up */ + if (!_evas_drm_tty_setup(info)) + { + /* setup of tty failed, close it */ + if ((info->info.tty >= 0) && (info->info.own_tty)) + close(info->info.tty); + + /* FIXME: Close card also ?? */ + + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +evas_drm_shutdown(Evas_Engine_Info_Drm *info) +{ + /* check for valid engine info */ + if (!info) return EINA_TRUE; + + /* check if we already opened the tty. if so, close it */ + if ((info->info.tty >= 0) && (info->info.own_tty)) + { + close(info->info.tty); + info->info.tty = -1; + } + + /* check if we already opened the card. if so, close it */ + if ((info->info.fd >= 0) && (info->info.own_fd)) + { + close(info->info.fd); + info->info.fd = -1; + } + + return EINA_TRUE; +} + +Eina_Bool +evas_drm_outbuf_setup(Outbuf *ob) +{ + drmModeRes *res; + drmModeConnector *conn; + int i = 0; + + /* check for valid Output buffer */ + if ((!ob) || (ob->priv.fd < 0)) return EINA_FALSE; + + /* setup drmHandleEvent context */ + memset(&ob->priv.ctx, 0, sizeof(ob->priv.ctx)); + ob->priv.ctx.version = DRM_EVENT_CONTEXT_VERSION; + ob->priv.ctx.page_flip_handler = _evas_drm_outbuf_page_flip; + ob->priv.ctx.vblank_handler = _evas_drm_outbuf_vblank; + + /* try to get drm resources */ + if (!(res = drmModeGetResources(ob->priv.fd))) + { + CRI("Could not get drm resources: %m"); + return EINA_FALSE; + } + + /* loop the connectors */ + for (; i < res->count_connectors; ++i) + { + int crtc = -1; + int m = 0; + + /* try to get this connector */ + if (!(conn = drmModeGetConnector(ob->priv.fd, res->connectors[i]))) + { + WRN("Could not get drm connector %d: %m", i); + continue; + } + + /* make sure this connector is actually connected */ + if (conn->connection != DRM_MODE_CONNECTED) + { + /* free connector resources */ + drmModeFreeConnector(conn); + continue; + } + + /* make sure it has modes */ + if (conn->count_modes == 0) + { + /* free connector resources */ + drmModeFreeConnector(conn); + continue; + } + + /* try to find a crtc for this connector */ + if ((crtc = _evas_drm_crtc_find(ob->priv.fd, res, conn)) < 0) + { + /* free connector resources */ + drmModeFreeConnector(conn); + continue; + } + + /* record the connector id */ + ob->priv.conn = conn->connector_id; + + /* record the crtc id */ + ob->priv.crtc = crtc; + + /* spew out connector properties for testing */ + /* drmModePropertyPtr props; */ + /* for (m = 0; m < conn->count_props; m++) */ + /* { */ + /* props = drmModeGetProperty(ob->priv.fd, conn->props[m]); */ + /* if (!props) continue; */ + /* DBG("Property Name: %s", props->name); */ + /* } */ + + /* record the current mode */ + memcpy(&ob->priv.mode, &conn->modes[0], sizeof(ob->priv.mode)); + DBG("Output Current Mode: %d: %d %d", ob->priv.conn, + conn->modes[0].hdisplay, conn->modes[0].vdisplay); + + for (m = 0; m < conn->count_modes; m++) + { + DBG("Output Available Mode: %d: %d %d %d", ob->priv.conn, + conn->modes[m].hdisplay, conn->modes[m].vdisplay, + conn->modes[m].vrefresh); + + /* try to find a mode which matches the requested size */ + if ((conn->modes[m].hdisplay == ob->w) && + (conn->modes[m].vdisplay == ob->h) && + (conn->modes[m].vrefresh == 60)) + { + memcpy(&ob->priv.mode, &conn->modes[m], + sizeof(ob->priv.mode)); + } + } + + /* free connector resources */ + drmModeFreeConnector(conn); + + break; + } + + /* free drm resources */ + drmModeFreeResources(res); + + return EINA_TRUE; +} + +void +evas_drm_outbuf_framebuffer_set(Outbuf *ob, Buffer *buffer) +{ + int ret; + + /* validate params */ + if ((!ob) || (!buffer)) return; + + ret = drmModeSetCrtc(ob->priv.fd, ob->priv.crtc, buffer->fb, 0, 0, + &ob->priv.conn, 1, &ob->priv.mode); + + if (ret) ERR("Failed to set crtc: %m"); + else buffer->valid = EINA_TRUE; +} + +Eina_Bool +evas_drm_framebuffer_create(int fd, Buffer *buffer, int depth) +{ + struct drm_mode_create_dumb carg; + struct drm_mode_destroy_dumb darg; + struct drm_mode_map_dumb marg; + + /* check for valid info */ + if (fd < 0) return EINA_FALSE; + + /* try to create a dumb buffer */ + memset(&carg, 0, sizeof(carg)); + carg.width = buffer->w; + carg.height = buffer->h; + carg.bpp = depth; + if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg) < 0) + { + ERR("Could not create dumb buffer: %m"); + return EINA_FALSE; + } + + buffer->stride = carg.pitch; + buffer->size = carg.size; + buffer->handle = carg.handle; + + /* try to create a framebuffer object */ + /* FIXME: Hardcoded bpp */ + if (drmModeAddFB(fd, buffer->w, buffer->h, 24, depth, buffer->stride, + buffer->handle, &buffer->fb)) + { + ERR("Could not create framebuffer object: %m"); + goto add_err; + } + + DBG("Creating dumb buffer: %d %d %d %d", buffer->fb, + buffer->w, buffer->h, depth); + + /* try to mmap the buffer */ + memset(&marg, 0, sizeof(marg)); + marg.handle = buffer->handle; + if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &marg)) + { + ERR("Could not map dumb buffer: %m"); + goto map_err; + } + + /* do actual mmap of memory */ + buffer->data = + mmap(0, buffer->size, (PROT_WRITE), +// mmap(0, buffer->size, (PROT_READ | PROT_WRITE), + MAP_SHARED, fd, marg.offset); + if (buffer->data == MAP_FAILED) + { + ERR("Could not mmap dumb buffer: %m"); + goto map_err; + } + + /* clear memory */ + memset(buffer->data, 0, buffer->size); + + return EINA_TRUE; + +map_err: + /* remove the framebuffer */ + drmModeRmFB(fd, buffer->fb); + +add_err: + /* destroy buffer */ + memset(&darg, 0, sizeof(darg)); + darg.handle = buffer->handle; + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg); + + return EINA_FALSE; +} + +void +evas_drm_framebuffer_destroy(int fd, Buffer *buffer) +{ + struct drm_mode_destroy_dumb darg; + + /* check for valid info */ + if (fd < 0) return; + + /* unmap the buffer data */ + if (buffer->data) munmap(buffer->data, buffer->size); + + /* remove the framebuffer */ + drmModeRmFB(fd, buffer->fb); + + /* destroy buffer */ + memset(&darg, 0, sizeof(darg)); + darg.handle = buffer->handle; + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg); +} + +Eina_Bool +evas_drm_framebuffer_send(Outbuf *ob, Buffer *buffer, Eina_Rectangle *rects, unsigned int count) +{ + int ret; + + /* check for valid Output buffer */ + if ((!ob) || (ob->priv.fd < 0)) return EINA_FALSE; + + /* check for valid buffer */ + if ((!buffer) || (!buffer->valid)) return EINA_FALSE; + +#ifdef DRM_MODE_FEATURE_DIRTYFB + drmModeClip *clip; + unsigned int i = 0; + + /* WRN("drmModeDirtyFB is experimental"); */ + + /* NB: alloca automatically frees memory */ + clip = alloca(count * sizeof(drmModeClip)); + for (i = 0; i < count; i++) + { + clip[i].x1 = rects[i].x; + clip[i].y1 = rects[i].y; + clip[i].x2 = rects[i].w; + clip[i].y2 = rects[i].h; + } + + ret = drmModeDirtyFB(ob->priv.fd, buffer->fb, clip, count); + if (ret) + { + if (ret == -EINVAL) + ERR("Could not set FB Dirty: %m"); + } +#endif + + ret = drmModePageFlip(ob->priv.fd, ob->priv.crtc, buffer->fb, + DRM_MODE_PAGE_FLIP_EVENT, ob); + if (ret) + { + ERR("Cannot flip crtc for connector %u: %m", ob->priv.conn); + return EINA_FALSE; + } + + ob->priv.sent = buffer; + ob->priv.pending_flip = EINA_TRUE; + +// while (ob->priv.pending_flip) + drmHandleEvent(ob->priv.fd, &ob->priv.ctx); + + return EINA_TRUE; +} diff --git a/src/modules/evas/engines/drm/evas_engine.c b/src/modules/evas/engines/drm/evas_engine.c index cc5e86fbd7..c3bdf3406a 100644 --- a/src/modules/evas/engines/drm/evas_engine.c +++ b/src/modules/evas/engines/drm/evas_engine.c @@ -1,6 +1,3 @@ -#include "evas_common_private.h" -#include "evas_private.h" -#include "Evas_Engine_Drm.h" #include "evas_engine.h" /* local structures */ @@ -25,7 +22,7 @@ struct _Render_Engine }; /* local function prototypes */ -static void *_output_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha, int swap); +static void *_output_setup(Evas_Engine_Info_Drm *info, int w, int h, int swap); /* function tables - filled in later (func and parent func) */ static Evas_Func func, pfunc; @@ -35,7 +32,7 @@ int _evas_engine_drm_log_dom; /* local functions */ static void * -_output_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha, int swap) +_output_setup(Evas_Engine_Info_Drm *info, int w, int h, int swap) { Render_Engine *re; @@ -53,15 +50,31 @@ _output_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool /* set tilesize */ evas_common_tilebuf_set_tile_size(re->tb, TILESIZE, TILESIZE); + /* if we have no drm device, get one */ + if (info->info.fd < 0) + { + /* try to init drm (this includes openening the card & tty) */ + if (!evas_drm_init(info, 0)) + { + if (re->tb) evas_common_tilebuf_free(re->tb); + free(re); + return NULL; + } + } + if (swap) { /* free any existing outbuf */ if (re->ob) evas_outbuf_free(re->ob); /* try to create new outbuf */ - if (!(re->ob = evas_outbuf_setup(w, h, rotation, depth, alpha))) + if (!(re->ob = evas_outbuf_setup(info, w, h))) { if (re->tb) evas_common_tilebuf_free(re->tb); + + /* shutdown drm card & tty */ + evas_drm_shutdown(info); + free(re); return NULL; } @@ -111,6 +124,12 @@ eng_setup(Evas *evas, void *einfo) /* try to get the evas public data */ if (!(epd = eo_data_scope_get(evas, EVAS_CLASS))) return 0; + /* set canvas reference + * + * NB: We do this here so that on a vt switch, we can disable + * rendering (or re-enable) for this canvas */ + info->info.evas = evas; + /* check for valid engine output */ if (!(re = epd->engine.data.output)) { @@ -138,9 +157,7 @@ eng_setup(Evas *evas, void *einfo) } /* try to create a new render_engine */ - if (!(re = _output_setup(epd->output.w, epd->output.h, - info->info.rotation, info->info.depth, - info->info.destination_alpha, swap))) + if (!(re = _output_setup(info, epd->output.w, epd->output.h, swap))) return 0; } else @@ -149,10 +166,7 @@ eng_setup(Evas *evas, void *einfo) if (re->ob) evas_outbuf_free(re->ob); /* try to create a new outbuf */ - if (!(re->ob = - evas_outbuf_setup(epd->output.w, epd->output.h, - info->info.rotation, info->info.depth, - info->info.destination_alpha))) + if (!(re->ob = evas_outbuf_setup(info, epd->output.w, epd->output.h))) return 0; } @@ -190,6 +204,9 @@ eng_output_free(void *data) evas_common_tilebuf_free_render_rects(re->prev_rects[1]); if (re->prev_rects[2]) evas_common_tilebuf_free_render_rects(re->prev_rects[2]); + + evas_drm_shutdown(re->info); + free(re); } @@ -197,6 +214,266 @@ eng_output_free(void *data) evas_common_image_shutdown(); } +static void +eng_output_resize(void *data, int w, int h) +{ + Render_Engine *re; + + /* try to get the render_engine */ + if (!(re = (Render_Engine *)data)) return; + + evas_outbuf_reconfigure(re->info, re->ob, w, h); + + if (re->tb) evas_common_tilebuf_free(re->tb); + if ((re->tb = evas_common_tilebuf_new(w, h))) + evas_common_tilebuf_set_tile_size(re->tb, TILESIZE, TILESIZE); +} + +static void +eng_output_tile_size_set(void *data, int w, int h) +{ + Render_Engine *re; + + /* try to get the render_engine */ + if (!(re = (Render_Engine *)data)) return; + if (re->tb) evas_common_tilebuf_set_tile_size(re->tb, w, h); +} + +static void +eng_output_redraws_rect_add(void *data, int x, int y, int w, int h) +{ + Render_Engine *re; + + /* try to get the render_engine */ + if (!(re = (Render_Engine *)data)) return; + if (re->tb) evas_common_tilebuf_add_redraw(re->tb, x, y, w, h); +} + +static void +eng_output_redraws_rect_del(void *data, int x, int y, int w, int h) +{ + Render_Engine *re; + + /* try to get the render_engine */ + if (!(re = (Render_Engine *)data)) return; + if (re->tb) evas_common_tilebuf_del_redraw(re->tb, x, y, w, h); +} + +static void +eng_output_redraws_clear(void *data) +{ + Render_Engine *re; + + /* try to get the render_engine */ + if (!(re = (Render_Engine *)data)) return; + if (re->tb) evas_common_tilebuf_clear(re->tb); +} + +static Tilebuf_Rect * +_merge_rects(Tilebuf *tb, Tilebuf_Rect *r1, Tilebuf_Rect *r2, Tilebuf_Rect *r3) +{ + Tilebuf_Rect *r, *rects; + + if (r1) + { + EINA_INLIST_FOREACH(EINA_INLIST_GET(r1), r) + evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h); + } + if (r2) + { + EINA_INLIST_FOREACH(EINA_INLIST_GET(r2), r) + evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h); + } + if (r3) + { + EINA_INLIST_FOREACH(EINA_INLIST_GET(r3), r) + evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h); + } + rects = evas_common_tilebuf_get_render_rects(tb); + +/* + // bounding box -> make a bounding box single region update of all regions. + // yes we could try and be smart and figure out size of regions, how far + // apart etc. etc. to try and figure out an optimal "set". this is a tradeoff + // between multiple update regions to render and total pixels to render. + if (rects) + { + px1 = rects->x; py1 = rects->y; + px2 = rects->x + rects->w; py2 = rects->y + rects->h; + EINA_INLIST_FOREACH(EINA_INLIST_GET(rects), r) + { + if (r->x < x1) px1 = r->x; + if (r->y < y1) py1 = r->y; + if ((r->x + r->w) > x2) px2 = r->x + r->w; + if ((r->y + r->h) > y2) py2 = r->y + r->h; + } + evas_common_tilebuf_free_render_rects(rects); + rects = calloc(1, sizeof(Tilebuf_Rect)); + if (rects) + { + rects->x = px1; + rects->y = py1; + rects->w = px2 - px1; + rects->h = py2 - py1; + } + } + */ + evas_common_tilebuf_clear(tb); + return rects; +} + +static void * +eng_output_redraws_next_update_get(void *data, int *x, int *y, int *w, int *h, int *cx, int *cy, int *cw, int *ch) +{ + Render_Engine *re; + RGBA_Image *img; + Tilebuf_Rect *rect; + + /* try to get the render_engine */ + if (!(re = (Render_Engine *)data)) return NULL; + +#define CLEAR_PREV_RECTS(x) \ + do { \ + if (re->prev_rects[x]) \ + evas_common_tilebuf_free_render_rects(re->prev_rects[x]); \ + re->prev_rects[x] = NULL; \ + } while (0) + + if (re->end) + { + re->end = EINA_FALSE; + return NULL; + } + + if (!re->rects) + { + re->mode = evas_outbuf_buffer_state_get(re->ob); + if ((re->rects = evas_common_tilebuf_get_render_rects(re->tb))) + { + if ((re->lost_back) || (re->mode == MODE_FULL)) + { + re->lost_back = EINA_FALSE; + evas_common_tilebuf_add_redraw(re->tb, + 0, 0, re->ob->w, re->ob->h); + evas_common_tilebuf_free_render_rects(re->rects); + re->rects = evas_common_tilebuf_get_render_rects(re->tb); + } + + evas_common_tilebuf_clear(re->tb); + CLEAR_PREV_RECTS(2); + re->prev_rects[2] = re->prev_rects[1]; + re->prev_rects[1] = re->prev_rects[0]; + re->prev_rects[0] = re->rects; + re->rects = NULL; + + switch (re->mode) + { + case MODE_FULL: + case MODE_COPY: + re->rects = + _merge_rects(re->tb, re->prev_rects[0], NULL, NULL); + break; + case MODE_DOUBLE: + re->rects = + _merge_rects(re->tb, re->prev_rects[0], re->prev_rects[1], NULL); + break; + case MODE_TRIPLE: + re->rects = + _merge_rects(re->tb, re->prev_rects[0], re->prev_rects[1], re->prev_rects[2]); + break; + default: + break; + } + } + + /* NB: Not sure this is entirely needed here as it's already done + * inside _merge_rects */ + /* evas_common_tilebuf_clear(re->tb); */ + re->cur_rect = EINA_INLIST_GET(re->rects); + } + + if (!re->cur_rect) return NULL; + rect = (Tilebuf_Rect *)re->cur_rect; + if (re->rects) + { + switch (re->mode) + { + case MODE_COPY: + case MODE_DOUBLE: + case MODE_TRIPLE: + rect = (Tilebuf_Rect *)re->cur_rect; + *x = rect->x; + *y = rect->y; + *w = rect->w; + *h = rect->h; + *cx = rect->x; + *cy = rect->y; + *cw = rect->w; + *ch = rect->h; + re->cur_rect = re->cur_rect->next; + break; + case MODE_FULL: + re->cur_rect = NULL; + if (x) *x = 0; + if (y) *y = 0; + if (w) *w = re->ob->w; + if (h) *h = re->ob->h; + if (cx) *cx = 0; + if (cy) *cy = 0; + if (cw) *cw = re->ob->w; + if (ch) *ch = re->ob->h; + break; + default: + break; + } + img = + evas_outbuf_update_region_new(re->ob, *x, *y, *w, *h, + cx, cy, cw, ch); + if (!re->cur_rect) + { + evas_common_tilebuf_free_render_rects(re->rects); + re->rects = NULL; + re->end = EINA_TRUE; + } + + return img; + } + + return NULL; +} + +static void +eng_output_redraws_next_update_push(void *data, void *img, int x, int y, int w, int h, Evas_Render_Mode render_mode) +{ + Render_Engine *re; + + if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return; + + if (!(re = (Render_Engine *)data)) return; +#if defined(BUILD_PIPE_RENDER) + evas_common_pipe_map_begin(img); +#endif + evas_outbuf_update_region_push(re->ob, img, x, y, w, h); + + /* NB: No reason to free region here. That is done on flush anyway */ + /* re->outbuf_update_region_free(re->ob, img); */ + + evas_common_cpu_end_opt(); +} + +static void +eng_output_flush(void *data, Evas_Render_Mode render_mode) +{ + Render_Engine *re; + + if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return; + + if (!(re = (Render_Engine *)data)) return; + evas_outbuf_flush(re->ob); + if (re->rects) evas_common_tilebuf_free_render_rects(re->rects); + re->rects = NULL; +} + /* module api functions */ static int module_open(Evas_Module *em) @@ -226,6 +503,15 @@ module_open(Evas_Module *em) EVAS_API_OVERRIDE(info_free, &func, eng_); EVAS_API_OVERRIDE(setup, &func, eng_); EVAS_API_OVERRIDE(output_free, &func, eng_); + EVAS_API_OVERRIDE(output_resize, &func, eng_); + EVAS_API_OVERRIDE(output_tile_size_set, &func, eng_); + EVAS_API_OVERRIDE(output_redraws_rect_add, &func, eng_); + EVAS_API_OVERRIDE(output_redraws_rect_del, &func, eng_); + EVAS_API_OVERRIDE(output_redraws_clear, &func, eng_); + EVAS_API_OVERRIDE(output_redraws_next_update_get, &func, eng_); + EVAS_API_OVERRIDE(output_redraws_next_update_push, &func, eng_); + EVAS_API_OVERRIDE(output_flush, &func, eng_); + /* EVAS_API_OVERRIDE(output_idle_flush, &func, eng_); */ /* advertise our engine functions */ em->functions = (void *)(&func); diff --git a/src/modules/evas/engines/drm/evas_engine.h b/src/modules/evas/engines/drm/evas_engine.h index 3cfbbeeb6a..5abfcd8a11 100644 --- a/src/modules/evas/engines/drm/evas_engine.h +++ b/src/modules/evas/engines/drm/evas_engine.h @@ -1,6 +1,22 @@ #ifndef EVAS_ENGINE_H # define EVAS_ENGINE_H +#include "evas_common_private.h" +#include "evas_macros.h" +#include "evas_private.h" +#include "Evas.h" +#include "Evas_Engine_Drm.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + extern int _evas_engine_drm_log_dom; # ifdef ERR @@ -28,6 +44,10 @@ extern int _evas_engine_drm_log_dom; # endif # define CRI(...) EINA_LOG_DOM_CRIT(_evas_engine_drm_log_dom, __VA_ARGS__) +/* define a maximum number of 'buffers' (double-buff, triple-buff, etc) */ +# define NUM_BUFFERS 2 + +typedef struct _Buffer Buffer; typedef struct _Outbuf Outbuf; enum @@ -38,15 +58,65 @@ enum MODE_TRIPLE }; +enum +{ + OUTBUF_DEPTH_NONE, + OUTBUF_DEPTH_ARGB_32BPP_8888_8888, + OUTBUF_DEPTH_RGB_32BPP_8888_8888, + OUTBUF_DEPTH_LAST +}; + +struct _Buffer +{ + int w, h; + int stride, size; + int handle; + unsigned int fb; + void *data; + + Eina_Bool valid : 1; +}; + struct _Outbuf { int w, h; unsigned int rotation, depth; Eina_Bool destination_alpha : 1; + + struct + { + RGBA_Image *onebuf; + Eina_Array onebuf_regions; + + Buffer buffer[NUM_BUFFERS], *sent; + int curr, num; + + int fd; + unsigned int conn, crtc; + drmModeModeInfo mode; + drmEventContext ctx; + Eina_Bool pending_flip : 1; + + Eina_List *pending_writes; + Eina_List *prev_pending_writes; + } priv; }; -Outbuf *evas_outbuf_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha); +Outbuf *evas_outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h); void evas_outbuf_free(Outbuf *ob); -void evas_outbuf_reconfigure(Outbuf *ob, int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha); +void evas_outbuf_reconfigure(Evas_Engine_Info_Drm *info, Outbuf *ob, int w, int h); +int evas_outbuf_buffer_state_get(Outbuf *ob); +RGBA_Image *evas_outbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch); +void evas_outbuf_update_region_push(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h); +void evas_outbuf_flush(Outbuf *ob); + +Eina_Bool evas_drm_init(Evas_Engine_Info_Drm *info, int card); +Eina_Bool evas_drm_shutdown(Evas_Engine_Info_Drm *info); + +Eina_Bool evas_drm_outbuf_setup(Outbuf *ob); +void evas_drm_outbuf_framebuffer_set(Outbuf *ob, Buffer *buffer); +Eina_Bool evas_drm_framebuffer_create(int fd, Buffer *buffer, int depth); +void evas_drm_framebuffer_destroy(int fd, Buffer *buffer); +Eina_Bool evas_drm_framebuffer_send(Outbuf *ob, Buffer *buffer, Eina_Rectangle *rects, unsigned int count); #endif diff --git a/src/modules/evas/engines/drm/evas_outbuf.c b/src/modules/evas/engines/drm/evas_outbuf.c index e6327b6d85..256f1b65a3 100644 --- a/src/modules/evas/engines/drm/evas_outbuf.c +++ b/src/modules/evas/engines/drm/evas_outbuf.c @@ -1,22 +1,135 @@ -#include "evas_common_private.h" -#include "evas_private.h" #include "evas_engine.h" +#ifdef EVAS_CSERVE2 +# include "evas_cs2_private.h" +#endif + +/* FIXME: We NEED to get the color map from the VT and use that for the mask */ +#define RED_MASK 0x00ff0000 +#define GREEN_MASK 0x0000ff00 +#define BLUE_MASK 0x000000ff + +static Eina_Bool +_evas_outbuf_buffer_new(Outbuf *ob, Buffer *buffer) +{ + /* check for valid outbuf */ + if (!ob) return EINA_FALSE; + + buffer->w = ob->w; + buffer->h = ob->h; + + /* sadly we cannot create buffers to be JUST the size of the canvas. + * drmModeSetCrtc will barf with ENOSPC if we try that :( so we have to + * allocate framebuffer objects to be the whole size of the display mode */ + + /* if (ob->priv.mode.hdisplay > buffer->w) */ + /* buffer->w = ob->priv.mode.hdisplay; */ + /* if (ob->priv.mode.vdisplay > buffer->h) */ + /* buffer->h = ob->priv.mode.vdisplay; */ + + /* create a drm fb for this buffer */ + if (!evas_drm_framebuffer_create(ob->priv.fd, buffer, ob->depth)) + { + CRI("Could not create drm framebuffer"); + return EINA_FALSE; + } + + /* evas_drm_outbuf_framebuffer_set(ob, buffer); */ + + return EINA_TRUE; +} + +static void * +_evas_outbuf_buffer_map(Outbuf *ob, int *w, int *h) +{ + if (w) *w = ob->w; + if (h) *h = ob->h; + return ob->priv.buffer[ob->priv.curr].data; +} + +static void +_evas_outbuf_buffer_put(Outbuf *ob, Buffer *buffer, Eina_Rectangle *rects, unsigned int count) +{ + /* validate input params */ + if ((!ob) || (!buffer)) return; + + if (!buffer->data) + { + CRI("Current Buffer Has No Data !!"); + /* TODO: call function to mmap buffer data */ + /* if (!_evas_outbuf_buffer_new(ob, buffer)); */ + return; + } + + if (ob->priv.sent != buffer) + { + if (!buffer->valid) evas_drm_outbuf_framebuffer_set(ob, buffer); + if (!evas_drm_framebuffer_send(ob, buffer, rects, count)) + ERR("Could not send buffer"); + } +} + +static void +_evas_outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects, unsigned int count) +{ + /* check for valid output buffer */ + if (!ob) return; + + _evas_outbuf_buffer_put(ob, &(ob->priv.buffer[ob->priv.curr]), + rects, count); +} Outbuf * -evas_outbuf_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha) +evas_outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h) { Outbuf *ob; + char *num; + int i = 0; - /* try to allocate space for out outbuf structure */ + /* try to allocate space for our outbuf structure */ if (!(ob = calloc(1, sizeof(Outbuf)))) return NULL; /* set some default outbuf properties */ ob->w = w; ob->h = h; - ob->rotation = rotation; - ob->depth = depth; - ob->destination_alpha = alpha; + ob->rotation = info->info.rotation; + ob->depth = info->info.depth; + ob->destination_alpha = info->info.destination_alpha; + + /* set drm file descriptor */ + ob->priv.fd = info->info.fd; + + /* try to setup the drm card for this outbuf */ + if (!evas_drm_outbuf_setup(ob)) + { + free(ob); + return NULL; + } + + ob->priv.num = NUM_BUFFERS; + + /* check for buffer override */ + if ((num = getenv("EVAS_DRM_BUFFERS"))) + { + ob->priv.num = atoi(num); + + /* cap maximum # of buffers */ + if (ob->priv.num <= 0) ob->priv.num = 1; + else if (ob->priv.num > 3) ob->priv.num = 3; + } + + /* with the connector and crtc set, we can now create buffers !! :) */ + for (; i < ob->priv.num; i++) + { + if (!_evas_outbuf_buffer_new(ob, &(ob->priv.buffer[i]))) + { + CRI("Failed to create buffer"); + break; + } + } + + /* set array step size for regions */ + eina_array_step_set(&ob->priv.onebuf_regions, sizeof(Eina_Array), 8); /* return the allocated outbuf structure */ return ob; @@ -25,21 +138,431 @@ evas_outbuf_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_ void evas_outbuf_free(Outbuf *ob) { + int i = 0; + + /* destroy the buffers */ + for (; i < ob->priv.num; i++) + evas_drm_framebuffer_destroy(ob->priv.fd, &(ob->priv.buffer[i])); + + eina_array_flush(&ob->priv.onebuf_regions); + /* free the allocated outbuf structure */ free(ob); } void -evas_outbuf_reconfigure(Outbuf *ob, int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha) +evas_outbuf_reconfigure(Evas_Engine_Info_Drm *info, Outbuf *ob, int w, int h) { + int i = 0; + /* check for changes */ - if ((ob->w == w) && (ob->h == h) && (ob->destination_alpha == alpha) && - (ob->rotation == rotation) && (ob->depth == depth)) return; + if ((ob->w == w) && (ob->h == h) && + (ob->destination_alpha == info->info.destination_alpha) && + (ob->rotation == info->info.rotation) && + (ob->depth == info->info.depth)) + return; /* set new outbuf properties */ - ob->w = w; - ob->h = h; - ob->rotation = rotation; - ob->depth = depth; - ob->destination_alpha = alpha; + ob->rotation =info->info. rotation; + ob->depth = info->info.depth; + ob->destination_alpha = info->info.destination_alpha; + + /* handle rotation */ + if ((ob->rotation == 0) || (ob->rotation == 180)) + { + ob->w = w; + ob->h = h; + } + else + { + ob->w = h; + ob->h = w; + } + + /* destroy the old buffers */ + for (; i < ob->priv.num; i++) + evas_drm_framebuffer_destroy(ob->priv.fd, &(ob->priv.buffer[i])); + + for (i = 0; i < ob->priv.num; i++) + { + if (!_evas_outbuf_buffer_new(ob, &(ob->priv.buffer[i]))) + { + CRI("Failed to create buffer"); + break; + } + } +} + +int +evas_outbuf_buffer_state_get(Outbuf *ob) +{ + int i = 0, n = 0, count = 0; + + /* check for valid output buffer */ + if (!ob) return MODE_FULL; + + for (; i < ob->priv.num; i++) + { + n = (ob->priv.num + ob->priv.curr - i) % ob->priv.num; + if (ob->priv.buffer[n].valid) count++; + else break; + } + + if (count == ob->priv.num) + { + if (count == 1) return MODE_COPY; + else if (count == 2) return MODE_DOUBLE; + else if (count == 3) return MODE_TRIPLE; + } + + return MODE_FULL; +} + +RGBA_Image * +evas_outbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch) +{ + RGBA_Image *img; + Eina_Rectangle *rect; + + RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, ob->w, ob->h); + if ((w <= 0) || (h <= 0)) return NULL; + + if (ob->rotation == 0) + { + if (!(img = ob->priv.onebuf)) + { + int bpl = 0; + int bw = 0, bh = 0; + void *data; + + data = _evas_outbuf_buffer_map(ob, &bw, &bh); + bpl = (bw * sizeof(int)); + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + img = (RGBA_Image *)evas_cache2_image_data(evas_common_image_cache2_get(), + bpl / sizeof(int), bh, + data, + ob->destination_alpha, + EVAS_COLORSPACE_ARGB8888); + else +#endif + img = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(), + bpl / sizeof(int), bh, + data, + ob->destination_alpha, + EVAS_COLORSPACE_ARGB8888); + + ob->priv.onebuf = img; + if (!img) return NULL; + } + + if (!(rect = eina_rectangle_new(x, y, w, h))) + return NULL; + + if (!eina_array_push(&ob->priv.onebuf_regions, rect)) + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_close(&img->cache_entry); + else +#endif + evas_cache_image_drop(&img->cache_entry); + + eina_rectangle_free(rect); + + return NULL; + } + + /* clip the region to the onebuf region */ + if (cx) *cx = x; + if (cy) *cy = y; + if (cw) *cw = w; + if (ch) *ch = h; + + return img; + } + else + { + if (!(rect = eina_rectangle_new(x, y, w, h))) + return NULL; + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + img = (RGBA_Image *)evas_cache2_image_empty(evas_common_image_cache2_get()); + else +#endif + img = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); + + if (!img) + { + eina_rectangle_free(rect); + return NULL; + } + + img->cache_entry.flags.alpha |= ob->destination_alpha; + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_surface_alloc(&img->cache_entry, w, h); + else +#endif + evas_cache_image_surface_alloc(&img->cache_entry, w, h); + + img->extended_info = rect; + + ob->priv.pending_writes = + eina_list_append(ob->priv.pending_writes, img); + + if (cx) *cx = 0; + if (cy) *cy = 0; + if (cw) *cw = w; + if (ch) *ch = h; + return img; + } + + return NULL; +} + +void +evas_outbuf_update_region_push(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h) +{ + Gfx_Func_Convert func = NULL; + Eina_Rectangle rect = {0, 0, 0, 0}, pr; + DATA32 *src; + DATA8 *dst; + int depth = 32, bpp = 0, bpl = 0, wid = 0; + int ww = 0, hh = 0; + int rx = 0, ry = 0; + + /* check for valid output buffer */ + if (!ob) return; + + /* check for pending writes */ + if (!ob->priv.pending_writes) return; + + if ((ob->rotation == 0) || (ob->rotation == 180)) + { + func = + evas_common_convert_func_get(0, w, h, depth, + RED_MASK, GREEN_MASK, BLUE_MASK, + PAL_MODE_NONE, ob->rotation); + } + else if ((ob->rotation == 90) || (ob->rotation == 270)) + { + func = + evas_common_convert_func_get(0, h, w, depth, + RED_MASK, GREEN_MASK, BLUE_MASK, + PAL_MODE_NONE, ob->rotation); + } + + /* make sure we have a valid convert function */ + if (!func) return; + + /* based on rotation, set rectangle position */ + if (ob->rotation == 0) + { + rect.x = x; + rect.y = y; + } + else if (ob->rotation == 90) + { + rect.x = y; + rect.y = (ob->w - x - w); + } + else if (ob->rotation == 180) + { + rect.x = (ob->w - x - w); + rect.y = (ob->h - y - h); + } + else if (ob->rotation == 270) + { + rect.x = (ob->h - y - h); + rect.y = x; + } + + /* based on rotation, set rectangle size */ + if ((ob->rotation == 0) || (ob->rotation == 180)) + { + rect.w = w; + rect.h = h; + } + else if ((ob->rotation == 90) || (ob->rotation == 270)) + { + rect.w = h; + rect.h = w; + } + + /* check for valid update image data */ + if (!(src = update->image.data)) return; + + bpp = depth / 8; + if (bpp <= 0) return; + + /* check for valid desination data */ + if (!(dst = _evas_outbuf_buffer_map(ob, &ww, &hh))) return; + + bpl = (ww * sizeof(int)); + + if (ob->rotation == 0) + { + RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh); + dst += (bpl * rect.y) + (rect.x + bpp); + w -= rx; + } + else if (ob->rotation == 180) + { + pr = rect; + RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh); + rx = pr.w - rect.w; + ry = pr.h - rect.h; + src += (update->cache_entry.w * ry) + rx; + w -= rx; + } + else if (ob->rotation == 90) + { + pr = rect; + RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh); + rx = pr.w - rect.w; ry = pr.h - rect.h; + src += ry; + w -= ry; + } + else if (ob->rotation == 270) + { + pr = rect; + RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh); + rx = pr.w - rect.w; ry = pr.h - rect.h; + src += (update->cache_entry.w * rx); + w -= ry; + } + + if ((rect.w <= 0) || (rect.h <= 0)) return; + + wid = bpl / bpp; + + dst += (bpl * rect.y) + (rect.x * bpp); + + func(src, dst, (update->cache_entry.w - w), (wid - rect.w), + rect.w, rect.h, x + rx, y + ry, NULL); +} + +void +evas_outbuf_flush(Outbuf *ob) +{ + Eina_Rectangle *rects; + RGBA_Image *img; + unsigned int n = 0, i = 0; + + /* check for valid output buffer */ + if (!ob) return; + + /* check for pending writes */ + if (!ob->priv.pending_writes) + { + Eina_Rectangle *rect; + Eina_Array_Iterator it; + + /* get number of buffer regions */ + n = eina_array_count_get(&ob->priv.onebuf_regions); + if (n == 0) return; + + /* allocate rectangles */ + if (!(rects = alloca(n * sizeof(Eina_Rectangle)))) return; + + /* loop the buffer regions and assign to rects */ + EINA_ARRAY_ITER_NEXT(&ob->priv.onebuf_regions, i, rect, it) + rects[i] = *rect; + + /* TODO: unmap the buffer ?? */ + /* evas_swapper_buffer_unmap(ob->priv.swapper); */ + + /* force a buffer swap */ + _evas_outbuf_buffer_swap(ob, rects, n); + + /* clean array */ + eina_array_clean(&ob->priv.onebuf_regions); + + img = ob->priv.onebuf; + ob->priv.onebuf = NULL; + if (img) + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_close(&img->cache_entry); + else +#endif + evas_cache_image_drop(&img->cache_entry); + } + } + else + { + /* get number of pending writes */ + n = eina_list_count(ob->priv.pending_writes); + if (n == 0) return; + + /* allocate rectangles */ + if (!(rects = alloca(n * sizeof(Eina_Rectangle)))) return; + + /* loop the pending writes */ + EINA_LIST_FREE(ob->priv.pending_writes, img) + { + Eina_Rectangle *rect; + int x = 0, y = 0, w = 0, h = 0; + + if (!(rect = img->extended_info)) continue; + + x = rect->x; y = rect->y; w = rect->w; h = rect->h; + + /* based on rotation, set rectangle position */ + if (ob->rotation == 0) + { + rects[i].x = x; + rects[i].y = y; + } + else if (ob->rotation == 90) + { + rects[i].x = y; + rects[i].y = (ob->w - x - w); + } + else if (ob->rotation == 180) + { + rects[i].x = (ob->w - x - w); + rects[i].y = (ob->h - y - h); + } + else if (ob->rotation == 270) + { + rects[i].x = (ob->h - y - h); + rects[i].y = x; + } + + /* based on rotation, set rectangle size */ + if ((ob->rotation == 0) || (ob->rotation == 180)) + { + rects[i].w = w; + rects[i].h = h; + } + else if ((ob->rotation == 90) || (ob->rotation == 270)) + { + rects[i].w = h; + rects[i].h = w; + } + + eina_rectangle_free(rect); + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_close(&img->cache_entry); + else +#endif + evas_cache_image_drop(&img->cache_entry); + + i++; + } + + /* TODO: unmap the buffer ?? */ + /* evas_swapper_buffer_unmap(ob->priv.swapper); */ + + /* force a buffer swap */ + _evas_outbuf_buffer_swap(ob, rects, n); + } }