forked from enlightenment/efl
evas-drm: Add Evas Drm Engine (software only currently)
@feature: Add working Evas_Drm Engine (software only currently) Signed-off-by: Chris Michael <cp.michael@samsung.com>
This commit is contained in:
parent
b36adec952
commit
b42af265be
|
@ -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 */
|
||||
|
|
|
@ -0,0 +1,630 @@
|
|||
#include "evas_engine.h"
|
||||
#include <linux/vt.h>
|
||||
#include <linux/kd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -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->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;
|
||||
ob->rotation = rotation;
|
||||
ob->depth = depth;
|
||||
ob->destination_alpha = alpha;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue