2
* Copyright (C) 2018 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
13
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23
* THE POSSIBILITY OF SUCH DAMAGE.
28
#if ENABLE(GRAPHICS_CONTEXT_GL)
29
#include "GraphicsContextGLOpenGLManager.h"
31
#include "GraphicsContextGLOpenGL.h"
34
#if HAVE(APPLE_GRAPHICS_CONTROL)
35
#include <sys/sysctl.h>
38
#if PLATFORM(MAC) && (USE(OPENGL) || USE(ANGLE))
39
#include "SwitchingGPUClient.h"
40
#include <OpenGL/OpenGL.h>
45
#if HAVE(APPLE_GRAPHICS_CONTROL)
52
static io_connect_t attachToAppleGraphicsControl()
54
mach_port_t masterPort = MACH_PORT_NULL;
56
if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
57
return MACH_PORT_NULL;
59
CFDictionaryRef classToMatch = IOServiceMatching("AppleGraphicsControl");
61
return MACH_PORT_NULL;
63
kern_return_t kernResult;
64
io_iterator_t iterator;
65
if ((kernResult = IOServiceGetMatchingServices(masterPort, classToMatch, &iterator)) != KERN_SUCCESS)
66
return MACH_PORT_NULL;
68
io_service_t serviceObject = IOIteratorNext(iterator);
69
IOObjectRelease(iterator);
71
return MACH_PORT_NULL;
73
io_connect_t dataPort;
74
IOObjectRetain(serviceObject);
75
kernResult = IOServiceOpen(serviceObject, mach_task_self(), 0, &dataPort);
76
IOObjectRelease(serviceObject);
78
return (kernResult == KERN_SUCCESS) ? dataPort : MACH_PORT_NULL;
81
static bool hasMuxCapability()
83
io_connect_t dataPort = attachToAppleGraphicsControl();
85
if (dataPort == MACH_PORT_NULL)
89
if (IOConnectCallScalarMethod(dataPort, kAGCOpen, nullptr, 0, nullptr, nullptr) == KERN_SUCCESS) {
90
IOConnectCallScalarMethod(dataPort, kAGCClose, nullptr, 0, nullptr, nullptr);
95
IOServiceClose(dataPort);
98
// This is detecting Mac hardware with an Intel g575 GPU, which
99
// we don't want to make available to muxing.
100
// Based on information from Apple's OpenGL team, such devices
101
// have four or fewer processors.
102
// <rdar://problem/30060378>
103
int names[2] = { CTL_HW, HW_NCPU };
105
size_t cpuCountLength = sizeof(cpuCount);
106
sysctl(names, 2, &cpuCount, &cpuCountLength, nullptr, 0);
107
result = cpuCount > 4;
113
bool hasLowAndHighPowerGPUs()
115
static bool canMux = hasMuxCapability();
118
#endif // HAVE(APPLE_GRAPHICS_CONTROL)
120
GraphicsContextGLOpenGLManager& GraphicsContextGLOpenGLManager::sharedManager()
122
static NeverDestroyed<GraphicsContextGLOpenGLManager> s_manager;
127
void GraphicsContextGLOpenGLManager::displayWasReconfigured(CGDirectDisplayID, CGDisplayChangeSummaryFlags flags, void*)
129
LOG(WebGL, "GraphicsContextGLOpenGLManager::displayWasReconfigured");
130
if (flags & kCGDisplaySetModeFlag)
131
GraphicsContextGLOpenGLManager::sharedManager().updateAllContexts();
135
void GraphicsContextGLOpenGLManager::updateAllContexts()
137
#if PLATFORM(MAC) && (USE(OPENGL) || USE(ANGLE))
138
for (const auto& context : m_contexts) {
139
context->updateCGLContext();
140
context->dispatchContextChangedNotification();
146
void GraphicsContextGLOpenGLManager::screenDidChange(PlatformDisplayID displayID, const HostWindow* window)
148
for (const auto& contextAndWindow : m_contextWindowMap) {
149
if (contextAndWindow.value == window) {
150
contextAndWindow.key->screenDidChange(displayID);
151
LOG(WebGL, "Changing context (%p) to display (%d).", contextAndWindow.key, displayID);
157
void GraphicsContextGLOpenGLManager::addContext(GraphicsContextGLOpenGL* context, HostWindow* window)
163
#if PLATFORM(MAC) && !ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
164
if (!m_contexts.size())
165
CGDisplayRegisterReconfigurationCallback(displayWasReconfigured, nullptr);
168
ASSERT(!m_contexts.contains(context));
169
m_contexts.append(context);
170
m_contextWindowMap.set(context, window);
173
void GraphicsContextGLOpenGLManager::removeContext(GraphicsContextGLOpenGL* context)
175
if (!m_contexts.contains(context))
177
m_contexts.removeFirst(context);
178
m_contextWindowMap.remove(context);
179
removeContextRequiringHighPerformance(context);
181
#if PLATFORM(MAC) && !ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
182
if (!m_contexts.size())
183
CGDisplayRemoveReconfigurationCallback(displayWasReconfigured, nullptr);
187
HostWindow* GraphicsContextGLOpenGLManager::hostWindowForContext(GraphicsContextGLOpenGL* context) const
189
ASSERT(m_contextWindowMap.contains(context));
190
return m_contextWindowMap.get(context);
193
void GraphicsContextGLOpenGLManager::addContextRequiringHighPerformance(GraphicsContextGLOpenGL* context)
199
ASSERT(m_contexts.contains(context));
200
ASSERT(!m_contextsRequiringHighPerformance.contains(context));
202
LOG(WebGL, "This context (%p) requires the high-performance GPU.", context);
203
m_contextsRequiringHighPerformance.add(context);
205
updateHighPerformanceState();
208
void GraphicsContextGLOpenGLManager::removeContextRequiringHighPerformance(GraphicsContextGLOpenGL* context)
213
if (!m_contextsRequiringHighPerformance.contains(context))
216
LOG(WebGL, "This context (%p) no longer requires the high-performance GPU.", context);
217
m_contextsRequiringHighPerformance.remove(context);
219
updateHighPerformanceState();
222
void GraphicsContextGLOpenGLManager::updateHighPerformanceState()
224
#if PLATFORM(MAC) && (USE(OPENGL) || USE(ANGLE))
225
if (!hasLowAndHighPowerGPUs())
228
if (m_contextsRequiringHighPerformance.size()) {
230
if (m_disableHighPerformanceGPUTimer.isActive()) {
231
LOG(WebGL, "Cancel pending timer for turning off high-performance GPU.");
232
m_disableHighPerformanceGPUTimer.stop();
235
if (!m_requestingHighPerformance) {
236
LOG(WebGL, "Request the high-performance GPU.");
237
m_requestingHighPerformance = true;
239
SwitchingGPUClient::singleton().requestHighPerformanceGPU();
244
// Don't immediately turn off the high-performance GPU. The user might be
245
// swapping back and forth between tabs or windows, and we don't want to cause
246
// churn if we can avoid it.
247
if (!m_disableHighPerformanceGPUTimer.isActive()) {
248
LOG(WebGL, "Set a timer to release the high-performance GPU.");
249
// FIXME: Expose this value as a Setting, which would require this class
250
// to reference a frame, page or document.
251
static const Seconds timeToKeepHighPerformanceGPUAlive { 10_s };
252
m_disableHighPerformanceGPUTimer.startOneShot(timeToKeepHighPerformanceGPUAlive);
258
void GraphicsContextGLOpenGLManager::disableHighPerformanceGPUTimerFired()
260
if (m_contextsRequiringHighPerformance.size())
263
m_requestingHighPerformance = false;
264
#if PLATFORM(MAC) && (USE(OPENGL) || USE(ANGLE))
265
SwitchingGPUClient::singleton().releaseHighPerformanceGPU();
269
void GraphicsContextGLOpenGLManager::recycleContextIfNecessary()
271
if (hasTooManyContexts()) {
272
LOG(WebGL, "GraphicsContextGLOpenGLManager recycled context (%p).", m_contexts[0]);
273
m_contexts[0]->recycleContext();
277
} // namespace WebCore
279
#endif // ENABLE(GRAPHICS_CONTEXT_GL)