diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/libANGLE/Display.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/Display.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/Display.cpp | 2506 |
1 files changed, 2506 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/Display.cpp b/gfx/angle/checkout/src/libANGLE/Display.cpp new file mode 100644 index 0000000000..4b7ced36fb --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/Display.cpp @@ -0,0 +1,2506 @@ +// +// 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 <algorithm> +#include <iterator> +#include <sstream> +#include <vector> + +#include <EGL/eglext.h> +#include <platform/PlatformMethods.h> + +#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 <versionhelpers.h> + +# 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<EGLNativeWindowType, Surface *, kWindowSurfaceMapSize> + 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<WindowSurfaceMap> 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<ANGLEPlatformDisplay, Display *, kANGLEPlatformDisplayMapSize> + ANGLEPlatformDisplayMap; +static ANGLEPlatformDisplayMap *GetANGLEPlatformDisplayMap() +{ + static angle::base::NoDestructor<ANGLEPlatformDisplayMap> displays; + return displays.get(); +} + +static constexpr size_t kDevicePlatformDisplayMapSize = 8; +typedef angle::FlatUnorderedMap<Device *, Display *, kDevicePlatformDisplayMapSize> + DevicePlatformDisplayMap; +static DevicePlatformDisplayMap *GetDevicePlatformDisplayMap() +{ + static angle::base::NoDestructor<DevicePlatformDisplayMap> 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<std::string> EGLStringArrayToStringVector(const char **ary) +{ + std::vector<std::string> 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<const angle::PlatformMethods *>( + mAttributeMap.get(EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX, 0)); + if (platformMethods != nullptr) + { + *ANGLEPlatformCurrent() = *platformMethods; + } + else + { + ANGLESetDefaultDisplayPlatform(this); + } + + const char **featuresForceEnabled = + reinterpret_cast<const char **>(mAttributeMap.get(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE, 0)); + const char **featuresForceDisabled = + reinterpret_cast<const char **>(mAttributeMap.get(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE, 0)); + mState.featureOverridesEnabled = EGLStringArrayToStringVector(featuresForceEnabled); + mState.featureOverridesDisabled = EGLStringArrayToStringVector(featuresForceDisabled); + mState.featuresAllDisabled = + static_cast<bool>(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<rx::DeviceImpl> 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<const Config *> Display::getConfigs(const egl::AttributeMap &attribs) const +{ + return mConfigSet.filter(attribs); +} + +std::vector<const Config *> 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<Image, Display> 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<egl::Sync, Display> 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<std::mutex> 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<gl::Context> 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<gl::Context *>(context)) != mState.contextSet.end(); +} + +bool Display::isValidSurface(const Surface *surface) const +{ + return mState.surfaceSet.find(const_cast<Surface *>(surface)) != mState.surfaceSet.end(); +} + +bool Display::isValidImage(const Image *image) const +{ + return mImageSet.find(const_cast<Image *>(image)) != mImageSet.end(); +} + +bool Display::isValidStream(const Stream *stream) const +{ + return mStreamSet.find(const_cast<Stream *>(stream)) != mStreamSet.end(); +} + +bool Display::isValidSync(const Sync *sync) const +{ + return mSyncSet.find(const_cast<Sync *>(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 <typename T> +static std::string GenerateExtensionsString(const T &extensions) +{ + std::vector<std::string> extensionsVector = extensions.getStrings(); + + std::ostringstream stream; + std::copy(extensionsVector.begin(), extensionsVector.end(), + std::ostream_iterator<std::string>(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<std::string> 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<EGLint>(BlobCache::kKeyLength); + + case EGL_PROGRAM_CACHE_SIZE_ANGLE: + return static_cast<EGLint>(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<EGLint>(mMemoryProgramCache.entryCount())); + + const BlobCache::Key *programHash = nullptr; + BlobCache::Value programBinary; + // TODO(jmadill): Make this thread-safe. + bool result = + mMemoryProgramCache.getAt(static_cast<size_t>(index), &programHash, &programBinary); + if (!result) + { + return EglBadAccess() << "Program binary not accessible."; + } + + ASSERT(keysize && binarysize); + + if (key) + { + ASSERT(*keysize == static_cast<EGLint>(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<size_t>(*binarysize)) + { + return EglBadAccess() << "Program binary too large or changed during access."; + } + + memcpy(binary, programBinary.data(), programBinary.size()); + } + + *binarysize = static_cast<EGLint>(programBinary.size()); + *keysize = static_cast<EGLint>(BlobCache::kKeyLength); + + return NoError(); +} + +Error Display::programCachePopulate(const void *key, + EGLint keysize, + const void *binary, + EGLint binarysize) +{ + ASSERT(keysize == static_cast<EGLint>(BlobCache::kKeyLength)); + + BlobCache::Key programHash; + memcpy(programHash.data(), key, BlobCache::kKeyLength); + + if (!mMemoryProgramCache.putBinary(programHash, reinterpret_cast<const uint8_t *>(binary), + static_cast<size_t>(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<size_t>(limit)); + return static_cast<EGLint>(initialSize); + } + + case EGL_PROGRAM_CACHE_TRIM_ANGLE: + return static_cast<EGLint>(mMemoryProgramCache.trim(static_cast<size_t>(limit))); + + default: + UNREACHABLE(); + return 0; + } +} + +void Display::overrideFrontendFeatures(const std::vector<std::string> &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<EGLAttrib>(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<angle::ScratchBuffer> *bufferVector) +{ + std::lock_guard<std::mutex> 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<angle::ScratchBuffer> *bufferVector) +{ + std::lock_guard<std::mutex> 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 |