// // Copyright 2002 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. // // Display.cpp: Implements the egl::Display class, representing the abstract // display on which graphics are drawn. Implements EGLDisplay. // [EGL 1.4] section 2.1.2 page 3. #include "libANGLE/Display.h" #include #include #include #include #include #include #include "anglebase/no_destructor.h" #include "common/android_util.h" #include "common/debug.h" #include "common/mathutil.h" #include "common/platform.h" #include "common/string_utils.h" #include "common/system_utils.h" #include "common/tls.h" #include "common/utilities.h" #include "gpu_info_util/SystemInfo.h" #include "libANGLE/Context.h" #include "libANGLE/Device.h" #include "libANGLE/EGLSync.h" #include "libANGLE/Image.h" #include "libANGLE/ResourceManager.h" #include "libANGLE/Stream.h" #include "libANGLE/Surface.h" #include "libANGLE/Thread.h" #include "libANGLE/capture/FrameCapture.h" #include "libANGLE/histogram_macros.h" #include "libANGLE/renderer/DeviceImpl.h" #include "libANGLE/renderer/DisplayImpl.h" #include "libANGLE/renderer/ImageImpl.h" #include "libANGLE/trace.h" #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) # include # include "libANGLE/renderer/d3d/DisplayD3D.h" #endif #if defined(ANGLE_ENABLE_OPENGL) # if defined(ANGLE_PLATFORM_WINDOWS) # include "libANGLE/renderer/gl/wgl/DisplayWGL.h" # elif defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_IOS) # include "libANGLE/renderer/gl/apple/DisplayApple_api.h" # elif defined(ANGLE_PLATFORM_LINUX) # include "libANGLE/renderer/gl/egl/DisplayEGL.h" # if defined(ANGLE_USE_GBM) # include "libANGLE/renderer/gl/egl/gbm/DisplayGbm.h" # endif # if defined(ANGLE_USE_X11) # include "libANGLE/renderer/gl/glx/DisplayGLX.h" # endif # elif defined(ANGLE_PLATFORM_ANDROID) # include "libANGLE/renderer/gl/egl/android/DisplayAndroid.h" # else # error Unsupported OpenGL platform. # endif #endif #if defined(ANGLE_ENABLE_NULL) # include "libANGLE/renderer/null/DisplayNULL.h" #endif // defined(ANGLE_ENABLE_NULL) #if defined(ANGLE_ENABLE_VULKAN) # include "libANGLE/renderer/vulkan/DisplayVk_api.h" #endif // defined(ANGLE_ENABLE_VULKAN) #if defined(ANGLE_ENABLE_METAL) # include "libANGLE/renderer/metal/DisplayMtl_api.h" #endif // defined(ANGLE_ENABLE_METAL) namespace egl { namespace { constexpr angle::SubjectIndex kGPUSwitchedSubjectIndex = 0; static constexpr size_t kWindowSurfaceMapSize = 32; typedef angle::FlatUnorderedMap WindowSurfaceMap; // Get a map of all EGL window surfaces to validate that no window has more than one EGL surface // associated with it. static WindowSurfaceMap *GetWindowSurfaces() { static angle::base::NoDestructor windowSurfaces; return windowSurfaces.get(); } struct ANGLEPlatformDisplay { ANGLEPlatformDisplay() = default; ANGLEPlatformDisplay(EGLNativeDisplayType nativeDisplayType) : nativeDisplayType(nativeDisplayType) {} ANGLEPlatformDisplay(EGLNativeDisplayType nativeDisplayType, EGLAttrib powerPreference, EGLAttrib platformANGLEType, EGLAttrib deviceIdHigh, EGLAttrib deviceIdLow) : nativeDisplayType(nativeDisplayType), powerPreference(powerPreference), platformANGLEType(platformANGLEType), deviceIdHigh(deviceIdHigh), deviceIdLow(deviceIdLow) {} auto tie() const { return std::tie(nativeDisplayType, powerPreference, platformANGLEType, deviceIdHigh, deviceIdLow); } EGLNativeDisplayType nativeDisplayType{EGL_DEFAULT_DISPLAY}; EGLAttrib powerPreference{EGL_LOW_POWER_ANGLE}; EGLAttrib platformANGLEType{EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE}; EGLAttrib deviceIdHigh{0}; EGLAttrib deviceIdLow{0}; }; inline bool operator==(const ANGLEPlatformDisplay &a, const ANGLEPlatformDisplay &b) { return a.tie() == b.tie(); } static constexpr size_t kANGLEPlatformDisplayMapSize = 9; typedef angle::FlatUnorderedMap ANGLEPlatformDisplayMap; static ANGLEPlatformDisplayMap *GetANGLEPlatformDisplayMap() { static angle::base::NoDestructor displays; return displays.get(); } static constexpr size_t kDevicePlatformDisplayMapSize = 8; typedef angle::FlatUnorderedMap DevicePlatformDisplayMap; static DevicePlatformDisplayMap *GetDevicePlatformDisplayMap() { static angle::base::NoDestructor displays; return displays.get(); } rx::DisplayImpl *CreateDisplayFromDevice(Device *eglDevice, const DisplayState &state) { rx::DisplayImpl *impl = nullptr; switch (eglDevice->getType()) { #if defined(ANGLE_ENABLE_D3D11) case EGL_D3D11_DEVICE_ANGLE: impl = new rx::DisplayD3D(state); break; #endif #if defined(ANGLE_ENABLE_D3D9) case EGL_D3D9_DEVICE_ANGLE: // Currently the only way to get EGLDeviceEXT representing a D3D9 device // is to retrieve one from an already-existing EGLDisplay. // When eglGetPlatformDisplayEXT is called with a D3D9 EGLDeviceEXT, // the already-existing display should be returned. // Therefore this codepath to create a new display from the device // should never be hit. UNREACHABLE(); break; #endif default: UNREACHABLE(); break; } ASSERT(impl != nullptr); return impl; } // On platforms with support for multiple back-ends, allow an environment variable to control // the default. This is useful to run angle with benchmarks without having to modify the // benchmark source. Possible values for this environment variable (ANGLE_DEFAULT_PLATFORM) // are: vulkan, gl, d3d11, null. EGLAttrib GetDisplayTypeFromEnvironment() { std::string angleDefaultEnv = angle::GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM"); angle::ToLower(&angleDefaultEnv); #if defined(ANGLE_ENABLE_VULKAN) if ((angleDefaultEnv == "vulkan") || (angleDefaultEnv == "vulkan-null") || (angleDefaultEnv == "swiftshader")) { return EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; } #endif #if defined(ANGLE_ENABLE_OPENGL) if (angleDefaultEnv == "gl") { return EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; } #endif #if defined(ANGLE_ENABLE_D3D11) if (angleDefaultEnv == "d3d11") { return EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE; } #endif #if defined(ANGLE_ENABLE_METAL) if (angleDefaultEnv == "metal") { return EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE; } #endif #if defined(ANGLE_ENABLE_NULL) if (angleDefaultEnv == "null") { return EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE; } #endif #if defined(ANGLE_ENABLE_METAL) if (angleDefaultEnv == "metal") { return EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE; } #endif #if defined(ANGLE_ENABLE_D3D11) return EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE; #elif defined(ANGLE_ENABLE_D3D9) return EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE; #elif defined(ANGLE_ENABLE_VULKAN) && defined(ANGLE_PLATFORM_ANDROID) return EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; #elif defined(ANGLE_ENABLE_OPENGL) # if defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_USE_GBM) return EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; # else return EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE; # endif #elif defined(ANGLE_ENABLE_METAL) return EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE; #elif defined(ANGLE_ENABLE_VULKAN) return EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; #elif defined(ANGLE_ENABLE_NULL) return EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE; #else # error No default ANGLE platform type #endif } EGLAttrib GetDeviceTypeFromEnvironment() { std::string angleDefaultEnv = angle::GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM"); angle::ToLower(&angleDefaultEnv); #if defined(ANGLE_ENABLE_VULKAN) if (angleDefaultEnv == "vulkan-null") { return EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE; } else if (angleDefaultEnv == "swiftshader") { return EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE; } #endif return EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE; } EGLAttrib GetPlatformTypeFromEnvironment() { #if defined(ANGLE_USE_OZONE) return 0; #elif defined(ANGLE_USE_X11) return EGL_PLATFORM_X11_EXT; #elif defined(ANGLE_USE_WAYLAND) return EGL_PLATFORM_WAYLAND_EXT; #elif defined(ANGLE_USE_VULKAN_DISPLAY) && defined(ANGLE_VULKAN_DISPLAY_MODE_SIMPLE) return EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE; #elif defined(ANGLE_USE_VULKAN_DISPLAY) && defined(ANGLE_VULKAN_DISPLAY_MODE_HEADLESS) return EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE; #else return 0; #endif // defined(ANGLE_USE_OZONE) } rx::DisplayImpl *CreateDisplayFromAttribs(EGLAttrib displayType, EGLAttrib deviceType, EGLAttrib platformType, const DisplayState &state) { ASSERT(displayType != EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); rx::DisplayImpl *impl = nullptr; switch (displayType) { case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: UNREACHABLE(); break; case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) impl = new rx::DisplayD3D(state); break; #else // A D3D display was requested on a platform that doesn't support it UNREACHABLE(); break; #endif case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE: #if defined(ANGLE_ENABLE_OPENGL) # if defined(ANGLE_PLATFORM_WINDOWS) impl = new rx::DisplayWGL(state); break; # elif defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_IOS) impl = rx::CreateDisplayCGLOrEAGL(state); break; # elif defined(ANGLE_PLATFORM_LINUX) # if defined(ANGLE_USE_GBM) if (platformType == 0) { // If platformType is unknown, use DisplayGbm now. In the future, it should use // DisplayEGL letting native EGL decide what display to use. impl = new rx::DisplayGbm(state); break; } # endif if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE) { impl = new rx::DisplayEGL(state); break; } # if defined(ANGLE_USE_X11) if (platformType == EGL_PLATFORM_X11_EXT) { impl = new rx::DisplayGLX(state); break; } # endif break; # elif defined(ANGLE_PLATFORM_ANDROID) // No GL support on this platform, fail display creation. impl = nullptr; break; # else # error Unsupported OpenGL platform. # endif #else // No display available UNREACHABLE(); break; #endif // defined(ANGLE_ENABLE_OPENGL) case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: #if defined(ANGLE_ENABLE_OPENGL) # if defined(ANGLE_PLATFORM_WINDOWS) impl = new rx::DisplayWGL(state); # elif defined(ANGLE_PLATFORM_LINUX) # if defined(ANGLE_USE_GBM) if (platformType == 0 || platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE) { // If platformType is unknown, use DisplayGbm now. In the future, it should use // DisplayEGL letting native EGL decide what display to use. // platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE is a hack, // to allow ChromeOS GLES backend to continue functioning when Vulkan is enabled. impl = new rx::DisplayGbm(state); break; } # endif if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE) { impl = new rx::DisplayEGL(state); break; } else { # if defined(ANGLE_USE_X11) if (platformType == EGL_PLATFORM_X11_EXT) { impl = new rx::DisplayGLX(state); break; } # endif } # elif defined(ANGLE_PLATFORM_ANDROID) impl = new rx::DisplayAndroid(state); # else // No GLES support on this platform, fail display creation. impl = nullptr; # endif #endif // defined(ANGLE_ENABLE_OPENGL) break; case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE: #if defined(ANGLE_ENABLE_VULKAN) # if defined(ANGLE_USE_VULKAN_NULL_DISPLAY) if (rx::IsVulkanNullDisplayAvailable()) { impl = rx::CreateVulkanNullDisplay(state); } break; # elif defined(ANGLE_PLATFORM_WINDOWS) if (rx::IsVulkanWin32DisplayAvailable()) { impl = rx::CreateVulkanWin32Display(state); } break; # elif defined(ANGLE_PLATFORM_LINUX) # if defined(ANGLE_USE_GBM) if (platformType == EGL_PLATFORM_GBM_KHR && rx::IsVulkanGbmDisplayAvailable()) { impl = rx::CreateVulkanGbmDisplay(state); break; } # endif # if defined(ANGLE_USE_X11) if (platformType == EGL_PLATFORM_X11_EXT && rx::IsVulkanXcbDisplayAvailable()) { impl = rx::CreateVulkanXcbDisplay(state); break; } # endif # if defined(ANGLE_USE_WAYLAND) if (platformType == EGL_PLATFORM_WAYLAND_EXT && rx::IsVulkanWaylandDisplayAvailable()) { impl = rx::CreateVulkanWaylandDisplay(state); break; } # endif # if defined(ANGLE_USE_VULKAN_DISPLAY) if (platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE && rx::IsVulkanSimpleDisplayAvailable()) { impl = rx::CreateVulkanSimpleDisplay(state); } else if (platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE && rx::IsVulkanHeadlessDisplayAvailable()) { impl = rx::CreateVulkanHeadlessDisplay(state); } else { // Not supported creation type on vulkan display, fail display creation. impl = nullptr; } # endif break; # elif defined(ANGLE_PLATFORM_ANDROID) if (rx::IsVulkanAndroidDisplayAvailable()) { impl = rx::CreateVulkanAndroidDisplay(state); } break; # elif defined(ANGLE_PLATFORM_FUCHSIA) if (rx::IsVulkanFuchsiaDisplayAvailable()) { impl = rx::CreateVulkanFuchsiaDisplay(state); } break; # elif defined(ANGLE_PLATFORM_GGP) if (rx::IsVulkanGGPDisplayAvailable()) { impl = rx::CreateVulkanGGPDisplay(state); } break; # elif defined(ANGLE_PLATFORM_APPLE) if (rx::IsVulkanMacDisplayAvailable()) { impl = rx::CreateVulkanMacDisplay(state); } break; # else # error Unsupported Vulkan platform. # endif #else // No display available UNREACHABLE(); break; #endif // defined(ANGLE_ENABLE_VULKAN) case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE: #if defined(ANGLE_ENABLE_METAL) if (rx::IsMetalDisplayAvailable()) { impl = rx::CreateMetalDisplay(state); break; } #endif // No display available UNREACHABLE(); break; case EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE: #if defined(ANGLE_ENABLE_NULL) impl = new rx::DisplayNULL(state); break; #else // No display available UNREACHABLE(); break; #endif // defined(ANGLE_ENABLE_NULL) default: UNREACHABLE(); break; } return impl; } void Display_logError(angle::PlatformMethods *platform, const char *errorMessage) { gl::Trace(gl::LOG_ERR, errorMessage); } void Display_logWarning(angle::PlatformMethods *platform, const char *warningMessage) { gl::Trace(gl::LOG_WARN, warningMessage); } void Display_logInfo(angle::PlatformMethods *platform, const char *infoMessage) { // Uncomment to get info spam #if defined(ANGLE_ENABLE_DEBUG_TRACE) gl::Trace(gl::LOG_INFO, infoMessage); #endif } const std::vector EGLStringArrayToStringVector(const char **ary) { std::vector vec; if (ary != nullptr) { for (; *ary != nullptr; ary++) { vec.push_back(std::string(*ary)); } } return vec; } void ANGLESetDefaultDisplayPlatform(angle::EGLDisplayType display) { angle::PlatformMethods *platformMethods = ANGLEPlatformCurrent(); ANGLEResetDisplayPlatform(display); platformMethods->logError = Display_logError; platformMethods->logWarning = Display_logWarning; platformMethods->logInfo = Display_logInfo; } void UpdateAttribsFromEnvironment(AttributeMap &attribMap) { EGLAttrib displayType = attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); if (displayType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) { displayType = GetDisplayTypeFromEnvironment(); attribMap.insert(EGL_PLATFORM_ANGLE_TYPE_ANGLE, displayType); } EGLAttrib deviceType = attribMap.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, 0); if (deviceType == 0) { deviceType = GetDeviceTypeFromEnvironment(); attribMap.insert(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, deviceType); } EGLAttrib platformType = attribMap.get(EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE, 0); if (platformType == 0) { platformType = GetPlatformTypeFromEnvironment(); attribMap.insert(EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE, platformType); } } static constexpr uint32_t kScratchBufferLifetime = 64u; } // anonymous namespace // ShareGroup ShareGroup::ShareGroup(rx::EGLImplFactory *factory) : mRefCount(1), mImplementation(factory->createShareGroup()), mFrameCaptureShared(new angle::FrameCaptureShared) {} void ShareGroup::finishAllContexts() { for (gl::Context *shareContext : mContexts) { if (shareContext->hasBeenCurrent() && !shareContext->isDestroyed()) { shareContext->finish(); } } } void ShareGroup::addSharedContext(gl::Context *context) { mContexts.insert(context); if (context->isRobustnessEnabled()) { mImplementation->onRobustContextAdd(); } } void ShareGroup::removeSharedContext(gl::Context *context) { mContexts.erase(context); } ShareGroup::~ShareGroup() { SafeDelete(mImplementation); } void ShareGroup::addRef() { // This is protected by global lock, so no atomic is required mRefCount++; } void ShareGroup::release(const Display *display) { if (--mRefCount == 0) { if (mImplementation) { mImplementation->onDestroy(display); } delete this; } } // DisplayState DisplayState::DisplayState(EGLNativeDisplayType nativeDisplayId) : label(nullptr), featuresAllDisabled(false), displayId(nativeDisplayId) {} DisplayState::~DisplayState() {} // Note that ANGLE support on Ozone platform is limited. Our preferred support Matrix for // EGL_ANGLE_platform_angle on Linux and Ozone/Linux/Fuchsia platforms should be the following: // // |--------------------------------------------------------| // | ANGLE type | DEVICE type | PLATFORM type | Display | // |--------------------------------------------------------| // | OPENGL | EGL | ANY | EGL | // | OPENGL | HARDWARE | X11_EXT | GLX | // | OPENGLES | HARDWARE | X11_EXT | GLX | // | OPENGLES | EGL | ANY | EGL | // | VULKAN | HARDWARE | X11_EXT | VkXcb | // | VULKAN | SWIFTSHADER | X11_EXT | VkXcb | // | OPENGLES | HARDWARE | SURFACELESS_MESA | EGL* | // | OPENGLES | HARDWARE | DEVICE_EXT | EGL | // | VULKAN | HARDWARE | SURFACELESS_MESA | VkBase** | // | VULKAN | SWIFTSHADER | SURFACELESS_MESA | VkBase** | // |--------------------------------------------------------| // // * No surfaceless support yet. // ** Not implemented yet. // // |-----------------------------------------------| // | OS | BUILD type | Default PLATFORM type | // |-----------------------------------------------| // | Linux | X11 | X11_EXT | // | Linux | Ozone | SURFACELESS_MESA | // | Fuchsia | Ozone | FUCHSIA*** | // |-----------------------------------------------| // // *** Chosen implicitly. No EGLAttrib available. // // For more details, please refer to // https://docs.google.com/document/d/1XjHiDZQISq1AMrg_l1TX1_kIKvDpU76hidn9i4cAjl8/edit?disco=AAAAJl9V_YY // // static Display *Display::GetDisplayFromNativeDisplay(EGLenum platform, EGLNativeDisplayType nativeDisplay, const AttributeMap &attribMap) { Display *display = nullptr; AttributeMap updatedAttribMap(attribMap); UpdateAttribsFromEnvironment(updatedAttribMap); EGLAttrib powerPreference = updatedAttribMap.get(EGL_POWER_PREFERENCE_ANGLE, EGL_LOW_POWER_ANGLE); EGLAttrib platformANGLEType = updatedAttribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); EGLAttrib deviceIdHigh = updatedAttribMap.get(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE, 0); EGLAttrib deviceIdLow = updatedAttribMap.get(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE, 0); ANGLEPlatformDisplayMap *displays = GetANGLEPlatformDisplayMap(); ANGLEPlatformDisplay displayKey(nativeDisplay, powerPreference, platformANGLEType, deviceIdHigh, deviceIdLow); const auto &iter = displays->find(displayKey); if (iter != displays->end()) { display = iter->second; } if (display == nullptr) { // Validate the native display if (!Display::isValidNativeDisplay(nativeDisplay)) { return nullptr; } display = new Display(platform, nativeDisplay, nullptr); displays->insert(std::make_pair(displayKey, display)); } // Apply new attributes if the display is not initialized yet. if (!display->isInitialized()) { display->setAttributes(updatedAttribMap); EGLAttrib displayType = display->mAttributeMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE); EGLAttrib deviceType = display->mAttributeMap.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE); EGLAttrib platformType = platform; if (platform == EGL_PLATFORM_ANGLE_ANGLE) { platformType = display->mAttributeMap.get(EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE); } rx::DisplayImpl *impl = CreateDisplayFromAttribs(displayType, deviceType, platformType, display->getState()); if (impl == nullptr) { // No valid display implementation for these attributes return nullptr; } #if defined(ANGLE_USE_ANDROID_TLS_SLOT) angle::gUseAndroidOpenGLTlsSlot = displayType == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; #endif // defined(ANGLE_PLATFORM_ANDROID) display->setupDisplayPlatform(impl); } return display; } // static Display *Display::GetExistingDisplayFromNativeDisplay(EGLNativeDisplayType nativeDisplay) { ANGLEPlatformDisplayMap *displays = GetANGLEPlatformDisplayMap(); const auto &iter = displays->find(nativeDisplay); // Check that there is a matching display if (iter == displays->end()) { return nullptr; } return iter->second; } // static Display *Display::GetDisplayFromDevice(Device *device, const AttributeMap &attribMap) { Display *display = nullptr; ASSERT(Device::IsValidDevice(device)); ANGLEPlatformDisplayMap *anglePlatformDisplays = GetANGLEPlatformDisplayMap(); DevicePlatformDisplayMap *devicePlatformDisplays = GetDevicePlatformDisplayMap(); // First see if this eglDevice is in use by a Display created using ANGLE platform for (auto &displayMapEntry : *anglePlatformDisplays) { egl::Display *iterDisplay = displayMapEntry.second; if (iterDisplay->getDevice() == device) { display = iterDisplay; } } if (display == nullptr) { // See if the eglDevice is in use by a Display created using the DEVICE platform const auto &iter = devicePlatformDisplays->find(device); if (iter != devicePlatformDisplays->end()) { display = iter->second; } } if (display == nullptr) { // Otherwise create a new Display display = new Display(EGL_PLATFORM_DEVICE_EXT, 0, device); devicePlatformDisplays->insert(std::make_pair(device, display)); } // Apply new attributes if the display is not initialized yet. if (!display->isInitialized()) { display->setAttributes(attribMap); rx::DisplayImpl *impl = CreateDisplayFromDevice(device, display->getState()); display->setupDisplayPlatform(impl); } return display; } // static Display::EglDisplaySet Display::GetEglDisplaySet() { Display::EglDisplaySet displays; ANGLEPlatformDisplayMap *anglePlatformDisplays = GetANGLEPlatformDisplayMap(); DevicePlatformDisplayMap *devicePlatformDisplays = GetDevicePlatformDisplayMap(); for (auto anglePlatformDisplayMapEntry : *anglePlatformDisplays) { displays.insert(anglePlatformDisplayMapEntry.second); } for (auto devicePlatformDisplayMapEntry : *devicePlatformDisplays) { displays.insert(devicePlatformDisplayMapEntry.second); } return displays; } Display::Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice) : mState(displayId), mImplementation(nullptr), mGPUSwitchedBinding(this, kGPUSwitchedSubjectIndex), mAttributeMap(), mConfigSet(), mStreamSet(), mInvalidContextSet(), mInvalidImageSet(), mInvalidStreamSet(), mInvalidSurfaceSet(), mInvalidSyncSet(), mInitialized(false), mDeviceLost(false), mCaps(), mDisplayExtensions(), mDisplayExtensionString(), mVendorString(), mVersionString(), mDevice(eglDevice), mSurface(nullptr), mPlatform(platform), mTextureManager(nullptr), mSemaphoreManager(nullptr), mBlobCache(gl::kDefaultMaxProgramCacheMemoryBytes), mMemoryProgramCache(mBlobCache), mMemoryShaderCache(mBlobCache), mGlobalTextureShareGroupUsers(0), mGlobalSemaphoreShareGroupUsers(0), mTerminatedByApi(false), mActiveThreads() {} Display::~Display() { switch (mPlatform) { case EGL_PLATFORM_ANGLE_ANGLE: case EGL_PLATFORM_GBM_KHR: case EGL_PLATFORM_WAYLAND_EXT: { ANGLEPlatformDisplayMap *displays = GetANGLEPlatformDisplayMap(); ANGLEPlatformDisplayMap::iterator iter = displays->find(ANGLEPlatformDisplay( mState.displayId, mAttributeMap.get(EGL_POWER_PREFERENCE_ANGLE, EGL_LOW_POWER_ANGLE), mAttributeMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE), mAttributeMap.get(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE, 0), mAttributeMap.get(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE, 0))); if (iter != displays->end()) { displays->erase(iter); } break; } case EGL_PLATFORM_DEVICE_EXT: { DevicePlatformDisplayMap *displays = GetDevicePlatformDisplayMap(); DevicePlatformDisplayMap::iterator iter = displays->find(mDevice); if (iter != displays->end()) { displays->erase(iter); } break; } default: { UNREACHABLE(); } } SafeDelete(mDevice); SafeDelete(mImplementation); } void Display::setLabel(EGLLabelKHR label) { mState.label = label; } 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 (gl::Context *context : mState.contextSet) { context->onGPUSwitch(); } } void Display::setupDisplayPlatform(rx::DisplayImpl *impl) { ASSERT(!mInitialized); ASSERT(impl != nullptr); SafeDelete(mImplementation); mImplementation = impl; // TODO(anglebug.com/7365): Remove PlatformMethods. const angle::PlatformMethods *platformMethods = reinterpret_cast( mAttributeMap.get(EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX, 0)); if (platformMethods != nullptr) { *ANGLEPlatformCurrent() = *platformMethods; } else { ANGLESetDefaultDisplayPlatform(this); } const char **featuresForceEnabled = reinterpret_cast(mAttributeMap.get(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE, 0)); const char **featuresForceDisabled = reinterpret_cast(mAttributeMap.get(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE, 0)); mState.featureOverridesEnabled = EGLStringArrayToStringVector(featuresForceEnabled); mState.featureOverridesDisabled = EGLStringArrayToStringVector(featuresForceDisabled); mState.featuresAllDisabled = static_cast(mAttributeMap.get(EGL_FEATURE_ALL_DISABLED_ANGLE, 0)); mImplementation->addObserver(&mGPUSwitchedBinding); } Error Display::initialize() { mTerminatedByApi = false; ASSERT(mImplementation != nullptr); mImplementation->setBlobCache(&mBlobCache); // Enable shader caching if debug layers are turned on. This allows us to test that shaders are // properly saved & restored on all platforms. The cache won't allocate space until it's used // and will be ignored entirely if the application / system sets it's own cache functions. if (rx::ShouldUseDebugLayers(mAttributeMap)) { mBlobCache.resize(1024 * 1024); } setGlobalDebugAnnotator(); gl::InitializeDebugMutexIfNeeded(); ANGLE_TRACE_EVENT0("gpu.angle", "egl::Display::initialize"); if (isInitialized()) { return NoError(); } Error error = mImplementation->initialize(this); if (error.isError()) { // Log extended error message here ERR() << "ANGLE Display::initialize error " << error.getID() << ": " << error.getMessage(); return error; } mCaps = mImplementation->getCaps(); mConfigSet = mImplementation->generateConfigs(); if (mConfigSet.size() == 0) { mImplementation->terminate(); return EglNotInitialized() << "No configs were generated."; } // OpenGL ES1 is implemented in the frontend, explicitly add ES1 support to all configs for (auto &config : mConfigSet) { // TODO(geofflang): Enable the conformant bit once we pass enough tests // config.second.conformant |= EGL_OPENGL_ES_BIT; config.second.renderableType |= EGL_OPENGL_ES_BIT; // If we aren't using desktop GL entry points, remove desktop GL support from all configs #if !defined(ANGLE_ENABLE_GL_DESKTOP_FRONTEND) config.second.renderableType &= ~EGL_OPENGL_BIT; #endif } if (!mState.featuresAllDisabled) { initializeFrontendFeatures(); } mFeatures.clear(); mFrontendFeatures.populateFeatureList(&mFeatures); mImplementation->populateFeatureList(&mFeatures); initDisplayExtensions(); initVendorString(); initVersionString(); initClientAPIString(); // Populate the Display's EGLDeviceEXT if the Display wasn't created using one if (mPlatform == EGL_PLATFORM_DEVICE_EXT) { // For EGL_PLATFORM_DEVICE_EXT, mDevice should always be populated using // an external device ASSERT(mDevice != nullptr); } else if (GetClientExtensions().deviceQueryEXT) { std::unique_ptr impl(mImplementation->createDevice()); ASSERT(impl); error = impl->initialize(); if (error.isError()) { ERR() << "Failed to initialize display because device creation failed: " << error.getMessage(); mImplementation->terminate(); return error; } // Don't leak Device memory. ASSERT(mDevice == nullptr); mDevice = new Device(this, impl.release()); } else { mDevice = nullptr; } mInitialized = true; return NoError(); } Error Display::destroyInvalidEglObjects() { // Destroy invalid EGL objects while (!mInvalidContextSet.empty()) { gl::Context *context = *mInvalidContextSet.begin(); context->setIsDestroyed(); ANGLE_TRY(releaseContextImpl(context, &mInvalidContextSet)); } while (!mInvalidImageSet.empty()) { destroyImageImpl(*mInvalidImageSet.begin(), &mInvalidImageSet); } while (!mInvalidStreamSet.empty()) { destroyStreamImpl(*mInvalidStreamSet.begin(), &mInvalidStreamSet); } while (!mInvalidSurfaceSet.empty()) { ANGLE_TRY(destroySurfaceImpl(*mInvalidSurfaceSet.begin(), &mInvalidSurfaceSet)); } while (!mInvalidSyncSet.empty()) { destroySyncImpl(*mInvalidSyncSet.begin(), &mInvalidSyncSet); } return NoError(); } Error Display::terminate(Thread *thread, TerminateReason terminateReason) { if (terminateReason == TerminateReason::Api) { mTerminatedByApi = true; } // All subsequent calls assume the display to be valid and terminated by app. // If it is not terminated or if it isn't even initialized, early return. if (!mTerminatedByApi || !mInitialized) { return NoError(); } // EGL 1.5 Specification // 3.2 Initialization // Termination marks all EGL-specific resources, such as contexts and surfaces, associated // with the specified display for deletion. Handles to all such resources are invalid as soon // as eglTerminate returns. Cache EGL objects that are no longer valid. // // It is fairly common for apps to call eglTerminate while some contexts and/or surfaces are // still current on some thread. Since objects are refCounted, trying to destroy them right away // would only result in a decRef. We instead cache such invalid objects and use other EGL // entrypoints like eglReleaseThread or thread exit events (on the Android platform) to // perform the necessary cleanup. mInvalidImageSet.insert(mImageSet.begin(), mImageSet.end()); mImageSet.clear(); mInvalidStreamSet.insert(mStreamSet.begin(), mStreamSet.end()); mStreamSet.clear(); mInvalidSurfaceSet.insert(mState.surfaceSet.begin(), mState.surfaceSet.end()); mState.surfaceSet.clear(); mInvalidSyncSet.insert(mSyncSet.begin(), mSyncSet.end()); mSyncSet.clear(); // Cache total number of contexts before invalidation. This is used as a check to verify that // no context is "lost" while being moved between the various sets. size_t contextSetSizeBeforeInvalidation = mState.contextSet.size() + mInvalidContextSet.size(); // If app called eglTerminate and no active threads remain, // force realease any context that is still current. ContextSet contextsStillCurrent = {}; for (gl::Context *context : mState.contextSet) { if (context->getRefCount() > 0) { if (terminateReason == TerminateReason::NoActiveThreads) { ASSERT(mTerminatedByApi); context->release(); (void)context->unMakeCurrent(this); } else { contextsStillCurrent.emplace(context); continue; } } // Add context that is not current to mInvalidContextSet for cleanup. mInvalidContextSet.emplace(context); } // There are many methods that require contexts that are still current to be present in // display's contextSet like during context release or to notify of state changes in a subject. // So as to not interrupt this flow, do not remove contexts that are still current on some // thread from display's contextSet even though eglTerminate marks such contexts as invalid. // // "mState.contextSet" will now contain only those contexts that are still current on some // thread. mState.contextSet = std::move(contextsStillCurrent); // Assert that the total number of contexts is the same before and after context invalidation. ASSERT(contextSetSizeBeforeInvalidation == mState.contextSet.size() + mInvalidContextSet.size()); if (!mState.contextSet.empty()) { // There was atleast 1 context that was current on some thread, early return. return NoError(); } mMemoryProgramCache.clear(); mMemoryShaderCache.clear(); mBlobCache.setBlobCacheFuncs(nullptr, nullptr); // The global texture and semaphore managers should be deleted with the last context that uses // it. ASSERT(mGlobalTextureShareGroupUsers == 0 && mTextureManager == nullptr); ASSERT(mGlobalSemaphoreShareGroupUsers == 0 && mSemaphoreManager == nullptr); // Clean up all invalid objects ANGLE_TRY(destroyInvalidEglObjects()); mConfigSet.clear(); if (mDevice != nullptr && mDevice->getOwningDisplay() != nullptr) { // Don't delete the device if it was created externally using eglCreateDeviceANGLE // We also shouldn't set it to null in case eglInitialize() is called again later SafeDelete(mDevice); } mImplementation->terminate(); mDeviceLost = false; mInitialized = false; gl::UninitializeDebugAnnotations(); // TODO(jmadill): Store Platform in Display and deinit here. ANGLEResetDisplayPlatform(this); return NoError(); } Error Display::prepareForCall() { return mImplementation->prepareForCall(); } Error Display::releaseThread() { ANGLE_TRY(mImplementation->releaseThread()); return destroyInvalidEglObjects(); } void Display::addActiveThread(Thread *thread) { mActiveThreads.insert(thread); } void Display::threadCleanup(Thread *thread) { mActiveThreads.erase(thread); const bool noActiveThreads = mActiveThreads.size() == 0; (void)terminate(thread, noActiveThreads ? TerminateReason::NoActiveThreads : TerminateReason::InternalCleanup); } std::vector Display::getConfigs(const egl::AttributeMap &attribs) const { return mConfigSet.filter(attribs); } std::vector Display::chooseConfig(const egl::AttributeMap &attribs) const { egl::AttributeMap attribsWithDefaults = AttributeMap(); // Insert default values for attributes that have either an Exact or Mask selection criteria, // and a default value that matters (e.g. isn't EGL_DONT_CARE): attribsWithDefaults.insert(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER); attribsWithDefaults.insert(EGL_LEVEL, 0); attribsWithDefaults.insert(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT); attribsWithDefaults.insert(EGL_SURFACE_TYPE, EGL_WINDOW_BIT); attribsWithDefaults.insert(EGL_TRANSPARENT_TYPE, EGL_NONE); if (getExtensions().pixelFormatFloat) { attribsWithDefaults.insert(EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT); } // Add the caller-specified values (Note: the poorly-named insert() method will replace any // of the default values from above): for (auto attribIter = attribs.begin(); attribIter != attribs.end(); attribIter++) { attribsWithDefaults.insert(attribIter->first, attribIter->second); } return mConfigSet.filter(attribsWithDefaults); } Error Display::createWindowSurface(const Config *configuration, EGLNativeWindowType window, const AttributeMap &attribs, Surface **outSurface) { if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } SurfacePointer surface(new WindowSurface(mImplementation, configuration, window, attribs, mFrontendFeatures.forceRobustResourceInit.enabled), this); ANGLE_TRY(surface->initialize(this)); ASSERT(outSurface != nullptr); *outSurface = surface.release(); mState.surfaceSet.insert(*outSurface); WindowSurfaceMap *windowSurfaces = GetWindowSurfaces(); ASSERT(windowSurfaces && windowSurfaces->find(window) == windowSurfaces->end()); windowSurfaces->insert(std::make_pair(window, *outSurface)); mSurface = *outSurface; return NoError(); } Error Display::createPbufferSurface(const Config *configuration, const AttributeMap &attribs, Surface **outSurface) { ASSERT(isInitialized()); if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } SurfacePointer surface(new PbufferSurface(mImplementation, configuration, attribs, mFrontendFeatures.forceRobustResourceInit.enabled), this); ANGLE_TRY(surface->initialize(this)); ASSERT(outSurface != nullptr); *outSurface = surface.release(); mState.surfaceSet.insert(*outSurface); return NoError(); } Error Display::createPbufferFromClientBuffer(const Config *configuration, EGLenum buftype, EGLClientBuffer clientBuffer, const AttributeMap &attribs, Surface **outSurface) { ASSERT(isInitialized()); if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } SurfacePointer surface( new PbufferSurface(mImplementation, configuration, buftype, clientBuffer, attribs, mFrontendFeatures.forceRobustResourceInit.enabled), this); ANGLE_TRY(surface->initialize(this)); ASSERT(outSurface != nullptr); *outSurface = surface.release(); mState.surfaceSet.insert(*outSurface); return NoError(); } Error Display::createPixmapSurface(const Config *configuration, NativePixmapType nativePixmap, const AttributeMap &attribs, Surface **outSurface) { ASSERT(isInitialized()); if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } SurfacePointer surface(new PixmapSurface(mImplementation, configuration, nativePixmap, attribs, mFrontendFeatures.forceRobustResourceInit.enabled), this); ANGLE_TRY(surface->initialize(this)); ASSERT(outSurface != nullptr); *outSurface = surface.release(); mState.surfaceSet.insert(*outSurface); return NoError(); } Error Display::createImage(const gl::Context *context, EGLenum target, EGLClientBuffer buffer, const AttributeMap &attribs, Image **outImage) { ASSERT(isInitialized()); if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } egl::ImageSibling *sibling = nullptr; if (IsTextureTarget(target)) { sibling = context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)}); } else if (IsRenderbufferTarget(target)) { sibling = context->getRenderbuffer({egl_gl::EGLClientBufferToGLObjectHandle(buffer)}); } else if (IsExternalImageTarget(target)) { sibling = new ExternalImageSibling(mImplementation, context, target, buffer, attribs); } else { UNREACHABLE(); } ASSERT(sibling != nullptr); angle::UniqueObjectPointer imagePtr( new Image(mImplementation, context, target, sibling, attribs), this); ANGLE_TRY(imagePtr->initialize(this)); Image *image = imagePtr.release(); ASSERT(outImage != nullptr); *outImage = image; // Add this image to the list of all images and hold a ref to it. image->addRef(); mImageSet.insert(image); return NoError(); } Error Display::createStream(const AttributeMap &attribs, Stream **outStream) { ASSERT(isInitialized()); Stream *stream = new Stream(this, attribs); ASSERT(stream != nullptr); mStreamSet.insert(stream); ASSERT(outStream != nullptr); *outStream = stream; return NoError(); } Error Display::createContext(const Config *configuration, gl::Context *shareContext, EGLenum clientType, const AttributeMap &attribs, gl::Context **outContext) { ASSERT(!mTerminatedByApi); ASSERT(isInitialized()); if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } // This display texture sharing will allow the first context to create the texture share group. bool usingDisplayTextureShareGroup = attribs.get(EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE) == EGL_TRUE; gl::TextureManager *shareTextures = nullptr; if (usingDisplayTextureShareGroup) { ASSERT((mTextureManager == nullptr) == (mGlobalTextureShareGroupUsers == 0)); if (mTextureManager == nullptr) { mTextureManager = new gl::TextureManager(); } mGlobalTextureShareGroupUsers++; shareTextures = mTextureManager; } // This display semaphore sharing will allow the first context to create the semaphore share // group. bool usingDisplaySemaphoreShareGroup = attribs.get(EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE, EGL_FALSE) == EGL_TRUE; gl::SemaphoreManager *shareSemaphores = nullptr; if (usingDisplaySemaphoreShareGroup) { ASSERT((mSemaphoreManager == nullptr) == (mGlobalSemaphoreShareGroupUsers == 0)); if (mSemaphoreManager == nullptr) { mSemaphoreManager = new gl::SemaphoreManager(); } mGlobalSemaphoreShareGroupUsers++; shareSemaphores = mSemaphoreManager; } gl::MemoryProgramCache *programCachePointer = &mMemoryProgramCache; // Check context creation attributes to see if we are using EGL_ANGLE_program_cache_control. // If not, keep caching enabled for EGL_ANDROID_blob_cache, which can have its callbacks set // at any time. bool usesProgramCacheControl = attribs.contains(EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE); if (usesProgramCacheControl) { bool programCacheControlEnabled = (attribs.get(EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE, GL_FALSE) == GL_TRUE); // A program cache size of zero indicates it should be disabled. if (!programCacheControlEnabled || mMemoryProgramCache.maxSize() == 0) { programCachePointer = nullptr; } } gl::MemoryShaderCache *shaderCachePointer = &mMemoryShaderCache; // Check if shader caching frontend feature is enabled. if (!mFrontendFeatures.cacheCompiledShader.enabled) { shaderCachePointer = nullptr; } gl::Context *context = new gl::Context( this, configuration, shareContext, shareTextures, shareSemaphores, programCachePointer, shaderCachePointer, clientType, attribs, mDisplayExtensions, GetClientExtensions()); Error error = context->initialize(); if (error.isError()) { delete context; return error; } if (shareContext != nullptr) { shareContext->setShared(); } ASSERT(context != nullptr); mState.contextSet.insert(context); ASSERT(outContext != nullptr); *outContext = context; return NoError(); } Error Display::createSync(const gl::Context *currentContext, EGLenum type, const AttributeMap &attribs, Sync **outSync) { ASSERT(isInitialized()); if (mImplementation->testDeviceLost()) { ANGLE_TRY(restoreLostDevice()); } angle::UniqueObjectPointer syncPtr(new Sync(mImplementation, type, attribs), this); ANGLE_TRY(syncPtr->initialize(this, currentContext)); Sync *sync = syncPtr.release(); sync->addRef(); mSyncSet.insert(sync); *outSync = sync; return NoError(); } Error Display::makeCurrent(Thread *thread, gl::Context *previousContext, egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) { if (!mInitialized) { return NoError(); } bool contextChanged = context != previousContext; if (previousContext != nullptr && contextChanged) { previousContext->release(); thread->setCurrent(nullptr); auto error = previousContext->unMakeCurrent(this); if (previousContext->getRefCount() == 0 && previousContext->isDestroyed()) { // The previous Context may have been created with a different Display. Display *previousDisplay = previousContext->getDisplay(); ANGLE_TRY(previousDisplay->releaseContext(previousContext, thread)); } ANGLE_TRY(error); } thread->setCurrent(context); ANGLE_TRY(mImplementation->makeCurrent(this, drawSurface, readSurface, context)); if (context != nullptr) { ANGLE_TRY(context->makeCurrent(this, drawSurface, readSurface)); if (contextChanged) { context->addRef(); } } // Tick all the scratch buffers to make sure they get cleaned up eventually if they stop being // used. { std::lock_guard lock(mScratchBufferMutex); for (angle::ScratchBuffer &scatchBuffer : mScratchBuffers) { scatchBuffer.tick(); } for (angle::ScratchBuffer &zeroFilledBuffer : mZeroFilledBuffers) { zeroFilledBuffer.tick(); } } return NoError(); } Error Display::restoreLostDevice() { for (ContextSet::iterator ctx = mState.contextSet.begin(); ctx != mState.contextSet.end(); ctx++) { if ((*ctx)->isResetNotificationEnabled()) { // If reset notifications have been requested, application must delete all contexts // first return EglContextLost(); } } return mImplementation->restoreLostDevice(this); } Error Display::destroySurfaceImpl(Surface *surface, SurfaceSet *surfaces) { if (surface->getType() == EGL_WINDOW_BIT) { WindowSurfaceMap *windowSurfaces = GetWindowSurfaces(); ASSERT(windowSurfaces); bool surfaceRemoved = false; for (WindowSurfaceMap::iterator iter = windowSurfaces->begin(); iter != windowSurfaces->end(); iter++) { if (iter->second == surface) { windowSurfaces->erase(iter); surfaceRemoved = true; break; } } ASSERT(surfaceRemoved); } auto iter = surfaces->find(surface); ASSERT(iter != surfaces->end()); surfaces->erase(iter); ANGLE_TRY(surface->onDestroy(this)); return NoError(); } void Display::destroyImageImpl(Image *image, ImageSet *images) { auto iter = images->find(image); ASSERT(iter != images->end()); (*iter)->release(this); images->erase(iter); } void Display::destroyStreamImpl(Stream *stream, StreamSet *streams) { streams->erase(stream); SafeDelete(stream); } // releaseContext must be called with the context being deleted as current. // To do that we can only call this in two places, Display::makeCurrent at the point where this // context is being made uncurrent and in Display::destroyContext where we make the context current // as part of destruction. Error Display::releaseContext(gl::Context *context, Thread *thread) { return releaseContextImpl(context, &mState.contextSet); } Error Display::releaseContextImpl(gl::Context *context, ContextSet *contexts) { ASSERT(context->getRefCount() == 0); // Use scoped_ptr to make sure the context is always freed. std::unique_ptr unique_context(context); ASSERT(contexts->find(context) != contexts->end()); contexts->erase(context); if (context->usingDisplayTextureShareGroup()) { ASSERT(mGlobalTextureShareGroupUsers >= 1 && mTextureManager != nullptr); if (mGlobalTextureShareGroupUsers == 1) { // If this is the last context using the global share group, destroy the global // texture manager so that the textures can be destroyed while a context still // exists mTextureManager->release(context); mTextureManager = nullptr; } mGlobalTextureShareGroupUsers--; } if (context->usingDisplaySemaphoreShareGroup()) { ASSERT(mGlobalSemaphoreShareGroupUsers >= 1 && mSemaphoreManager != nullptr); if (mGlobalSemaphoreShareGroupUsers == 1) { // If this is the last context using the global share group, destroy the global // semaphore manager so that the semaphores can be destroyed while a context still // exists mSemaphoreManager->release(context); mSemaphoreManager = nullptr; } mGlobalSemaphoreShareGroupUsers--; } ANGLE_TRY(context->onDestroy(this)); return NoError(); } Error Display::destroyContext(Thread *thread, gl::Context *context) { auto *currentContext = thread->getContext(); auto *currentDrawSurface = thread->getCurrentDrawSurface(); auto *currentReadSurface = thread->getCurrentReadSurface(); context->setIsDestroyed(); // If the context is still current on at least 1 thread, just return since it'll be released // once no threads have it current anymore. if (context->getRefCount() > 0) { return NoError(); } // For external context, we cannot change the current native context, and the API user should // make sure the native context is current. if (context->isExternal()) { ANGLE_TRY(releaseContext(context, thread)); } else { // Keep |currentContext| alive, while releasing |context|. gl::ScopedContextRef scopedContextRef(currentContext); // keep |currentDrawSurface| and |currentReadSurface| alive as well // while releasing |context|. ScopedSurfaceRef drawSurfaceRef(currentDrawSurface); ScopedSurfaceRef readSurfaceRef( currentReadSurface == currentDrawSurface ? nullptr : currentReadSurface); // Make the context current, so we can release resources belong to the context, and then // when context is released from the current, it will be destroyed. // TODO(http://www.anglebug.com/6322): Don't require a Context to be current in order to // destroy it. ANGLE_TRY(makeCurrent(thread, currentContext, nullptr, nullptr, context)); ANGLE_TRY( makeCurrent(thread, context, currentDrawSurface, currentReadSurface, currentContext)); } // If eglTerminate() has previously been called and this is the last Context the Display owns, // we can now fully terminate the display and release all of its resources. if (mTerminatedByApi) { for (const gl::Context *ctx : mState.contextSet) { if (ctx->getRefCount() > 0) { return NoError(); } } return terminate(thread, TerminateReason::InternalCleanup); } return NoError(); } void Display::destroySyncImpl(Sync *sync, SyncSet *syncs) { auto iter = syncs->find(sync); ASSERT(iter != syncs->end()); (*iter)->release(this); syncs->erase(iter); } void Display::destroyImage(Image *image) { return destroyImageImpl(image, &mImageSet); } void Display::destroyStream(Stream *stream) { return destroyStreamImpl(stream, &mStreamSet); } Error Display::destroySurface(Surface *surface) { return destroySurfaceImpl(surface, &mState.surfaceSet); } void Display::destroySync(Sync *sync) { return destroySyncImpl(sync, &mSyncSet); } bool Display::isDeviceLost() const { ASSERT(isInitialized()); return mDeviceLost; } bool Display::testDeviceLost() { ASSERT(isInitialized()); if (!mDeviceLost && mImplementation->testDeviceLost()) { notifyDeviceLost(); } return mDeviceLost; } void Display::notifyDeviceLost() { if (mDeviceLost) { return; } for (ContextSet::iterator context = mState.contextSet.begin(); context != mState.contextSet.end(); context++) { (*context)->markContextLost(gl::GraphicsResetStatus::UnknownContextReset); } mDeviceLost = true; } void Display::setBlobCacheFuncs(EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get) { mBlobCache.setBlobCacheFuncs(set, get); mImplementation->setBlobCacheFuncs(set, get); } // static EGLClientBuffer Display::GetNativeClientBuffer(const AHardwareBuffer *buffer) { return angle::android::AHardwareBufferToClientBuffer(buffer); } // static Error Display::CreateNativeClientBuffer(const egl::AttributeMap &attribMap, EGLClientBuffer *eglClientBuffer) { int androidHardwareBufferFormat = gl::GetAndroidHardwareBufferFormatFromChannelSizes(attribMap); int width = attribMap.getAsInt(EGL_WIDTH, 0); int height = attribMap.getAsInt(EGL_HEIGHT, 0); int usage = attribMap.getAsInt(EGL_NATIVE_BUFFER_USAGE_ANDROID, 0); // https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_lock // for AHardwareBuffer_lock() // The passed AHardwareBuffer must have one layer, otherwise the call will fail. constexpr int kLayerCount = 1; *eglClientBuffer = angle::android::CreateEGLClientBufferFromAHardwareBuffer( width, height, kLayerCount, androidHardwareBufferFormat, usage); return (*eglClientBuffer == nullptr) ? egl::EglBadParameter() << "native client buffer allocation failed." : NoError(); } Error Display::waitClient(const gl::Context *context) { return mImplementation->waitClient(context); } Error Display::waitNative(const gl::Context *context, EGLint engine) { return mImplementation->waitNative(context, engine); } const Caps &Display::getCaps() const { return mCaps; } bool Display::isInitialized() const { return mInitialized; } bool Display::isValidConfig(const Config *config) const { return mConfigSet.contains(config); } bool Display::isValidContext(const gl::Context *context) const { return mState.contextSet.find(const_cast(context)) != mState.contextSet.end(); } bool Display::isValidSurface(const Surface *surface) const { return mState.surfaceSet.find(const_cast(surface)) != mState.surfaceSet.end(); } bool Display::isValidImage(const Image *image) const { return mImageSet.find(const_cast(image)) != mImageSet.end(); } bool Display::isValidStream(const Stream *stream) const { return mStreamSet.find(const_cast(stream)) != mStreamSet.end(); } bool Display::isValidSync(const Sync *sync) const { return mSyncSet.find(const_cast(sync)) != mSyncSet.end(); } bool Display::hasExistingWindowSurface(EGLNativeWindowType window) { WindowSurfaceMap *windowSurfaces = GetWindowSurfaces(); ASSERT(windowSurfaces); return windowSurfaces->find(window) != windowSurfaces->end(); } static ClientExtensions GenerateClientExtensions() { ClientExtensions extensions; extensions.clientExtensions = true; extensions.platformBase = true; extensions.platformANGLE = true; #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) extensions.platformANGLED3D = true; extensions.platformDevice = true; #endif #if defined(ANGLE_USE_GBM) extensions.platformGbmKHR = true; #endif #if defined(ANGLE_USE_WAYLAND) extensions.platformWaylandEXT = true; #endif #if defined(ANGLE_ENABLE_D3D11) # if defined(ANGLE_ENABLE_WINDOWS_UWP) extensions.platformANGLED3D11ON12 = true; # else extensions.platformANGLED3D11ON12 = IsWindows10OrGreater(); # endif extensions.platformANGLEDeviceId = true; #endif #if defined(ANGLE_ENABLE_OPENGL) extensions.platformANGLEOpenGL = true; #endif #if defined(ANGLE_ENABLE_NULL) extensions.platformANGLENULL = true; #endif #if defined(ANGLE_ENABLE_D3D11) extensions.deviceCreation = true; extensions.deviceCreationD3D11 = true; extensions.experimentalPresentPath = true; #endif #if defined(ANGLE_ENABLE_VULKAN) extensions.platformANGLEVulkan = true; extensions.platformANGLEDeviceId = true; #endif #if defined(ANGLE_ENABLE_SWIFTSHADER) extensions.platformANGLEDeviceTypeSwiftShader = true; #endif #if defined(ANGLE_ENABLE_METAL) extensions.platformANGLEMetal = true; extensions.platformANGLEDeviceId = true; #endif #if defined(ANGLE_USE_X11) extensions.x11Visual = true; #endif #if defined(ANGLE_PLATFORM_LINUX) extensions.platformANGLEDeviceTypeEGLANGLE = true; #endif #if (defined(ANGLE_PLATFORM_IOS) && !defined(ANGLE_PLATFORM_MACCATALYST)) || \ (defined(ANGLE_PLATFORM_MACCATALYST) && defined(ANGLE_CPU_ARM64)) extensions.platformANGLEDeviceContextVolatileEagl = true; #endif #if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) extensions.platformANGLEDeviceContextVolatileCgl = true; #endif #if defined(ANGLE_ENABLE_METAL) extensions.displayPowerPreferenceANGLE = true; #endif extensions.clientGetAllProcAddresses = true; extensions.debug = true; extensions.featureControlANGLE = true; extensions.deviceQueryEXT = true; return extensions; } template static std::string GenerateExtensionsString(const T &extensions) { std::vector extensionsVector = extensions.getStrings(); std::ostringstream stream; std::copy(extensionsVector.begin(), extensionsVector.end(), std::ostream_iterator(stream, " ")); return stream.str(); } // static const ClientExtensions &Display::GetClientExtensions() { static const ClientExtensions clientExtensions = GenerateClientExtensions(); return clientExtensions; } // static const std::string &Display::GetClientExtensionString() { static const angle::base::NoDestructor clientExtensionsString( GenerateExtensionsString(GetClientExtensions())); return *clientExtensionsString; } void Display::initDisplayExtensions() { mDisplayExtensions = mImplementation->getExtensions(); // Some extensions are always available because they are implemented in the EGL layer. mDisplayExtensions.createContext = true; mDisplayExtensions.createContextNoError = !mFrontendFeatures.forceGlErrorChecking.enabled; mDisplayExtensions.createContextWebGLCompatibility = true; mDisplayExtensions.createContextBindGeneratesResource = true; mDisplayExtensions.createContextClientArrays = true; mDisplayExtensions.pixelFormatFloat = true; mDisplayExtensions.reusableSyncKHR = true; // Force EGL_KHR_get_all_proc_addresses on. mDisplayExtensions.getAllProcAddresses = true; // Enable program cache control since it is not back-end dependent. mDisplayExtensions.programCacheControlANGLE = true; // Request extension is implemented in the ANGLE frontend mDisplayExtensions.createContextExtensionsEnabled = true; // Blob cache extension is provided by the ANGLE frontend mDisplayExtensions.blobCache = true; // The EGL_ANDROID_recordable extension is provided by the ANGLE frontend, and will always // say that ANativeWindow is not recordable. mDisplayExtensions.recordable = true; // All backends support specific context versions mDisplayExtensions.createContextBackwardsCompatible = true; mDisplayExtensionString = GenerateExtensionsString(mDisplayExtensions); } bool Display::isValidNativeWindow(EGLNativeWindowType window) const { return mImplementation->isValidNativeWindow(window); } Error Display::validateClientBuffer(const Config *configuration, EGLenum buftype, EGLClientBuffer clientBuffer, const AttributeMap &attribs) const { return mImplementation->validateClientBuffer(configuration, buftype, clientBuffer, attribs); } Error Display::validateImageClientBuffer(const gl::Context *context, EGLenum target, EGLClientBuffer clientBuffer, const egl::AttributeMap &attribs) const { return mImplementation->validateImageClientBuffer(context, target, clientBuffer, attribs); } Error Display::valdiatePixmap(const Config *config, EGLNativePixmapType pixmap, const AttributeMap &attributes) const { return mImplementation->validatePixmap(config, pixmap, attributes); } bool Display::isValidDisplay(const egl::Display *display) { const ANGLEPlatformDisplayMap *anglePlatformDisplayMap = GetANGLEPlatformDisplayMap(); for (const auto &displayPair : *anglePlatformDisplayMap) { if (displayPair.second == display) { return true; } } const DevicePlatformDisplayMap *devicePlatformDisplayMap = GetDevicePlatformDisplayMap(); for (const auto &displayPair : *devicePlatformDisplayMap) { if (displayPair.second == display) { return true; } } return false; } bool Display::isValidNativeDisplay(EGLNativeDisplayType display) { // TODO(jmadill): handle this properly if (display == EGL_DEFAULT_DISPLAY) { return true; } #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_ENABLE_WINDOWS_UWP) if (display == EGL_SOFTWARE_DISPLAY_ANGLE || display == EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE || display == EGL_D3D11_ONLY_DISPLAY_ANGLE) { return true; } return (WindowFromDC(display) != nullptr); #else return true; #endif } void Display::initVendorString() { mVendorString = "Google Inc."; std::string vendorStringImpl = mImplementation->getVendorString(); if (!vendorStringImpl.empty()) { mVendorString += " (" + vendorStringImpl + ")"; } } void Display::initVersionString() { mVersionString = mImplementation->getVersionString(true); } void Display::initClientAPIString() { // If the max supported desktop version is not None, we support a desktop GL frontend. if (mImplementation->getMaxSupportedDesktopVersion().valid()) { mClientAPIString = "OpenGL_ES OpenGL"; } else { mClientAPIString = "OpenGL_ES"; } } void Display::initializeFrontendFeatures() { // Enable on all Impls ANGLE_FEATURE_CONDITION((&mFrontendFeatures), loseContextOnOutOfMemory, true); ANGLE_FEATURE_CONDITION((&mFrontendFeatures), allowCompressedFormats, true); // No longer enable this on any Impl - crbug.com/1165751 ANGLE_FEATURE_CONDITION((&mFrontendFeatures), scalarizeVecAndMatConstructorArgs, false); // Disabled by default. To reduce the risk, create a feature to enable // compressing pipeline cache in multi-thread pool. ANGLE_FEATURE_CONDITION(&mFrontendFeatures, enableCompressingPipelineCacheInThreadPool, false); // Disabled by default until work on the extension is complete - anglebug.com/7279. ANGLE_FEATURE_CONDITION(&mFrontendFeatures, emulatePixelLocalStorage, false); mImplementation->initializeFrontendFeatures(&mFrontendFeatures); rx::ApplyFeatureOverrides(&mFrontendFeatures, mState); } const DisplayExtensions &Display::getExtensions() const { return mDisplayExtensions; } const std::string &Display::getExtensionString() const { return mDisplayExtensionString; } const std::string &Display::getVendorString() const { return mVendorString; } const std::string &Display::getVersionString() const { return mVersionString; } const std::string &Display::getClientAPIString() const { return mClientAPIString; } std::string Display::getBackendRendererDescription() const { return mImplementation->getRendererDescription(); } std::string Display::getBackendVendorString() const { return mImplementation->getVendorString(); } std::string Display::getBackendVersionString(bool includeFullVersion) const { return mImplementation->getVersionString(includeFullVersion); } Device *Display::getDevice() const { return mDevice; } Surface *Display::getWGLSurface() const { return mSurface; } gl::Version Display::getMaxSupportedESVersion() const { return mImplementation->getMaxSupportedESVersion(); } EGLint Display::programCacheGetAttrib(EGLenum attrib) const { switch (attrib) { case EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE: return static_cast(BlobCache::kKeyLength); case EGL_PROGRAM_CACHE_SIZE_ANGLE: return static_cast(mMemoryProgramCache.entryCount()); default: UNREACHABLE(); return 0; } } Error Display::programCacheQuery(EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize) { ASSERT(index >= 0 && index < static_cast(mMemoryProgramCache.entryCount())); const BlobCache::Key *programHash = nullptr; BlobCache::Value programBinary; // TODO(jmadill): Make this thread-safe. bool result = mMemoryProgramCache.getAt(static_cast(index), &programHash, &programBinary); if (!result) { return EglBadAccess() << "Program binary not accessible."; } ASSERT(keysize && binarysize); if (key) { ASSERT(*keysize == static_cast(BlobCache::kKeyLength)); memcpy(key, programHash->data(), BlobCache::kKeyLength); } if (binary) { // Note: we check the size here instead of in the validation code, since we need to // access the cache as atomically as possible. It's possible that the cache contents // could change between the validation size check and the retrieval. if (programBinary.size() > static_cast(*binarysize)) { return EglBadAccess() << "Program binary too large or changed during access."; } memcpy(binary, programBinary.data(), programBinary.size()); } *binarysize = static_cast(programBinary.size()); *keysize = static_cast(BlobCache::kKeyLength); return NoError(); } Error Display::programCachePopulate(const void *key, EGLint keysize, const void *binary, EGLint binarysize) { ASSERT(keysize == static_cast(BlobCache::kKeyLength)); BlobCache::Key programHash; memcpy(programHash.data(), key, BlobCache::kKeyLength); if (!mMemoryProgramCache.putBinary(programHash, reinterpret_cast(binary), static_cast(binarysize))) { return EglBadAccess() << "Failed to copy program binary into the cache."; } return NoError(); } EGLint Display::programCacheResize(EGLint limit, EGLenum mode) { switch (mode) { case EGL_PROGRAM_CACHE_RESIZE_ANGLE: { size_t initialSize = mMemoryProgramCache.size(); mMemoryProgramCache.resize(static_cast(limit)); return static_cast(initialSize); } case EGL_PROGRAM_CACHE_TRIM_ANGLE: return static_cast(mMemoryProgramCache.trim(static_cast(limit))); default: UNREACHABLE(); return 0; } } void Display::overrideFrontendFeatures(const std::vector &featureNames, bool enabled) { mFrontendFeatures.overrideFeatures(featureNames, enabled); } const char *Display::queryStringi(const EGLint name, const EGLint index) { const char *result = nullptr; switch (name) { case EGL_FEATURE_NAME_ANGLE: result = mFeatures[index]->name; break; case EGL_FEATURE_CATEGORY_ANGLE: result = angle::FeatureCategoryToString(mFeatures[index]->category); break; case EGL_FEATURE_DESCRIPTION_ANGLE: result = mFeatures[index]->description; break; case EGL_FEATURE_BUG_ANGLE: result = mFeatures[index]->bug; break; case EGL_FEATURE_STATUS_ANGLE: result = angle::FeatureStatusToString(mFeatures[index]->enabled); break; case EGL_FEATURE_CONDITION_ANGLE: result = mFeatures[index]->condition; break; default: UNREACHABLE(); return nullptr; } return result; } EGLAttrib Display::queryAttrib(const EGLint attribute) { EGLAttrib value = 0; switch (attribute) { case EGL_DEVICE_EXT: value = reinterpret_cast(mDevice); break; case EGL_FEATURE_COUNT_ANGLE: value = mFeatures.size(); break; default: UNREACHABLE(); } return value; } angle::ScratchBuffer Display::requestScratchBuffer() { return requestScratchBufferImpl(&mScratchBuffers); } void Display::returnScratchBuffer(angle::ScratchBuffer scratchBuffer) { returnScratchBufferImpl(std::move(scratchBuffer), &mScratchBuffers); } angle::ScratchBuffer Display::requestZeroFilledBuffer() { return requestScratchBufferImpl(&mZeroFilledBuffers); } void Display::returnZeroFilledBuffer(angle::ScratchBuffer zeroFilledBuffer) { returnScratchBufferImpl(std::move(zeroFilledBuffer), &mZeroFilledBuffers); } angle::ScratchBuffer Display::requestScratchBufferImpl( std::vector *bufferVector) { std::lock_guard lock(mScratchBufferMutex); if (!bufferVector->empty()) { angle::ScratchBuffer buffer = std::move(bufferVector->back()); bufferVector->pop_back(); return buffer; } return angle::ScratchBuffer(kScratchBufferLifetime); } void Display::returnScratchBufferImpl(angle::ScratchBuffer scratchBuffer, std::vector *bufferVector) { std::lock_guard lock(mScratchBufferMutex); bufferVector->push_back(std::move(scratchBuffer)); } Error Display::handleGPUSwitch() { ANGLE_TRY(mImplementation->handleGPUSwitch()); initVendorString(); return NoError(); } Error Display::forceGPUSwitch(EGLint gpuIDHigh, EGLint gpuIDLow) { ANGLE_TRY(mImplementation->forceGPUSwitch(gpuIDHigh, gpuIDLow)); initVendorString(); return NoError(); } bool Display::supportsDmaBufFormat(EGLint format) const { return mImplementation->supportsDmaBufFormat(format); } Error Display::queryDmaBufFormats(EGLint max_formats, EGLint *formats, EGLint *num_formats) { ANGLE_TRY(mImplementation->queryDmaBufFormats(max_formats, formats, num_formats)); return NoError(); } Error Display::queryDmaBufModifiers(EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers) { ANGLE_TRY(mImplementation->queryDmaBufModifiers(format, max_modifiers, modifiers, external_only, num_modifiers)); return NoError(); } } // namespace egl