summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDerek Foreman <derekf@osg.samsung.com>2016-02-18 13:22:19 -0600
committerMike Blumenkrantz <zmike@osg.samsung.com>2016-02-18 15:50:15 -0500
commit5d7271683be0e790c1b845e7413a36992ebfb703 (patch)
treea1fddfd6a59a7ac9922c45676208fa687776c10c /src
parent1f576da49e662a563d16ea4915e485516aa2b4ee (diff)
ecore_drm: Change page flipping logic so we can't tear
Summary: Previously if we ever tried to queue up two page flips in less than a retrace interval (which can easily happen since the evas clock isn't based on vblank) we'd give up on ever using page flips again, and tear on every screen update. This fixes that by using a vblank callback for custom ticks and using page flips whenever possible. If a page flip fails it means a page flip raced with the vblank ticker, so we need to queue up that frame when the current page flip completes. This ensures that while we might drop interim frames, we will never lose the most recent. Now it should only be possible to tear if two ticks fire during the wait for a page flip to complete. This would result in rendering taking place in the front buffer. I don't think this can happen, but an error is logged if it does. Reviewers: zmike, devilhorns Subscribers: cedric, jpeg Differential Revision: https://phab.enlightenment.org/D3594
Diffstat (limited to 'src')
-rw-r--r--src/lib/ecore_drm/Ecore_Drm.h4
-rw-r--r--src/lib/ecore_drm/ecore_drm_device.c132
-rw-r--r--src/lib/ecore_drm/ecore_drm_fb.c102
-rw-r--r--src/lib/ecore_drm/ecore_drm_private.h1
-rw-r--r--src/modules/evas/engines/drm/evas_outbuf.c31
5 files changed, 116 insertions, 154 deletions
diff --git a/src/lib/ecore_drm/Ecore_Drm.h b/src/lib/ecore_drm/Ecore_Drm.h
index 0366899..8d2787b 100644
--- a/src/lib/ecore_drm/Ecore_Drm.h
+++ b/src/lib/ecore_drm/Ecore_Drm.h
@@ -671,10 +671,12 @@ EAPI void ecore_drm_fb_dirty(Ecore_Drm_Fb *fb, Eina_Rectangle *rects, unsigned i
671 * @param dev The Ecore_Drm_Device to use 671 * @param dev The Ecore_Drm_Device to use
672 * @param fb The Ecore_Drm_Fb to make the current framebuffer 672 * @param fb The Ecore_Drm_Fb to make the current framebuffer
673 * 673 *
674 * @deprecated just call ecore_drm_fb_send() instead.
675 *
674 * @ingroup Ecore_Drm_Fb_Group 676 * @ingroup Ecore_Drm_Fb_Group
675 * @since 1.14 677 * @since 1.14
676 */ 678 */
677EAPI void ecore_drm_fb_set(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb); 679EINA_DEPRECATED EAPI void ecore_drm_fb_set(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb);
678 680
679/** 681/**
680 * Send an Ecore_Drm_Fb to the Ecore_Drm_Device 682 * Send an Ecore_Drm_Fb to the Ecore_Drm_Device
diff --git a/src/lib/ecore_drm/ecore_drm_device.c b/src/lib/ecore_drm/ecore_drm_device.c
index cca3332..756a5fc 100644
--- a/src/lib/ecore_drm/ecore_drm_device.c
+++ b/src/lib/ecore_drm/ecore_drm_device.c
@@ -10,93 +10,86 @@
10 ((x) >= (xx)) && ((y) >= (yy))) 10 ((x) >= (xx)) && ((y) >= (yy)))
11 11
12static Eina_List *drm_devices; 12static Eina_List *drm_devices;
13static int flip_count = 0; 13static int ticking = 0;
14 14
15static void 15static void
16_ecore_drm_device_cb_page_flip(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data) 16_ecore_drm_tick_schedule(Ecore_Drm_Device *dev)
17{ 17{
18 Ecore_Drm_Pageflip_Callback *cb; 18 drmVBlank vbl;
19
20 /* DBG("Drm Page Flip Event"); */
21
22 if (!(cb = data)) return;
23
24 flip_count++;
25 if (flip_count < cb->count) return;
26 19
27 cb->dev->current = cb->dev->next; 20 if (!ticking) return;
28 cb->dev->next = NULL;
29 21
30 flip_count = 0; 22 vbl.request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
31 if (cb->func) cb->func(cb->data); 23 vbl.request.sequence = 1;
32 /* free(cb); */ 24 vbl.request.signal = (unsigned long)dev;
25 drmWaitVBlank(dev->drm.fd, &vbl);
26}
33 27
34 /* Ecore_Drm_Output *output; */ 28static void
29_ecore_drm_tick_begin(void *data)
30{
31 ticking = 1;
32 _ecore_drm_tick_schedule(data);
33}
35 34
36 /* DBG("Drm Page Flip Event"); */ 35static void
36_ecore_drm_tick_end(void *data EINA_UNUSED)
37{
38 ticking = 0;
39}
37 40
38 /* if (!(output = data)) return; */ 41static void
42_ecore_drm_tick_source_set(Ecore_Drm_Device *dev)
43{
44 ecore_animator_custom_source_tick_begin_callback_set
45 (_ecore_drm_tick_begin, dev);
46 ecore_animator_custom_source_tick_end_callback_set
47 (_ecore_drm_tick_end, dev);
48 ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM);
49}
39 50
40 /* if (output->pending_flip) */ 51static void
41 /* { */ 52_ecore_drm_device_cb_page_flip(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data)
42 /* if (output->dev->current) */ 53{
43 /* ecore_drm_output_fb_release(output, output->dev->current); */ 54 Ecore_Drm_Output *output = data;
44 /* output->dev->current = output->dev->next; */ 55 Ecore_Drm_Fb *next;
45 /* output->dev->next = NULL; */
46 /* } */
47 56
48 /* output->pending_flip = EINA_FALSE; */ 57 if (output->pending_destroy)
49 /* if (output->pending_destroy) */ 58 {
50 /* { */ 59 ecore_drm_output_free(output);
51 /* output->pending_destroy = EINA_FALSE; */ 60 return;
52 /* ecore_drm_output_free(output); */ 61 }
53 /* } */ 62 /* We were unable to queue a page on the last flip attempt, so we'll
54 /* else if (!output->pending_vblank) */ 63 * try again now. */
55 /* ecore_drm_output_repaint(output); */ 64 next = output->next;
65 if (next)
66 {
67 output->next = NULL;
68 ecore_drm_fb_send(output->dev, next, NULL, NULL);
69 }
56} 70}
57 71
58static void 72static void
59_ecore_drm_device_cb_vblank(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data) 73_ecore_drm_device_cb_vblank(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data)
60{ 74{
61 Ecore_Drm_Sprite *sprite; 75 ecore_animator_custom_tick();
62 Ecore_Drm_Output *output; 76 if (ticking) _ecore_drm_tick_schedule(data);
63
64 /* DBG("Drm VBlank Event"); */
65
66 if (!(sprite = data)) return;
67
68 output = sprite->output;
69 output->pending_vblank = EINA_FALSE;
70
71 ecore_drm_output_fb_release(output, sprite->current_fb);
72 sprite->current_fb = sprite->next_fb;
73 sprite->next_fb = NULL;
74
75 if (!output->pending_flip) _ecore_drm_output_frame_finish(output);
76} 77}
77 78
78#if 0 79static Eina_Bool
79static Eina_Bool 80_cb_drm_event_handle(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
80_ecore_drm_device_cb_idle(void *data)
81{ 81{
82 Ecore_Drm_Device *dev; 82 Ecore_Drm_Device *dev = data;
83 Ecore_Drm_Output *output; 83 int err;
84 Eina_List *l;
85
86 if (!(dev = data)) return ECORE_CALLBACK_CANCEL;
87 84
88 if (!dev->active) return ECORE_CALLBACK_RENEW; 85 err = drmHandleEvent(dev->drm.fd, &dev->drm_ctx);
89 86 if (err)
90 EINA_LIST_FOREACH(dev->outputs, l, output)
91 { 87 {
92 if ((!output->enabled) || (!output->need_repaint)) continue; 88 ERR("drmHandleEvent failed to read an event");
93 if (output->repaint_scheduled) continue; 89 return EINA_FALSE;
94 _ecore_drm_output_repaint_start(output);
95 } 90 }
96 91 return EINA_TRUE;
97 return ECORE_CALLBACK_RENEW;
98} 92}
99#endif
100 93
101static void 94static void
102_ecore_drm_device_cb_output_event(const char *device EINA_UNUSED, Eeze_Udev_Event event EINA_UNUSED, void *data, Eeze_Udev_Watch *watch EINA_UNUSED) 95_ecore_drm_device_cb_output_event(const char *device EINA_UNUSED, Eeze_Udev_Event event EINA_UNUSED, void *data, Eeze_Udev_Watch *watch EINA_UNUSED)
@@ -379,6 +372,10 @@ ecore_drm_device_open(Ecore_Drm_Device *dev)
379 eeze_udev_watch_add(EEZE_UDEV_TYPE_DRM, events, 372 eeze_udev_watch_add(EEZE_UDEV_TYPE_DRM, events,
380 _ecore_drm_device_cb_output_event, dev); 373 _ecore_drm_device_cb_output_event, dev);
381 374
375 dev->drm.hdlr =
376 ecore_main_fd_handler_add(dev->drm.fd, ECORE_FD_READ,
377 _cb_drm_event_handle, dev, NULL, NULL);
378
382 /* dev->drm.idler = */ 379 /* dev->drm.idler = */
383 /* ecore_idle_enterer_add(_ecore_drm_device_cb_idle, dev); */ 380 /* ecore_idle_enterer_add(_ecore_drm_device_cb_idle, dev); */
384 381
@@ -546,6 +543,7 @@ ecore_drm_device_software_setup(Ecore_Drm_Device *dev)
546 DBG("\tSize: %d", dev->dumb[i]->size); 543 DBG("\tSize: %d", dev->dumb[i]->size);
547 DBG("\tW: %d\tH: %d", dev->dumb[i]->w, dev->dumb[i]->h); 544 DBG("\tW: %d\tH: %d", dev->dumb[i]->w, dev->dumb[i]->h);
548 } 545 }
546 _ecore_drm_tick_source_set(dev);
549 547
550 return EINA_TRUE; 548 return EINA_TRUE;
551 549
diff --git a/src/lib/ecore_drm/ecore_drm_fb.c b/src/lib/ecore_drm/ecore_drm_fb.c
index 234b06f..8b7ca52 100644
--- a/src/lib/ecore_drm/ecore_drm_fb.c
+++ b/src/lib/ecore_drm/ecore_drm_fb.c
@@ -167,7 +167,15 @@ ecore_drm_fb_dirty(Ecore_Drm_Fb *fb, Eina_Rectangle *rects, unsigned int count)
167} 167}
168 168
169EAPI void 169EAPI void
170ecore_drm_fb_set(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb) 170ecore_drm_fb_set(Ecore_Drm_Device *dev EINA_UNUSED, Ecore_Drm_Fb *fb EINA_UNUSED)
171{
172 /* ecore_drm_fb_set no longer has any functionality distinct from
173 * ecore_drm_fb_send so it is reduced to NO-OP to prevent messing up state
174 */
175}
176
177EAPI void
178ecore_drm_fb_send(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb, Ecore_Drm_Pageflip_Cb func EINA_UNUSED, void *data EINA_UNUSED)
171{ 179{
172 Ecore_Drm_Output *output; 180 Ecore_Drm_Output *output;
173 Eina_List *l; 181 Eina_List *l;
@@ -185,88 +193,58 @@ ecore_drm_fb_set(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb)
185 } 193 }
186 } 194 }
187 195
188 if (!dev->next) dev->next = fb; 196 if (!dev->outputs) return;
189 if (!dev->next) return;
190 197
191 EINA_LIST_FOREACH(dev->outputs, l, output) 198 EINA_LIST_FOREACH(dev->outputs, l, output)
192 { 199 {
193 int x = 0, y = 0;
194
195 if ((!output->enabled) || (!output->current_mode)) continue; 200 if ((!output->enabled) || (!output->current_mode)) continue;
196 201
197 if (!output->cloned) 202 if (output->next) WRN("fb reused too soon, tearing may be visible");
198 {
199 x = output->x;
200 y = output->y;
201 }
202 203
203 if ((!dev->current) || 204 /* If we changed display parameters or haven't displayed anything
204 (dev->current->stride != dev->next->stride)) 205 * yet we need to do a SetCrtc
206 */
207 if ((!output->current) ||
208 (output->current->stride != fb->stride))
205 { 209 {
206 if (drmModeSetCrtc(dev->drm.fd, output->crtc_id, dev->next->id, 210 int x = 0, y = 0;
211
212 if (!output->cloned)
213 {
214 x = output->x;
215 y = output->y;
216 }
217 if (drmModeSetCrtc(dev->drm.fd, output->crtc_id, fb->id,
207 x, y, &output->conn_id, 1, 218 x, y, &output->conn_id, 1,
208 &output->current_mode->info)) 219 &output->current_mode->info))
209 { 220 {
210 ERR("Failed to set Mode %dx%d for Output %s: %m", 221 ERR("Failed to set Mode %dx%d for Output %s: %m",
211 output->current_mode->width, output->current_mode->height, 222 output->current_mode->width, output->current_mode->height,
212 output->name); 223 output->name);
224 continue;
213 } 225 }
226 output->current = fb;
227 output->next = NULL;
214 228
215 /* TODO: set dpms on ?? */ 229 /* TODO: set dpms on ?? */
230 continue;
216 } 231 }
217 }
218}
219
220EAPI void
221ecore_drm_fb_send(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb, Ecore_Drm_Pageflip_Cb func, void *data)
222{
223 Ecore_Drm_Output *output;
224 Eina_List *l;
225 Ecore_Drm_Pageflip_Callback *cb;
226
227 EINA_SAFETY_ON_NULL_RETURN(dev);
228 EINA_SAFETY_ON_NULL_RETURN(fb);
229 EINA_SAFETY_ON_NULL_RETURN(func);
230
231 if (eina_list_count(dev->outputs) < 1) return;
232
233 if (fb->pending_flip) return;
234
235 if (!(cb = calloc(1, sizeof(Ecore_Drm_Pageflip_Callback))))
236 return;
237
238 cb->dev = dev;
239 cb->func = func;
240 cb->data = data;
241
242 EINA_LIST_FOREACH(dev->outputs, l, output)
243 if (output->enabled) cb->count++;
244
245 EINA_LIST_FOREACH(dev->outputs, l, output)
246 {
247 if ((!output->enabled) || (!output->current_mode)) continue;
248 232
233 /* The normal case: We do a flip which waits for vblank and
234 * posts an event.
235 */
249 if (drmModePageFlip(dev->drm.fd, output->crtc_id, fb->id, 236 if (drmModePageFlip(dev->drm.fd, output->crtc_id, fb->id,
250 DRM_MODE_PAGE_FLIP_EVENT, cb) < 0) 237 DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
251 { 238 {
252 ERR("Cannot flip crtc %u for connector %u: %m", 239 /* Failure to flip - likely there's already a flip
240 * queued, and we can't cancel, so just store this
241 * fb for later and it'll be queued in the flip
242 * handler */
243 DBG("flip crtc %u for connector %u failed, re-queued",
253 output->crtc_id, output->conn_id); 244 output->crtc_id, output->conn_id);
245 output->next = fb;
254 continue; 246 continue;
255 } 247 }
256 248 output->current = fb;
257 fb->pending_flip = EINA_TRUE;
258 }
259
260 while (fb->pending_flip)
261 {
262 int ret = 0;
263
264 ret = drmHandleEvent(dev->drm.fd, &dev->drm_ctx);
265 if (ret < 0)
266 {
267 ERR("drmHandleEvent Failed");
268 free(cb);
269 break;
270 }
271 } 249 }
272} 250}
diff --git a/src/lib/ecore_drm/ecore_drm_private.h b/src/lib/ecore_drm/ecore_drm_private.h
index de78d6d..4271a7e 100644
--- a/src/lib/ecore_drm/ecore_drm_private.h
+++ b/src/lib/ecore_drm/ecore_drm_private.h
@@ -150,6 +150,7 @@ struct _Ecore_Drm_Output
150 } edid; 150 } edid;
151 151
152 Ecore_Drm_Backlight *backlight; 152 Ecore_Drm_Backlight *backlight;
153 Ecore_Drm_Fb *current, *next;
153 154
154 Eina_Bool primary : 1; 155 Eina_Bool primary : 1;
155 Eina_Bool connected : 1; 156 Eina_Bool connected : 1;
diff --git a/src/modules/evas/engines/drm/evas_outbuf.c b/src/modules/evas/engines/drm/evas_outbuf.c
index 11d3dc6..64b3ba9 100644
--- a/src/modules/evas/engines/drm/evas_outbuf.c
+++ b/src/modules/evas/engines/drm/evas_outbuf.c
@@ -8,23 +8,6 @@
8#define GREEN_MASK 0x00ff00 8#define GREEN_MASK 0x00ff00
9#define BLUE_MASK 0x0000ff 9#define BLUE_MASK 0x0000ff
10 10
11static void
12_evas_outbuf_cb_pageflip(void *data)
13{
14 Outbuf *ob;
15 Ecore_Drm_Fb *fb;
16
17 if (!(ob = data)) return;
18
19 /* DBG("Outbuf Pagelip Done"); */
20
21 if ((fb = ob->priv.buffer[ob->priv.curr]))
22 fb->pending_flip = EINA_FALSE;
23
24 ob->priv.last = ob->priv.curr;
25 ob->priv.curr = (ob->priv.curr + 1) % ob->priv.num;
26}
27
28static void 11static void
29_evas_outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects, unsigned int count) 12_evas_outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects, unsigned int count)
30{ 13{
@@ -35,11 +18,11 @@ _evas_outbuf_buffer_swap(Outbuf *ob, Eina_Rectangle *rects, unsigned int count)
35 /* mark the fb as dirty */ 18 /* mark the fb as dirty */
36 ecore_drm_fb_dirty(buff, rects, count); 19 ecore_drm_fb_dirty(buff, rects, count);
37 20
38 /* if this buffer is not valid, we need to set it */
39 ecore_drm_fb_set(ob->info->info.dev, buff);
40
41 /* send this buffer to the crtc */ 21 /* send this buffer to the crtc */
42 ecore_drm_fb_send(ob->info->info.dev, buff, _evas_outbuf_cb_pageflip, ob); 22 ecore_drm_fb_send(ob->info->info.dev, buff, NULL, NULL);
23
24 ob->priv.last = ob->priv.curr;
25 ob->priv.curr = (ob->priv.curr + 1) % ob->priv.num;
43} 26}
44 27
45Outbuf * 28Outbuf *
@@ -62,8 +45,8 @@ evas_outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h)
62 ob->destination_alpha = info->info.destination_alpha; 45 ob->destination_alpha = info->info.destination_alpha;
63 ob->vsync = info->info.vsync; 46 ob->vsync = info->info.vsync;
64 47
65 /* default to double-buffer */ 48 /* we must triple-buffer to prevent problems with the page flip handler */
66 ob->priv.num = 2; 49 ob->priv.num = 3;
67 50
68 /* check for buffer override */ 51 /* check for buffer override */
69 if ((num = getenv("EVAS_DRM_BUFFERS"))) 52 if ((num = getenv("EVAS_DRM_BUFFERS")))
@@ -98,7 +81,7 @@ evas_outbuf_setup(Evas_Engine_Info_Drm *info, int w, int h)
98 } 81 }
99 82
100 /* set the front buffer to be the one on the crtc */ 83 /* set the front buffer to be the one on the crtc */
101 ecore_drm_fb_set(info->info.dev, ob->priv.buffer[0]); 84 ecore_drm_fb_send(info->info.dev, ob->priv.buffer[0], NULL, NULL);
102 85
103 return ob; 86 return ob;
104} 87}