1// 2// Copyright 2015 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6 7// DisplayCGL.mm: CGL implementation of egl::Display 8 9#include "common/platform.h" 10 11#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) 12 13# include "libANGLE/renderer/gl/cgl/DisplayCGL.h" 14 15# import <Cocoa/Cocoa.h> 16# include <EGL/eglext.h> 17# include <dlfcn.h> 18 19# include "common/debug.h" 20# include "gpu_info_util/SystemInfo.h" 21# include "libANGLE/Display.h" 22# include "libANGLE/renderer/gl/cgl/ContextCGL.h" 23# include "libANGLE/renderer/gl/cgl/DeviceCGL.h" 24# include "libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.h" 25# include "libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h" 26# include "libANGLE/renderer/gl/cgl/RendererCGL.h" 27# include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h" 28 29namespace 30{ 31 32const char *kDefaultOpenGLDylibName = 33 "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"; 34const char *kFallbackOpenGLDylibName = "GL"; 35 36} 37 38namespace rx 39{ 40 41class FunctionsGLCGL : public FunctionsGL 42{ 43 public: 44 FunctionsGLCGL(void *dylibHandle) : mDylibHandle(dylibHandle) {} 45 46 ~FunctionsGLCGL() override { dlclose(mDylibHandle); } 47 48 private: 49 void *loadProcAddress(const std::string &function) const override 50 { 51 return dlsym(mDylibHandle, function.c_str()); 52 } 53 54 void *mDylibHandle; 55}; 56 57DisplayCGL::DisplayCGL(const egl::DisplayState &state) 58 : DisplayGL(state), 59 mEGLDisplay(nullptr), 60 mContext(nullptr), 61 mPixelFormat(nullptr), 62 mSupportsGPUSwitching(false), 63 mDiscreteGPUPixelFormat(nullptr), 64 mDiscreteGPURefs(0) 65{} 66 67DisplayCGL::~DisplayCGL() {} 68 69egl::Error DisplayCGL::initialize(egl::Display *display) 70{ 71 mEGLDisplay = display; 72 73 angle::SystemInfo info; 74 if (!angle::GetSystemInfo(&info)) 75 { 76 return egl::EglNotInitialized() << "Unable to query ANGLE's SystemInfo."; 77 } 78 mSupportsGPUSwitching = info.isMacSwitchable; 79 80 { 81 // TODO(cwallez) investigate which pixel format we want 82 std::vector<CGLPixelFormatAttribute> attribs; 83 attribs.push_back(kCGLPFAOpenGLProfile); 84 attribs.push_back(static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core)); 85 attribs.push_back(kCGLPFAAllowOfflineRenderers); 86 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); 87 GLint nVirtualScreens = 0; 88 CGLChoosePixelFormat(attribs.data(), &mPixelFormat, &nVirtualScreens); 89 90 if (mPixelFormat == nullptr) 91 { 92 return egl::EglNotInitialized() << "Could not create the context's pixel format."; 93 } 94 } 95 96 CGLCreateContext(mPixelFormat, nullptr, &mContext); 97 if (mContext == nullptr) 98 { 99 return egl::EglNotInitialized() << "Could not create the CGL context."; 100 } 101 CGLSetCurrentContext(mContext); 102 103 // There is no equivalent getProcAddress in CGL so we open the dylib directly 104 void *handle = dlopen(kDefaultOpenGLDylibName, RTLD_NOW); 105 if (!handle) 106 { 107 handle = dlopen(kFallbackOpenGLDylibName, RTLD_NOW); 108 } 109 if (!handle) 110 { 111 return egl::EglNotInitialized() << "Could not open the OpenGL Framework."; 112 } 113 114 std::unique_ptr<FunctionsGL> functionsGL(new FunctionsGLCGL(handle)); 115 functionsGL->initialize(display->getAttributeMap()); 116 117 mRenderer.reset(new RendererCGL(std::move(functionsGL), display->getAttributeMap(), this)); 118 119 const gl::Version &maxVersion = mRenderer->getMaxSupportedESVersion(); 120 if (maxVersion < gl::Version(2, 0)) 121 { 122 return egl::EglNotInitialized() << "OpenGL ES 2.0 is not supportable."; 123 } 124 125 return DisplayGL::initialize(display); 126} 127 128void DisplayCGL::terminate() 129{ 130 DisplayGL::terminate(); 131 132 mRenderer.reset(); 133 if (mPixelFormat != nullptr) 134 { 135 CGLDestroyPixelFormat(mPixelFormat); 136 mPixelFormat = nullptr; 137 } 138 if (mContext != nullptr) 139 { 140 CGLSetCurrentContext(nullptr); 141 CGLReleaseContext(mContext); 142 mContext = nullptr; 143 } 144} 145 146SurfaceImpl *DisplayCGL::createWindowSurface(const egl::SurfaceState &state, 147 EGLNativeWindowType window, 148 const egl::AttributeMap &attribs) 149{ 150 return new WindowSurfaceCGL(state, mRenderer.get(), window, mContext); 151} 152 153SurfaceImpl *DisplayCGL::createPbufferSurface(const egl::SurfaceState &state, 154 const egl::AttributeMap &attribs) 155{ 156 EGLint width = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0)); 157 EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0)); 158 return new PbufferSurfaceCGL(state, mRenderer.get(), width, height); 159} 160 161SurfaceImpl *DisplayCGL::createPbufferFromClientBuffer(const egl::SurfaceState &state, 162 EGLenum buftype, 163 EGLClientBuffer clientBuffer, 164 const egl::AttributeMap &attribs) 165{ 166 ASSERT(buftype == EGL_IOSURFACE_ANGLE); 167 168 return new IOSurfaceSurfaceCGL(state, mContext, clientBuffer, attribs); 169} 170 171SurfaceImpl *DisplayCGL::createPixmapSurface(const egl::SurfaceState &state, 172 NativePixmapType nativePixmap, 173 const egl::AttributeMap &attribs) 174{ 175 UNIMPLEMENTED(); 176 return nullptr; 177} 178 179ContextImpl *DisplayCGL::createContext(const gl::State &state, 180 gl::ErrorSet *errorSet, 181 const egl::Config *configuration, 182 const gl::Context *shareContext, 183 const egl::AttributeMap &attribs) 184{ 185 bool usesDiscreteGPU = false; 186 187 if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, EGL_LOW_POWER_ANGLE) == EGL_HIGH_POWER_ANGLE) 188 { 189 // Should have been rejected by validation if not supported. 190 ASSERT(mSupportsGPUSwitching); 191 // Create discrete pixel format if necessary. 192 if (!mDiscreteGPUPixelFormat) 193 { 194 CGLPixelFormatAttribute discreteAttribs[] = {static_cast<CGLPixelFormatAttribute>(0)}; 195 GLint numPixelFormats = 0; 196 if (CGLChoosePixelFormat(discreteAttribs, &mDiscreteGPUPixelFormat, &numPixelFormats) != 197 kCGLNoError) 198 { 199 ERR() << "Error choosing discrete pixel format."; 200 return nullptr; 201 } 202 } 203 ++mDiscreteGPURefs; 204 usesDiscreteGPU = true; 205 } 206 207 return new ContextCGL(state, errorSet, mRenderer, usesDiscreteGPU); 208} 209 210DeviceImpl *DisplayCGL::createDevice() 211{ 212 return new DeviceCGL(); 213} 214 215egl::ConfigSet DisplayCGL::generateConfigs() 216{ 217 // TODO(cwallez): generate more config permutations 218 egl::ConfigSet configs; 219 220 const gl::Version &maxVersion = getMaxSupportedESVersion(); 221 ASSERT(maxVersion >= gl::Version(2, 0)); 222 bool supportsES3 = maxVersion >= gl::Version(3, 0); 223 224 egl::Config config; 225 226 // Native stuff 227 config.nativeVisualID = 0; 228 config.nativeVisualType = 0; 229 config.nativeRenderable = EGL_TRUE; 230 231 // Buffer sizes 232 config.redSize = 8; 233 config.greenSize = 8; 234 config.blueSize = 8; 235 config.alphaSize = 8; 236 config.depthSize = 24; 237 config.stencilSize = 8; 238 239 config.colorBufferType = EGL_RGB_BUFFER; 240 config.luminanceSize = 0; 241 config.alphaMaskSize = 0; 242 243 config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize; 244 245 config.transparentType = EGL_NONE; 246 247 // Pbuffer 248 config.maxPBufferWidth = 4096; 249 config.maxPBufferHeight = 4096; 250 config.maxPBufferPixels = 4096 * 4096; 251 252 // Caveat 253 config.configCaveat = EGL_NONE; 254 255 // Misc 256 config.sampleBuffers = 0; 257 config.samples = 0; 258 config.level = 0; 259 config.bindToTextureRGB = EGL_FALSE; 260 config.bindToTextureRGBA = EGL_FALSE; 261 262 config.bindToTextureTarget = EGL_TEXTURE_RECTANGLE_ANGLE; 263 264 config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; 265 266 config.minSwapInterval = 1; 267 config.maxSwapInterval = 1; 268 269 config.renderTargetFormat = GL_RGBA8; 270 config.depthStencilFormat = GL_DEPTH24_STENCIL8; 271 272 config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); 273 config.renderableType = config.conformant; 274 275 config.matchNativePixmap = EGL_NONE; 276 277 config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; 278 279 configs.add(config); 280 return configs; 281} 282 283bool DisplayCGL::testDeviceLost() 284{ 285 // TODO(cwallez) investigate implementing this 286 return false; 287} 288 289egl::Error DisplayCGL::restoreLostDevice(const egl::Display *display) 290{ 291 UNIMPLEMENTED(); 292 return egl::EglBadDisplay(); 293} 294 295bool DisplayCGL::isValidNativeWindow(EGLNativeWindowType window) const 296{ 297 NSObject *layer = (__bridge NSObject *)window; 298 return [layer isKindOfClass:[CALayer class]]; 299} 300 301egl::Error DisplayCGL::validateClientBuffer(const egl::Config *configuration, 302 EGLenum buftype, 303 EGLClientBuffer clientBuffer, 304 const egl::AttributeMap &attribs) const 305{ 306 ASSERT(buftype == EGL_IOSURFACE_ANGLE); 307 308 if (!IOSurfaceSurfaceCGL::validateAttributes(clientBuffer, attribs)) 309 { 310 return egl::EglBadAttribute(); 311 } 312 313 return egl::NoError(); 314} 315 316std::string DisplayCGL::getVendorString() const 317{ 318 // TODO(cwallez) find a useful vendor string 319 return ""; 320} 321 322CGLContextObj DisplayCGL::getCGLContext() const 323{ 324 return mContext; 325} 326 327CGLPixelFormatObj DisplayCGL::getCGLPixelFormat() const 328{ 329 return mPixelFormat; 330} 331 332void DisplayCGL::generateExtensions(egl::DisplayExtensions *outExtensions) const 333{ 334 outExtensions->iosurfaceClientBuffer = true; 335 outExtensions->surfacelessContext = true; 336 outExtensions->deviceQuery = true; 337 338 // Contexts are virtualized so textures can be shared globally 339 outExtensions->displayTextureShareGroup = true; 340 341 if (mSupportsGPUSwitching) 342 { 343 outExtensions->powerPreference = true; 344 } 345 346 DisplayGL::generateExtensions(outExtensions); 347} 348 349void DisplayCGL::generateCaps(egl::Caps *outCaps) const 350{ 351 outCaps->textureNPOT = true; 352} 353 354egl::Error DisplayCGL::waitClient(const gl::Context *context) 355{ 356 // TODO(cwallez) UNIMPLEMENTED() 357 return egl::NoError(); 358} 359 360egl::Error DisplayCGL::waitNative(const gl::Context *context, EGLint engine) 361{ 362 // TODO(cwallez) UNIMPLEMENTED() 363 return egl::NoError(); 364} 365 366gl::Version DisplayCGL::getMaxSupportedESVersion() const 367{ 368 return mRenderer->getMaxSupportedESVersion(); 369} 370 371egl::Error DisplayCGL::makeCurrentSurfaceless(gl::Context *context) 372{ 373 // We have nothing to do as mContext is always current, and that CGL is surfaceless by 374 // default. 375 return egl::NoError(); 376} 377 378class WorkerContextCGL final : public WorkerContext 379{ 380 public: 381 WorkerContextCGL(CGLContextObj context); 382 ~WorkerContextCGL() override; 383 384 bool makeCurrent() override; 385 void unmakeCurrent() override; 386 387 private: 388 CGLContextObj mContext; 389}; 390 391WorkerContextCGL::WorkerContextCGL(CGLContextObj context) : mContext(context) {} 392 393WorkerContextCGL::~WorkerContextCGL() 394{ 395 CGLSetCurrentContext(nullptr); 396 CGLReleaseContext(mContext); 397 mContext = nullptr; 398} 399 400bool WorkerContextCGL::makeCurrent() 401{ 402 CGLError error = CGLSetCurrentContext(mContext); 403 if (error != kCGLNoError) 404 { 405 ERR() << "Unable to make gl context current."; 406 return false; 407 } 408 return true; 409} 410 411void WorkerContextCGL::unmakeCurrent() 412{ 413 CGLSetCurrentContext(nullptr); 414} 415 416WorkerContext *DisplayCGL::createWorkerContext(std::string *infoLog) 417{ 418 CGLContextObj context = nullptr; 419 CGLCreateContext(mPixelFormat, mContext, &context); 420 if (context == nullptr) 421 { 422 *infoLog += "Could not create the CGL context."; 423 return nullptr; 424 } 425 426 return new WorkerContextCGL(context); 427} 428 429void DisplayCGL::unreferenceDiscreteGPU() 430{ 431 ASSERT(mDiscreteGPURefs > 0); 432 if (--mDiscreteGPURefs == 0) 433 { 434 CGLDestroyPixelFormat(mDiscreteGPUPixelFormat); 435 mDiscreteGPUPixelFormat = nullptr; 436 } 437} 438 439void DisplayCGL::initializeFrontendFeatures(angle::FrontendFeatures *features) const 440{ 441 mRenderer->initializeFrontendFeatures(features); 442} 443 444void DisplayCGL::populateFeatureList(angle::FeatureList *features) 445{ 446 mRenderer->getFeatures().populateFeatureList(features); 447} 448} 449 450#endif // defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) 451