Bug 168559 - Activate/deactivate high performance GPU when requested
Summary: Activate/deactivate high performance GPU when requested
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebGL (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Dean Jackson
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2017-02-18 09:23 PST by Dean Jackson
Modified: 2017-02-20 04:30 PST (History)
12 users (show)

See Also:


Attachments
Patch (19.43 KB, patch)
2017-02-18 09:30 PST, Dean Jackson
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews105 for mac-elcapitan-wk2 (840.17 KB, application/zip)
2017-02-18 14:05 PST, Build Bot
no flags Details
Patch (29.54 KB, patch)
2017-02-19 08:32 PST, Dean Jackson
jonlee: review+
buildbot: commit-queue-
Details | Formatted Diff | Diff
Archive of layout-test-results from ews103 for mac-elcapitan (891.61 KB, application/zip)
2017-02-19 09:37 PST, Build Bot
no flags Details
Archive of layout-test-results from ews115 for mac-elcapitan (1.63 MB, application/zip)
2017-02-19 09:50 PST, Build Bot
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dean Jackson 2017-02-18 09:23:48 PST
Activate/deactivate high performance GPU when requested
Comment 1 Dean Jackson 2017-02-18 09:26:37 PST
On systems with more than one GPU, toggle the discrete GPU when there is WebGL content that requested high-performance.

But also react to visibility and activity events, so that we can migrate back to the integrated GPU when in a background tab, etc.
Comment 2 Radar WebKit Bug Importer 2017-02-18 09:26:50 PST
<rdar://problem/30592266>
Comment 3 Dean Jackson 2017-02-18 09:30:43 PST
Created attachment 302050 [details]
Patch
Comment 4 Dean Jackson 2017-02-18 09:33:46 PST
This patch isn't ready for formal review yet, because I still need to add code that delays the switch back from dGPU to iGPU. This will be a simple timer that waits 10 seconds, just in case we are swapping through tabs - we don't want to churn the GPU.
Comment 5 Build Bot 2017-02-18 14:05:54 PST
Comment on attachment 302050 [details]
Patch

Attachment 302050 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.webkit.org/results/3151019

New failing tests:
fast/canvas/webgl/context-creation-attributes.html
Comment 6 Build Bot 2017-02-18 14:05:59 PST
Created attachment 302056 [details]
Archive of layout-test-results from ews105 for mac-elcapitan-wk2

The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews.
Bot: ews105  Port: mac-elcapitan-wk2  Platform: Mac OS X 10.11.6
Comment 7 Jon Lee 2017-02-18 18:56:58 PST
Comment on attachment 302050 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=302050&action=review

r- based on offline discussion.

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:633
> +    if (page) {

Reverse the conditional and return early.

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:5889
> +        m_context->setContextIsActive(newActivityState & ActivityState::IsVisible);

This doesn't include occlusion. My own hacked version:

    ActivityState::Flags changed = oldActivityState ^ newActivityState;
    if (changed & (ActivityState::IsVisible | ActivityState::IsVisibleOrOccluded))
        m_context->setContextIsActive((newActivityState & ActivityState::IsVisible)&&(newActivityState & ~ActivityState::IsVisibleOrOccluded));

almost worked. It would switch to iGPU at first, then a split second later switch back to dGPU.

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:65
> +static Vector<GraphicsContext3D*>& liveContexts()

All of these static functions should be in a Manager-type class, I think.

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:68
> +    return s_liveContexts;

"contexts" instead of "active" or "live" ones.

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:135
> +    CGLPixelFormatObj* highPerformanceLock = highPerformancePixelFormatObj();

We should refactor mechanics of retainHighPerformanceGraphics/releaseHighPerformanceGraphics from maintenance of the contexts list.
Comment 8 Jon Lee 2017-02-18 18:58:17 PST
Comment on attachment 302050 [details]
Patch

Whoops; I missed Dean's earlier comment.
Comment 9 Dean Jackson 2017-02-19 07:36:50 PST
Comment on attachment 302050 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=302050&action=review

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:633
>> +    if (page) {
> 
> Reverse the conditional and return early.

Fixed.

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:5889
>> +        m_context->setContextIsActive(newActivityState & ActivityState::IsVisible);
> 
> This doesn't include occlusion. My own hacked version:
> 
>     ActivityState::Flags changed = oldActivityState ^ newActivityState;
>     if (changed & (ActivityState::IsVisible | ActivityState::IsVisibleOrOccluded))
>         m_context->setContextIsActive((newActivityState & ActivityState::IsVisible)&&(newActivityState & ~ActivityState::IsVisibleOrOccluded));
> 
> almost worked. It would switch to iGPU at first, then a split second later switch back to dGPU.

Do we want this? Visible or Occluded seems wrong. If the window is occluded, we want to turn off the dGPU. I'm not sure I understand the point of this flag, unless it is meant to mean partially occluded (although that sounds like visible to me!)

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:65
>> +static Vector<GraphicsContext3D*>& liveContexts()
> 
> All of these static functions should be in a Manager-type class, I think.

I've fixed all this too.
Comment 10 Dean Jackson 2017-02-19 08:32:16 PST
Created attachment 302081 [details]
Patch
Comment 11 Dean Jackson 2017-02-19 08:33:47 PST
Ready for review now. I'm still thinking of the best way to test, given a full test requires particular hardware.
Comment 12 Build Bot 2017-02-19 09:37:07 PST
Comment on attachment 302081 [details]
Patch

Attachment 302081 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/3154943

New failing tests:
fast/canvas/webgl/context-creation-attributes.html
Comment 13 Build Bot 2017-02-19 09:37:12 PST
Created attachment 302083 [details]
Archive of layout-test-results from ews103 for mac-elcapitan

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews103  Port: mac-elcapitan  Platform: Mac OS X 10.11.6
Comment 14 Build Bot 2017-02-19 09:50:36 PST
Comment on attachment 302081 [details]
Patch

Attachment 302081 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/3154961

New failing tests:
fast/canvas/webgl/context-creation-attributes.html
Comment 15 Build Bot 2017-02-19 09:50:40 PST
Created attachment 302084 [details]
Archive of layout-test-results from ews115 for mac-elcapitan

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews115  Port: mac-elcapitan  Platform: Mac OS X 10.11.6
Comment 16 Jon Lee 2017-02-19 11:30:27 PST
Comment on attachment 302081 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=302081&action=review

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:501
> +        addActivityStateChangeObserver();

Maybe it's a better idea to push the check into the call to avoid having to remember add the if in multiple places?

Then rename as `addActivityStateChangeObserverIfNeeded`

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:637
> +    m_addedActivityStateChangeObserver = true;

I don't think this bool is needed at all? But if so, shouldn't this whole function include a check for `if (!m_addedActivityStateChangeObserver)`? Otherwise there's a possibility of calling `setContextVisibility` multiple times when this function is accidentally called multiple times.

That said, I think there would be no harm in that if the list of contexts in the manager were a HashSet.

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:640
> +    // state change that would tell the context it is active.

Do you mean "tell the context is it visible"?

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:642
> +        m_context->setContextVisibility(true);

In the accidental case this method gets calls twice, if the context is not visible, then you won't have a chance to turn visibility to false. Use `isVisibleAndActive` instead of `true`?

Also, here you're checking for visible and active, but in `activityStateDidChange` you're only checking for visible. Shouldn't they use the same check?

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:647
> +    if (m_addedActivityStateChangeObserver) {

Don't think the bool is needed. If the observer isn't in the list it will just be a no-op.

> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:5630
> +        addActivityStateChangeObserver();

Could convert to `addActivityStateChangeObserverIfNecessary`.

> Source/WebCore/platform/graphics/GraphicsContext3D.h:1148
> +    GraphicsContext3DPowerPreference usedPowerPreference() const { return m_usedPowerPreference; }

What does it mean "used" power preference? The manager is in control of honoring the preference. Maybe just "powerPreference"?

> Source/WebCore/platform/graphics/GraphicsContext3D.h:1383
> +    GraphicsContext3DPowerPreference m_usedPowerPreference { GraphicsContext3DPowerPreference::Default };

What does it mean "used" power preference? The manager is in control of honoring the preference. Maybe just "powerPreference"?

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:67
> +class GraphicsContext3DManager {

Should split this to its own class, in a future patch.

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:90
> +    Vector<GraphicsContext3D*> m_contextsRequiringHighPerformance;

Couldn't these just be HashSets?

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:139
> +    m_contexts.removeFirst(context);

Should this function also clean up m_contextsRequiringHighPerformance?

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:149
> +    ASSERT(context);

Early return for `!hasMuxableGPU()`?

I'm unsure that relying on proper setting of m_usedPowerPreference is enough for future-proofing.

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:163
> +{

Early return for `!hasMuxableGPU()`?

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:175
> +#if PLATFORM(MAC)

Early return for `!hasMuxableGPU()`?

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:198
> +    } else if (!m_contextsRequiringHighPerformance.size() && m_pixelFormatObj) {

`!m_contextsRequiringHighPerformance.size()` is already true by this point. Remove.

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:204
> +            static const int timeToKeepHighPerformanceGPUAliveInSeconds = 10;

Is there a standard timeout we could use? Gavin, any suggestions?

> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:569
> +    manager().removeContext(this);

Can we combine this? Removal of a context should force removal of the context in the high perf list.
Comment 17 Jon Lee 2017-02-19 13:10:56 PST
Comment on attachment 302081 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=302081&action=review

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:204
>> +            static const int timeToKeepHighPerformanceGPUAliveInSeconds = 10;
> 
> Is there a standard timeout we could use? Gavin, any suggestions?

This will also require tests to wait for this long. Will there be a way to configure the timeout to 0 for tests?
Comment 18 Jon Lee 2017-02-20 01:06:33 PST
Comment on attachment 302081 [details]
Patch

Most of my comments could be addressed in a later patch. r=me with bug tests fixed.
Comment 19 Dean Jackson 2017-02-20 04:27:23 PST
Comment on attachment 302081 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=302081&action=review

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:501
>> +        addActivityStateChangeObserver();
> 
> Maybe it's a better idea to push the check into the call to avoid having to remember add the if in multiple places?
> 
> Then rename as `addActivityStateChangeObserverIfNeeded`

yes, thanks. done.

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:637
>> +    m_addedActivityStateChangeObserver = true;
> 
> I don't think this bool is needed at all? But if so, shouldn't this whole function include a check for `if (!m_addedActivityStateChangeObserver)`? Otherwise there's a possibility of calling `setContextVisibility` multiple times when this function is accidentally called multiple times.
> 
> That said, I think there would be no harm in that if the list of contexts in the manager were a HashSet.

Removed the bool. I vaguely remember having a reason for it at some time :)

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:640
>> +    // state change that would tell the context it is active.
> 
> Do you mean "tell the context is it visible"?

Yes.

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:642
>> +        m_context->setContextVisibility(true);
> 
> In the accidental case this method gets calls twice, if the context is not visible, then you won't have a chance to turn visibility to false. Use `isVisibleAndActive` instead of `true`?
> 
> Also, here you're checking for visible and active, but in `activityStateDidChange` you're only checking for visible. Shouldn't they use the same check?

changed to isVisible.

I've also changed to simply call m_context->setContextVisibility(page->isVisible()), although the thinking was that if the context is not visible, then there is no need to enable the dGPU.

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:647
>> +    if (m_addedActivityStateChangeObserver) {
> 
> Don't think the bool is needed. If the observer isn't in the list it will just be a no-op.

Removed.

>> Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:5630
>> +        addActivityStateChangeObserver();
> 
> Could convert to `addActivityStateChangeObserverIfNecessary`.

Done.

>> Source/WebCore/platform/graphics/GraphicsContext3D.h:1148
>> +    GraphicsContext3DPowerPreference usedPowerPreference() const { return m_usedPowerPreference; }
> 
> What does it mean "used" power preference? The manager is in control of honoring the preference. Maybe just "powerPreference"?

Renamed to 'powerPreferenceUsedForCreation'

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:90
>> +    Vector<GraphicsContext3D*> m_contextsRequiringHighPerformance;
> 
> Couldn't these just be HashSets?

m_contextsRequiringHighPerformance can be a HashSet. Changed.

m_contexts cannot, since it needs to be ordered for recycling the oldest context.

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:139
>> +    m_contexts.removeFirst(context);
> 
> Should this function also clean up m_contextsRequiringHighPerformance?

Yes, added. And that meant i could remove some other code.

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:149
>> +    ASSERT(context);
> 
> Early return for `!hasMuxableGPU()`?
> 
> I'm unsure that relying on proper setting of m_usedPowerPreference is enough for future-proofing.

Early return added.

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:163
>> +{
> 
> Early return for `!hasMuxableGPU()`?

I didn't do that one, because it means the addContextRequiringHighPerformance must have an equivalent check. I feel like it's ok to be less cautious in this method because it doesn't do bad things.

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:175
>> +#if PLATFORM(MAC)
> 
> Early return for `!hasMuxableGPU()`?

Ditto.

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:198
>> +    } else if (!m_contextsRequiringHighPerformance.size() && m_pixelFormatObj) {
> 
> `!m_contextsRequiringHighPerformance.size()` is already true by this point. Remove.

Thanks.

>>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:204
>>> +            static const int timeToKeepHighPerformanceGPUAliveInSeconds = 10;
>> 
>> Is there a standard timeout we could use? Gavin, any suggestions?
> 
> This will also require tests to wait for this long. Will there be a way to configure the timeout to 0 for tests?

I've added a FIXME to this. Unfortunately this class doesn't have a reference to anything with a Settings object. And adding a reference would mean that the manager() call has to take a parameter.

>> Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm:569
>> +    manager().removeContext(this);
> 
> Can we combine this? Removal of a context should force removal of the context in the high perf list.

Yes, that's done.
Comment 20 Dean Jackson 2017-02-20 04:30:39 PST
Committed r212633: <http://trac.webkit.org/changeset/212633>