On Friday, July 18, 2008 1:12 am Michel Dänzer wrote:
> On Thu, 2008-07-17 at 09:32 -0700, Jesse Barnes wrote:
> > On Wednesday, July 16, 2008 11:51 pm Michel Dänzer wrote:
> > > On Wed, 2008-07-16 at 16:01 -0700, Jesse Barnes wrote:
> > > > @@ -408,8 +426,10 @@ int drm_vblank_get(struct drm_device *dev, int
> > > > crtc) ret = dev->driver->enable_vblank(dev, crtc); if (ret)
> > > >                         atomic_dec(&dev->vblank_refcount[crtc]);
> > > > -               else
> > > > +               else {
> > > >                         dev->vblank_enabled[crtc] = 1;
> > > > +                       drm_update_vblank_count(dev, crtc);
> > > > +               }
> > >
> > > I think this is missing a corresponding
> > >
> > >   dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc);
> > >
> > > when the interrupt gets disabled, to make sure this does the right
> > > thing.
> >
> > Oh right, I forgot about the wraparound logic.
>
> It's not about that; drm_update_vblank_count() just needs to increase
> dev->_vblank_count[crtc] by the number of frames since the interrupt was
> disabled, not since the last time it was enabled.

But we also use the last_count to handle wraparound if it occurred.  Either 
way it needs to be update at disable time, you're right.

> > I also got to thinking about the modeset ioctl.  With a few changes I
> > think we can avoid the huge jumps we used to see (both forward &
> > backward).
>
> I thought we'd been over that before on IRC... Please revert the
> drm_modeset_ctl() changes, what was there before should DTRT under any
> circumstances and certainly does IME. If you've found specific cases
> where it doesn't, please provide more information about those. (Thought
> experiments are fine :)

Ok, we've been over this on IRC, but I'll document it here for our audience 
(who I'm sure are on the edge of their seats by now).

The initial API relied on drm_update_vblank_count() to be called before 
getting the actual count, that way we'd account for any wraparound that might 
have occurred in the hw register, and we'd also update the counter with any 
events that happened while interrupts were off.

But this introduced problems.  Around mode sets, the counter would always be 
reset, so the update_vblank_count() wraparound logic would be triggered, 
increasing the counter by huge amounts on every mode set.  So if a client was 
doing a wait on count + 1 but we incremented our internal counter by count + 
wraparound (0xffffff in the intel case) it would just hang waiting forever in 
the case of AIGLX (this would happen if a modeset occurred while an app was 
running for example).

So you fixed up the compensation logic in pre/post modeset, which fixed this 
case, so that userspace would never see big jumps.

In this last push, I changed things over to use the internal counter directly 
while interrupts were on, using update_vblank_count to add in lost events 
when interrupts went from off->on.  So while interrupts are enabled, there 
are no issues.  And if we go from off->on we add the frame count to the 
counter and check for wraparound, so things generally work.

*However*, what I neglected to do was fixup drm_wait_vblank() and the rest of 
the update_vblank_count callers to use drm_vblank_get(), which will both 
enable interrupts (if they're off) and account for any lost events if we 
happen to be going from an off->on state.  So if wait_vblank() is called and 
interrupts are currently off, we'll get a stale value from 
drm_vblank_count(), then call get() which will update it, and then interrupts 
will be enabled and things should work.  This might mean tearing on the first 
frame of a given app.  Likewise for scheduled swaps, they may occur too soon 
if interrupts are off when they're first scheduled.

The new modeset code is also susceptible to problems if interrupts go from 
on->off between pre & post mode set, since in that case we'll use the last 
known pre-modeset count to calculate the difference to add to the counter.

>
> > > > @@ -528,7 +550,6 @@ int drm_wait_vblank(struct drm_device *dev, void
> > > > *data, if (crtc >= dev->num_crtcs)
> > > >                 return -EINVAL;
> > > >
> > > > -       drm_update_vblank_count(dev, crtc);
> > > >         seq = drm_vblank_count(dev, crtc);
> > > >
> > > >         switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
> > >
> > > I don't think this can be removed altogether, or seq will be stale if
> > > the interrupt is disabled when drm_wait_vblank() is called. So I guess
> > > call drm_update_vblank_count() when the interrupt is disabled, or just
> > > bail inside drm_update_vblank_count() when it is enabled.
> >
> > Yeah, I think just a get_vblank_counter() in vblank_disable_fn() is
> > sufficient, since we'll hit the wraparound logic again when we re-enable.
> > Look ok?
>
> I'm afraid not. Again, the wraparound logic is irrelevant for this, it's
> in drm_update_vblank_count() after all...
>
> What's wrong with
>
>       if (!dev->vblank_enabled[crtc])
>               drm_update_vblank_count(dev, crtc);
>       seq = drm_vblank_count(dev, crtc);
>       ...

That's one way of addressing the interrupts-are-off counter update, but really 
I had intended to get rid of drm_update_vblank_count() as part of the API 
since it led to the spurious wraparound problem in the first place, and 
isolate all the update logic to drm_vblank_get().  My thinking was that 
get/put around any API usage would be simpler, but maybe keeping the 
update_vblank_count() call in place is better, I dunno.

Jesse


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to