595 lines
14 KiB
C
595 lines
14 KiB
C
#include "evas_engine.h"
|
|
|
|
/* 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
|
|
|
|
#define MAX_BUFFERS 10
|
|
#define QUEUE_TRIM_DURATION 100
|
|
|
|
static Outbuf_Fb *
|
|
_outbuf_fb_create(Outbuf *ob, int w, int h)
|
|
{
|
|
Outbuf_Fb *out;
|
|
|
|
out = calloc(1, sizeof(Outbuf_Fb));
|
|
if (!out) return NULL;
|
|
|
|
out->fb =
|
|
ecore_drm2_fb_create(ob->dev, w, h,
|
|
ob->depth, ob->bpp, ob->format);
|
|
if (!out->fb)
|
|
{
|
|
WRN("Failed To Create FB: %d %d", w, h);
|
|
free(out);
|
|
return NULL;
|
|
}
|
|
|
|
out->age = 0;
|
|
out->drawn = EINA_FALSE;
|
|
out->valid = EINA_TRUE;
|
|
|
|
return out;
|
|
}
|
|
|
|
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;
|
|
free(ofb);
|
|
}
|
|
|
|
static Outbuf_Fb *
|
|
_outbuf_fb_wait(Outbuf *ob)
|
|
{
|
|
Eina_List *l;
|
|
Outbuf_Fb *ofb, *best = NULL;
|
|
int best_age = -1, num_required = 1, num_allocated = 0;
|
|
|
|
/* We pick the oldest available buffer to avoid using the same two
|
|
* repeatedly and then having the third be stale when we need it
|
|
*/
|
|
EINA_LIST_FOREACH(ob->priv.fb_list, l, ofb)
|
|
{
|
|
num_allocated++;
|
|
if (ecore_drm2_fb_busy_get(ofb->fb))
|
|
{
|
|
num_required++;
|
|
continue;
|
|
}
|
|
if (ofb->valid && (ofb->age > best_age))
|
|
{
|
|
best = ofb;
|
|
best_age = best->age;
|
|
}
|
|
}
|
|
|
|
if (num_required < num_allocated)
|
|
ob->priv.unused_duration++;
|
|
else
|
|
ob->priv.unused_duration = 0;
|
|
|
|
/* If we've had unused buffers for longer than QUEUE_TRIM_DURATION, then
|
|
* destroy the oldest buffer (currently in best) and recursively call
|
|
* ourself to get the next oldest.
|
|
*/
|
|
if (best && (ob->priv.unused_duration > QUEUE_TRIM_DURATION))
|
|
{
|
|
ob->priv.unused_duration = 0;
|
|
ob->priv.fb_list = eina_list_remove(ob->priv.fb_list, best);
|
|
_outbuf_fb_destroy(best);
|
|
best = _outbuf_fb_wait(ob);
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
static Outbuf_Fb *
|
|
_outbuf_fb_assign(Outbuf *ob)
|
|
{
|
|
int fw = 0, fh = 0;
|
|
Outbuf_Fb *ofb;
|
|
Eina_List *l;
|
|
|
|
ob->priv.draw = _outbuf_fb_wait(ob);
|
|
if (!ob->priv.draw)
|
|
{
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(eina_list_count(ob->priv.fb_list) >= MAX_BUFFERS, NULL);
|
|
|
|
if ((ob->rotation == 0) || (ob->rotation == 180))
|
|
{
|
|
fw = ob->w;
|
|
fh = ob->h;
|
|
}
|
|
else if ((ob->rotation == 90) || (ob->rotation == 270))
|
|
{
|
|
fw = ob->h;
|
|
fh = ob->w;
|
|
}
|
|
ob->priv.draw = _outbuf_fb_create(ob, fw, fh);
|
|
if (ob->priv.draw)
|
|
ob->priv.fb_list = eina_list_append(ob->priv.fb_list, ob->priv.draw);
|
|
}
|
|
|
|
while (!ob->priv.draw)
|
|
{
|
|
ecore_drm2_fb_release(ob->priv.output, EINA_TRUE);
|
|
ob->priv.draw = _outbuf_fb_wait(ob);
|
|
}
|
|
|
|
EINA_LIST_FOREACH(ob->priv.fb_list, l, ofb)
|
|
{
|
|
if ((ofb->valid) && (ofb->drawn))
|
|
{
|
|
ofb->age++;
|
|
if (ofb->age > 4)
|
|
{
|
|
ofb->age = 0;
|
|
ofb->drawn = EINA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ob->priv.draw;
|
|
}
|
|
|
|
static void
|
|
_outbuf_buffer_swap(Outbuf *ob)
|
|
{
|
|
Outbuf_Fb *ofb;
|
|
|
|
ofb = ob->priv.draw;
|
|
if (!ofb)
|
|
{
|
|
ecore_drm2_fb_release(ob->priv.output, EINA_TRUE);
|
|
ofb = _outbuf_fb_assign(ob);
|
|
if (!ofb)
|
|
{
|
|
ERR("Could not assign front buffer");
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Outbuf *
|
|
_outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h)
|
|
{
|
|
Outbuf *ob;
|
|
|
|
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;
|
|
|
|
return ob;
|
|
}
|
|
|
|
void
|
|
_outbuf_free(Outbuf *ob)
|
|
{
|
|
Outbuf_Fb *ofb;
|
|
|
|
while (ob->priv.pending)
|
|
{
|
|
RGBA_Image *img;
|
|
Eina_Rectangle *rect;
|
|
|
|
img = ob->priv.pending->data;
|
|
ob->priv.pending =
|
|
eina_list_remove_list(ob->priv.pending, ob->priv.pending);
|
|
|
|
rect = img->extended_info;
|
|
|
|
evas_cache_image_drop(&img->cache_entry);
|
|
|
|
eina_rectangle_free(rect);
|
|
}
|
|
|
|
/* TODO: idle flush */
|
|
|
|
_outbuf_flush(ob, NULL, NULL, EVAS_RENDER_MODE_UNDEF);
|
|
|
|
EINA_LIST_FREE(ob->priv.fb_list, ofb)
|
|
_outbuf_fb_destroy(ofb);
|
|
|
|
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)
|
|
{
|
|
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->w = w;
|
|
ob->h = h;
|
|
ob->depth = depth;
|
|
ob->format = format;
|
|
ob->rotation = rotation;
|
|
ob->priv.unused_duration = 0;
|
|
|
|
_outbuf_idle_flush(ob);
|
|
}
|
|
|
|
Render_Output_Swap_Mode
|
|
_outbuf_state_get(Outbuf *ob)
|
|
{
|
|
int age;
|
|
|
|
if (!ob->priv.draw) return MODE_FULL;
|
|
|
|
age = ob->priv.draw->age;
|
|
if (age > 4) 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;
|
|
Eina_Rectangle *rect;
|
|
|
|
if ((w <= 0) || (h <= 0)) return NULL;
|
|
|
|
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, ob->w, ob->h);
|
|
|
|
if (!(rect = eina_rectangle_new(x, y, w, h)))
|
|
return NULL;
|
|
|
|
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;
|
|
|
|
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_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);
|
|
|
|
evas_cache_image_drop(&img->cache_entry);
|
|
|
|
i++;
|
|
}
|
|
|
|
_outbuf_buffer_swap(ob);
|
|
}
|
|
|
|
void
|
|
_outbuf_damage_region_set(Outbuf *ob, Tilebuf_Rect *damage)
|
|
{
|
|
Tilebuf_Rect *tr;
|
|
Eina_Rectangle *rects;
|
|
Ecore_Drm2_Fb *fb;
|
|
int count, i = 0;
|
|
|
|
if (!ob->priv.draw) return;
|
|
|
|
fb = ob->priv.draw->fb;
|
|
|
|
count = eina_inlist_count(EINA_INLIST_GET(damage));
|
|
rects = alloca(count * sizeof(Eina_Rectangle));
|
|
|
|
EINA_INLIST_FOREACH(damage, tr)
|
|
{
|
|
rects[i].x = tr->x;
|
|
rects[i].y = tr->y;
|
|
rects[i].w = tr->w;
|
|
rects[i].h = tr->h;
|
|
i++;
|
|
}
|
|
|
|
ecore_drm2_fb_dirty(fb, rects, count);
|
|
}
|
|
|
|
void
|
|
_outbuf_idle_flush(Outbuf *ob)
|
|
{
|
|
while (ob->priv.pending)
|
|
{
|
|
RGBA_Image *img;
|
|
Eina_Rectangle *rect;
|
|
|
|
img = ob->priv.pending->data;
|
|
ob->priv.pending =
|
|
eina_list_remove_list(ob->priv.pending, ob->priv.pending);
|
|
|
|
rect = img->extended_info;
|
|
|
|
evas_cache_image_drop(&img->cache_entry);
|
|
|
|
eina_rectangle_free(rect);
|
|
}
|
|
|
|
while (ecore_drm2_fb_release(ob->priv.output, EINA_TRUE));
|
|
}
|