Reland "Define and expose EGL_ANGLE_power_preference extension."

This is a reland of ac58e63295

The original CL was reverted in a rush because I thought there was an
uninitialized variable bug, but upon later re-review this turned out to
not be the case.

Original change's description:
> Define and expose EGL_ANGLE_power_preference extension.
>
> Allows application to select the integrated or discrete GPU on
> dual-GPU macOS systems.
>
> Tested by modifying the example program at:
> https://github.com/grorg/ANGLEIOSurfaceTest
>
> and verifying that both integrated and discrete GPUs can be selected.
> (The changes to that program will be upstreamed once some build issues
> are resolved.)
>
> Bug: 2813
> Change-Id: Ibeb17778512896456d220e9bc4cf8f222aa57057
> Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1570081
> Commit-Queue: Kenneth Russell <kbr@chromium.org>
> Reviewed-by: Geoff Lang <geofflang@chromium.org>

Bug: 2813
Tbr: geofflang@chromium.org
Tbr: cwallez@chromium.org
Change-Id: Iea000dd718f4f4b4f57237adb1dc44381b10106b
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1575419
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Kenneth Russell <kbr@chromium.org>
This commit is contained in:
Kenneth Russell 2019-04-11 21:55:20 -07:00 коммит произвёл Commit Bot
Родитель 145ec7fa7c
Коммит 51386f4a5e
13 изменённых файлов: 278 добавлений и 10 удалений

Просмотреть файл

@ -0,0 +1,85 @@
Name
ANGLE_power_preference
Name Strings
EGL_ANGLE_power_preference
Contributors
Kenneth Russell
Contacts
Kenneth Russell, Google Inc. (kbr 'at' google.com)
Status
Draft
Version
Version 1, April 16, 2019
Number
EGL Extension #??
Dependencies
This extension is written against the wording of the EGL 1.4
Specification.
Overview
This extension allows selection of the high- or low-power GPU on
dual-GPU systems, specifically on macOS.
New Types
None
New Procedures and Functions
None
New Tokens
Accepted as an attribute name in the <*attrib_list> argument to
eglCreateContext:
EGL_POWER_PREFERENCE_ANGLE 0x3482
Accepted as an attribute value in the <*attrib_list> argument to
eglCreateContext:
EGL_LOW_POWER_ANGLE 0x0001
EGL_HIGH_POWER_ANGLE 0x0002
Additions to the EGL 1.4 Specification
Add the following to section 3.7.1 "Creating Rendering Contexts":
EGL_POWER_PREFERENCE_ANGLE indicates whether the context should be
created on the integrated (low-power) or discrete (high-power) GPU
on dual-GPU systems. EGL_POWER_PREFERENCE_ANGLE is only a legal
context creation attribute when the EGL_ANGLE_power_preference
extension is advertised. The valid values for this attribute are
EGL_LOW_POWER_ANGLE and EGL_HIGH_POWER_ANGLE. If this extension is
advertised and this context creation attribute is not specified,
the default value is EGL_LOW_POWER_ANGLE.
The containing application must set the
NSSupportsAutomaticGraphicsSwitching attribute to true in its
Info.plist in order for this extension to operate as advertised.
Issues
None yet.
Revision History
Rev. Date Author Changes
---- ------------- --------- ----------------------------------------
1 Apr 16, 2019 kbr Initial version

Просмотреть файл

@ -202,6 +202,13 @@ EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncValuesCHROMIUM(EGLDisplay dpy,
#endif
#endif /* EGL_CHROMIUM_get_sync_values */
#ifndef EGL_ANGLE_power_preference
#define EGL_ANGLE_power_preference 1
#define EGL_POWER_PREFERENCE_ANGLE 0x3482
#define EGL_LOW_POWER_ANGLE 0x0001
#define EGL_HIGH_POWER_ANGLE 0x0002
#endif /* EGL_ANGLE_power_preference */
// clang-format on
#endif // INCLUDE_EGL_EGLEXT_ANGLE_

Просмотреть файл

@ -67,6 +67,8 @@ struct SystemInfo
bool isOptimus = false;
bool isAMDSwitchable = false;
// Only true on dual-GPU Mac laptops.
bool isMacSwitchable = false;
// Only available on Android
std::string machineManufacturer;

Просмотреть файл

@ -164,6 +164,19 @@ bool GetSystemInfo(SystemInfo *info)
FindPrimaryGPU(info);
// Figure out whether this is a dual-GPU system.
//
// TODO(kbr): this code was ported over from Chromium, and its correctness
// could be improved - need to use Mac-specific APIs to determine whether
// offline renderers are allowed, and whether these two GPUs are really the
// integrated/discrete GPUs in a laptop.
if (info->gpus.size() == 2 &&
((IsIntel(info->gpus[0].vendorId) && !IsIntel(info->gpus[1].vendorId)) ||
(!IsIntel(info->gpus[0].vendorId) && IsIntel(info->gpus[1].vendorId))))
{
info->isMacSwitchable = true;
}
return true;
}

Просмотреть файл

@ -37,7 +37,7 @@ EGLAttrib AttributeMap::get(EGLAttrib key) const
EGLAttrib AttributeMap::get(EGLAttrib key, EGLAttrib defaultValue) const
{
auto iter = mAttributes.find(key);
return (mAttributes.find(key) != mAttributes.end()) ? iter->second : defaultValue;
return (iter != mAttributes.end()) ? iter->second : defaultValue;
}
EGLint AttributeMap::getAsInt(EGLAttrib key) const

Просмотреть файл

@ -1391,7 +1391,8 @@ DisplayExtensions::DisplayExtensions()
blobCache(false),
imageNativeBuffer(false),
getFrameTimestamps(false),
recordable(false)
recordable(false),
powerPreference(false)
{}
std::vector<std::string> DisplayExtensions::getStrings() const
@ -1444,7 +1445,8 @@ std::vector<std::string> DisplayExtensions::getStrings() const
InsertExtensionString("EGL_ANDROID_blob_cache", blobCache, &extensionStrings);
InsertExtensionString("EGL_ANDROID_image_native_buffer", imageNativeBuffer, &extensionStrings);
InsertExtensionString("EGL_ANDROID_get_frame_timestamps", getFrameTimestamps, &extensionStrings);
InsertExtensionString("EGL_ANDROID_recordable", recordable, &extensionStrings);
InsertExtensionString("EGL_ANDROID_recordable", recordable, &extensionStrings);
InsertExtensionString("EGL_ANGLE_power_preference", powerPreference, &extensionStrings);
// TODO(jmadill): Enable this when complete.
//InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings);
// clang-format on

Просмотреть файл

@ -870,6 +870,9 @@ struct DisplayExtensions
// EGL_ANDROID_recordable
bool recordable;
// EGL_ANGLE_power_preference
bool powerPreference;
};
struct DeviceExtensions

Просмотреть файл

@ -0,0 +1,41 @@
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ContextCGL:
// Mac-specific subclass of ContextGL.
//
#include "libANGLE/renderer/gl/cgl/ContextCGL.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/renderer/gl/cgl/DisplayCGL.h"
namespace rx
{
ContextCGL::ContextCGL(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)
{
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)
{
GetImplAs<DisplayCGL>(display)->unreferenceDiscreteGPU();
}
}
}
} // namespace rx

Просмотреть файл

@ -0,0 +1,35 @@
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ContextCGL:
// Mac-specific subclass of ContextGL.
//
#ifndef LIBANGLE_RENDERER_GL_CGL_CONTEXTCGL_H_
#define LIBANGLE_RENDERER_GL_CGL_CONTEXTCGL_H_
#include "libANGLE/renderer/gl/ContextGL.h"
#include "libANGLE/renderer/gl/RendererGL.h"
namespace rx
{
class ContextCGL : public ContextGL
{
public:
ContextCGL(const gl::State &state,
gl::ErrorSet *errorSet,
const std::shared_ptr<RendererGL> &renderer,
bool usesDiscreteGPU);
void onDestroy(const gl::Context *context) override;
private:
bool mUsesDiscreteGpu;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_GL_CGL_CONTEXTCGL_H_

Просмотреть файл

@ -74,6 +74,11 @@ class DisplayCGL : public DisplayGL
WorkerContext *createWorkerContext(std::string *infoLog);
// Support for dual-GPU MacBook Pros. If the context was created
// preferring the high-power GPU, unreference that GPU during
// context destruction.
void unreferenceDiscreteGPU();
private:
egl::Error makeCurrentSurfaceless(gl::Context *context) override;
@ -85,6 +90,9 @@ class DisplayCGL : public DisplayGL
egl::Display *mEGLDisplay;
CGLContextObj mContext;
CGLPixelFormatObj mPixelFormat;
bool mSupportsGPUSwitching;
CGLPixelFormatObj mDiscreteGPUPixelFormat;
int mDiscreteGPURefs;
};
} // namespace rx

Просмотреть файл

@ -13,8 +13,9 @@
#include <dlfcn.h>
#include "common/debug.h"
#include "gpu_info_util/SystemInfo.h"
#include "libANGLE/Display.h"
#include "libANGLE/renderer/gl/ContextGL.h"
#include "libANGLE/renderer/gl/cgl/ContextCGL.h"
#include "libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.h"
#include "libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h"
#include "libANGLE/renderer/gl/cgl/RendererCGL.h"
@ -49,7 +50,13 @@ class FunctionsGLCGL : public FunctionsGL
};
DisplayCGL::DisplayCGL(const egl::DisplayState &state)
: DisplayGL(state), mEGLDisplay(nullptr), mContext(nullptr), mPixelFormat(nullptr)
: DisplayGL(state),
mEGLDisplay(nullptr),
mContext(nullptr),
mPixelFormat(nullptr),
mSupportsGPUSwitching(false),
mDiscreteGPUPixelFormat(nullptr),
mDiscreteGPURefs(0)
{}
DisplayCGL::~DisplayCGL() {}
@ -58,13 +65,25 @@ egl::Error DisplayCGL::initialize(egl::Display *display)
{
mEGLDisplay = display;
angle::SystemInfo info;
if (!angle::GetSystemInfo(&info))
{
return egl::EglNotInitialized() << "Unable to query ANGLE's SystemInfo.";
}
mSupportsGPUSwitching = info.isMacSwitchable;
{
// TODO(cwallez) investigate which pixel format we want
CGLPixelFormatAttribute attribs[] = {
kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core),
static_cast<CGLPixelFormatAttribute>(0)};
std::vector<CGLPixelFormatAttribute> attribs;
attribs.push_back(kCGLPFAOpenGLProfile);
attribs.push_back(static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core));
if (mSupportsGPUSwitching)
{
attribs.push_back(kCGLPFAAllowOfflineRenderers);
}
attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
GLint nVirtualScreens = 0;
CGLChoosePixelFormat(attribs, &mPixelFormat, &nVirtualScreens);
CGLChoosePixelFormat(attribs.data(), &mPixelFormat, &nVirtualScreens);
if (mPixelFormat == nullptr)
{
@ -161,7 +180,29 @@ ContextImpl *DisplayCGL::createContext(const gl::State &state,
const gl::Context *shareContext,
const egl::AttributeMap &attribs)
{
return new ContextGL(state, errorSet, mRenderer);
bool usesDiscreteGPU = false;
if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, EGL_LOW_POWER_ANGLE) == EGL_HIGH_POWER_ANGLE)
{
// Should have been rejected by validation if not supported.
ASSERT(mSupportsGPUSwitching);
// Create discrete pixel format if necessary.
if (!mDiscreteGPUPixelFormat)
{
CGLPixelFormatAttribute discreteAttribs[] = {static_cast<CGLPixelFormatAttribute>(0)};
GLint numPixelFormats = 0;
if (CGLChoosePixelFormat(discreteAttribs, &mDiscreteGPUPixelFormat, &numPixelFormats) !=
kCGLNoError)
{
ERR() << "Error choosing discrete pixel format.";
return nullptr;
}
}
++mDiscreteGPURefs;
usesDiscreteGPU = true;
}
return new ContextCGL(state, errorSet, mRenderer, usesDiscreteGPU);
}
DeviceImpl *DisplayCGL::createDevice()
@ -288,6 +329,11 @@ void DisplayCGL::generateExtensions(egl::DisplayExtensions *outExtensions) const
// Contexts are virtualized so textures can be shared globally
outExtensions->displayTextureShareGroup = true;
if (mSupportsGPUSwitching)
{
outExtensions->powerPreference = true;
}
DisplayGL::generateExtensions(outExtensions);
}
@ -370,4 +416,14 @@ WorkerContext *DisplayCGL::createWorkerContext(std::string *infoLog)
return new WorkerContextCGL(context);
}
void DisplayCGL::unreferenceDiscreteGPU()
{
ASSERT(mDiscreteGPURefs > 0);
if (--mDiscreteGPURefs == 0)
{
CGLDestroyPixelFormat(mDiscreteGPUPixelFormat);
mDiscreteGPUPixelFormat = nullptr;
}
}
}

Просмотреть файл

@ -1127,6 +1127,20 @@ Error ValidateCreateContext(Display *display,
}
break;
case EGL_POWER_PREFERENCE_ANGLE:
if (!display->getExtensions().powerPreference)
{
return EglBadAttribute() << "Attribute EGL_POWER_PREFERENCE_ANGLE "
"requires EGL_ANGLE_power_preference.";
}
if (value != EGL_LOW_POWER_ANGLE && value != EGL_HIGH_POWER_ANGLE)
{
return EglBadAttribute()
<< "EGL_POWER_PREFERENCE_ANGLE must be "
"either EGL_LOW_POWER_ANGLE or EGL_HIGH_POWER_ANGLE.";
}
break;
default:
return EglBadAttribute() << "Unknown attribute.";
}

Просмотреть файл

@ -747,6 +747,8 @@ libangle_gl_egl_android_sources = [
]
libangle_gl_cgl_sources = [
"src/libANGLE/renderer/gl/cgl/ContextCGL.cpp",
"src/libANGLE/renderer/gl/cgl/ContextCGL.h",
"src/libANGLE/renderer/gl/cgl/DisplayCGL.mm",
"src/libANGLE/renderer/gl/cgl/DisplayCGL.h",
"src/libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.mm",