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