Edit

kc3-lang/angle/src/libANGLE/Display.cpp

Branch :

  • Show log

    Commit

  • Author : Ian Elliott
    Date : 2019-03-12 16:23:55
    Hash : 4e87659e
    Message : Fix eglChooseConfig for attributes that have exact default values. The implementation of eglChooseConfig does not match the specification for attributes not provided by the calling function. It is supposed to use the default value and match semantics, per the specification. This is fine for many attributes (where the value in the EGLConfig doesn't matter). Currently, this affects the following attributes: - EGL_COLOR_BUFFER_TYPE - EGL_LEVEL - EGL_RENDERABLE_TYPE - EGL_SURFACE_TYPE - EGL_TRANSPARENT_TYPE - EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE - EGL_COLOR_COMPONENT_TYPE_EXT This change causes 55 of 65 of the dEQP-EGL.functional.choose_config.* tests to start passing. Bug: angleproject:3172 Change-Id: I287af5ba7d296694d9a78ded5d1e3bc4e7043d03 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1506696 Commit-Queue: Ian Elliott <ianelliott@google.com> Reviewed-by: Yuly Novikov <ynovikov@chromium.org>

  • src/libANGLE/Display.cpp
  • //
    // Copyright (c) 2002-2014 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 <map>
    #include <sstream>
    #include <vector>
    
    #include <EGL/eglext.h>
    #include <platform/Platform.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/utilities.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/histogram_macros.h"
    #include "libANGLE/renderer/DeviceImpl.h"
    #include "libANGLE/renderer/DisplayImpl.h"
    #include "libANGLE/renderer/ImageImpl.h"
    #include "third_party/trace_event/trace_event.h"
    
    #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
    #    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_USE_X11)
    #        include "libANGLE/renderer/gl/glx/DisplayGLX.h"
    #    elif defined(ANGLE_PLATFORM_APPLE)
    #        include "libANGLE/renderer/gl/cgl/DisplayCGL.h"
    #    elif defined(ANGLE_USE_OZONE)
    #        include "libANGLE/renderer/gl/egl/ozone/DisplayOzone.h"
    #    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)
    #    if defined(ANGLE_PLATFORM_WINDOWS)
    #        include "libANGLE/renderer/vulkan/win32/DisplayVkWin32.h"
    #    elif defined(ANGLE_PLATFORM_LINUX)
    #        include "libANGLE/renderer/vulkan/xcb/DisplayVkXcb.h"
    #    elif defined(ANGLE_PLATFORM_ANDROID)
    #        include "libANGLE/renderer/vulkan/android/DisplayVkAndroid.h"
    #    elif defined(ANGLE_PLATFORM_FUCHSIA)
    #        include "libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.h"
    #    else
    #        error Unsupported Vulkan platform.
    #    endif
    #endif  // defined(ANGLE_ENABLE_VULKAN)
    
    namespace egl
    {
    
    namespace
    {
    
    typedef std::map<EGLNativeWindowType, Surface *> WindowSurfaceMap;
    // Get a map of all EGL window surfaces to validate that no window has more than one EGL surface
    // associated with it.
    static WindowSurfaceMap *GetWindowSurfaces()
    {
        static WindowSurfaceMap windowSurfaces;
        return &windowSurfaces;
    }
    
    typedef std::map<EGLNativeDisplayType, Display *> ANGLEPlatformDisplayMap;
    static ANGLEPlatformDisplayMap *GetANGLEPlatformDisplayMap()
    {
        static ANGLEPlatformDisplayMap displays;
        return &displays;
    }
    
    typedef std::map<Device *, Display *> DevicePlatformDisplayMap;
    static DevicePlatformDisplayMap *GetDevicePlatformDisplayMap()
    {
        static DevicePlatformDisplayMap displays;
        return &displays;
    }
    
    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.
    EGLAttrib GetDisplayTypeFromEnvironment()
    {
        std::string angleDefaultEnv = angle::GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM");
        angle::ToLower(&angleDefaultEnv);
    
    #if defined(ANGLE_ENABLE_VULKAN)
        if (angleDefaultEnv == "vulkan")
        {
            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
    
        return EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
    }
    
    rx::DisplayImpl *CreateDisplayFromAttribs(const AttributeMap &attribMap, const DisplayState &state)
    {
        rx::DisplayImpl *impl = nullptr;
        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();
        }
    
        switch (displayType)
        {
            case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
    #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
                // Default to D3D displays
                impl = new rx::DisplayD3D(state);
    #elif defined(ANGLE_USE_X11)
                impl = new rx::DisplayGLX(state);
    #elif defined(ANGLE_PLATFORM_APPLE)
                impl = new rx::DisplayCGL(state);
    #elif defined(ANGLE_PLATFORM_FUCHSIA)
                impl = new rx::DisplayVkFuchsia(state);
    #elif defined(ANGLE_USE_OZONE)
                impl = new rx::DisplayOzone(state);
    #elif defined(ANGLE_PLATFORM_ANDROID)
    #    if defined(ANGLE_ENABLE_VULKAN)
                impl = new rx::DisplayVkAndroid(state);
    #    else
                impl = new rx::DisplayAndroid(state);
    #    endif
    #else
                // No display available
                UNREACHABLE();
    #endif
                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);
    #else
                // A D3D display was requested on a platform that doesn't support it
                UNREACHABLE();
    #endif
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
    #if defined(ANGLE_ENABLE_OPENGL)
    #    if defined(ANGLE_PLATFORM_WINDOWS)
                impl = new rx::DisplayWGL(state);
    #    elif defined(ANGLE_USE_X11)
                impl = new rx::DisplayGLX(state);
    #    elif defined(ANGLE_PLATFORM_APPLE)
                impl = new rx::DisplayCGL(state);
    #    elif defined(ANGLE_USE_OZONE)
                // This might work but has never been tried, so disallow for now.
                impl = nullptr;
    #    elif defined(ANGLE_PLATFORM_ANDROID)
                // No GL support on this platform, fail display creation.
                impl = nullptr;
    #    else
    #        error Unsupported OpenGL platform.
    #    endif
    #else
                // No display available
                UNREACHABLE();
    #endif  // defined(ANGLE_ENABLE_OPENGL)
                break;
    
            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_USE_X11)
                impl = new rx::DisplayGLX(state);
    #    elif defined(ANGLE_USE_OZONE)
                impl = new rx::DisplayOzone(state);
    #    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_PLATFORM_WINDOWS)
                impl = new rx::DisplayVkWin32(state);
    #    elif defined(ANGLE_PLATFORM_LINUX)
                impl = new rx::DisplayVkXcb(state);
    #    elif defined(ANGLE_PLATFORM_ANDROID)
                impl = new rx::DisplayVkAndroid(state);
    #    elif defined(ANGLE_PLATFORM_FUCHSIA)
                impl = new rx::DisplayVkFuchsia(state);
    #    else
    #        error Unsupported Vulkan platform.
    #    endif
    #else
                // No display available
                UNREACHABLE();
    #endif  // defined(ANGLE_ENABLE_VULKAN)
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE:
    #if defined(ANGLE_ENABLE_NULL)
                impl = new rx::DisplayNULL(state);
    #else
                // No display available
                UNREACHABLE();
    #endif  // defined(ANGLE_ENABLE_NULL)
                break;
    
            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
        // gl::Trace(gl::LOG_WARN, infoMessage);
    }
    
    void ANGLESetDefaultDisplayPlatform(angle::EGLDisplayType display)
    {
        angle::PlatformMethods *platformMethods = ANGLEPlatformCurrent();
        if (platformMethods->logError != angle::DefaultLogError)
        {
            // Don't reset pre-set Platform to Default
            return;
        }
    
        ANGLEResetDisplayPlatform(display);
        platformMethods->logError   = Display_logError;
        platformMethods->logWarning = Display_logWarning;
        platformMethods->logInfo    = Display_logInfo;
    }
    
    }  // anonymous namespace
    
    DisplayState::DisplayState() : label(nullptr) {}
    
    DisplayState::~DisplayState() {}
    
    // static
    Display *Display::GetDisplayFromNativeDisplay(EGLNativeDisplayType nativeDisplay,
                                                  const AttributeMap &attribMap)
    {
        Display *display = nullptr;
    
        ANGLEPlatformDisplayMap *displays = GetANGLEPlatformDisplayMap();
        const auto &iter                  = displays->find(nativeDisplay);
        if (iter != displays->end())
        {
            display = iter->second;
        }
    
        if (display == nullptr)
        {
            // Validate the native display
            if (!Display::isValidNativeDisplay(nativeDisplay))
            {
                return nullptr;
            }
    
            display = new Display(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, nullptr);
            displays->insert(std::make_pair(nativeDisplay, display));
        }
    
        // Apply new attributes if the display is not initialized yet.
        if (!display->isInitialized())
        {
            rx::DisplayImpl *impl = CreateDisplayFromAttribs(attribMap, display->getState());
            if (impl == nullptr)
            {
                // No valid display implementation for these attributes
                return nullptr;
            }
    
            display->setAttributes(impl, attribMap);
        }
    
        return display;
    }
    
    // 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())
        {
            rx::DisplayImpl *impl = CreateDisplayFromDevice(device, display->getState());
            display->setAttributes(impl, attribMap);
        }
    
        return display;
    }
    
    Display::Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice)
        : mImplementation(nullptr),
          mDisplayId(displayId),
          mAttributeMap(),
          mConfigSet(),
          mContextSet(),
          mStreamSet(),
          mInitialized(false),
          mDeviceLost(false),
          mCaps(),
          mDisplayExtensions(),
          mDisplayExtensionString(),
          mVendorString(),
          mDevice(eglDevice),
          mPlatform(platform),
          mTextureManager(nullptr),
          mBlobCache(gl::kDefaultMaxProgramCacheMemoryBytes),
          mMemoryProgramCache(mBlobCache),
          mGlobalTextureShareGroupUsers(0)
    {}
    
    Display::~Display()
    {
        // TODO(jmadill): When is this called?
        // terminate();
    
        if (mPlatform == EGL_PLATFORM_ANGLE_ANGLE)
        {
            ANGLEPlatformDisplayMap *displays      = GetANGLEPlatformDisplayMap();
            ANGLEPlatformDisplayMap::iterator iter = displays->find(mDisplayId);
            if (iter != displays->end())
            {
                displays->erase(iter);
            }
        }
        else if (mPlatform == EGL_PLATFORM_DEVICE_EXT)
        {
            DevicePlatformDisplayMap *displays      = GetDevicePlatformDisplayMap();
            DevicePlatformDisplayMap::iterator iter = displays->find(mDevice);
            if (iter != displays->end())
            {
                displays->erase(iter);
            }
        }
        else
        {
            UNREACHABLE();
        }
    
        SafeDelete(mDevice);
        SafeDelete(mImplementation);
    }
    
    void Display::setLabel(EGLLabelKHR label)
    {
        mState.label = label;
    }
    
    EGLLabelKHR Display::getLabel() const
    {
        return mState.label;
    }
    
    void Display::setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap)
    {
        ASSERT(!mInitialized);
    
        ASSERT(impl != nullptr);
        SafeDelete(mImplementation);
        mImplementation = impl;
    
        mAttributeMap = attribMap;
    }
    
    Error Display::initialize()
    {
        mImplementation->setBlobCache(&mBlobCache);
    
        // TODO(jmadill): Store Platform in Display and init here.
        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);
        }
    
        gl::InitializeDebugAnnotations(&mAnnotator);
    
        gl::InitializeDebugMutexIfNeeded();
    
        SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.DisplayInitializeMS");
        TRACE_EVENT0("gpu.angle", "egl::Display::initialize");
    
        ASSERT(mImplementation != nullptr);
    
        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();
        }
    
        // 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;
        }
    
        initDisplayExtensions();
        initVendorString();
    
        // Populate the Display's EGLDeviceEXT if the Display wasn't created using one
        if (mPlatform != EGL_PLATFORM_DEVICE_EXT)
        {
            if (mDisplayExtensions.deviceQuery)
            {
                std::unique_ptr<rx::DeviceImpl> impl(mImplementation->createDevice());
                ASSERT(impl != nullptr);
                error = impl->initialize();
                if (error.isError())
                {
                    ERR() << "Failed to initialize display because device creation failed: "
                          << error.getMessage();
                    mImplementation->terminate();
                    return error;
                }
                mDevice = new Device(this, impl.release());
            }
            else
            {
                mDevice = nullptr;
            }
        }
        else
        {
            // For EGL_PLATFORM_DEVICE_EXT, mDevice should always be populated using
            // an external device
            ASSERT(mDevice != nullptr);
        }
    
        mInitialized = true;
    
        return NoError();
    }
    
    Error Display::terminate(const Thread *thread)
    {
        if (!mInitialized)
        {
            return NoError();
        }
    
        mMemoryProgramCache.clear();
        mBlobCache.setBlobCacheFuncs(nullptr, nullptr);
    
        while (!mContextSet.empty())
        {
            ANGLE_TRY(destroyContext(thread, *mContextSet.begin()));
        }
    
        ANGLE_TRY(makeCurrent(nullptr, nullptr, nullptr));
    
        // The global texture manager should be deleted with the last context that uses it.
        ASSERT(mGlobalTextureShareGroupUsers == 0 && mTextureManager == nullptr);
    
        while (!mImageSet.empty())
        {
            destroyImage(*mImageSet.begin());
        }
    
        while (!mStreamSet.empty())
        {
            destroyStream(*mStreamSet.begin());
        }
    
        while (!mSyncSet.empty())
        {
            destroySync(*mSyncSet.begin());
        }
    
        while (!mState.surfaceSet.empty())
        {
            ANGLE_TRY(destroySurface(*mState.surfaceSet.begin()));
        }
    
        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();
    }
    
    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),
                               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));
    
        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), 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), 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),
                               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,
                                 const gl::Context *shareContext,
                                 const AttributeMap &attribs,
                                 gl::Context **outContext)
    {
        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;
        }
    
        gl::MemoryProgramCache *cachePointer = &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 =
            mAttributeMap.contains(EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE);
        if (usesProgramCacheControl)
        {
            bool programCacheControlEnabled =
                mAttributeMap.get(EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE, GL_FALSE);
            // A program cache size of zero indicates it should be disabled.
            if (!programCacheControlEnabled || mMemoryProgramCache.maxSize() == 0)
            {
                cachePointer = nullptr;
            }
        }
    
        gl::Context *context =
            new gl::Context(mImplementation, configuration, shareContext, shareTextures, cachePointer,
                            attribs, mDisplayExtensions, GetClientExtensions());
    
        ASSERT(context != nullptr);
        mContextSet.insert(context);
    
        ASSERT(outContext != nullptr);
        *outContext = context;
        return NoError();
    }
    
    Error Display::createSync(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));
    
        Sync *sync = syncPtr.release();
    
        sync->addRef();
        mSyncSet.insert(sync);
    
        *outSync = sync;
        return NoError();
    }
    
    Error Display::makeCurrent(egl::Surface *drawSurface,
                               egl::Surface *readSurface,
                               gl::Context *context)
    {
        ANGLE_TRY(mImplementation->makeCurrent(drawSurface, readSurface, context));
    
        if (context != nullptr)
        {
            ASSERT(readSurface == drawSurface);
            ANGLE_TRY(context->makeCurrent(this, drawSurface));
        }
    
        return NoError();
    }
    
    Error Display::restoreLostDevice()
    {
        for (ContextSet::iterator ctx = mContextSet.begin(); ctx != mContextSet.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::destroySurface(Surface *surface)
    {
        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);
        }
    
        mState.surfaceSet.erase(surface);
        ANGLE_TRY(surface->onDestroy(this));
        return NoError();
    }
    
    void Display::destroyImage(egl::Image *image)
    {
        auto iter = mImageSet.find(image);
        ASSERT(iter != mImageSet.end());
        (*iter)->release(this);
        mImageSet.erase(iter);
    }
    
    void Display::destroyStream(egl::Stream *stream)
    {
        mStreamSet.erase(stream);
        SafeDelete(stream);
    }
    
    Error Display::destroyContext(const Thread *thread, gl::Context *context)
    {
        gl::Context *currentContext   = thread->getContext();
        bool changeContextForDeletion = context != currentContext;
    
        // Make the context being deleted current during it's deletion.  This allows it to delete any
        // resources it's holding.
        if (changeContextForDeletion)
        {
            ANGLE_TRY(makeCurrent(nullptr, nullptr, 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--;
        }
    
        ANGLE_TRY(context->onDestroy(this));
        mContextSet.erase(context);
        SafeDelete(context);
    
        // Set the previous context back to current
        if (changeContextForDeletion)
        {
            ANGLE_TRY(makeCurrent(thread->getCurrentDrawSurface(), thread->getCurrentReadSurface(),
                                  currentContext));
        }
    
        return NoError();
    }
    
    void Display::destroySync(egl::Sync *sync)
    {
        auto iter = mSyncSet.find(sync);
        ASSERT(iter != mSyncSet.end());
        (*iter)->release(this);
        mSyncSet.erase(iter);
    }
    
    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 = mContextSet.begin(); context != mContextSet.end();
             context++)
        {
            (*context)->markContextLost();
        }
    
        mDeviceLost = true;
    }
    
    void Display::setBlobCacheFuncs(EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get)
    {
        mBlobCache.setBlobCacheFuncs(set, get);
        mImplementation->setBlobCacheFuncs(set, get);
    }
    
    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 mContextSet.find(const_cast<gl::Context *>(context)) != mContextSet.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_ENABLE_OPENGL)
        extensions.platformANGLEOpenGL = true;
    
        // Selecting context virtualization is currently only supported in the OpenGL backend.
        extensions.platformANGLEContextVirtualization = 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;
    #endif
    
    #if defined(ANGLE_USE_X11)
        extensions.x11Visual = true;
    #endif
    
        extensions.clientGetAllProcAddresses = true;
        extensions.debug                     = true;
        extensions.explicitContext           = 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 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               = true;
        mDisplayExtensions.createContextWebGLCompatibility    = true;
        mDisplayExtensions.createContextBindGeneratesResource = true;
        mDisplayExtensions.createContextClientArrays          = true;
        mDisplayExtensions.pixelFormatFloat                   = true;
    
        // Force EGL_KHR_get_all_proc_addresses on.
        mDisplayExtensions.getAllProcAddresses = true;
    
        // Enable program cache control since it is not back-end dependent.
        mDisplayExtensions.programCacheControl = 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;
    
        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);
    }
    
    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_STORE)
        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 = mImplementation->getVendorString();
    }
    
    const DisplayExtensions &Display::getExtensions() const
    {
        return mDisplayExtensions;
    }
    
    const std::string &Display::getExtensionString() const
    {
        return mDisplayExtensionString;
    }
    
    const std::string &Display::getVendorString() const
    {
        return mVendorString;
    }
    
    Device *Display::getDevice() const
    {
        return mDevice;
    }
    
    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);
    
        mMemoryProgramCache.putBinary(programHash, reinterpret_cast<const uint8_t *>(binary),
                                      static_cast<size_t>(binarysize));
        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;
        }
    }
    
    Error Display::clientWaitSync(Sync *sync, EGLint flags, EGLTime timeout, EGLint *outResult)
    {
        return sync->clientWait(this, flags, timeout, outResult);
    }
    
    Error Display::waitSync(Sync *sync, EGLint flags)
    {
        return sync->serverWait(this, flags);
    }
    
    }  // namespace egl