Closed Bug 1014815 Opened 5 years ago Closed 5 years ago

SurfaceStream doesn't give mStaging enough time to resolve before blocking on it

Categories

(Core :: Graphics: Layers, defect)

x86
macOS
defect
Not set

Tracking

()

RESOLVED WONTFIX
Tracking Status
b2g-v1.4 --- ?

People

(Reporter: BenWa, Assigned: BenWa)

References

Details

Attachments

(1 file, 5 obsolete files)

Currently we're moving from mProducer -> mStaging -> mConsumer in the same frame. This means that from the time we go from the last gl command to the time we want to present we can have a little as 0ms before waiting on the GPU.

I'm suggesting that we instead keep 3 buffers but introduce a one frame delay before moving things to mConsumer. This gives the GPU the time to work while we prepare the next frame on the GPU letting the CPU+GPU work together.
Attached patch patch (obsolete) — Splinter Review
Assignee: nobody → bgirard
Status: NEW → ASSIGNED
Depends on: 999694
Attached patch hiben.patch (obsolete) — Splinter Review
This fixes the stuck frame problem.
Attachment #8427242 - Attachment is obsolete: true
Attachment #8428931 - Flags: review?(jgilbert)
Comment on attachment 8428931 [details] [diff] [review]
hiben.patch

Review of attachment 8428931 [details] [diff] [review]:
-----------------------------------------------------------------

Sorry this patch needs cleaning up, got it to work just before leaving work. Just ignore the printf and give me feedback. Wanted to avoid having another day of delay.
Attached patch patch (obsolete) — Splinter Review
Attachment #8428931 - Attachment is obsolete: true
Attachment #8428931 - Flags: review?(jgilbert)
Comment on attachment 8429362 [details] [diff] [review]
patch

Review of attachment 8429362 [details] [diff] [review]:
-----------------------------------------------------------------

::: gfx/gl/SurfaceStream.cpp
@@ +54,5 @@
>              break;
> +        }
> +        case SurfaceStreamType::TripleBuffer: {
> +            bool delayFrame = false;
> +#ifdef MOZ_WIDGET_GONK

The better way to do this is with a bool ShouldDelayFrame() func, and put our (ifdef) logic in there.

@@ +479,4 @@
>  {
>      MonitorAutoLock lock(mMonitor);
>      if (mStaging) {
> +        if (!mConsumer || !mSwapDelay) {

This func definitely needs more comments. The mental load here is pretty high.

Invert this if/else order. It's easier to understand if we do:
if (mConsumer && mSwapDelay)
else // !(mConsumer && mSwapDelay) => (!mConsumer || !mSwapDelay), right?

I would also recommend trying keeping mSwapDelay and !mSwapDelay as separate, instead of interleaving this extra variable in our swap logic. That way, it should be more obvious what we do in each case in isolation.

@@ -464,1 @@
>              Scrap(mConsumer);

Why were we scrapping the already-null mConsumer?

::: gfx/gl/SurfaceStream.h
@@ +56,5 @@
> +    /**
> +     * If we have a pending frame in the queue then well need
> +     * another SwapConsumer.
> +     */
> +    bool DelayedFrame() {

HasDelayedFrame

@@ +202,4 @@
>      SharedSurface* mStaging;
>      SharedSurface* mConsumer;
>      SharedSurface* mDelay;
> +    bool mSwapDelay;

Shouldn't this be const? Also probably 'mHas/UseSwapDelay'.
(In reply to Jeff Gilbert [:jgilbert] from comment #6)
> Invert this if/else order. It's easier to understand if we do:
> if (mConsumer && mSwapDelay)
> else // !(mConsumer && mSwapDelay) => (!mConsumer || !mSwapDelay), right?

Good suggestion. And it is true via De Morgan' laws, which is worth memorizing IMO for this sort of simplification:
http://en.wikipedia.org/wiki/De_Morgan%27s_laws

> 
> I would also recommend trying keeping mSwapDelay and !mSwapDelay as
> separate, instead of interleaving this extra variable in our swap logic.
> That way, it should be more obvious what we do in each case in isolation.
> 
> @@ -464,1 @@
> >              Scrap(mConsumer);
> 
> Why were we scrapping the already-null mConsumer?

I'm not sure but now it needs to happen to be safe.
Attached patch patch v2 (obsolete) — Splinter Review
Attachment #8429362 - Attachment is obsolete: true
Attachment #8434337 - Flags: review?(jgilbert)
Comment on attachment 8434337 [details] [diff] [review]
patch v2

Review of attachment 8434337 [details] [diff] [review]:
-----------------------------------------------------------------

::: gfx/gl/SurfaceStream.cpp
@@ +36,5 @@
> +{
> +#ifdef MOZ_WIDGET_GONK
> +  return true;
> +#else
> +  return true;

This should be false, this was meant for try where I wanted to make sure this configuration didn't regress anywhere.
(In reply to Benoit Girard (:BenWa) from comment #7)
> (In reply to Jeff Gilbert [:jgilbert] from comment #6)
> > Invert this if/else order. It's easier to understand if we do:
> > if (mConsumer && mSwapDelay)
> > else // !(mConsumer && mSwapDelay) => (!mConsumer || !mSwapDelay), right?
> 
> Good suggestion. And it is true via De Morgan' laws, which is worth
> memorizing IMO for this sort of simplification:
> http://en.wikipedia.org/wiki/De_Morgan%27s_laws
I remember these, but couldn't remember the name. Regardless, it's in our best interest to write code that's most obvious, and not expect people to be fluent in boolean logical transformations. :)
> 
> > 
> > I would also recommend trying keeping mSwapDelay and !mSwapDelay as
> > separate, instead of interleaving this extra variable in our swap logic.
> > That way, it should be more obvious what we do in each case in isolation.
> > 
> > @@ -464,1 @@
> > >              Scrap(mConsumer);
> > 
> > Why were we scrapping the already-null mConsumer?
> 
> I'm not sure but now it needs to happen to be safe.

I would like to try to remove this, since it's obviously wrong.
Comment on attachment 8434337 [details] [diff] [review]
patch v2

Review of attachment 8434337 [details] [diff] [review]:
-----------------------------------------------------------------

::: gfx/gl/SurfaceStream.h
@@ +56,5 @@
> +    /**
> +     * If we have a pending frame in the queue then well need
> +     * another SwapConsumer.
> +     */
> +    bool HasDelayedFrame() const {

This would be better as a virtual, which we could then implement in a subclass as:
virtual bool HasDelayedFrame() const MOZ_OVERRIDE { return !!mDelay; }
Attachment #8434337 - Flags: review?(jgilbert) → review+
We're thinking of taking this bug on 1.4 instead of bug 1001417
status-b2g-v1.4: --- → ?
But I just realized this has reftest failures. So maybe bug 1001417 is better.
Attachment #8436139 - Attachment is obsolete: true
Blocks: 1014011
2 rAF:
https://tbpl.mozilla.org/?tree=Try&rev=5a81349bbf3a
2 rAF + Timeout (just in case since our tests are so slow):
https://tbpl.mozilla.org/?tree=Try&rev=de013b7c0c6d
BenWa, I have one question. attachment 8437184 [details] [diff] [review] delay one frame. How does the patch ensure the last rendered frame is always rendered to the screen? SurfaceStream is used also for SkiaGL. And I just thought that there might be a case that last rendered frame is not rendered to a display.
Flags: needinfo?(bgirard)
(In reply to Benoit Girard (:BenWa) from comment #0)
> I'm suggesting that we instead keep 3 buffers but introduce a one frame
> delay before moving things to mConsumer.

Does this mean that there will be one added vsync worth of latency (16ms) incurred to graphics rendering?
(In reply to Sotaro Ikeda [:sotaro] from comment #20)

This line here will make sure we display the next frame:

+        if (context->GL()->Screen()->HasDelayedFrame()) {
+            context->Invalidate();
+        }

(In reply to Jukka Jylänki from comment #21)
> (In reply to Benoit Girard (:BenWa) from comment #0)
> > I'm suggesting that we instead keep 3 buffers but introduce a one frame
> > delay before moving things to mConsumer.
> 
> Does this mean that there will be one added vsync worth of latency (16ms)
> incurred to graphics rendering?

Yes.
Flags: needinfo?(bgirard)
I can't say I'd understand the internal details of the compositor at all, but that sounds suboptimal. Is there any other way?
Yes, bug 1001417 another option. The tradeoff are much more complicated then one has delay, one does not however.
Note that we're probably moving towards the reality where everything is delayed by a frame as we do bug 987532.  If we're going to miss the frames often enough, the though is that "missing" all of them gives you better uniformity and better overall experience.
I talked with Jeff and Sotaro today, for now we're considering bug 1001417 because we think it as a lower chance of regression. With the frame delay we risk regressions with DrawWindow, invalidation and webgl updates no longer syncing up with the same transaction as content. We will come back to do if there's excessive waiting in the compositor for the fence.
No longer blocks: 1014011
For now we're officially going with bug 1001417 instead of this approach. It performs much better then I anticipated. We should re-open this if it causes too much blocking on the compositor thread.
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.