1 #include "evas_engine.h"
2 
3 /* FIXME: We NEED to get the color map from the VT and use that for the mask */
4 #define RED_MASK 0xff0000
5 #define GREEN_MASK 0x00ff00
6 #define BLUE_MASK 0x0000ff
7 
8 #define MAX_BUFFERS 10
9 #define QUEUE_TRIM_DURATION 100
10 
11 static Outbuf_Fb *
_outbuf_fb_create(Outbuf * ob,int w,int h)12 _outbuf_fb_create(Outbuf *ob, int w, int h)
13 {
14    Outbuf_Fb *out;
15 
16    out = calloc(1, sizeof(Outbuf_Fb));
17    if (!out) return NULL;
18 
19    out->fb =
20      ecore_drm2_fb_create(ob->dev, w, h,
21                           ob->depth, ob->bpp, ob->format);
22    if (!out->fb)
23      {
24         WRN("Failed To Create FB: %d %d", w, h);
25         free(out);
26         return NULL;
27      }
28 
29    out->age = 0;
30    out->drawn = EINA_FALSE;
31    out->valid = EINA_TRUE;
32 
33    return out;
34 }
35 
36 static void
_outbuf_fb_destroy(Outbuf_Fb * ofb)37 _outbuf_fb_destroy(Outbuf_Fb *ofb)
38 {
39    ecore_drm2_fb_discard(ofb->fb);
40 
41    memset(ofb, 0, sizeof(*ofb));
42    ofb->valid = EINA_FALSE;
43    ofb->drawn = EINA_FALSE;
44    ofb->age = 0;
45    free(ofb);
46 }
47 
48 static Outbuf_Fb *
_outbuf_fb_wait(Outbuf * ob)49 _outbuf_fb_wait(Outbuf *ob)
50 {
51    Eina_List *l;
52    Outbuf_Fb *ofb, *best = NULL;
53    int best_age = -1, num_required = 1, num_allocated = 0;
54 
55    /* We pick the oldest available buffer to avoid using the same two
56     * repeatedly and then having the third be stale when we need it
57     */
58    EINA_LIST_FOREACH(ob->priv.fb_list, l, ofb)
59      {
60         num_allocated++;
61         if (ecore_drm2_fb_busy_get(ofb->fb))
62           {
63              num_required++;
64              continue;
65           }
66         if (ofb->valid && (ofb->age > best_age))
67           {
68              best = ofb;
69              best_age = best->age;
70           }
71      }
72 
73    if (num_required < num_allocated)
74       ob->priv.unused_duration++;
75    else
76       ob->priv.unused_duration = 0;
77 
78    /* If we've had unused buffers for longer than QUEUE_TRIM_DURATION, then
79     * destroy the oldest buffer (currently in best) and recursively call
80     * ourself to get the next oldest.
81     */
82    if (best && (ob->priv.unused_duration > QUEUE_TRIM_DURATION))
83      {
84         ob->priv.unused_duration = 0;
85         ob->priv.fb_list = eina_list_remove(ob->priv.fb_list, best);
86         _outbuf_fb_destroy(best);
87         best = _outbuf_fb_wait(ob);
88      }
89 
90    return best;
91 }
92 
93 static Outbuf_Fb *
_outbuf_fb_assign(Outbuf * ob)94 _outbuf_fb_assign(Outbuf *ob)
95 {
96    int fw = 0, fh = 0;
97    Outbuf_Fb *ofb;
98    Eina_List *l;
99 
100    ob->priv.draw = _outbuf_fb_wait(ob);
101    if (!ob->priv.draw)
102      {
103         EINA_SAFETY_ON_TRUE_RETURN_VAL(eina_list_count(ob->priv.fb_list) >= MAX_BUFFERS, NULL);
104 
105         if ((ob->rotation == 0) || (ob->rotation == 180))
106           {
107              fw = ob->w;
108              fh = ob->h;
109           }
110         else if ((ob->rotation == 90) || (ob->rotation == 270))
111           {
112              fw = ob->h;
113              fh = ob->w;
114           }
115         ob->priv.draw = _outbuf_fb_create(ob, fw, fh);
116         if (ob->priv.draw)
117           ob->priv.fb_list = eina_list_append(ob->priv.fb_list, ob->priv.draw);
118      }
119 
120    while (!ob->priv.draw)
121      {
122         ecore_drm2_fb_release(ob->priv.output, EINA_TRUE);
123         ob->priv.draw = _outbuf_fb_wait(ob);
124      }
125 
126    EINA_LIST_FOREACH(ob->priv.fb_list, l, ofb)
127      {
128         if ((ofb->valid) && (ofb->drawn))
129           {
130              ofb->age++;
131              if (ofb->age > 4)
132                {
133                   ofb->age = 0;
134                   ofb->drawn = EINA_FALSE;
135                }
136           }
137      }
138 
139    return ob->priv.draw;
140 }
141 
142 static void
_outbuf_buffer_swap(Outbuf * ob)143 _outbuf_buffer_swap(Outbuf *ob)
144 {
145    Outbuf_Fb *ofb;
146 
147    ofb = ob->priv.draw;
148    if (!ofb)
149      {
150         ecore_drm2_fb_release(ob->priv.output, EINA_TRUE);
151         ofb = _outbuf_fb_assign(ob);
152         if (!ofb)
153           {
154              ERR("Could not assign front buffer");
155              return;
156           }
157      }
158 
159    if (!ob->priv.plane)
160      ob->priv.plane = ecore_drm2_plane_assign(ob->priv.output, ofb->fb, 0, 0);
161    else ecore_drm2_plane_fb_set(ob->priv.plane, ofb->fb);
162 
163    ecore_drm2_fb_flip(ofb->fb, ob->priv.output);
164    ofb->drawn = EINA_TRUE;
165    ofb->age = 0;
166 }
167 
168 Outbuf *
_outbuf_setup(Evas_Engine_Info_Drm * info,int w,int h)169 _outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h)
170 {
171    Outbuf *ob;
172 
173    ob = calloc(1, sizeof(Outbuf));
174    if (!ob) return NULL;
175 
176    ob->w = w;
177    ob->h = h;
178    ob->dev = info->info.dev;
179    ob->alpha = info->info.alpha;
180    ob->rotation = info->info.rotation;
181 
182    ob->bpp = info->info.bpp;
183    ob->depth = info->info.depth;
184    ob->format = info->info.format;
185 
186    ob->priv.output = info->info.output;
187 
188    return ob;
189 }
190 
191 void
_outbuf_free(Outbuf * ob)192 _outbuf_free(Outbuf *ob)
193 {
194    Outbuf_Fb *ofb;
195 
196    while (ob->priv.pending)
197      {
198         RGBA_Image *img;
199         Eina_Rectangle *rect;
200 
201         img = ob->priv.pending->data;
202         ob->priv.pending =
203           eina_list_remove_list(ob->priv.pending, ob->priv.pending);
204 
205         rect = img->extended_info;
206 
207         evas_cache_image_drop(&img->cache_entry);
208 
209         eina_rectangle_free(rect);
210      }
211 
212    /* TODO: idle flush */
213 
214    _outbuf_flush(ob, NULL, NULL, EVAS_RENDER_MODE_UNDEF);
215 
216    EINA_LIST_FREE(ob->priv.fb_list, ofb)
217      _outbuf_fb_destroy(ofb);
218 
219    free(ob);
220 }
221 
222 int
_outbuf_rotation_get(Outbuf * ob)223 _outbuf_rotation_get(Outbuf *ob)
224 {
225    return ob->rotation;
226 }
227 
228 void
_outbuf_reconfigure(Outbuf * ob,int w,int h,int rotation,Outbuf_Depth depth)229 _outbuf_reconfigure(Outbuf *ob, int w, int h, int rotation, Outbuf_Depth depth)
230 {
231    unsigned int format = DRM_FORMAT_ARGB8888;
232 
233    switch (depth)
234      {
235       case OUTBUF_DEPTH_RGB_16BPP_565_565_DITHERED:
236         format = DRM_FORMAT_RGB565;
237         break;
238       case OUTBUF_DEPTH_RGB_16BPP_555_555_DITHERED:
239         format = DRM_FORMAT_RGBX5551;
240         break;
241       case OUTBUF_DEPTH_RGB_16BPP_444_444_DITHERED:
242         format = DRM_FORMAT_RGBX4444;
243         break;
244       case OUTBUF_DEPTH_RGB_16BPP_565_444_DITHERED:
245         format = DRM_FORMAT_RGB565;
246         break;
247       case OUTBUF_DEPTH_RGB_32BPP_888_8888:
248         format = DRM_FORMAT_RGBX8888;
249         break;
250       case OUTBUF_DEPTH_ARGB_32BPP_8888_8888:
251         format = DRM_FORMAT_ARGB8888;
252         break;
253       case OUTBUF_DEPTH_BGRA_32BPP_8888_8888:
254         format = DRM_FORMAT_BGRA8888;
255         break;
256       case OUTBUF_DEPTH_BGR_32BPP_888_8888:
257         format = DRM_FORMAT_BGRX8888;
258         break;
259       case OUTBUF_DEPTH_RGB_24BPP_888_888:
260         format = DRM_FORMAT_RGB888;
261         break;
262       case OUTBUF_DEPTH_BGR_24BPP_888_888:
263         format = DRM_FORMAT_BGR888;
264         break;
265       case OUTBUF_DEPTH_INHERIT:
266       default:
267         depth = ob->depth;
268         format = ob->format;
269         break;
270      }
271 
272    if ((ob->w == w) && (ob->h == h) && (ob->rotation == rotation) &&
273        (ob->depth == depth) && (ob->format == format))
274      return;
275 
276    ob->w = w;
277    ob->h = h;
278    ob->depth = depth;
279    ob->format = format;
280    ob->rotation = rotation;
281    ob->priv.unused_duration = 0;
282 
283    _outbuf_idle_flush(ob);
284 }
285 
286 Render_Output_Swap_Mode
_outbuf_state_get(Outbuf * ob)287 _outbuf_state_get(Outbuf *ob)
288 {
289    int age;
290 
291    if (!ob->priv.draw) return MODE_FULL;
292 
293    age = ob->priv.draw->age;
294    if (age > 4) return MODE_FULL;
295    else if (age == 1) return MODE_COPY;
296    else if (age == 2) return MODE_DOUBLE;
297    else if (age == 3) return MODE_TRIPLE;
298    else if (age == 4) return MODE_QUADRUPLE;
299 
300    return MODE_FULL;
301 }
302 
303 void *
_outbuf_update_region_new(Outbuf * ob,int x,int y,int w,int h,int * cx,int * cy,int * cw,int * ch)304 _outbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch)
305 {
306    RGBA_Image *img = NULL;
307    Eina_Rectangle *rect;
308 
309    if ((w <= 0) || (h <= 0)) return NULL;
310 
311    RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, ob->w, ob->h);
312 
313    if (!(rect = eina_rectangle_new(x, y, w, h)))
314      return NULL;
315 
316    img = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get());
317 
318    if (!img)
319      {
320         eina_rectangle_free(rect);
321         return NULL;
322      }
323 
324    img->cache_entry.flags.alpha = ob->alpha;
325 
326    evas_cache_image_surface_alloc(&img->cache_entry, w, h);
327 
328    img->extended_info = rect;
329 
330    if (cx) *cx = 0;
331    if (cy) *cy = 0;
332    if (cw) *cw = w;
333    if (ch) *ch = h;
334 
335    /* add this cached image data to pending writes */
336    ob->priv.pending =
337      eina_list_append(ob->priv.pending, img);
338 
339    return img;
340 }
341 
342 void
_outbuf_update_region_push(Outbuf * ob,RGBA_Image * update,int x,int y,int w,int h)343 _outbuf_update_region_push(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h)
344 {
345    Gfx_Func_Convert func = NULL;
346    Eina_Rectangle rect = {0, 0, 0, 0}, pr;
347    DATA32 *src;
348    DATA8 *dst;
349    Ecore_Drm2_Fb *buff;
350    int bpp = 0, bpl = 0;
351    int rx = 0, ry = 0;
352    int bw = 0, bh = 0;
353 
354    /* check for valid output buffer */
355    if (!ob) return;
356 
357    /* check for pending writes */
358    if (!ob->priv.pending) return;
359 
360    /* check for valid source data */
361    if (!(src = update->image.data)) return;
362 
363    /* check for valid desination data */
364    if (!ob->priv.draw) return;
365    buff = ob->priv.draw->fb;
366 
367    dst = ecore_drm2_fb_data_get(buff);
368    if (!dst) return;
369 
370    if ((ob->rotation == 0) || (ob->rotation == 180))
371      {
372         func =
373           evas_common_convert_func_get(0, w, h, ob->bpp,
374                                        RED_MASK, GREEN_MASK, BLUE_MASK,
375                                        PAL_MODE_NONE, ob->rotation);
376      }
377    else if ((ob->rotation == 90) || (ob->rotation == 270))
378      {
379         func =
380           evas_common_convert_func_get(0, h, w, ob->bpp,
381                                        RED_MASK, GREEN_MASK, BLUE_MASK,
382                                        PAL_MODE_NONE, ob->rotation);
383      }
384 
385    /* make sure we have a valid convert function */
386    if (!func) return;
387 
388    /* based on rotation, set rectangle position */
389    if (ob->rotation == 0)
390      {
391         rect.x = x;
392         rect.y = y;
393      }
394    else if (ob->rotation == 90)
395      {
396         rect.x = y;
397         rect.y = (ob->w - x - w);
398      }
399    else if (ob->rotation == 180)
400      {
401         rect.x = (ob->w - x - w);
402         rect.y = (ob->h - y - h);
403      }
404    else if (ob->rotation == 270)
405      {
406         rect.x = (ob->h - y - h);
407         rect.y = x;
408      }
409 
410    /* based on rotation, set rectangle size */
411    if ((ob->rotation == 0) || (ob->rotation == 180))
412      {
413         rect.w = w;
414         rect.h = h;
415      }
416    else if ((ob->rotation == 90) || (ob->rotation == 270))
417      {
418         rect.w = h;
419         rect.h = w;
420      }
421 
422    bpp = ob->bpp / 8;
423    bw = ob->w;
424    bh = ob->h;
425    /* bpp = (ob->depth / 8); */
426    /* if (bpp <= 0) return; */
427 
428    bpl = ecore_drm2_fb_stride_get(buff);
429 
430    if (ob->rotation == 0)
431      {
432         RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h,
433                            0, 0, bw, bh);
434         dst += (bpl * rect.y) + (rect.x * bpp);
435         w -= rx;
436      }
437    else if (ob->rotation == 180)
438      {
439         pr = rect;
440         RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h,
441                            0, 0, bw, bh);
442         rx = pr.w - rect.w;
443         ry = pr.h - rect.h;
444         src += (update->cache_entry.w * ry) + rx;
445         w -= rx;
446      }
447    else if (ob->rotation == 90)
448      {
449         pr = rect;
450         RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h,
451                            0, 0, bw, bh);
452         rx = pr.w - rect.w; ry = pr.h - rect.h;
453         src += ry;
454         w -= ry;
455      }
456    else if (ob->rotation == 270)
457      {
458         pr = rect;
459         RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h,
460                            0, 0, bw, bh);
461         rx = pr.w - rect.w; ry = pr.h - rect.h;
462         src += (update->cache_entry.w * rx);
463         w -= ry;
464      }
465 
466    if ((rect.w <= 0) || (rect.h <= 0)) return;
467 
468    func(src, dst, (update->cache_entry.w - w), ((bpl / bpp) - rect.w),
469         rect.w, rect.h, x + rx, y + ry, NULL);
470 }
471 
472 void
_outbuf_flush(Outbuf * ob,Tilebuf_Rect * surface_damage EINA_UNUSED,Tilebuf_Rect * buffer_damage EINA_UNUSED,Evas_Render_Mode render_mode)473 _outbuf_flush(Outbuf *ob, Tilebuf_Rect *surface_damage EINA_UNUSED, Tilebuf_Rect *buffer_damage EINA_UNUSED, Evas_Render_Mode render_mode)
474 {
475    Eina_Rectangle *r;
476    RGBA_Image *img;
477    unsigned int i = 0;
478 
479    if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;
480 
481    if (ob->priv.rect_count) free(ob->priv.rects);
482 
483    /* get number of pending writes */
484    ob->priv.rect_count = eina_list_count(ob->priv.pending);
485    if (ob->priv.rect_count == 0) return;
486 
487    /* allocate rectangles */
488    ob->priv.rects = malloc(ob->priv.rect_count * sizeof(Eina_Rectangle));
489    if (!ob->priv.rects) return;
490    r = ob->priv.rects;
491 
492    /* loop the pending writes */
493    EINA_LIST_FREE(ob->priv.pending, img)
494      {
495         Eina_Rectangle *rect;
496         int x = 0, y = 0, w = 0, h = 0;
497 
498         rect = img->extended_info;
499         if (!rect) continue;
500 
501         x = rect->x; y = rect->y; w = rect->w; h = rect->h;
502 
503         /* based on rotation, set rectangle position */
504         if (ob->rotation == 0)
505           {
506              r[i].x = x;
507              r[i].y = y;
508           }
509         else if (ob->rotation == 90)
510           {
511              r[i].x = y;
512              r[i].y = (ob->w - x - w);
513           }
514         else if (ob->rotation == 180)
515           {
516              r[i].x = (ob->w - x - w);
517              r[i].y = (ob->h - y - h);
518           }
519         else if (ob->rotation == 270)
520           {
521              r[i].x = (ob->h - y - h);
522              r[i].y = x;
523           }
524 
525         /* based on rotation, set rectangle size */
526         if ((ob->rotation == 0) || (ob->rotation == 180))
527           {
528              r[i].w = w;
529              r[i].h = h;
530           }
531         else if ((ob->rotation == 90) || (ob->rotation == 270))
532           {
533              r[i].w = h;
534              r[i].h = w;
535           }
536 
537         eina_rectangle_free(rect);
538 
539         evas_cache_image_drop(&img->cache_entry);
540 
541         i++;
542      }
543 
544    _outbuf_buffer_swap(ob);
545 }
546 
547 void
_outbuf_damage_region_set(Outbuf * ob,Tilebuf_Rect * damage)548 _outbuf_damage_region_set(Outbuf *ob, Tilebuf_Rect *damage)
549 {
550    Tilebuf_Rect *tr;
551    Eina_Rectangle *rects;
552    Ecore_Drm2_Fb *fb;
553    int count, i = 0;
554 
555    if (!ob->priv.draw) return;
556 
557    fb = ob->priv.draw->fb;
558 
559    count = eina_inlist_count(EINA_INLIST_GET(damage));
560    rects = alloca(count * sizeof(Eina_Rectangle));
561 
562    EINA_INLIST_FOREACH(damage, tr)
563      {
564         rects[i].x = tr->x;
565         rects[i].y = tr->y;
566         rects[i].w = tr->w;
567         rects[i].h = tr->h;
568         i++;
569      }
570 
571    ecore_drm2_fb_dirty(fb, rects, count);
572 }
573 
574 void
_outbuf_idle_flush(Outbuf * ob)575 _outbuf_idle_flush(Outbuf *ob)
576 {
577    while (ob->priv.pending)
578      {
579         RGBA_Image *img;
580         Eina_Rectangle *rect;
581 
582         img = ob->priv.pending->data;
583         ob->priv.pending =
584           eina_list_remove_list(ob->priv.pending, ob->priv.pending);
585 
586         rect = img->extended_info;
587 
588         evas_cache_image_drop(&img->cache_entry);
589 
590         eina_rectangle_free(rect);
591      }
592 
593    while (ecore_drm2_fb_release(ob->priv.output, EINA_TRUE));
594 }
595