efl/src/modules/evas/engines/drm/evas_outbuf.c

537 lines
13 KiB
C

#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 0xff0000
#define GREEN_MASK 0x00ff00
#define BLUE_MASK 0x0000ff
static void
_outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects, unsigned int count)
{
/* Ecore_Drm2_Plane *plane; */
Outbuf_Fb *ofb;
ofb = ob->priv.draw;
if (!ofb) return;
ecore_drm2_fb_dirty(ofb->fb, rects, count);
if (!ob->priv.plane)
ob->priv.plane = ecore_drm2_plane_assign(ob->priv.output, ofb->fb, 0, 0);
else ecore_drm2_plane_fb_set(ob->priv.plane, ofb->fb);
ecore_drm2_fb_flip(ofb->fb, ob->priv.output);
ofb->drawn = EINA_TRUE;
ofb->age = 0;
}
static Eina_Bool
_outbuf_fb_create(Outbuf *ob, Outbuf_Fb *ofb)
{
ofb->fb =
ecore_drm2_fb_create(ob->dev, ob->w, ob->h,
ob->depth, ob->bpp, ob->format);
if (!ofb->fb) return EINA_FALSE;
ofb->age = 0;
ofb->drawn = EINA_FALSE;
ofb->valid = EINA_TRUE;
return EINA_TRUE;
}
static void
_outbuf_fb_destroy(Outbuf_Fb *ofb)
{
ecore_drm2_fb_discard(ofb->fb);
memset(ofb, 0, sizeof(*ofb));
ofb->valid = EINA_FALSE;
ofb->drawn = EINA_FALSE;
ofb->age = 0;
}
Outbuf *
_outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h)
{
Outbuf *ob;
char *num;
int i = 0;
ob = calloc(1, sizeof(Outbuf));
if (!ob) return NULL;
ob->w = w;
ob->h = h;
ob->dev = info->info.dev;
ob->alpha = info->info.alpha;
ob->rotation = info->info.rotation;
ob->bpp = info->info.bpp;
ob->depth = info->info.depth;
ob->format = info->info.format;
ob->priv.output = info->info.output;
ob->priv.num = 3;
num = getenv("EVAS_DRM_BUFFERS");
if (num)
{
ob->priv.num = atoi(num);
if (ob->priv.num <= 0) ob->priv.num = 3;
else if (ob->priv.num > 4) ob->priv.num = 4;
}
if ((!w) || (!h)) return ob;
for (i = 0; i < ob->priv.num; i++)
{
if (!_outbuf_fb_create(ob, &(ob->priv.ofb[i])))
{
WRN("Failed to create framebuffer %d", i);
continue;
}
}
return ob;
}
void
_outbuf_free(Outbuf *ob)
{
int i = 0;
for (i = 0; i < ob->priv.num; i++)
_outbuf_fb_destroy(&ob->priv.ofb[i]);
free(ob);
}
int
_outbuf_rotation_get(Outbuf *ob)
{
return ob->rotation;
}
void
_outbuf_reconfigure(Outbuf *ob, int w, int h, int rotation, Outbuf_Depth depth)
{
int i = 0;
unsigned int format = DRM_FORMAT_ARGB8888;
switch (depth)
{
case OUTBUF_DEPTH_RGB_16BPP_565_565_DITHERED:
format = DRM_FORMAT_RGB565;
break;
case OUTBUF_DEPTH_RGB_16BPP_555_555_DITHERED:
format = DRM_FORMAT_RGBX5551;
break;
case OUTBUF_DEPTH_RGB_16BPP_444_444_DITHERED:
format = DRM_FORMAT_RGBX4444;
break;
case OUTBUF_DEPTH_RGB_16BPP_565_444_DITHERED:
format = DRM_FORMAT_RGB565;
break;
case OUTBUF_DEPTH_RGB_32BPP_888_8888:
format = DRM_FORMAT_RGBX8888;
break;
case OUTBUF_DEPTH_ARGB_32BPP_8888_8888:
format = DRM_FORMAT_ARGB8888;
break;
case OUTBUF_DEPTH_BGRA_32BPP_8888_8888:
format = DRM_FORMAT_BGRA8888;
break;
case OUTBUF_DEPTH_BGR_32BPP_888_8888:
format = DRM_FORMAT_BGRX8888;
break;
case OUTBUF_DEPTH_RGB_24BPP_888_888:
format = DRM_FORMAT_RGB888;
break;
case OUTBUF_DEPTH_BGR_24BPP_888_888:
format = DRM_FORMAT_BGR888;
break;
case OUTBUF_DEPTH_INHERIT:
default:
depth = ob->depth;
format = ob->format;
break;
}
if ((ob->w == w) && (ob->h == h) && (ob->rotation == rotation) &&
(ob->depth == depth) && (ob->format == format))
return;
ob->depth = depth;
ob->format = format;
ob->rotation = rotation;
ob->w = w;
ob->h = h;
if ((ob->rotation == 90) || (ob->rotation == 270))
{
ob->w = h;
ob->h = w;
}
for (i = 0; i < ob->priv.num; i++)
_outbuf_fb_destroy(&ob->priv.ofb[i]);
if ((!w) || (!h)) return;
for (i = 0; i < ob->priv.num; i++)
{
if (!_outbuf_fb_create(ob, &(ob->priv.ofb[i])))
{
WRN("Failed to create framebuffer %d", i);
continue;
}
}
}
static Outbuf_Fb *
_outbuf_fb_wait(Outbuf *ob)
{
int i = 0, best = -1, best_age = -1;
/* We pick the oldest available buffer to avoid using the same two
* repeatedly and then having the third be stale when we need it
*/
for (i = 0; i < ob->priv.num; i++)
{
if (ecore_drm2_fb_busy_get(ob->priv.ofb[i].fb)) continue;
if (ob->priv.ofb[i].valid && (ob->priv.ofb[i].age > best_age))
{
best = i;
best_age = ob->priv.ofb[i].age;
}
}
if (best >= 0) return &(ob->priv.ofb[best]);
return NULL;
}
static Eina_Bool
_outbuf_fb_assign(Outbuf *ob)
{
int i;
ob->priv.draw = _outbuf_fb_wait(ob);
while (!ob->priv.draw)
{
ecore_drm2_fb_release(ob->priv.output, EINA_TRUE);
ob->priv.draw = _outbuf_fb_wait(ob);
}
for (i = 0; i < ob->priv.num; i++)
{
if ((ob->priv.ofb[i].valid) && (ob->priv.ofb[i].drawn))
{
ob->priv.ofb[i].age++;
if (ob->priv.ofb[i].age > 4)
{
ob->priv.ofb[i].age = 0;
ob->priv.ofb[i].drawn = EINA_FALSE;
}
}
}
return EINA_TRUE;
}
Render_Engine_Swap_Mode
_outbuf_state_get(Outbuf *ob)
{
int age;
if (!_outbuf_fb_assign(ob)) return MODE_FULL;
age = ob->priv.draw->age;
if (age > ob->priv.num) return MODE_FULL;
else if (age == 1) return MODE_COPY;
else if (age == 2) return MODE_DOUBLE;
else if (age == 3) return MODE_TRIPLE;
else if (age == 4) return MODE_QUADRUPLE;
return MODE_FULL;
}
void *
_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 = NULL;
if ((w <= 0) || (h <= 0)) return NULL;
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, ob->w, ob->h);
if (ob->rotation == 0) // && (ob->depth == 32))
{
Eina_Rectangle *rect;
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->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;
if (cx) *cx = 0;
if (cy) *cy = 0;
if (cw) *cw = w;
if (ch) *ch = h;
/* add this cached image data to pending writes */
ob->priv.pending =
eina_list_append(ob->priv.pending, img);
}
return img;
}
void
_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;
Ecore_Drm2_Fb *buff;
int bpp = 0, bpl = 0;
int rx = 0, ry = 0;
int bw = 0, bh = 0;
/* check for valid output buffer */
if (!ob) return;
/* check for pending writes */
if (!ob->priv.pending) return;
/* check for valid source data */
if (!(src = update->image.data)) return;
/* check for valid desination data */
if (!ob->priv.draw) return;
buff = ob->priv.draw->fb;
dst = ecore_drm2_fb_data_get(buff);
if (!dst) return;
if ((ob->rotation == 0) || (ob->rotation == 180))
{
func =
evas_common_convert_func_get(0, w, h, ob->bpp,
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, ob->bpp,
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;
}
bpp = ob->bpp / 8;
bw = ob->w;
bh = ob->h;
/* bpp = (ob->depth / 8); */
/* if (bpp <= 0) return; */
bpl = ecore_drm2_fb_stride_get(buff);
if (ob->rotation == 0)
{
RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h,
0, 0, bw, bh);
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, bw, bh);
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, bw, bh);
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, bw, bh);
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;
func(src, dst, (update->cache_entry.w - w), ((bpl / bpp) - rect.w),
rect.w, rect.h, x + rx, y + ry, NULL);
}
void
_outbuf_update_region_free(Outbuf *ob EINA_UNUSED, RGBA_Image *update EINA_UNUSED)
{
}
void
_outbuf_flush(Outbuf *ob, Tilebuf_Rect *surface_damage EINA_UNUSED, Tilebuf_Rect *buffer_damage EINA_UNUSED, Evas_Render_Mode render_mode)
{
Eina_Rectangle *r;
RGBA_Image *img;
unsigned int i = 0;
if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;
if (ob->priv.rect_count) free(ob->priv.rects);
/* get number of pending writes */
ob->priv.rect_count = eina_list_count(ob->priv.pending);
if (ob->priv.rect_count == 0) return;
/* allocate rectangles */
ob->priv.rects = malloc(ob->priv.rect_count * sizeof(Eina_Rectangle));
if (!ob->priv.rects) return;
r = ob->priv.rects;
/* loop the pending writes */
EINA_LIST_FREE(ob->priv.pending, img)
{
Eina_Rectangle *rect;
int x = 0, y = 0, w = 0, h = 0;
rect = img->extended_info;
if (!rect) continue;
x = rect->x; y = rect->y; w = rect->w; h = rect->h;
/* based on rotation, set rectangle position */
if (ob->rotation == 0)
{
r[i].x = x;
r[i].y = y;
}
else if (ob->rotation == 90)
{
r[i].x = y;
r[i].y = (ob->w - x - w);
}
else if (ob->rotation == 180)
{
r[i].x = (ob->w - x - w);
r[i].y = (ob->h - y - h);
}
else if (ob->rotation == 270)
{
r[i].x = (ob->h - y - h);
r[i].y = x;
}
/* based on rotation, set rectangle size */
if ((ob->rotation == 0) || (ob->rotation == 180))
{
r[i].w = w;
r[i].h = h;
}
else if ((ob->rotation == 90) || (ob->rotation == 270))
{
r[i].w = h;
r[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++;
}
}
void
_outbuf_redraws_clear(Outbuf *ob)
{
if (!ob->priv.rect_count) return;
_outbuf_buffer_swap(ob, ob->priv.rects, ob->priv.rect_count);
free(ob->priv.rects);
ob->priv.rect_count = 0;
}