Closed Bug 650988 Opened 13 years ago Closed 12 years ago

Do image scaling on the GPU

Categories

(Core :: Graphics, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla15
Tracking Status
fennec + ---

People

(Reporter: mattwoodrow, Assigned: gal)

References

(Blocks 1 open bug)

Details

(Keywords: mobile, perf)

Attachments

(4 files, 11 obsolete files)

27.32 KB, patch
roc
: review+
Details | Diff | Splinter Review
29.36 KB, patch
cjones
: review+
Details | Diff | Splinter Review
1.21 MB, image/png
Details
1.19 MB, image/png
Details
Attached patch Give images their own layer (obsolete) — Splinter Review
We should look into creating separate layers for images so that scaling happens on the GPU.

We probably need to profile this to determine heuristics of when to do this, and when to stick with scaling in the content process.

Attached is my efforts while stuck on a plane, seems to mainly work except the images flicker sometimes. Only with GL layers though, BasicLayers appears to work 100% correct.
Attachment #526876 - Flags: feedback?(jones.chris.g)
lets make sure we don't bump memory usage for mobile.
This will almost always increase total memory usage, but hopefully with a substantial performance win.

Hopefully we can tune this so we can get the best of both.

One thought I had (and this is substantially more complex), is we can delete the decoded image's backing store once it has been uploaded to a texture. Not sure how this would work exactly, since the GL texture objects lifetime would be tied to a layout (content process) object, instead of a layers object. It would fix the extra memory usage problem though.
Is this the same as bug 598874?  Doesn't particularly matter except that roc posted a clever idea there for JPEG images.
This is indeed identical to bug 598874. Want me to close this and move the attachment over?
Nah, here is fine.  On second thought, it's probably better to track roc's idea in a separate bug.
Blocks: 598736
Status: NEW → RESOLVED
Closed: 13 years ago
Resolution: --- → DUPLICATE
Urgh.
Status: RESOLVED → REOPENED
Resolution: DUPLICATE → ---
tracking-fennec: --- → ?
Keywords: mobile, perf
OS: Mac OS X → All
Hardware: x86 → All
tracking-fennec: ? → 2.0-
Whiteboard: [fennec-6]
Comment on attachment 526876 [details] [diff] [review]
Give images their own layer

As a proof-of-concept this looks OK, though there are bugs in it ;).

The biggest omission from the heuristics here is the pres-shell resolution, which is the common case we care about on fennec.  I'm not sure if GetDestRect() here takes CSS transforms into account, but that's another input that would be nice to have.

Another big omission is that we only want to do this if we have "free" resampling in the chrome process.  We don't want to spin CPU resampling while the UI remains blocked unless we get some secondary benefit like reduced memory traffic.  Probably another IsCompositingCheap() check somewhere near here would be OK initially.

I think it's going to be fairly difficult to tune the heuristics.  Are there pages anyone knows of where we have high repaint lag from resampling large images?  (vs. say, decoding them on draw.)  We might need to construct those benchmarks.  I'm not comfortable landing a successor to this patch until we have a good suite to tune on.  It would be even better to get these kinds of tests into talos or another perf-test suite that's being ramped up right now.

(Clearing feedback request because I'm not sure whether + or - makes sense.)
Attachment #526876 - Flags: feedback?(jones.chris.g)
How does the pres shell resolution code work for other layer types? Like video and canvas?

The IsCompositingCheap() check is on my todo list, will fix this.

Stuart: Any ideas regarding testing this?
tracking-fennec: - → 6+
Whiteboard: [fennec-6]
Given that we already retain images, I'm not sure why this approach should use more memory.  We probably need logic to create/destroy layers based on images coming/going from the image cache.  I suppose we'll want most images to live on the GPU from the start and use those as the official retained copy rather than the software one, so probably imagelib based hacking is also required here as well.  Probably a decent chunk of work, but if all done I don't see why we'd be worse off in any respect here.
The simple implementation (as I currently have it), won't affect the memory usage of ThebesLayers much (if at all) but it will cause the images to be stored a second time in texture memory.

But yes, we can definitely work around this with imagelib changes. We can also decode directly to YUV textures and speed up decoding.

As Chris said, the main blocker on getting this finished is deciding how to measure the performance changes of it.
tracking-fennec: 6+ → 7+
Looks like Matt is doing the work, so I am assigning the bug to him. "nobody" wasn't right.
Assignee: nobody → matt.woodrow
tracking-fennec: 7+ → ?
tracking-fennec: ? → 8+
tracking-fennec: 8+ → +
Attachment #526876 - Attachment is obsolete: true
Attached patch rebased - actually compiles (obsolete) — Splinter Review
last one didn't compile
Attachment #554403 - Attachment is obsolete: true
A few observations: does not create an image layer on google.de, but on other pages I get lots of white (empty?) images. This is on Maemo6.
To make this work on background-image, nsDisplayBackground just needs similar treatment to nsDisplayImage here, hopefully with some refactoring to share code.

GetLayerState should be modified to pass in scale parameters so to make the heuristics here work properly.

I would do it without support for tiled images first, then add support for tiled images to ImageLayer and and tiled background-image support on top of that.
Matt, I am totally not qualified to work on this but I will poke at it. Feel free to steal back at any time if you have a few cycles to work on this.
Assignee: matt.woodrow → gal
Working on an updated patch for nsImageFrame and looking at how to do it for background images. I think I want a layer for the background image only, but right now ProcessDisplayItems assumes that we only get one layer back for a frame, so I would let frames with scaled backgrounds return a ContainerLayer that contains all the layers it needs. roc, does that sound right?
Attached patch WIP (obsolete) — Splinter Review
Attachment #554412 - Attachment is obsolete: true
Attached patch WIP, compiling (obsolete) — Splinter Review
Attachment #619497 - Attachment is obsolete: true
Patch works. What cnn.com does to be so dog slow on mobile is scaling up 1px images to really large sizes, by the way. Go cnn.com!
Attached patch patch (obsolete) — Splinter Review
We should do this everywhere, not just mobile. I will file a separate bug to do this for background images too.
Attachment #619498 - Attachment is obsolete: true
Attachment #619499 - Flags: review?(roc)
Blocks: 750172
No longer blocks: 750172
Depends on: 750172
Summary: Do image scaling on the GPU on mobile → Do image scaling on the GPU
Background image scaling on the GPU cloned as bug 750172.
(In reply to Andreas Gal :gal from comment #21)
> Patch works. What cnn.com does to be so dog slow on mobile is scaling up 1px
> images to really large sizes, by the way. Go cnn.com!

Those images are fully transparent, aren't they? We can optimize that particularly well...
Yes, those layers should turn into color layers.
Comment on attachment 619499 [details] [diff] [review]
patch

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

::: layout/generic/nsImageFrame.cpp
@@ +1268,5 @@
> +
> +  // Adjust it by the zoom factor.
> +  nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
> +  scale.width *= presShell->GetXResolution();
> +  scale.height *= presShell->GetYResolution();

This isn't right for images that are inside a transform. We need to thread a FrameLayerBuilder::ContainerParameters parameter through all calls to GetLayerState and use its resolution here.

@@ +1279,5 @@
> +  // If we are downscaling by more than 2x, avoid using a layer because of the
> +  // reduced memory usage of redrawing at the lower resolution.
> +  if (scale.width * scale.height < 0.5 * 0.5) {
> +    return LAYER_INACTIVE;
> +  }

Maybe instead of this check, we should simply check the overall size of the scaled image. If it's less than some threshold, don't use a layer. So small images scaled up (or down) would not need layers, and very large images scaled down could still use a layer.
Attachment #619499 - Flags: review?(roc) → review-
(In reply to Andreas Gal :gal from comment #25)
> Yes, those layers should turn into color layers.

Fully transparent images can turn into nothing at all. We can actually eliminate them at display-list-construction time.

If we have important cases where single-color images (not fully transparent) are being used, we just need to make sure that drawing such images turns into a simple color fill that doesn't require scaling.

In general I would rather do those things than layer-ize general images, because those are clear wins that don't require tradeoffs.
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #27)
> In general I would rather do those things than layer-ize general images,
> because those are clear wins that don't require tradeoffs.

(We can still layer-ize general images if we have data showing it's a good win after we've done those other optimizations.)
++1x1 image -> color layer trick
Attached patch patch (obsolete) — Splinter Review
Attachment #619499 - Attachment is obsolete: true
Comment on attachment 619801 [details] [diff] [review]
patch

This offloads a bunch of image scaling onto the GPU on cnn.com.
Attachment #619801 - Flags: review?(roc)
Do you have any performance numbers off hand?
I will try to get to it tonight, otherwise jeffm will do it in the morning. This doesn't address background images yet, so it won't handle the biggest chunk of cycles we spend on scaling.
Comment on attachment 619801 [details] [diff] [review]
patch

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

::: layout/generic/nsImageFrame.cpp
@@ +1278,5 @@
> +
> +  // If the target size is pretty small, no point in using a layer.
> +  if (destRect.width * destRect.height < 64 * 64) {
> +    return LAYER_INACTIVE;
> +  }

This calculation needs to take aParameters.mXScale/mYScale into account. Probably want to scale destRect width/height by mX/YScale before doing the rest.

Also ... We should do all this only if (aManager->IsCompositingCheap()), i.e., we're GPU accelerated.

It might also turn out that this is bad because it forces us to create more transparent layers on top of the images, or something like that. But we shall see.
Attachment #619801 - Flags: review?(roc) → review-
Attached patch patch (obsolete) — Splinter Review
Attachment #619801 - Attachment is obsolete: true
Attachment #619980 - Flags: review?(roc)
Comment on attachment 619980 [details] [diff] [review]
patch

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

::: layout/generic/nsImageFrame.cpp
@@ +1278,5 @@
> +    return LAYER_INACTIVE;
> +  }
> +
> +  destRect.width *= aParameters.mXScale;
> +  destRect.height *= aParameters.mYScale;

Move this up to right after GetDestRect(), then you don't need to multiple scale.width and scale.height by aParameters.mX/YScale separately.
Attachment #619980 - Flags: review?(roc) → review-
Attached patch patchSplinter Review
Attachment #619980 - Attachment is obsolete: true
Attachment #620024 - Flags: review?(roc)
Attachment #620200 - Attachment is obsolete: true
Comment on attachment 620201 [details] [diff] [review]
patch with pref to disable it by default

You need to learn the joys of mq ;).  Easier to review as separate patch.

>+bool
>+nsLayoutUtils::UseGPUToScaleImages()

>+    sUseGPUToScaleImagesEnabled = mozilla::Preferences::GetBool("layout.gpu-scaling.enabled", true);

sGPUImageScalingEnabled, pref name "layout.gpu-image-scaling.enabled"

This should default to false, and you need to enable it explicitly for
fennec-android, mobile/android/app/mobile.js.  Right?

>diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h

>   /**
>+   * Checks whether we want to use the GPU to scale images when
>+   * possible.
>+   */
>+  static bool UseGPUToScaleImages();
>+

Call this |GPUImageScalingEnabled()|.  "Use..()" and the comment here suggests it's doing
an interesting logic check, which it's not.

r=me on pref check code only, with those fixes.
Attachment #620201 - Flags: review+
As an added benefit, this patch substantially improves image scaling quality. Checkout the "Click to play" text in the image for example. The screenshots were taken on a MacOSX desktop (and zoomed in).
TIFF?! for shame. :)
Attachment #620212 - Attachment is obsolete: true
Mac OS X Grab :) It was tiff or jpeg ;)
Target Milestone: --- → mozilla15
https://hg.mozilla.org/mozilla-central/rev/ed9e42293a6f
Status: REOPENED → RESOLVED
Closed: 13 years ago12 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: