Commit af727792 by Kenneth Russell Committed by Commit Bot

Improve EGL_ANGLE_power_preference on dual-GPU MacBook Pros.

Add the ability to release and reacquire the high-power GPU, and to respond to changes in the active GPU. In Chromium, the GPU process can not access the WindowServer. An external process must inform ANGLE that the active GPU has changed, and that ANGLE should switch its internal context to the new GPU. Incorporates a couple of functions from WebKit, used with permission, to effect this GPU switch. A follow-on change in Chromium which uses these new APIs will make the existing dual-GPU tests pass with ANGLE and the passthrough command decoder. Carry forward Chromium's workaround of disabling GPU switching on older MacBook Pros to ensure stability. Document the process of adding new EGL extensions to ANGLE. Bug: chromium:1091824 Change-Id: I499739156e851b493555d4d6e4aef87d8b97fa31 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2240638 Commit-Queue: Kenneth Russell <kbr@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 0bd0a913
# Introduction
This page describes how to add new extensions to ANGLE.
# Adding EGL extensions
Note: see also [anglebug.com/2621](http://anglebug.com/2621), linked
from the [starter project](Starter-Projects.md) doc, to simplify some
of these steps.
For extensions requiring new entry points:
* Add the extension xml to this file:
https://source.chromium.org/chromium/chromium/src/+/master:third_party/angle/scripts/egl_angle_ext.xml
* Note the prototypes for the new entry points must be added to the
top of the file, and the functions themselves grouped under the
extension name to the bottom of the file.
* Modify `scripts/registry_xml.py` to add the new extension as needed.
* run
[scripts/run_code_generation.py](https://source.chromium.org/chromium/chromium/src/+/master:third_party/angle/scripts/run_code_generation.py)
* The entry point itself goes in the
[entry_points_egl_ext.h](https://source.chromium.org/chromium/chromium/src/+/master:third_party/angle/src/libGLESv2/entry_points_egl_ext.h)
and cpp files.
* Update
[eglext_angle.h](https://source.chromium.org/chromium/chromium/src/+/master:third_party/angle/include/EGL/eglext_angle.h)
with the new entry points and/or enums.
* Add members to the appropriate Extensions struct in
[Caps.h](https://source.chromium.org/chromium/chromium/src/+/master:third_party/angle/src/libANGLE/Caps.h)
* Initialize extension availability in the `Display` subclass's
`generateExtensions` method for displays that can support the extension.
......@@ -42,7 +42,13 @@ New Types
New Procedures and Functions
None
void eglReleaseHighPowerGPUANGLE(
EGLDisplay dpy,
EGLContext context);
void eglReacquireHighPowerGPUANGLE(
EGLDisplay dpy,
EGLContext context);
void eglHandleGPUSwitchANGLE(EGLDisplay dpy);
New Tokens
......@@ -74,6 +80,46 @@ Additions to the EGL 1.4 Specification
NSSupportsAutomaticGraphicsSwitching attribute to true in its
Info.plist in order for this extension to operate as advertised.
eglReleaseHighPowerGPUANGLE, when passed an EGLContext allocated
with the EGL_POWER_PREFERENCE_ANGLE context creation attribute set
to EGL_HIGH_POWER_ANGLE, will cause that context to release its
hold on the high-power GPU.
eglReacquireHighPowerGPUANGLE, when passed an EGLContext allocated
with the EGL_POWER_PREFERENCE_ANGLE context creation attribute set
to EGL_HIGH_POWER_ANGLE and which was previously released via
eglReleaseHighPowerGPUANGLE, will cause that context to reacquire
its hold on the high-power GPU.
eglReleaseHighPowerGPUANGLE and eglReacquireHighPowerGPUANGLE have
no effect on contexts that were allocated with the
EGL_LOW_POWER_ANGLE preference, or contexts not allocated with
either preference.
For either eglReleaseHighPowerGPUANGLE or
eglReacquireHighPowerGPUANGLE:
If |dpy| is not a valid display, an EGL_BAD_DISPLAY error is
generated.
if |dpy| is an uninitialized display, an EGL_NOT_INITIALIZED error
is generated.
If |context| is not a valid context, an EGL_BAD_CONTEXT error is
generated.
eglHandleGPUSwitchANGLE should be called in response to a display
reconfiguration callback (registered via
CGDisplayRegisterReconfigurationCallback) in order to complete
transitions between the low-power and high-power GPUs. For calls
to this function:
If |dpy| is not a valid display, an EGL_BAD_DISPLAY error is
generated.
if |dpy| is an uninitialized display, an EGL_NOT_INITIALIZED error
is generated.
Issues
None yet.
......@@ -83,3 +129,6 @@ Revision History
Rev. Date Author Changes
---- ------------- --------- ----------------------------------------
1 Apr 16, 2019 kbr Initial version
2 June 5, 2020 kbr Add eglReleaseHighPowerGPUANGLE,
eglReacquireHighPowerGPUANGLE, and
eglHandleGPUSwitchANGLE
......@@ -254,6 +254,14 @@ EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateANGLE(EGLDisplay dpy,
#define EGL_POWER_PREFERENCE_ANGLE 0x3482
#define EGL_LOW_POWER_ANGLE 0x0001
#define EGL_HIGH_POWER_ANGLE 0x0002
typedef void(EGLAPIENTRYP PFNEGLRELEASEHIGHPOWERGPUANGLEPROC) (EGLDisplay dpy, EGLContext ctx);
typedef void(EGLAPIENTRYP PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC) (EGLDisplay dpy, EGLContext ctx);
typedef void(EGLAPIENTRYP PFNEGLHANDLEGPUSWITCHANGLEPROC) (EGLDisplay dpy);
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI void EGLAPIENTRY eglReleaseHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx);
EGLAPI void EGLAPIENTRY eglReacquireHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx);
EGLAPI void EGLAPIENTRY eglHandleGPUSwitchANGLE(EGLDisplay dpy);
#endif
#endif /* EGL_ANGLE_power_preference */
#ifndef EGL_ANGLE_feature_control
......
......@@ -453,6 +453,11 @@ struct FeaturesGL : FeatureSetBase
"emulate_copyteximage2d_from_renderbuffers", FeatureCategory::OpenGLWorkarounds,
"CopyTexImage2D spuriously returns errors on iOS when copying from renderbuffers.",
&members, "https://anglebug.com/4674"};
Feature disableGPUSwitchingSupport = {
"disable_gpu_switching_support", FeatureCategory::OpenGLWorkarounds,
"Disable GPU switching support (use only the low-power GPU) on older MacBook Pros.",
&members, "https://crbug.com/1091824"};
};
inline FeaturesGL::FeaturesGL() = default;
......
......@@ -2,7 +2,7 @@
"scripts/egl.xml":
"842e24514c4cfe09fba703c17a0fd292",
"scripts/egl_angle_ext.xml":
"854e99785af19f8f4eea4f73005a0451",
"087d6a3996a91fbb0b664cac57c50c4c",
"scripts/generate_loader.py":
"c6c02d859fabc61410470f50198a5a80",
"scripts/gl.xml":
......@@ -10,17 +10,17 @@
"scripts/gl_angle_ext.xml":
"079cc4829de7ce638faf7bbf66e141ad",
"scripts/registry_xml.py":
"7e38cd315a93dce57645283ab2ee80d5",
"f45ffbc8a3c8a7144c2e98e19d1127a0",
"scripts/wgl.xml":
"aa96419c582af2f6673430e2847693f4",
"src/libEGL/egl_loader_autogen.cpp":
"acf3ea19b84b12bfd1197fed020440b3",
"394c31d7e61b285be5bc6a2cb8325a8d",
"src/libEGL/egl_loader_autogen.h":
"5f1507c78b970f6898d1e90da20e425d",
"343b43d7318884204c0699cfaa8ef2dc",
"util/egl_loader_autogen.cpp":
"c2a5f6cb5e3531de16860ffc4f9df6d5",
"6eb720862e78992d0ed3abfe50bfa0ff",
"util/egl_loader_autogen.h":
"3b5804374e184203cd3f89cee12a0e57",
"591c6d6b1ae0a90758204993ca2638a0",
"util/gles_loader_autogen.cpp":
"e4d3034c13ac10c76b854c2749379939",
"util/gles_loader_autogen.h":
......
......@@ -2,7 +2,7 @@
"scripts/egl.xml":
"842e24514c4cfe09fba703c17a0fd292",
"scripts/egl_angle_ext.xml":
"854e99785af19f8f4eea4f73005a0451",
"087d6a3996a91fbb0b664cac57c50c4c",
"scripts/entry_point_packed_gl_enums.json":
"63f508a08611e75810daedb297dca0e9",
"scripts/generate_entry_points.py":
......@@ -12,7 +12,7 @@
"scripts/gl_angle_ext.xml":
"079cc4829de7ce638faf7bbf66e141ad",
"scripts/registry_xml.py":
"7e38cd315a93dce57645283ab2ee80d5",
"f45ffbc8a3c8a7144c2e98e19d1127a0",
"scripts/wgl.xml":
"aa96419c582af2f6673430e2847693f4",
"src/libANGLE/Context_gl_1_0_autogen.h":
......@@ -256,9 +256,9 @@
"src/libGLESv2/libGLESv2_autogen.cpp":
"60649d6c45cb07f341f8f06f4bd7d101",
"src/libGLESv2/libGLESv2_autogen.def":
"2ae49d834d225ee5e117b4a6e81ec9de",
"355b36db208d53a2276fe948fd6ba568",
"src/libGLESv2/libGLESv2_no_capture_autogen.def":
"564f1da48701f02110e4c7949de5e4c2",
"b8724eeed021c3cefc2878bf2dc28c32",
"src/libGLESv2/libGLESv2_with_capture_autogen.def":
"5e67a80bbebfd45c53b2758e9b30e49b"
"c7297239ec1b1b13f0fc81bac5b6600a"
}
\ No newline at end of file
......@@ -6,7 +6,7 @@
"scripts/gl_angle_ext.xml":
"079cc4829de7ce638faf7bbf66e141ad",
"scripts/registry_xml.py":
"7e38cd315a93dce57645283ab2ee80d5",
"f45ffbc8a3c8a7144c2e98e19d1127a0",
"src/libANGLE/gl_enum_utils_autogen.cpp":
"48627c0865d15bf4014327de5858d3c5",
"src/libANGLE/gl_enum_utils_autogen.h":
......
......@@ -2,7 +2,7 @@
"scripts/egl.xml":
"842e24514c4cfe09fba703c17a0fd292",
"scripts/egl_angle_ext.xml":
"854e99785af19f8f4eea4f73005a0451",
"087d6a3996a91fbb0b664cac57c50c4c",
"scripts/gen_proc_table.py":
"05587426d508d13eec257c4e5f3a0a13",
"scripts/gl.xml":
......@@ -10,11 +10,11 @@
"scripts/gl_angle_ext.xml":
"079cc4829de7ce638faf7bbf66e141ad",
"scripts/registry_xml.py":
"7e38cd315a93dce57645283ab2ee80d5",
"f45ffbc8a3c8a7144c2e98e19d1127a0",
"scripts/wgl.xml":
"aa96419c582af2f6673430e2847693f4",
"src/libGL/proc_table_wgl_autogen.cpp":
"253f59ef3aa9ccfcab1364ae5101dbe2",
"src/libGLESv2/proc_table_egl_autogen.cpp":
"d35c6036573a3b9a69438687b4f7136c"
"289be4d639a17429af8f41d53e9c1d0e"
}
\ No newline at end of file
......@@ -94,6 +94,20 @@
<param><ptype>EGLSurface</ptype> <name>surface</name></param>
<param><ptype>EGLFrameTokenANGLE</ptype> <name>frametoken</name></param>
</command>
<command>
<proto><pytpe>void</pytpe> <name>eglReleaseHighPowerGPUANGLE</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
<param><ptype>EGLContext</ptype> <name>ctx</name></param>
</command>
<command>
<proto><pytpe>void</pytpe> <name>eglReacquireHighPowerGPUANGLE</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
<param><ptype>EGLContext</ptype> <name>ctx</name></param>
</command>
<command>
<proto><pytpe>void</pytpe> <name>eglHandleGPUSwitchANGLE</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
</command>
</commands>
<!-- SECTION: ANGLE extension interface definitions -->
<extensions>
......@@ -138,5 +152,12 @@
<command name="eglSwapBuffersWithFrameTokenANGLE"/>
</require>
</extension>
<extension name="EGL_ANGLE_power_preference" supported="egl">
<require>
<command name="eglReleaseHighPowerGPUANGLE"/>
<command name="eglReacquireHighPowerGPUANGLE"/>
<command name="eglHandleGPUSwitchANGLE"/>
</require>
</extension>
</extensions>
</registry>
......@@ -126,6 +126,7 @@ supported_egl_extensions = [
"EGL_ANGLE_device_d3d",
"EGL_ANGLE_feature_control",
"EGL_ANGLE_ggp_stream_descriptor",
"EGL_ANGLE_power_preference",
"EGL_ANGLE_program_cache_control",
"EGL_ANGLE_query_surface_pointer",
"EGL_ANGLE_stream_producer_d3d_texture",
......
......@@ -126,6 +126,15 @@ void GetDualGPUInfo(SystemInfo *info);
void PrintSystemInfo(const SystemInfo &info);
VersionInfo ParseNvidiaDriverVersion(uint32_t version);
#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
// Helper to get the active GPU ID from a given Core Graphics display ID.
uint64_t GetGpuIDFromDisplayID(uint32_t displayID);
// Helper to get the active GPU ID from an OpenGL display mask.
uint64_t GetGpuIDFromOpenGLDisplayMask(uint32_t displayMask);
#endif
} // namespace angle
#endif // GPU_INFO_UTIL_SYSTEM_INFO_H_
......@@ -21,52 +21,9 @@ namespace angle
namespace
{
using PlatformDisplayID = uint32_t;
constexpr CGLRendererProperty kCGLRPRegistryIDLow = static_cast<CGLRendererProperty>(140);
constexpr CGLRendererProperty kCGLRPRegistryIDHigh = static_cast<CGLRendererProperty>(141);
// Code from WebKit to get the active GPU's ID given a display ID.
uint64_t GetGpuIDFromDisplayID(PlatformDisplayID displayID)
{
GLuint displayMask = CGDisplayIDToOpenGLDisplayMask(displayID);
GLint numRenderers = 0;
CGLRendererInfoObj rendererInfo = nullptr;
CGLError error = CGLQueryRendererInfo(displayMask, &rendererInfo, &numRenderers);
if (!numRenderers || !rendererInfo || error != kCGLNoError)
return 0;
// The 0th renderer should not be the software renderer.
GLint isAccelerated;
error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPAccelerated, &isAccelerated);
if (!isAccelerated || error != kCGLNoError)
{
CGLDestroyRendererInfo(rendererInfo);
return 0;
}
GLint gpuIDLow = 0;
GLint gpuIDHigh = 0;
error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow);
if (error != kCGLNoError || gpuIDLow < 0)
{
CGLDestroyRendererInfo(rendererInfo);
return 0;
}
error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh);
if (error != kCGLNoError || gpuIDHigh < 0)
{
CGLDestroyRendererInfo(rendererInfo);
return 0;
}
CGLDestroyRendererInfo(rendererInfo);
return static_cast<uint64_t>(gpuIDHigh) << 32 | gpuIDLow;
}
std::string GetMachineModel()
{
io_service_t platformExpert = IOServiceGetMatchingService(
......@@ -196,6 +153,61 @@ void SetActiveGPUIndex(SystemInfo *info)
} // anonymous namespace
// Code from WebKit to get the active GPU's ID given a Core Graphics display ID.
// https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/mac/PlatformScreenMac.mm
// Used with permission.
uint64_t GetGpuIDFromDisplayID(uint32_t displayID)
{
return GetGpuIDFromOpenGLDisplayMask(CGDisplayIDToOpenGLDisplayMask(displayID));
}
// Code from WebKit to query the GPU ID given an OpenGL display mask.
// https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/mac/PlatformScreenMac.mm
// Used with permission.
uint64_t GetGpuIDFromOpenGLDisplayMask(uint32_t displayMask)
{
if (@available(macOS 10.13, *))
{
GLint numRenderers = 0;
CGLRendererInfoObj rendererInfo = nullptr;
CGLError error = CGLQueryRendererInfo(displayMask, &rendererInfo, &numRenderers);
if (!numRenderers || !rendererInfo || error != kCGLNoError)
return 0;
// The 0th renderer should not be the software renderer.
GLint isAccelerated;
error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPAccelerated, &isAccelerated);
if (!isAccelerated || error != kCGLNoError)
{
CGLDestroyRendererInfo(rendererInfo);
return 0;
}
GLint gpuIDLow = 0;
GLint gpuIDHigh = 0;
error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow);
if (error != kCGLNoError)
{
CGLDestroyRendererInfo(rendererInfo);
return 0;
}
error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh);
if (error != kCGLNoError)
{
CGLDestroyRendererInfo(rendererInfo);
return 0;
}
CGLDestroyRendererInfo(rendererInfo);
return (static_cast<uint64_t>(static_cast<uint32_t>(gpuIDHigh)) << 32) |
static_cast<uint64_t>(static_cast<uint32_t>(gpuIDLow));
}
return 0;
}
bool GetSystemInfo(SystemInfo *info)
{
{
......
......@@ -300,7 +300,7 @@ Context::Context(egl::Display *display,
mExplicitContextAvailable(clientExtensions.explicitContext),
mCurrentDrawSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)),
mCurrentReadSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)),
mDisplay(static_cast<egl::Display *>(EGL_NO_DISPLAY)),
mDisplay(display),
mWebGLContext(GetWebGLContext(attribs)),
mBufferAccessValidationEnabled(false),
mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)),
......@@ -329,6 +329,9 @@ Context::Context(egl::Display *display,
{
mImageObserverBindings.emplace_back(this, imageIndex);
}
// Implementations now require the display to be set at context creation.
ASSERT(mDisplay);
}
void Context::initialize()
......@@ -8456,6 +8459,23 @@ void Context::getRenderbufferImage(GLenum target, GLenum format, GLenum type, vo
format, type, pixels));
}
egl::Error Context::releaseHighPowerGPU()
{
return mImplementation->releaseHighPowerGPU(this);
}
egl::Error Context::reacquireHighPowerGPU()
{
return mImplementation->reacquireHighPowerGPU(this);
}
void Context::onGPUSwitch()
{
// Re-initialize the renderer string, which just changed, and
// which must be visible to applications.
initRendererString();
}
// ErrorSet implementation.
ErrorSet::ErrorSet(Context *context) : mContext(context) {}
......
......@@ -608,6 +608,11 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
Program *getActiveLinkedProgram() const;
// EGL_ANGLE_power_preference implementation.
egl::Error releaseHighPowerGPU();
egl::Error reacquireHighPowerGPU();
void onGPUSwitch();
private:
void initialize();
......
......@@ -87,6 +87,8 @@ namespace egl
namespace
{
constexpr angle::SubjectIndex kGPUSwitchedSubjectIndex = 0;
typedef std::map<EGLNativeWindowType, Surface *> WindowSurfaceMap;
// Get a map of all EGL window surfaces to validate that no window has more than one EGL surface
// associated with it.
......@@ -633,6 +635,7 @@ Display *Display::GetDisplayFromDevice(Device *device, const AttributeMap &attri
Display::Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice)
: mState(displayId),
mImplementation(nullptr),
mGPUSwitchedBinding(this, kGPUSwitchedSubjectIndex),
mAttributeMap(),
mConfigSet(),
mContextSet(),
......@@ -694,6 +697,16 @@ EGLLabelKHR Display::getLabel() const
return mState.label;
}
void Display::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
{
ASSERT(index == kGPUSwitchedSubjectIndex);
ASSERT(message == angle::SubjectMessage::SubjectChanged);
for (ContextSet::iterator ctx = mContextSet.begin(); ctx != mContextSet.end(); ctx++)
{
(*ctx)->onGPUSwitch();
}
}
void Display::setupDisplayPlatform(rx::DisplayImpl *impl)
{
ASSERT(!mInitialized);
......@@ -723,6 +736,7 @@ void Display::setupDisplayPlatform(rx::DisplayImpl *impl)
mState.featureOverridesDisabled = EGLStringArrayToStringVector(featuresForceDisabled);
mState.featuresAllDisabled =
static_cast<bool>(mAttributeMap.get(EGL_FEATURE_ALL_DISABLED_ANGLE, 0));
mImplementation->addObserver(&mGPUSwitchedBinding);
}
void Display::updateAttribsFromEnvironment(const AttributeMap &attribMap)
......@@ -1872,4 +1886,9 @@ void Display::returnScratchBufferImpl(angle::ScratchBuffer scratchBuffer,
bufferVector->push_back(std::move(scratchBuffer));
}
egl::Error Display::handleGPUSwitch()
{
return mImplementation->handleGPUSwitch();
}
} // namespace egl
......@@ -23,6 +23,7 @@
#include "libANGLE/Error.h"
#include "libANGLE/LoggingAnnotator.h"
#include "libANGLE/MemoryProgramCache.h"
#include "libANGLE/Observer.h"
#include "libANGLE/Version.h"
#include "platform/Feature.h"
#include "platform/FrontendFeatures.h"
......@@ -86,7 +87,9 @@ class ShareGroup final : angle::NonCopyable
// Constant coded here as a sanity limit.
constexpr EGLAttrib kProgramCacheSizeAbsoluteMax = 0x4000000;
class Display final : public LabeledObject, angle::NonCopyable
class Display final : public LabeledObject,
public angle::ObserverInterface,
public angle::NonCopyable
{
public:
~Display() override;
......@@ -94,6 +97,9 @@ class Display final : public LabeledObject, angle::NonCopyable
void setLabel(EGLLabelKHR label) override;
EGLLabelKHR getLabel() const override;
// Observer implementation.
void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;
Error initialize();
Error terminate(const Thread *thread);
......@@ -240,6 +246,8 @@ class Display final : public LabeledObject, angle::NonCopyable
angle::ScratchBuffer requestZeroFilledBuffer();
void returnZeroFilledBuffer(angle::ScratchBuffer zeroFilledBuffer);
egl::Error handleGPUSwitch();
private:
Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice);
......@@ -261,6 +269,7 @@ class Display final : public LabeledObject, angle::NonCopyable
DisplayState mState;
rx::DisplayImpl *mImplementation;
angle::ObserverBinding mGPUSwitchedBinding;
AttributeMap mAttributeMap;
......
......@@ -50,4 +50,14 @@ egl::ContextPriority ContextImpl::getContextPriority() const
return egl::ContextPriority::Medium;
}
egl::Error ContextImpl::releaseHighPowerGPU(gl::Context *)
{
return egl::NoError();
}
egl::Error ContextImpl::reacquireHighPowerGPU(gl::Context *)
{
return egl::NoError();
}
} // namespace rx
......@@ -191,6 +191,10 @@ class ContextImpl : public GLImplFactory
virtual egl::ContextPriority getContextPriority() const;
// EGL_ANGLE_power_preference implementation.
virtual egl::Error releaseHighPowerGPU(gl::Context *context);
virtual egl::Error reacquireHighPowerGPU(gl::Context *context);
protected:
const gl::State &mState;
gl::MemoryProgramCache *mMemoryProgramCache;
......
......@@ -34,6 +34,11 @@ const egl::DisplayExtensions &DisplayImpl::getExtensions() const
return mExtensions;
}
egl::Error DisplayImpl::handleGPUSwitch()
{
return egl::NoError();
}
egl::Error DisplayImpl::validateClientBuffer(const egl::Config *configuration,
EGLenum buftype,
EGLClientBuffer clientBuffer,
......
......@@ -13,6 +13,7 @@
#include "libANGLE/Caps.h"
#include "libANGLE/Config.h"
#include "libANGLE/Error.h"
#include "libANGLE/Observer.h"
#include "libANGLE/Stream.h"
#include "libANGLE/Version.h"
#include "libANGLE/renderer/EGLImplFactory.h"
......@@ -58,7 +59,7 @@ class ShareGroupImpl : angle::NonCopyable
virtual ~ShareGroupImpl() {}
};
class DisplayImpl : public EGLImplFactory
class DisplayImpl : public EGLImplFactory, public angle::Subject
{
public:
DisplayImpl(const egl::DisplayState &state);
......@@ -112,6 +113,8 @@ class DisplayImpl : public EGLImplFactory
const egl::DisplayState &getState() const { return mState; }
virtual egl::Error handleGPUSwitch();
protected:
const egl::DisplayState &mState;
......
......@@ -16,26 +16,50 @@
namespace rx
{
ContextCGL::ContextCGL(const gl::State &state,
ContextCGL::ContextCGL(DisplayCGL *display,
const gl::State &state,
gl::ErrorSet *errorSet,
const std::shared_ptr<RendererGL> &renderer,
bool usesDiscreteGPU)
: ContextGL(state, errorSet, renderer), mUsesDiscreteGpu(usesDiscreteGPU)
{}
void ContextCGL::onDestroy(const gl::Context *context)
: ContextGL(state, errorSet, renderer),
mUsesDiscreteGpu(usesDiscreteGPU),
mReleasedDiscreteGpu(false)
{
if (mUsesDiscreteGpu)
{
egl::Display *display = context->getDisplay();
// TODO(kbr): if the context is created and destroyed without ever
// making it current, it is possible to leak retentions of the
// discrete GPU.
if (display)
(void)display->referenceDiscreteGPU();
}
}
egl::Error ContextCGL::releaseHighPowerGPU(gl::Context *context)
{
if (mUsesDiscreteGpu && !mReleasedDiscreteGpu)
{
mReleasedDiscreteGpu = true;
return GetImplAs<DisplayCGL>(context->getDisplay())->unreferenceDiscreteGPU();
}
return egl::NoError();
}
egl::Error ContextCGL::reacquireHighPowerGPU(gl::Context *context)
{
if (mUsesDiscreteGpu && mReleasedDiscreteGpu)
{
GetImplAs<DisplayCGL>(display)->unreferenceDiscreteGPU();
mReleasedDiscreteGpu = false;
return GetImplAs<DisplayCGL>(context->getDisplay())->referenceDiscreteGPU();
}
return egl::NoError();
}
void ContextCGL::onDestroy(const gl::Context *context)
{
if (mUsesDiscreteGpu && !mReleasedDiscreteGpu)
{
(void)GetImplAs<DisplayCGL>(context->getDisplay())->unreferenceDiscreteGPU();
}
ContextGL::onDestroy(context);
}
} // namespace rx
......@@ -15,19 +15,25 @@
namespace rx
{
class DisplayCGL;
class ContextCGL : public ContextGL
{
public:
ContextCGL(const gl::State &state,
ContextCGL(DisplayCGL *display,
const gl::State &state,
gl::ErrorSet *errorSet,
const std::shared_ptr<RendererGL> &renderer,
bool usesDiscreteGPU);
void onDestroy(const gl::Context *context) override;
egl::Error releaseHighPowerGPU(gl::Context *context) override;
egl::Error reacquireHighPowerGPU(gl::Context *context) override;
private:
bool mUsesDiscreteGpu;
bool mReleasedDiscreteGpu;
};
} // namespace rx
......
......@@ -31,6 +31,10 @@ class DisplayCGL : public DisplayGL
egl::Error initialize(egl::Display *display) override;
void terminate() override;
egl::Error makeCurrent(egl::Surface *drawSurface,
egl::Surface *readSurface,
gl::Context *context) override;
SurfaceImpl *createWindowSurface(const egl::SurfaceState &state,
EGLNativeWindowType window,
const egl::AttributeMap &attribs) override;
......@@ -79,10 +83,11 @@ class DisplayCGL : public DisplayGL
void populateFeatureList(angle::FeatureList *features) override;
// Support for dual-GPU MacBook Pros. If the context was created
// preferring the high-power GPU, unreference that GPU during
// context destruction.
void unreferenceDiscreteGPU();
// Support for dual-GPU MacBook Pros. Used only by ContextCGL. The use of
// these entry points is gated by the presence of dual GPUs.
egl::Error referenceDiscreteGPU();
egl::Error unreferenceDiscreteGPU();
egl::Error handleGPUSwitch() override;
private:
egl::Error makeCurrentSurfaceless(gl::Context *context) override;
......@@ -90,14 +95,21 @@ class DisplayCGL : public DisplayGL
void generateExtensions(egl::DisplayExtensions *outExtensions) const override;
void generateCaps(egl::Caps *outCaps) const override;
void checkDiscreteGPUStatus();
std::shared_ptr<RendererGL> mRenderer;
egl::Display *mEGLDisplay;
CGLContextObj mContext;
CGLPixelFormatObj mPixelFormat;
bool mSupportsGPUSwitching;
uint64_t mCurrentGPUID;
CGLPixelFormatObj mDiscreteGPUPixelFormat;
int mDiscreteGPURefs;
// This comes from the ANGLE platform's DefaultMonotonicallyIncreasingTime. If the discrete GPU
// is unref'd for the last time, this is set to the time of that last unref. If it isn't
// activated again in 10 seconds, the discrete GPU pixel format is deleted.
double mLastDiscreteGPUUnrefTime;
};
} // namespace rx
......
......@@ -14,6 +14,7 @@
#include "common/mathutil.h"
#include "common/platform.h"
#include "gpu_info_util/SystemInfo.h"
#include "libANGLE/Buffer.h"
#include "libANGLE/Caps.h"
#include "libANGLE/Context.h"
......@@ -1754,6 +1755,26 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature
ANGLE_FEATURE_CONDITION(
features, emulateCopyTexImage2DFromRenderbuffers,
IsApple() && functions->standard == STANDARD_GL_ES && !(isAMD && IsWindows()));
// Don't attempt to use the discrete GPU on NVIDIA-based MacBook Pros, since the
// driver is unstable in this situation.
//
// Note that this feature is only set here in order to advertise this workaround
// externally. GPU switching support must be enabled or disabled early, during display
// initialization, before these features are set up.
bool isDualGPUMacWithNVIDIA = false;
if (IsApple() && functions->standard == STANDARD_GL_DESKTOP)
{
angle::SystemInfo info;
if (angle::GetSystemInfo(&info))
{
// The full system information must be queried to see whether it's a dual-GPU
// NVIDIA MacBook Pro since it's likely that the integrated GPU will be active
// when these features are initialized.
isDualGPUMacWithNVIDIA = info.isMacSwitchable && info.hasNVIDIAGPU();
}
}
ANGLE_FEATURE_CONDITION(features, disableGPUSwitchingSupport, isDualGPUMacWithNVIDIA);
}
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
......
......@@ -67,6 +67,9 @@ PFNEGLCREATEDEVICEANGLEPROC l_EGL_CreateDeviceANGLE;
PFNEGLRELEASEDEVICEANGLEPROC l_EGL_ReleaseDeviceANGLE;
PFNEGLQUERYDISPLAYATTRIBANGLEPROC l_EGL_QueryDisplayAttribANGLE;
PFNEGLQUERYSTRINGIANGLEPROC l_EGL_QueryStringiANGLE;
PFNEGLHANDLEGPUSWITCHANGLEPROC l_EGL_HandleGPUSwitchANGLE;
PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC l_EGL_ReacquireHighPowerGPUANGLE;
PFNEGLRELEASEHIGHPOWERGPUANGLEPROC l_EGL_ReleaseHighPowerGPUANGLE;
PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC l_EGL_ProgramCacheGetAttribANGLE;
PFNEGLPROGRAMCACHEPOPULATEANGLEPROC l_EGL_ProgramCachePopulateANGLE;
PFNEGLPROGRAMCACHEQUERYANGLEPROC l_EGL_ProgramCacheQueryANGLE;
......@@ -197,6 +200,12 @@ void LoadEGL_EGL(LoadProc loadProc)
loadProc("EGL_QueryDisplayAttribANGLE"));
l_EGL_QueryStringiANGLE =
reinterpret_cast<PFNEGLQUERYSTRINGIANGLEPROC>(loadProc("EGL_QueryStringiANGLE"));
l_EGL_HandleGPUSwitchANGLE =
reinterpret_cast<PFNEGLHANDLEGPUSWITCHANGLEPROC>(loadProc("EGL_HandleGPUSwitchANGLE"));
l_EGL_ReacquireHighPowerGPUANGLE = reinterpret_cast<PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC>(
loadProc("EGL_ReacquireHighPowerGPUANGLE"));
l_EGL_ReleaseHighPowerGPUANGLE = reinterpret_cast<PFNEGLRELEASEHIGHPOWERGPUANGLEPROC>(
loadProc("EGL_ReleaseHighPowerGPUANGLE"));
l_EGL_ProgramCacheGetAttribANGLE = reinterpret_cast<PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC>(
loadProc("EGL_ProgramCacheGetAttribANGLE"));
l_EGL_ProgramCachePopulateANGLE = reinterpret_cast<PFNEGLPROGRAMCACHEPOPULATEANGLEPROC>(
......
......@@ -72,6 +72,9 @@
#define EGL_ReleaseDeviceANGLE l_EGL_ReleaseDeviceANGLE
#define EGL_QueryDisplayAttribANGLE l_EGL_QueryDisplayAttribANGLE
#define EGL_QueryStringiANGLE l_EGL_QueryStringiANGLE
#define EGL_HandleGPUSwitchANGLE l_EGL_HandleGPUSwitchANGLE
#define EGL_ReacquireHighPowerGPUANGLE l_EGL_ReacquireHighPowerGPUANGLE
#define EGL_ReleaseHighPowerGPUANGLE l_EGL_ReleaseHighPowerGPUANGLE
#define EGL_ProgramCacheGetAttribANGLE l_EGL_ProgramCacheGetAttribANGLE
#define EGL_ProgramCachePopulateANGLE l_EGL_ProgramCachePopulateANGLE
#define EGL_ProgramCacheQueryANGLE l_EGL_ProgramCacheQueryANGLE
......@@ -168,6 +171,9 @@ ANGLE_NO_EXPORT extern PFNEGLCREATEDEVICEANGLEPROC l_EGL_CreateDeviceANGLE;
ANGLE_NO_EXPORT extern PFNEGLRELEASEDEVICEANGLEPROC l_EGL_ReleaseDeviceANGLE;
ANGLE_NO_EXPORT extern PFNEGLQUERYDISPLAYATTRIBANGLEPROC l_EGL_QueryDisplayAttribANGLE;
ANGLE_NO_EXPORT extern PFNEGLQUERYSTRINGIANGLEPROC l_EGL_QueryStringiANGLE;
ANGLE_NO_EXPORT extern PFNEGLHANDLEGPUSWITCHANGLEPROC l_EGL_HandleGPUSwitchANGLE;
ANGLE_NO_EXPORT extern PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC l_EGL_ReacquireHighPowerGPUANGLE;
ANGLE_NO_EXPORT extern PFNEGLRELEASEHIGHPOWERGPUANGLEPROC l_EGL_ReleaseHighPowerGPUANGLE;
ANGLE_NO_EXPORT extern PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC l_EGL_ProgramCacheGetAttribANGLE;
ANGLE_NO_EXPORT extern PFNEGLPROGRAMCACHEPOPULATEANGLEPROC l_EGL_ProgramCachePopulateANGLE;
ANGLE_NO_EXPORT extern PFNEGLPROGRAMCACHEQUERYANGLEPROC l_EGL_ProgramCacheQueryANGLE;
......
......@@ -1560,4 +1560,57 @@ EGLBoolean EGLAPIENTRY EGL_SwapBuffersWithFrameTokenANGLE(EGLDisplay dpy,
thread->setSuccess();
return EGL_TRUE;
}
void EGLAPIENTRY EGL_ReleaseHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx)
{
ANGLE_SCOPED_GLOBAL_LOCK();
FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLContext ctx = 0x%016" PRIxPTR,
(uintptr_t)dpy, (uintptr_t)ctx);
Thread *thread = egl::GetCurrentThread();
egl::Display *display = static_cast<egl::Display *>(dpy);
gl::Context *context = static_cast<gl::Context *>(ctx);
ANGLE_EGL_TRY(thread, ValidateContext(display, context), "eglReleaseHighPowerGPUANGLE",
GetDisplayIfValid(display));
ANGLE_EGL_TRY(thread, context->releaseHighPowerGPU(), "eglReleaseHighPowerGPUANGLE",
GetDisplayIfValid(display));
thread->setSuccess();
}
void EGLAPIENTRY EGL_ReacquireHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx)
{
ANGLE_SCOPED_GLOBAL_LOCK();
FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLContext ctx = 0x%016" PRIxPTR,
(uintptr_t)dpy, (uintptr_t)ctx);
Thread *thread = egl::GetCurrentThread();
egl::Display *display = static_cast<egl::Display *>(dpy);
gl::Context *context = static_cast<gl::Context *>(ctx);
ANGLE_EGL_TRY(thread, ValidateContext(display, context), "eglReacquireHighPowerGPUANGLE",
GetDisplayIfValid(display));
ANGLE_EGL_TRY(thread, context->reacquireHighPowerGPU(), "eglReacquireHighPowerGPUANGLE",
GetDisplayIfValid(display));
thread->setSuccess();
}
void EGLAPIENTRY EGL_HandleGPUSwitchANGLE(EGLDisplay dpy)
{
ANGLE_SCOPED_GLOBAL_LOCK();
FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR, (uintptr_t)dpy);
Thread *thread = egl::GetCurrentThread();
egl::Display *display = static_cast<egl::Display *>(dpy);
ANGLE_EGL_TRY(thread, ValidateDisplay(display), "eglHandleGPUSwitchANGLE",
GetDisplayIfValid(display));
ANGLE_EGL_TRY(thread, display->handleGPUSwitch(), "eglHandleGPUSwitchANGLE",
GetDisplayIfValid(display));
thread->setSuccess();
}
} // extern "C"
......@@ -229,6 +229,13 @@ EGL_SwapBuffersWithFrameTokenANGLE(EGLDisplay dpy,
EGLSurface surface,
EGLFrameTokenANGLE frametoken);
// EGL_ANGLE_power_preference
ANGLE_EXPORT void EGLAPIENTRY EGL_ReleaseHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx);
ANGLE_EXPORT void EGLAPIENTRY EGL_ReacquireHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx);
ANGLE_EXPORT void EGLAPIENTRY EGL_HandleGPUSwitchANGLE(EGLDisplay dpy);
} // extern "C"
#endif // LIBGLESV2_ENTRYPOINTSEGLEXT_H_
......@@ -1664,6 +1664,11 @@ EXPORTS
EGL_QueryDisplayAttribANGLE
EGL_QueryStringiANGLE
; EGL_ANGLE_power_preference
EGL_HandleGPUSwitchANGLE
EGL_ReacquireHighPowerGPUANGLE
EGL_ReleaseHighPowerGPUANGLE
; EGL_ANGLE_program_cache_control
EGL_ProgramCacheGetAttribANGLE
EGL_ProgramCachePopulateANGLE
......
......@@ -1664,6 +1664,11 @@ EXPORTS
EGL_QueryDisplayAttribANGLE
EGL_QueryStringiANGLE
; EGL_ANGLE_power_preference
EGL_HandleGPUSwitchANGLE
EGL_ReacquireHighPowerGPUANGLE
EGL_ReleaseHighPowerGPUANGLE
; EGL_ANGLE_program_cache_control
EGL_ProgramCacheGetAttribANGLE
EGL_ProgramCachePopulateANGLE
......
......@@ -1664,6 +1664,11 @@ EXPORTS
EGL_QueryDisplayAttribANGLE
EGL_QueryStringiANGLE
; EGL_ANGLE_power_preference
EGL_HandleGPUSwitchANGLE
EGL_ReacquireHighPowerGPUANGLE
EGL_ReleaseHighPowerGPUANGLE
; EGL_ANGLE_program_cache_control
EGL_ProgramCacheGetAttribANGLE
EGL_ProgramCachePopulateANGLE
......
......@@ -80,6 +80,7 @@ const ProcEntry g_procTable[] = {
{"eglGetSyncAttrib", P(EGL_GetSyncAttrib)},
{"eglGetSyncAttribKHR", P(EGL_GetSyncAttribKHR)},
{"eglGetSyncValuesCHROMIUM", P(EGL_GetSyncValuesCHROMIUM)},
{"eglHandleGPUSwitchANGLE", P(EGL_HandleGPUSwitchANGLE)},
{"eglInitialize", P(EGL_Initialize)},
{"eglLabelObjectKHR", P(EGL_LabelObjectKHR)},
{"eglMakeCurrent", P(EGL_MakeCurrent)},
......@@ -102,7 +103,9 @@ const ProcEntry g_procTable[] = {
{"eglQueryStringiANGLE", P(EGL_QueryStringiANGLE)},
{"eglQuerySurface", P(EGL_QuerySurface)},
{"eglQuerySurfacePointerANGLE", P(EGL_QuerySurfacePointerANGLE)},
{"eglReacquireHighPowerGPUANGLE", P(EGL_ReacquireHighPowerGPUANGLE)},
{"eglReleaseDeviceANGLE", P(EGL_ReleaseDeviceANGLE)},
{"eglReleaseHighPowerGPUANGLE", P(EGL_ReleaseHighPowerGPUANGLE)},
{"eglReleaseTexImage", P(EGL_ReleaseTexImage)},
{"eglReleaseThread", P(EGL_ReleaseThread)},
{"eglSetBlobCacheFuncsANDROID", P(EGL_SetBlobCacheFuncsANDROID)},
......@@ -1518,5 +1521,5 @@ const ProcEntry g_procTable[] = {
{"glWeightPointerOES", P(gl::WeightPointerOES)},
{"glWeightPointerOESContextANGLE", P(gl::WeightPointerOESContextANGLE)}};
const size_t g_numProcs = 1424;
const size_t g_numProcs = 1427;
} // namespace egl
......@@ -69,6 +69,9 @@ ANGLE_UTIL_EXPORT PFNEGLCREATEDEVICEANGLEPROC l_eglCreateDeviceANGLE;
ANGLE_UTIL_EXPORT PFNEGLRELEASEDEVICEANGLEPROC l_eglReleaseDeviceANGLE;
ANGLE_UTIL_EXPORT PFNEGLQUERYDISPLAYATTRIBANGLEPROC l_eglQueryDisplayAttribANGLE;
ANGLE_UTIL_EXPORT PFNEGLQUERYSTRINGIANGLEPROC l_eglQueryStringiANGLE;
ANGLE_UTIL_EXPORT PFNEGLHANDLEGPUSWITCHANGLEPROC l_eglHandleGPUSwitchANGLE;
ANGLE_UTIL_EXPORT PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC l_eglReacquireHighPowerGPUANGLE;
ANGLE_UTIL_EXPORT PFNEGLRELEASEHIGHPOWERGPUANGLEPROC l_eglReleaseHighPowerGPUANGLE;
ANGLE_UTIL_EXPORT PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC l_eglProgramCacheGetAttribANGLE;
ANGLE_UTIL_EXPORT PFNEGLPROGRAMCACHEPOPULATEANGLEPROC l_eglProgramCachePopulateANGLE;
ANGLE_UTIL_EXPORT PFNEGLPROGRAMCACHEQUERYANGLEPROC l_eglProgramCacheQueryANGLE;
......@@ -198,6 +201,12 @@ void LoadEGL(LoadProc loadProc)
reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBANGLEPROC>(loadProc("eglQueryDisplayAttribANGLE"));
l_eglQueryStringiANGLE =
reinterpret_cast<PFNEGLQUERYSTRINGIANGLEPROC>(loadProc("eglQueryStringiANGLE"));
l_eglHandleGPUSwitchANGLE =
reinterpret_cast<PFNEGLHANDLEGPUSWITCHANGLEPROC>(loadProc("eglHandleGPUSwitchANGLE"));
l_eglReacquireHighPowerGPUANGLE = reinterpret_cast<PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC>(
loadProc("eglReacquireHighPowerGPUANGLE"));
l_eglReleaseHighPowerGPUANGLE = reinterpret_cast<PFNEGLRELEASEHIGHPOWERGPUANGLEPROC>(
loadProc("eglReleaseHighPowerGPUANGLE"));
l_eglProgramCacheGetAttribANGLE = reinterpret_cast<PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC>(
loadProc("eglProgramCacheGetAttribANGLE"));
l_eglProgramCachePopulateANGLE = reinterpret_cast<PFNEGLPROGRAMCACHEPOPULATEANGLEPROC>(
......
......@@ -73,6 +73,9 @@
#define eglReleaseDeviceANGLE l_eglReleaseDeviceANGLE
#define eglQueryDisplayAttribANGLE l_eglQueryDisplayAttribANGLE
#define eglQueryStringiANGLE l_eglQueryStringiANGLE
#define eglHandleGPUSwitchANGLE l_eglHandleGPUSwitchANGLE
#define eglReacquireHighPowerGPUANGLE l_eglReacquireHighPowerGPUANGLE
#define eglReleaseHighPowerGPUANGLE l_eglReleaseHighPowerGPUANGLE
#define eglProgramCacheGetAttribANGLE l_eglProgramCacheGetAttribANGLE
#define eglProgramCachePopulateANGLE l_eglProgramCachePopulateANGLE
#define eglProgramCacheQueryANGLE l_eglProgramCacheQueryANGLE
......@@ -169,6 +172,9 @@ ANGLE_UTIL_EXPORT extern PFNEGLCREATEDEVICEANGLEPROC l_eglCreateDeviceANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLRELEASEDEVICEANGLEPROC l_eglReleaseDeviceANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLQUERYDISPLAYATTRIBANGLEPROC l_eglQueryDisplayAttribANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLQUERYSTRINGIANGLEPROC l_eglQueryStringiANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLHANDLEGPUSWITCHANGLEPROC l_eglHandleGPUSwitchANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLREACQUIREHIGHPOWERGPUANGLEPROC l_eglReacquireHighPowerGPUANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLRELEASEHIGHPOWERGPUANGLEPROC l_eglReleaseHighPowerGPUANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC l_eglProgramCacheGetAttribANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLPROGRAMCACHEPOPULATEANGLEPROC l_eglProgramCachePopulateANGLE;
ANGLE_UTIL_EXPORT extern PFNEGLPROGRAMCACHEQUERYANGLEPROC l_eglProgramCacheQueryANGLE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment