summaryrefslogtreecommitdiff
path: root/src/lib/ecore_drm/ecore_drm_fb.c
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/lib/ecore_drm/ecore_drm_fb.c
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/lib/ecore_drm/ecore_drm_fb.c')
-rw-r--r--src/lib/ecore_drm/ecore_drm_fb.c102
1 files changed, 40 insertions, 62 deletions
diff --git a/src/lib/ecore_drm/ecore_drm_fb.c b/src/lib/ecore_drm/ecore_drm_fb.c
index 234b06f17c..8b7ca52fa9 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}