Closed Bug 430450 Opened 16 years ago Closed 16 years ago

vertical stripes in windowless plugins

Categories

(Core Graveyard :: Plug-ins, defect, P2)

x86
Linux
defect

Tracking

(Not tracked)

RESOLVED FIXED
mozilla1.9.1a1

People

(Reporter: otte, Assigned: karlt)

References

()

Details

(Keywords: fixed1.9.0.2)

Attachments

(4 files, 3 obsolete files)

User-Agent:       Mozilla/5.0 (X11; U; Linux i686; en; rv:1.9b4) Gecko Epiphany/2.22
Build Identifier: 

This is a problem that only happens for plugins that use windowless mode, which is why I can only tell how to reproduce it with swfdec-mozilla. I tried reproducing it with diamondx, but I didn't manage to do make it appear on my simple HTML test page.

Reproducible: Always

Steps to Reproduce:
1. Use Firefox on Linux (other Gecko browsers should work fine, too)
2. install swfdec-mozilla plugin from Swfdec git. 
2. Go to www.youtube.com
3. If the Flash doesn't start playing immediately, click the play button.
4. If the bug isn't visible resize the browser window.
Actual Results:  
drawn areas show a 1-pixel wide horizontal black stripe at the left side. (Sometimes at the right side, too?).
You can see this best for windows that repaint random areas (as the youtube home page does), because then the stripes become more. Otherwise it's just a 1-pixel wide line at the left. The lines seems to be drawn in the color used as background for the current area, so positioning the EMBED in a "background-color: green" DIV might help for test cases.

Expected Results:  
No visual glitches.

I did this testing on Ubuntu Hardy.
Status: UNCONFIRMED → NEW
Ever confirmed: true
We're seeing this on ff3b5 with moonlight
from a quick look at the code it looks a lot like nsObjectFrame might have some of the rectangle rounding problems that roc blogged about a while ago such that in some circumstances the rounding comes out wrong.
Attached patch partial patchSplinter Review
This fixes the specific problem for me but there are several other suspicious rounding errors in nsObjectFrame.cs
I'm pretty sure this is the same thing I am seeing in the official Flash Player while developing windowless mode support. The same problem does not occur under Opera.
Opera has windowless support on X? Cool.
Yep, Opera (9.5 beta, at least) has windowless support on X. Transparency, too.
Assignee: nobody → mozbugz
Flags: wanted1.9.1?
Priority: -- → P2
I can reproduce with the unixprinting sample plugin patched with attachment 270128 [details] [diff] [review] on the page in attachment 169652 [details].

The effect is visible after resetting page zoom, then (non-text-only) zooming two steps, then dragging another window horizontally or vertically across the plugin area.

It's as if the outermost pixels that the plug draws are applied with
an alpha < 1.
Status: NEW → ASSIGNED
The origin of the frame is not necessarily aligned to the browser's device
pixels.  What seems to be happening is that gfxXlibNativeRenderer gives the
plugin a drawable with pixels aligned to the origin of the frame, and then
smudges the result into the browser's device pixels.

(In reply to comment #3)
> Created an attachment (id=319501) [details]
> partial patch

I'm not confident rounding out to browser pixels is the right thing to do for
pluginDirtyRect because that is used for plugin pixels:

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/layout/generic/nsObjectFrame.cpp&rev=1.653&mark=4143-4146#4143

The offset between browser and plugin pixels would need to be taken into
account.

--

But really I suspect the right thing to do is to align plugin pixels with
browser pixels.  Maybe this is a good place to do that:

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/layout/generic/nsObjectFrame.cpp&rev=1.653&mark=1093,1095#1090

I'll do some experimenting.

--

As a related issue, window width and height advertised to the plugin are
obtained by rounding frame width and height, without considering offsets in
the origin.

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/layout/generic/nsObjectFrame.cpp&rev=1.653&mark=894-895#889

This is inconsistent with the sizing of widgets for windowed plugins which
round the rectangle bounds:

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/view/src/nsView.cpp&rev=3.260&mark=385#360

I suspect something like nsRect::ScaleRoundPreservingCentersInverse would be
better for nsObjectFrame::FixupWindow too.
(In reply to comment #8)
> (In reply to comment #3)
> > Created an attachment (id=319501) [details] [details]
> > partial patch
> 
> I'm not confident rounding out to browser pixels is the right thing to do for
> pluginDirtyRect because that is used for plugin pixels:

Oh, sorry.  That is _plugin_ pixels that you are rounding to, so that seems (part of) the right thing to do for X11.  I'll think about Windows/Mac.

> But really I suspect the right thing to do is to align plugin pixels with
> browser pixels.

I think we should do this too.  It would be more efficient and look better within the plugin.  There may be some issues round the edges of the plugin rect, but no worse than with windowed plugins.
Marking wanted, feel free to adjust the priority if you disagree.
Flags: wanted1.9.1? → wanted1.9.1+
There a 3 changes here:

1) When the only graphics transform is a translation, snap the plugin rect to
   browser device pixels.  (This is similar to what happens with images.)

   This was enough to solve the problem in the situations that I tested
   because the dirty rect is aligned to browser device pixels.  But perhaps
   this is not always the case - consider an expose event after an unaligned
   partially-covering frame (e.g. menu).

   This also save creating temporary xlib surfaces and means that plugin need
   only draw once instead of twice (to get alpha info).

2) For general transforms and (if there are) non-aligned dirty rects in
   translation-only transforms, round out the dirty rect to plugin pixels.

3) Renderer::Draw need only draw the dirty rect.  Previously temporary xlib
   surfaces were the size of the plugin rect.
Corrected use of width for height.  Added some comments.
Attachment #324576 - Attachment is obsolete: true
Attachment #324914 - Flags: review?(roc)
+  nsPresContext* presContext = mOwner->PresContext();
+  gfxRect dirtyGfxRect(presContext->AppUnitsToGfxUnits(aDirtyRect.x),
+                       presContext->AppUnitsToGfxUnits(aDirtyRect.y),
+                       presContext->AppUnitsToGfxUnits(aDirtyRect.width),
+                       presContext->AppUnitsToGfxUnits(aDirtyRect.height));

Mind adding an nsPresContext method gfxRect AppUnitsToGfxUnits(const nsRect&)? We have this pattern in a few places already. You don't have to convert existing users though (although it would be great if you did in a followup patch).

I think you really want to use gfxContext::UserToDevicePixelSnapped here.

Mind breaking item #3 out into a separate patch?
Comment on attachment 324914 [details] [diff] [review]
snap to pixels, round dirty rect out, draw only dirty v1.1

(In reply to comment #14)
> +  nsPresContext* presContext = mOwner->PresContext();
> +  gfxRect dirtyGfxRect(presContext->AppUnitsToGfxUnits(aDirtyRect.x),
> +                       presContext->AppUnitsToGfxUnits(aDirtyRect.y),
> +                       presContext->AppUnitsToGfxUnits(aDirtyRect.width),
> +                       presContext->AppUnitsToGfxUnits(aDirtyRect.height));
> 
> Mind adding an nsPresContext method gfxRect AppUnitsToGfxUnits(const nsRect&)?
> We have this pattern in a few places already. You don't have to convert
> existing users though (although it would be great if you did in a followup
> patch).

That sounds a good idea.
Do we need it on the nsIDeviceContext as well or instead?

> I think you really want to use gfxContext::UserToDevicePixelSnapped here.

It didn't really seem to do what we need.

We need to RoundOut() the dirty rect to plugin pixels.

If aDirtyRect is ever not aligned to browser pixels then perhaps we should
first RoundOut() to browser pixels, but that doesn't really seem to be the
role of nsPluginInstanceOwner::Paint, which should only need to paint what it
is told to paint. i.e. it should be told to paint full browser pixels and that
seems to be what is happening.

UserToDevicePixelsSnapped rounds to the nearest pixel, whereas dirty rects
should be rounded out.

We should clip to the dirty rect before it is rounded out to plugin pixels, so
that when the plugin over-draws, the NativeRenderer doesn't half draw the
surrrounding pixels.  But the clip rect already seems to be set appropriately.
Do other frames need to worry about clipping their drawing to the dirty rect?

> Mind breaking item #3 out into a separate patch?

Thought you might ask that ;).
I don't think that will be too much trouble.
Attachment #324914 - Flags: review?(roc)
(In reply to comment #15)
> (From update of attachment 324914 [details] [diff] [review])
> (In reply to comment #14)
> > +  nsPresContext* presContext = mOwner->PresContext();
> > +  gfxRect dirtyGfxRect(presContext->AppUnitsToGfxUnits(aDirtyRect.x),
> > +                       presContext->AppUnitsToGfxUnits(aDirtyRect.y),
> > +                       presContext->AppUnitsToGfxUnits(aDirtyRect.width),
> > +                       presContext->AppUnitsToGfxUnits(aDirtyRect.height));
> > 
> > Mind adding an nsPresContext method gfxRect AppUnitsToGfxUnits(const nsRect&)?
> > We have this pattern in a few places already. You don't have to convert
> > existing users though (although it would be great if you did in a followup
> > patch).
> 
> That sounds a good idea.
> Do we need it on the nsIDeviceContext as well or instead?

I think it should be fine on the prescontext. Few places have only a devicecontext.

> > I think you really want to use gfxContext::UserToDevicePixelSnapped here.
> 
> It didn't really seem to do what we need.
> 
> We need to RoundOut() the dirty rect to plugin pixels.
> 
> If aDirtyRect is ever not aligned to browser pixels then perhaps we should
> first RoundOut() to browser pixels, but that doesn't really seem to be the
> role of nsPluginInstanceOwner::Paint, which should only need to paint what it
> is told to paint. i.e. it should be told to paint full browser pixels and that
> seems to be what is happening.
> 
> UserToDevicePixelsSnapped rounds to the nearest pixel, whereas dirty rects
> should be rounded out.

OK, here's what I think it should do. First make the dirty rect relative to the plugin rect and round out to device pixel boundaries, unconditionally, no matter what the current matrix is. Then we should be snapping the plugin rect using UserToDevicePixelsSnapped. (We may need to do that at invalidation time too so that we invalidate the right area.) Then we should be in good shape.

> We should clip to the dirty rect before it is rounded out to plugin pixels, so
> that when the plugin over-draws, the NativeRenderer doesn't half draw the
> surrrounding pixels.  But the clip rect already seems to be set appropriately.
> Do other frames need to worry about clipping their drawing to the dirty rect?

No.
(In reply to comment #16)
Maybe I'm missing something here, but I can't see how to make this work:

> OK, here's what I think it should do. First make the dirty rect relative to
> the plugin rect and round out to device pixel boundaries, unconditionally,
> no matter what the current matrix is.

To transform the dirty rect from frame to plugin coords we need to know the
position of the plugin, which depends on the snapping:

> Then we should be snapping the plugin rect using
> UserToDevicePixelsSnapped.

And part of the reason for operating directly on the transformation matrix
instead of calculating an offset then translating like

  snappedx = round(oldx + x0) - x0;
  x0 += snappedx - oldx;

is that any rounding errors would cause a temporary surface to be created in
the native renderer.
Perhaps we can be sure that x0 becomes an integer here because we can use
oldx = 0?
But it seems clearer if x0 is rounded:

  x0 = round(x0);

> (We may need to do that at invalidation time too so that we invalidate the
> right area.)

Good point.  I'll check that out.
Hmm.

We should probably avoid adding a translation to the rendering context before calling nsPluginInstanceOwner::Paint. Elsewhere in layout we've basically stopped doing that, to make it easier to snap things to device pixels.

Like images, let's use UserToDevicePixelsSnapped to figure out the screen rectangle where the plugin should be rendered.

Then you can take the dirty rect and scale it to devpixel units, rounding out. Then we have the plugin rect and the dirty rect in the same coordinate system. In the common case, when there is only a translation in the matrix, both will be pixel-aligned. And we've done it without looking directly at the matrix. How does that sound.
(In reply to comment #14)
> Mind adding an nsPresContext method gfxRect AppUnitsToGfxUnits(const nsRect&)?
Attachment #325702 - Flags: review?(roc)
There are 2 changes here:

1) When the only graphics transform is a translation, snap the plugin rect to
   browser device pixels.

2) For general transforms, round out the dirty rect to plugin pixels.

(In reply to comment #18)
> We should probably avoid adding a translation to the rendering context before
> calling nsPluginInstanceOwner::Paint. Elsewhere in layout we've basically
> stopped doing that, to make it easier to snap things to device pixels.
> 
> Like images, let's use UserToDevicePixelsSnapped to figure out the screen
> rectangle where the plugin should be rendered.
> 
> Then you can take the dirty rect and scale it to devpixel units, rounding out.
> Then we have the plugin rect and the dirty rect in the same coordinate system.

> In the common case, when there is only a translation in the matrix, both
> will be pixel-aligned. And we've done it without looking directly at the
> matrix. How does that sound.

Done.
I was concerned about how that would work with this translation,

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/view/src/nsViewManager.cpp&rev=3.485&mark=491-496#491

but Rob has assured me there is no need to deal with cases when these offsets
are not integer.
Attachment #325703 - Flags: review?(roc)
Comment on attachment 325703 [details] [diff] [review]
snap to pixels, round dirty rect out

nits:

+  // With translation-only transmation matrices, frameRect is already

typo

+  if (PRInt32(mWindow->width) != mPluginSize.width ||
+      PRInt32(mWindow->height) != mPluginSize.height) {

nsIntSize(mWindow->width, mWindow->height) != mPluginSize

Looks good otherwise.
Attachment #325703 - Flags: superreview+
Attachment #325703 - Flags: review?(roc)
Attachment #325703 - Flags: review+
We should consider this for 3.0.1. The risk for non-X11 and non-windowless plugins is extremely low. And we don't want to cause trouble for any upcoming releases of windowless X11 plugins.
Flags: wanted1.9.0.x?
(In reply to comment #21)
> (From update of attachment 325703 [details] [diff] [review])
> nits
Attachment #325703 - Attachment is obsolete: true
(In reply to comment #16)
> ... we should be snapping the plugin rect
> using UserToDevicePixelsSnapped. (We may need to do that at invalidation time
> too so that we invalidate the right area.)

nsRect::ScaleRoundOut should take care of that here:

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/view/src/nsViewManager.cpp&rev=3.485&mark=2052#2034
(In reply to comment #22)
> We should consider this for 3.0.1. The risk for non-X11 and non-windowless
> plugins is extremely low. And we don't want to cause trouble for any upcoming
> releases of windowless X11 plugins.
> 
I'm strongly in favour of this, as I want to enable windowless mode in Swfdec with the next GNOME release, and don't want to resort to user agent sniffing.
Status: ASSIGNED → RESOLVED
Closed: 16 years ago
Resolution: --- → FIXED
Requesting blocking 1.9.0.2 because both swfdec and Adobe now have windowless-capable Flash plugins available (though Adobe's at least is still in beta), and moonlight also has a windowless plugin.

As well as the visible lines, there is a significant and noticeable performance impact here.
Flags: blocking1.9.0.2?
(In reply to comment #11)
> 3) Renderer::Draw need only draw the dirty rect.  Previously temporary xlib
>    surfaces were the size of the plugin rect.

(In reply to comment #14)
> Mind breaking item #3 out into a separate patch?

Filed bug 444498.
Blocks: 384845
Depends on: 445707
Target Milestone: --- → mozilla1.9.1a1
Karl, can you attach a safe 1.9.0.x patch for this? Flash 10 is now at RC. We might want to take this...
Flags: wanted1.9.0.x? → wanted1.9.0.x+
Attached patch patch for 1.9.0Splinter Review
This is the same code that landed on trunk.  The only difference is in
surrounding context due to other patches landing in a different order.  The
code has been well tested on trunk, so making any changes would only add risk.

This should be taken with the patch for bug 445707.
Attachment #324914 - Attachment is obsolete: true
Attachment #333872 - Flags: approval1.9.0.2?
Comment on attachment 333872 [details] [diff] [review]
patch for 1.9.0

Approved for 1.9.0.2. Please land in CVS when the tree is green. a=ss
Attachment #333872 - Flags: approval1.9.0.2? → approval1.9.0.2+
Oh, and can we get a test for this or no, because it uses plugins?
Flags: in-testsuite?
It's plugins, unfortunately.  Bug 386144 would help with this.
fwiw, we won't block on this, but the patch is approved.
Flags: blocking1.9.0.2? → blocking1.9.0.2-
this patch causes  4 compiler warnings C4244 for roughly every file under layout as it includes nsPresContext.h
(In reply to comment #37)
> this patch causes  4 compiler warnings C4244 for roughly every file under
> layout as it includes nsPresContext.h

Which patch?
Is it the inclusion of gfxRect.h?

Can you paste an error message, please?
I'm having trouble finding one on the tinderbox logs.
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: