Edit

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

Branch :

  • Show log

    Commit

  • Author : Jeff Vigil
    Date : 2021-03-11 09:22:33
    Hash : cef5b717
    Message : EGL: Add attribute check in ValidateQueryContext Implementation of EGL_IMG_context_priority was missing attribute check in ValidateQueryContext Add test case. Test: angle_end2end_test --gtest_filter=EGLCreateContextAttribsTest Bug: angleproject:5746 Change-Id: Idf5df29fcb343d969a031675b6c5985a9be0a705 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2752891 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/validationEGL.cpp
  • //
    // Copyright 2016 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.
    //
    
    // validationEGL.cpp: Validation functions for generic EGL entry point parameters
    
    #include "libANGLE/validationEGL.h"
    
    #include "common/utilities.h"
    #include "libANGLE/Config.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Device.h"
    #include "libANGLE/Display.h"
    #include "libANGLE/EGLSync.h"
    #include "libANGLE/Image.h"
    #include "libANGLE/Stream.h"
    #include "libANGLE/Surface.h"
    #include "libANGLE/Texture.h"
    #include "libANGLE/Thread.h"
    #include "libANGLE/formatutils.h"
    
    #include <EGL/eglext.h>
    
    namespace egl
    {
    namespace
    {
    size_t GetMaximumMipLevel(const gl::Context *context, gl::TextureType type)
    {
        const gl::Caps &caps = context->getCaps();
    
        int maxDimension = 0;
        switch (type)
        {
            case gl::TextureType::_2D:
            case gl::TextureType::_2DArray:
            case gl::TextureType::_2DMultisample:
                maxDimension = caps.max2DTextureSize;
                break;
            case gl::TextureType::Rectangle:
                maxDimension = caps.maxRectangleTextureSize;
                break;
            case gl::TextureType::CubeMap:
                maxDimension = caps.maxCubeMapTextureSize;
                break;
            case gl::TextureType::_3D:
                maxDimension = caps.max3DTextureSize;
                break;
    
            default:
                UNREACHABLE();
        }
    
        return gl::log2(maxDimension);
    }
    
    bool TextureHasNonZeroMipLevelsSpecified(const gl::Context *context, const gl::Texture *texture)
    {
        size_t maxMip = GetMaximumMipLevel(context, texture->getType());
        for (size_t level = 1; level < maxMip; level++)
        {
            if (texture->getType() == gl::TextureType::CubeMap)
            {
                for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets())
                {
                    if (texture->getFormat(face, level).valid())
                    {
                        return true;
                    }
                }
            }
            else
            {
                if (texture->getFormat(gl::NonCubeTextureTypeToTarget(texture->getType()), level)
                        .valid())
                {
                    return true;
                }
            }
        }
    
        return false;
    }
    
    bool CubeTextureHasUnspecifiedLevel0Face(const gl::Texture *texture)
    {
        ASSERT(texture->getType() == gl::TextureType::CubeMap);
        for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets())
        {
            if (!texture->getFormat(face, 0).valid())
            {
                return true;
            }
        }
    
        return false;
    }
    
    bool ValidateStreamAttribute(const ValidationContext *val,
                                 const EGLAttrib attribute,
                                 const EGLAttrib value,
                                 const DisplayExtensions &extensions)
    {
        switch (attribute)
        {
            case EGL_STREAM_STATE_KHR:
            case EGL_PRODUCER_FRAME_KHR:
            case EGL_CONSUMER_FRAME_KHR:
                val->setError(EGL_BAD_ACCESS, "Attempt to initialize readonly parameter");
                return false;
            case EGL_CONSUMER_LATENCY_USEC_KHR:
                // Technically not in spec but a latency < 0 makes no sense so we check it
                if (value < 0)
                {
                    val->setError(EGL_BAD_PARAMETER, "Latency must be positive");
                    return false;
                }
                break;
            case EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR:
                if (!extensions.streamConsumerGLTexture)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Consumer GL extension not enabled");
                    return false;
                }
                // Again not in spec but it should be positive anyways
                if (value < 0)
                {
                    val->setError(EGL_BAD_PARAMETER, "Timeout must be positive");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid stream attribute");
                return false;
        }
        return true;
    }
    
    bool ValidateCreateImageMipLevelCommon(const ValidationContext *val,
                                           const gl::Context *context,
                                           const gl::Texture *texture,
                                           EGLAttrib level)
    {
        // Note that the spec EGL_create_image spec does not explicitly specify an error
        // when the level is outside the base/max level range, but it does mention that the
        // level "must be a part of the complete texture object <buffer>". It can be argued
        // that out-of-range levels are not a part of the complete texture.
        const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
        if (level > 0 &&
            (!texture->isMipmapComplete() || static_cast<GLuint>(level) < effectiveBaseLevel ||
             static_cast<GLuint>(level) > texture->getTextureState().getMipmapMaxLevel()))
        {
            val->setError(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
            return false;
        }
    
        if (level == 0 && !texture->isMipmapComplete() &&
            TextureHasNonZeroMipLevelsSpecified(context, texture))
        {
            val->setError(EGL_BAD_PARAMETER,
                          "if level is zero and the texture is incomplete, it must "
                          "have no mip levels specified except zero.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateConfigAttribute(const ValidationContext *val,
                                 const Display *display,
                                 EGLAttrib attribute)
    {
        switch (attribute)
        {
            case EGL_BUFFER_SIZE:
            case EGL_ALPHA_SIZE:
            case EGL_BLUE_SIZE:
            case EGL_GREEN_SIZE:
            case EGL_RED_SIZE:
            case EGL_DEPTH_SIZE:
            case EGL_STENCIL_SIZE:
            case EGL_CONFIG_CAVEAT:
            case EGL_CONFIG_ID:
            case EGL_LEVEL:
            case EGL_NATIVE_RENDERABLE:
            case EGL_NATIVE_VISUAL_ID:
            case EGL_NATIVE_VISUAL_TYPE:
            case EGL_SAMPLES:
            case EGL_SAMPLE_BUFFERS:
            case EGL_SURFACE_TYPE:
            case EGL_TRANSPARENT_TYPE:
            case EGL_TRANSPARENT_BLUE_VALUE:
            case EGL_TRANSPARENT_GREEN_VALUE:
            case EGL_TRANSPARENT_RED_VALUE:
            case EGL_BIND_TO_TEXTURE_RGB:
            case EGL_BIND_TO_TEXTURE_RGBA:
            case EGL_MIN_SWAP_INTERVAL:
            case EGL_MAX_SWAP_INTERVAL:
            case EGL_LUMINANCE_SIZE:
            case EGL_ALPHA_MASK_SIZE:
            case EGL_COLOR_BUFFER_TYPE:
            case EGL_RENDERABLE_TYPE:
            case EGL_MATCH_NATIVE_PIXMAP:
            case EGL_CONFORMANT:
            case EGL_MAX_PBUFFER_WIDTH:
            case EGL_MAX_PBUFFER_HEIGHT:
            case EGL_MAX_PBUFFER_PIXELS:
                break;
    
            case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE:
                if (!display->getExtensions().surfaceOrientation)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EGL_ANGLE_surface_orientation is not enabled.");
                    return false;
                }
                break;
    
            case EGL_COLOR_COMPONENT_TYPE_EXT:
                if (!display->getExtensions().pixelFormatFloat)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EGL_EXT_pixel_format_float is not enabled.");
                    return false;
                }
                break;
    
            case EGL_RECORDABLE_ANDROID:
                if (!display->getExtensions().recordable)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EGL_ANDROID_recordable is not enabled.");
                    return false;
                }
                break;
    
            case EGL_FRAMEBUFFER_TARGET_ANDROID:
                if (!display->getExtensions().framebufferTargetANDROID)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EGL_ANDROID_framebuffer_target is not enabled.");
                    return false;
                }
                break;
    
            case EGL_BIND_TO_TEXTURE_TARGET_ANGLE:
                if (!display->getExtensions().iosurfaceClientBuffer)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_ANGLE_iosurface_client_buffer is not enabled.");
                    return false;
                }
                break;
    
            case EGL_Y_INVERTED_NOK:
                if (!display->getExtensions().textureFromPixmapNOK)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EGL_NOK_texture_from_pixmap is not enabled.");
                    return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Unknown attribute.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateConfigAttributeValue(const ValidationContext *val,
                                      const Display *display,
                                      EGLAttrib attribute,
                                      EGLAttrib value)
    {
        switch (attribute)
        {
    
            case EGL_BIND_TO_TEXTURE_RGB:
            case EGL_BIND_TO_TEXTURE_RGBA:
                switch (value)
                {
                    case EGL_DONT_CARE:
                    case EGL_TRUE:
                    case EGL_FALSE:
                        break;
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE, "EGL_bind_to_texture invalid attribute: 0x%X",
                                      static_cast<uint32_t>(value));
                        return false;
                }
                break;
    
            case EGL_COLOR_BUFFER_TYPE:
                switch (value)
                {
                    case EGL_RGB_BUFFER:
                    case EGL_LUMINANCE_BUFFER:
                    // EGL_DONT_CARE doesn't match the spec, but does match dEQP usage
                    case EGL_DONT_CARE:
                        break;
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_color_buffer_type invalid attribute: 0x%X",
                                      static_cast<uint32_t>(value));
                        return false;
                }
                break;
    
            case EGL_NATIVE_RENDERABLE:
                switch (value)
                {
                    case EGL_DONT_CARE:
                    case EGL_TRUE:
                    case EGL_FALSE:
                        break;
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_native_renderable invalid attribute: 0x%X",
                                      static_cast<uint32_t>(value));
                        return false;
                }
                break;
    
            case EGL_TRANSPARENT_TYPE:
                switch (value)
                {
                    case EGL_NONE:
                    case EGL_TRANSPARENT_RGB:
                    // EGL_DONT_CARE doesn't match the spec, but does match dEQP usage
                    case EGL_DONT_CARE:
                        break;
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE, "EGL_transparent_type invalid attribute: 0x%X",
                                      static_cast<uint32_t>(value));
                        return false;
                }
                break;
    
            case EGL_RECORDABLE_ANDROID:
                switch (value)
                {
                    case EGL_TRUE:
                    case EGL_FALSE:
                    case EGL_DONT_CARE:
                        break;
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_RECORDABLE_ANDROID invalid attribute: 0x%X",
                                      static_cast<uint32_t>(value));
                        return false;
                }
                break;
    
            case EGL_COLOR_COMPONENT_TYPE_EXT:
                switch (value)
                {
                    case EGL_COLOR_COMPONENT_TYPE_FIXED_EXT:
                    case EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT:
                    case EGL_DONT_CARE:
                        break;
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_COLOR_COMPONENT_TYPE_EXT invalid attribute: 0x%X",
                                      static_cast<uint32_t>(value));
                        return false;
                }
                break;
    
            default:
                break;
        }
    
        return true;
    }
    
    bool ValidateConfigAttributes(const ValidationContext *val,
                                  const Display *display,
                                  const AttributeMap &attributes)
    {
        for (const auto &attrib : attributes)
        {
            ANGLE_VALIDATION_TRY(ValidateConfigAttribute(val, display, attrib.first));
            ANGLE_VALIDATION_TRY(
                ValidateConfigAttributeValue(val, display, attrib.first, attrib.second));
        }
    
        return true;
    }
    
    bool ValidateColorspaceAttribute(const ValidationContext *val,
                                     const DisplayExtensions &displayExtensions,
                                     EGLAttrib colorSpace)
    {
        switch (colorSpace)
        {
            case EGL_GL_COLORSPACE_SRGB:
                break;
            case EGL_GL_COLORSPACE_LINEAR:
                break;
            case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
                if (!displayExtensions.glColorspaceDisplayP3Linear)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EXT_gl_colorspace_display_p3_linear is not available.");
                    return false;
                }
                break;
            case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
                if (!displayExtensions.glColorspaceDisplayP3)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EXT_gl_colorspace_display_p3 is not available.");
                    return false;
                }
                break;
            case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
                if (!displayExtensions.glColorspaceDisplayP3Passthrough)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_EXT_gl_colorspace_display_p3_passthrough is not available.");
                    return false;
                }
                break;
            case EGL_GL_COLORSPACE_SCRGB_EXT:
                if (!displayExtensions.glColorspaceScrgb)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "EXT_gl_colorspace_scrgb is not available.");
                    return false;
                }
                break;
            case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
                if (!displayExtensions.glColorspaceScrgbLinear)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EXT_gl_colorspace_scrgb_linear is not available.");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE);
                return false;
        }
        return true;
    }
    bool ValidatePlatformType(const ValidationContext *val,
                              const ClientExtensions &clientExtensions,
                              EGLAttrib platformType)
    {
        switch (platformType)
        {
            case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
            case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
                if (!clientExtensions.platformANGLED3D)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Direct3D platform is unsupported.");
                    return false;
                }
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
            case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
                if (!clientExtensions.platformANGLEOpenGL)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "OpenGL platform is unsupported.");
                    return false;
                }
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE:
                if (!clientExtensions.platformANGLENULL)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "Display type EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE "
                                  "requires EGL_ANGLE_platform_angle_null.");
                    return false;
                }
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
                if (!clientExtensions.platformANGLEVulkan)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Vulkan platform is unsupported.");
                    return false;
                }
                break;
    
            case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
                if (!clientExtensions.platformANGLEMetal)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Metal platform is unsupported.");
                    return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Unknown platform type.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateGetPlatformDisplayCommon(const ValidationContext *val,
                                          EGLenum platform,
                                          const void *native_display,
                                          const AttributeMap &attribMap)
    {
        const ClientExtensions &clientExtensions = Display::GetClientExtensions();
    
        switch (platform)
        {
            case EGL_PLATFORM_ANGLE_ANGLE:
                if (!clientExtensions.platformANGLE)
                {
                    val->setError(EGL_BAD_PARAMETER, "Platform ANGLE extension is not active");
                    return false;
                }
                break;
            case EGL_PLATFORM_DEVICE_EXT:
                if (!clientExtensions.platformDevice)
                {
                    val->setError(EGL_BAD_PARAMETER, "Platform Device extension is not active");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_CONFIG, "Bad platform type.");
                return false;
        }
    
        if (platform == EGL_PLATFORM_ANGLE_ANGLE)
        {
            EGLAttrib platformType       = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
            bool enableAutoTrimSpecified = false;
            bool enableD3D11on12         = false;
            bool presentPathSpecified    = false;
            bool luidSpecified           = false;
    
            Optional<EGLAttrib> majorVersion;
            Optional<EGLAttrib> minorVersion;
            Optional<EGLAttrib> deviceType;
            Optional<EGLAttrib> eglHandle;
    
            for (const auto &curAttrib : attribMap)
            {
                const EGLAttrib value = curAttrib.second;
    
                switch (curAttrib.first)
                {
                    case EGL_PLATFORM_ANGLE_TYPE_ANGLE:
                    {
                        ANGLE_VALIDATION_TRY(ValidatePlatformType(val, clientExtensions, value));
                        platformType = value;
                        break;
                    }
    
                    case EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE:
                        if (value != EGL_DONT_CARE)
                        {
                            majorVersion = value;
                        }
                        break;
    
                    case EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE:
                        if (value != EGL_DONT_CARE)
                        {
                            minorVersion = value;
                        }
                        break;
    
                    case EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE:
                        switch (value)
                        {
                            case EGL_TRUE:
                            case EGL_FALSE:
                                break;
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE, "Invalid automatic trim attribute");
                                return false;
                        }
                        enableAutoTrimSpecified = true;
                        break;
    
                    case EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE:
                        if (!clientExtensions.platformANGLED3D ||
                            !clientExtensions.platformANGLED3D11ON12)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE extension not active.");
                            return false;
                        }
    
                        switch (value)
                        {
                            case EGL_TRUE:
                            case EGL_FALSE:
                                break;
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE, "Invalid D3D11on12 attribute");
                                return false;
                        }
                        enableD3D11on12 = true;
                        break;
    
                    case EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE:
                        if (!clientExtensions.experimentalPresentPath)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "EGL_ANGLE_experimental_present_path extension not active");
                            return false;
                        }
    
                        switch (value)
                        {
                            case EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE:
                            case EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE:
                                break;
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE,
                                              "Invalid value for EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE");
                                return false;
                        }
                        presentPathSpecified = true;
                        break;
    
                    case EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE:
                        switch (value)
                        {
                            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE:
                            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
                                break;
    
                            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE:
                            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE:
                                if (!clientExtensions.platformANGLED3D)
                                {
                                    val->setError(EGL_BAD_ATTRIBUTE,
                                                  "EGL_ANGLE_platform_angle_d3d is not supported");
                                    return false;
                                }
                                break;
    
                            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE:
                                if (!clientExtensions.platformANGLEDeviceTypeEGLANGLE)
                                {
                                    val->setError(EGL_BAD_ATTRIBUTE,
                                                  "EGL_ANGLE_platform_angle_device_type_"
                                                  "egl_angle is not supported");
                                    return false;
                                }
                                break;
    
                            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
                                if (!clientExtensions.platformANGLEDeviceTypeSwiftShader)
                                {
                                    val->setError(EGL_BAD_ATTRIBUTE,
                                                  "EGL_ANGLE_platform_angle_device_type_"
                                                  "swiftshader is not supported");
                                    return false;
                                }
                                break;
    
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE,
                                              "Invalid value for "
                                              "EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE "
                                              "attrib");
                                return false;
                        }
                        deviceType = value;
                        break;
    
                    case EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE:
                        if (!clientExtensions.platformANGLE)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "EGL_ANGLE_platform_angle extension not active");
                            return false;
                        }
                        if (value != EGL_TRUE && value != EGL_FALSE && value != EGL_DONT_CARE)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE "
                                          "must be EGL_TRUE, EGL_FALSE, or "
                                          "EGL_DONT_CARE.");
                            return false;
                        }
                        break;
    
                    case EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE:
                        if (value != EGL_DONT_CARE)
                        {
                            eglHandle = value;
                        }
                        break;
    
                    case EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE:
                        if (!clientExtensions.platformANGLEContextVirtualization)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "EGL_ANGLE_platform_angle_context_"
                                          "virtualization extension not active");
                            return false;
                        }
    
                        switch (value)
                        {
                            case EGL_DONT_CARE:
                            case EGL_FALSE:
                            case EGL_TRUE:
                                break;
    
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE,
                                              "Invalid value for "
                                              "EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_"
                                              "ANGLE attrib");
                                return false;
                        }
                        break;
    
                    case EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE:
                    case EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE:
                        luidSpecified = true;
                        break;
                    case EGL_PLATFORM_ANGLE_DEVICE_CONTEXT_VOLATILE_EAGL_ANGLE:
                        // The property does not have an effect if it's not active, so do not check
                        // for non-support.
                        switch (value)
                        {
                            case EGL_FALSE:
                            case EGL_TRUE:
                                break;
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE,
                                              "Invalid value for "
                                              "EGL_PLATFORM_ANGLE_DEVICE_CONTEXT_VOLATILE_"
                                              "EAGL_ANGLE attrib");
                                return false;
                        }
                        break;
                    case EGL_PLATFORM_ANGLE_DEVICE_CONTEXT_VOLATILE_CGL_ANGLE:
                        // The property does not have an effect if it's not active, so do not check
                        // for non-support.
                        switch (value)
                        {
                            case EGL_FALSE:
                            case EGL_TRUE:
                                break;
                            default:
                                val->setError(EGL_BAD_ATTRIBUTE,
                                              "Invalid value for "
                                              "EGL_PLATFORM_ANGLE_DEVICE_CONTEXT_VOLATILE_"
                                              "CGL_ANGLE attrib");
                                return false;
                        }
                        break;
                    default:
                        break;
                }
            }
    
            if (!majorVersion.valid() && minorVersion.valid())
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "Must specify major version if you specify a minor version.");
                return false;
            }
    
            if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE &&
                platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE requires a "
                              "device type of EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.");
                return false;
            }
    
            if (enableAutoTrimSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE "
                              "requires a device type of "
                              "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.");
                return false;
            }
    
            if (enableD3D11on12)
            {
                if (platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE "
                                  "requires a platform type of "
                                  "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.");
                    return false;
                }
    
                if (deviceType.valid() && deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE &&
                    deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE requires a device "
                                  "type of EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE "
                                  "or EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE");
                    return false;
                }
            }
    
            if (presentPathSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE requires a "
                              "device type of EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.");
                return false;
            }
    
            if (luidSpecified)
            {
                if (platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE and "
                                  "EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE "
                                  "require a platform type of "
                                  "EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.");
                    return false;
                }
    
                if (attribMap.get(EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE, 0) == 0 &&
                    attribMap.get(EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE, 0) == 0)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "If either EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE "
                                  "and/or EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE are "
                                  "specified, at least one must non-zero.");
                    return false;
                }
            }
    
            if (deviceType.valid())
            {
                switch (deviceType.value())
                {
                    case EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE:
                    case EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE:
                        if (platformType != EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE &&
                            platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "This device type requires a "
                                          "platform type of EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE or "
                                          "EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE.");
                            return false;
                        }
                        break;
    
                    case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
                        if (platformType != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
                        {
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "This device type requires a "
                                          "platform type of EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE.");
                            return false;
                        }
                        break;
    
                    default:
                        break;
                }
            }
    
            if (platformType == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
            {
                if ((majorVersion.valid() && majorVersion.value() != 1) ||
                    (minorVersion.valid() && minorVersion.value() != 0))
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE currently "
                                  "only supports Vulkan 1.0.");
                    return false;
                }
            }
    
            if (eglHandle.valid() && platformType != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE &&
                platformType != EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE requires a "
                              "device type of EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE.");
                return false;
            }
        }
        else if (platform == EGL_PLATFORM_DEVICE_EXT)
        {
            const Device *eglDevice = static_cast<const Device *>(native_display);
            if (eglDevice == nullptr || !Device::IsValidDevice(eglDevice))
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "native_display should be a valid EGL device if "
                              "platform equals EGL_PLATFORM_DEVICE_EXT");
                return false;
            }
        }
        else
        {
            UNREACHABLE();
        }
    
        if (attribMap.contains(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE))
        {
            if (!clientExtensions.featureControlANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE, "EGL_ANGLE_feature_control is not supported");
                return false;
            }
            else if (attribMap.get(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE, 0) == 0)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_FEATURE_OVERRIDES_ENABLED_ANGLE must be a valid pointer");
                return false;
            }
        }
        if (attribMap.contains(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE))
        {
            if (!clientExtensions.featureControlANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE, "EGL_ANGLE_feature_control is not supported");
                return false;
            }
            else if (attribMap.get(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE, 0) == 0)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_FEATURE_OVERRIDES_DISABLED_ANGLE must be a valid pointer");
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateStream(const ValidationContext *val, const Display *display, const Stream *stream)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.stream)
        {
            val->setError(EGL_BAD_ACCESS, "Stream extension not active");
            return false;
        }
    
        if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
        {
            val->setError(EGL_BAD_STREAM_KHR, "Invalid stream");
            return false;
        }
    
        return true;
    }
    
    bool ValidateLabeledObject(const ValidationContext *val,
                               const Display *display,
                               ObjectType objectType,
                               EGLObjectKHR object,
                               LabeledObject **outLabeledObject)
    {
        switch (objectType)
        {
            case ObjectType::Context:
            {
                gl::Context *context = static_cast<gl::Context *>(object);
                ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
                *outLabeledObject = context;
                break;
            }
    
            case ObjectType::Display:
            {
                ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
                if (display != object)
                {
                    if (val)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "when object type is EGL_OBJECT_DISPLAY_KHR, the "
                                      "object must be the same as the display.");
                    }
                    return false;
                }
    
                *outLabeledObject = static_cast<Display *>(object);
                break;
            }
    
            case ObjectType::Image:
            {
                Image *image = static_cast<Image *>(object);
                ANGLE_VALIDATION_TRY(ValidateImage(val, display, image));
                *outLabeledObject = image;
                break;
            }
    
            case ObjectType::Stream:
            {
                Stream *stream = static_cast<Stream *>(object);
                ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
                *outLabeledObject = stream;
                break;
            }
    
            case ObjectType::Surface:
            {
                Surface *surface = static_cast<Surface *>(object);
                ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
                *outLabeledObject = surface;
                break;
            }
    
            case ObjectType::Sync:
            {
                Sync *sync = static_cast<Sync *>(object);
                ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
                *outLabeledObject = sync;
                break;
            }
    
            case ObjectType::Thread:
            {
                ASSERT(val);
                *outLabeledObject = val->eglThread;
                break;
            }
    
            default:
                if (val)
                {
                    val->setError(EGL_BAD_PARAMETER, "unknown object type.");
                }
                return false;
        }
    
        return true;
    }
    
    // This is a common sub-check of Display status that's shared by multiple functions
    bool ValidateDisplayPointer(const ValidationContext *val, const Display *display)
    {
        if (display == EGL_NO_DISPLAY)
        {
            if (val)
            {
                val->setError(EGL_BAD_DISPLAY, "display is EGL_NO_DISPLAY.");
            }
            return false;
        }
    
        if (!Display::isValidDisplay(display))
        {
            if (val)
            {
                val->setError(EGL_BAD_DISPLAY, "display is not a valid display.");
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidCompositorTimingName(CompositorTiming name)
    {
        switch (name)
        {
            case CompositorTiming::CompositeDeadline:
            case CompositorTiming::CompositInterval:
            case CompositorTiming::CompositToPresentLatency:
                return true;
    
            default:
                return false;
        }
    }
    
    bool ValidTimestampType(Timestamp timestamp)
    {
        switch (timestamp)
        {
            case Timestamp::RequestedPresentTime:
            case Timestamp::RenderingCompleteTime:
            case Timestamp::CompositionLatchTime:
            case Timestamp::FirstCompositionStartTime:
            case Timestamp::LastCompositionStartTime:
            case Timestamp::FirstCompositionGPUFinishedTime:
            case Timestamp::DisplayPresentTime:
            case Timestamp::DequeueReadyTime:
            case Timestamp::ReadsDoneTime:
                return true;
    
            default:
                return false;
        }
    }
    
    bool ValidateCompatibleSurface(const ValidationContext *val,
                                   const Display *display,
                                   const gl::Context *context,
                                   const Surface *surface)
    {
        const Config *contextConfig = context->getConfig();
        const Config *surfaceConfig = surface->getConfig();
    
        // Surface compatible with client API - only OPENGL_ES supported
        switch (context->getClientMajorVersion())
        {
            case 1:
                if (!(surfaceConfig->renderableType & EGL_OPENGL_ES_BIT))
                {
                    val->setError(EGL_BAD_MATCH, "Surface not compatible with OpenGL ES 1.x.");
                    return false;
                }
                break;
            case 2:
                if (!(surfaceConfig->renderableType & EGL_OPENGL_ES2_BIT))
                {
                    val->setError(EGL_BAD_MATCH, "Surface not compatible with OpenGL ES 2.x.");
                    return false;
                }
                break;
            case 3:
                if (!(surfaceConfig->renderableType & (EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT)))
                {
                    val->setError(EGL_BAD_MATCH, "Surface not compatible with OpenGL ES 3.x.");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_MATCH, "Surface not compatible with Context API.");
                return false;
        }
    
        // EGL KHR no config context
        if (context->getConfig() == EGL_NO_CONFIG_KHR)
        {
            const DisplayExtensions &displayExtensions = display->getExtensions();
            if (displayExtensions.noConfigContext)
            {
                return true;
            }
            val->setError(EGL_BAD_MATCH, "Context with no config is not supported.");
            return false;
        }
    
        if (!surface->flexibleSurfaceCompatibilityRequested())
        {
            // Config compatibility is defined in section 2.2 of the EGL 1.5 spec
    
            bool colorBufferCompat = surfaceConfig->colorBufferType == contextConfig->colorBufferType;
            if (!colorBufferCompat)
            {
                val->setError(EGL_BAD_MATCH, "Color buffer types are not compatible.");
                return false;
            }
    
            bool colorCompat = surfaceConfig->redSize == contextConfig->redSize &&
                               surfaceConfig->greenSize == contextConfig->greenSize &&
                               surfaceConfig->blueSize == contextConfig->blueSize &&
                               surfaceConfig->alphaSize == contextConfig->alphaSize &&
                               surfaceConfig->luminanceSize == contextConfig->luminanceSize;
            if (!colorCompat)
            {
                val->setError(EGL_BAD_MATCH, "Color buffer sizes are not compatible.");
                return false;
            }
    
            bool componentTypeCompat =
                surfaceConfig->colorComponentType == contextConfig->colorComponentType;
            if (!componentTypeCompat)
            {
                val->setError(EGL_BAD_MATCH, "Color buffer component types are not compatible.");
                return false;
            }
    
            bool dsCompat = surfaceConfig->depthSize == contextConfig->depthSize &&
                            surfaceConfig->stencilSize == contextConfig->stencilSize;
            if (!dsCompat)
            {
                val->setError(EGL_BAD_MATCH, "Depth-stencil buffer types are not compatible.");
                return false;
            }
        }
    
        bool surfaceTypeCompat = (surfaceConfig->surfaceType & contextConfig->surfaceType) != 0;
        if (!surfaceTypeCompat)
        {
            val->setError(EGL_BAD_MATCH, "Surface type is not compatible.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreateSyncBase(const ValidationContext *val,
                                const Display *display,
                                EGLenum type,
                                const AttributeMap &attribs,
                                bool isExt)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        gl::Context *currentContext  = val->eglThread->getContext();
        egl::Display *currentDisplay = currentContext ? currentContext->getDisplay() : nullptr;
    
        switch (type)
        {
            case EGL_SYNC_FENCE_KHR:
                if (!attribs.isEmpty())
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                    return false;
                }
    
                if (!display->getExtensions().fenceSync)
                {
                    val->setError(EGL_BAD_MATCH, "EGL_KHR_fence_sync extension is not available");
                    return false;
                }
    
                if (display != currentDisplay)
                {
                    val->setError(EGL_BAD_MATCH,
                                  "CreateSync can only be called on the current display");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateContext(val, currentDisplay, currentContext));
    
                if (!currentContext->getExtensions().eglSyncOES)
                {
                    val->setError(EGL_BAD_MATCH,
                                  "EGL_SYNC_FENCE_KHR cannot be used without "
                                  "GL_OES_EGL_sync support.");
                    return false;
                }
                break;
    
            case EGL_SYNC_NATIVE_FENCE_ANDROID:
                if (!display->getExtensions().fenceSync)
                {
                    val->setError(EGL_BAD_MATCH, "EGL_KHR_fence_sync extension is not available");
                    return false;
                }
    
                if (!display->getExtensions().nativeFenceSyncANDROID)
                {
                    val->setError(EGL_BAD_DISPLAY,
                                  "EGL_ANDROID_native_fence_sync extension is not available.");
                    return false;
                }
    
                if (display != currentDisplay)
                {
                    val->setError(EGL_BAD_MATCH,
                                  "CreateSync can only be called on the current display");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateContext(val, currentDisplay, currentContext));
    
                if (!currentContext->getExtensions().eglSyncOES)
                {
                    val->setError(EGL_BAD_MATCH,
                                  "EGL_SYNC_FENCE_KHR cannot be used without "
                                  "GL_OES_EGL_sync support.");
                    return false;
                }
    
                for (const auto &attributeIter : attribs)
                {
                    EGLAttrib attribute = attributeIter.first;
    
                    switch (attribute)
                    {
                        case EGL_SYNC_NATIVE_FENCE_FD_ANDROID:
                            break;
    
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                            return false;
                    }
                }
                break;
    
            case EGL_SYNC_REUSABLE_KHR:
                if (!attribs.isEmpty())
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                    return false;
                }
    
                if (!display->getExtensions().reusableSyncKHR)
                {
                    val->setError(EGL_BAD_MATCH, "EGL_KHR_reusable_sync extension is not available.");
                    return false;
                }
                break;
    
            default:
                if (isExt)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Invalid type parameter");
                    return false;
                }
                else
                {
                    val->setError(EGL_BAD_PARAMETER, "Invalid type parameter");
                    return false;
                }
        }
    
        return true;
    }
    
    bool ValidateGetSyncAttribBase(const ValidationContext *val,
                                   const Display *display,
                                   const Sync *sync,
                                   EGLint attribute)
    {
        ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
    
        switch (attribute)
        {
            case EGL_SYNC_CONDITION_KHR:
                switch (sync->getType())
                {
                    case EGL_SYNC_FENCE_KHR:
                    case EGL_SYNC_NATIVE_FENCE_ANDROID:
                        break;
    
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_SYNC_CONDITION_KHR is not valid for this sync type.");
                        return false;
                }
                break;
    
            // The following attributes are accepted by all types
            case EGL_SYNC_TYPE_KHR:
            case EGL_SYNC_STATUS_KHR:
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                return false;
        }
    
        return true;
    }
    
    bool ValidateQueryDisplayAttribBase(const ValidationContext *val,
                                        const Display *display,
                                        const EGLint attribute)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        switch (attribute)
        {
            case EGL_DEVICE_EXT:
                if (!Display::GetClientExtensions().deviceQueryEXT)
                {
                    val->setError(EGL_BAD_DISPLAY, "EGL_EXT_device_query extension is not available.");
                    return false;
                }
                break;
    
            case EGL_FEATURE_COUNT_ANGLE:
                if (!Display::GetClientExtensions().featureControlANGLE)
                {
                    val->setError(EGL_BAD_DISPLAY,
                                  "EGL_ANGLE_feature_control extension is not available.");
                    return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "attribute is not valid.");
                return false;
        }
    
        return true;
    }
    }  // anonymous namespace
    
    void ValidationContext::setError(EGLint error) const
    {
        eglThread->setError(error, entryPoint, labeledObject, nullptr);
    }
    
    void ValidationContext::setError(EGLint error, const char *message...) const
    {
        ASSERT(message);
    
        constexpr uint32_t kBufferSize = 1000;
        char buffer[kBufferSize];
    
        va_list args;
        va_start(args, message);
        vsnprintf(buffer, kBufferSize, message, args);
    
        eglThread->setError(error, entryPoint, labeledObject, buffer);
    }
    
    bool ValidateDisplay(const ValidationContext *val, const Display *display)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplayPointer(val, display));
    
        if (!display->isInitialized())
        {
            if (val)
            {
                val->setError(EGL_NOT_INITIALIZED, "display is not initialized.");
            }
            return false;
        }
    
        if (display->isDeviceLost())
        {
            if (val)
            {
                val->setError(EGL_CONTEXT_LOST, "display had a context loss");
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidateSurface(const ValidationContext *val, const Display *display, const Surface *surface)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->isValidSurface(surface))
        {
            if (val)
            {
                val->setError(EGL_BAD_SURFACE);
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidateConfig(const ValidationContext *val, const Display *display, const Config *config)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->isValidConfig(config))
        {
            if (val)
            {
                val->setError(EGL_BAD_CONFIG);
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidateContext(const ValidationContext *val,
                         const Display *display,
                         const gl::Context *context)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->isValidContext(context))
        {
            if (val)
            {
                val->setError(EGL_BAD_CONTEXT);
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidateImage(const ValidationContext *val, const Display *display, const Image *image)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->isValidImage(image))
        {
            if (val)
            {
                val->setError(EGL_BAD_PARAMETER, "image is not valid.");
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidateDevice(const ValidationContext *val, const Device *device)
    {
        if (device == EGL_NO_DEVICE_EXT)
        {
            if (val)
            {
                val->setError(EGL_BAD_ACCESS, "device is EGL_NO_DEVICE.");
            }
            return false;
        }
    
        if (!Device::IsValidDevice(device))
        {
            if (val)
            {
                val->setError(EGL_BAD_ACCESS, "device is not valid.");
            }
            return false;
        }
    
        return true;
    }
    
    bool ValidateSync(const ValidationContext *val, const Display *display, const Sync *sync)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->isValidSync(sync))
        {
            if (val)
            {
                val->setError(EGL_BAD_PARAMETER, "sync object is not valid.");
            }
            return false;
        }
    
        return true;
    }
    
    const Thread *GetThreadIfValid(const Thread *thread)
    {
        // Threads should always be valid
        return thread;
    }
    
    const Display *GetDisplayIfValid(const Display *display)
    {
        return ValidateDisplay(nullptr, display) ? display : nullptr;
    }
    
    const Surface *GetSurfaceIfValid(const Display *display, const Surface *surface)
    {
        return ValidateSurface(nullptr, display, surface) ? surface : nullptr;
    }
    
    const Image *GetImageIfValid(const Display *display, const Image *image)
    {
        return ValidateImage(nullptr, display, image) ? image : nullptr;
    }
    
    const Stream *GetStreamIfValid(const Display *display, const Stream *stream)
    {
        return ValidateStream(nullptr, display, stream) ? stream : nullptr;
    }
    
    const gl::Context *GetContextIfValid(const Display *display, const gl::Context *context)
    {
        return ValidateContext(nullptr, display, context) ? context : nullptr;
    }
    
    const Device *GetDeviceIfValid(const Device *device)
    {
        return ValidateDevice(nullptr, device) ? device : nullptr;
    }
    
    const Sync *GetSyncIfValid(const Display *display, const Sync *sync)
    {
        return ValidateSync(nullptr, display, sync) ? sync : nullptr;
    }
    
    LabeledObject *GetLabeledObjectIfValid(Thread *thread,
                                           const Display *display,
                                           ObjectType objectType,
                                           EGLObjectKHR object)
    {
        if (objectType == ObjectType::Thread)
        {
            return thread;
        }
    
        LabeledObject *labeledObject = nullptr;
        if (ValidateLabeledObject(nullptr, display, objectType, object, &labeledObject))
        {
            return labeledObject;
        }
    
        return nullptr;
    }
    
    bool ValidateInitialize(const ValidationContext *val,
                            const Display *display,
                            const EGLint *major,
                            const EGLint *minor)
    {
        return ValidateDisplayPointer(val, display);
    }
    
    bool ValidateTerminate(const ValidationContext *val, const Display *display)
    {
        return ValidateDisplayPointer(val, display);
    }
    
    bool ValidateCreateContext(const ValidationContext *val,
                               const Display *display,
                               const Config *configuration,
                               const gl::Context *shareContext,
                               const AttributeMap &attributes)
    {
        if (configuration)
        {
            ANGLE_VALIDATION_TRY(ValidateConfig(val, display, configuration));
        }
        else
        {
            ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
            const DisplayExtensions &displayExtensions = display->getExtensions();
            if (!displayExtensions.noConfigContext)
            {
                val->setError(EGL_BAD_CONFIG);
                return false;
            }
        }
    
        // Get the requested client version (default is 1) and check it is 2 or 3.
        EGLAttrib clientMajorVersion = 1;
        EGLAttrib clientMinorVersion = 0;
        EGLAttrib contextFlags       = 0;
        bool resetNotification       = false;
        for (AttributeMap::const_iterator attributeIter = attributes.begin();
             attributeIter != attributes.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            EGLAttrib value     = attributeIter->second;
    
            switch (attribute)
            {
                case EGL_CONTEXT_CLIENT_VERSION:
                    clientMajorVersion = value;
                    break;
    
                case EGL_CONTEXT_MINOR_VERSION:
                    clientMinorVersion = value;
                    break;
    
                case EGL_CONTEXT_FLAGS_KHR:
                    contextFlags = value;
                    break;
    
                case EGL_CONTEXT_OPENGL_DEBUG:
                    break;
    
                case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR:
                    // Only valid for OpenGL (non-ES) contexts
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
    
                case EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT:
                    if (!display->getExtensions().createContextRobustness)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR:
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR is not"
                                  " valid for GLES with EGL 1.4 and KHR_create_context. Use"
                                  " EXT_create_context_robustness.");
                    return false;
                case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT:
                    if (!display->getExtensions().createContextRobustness)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    if (value == EGL_LOSE_CONTEXT_ON_RESET_EXT)
                    {
                        resetNotification = true;
                    }
                    else if (value != EGL_NO_RESET_NOTIFICATION_EXT)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_OPENGL_NO_ERROR_KHR:
                    if (!display->getExtensions().createContextNoError)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "Invalid Context attribute.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "Attribute must be EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE:
                    if (!display->getExtensions().createContextWebGLCompatibility)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute "
                                      "EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE requires "
                                      "EGL_ANGLE_create_context_webgl_compatibility.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE must be "
                                      "EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM:
                    if (!display->getExtensions().createContextBindGeneratesResource)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM requires "
                                      "EGL_CHROMIUM_create_context_bind_generates_resource.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM "
                                      "must be EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE:
                    if (!display->getExtensions().displayTextureShareGroup)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute "
                                      "EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE requires "
                                      "EGL_ANGLE_display_texture_share_group.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE must be "
                                      "EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    if (shareContext &&
                        (shareContext->usingDisplayTextureShareGroup() != (value == EGL_TRUE)))
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "All contexts within a share group must be "
                                      "created with the same value of "
                                      "EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE.");
                        return false;
                    }
                    break;
    
                case EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE:
                    if (!display->getExtensions().displayTextureShareGroup)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute "
                                      "EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE requires "
                                      "EGL_ANGLE_display_semaphore_share_group.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE must be "
                                      "EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    if (shareContext &&
                        (shareContext->usingDisplaySemaphoreShareGroup() != (value == EGL_TRUE)))
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "All contexts within a share group must be "
                                      "created with the same value of "
                                      "EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE.");
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE:
                    if (!display->getExtensions().createContextClientArrays)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE requires "
                                      "EGL_ANGLE_create_context_client_arrays.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE must "
                                      "be EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE:
                    if (!display->getExtensions().programCacheControl)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE "
                                      "requires EGL_ANGLE_program_cache_control.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE must "
                                      "be EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
                    if (!display->getExtensions().robustResourceInitialization)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
                                      "requires EGL_ANGLE_robust_resource_initialization.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
                                      "either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_EXTENSIONS_ENABLED_ANGLE:
                    if (!display->getExtensions().createContextExtensionsEnabled)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_EXTENSIONS_ENABLED_ANGLE "
                                      "requires EGL_ANGLE_create_context_extensions_enabled.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_EXTENSIONS_ENABLED_ANGLE must be "
                                      "either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_POWER_PREFERENCE_ANGLE:
                    if (!display->getExtensions().powerPreference)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_POWER_PREFERENCE_ANGLE "
                                      "requires EGL_ANGLE_power_preference.");
                        return false;
                    }
                    if (value != EGL_LOW_POWER_ANGLE && value != EGL_HIGH_POWER_ANGLE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_POWER_PREFERENCE_ANGLE must be "
                                      "either EGL_LOW_POWER_ANGLE or EGL_HIGH_POWER_ANGLE.");
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE:
                    if (!display->getExtensions().createContextBackwardsCompatible)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE "
                                      "requires EGL_ANGLE_create_context_backwards_compatible.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE must be "
                                      "either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_CONTEXT_PRIORITY_LEVEL_IMG:
                    if (!display->getExtensions().contextPriority)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_CONTEXT_PRIORITY_LEVEL_IMG requires "
                                      "extension EGL_IMG_context_priority.");
                        return false;
                    }
                    switch (value)
                    {
                        case EGL_CONTEXT_PRIORITY_LOW_IMG:
                        case EGL_CONTEXT_PRIORITY_MEDIUM_IMG:
                        case EGL_CONTEXT_PRIORITY_HIGH_IMG:
                            break;
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE,
                                          "Attribute EGL_CONTEXT_PRIORITY_LEVEL_IMG "
                                          "must be one of: EGL_CONTEXT_PRIORITY_LOW_IMG, "
                                          "EGL_CONTEXT_PRIORITY_MEDIUM_IMG, or "
                                          "EGL_CONTEXT_PRIORITY_HIGH_IMG.");
                            return false;
                    }
                    break;
    
                case EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV:
                    if (!display->getExtensions().robustnessVideoMemoryPurgeNV)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV requires "
                                      "extension EGL_NV_robustness_video_memory_purge.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV must "
                                      "be either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_EXTERNAL_CONTEXT_ANGLE:
                    if (!display->getExtensions().externalContextAndSurface)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute "
                                      "EGL_EXTERNAL_CONTEXT_ANGLE requires "
                                      "EGL_ANGLE_external_context_and_surface.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_EXTERNAL_CONTEXT_ANGLE must "
                                      "be either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    if (value == EGL_TRUE && shareContext)
                    {
                        val->setError(
                            EGL_BAD_ATTRIBUTE,
                            "EGL_EXTERNAL_CONTEXT_ANGLE doesn't allow creating with sharedContext.");
                        return false;
                    }
                    break;
                case EGL_EXTERNAL_CONTEXT_SAVE_STATE_ANGLE:
                    if (!display->getExtensions().externalContextAndSurface)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute "
                                      "EGL_EXTERNAL_CONTEXT_SAVE_STATE_ANGLE requires "
                                      "EGL_ANGLE_external_context_and_surface.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_EXTERNAL_CONTEXT_SAVE_STATE_ANGLE must "
                                      "be either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
                default:
                    val->setError(EGL_BAD_ATTRIBUTE, "Unknown attribute.");
                    return false;
            }
        }
    
        switch (clientMajorVersion)
        {
            case 1:
                if (clientMinorVersion != 0 && clientMinorVersion != 1)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                if (configuration == EGL_NO_CONFIG_KHR)
                {
                    val->setError(EGL_BAD_MATCH);
                    return false;
                }
                if ((configuration != EGL_NO_CONFIG_KHR) &&
                    !(configuration->renderableType & EGL_OPENGL_ES_BIT))
                {
                    val->setError(EGL_BAD_MATCH);
                    return false;
                }
                break;
    
            case 2:
                if (clientMinorVersion != 0)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                if ((configuration != EGL_NO_CONFIG_KHR) &&
                    !(configuration->renderableType & EGL_OPENGL_ES2_BIT))
                {
                    val->setError(EGL_BAD_MATCH);
                    return false;
                }
                break;
            case 3:
                if (clientMinorVersion < 0 || clientMinorVersion > 2)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                if ((configuration != EGL_NO_CONFIG_KHR) &&
                    !(configuration->renderableType & EGL_OPENGL_ES3_BIT))
                {
                    val->setError(EGL_BAD_MATCH);
                    return false;
                }
                if (display->getMaxSupportedESVersion() <
                    gl::Version(static_cast<GLuint>(clientMajorVersion),
                                static_cast<GLuint>(clientMinorVersion)))
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Requested GLES version is not supported.");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE);
                return false;
                break;
        }
    
        // Note: EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR does not apply to ES
        const EGLint validContextFlags =
            (EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR | EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR);
        if ((contextFlags & ~validContextFlags) != 0)
        {
            val->setError(EGL_BAD_ATTRIBUTE);
            return false;
        }
    
        if (shareContext)
        {
            // Shared context is invalid or is owned by another display
            if (!display->isValidContext(shareContext))
            {
                val->setError(EGL_BAD_MATCH);
                return false;
            }
    
            if (shareContext->isResetNotificationEnabled() != resetNotification)
            {
                val->setError(EGL_BAD_MATCH);
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateCreateWindowSurface(const ValidationContext *val,
                                     const Display *display,
                                     const Config *config,
                                     EGLNativeWindowType window,
                                     const AttributeMap &attributes)
    {
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, config));
    
        if (!display->isValidNativeWindow(window))
        {
            val->setError(EGL_BAD_NATIVE_WINDOW);
            return false;
        }
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
    
        for (AttributeMap::const_iterator attributeIter = attributes.begin();
             attributeIter != attributes.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            EGLAttrib value     = attributeIter->second;
    
            switch (attribute)
            {
                case EGL_RENDER_BUFFER:
                    switch (value)
                    {
                        case EGL_BACK_BUFFER:
                            break;
                        case EGL_SINGLE_BUFFER:
                            val->setError(EGL_BAD_MATCH);
                            return false;  // Rendering directly to front buffer not supported
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE);
                            return false;
                    }
                    break;
    
                case EGL_POST_SUB_BUFFER_SUPPORTED_NV:
                    if (!displayExtensions.postSubBuffer)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    break;
    
                case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
                    if (!displayExtensions.flexibleSurfaceCompatibility)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    break;
    
                case EGL_WIDTH:
                case EGL_HEIGHT:
                    if (!displayExtensions.windowFixedSize)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER);
                        return false;
                    }
                    break;
    
                case EGL_FIXED_SIZE_ANGLE:
                    if (!displayExtensions.windowFixedSize)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    break;
    
                case EGL_SURFACE_ORIENTATION_ANGLE:
                    if (!displayExtensions.surfaceOrientation)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_ANGLE_surface_orientation is not enabled.");
                        return false;
                    }
                    break;
    
                case EGL_VG_COLORSPACE:
                    val->setError(EGL_BAD_MATCH);
                    return false;
    
                case EGL_GL_COLORSPACE:
                    ANGLE_VALIDATION_TRY(ValidateColorspaceAttribute(val, displayExtensions, value));
                    break;
    
                case EGL_VG_ALPHA_FORMAT:
                    val->setError(EGL_BAD_MATCH);
                    return false;
    
                case EGL_DIRECT_COMPOSITION_ANGLE:
                    if (!displayExtensions.directComposition)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE);
                        return false;
                    }
                    break;
    
                case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
                    if (!display->getExtensions().robustResourceInitialization)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
                                      "requires EGL_ANGLE_robust_resource_initialization.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
                                      "either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                case EGL_GGP_STREAM_DESCRIPTOR_ANGLE:
                    if (!display->getExtensions().ggpStreamDescriptor)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_GGP_STREAM_DESCRIPTOR_ANGLE requires "
                                      "EGL_ANGLE_ggp_stream_descriptor.");
                        return false;
                    }
                    break;
    
                default:
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
            }
        }
    
        if (Display::hasExistingWindowSurface(window))
        {
            val->setError(EGL_BAD_ALLOC);
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreatePbufferSurface(const ValidationContext *val,
                                      const Display *display,
                                      const Config *config,
                                      const AttributeMap &attributes)
    {
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, config));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
    
        for (AttributeMap::const_iterator attributeIter = attributes.begin();
             attributeIter != attributes.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            EGLAttrib value     = attributeIter->second;
    
            switch (attribute)
            {
                case EGL_WIDTH:
                case EGL_HEIGHT:
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER);
                        return false;
                    }
                    break;
    
                case EGL_LARGEST_PBUFFER:
                    break;
    
                case EGL_TEXTURE_FORMAT:
                    switch (value)
                    {
                        case EGL_NO_TEXTURE:
                        case EGL_TEXTURE_RGB:
                        case EGL_TEXTURE_RGBA:
                            break;
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE);
                            return false;
                    }
                    break;
    
                case EGL_TEXTURE_TARGET:
                    switch (value)
                    {
                        case EGL_NO_TEXTURE:
                        case EGL_TEXTURE_2D:
                            break;
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE);
                            return false;
                    }
                    break;
    
                case EGL_MIPMAP_TEXTURE:
                    break;
    
                case EGL_VG_COLORSPACE:
                    break;
    
                case EGL_GL_COLORSPACE:
                    ANGLE_VALIDATION_TRY(ValidateColorspaceAttribute(val, displayExtensions, value));
                    break;
    
                case EGL_VG_ALPHA_FORMAT:
                    break;
    
                case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
                    if (!displayExtensions.flexibleSurfaceCompatibility)
                    {
                        val->setError(
                            EGL_BAD_ATTRIBUTE,
                            "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used "
                            "without EGL_ANGLE_flexible_surface_compatibility support.");
                        return false;
                    }
                    break;
    
                case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
                    if (!display->getExtensions().robustResourceInitialization)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "Attribute EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
                                      "requires EGL_ANGLE_robust_resource_initialization.");
                        return false;
                    }
                    if (value != EGL_TRUE && value != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
                                      "either EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
    
                default:
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
            }
        }
    
        if (!(config->surfaceType & EGL_PBUFFER_BIT))
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
    
        const Caps &caps = display->getCaps();
    
        EGLAttrib textureFormat = attributes.get(EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE);
        EGLAttrib textureTarget = attributes.get(EGL_TEXTURE_TARGET, EGL_NO_TEXTURE);
    
        if ((textureFormat != EGL_NO_TEXTURE && textureTarget == EGL_NO_TEXTURE) ||
            (textureFormat == EGL_NO_TEXTURE && textureTarget != EGL_NO_TEXTURE))
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
    
        if ((textureFormat == EGL_TEXTURE_RGB && config->bindToTextureRGB != EGL_TRUE) ||
            (textureFormat == EGL_TEXTURE_RGBA && config->bindToTextureRGBA != EGL_TRUE))
        {
            val->setError(EGL_BAD_ATTRIBUTE);
            return false;
        }
    
        EGLint width  = static_cast<EGLint>(attributes.get(EGL_WIDTH, 0));
        EGLint height = static_cast<EGLint>(attributes.get(EGL_HEIGHT, 0));
        if (textureFormat != EGL_NO_TEXTURE && !caps.textureNPOT &&
            (!gl::isPow2(width) || !gl::isPow2(height)))
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreatePbufferFromClientBuffer(const ValidationContext *val,
                                               const Display *display,
                                               EGLenum buftype,
                                               EGLClientBuffer buffer,
                                               const Config *config,
                                               const AttributeMap &attributes)
    {
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, config));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
    
        switch (buftype)
        {
            case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
                if (!displayExtensions.d3dShareHandleClientBuffer)
                {
                    val->setError(EGL_BAD_PARAMETER);
                    return false;
                }
                if (buffer == nullptr)
                {
                    val->setError(EGL_BAD_PARAMETER);
                    return false;
                }
                break;
    
            case EGL_D3D_TEXTURE_ANGLE:
                if (!displayExtensions.d3dTextureClientBuffer)
                {
                    val->setError(EGL_BAD_PARAMETER);
                    return false;
                }
                if (buffer == nullptr)
                {
                    val->setError(EGL_BAD_PARAMETER);
                    return false;
                }
                break;
    
            case EGL_IOSURFACE_ANGLE:
                if (!displayExtensions.iosurfaceClientBuffer)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "<buftype> EGL_IOSURFACE_ANGLE requires the "
                                  "EGL_ANGLE_iosurface_client_buffer extension.");
                    return false;
                }
                if (buffer == nullptr)
                {
                    val->setError(EGL_BAD_PARAMETER, "<buffer> must be non null");
                    return false;
                }
                break;
            case EGL_EXTERNAL_SURFACE_ANGLE:
                if (!display->getExtensions().externalContextAndSurface)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "Attribute "
                                  "EGL_EXTERNAL_SURFACE_ANGLE requires "
                                  "EGL_ANGLE_external_context_and_surface.");
                    return false;
                }
                if (buffer != nullptr)
                {
                    val->setError(EGL_BAD_PARAMETER, "<buffer> must be null");
                    return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_PARAMETER);
                return false;
        }
    
        for (AttributeMap::const_iterator attributeIter = attributes.begin();
             attributeIter != attributes.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            EGLAttrib value     = attributeIter->second;
    
            switch (attribute)
            {
                case EGL_WIDTH:
                case EGL_HEIGHT:
                    if (buftype != EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE &&
                        buftype != EGL_D3D_TEXTURE_ANGLE && buftype != EGL_IOSURFACE_ANGLE &&
                        buftype != EGL_EXTERNAL_SURFACE_ANGLE)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "Width and Height are not supported for this <buftype>");
                        return false;
                    }
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER, "Width and Height must be positive");
                        return false;
                    }
                    break;
    
                case EGL_TEXTURE_FORMAT:
                    switch (value)
                    {
                        case EGL_NO_TEXTURE:
                        case EGL_TEXTURE_RGB:
                        case EGL_TEXTURE_RGBA:
                            break;
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE, "Invalid value for EGL_TEXTURE_FORMAT");
                            return false;
                    }
                    break;
    
                case EGL_TEXTURE_TARGET:
                    switch (value)
                    {
                        case EGL_NO_TEXTURE:
                        case EGL_TEXTURE_2D:
                            break;
                        case EGL_TEXTURE_RECTANGLE_ANGLE:
                            if (buftype != EGL_IOSURFACE_ANGLE)
                            {
                                val->setError(EGL_BAD_PARAMETER,
                                              "<buftype> doesn't support rectangle texture targets");
                                return false;
                            }
                            break;
    
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE, "Invalid value for EGL_TEXTURE_TARGET");
                            return false;
                    }
                    break;
    
                case EGL_MIPMAP_TEXTURE:
                    break;
    
                case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
                    if (!displayExtensions.flexibleSurfaceCompatibility)
                    {
                        val->setError(
                            EGL_BAD_ATTRIBUTE,
                            "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used "
                            "without EGL_ANGLE_flexible_surface_compatibility support.");
                        return false;
                    }
                    break;
    
                case EGL_IOSURFACE_PLANE_ANGLE:
                    if (buftype != EGL_IOSURFACE_ANGLE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "<buftype> doesn't support iosurface plane");
                        return false;
                    }
                    break;
    
                case EGL_TEXTURE_TYPE_ANGLE:
                    if (buftype != EGL_IOSURFACE_ANGLE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "<buftype> doesn't support texture type");
                        return false;
                    }
                    break;
    
                case EGL_TEXTURE_INTERNAL_FORMAT_ANGLE:
                    if (buftype != EGL_IOSURFACE_ANGLE && buftype != EGL_D3D_TEXTURE_ANGLE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "<buftype> doesn't support texture internal format");
                        return false;
                    }
                    break;
    
                case EGL_GL_COLORSPACE:
                    if (buftype != EGL_D3D_TEXTURE_ANGLE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "<buftype> doesn't support setting GL colorspace");
                        return false;
                    }
                    break;
    
                case EGL_IOSURFACE_USAGE_HINT_ANGLE:
                    if (value & ~(EGL_IOSURFACE_READ_HINT_ANGLE | EGL_IOSURFACE_WRITE_HINT_ANGLE))
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "IOSurface usage hint must only contain READ or WRITE");
                        return false;
                    }
                    break;
    
                case EGL_TEXTURE_OFFSET_X_ANGLE:
                case EGL_TEXTURE_OFFSET_Y_ANGLE:
                    if (buftype != EGL_D3D_TEXTURE_ANGLE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "<buftype> doesn't support setting texture offset");
                        return false;
                    }
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "Texture offset cannot be negative");
                        return false;
                    }
                    break;
    
                default:
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
            }
        }
    
        EGLAttrib colorspace = attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR);
        if (colorspace != EGL_GL_COLORSPACE_LINEAR && colorspace != EGL_GL_COLORSPACE_SRGB)
        {
            val->setError(EGL_BAD_ATTRIBUTE, "invalid GL colorspace");
            return false;
        }
    
        if (!(config->surfaceType & EGL_PBUFFER_BIT))
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
    
        EGLAttrib textureFormat = attributes.get(EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE);
        EGLAttrib textureTarget = attributes.get(EGL_TEXTURE_TARGET, EGL_NO_TEXTURE);
        if ((textureFormat != EGL_NO_TEXTURE && textureTarget == EGL_NO_TEXTURE) ||
            (textureFormat == EGL_NO_TEXTURE && textureTarget != EGL_NO_TEXTURE))
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
        if ((textureFormat == EGL_TEXTURE_RGB && config->bindToTextureRGB != EGL_TRUE) ||
            (textureFormat == EGL_TEXTURE_RGBA && config->bindToTextureRGBA != EGL_TRUE))
        {
            // TODO(cwallez@chromium.org): For IOSurface pbuffers we require that EGL_TEXTURE_RGBA is
            // set so that eglBindTexImage works. Normally this is only allowed if the config exposes
            // the bindToTextureRGB/RGBA flag. This issue is that enabling this flags means that
            // eglBindTexImage should also work for regular pbuffers which isn't implemented on macOS.
            // Instead of adding the flag we special case the check here to be ignored for IOSurfaces.
            // The TODO is to find a proper solution for this, maybe by implementing eglBindTexImage on
            // OSX?
            if (buftype != EGL_IOSURFACE_ANGLE)
            {
                val->setError(EGL_BAD_ATTRIBUTE);
                return false;
            }
        }
    
        if (buftype == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE)
        {
            EGLint width  = static_cast<EGLint>(attributes.get(EGL_WIDTH, 0));
            EGLint height = static_cast<EGLint>(attributes.get(EGL_HEIGHT, 0));
    
            if (width == 0 || height == 0)
            {
                val->setError(EGL_BAD_ATTRIBUTE);
                return false;
            }
    
            const Caps &caps = display->getCaps();
            if (textureFormat != EGL_NO_TEXTURE && !caps.textureNPOT &&
                (!gl::isPow2(width) || !gl::isPow2(height)))
            {
                val->setError(EGL_BAD_MATCH);
                return false;
            }
        }
    
        if (buftype == EGL_IOSURFACE_ANGLE)
        {
            if (static_cast<EGLenum>(textureTarget) != config->bindToTextureTarget)
            {
                val->setError(EGL_BAD_ATTRIBUTE,
                              "EGL_IOSURFACE requires the texture target to match the config");
                return false;
            }
            if (textureFormat != EGL_TEXTURE_RGBA)
            {
                val->setError(EGL_BAD_ATTRIBUTE, "EGL_IOSURFACE requires the EGL_TEXTURE_RGBA format");
                return false;
            }
    
            if (!attributes.contains(EGL_WIDTH) || !attributes.contains(EGL_HEIGHT) ||
                !attributes.contains(EGL_TEXTURE_FORMAT) ||
                !attributes.contains(EGL_TEXTURE_TYPE_ANGLE) ||
                !attributes.contains(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE) ||
                !attributes.contains(EGL_IOSURFACE_PLANE_ANGLE))
            {
                val->setError(EGL_BAD_PARAMETER, "Missing required attribute for EGL_IOSURFACE");
                return false;
            }
        }
    
        ANGLE_EGL_TRY_RETURN(val->eglThread,
                             display->validateClientBuffer(config, buftype, buffer, attributes),
                             val->entryPoint, val->labeledObject, false);
    
        return true;
    }
    
    bool ValidateCreatePixmapSurface(const ValidationContext *val,
                                     const Display *display,
                                     const Config *config,
                                     EGLNativePixmapType pixmap,
                                     const AttributeMap &attributes)
    {
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, config));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
    
        for (AttributeMap::const_iterator attributeIter = attributes.begin();
             attributeIter != attributes.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            EGLAttrib value     = attributeIter->second;
    
            switch (attribute)
            {
                case EGL_GL_COLORSPACE:
                    ANGLE_VALIDATION_TRY(ValidateColorspaceAttribute(val, displayExtensions, value));
                    break;
    
                case EGL_VG_COLORSPACE:
                    break;
                case EGL_VG_ALPHA_FORMAT:
                    break;
    
                case EGL_TEXTURE_FORMAT:
                    if (!displayExtensions.textureFromPixmapNOK)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "EGL_NOK_texture_from_pixmap is not enabled.");
                        return false;
                    }
                    switch (value)
                    {
                        case EGL_NO_TEXTURE:
                        case EGL_TEXTURE_RGB:
                        case EGL_TEXTURE_RGBA:
                            break;
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE);
                            return false;
                    }
                    break;
    
                case EGL_TEXTURE_TARGET:
                    if (!displayExtensions.textureFromPixmapNOK)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "EGL_NOK_texture_from_pixmap is not enabled.");
                        return false;
                    }
                    switch (value)
                    {
                        case EGL_NO_TEXTURE:
                        case EGL_TEXTURE_2D:
                            break;
                        default:
                            val->setError(EGL_BAD_ATTRIBUTE);
                            return false;
                    }
                    break;
    
                case EGL_MIPMAP_TEXTURE:
                    if (!displayExtensions.textureFromPixmapNOK)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "EGL_NOK_texture_from_pixmap is not enabled.");
                        return false;
                    }
                    break;
    
                default:
                    val->setError(EGL_BAD_ATTRIBUTE, "Unknown attribute");
                    return false;
            }
        }
    
        if (!(config->surfaceType & EGL_PIXMAP_BIT))
        {
            val->setError(EGL_BAD_MATCH, "Congfig does not suport pixmaps.");
            return false;
        }
    
        ANGLE_EGL_TRY_RETURN(val->eglThread, display->valdiatePixmap(config, pixmap, attributes),
                             val->entryPoint, val->labeledObject, false);
    
        return true;
    }
    
    bool ValidateMakeCurrent(const ValidationContext *val,
                             const Display *display,
                             const Surface *draw,
                             const Surface *read,
                             const gl::Context *context)
    {
        if (context == EGL_NO_CONTEXT && (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE))
        {
            val->setError(EGL_BAD_MATCH, "If ctx is EGL_NO_CONTEXT, surfaces must be EGL_NO_SURFACE");
            return false;
        }
    
        // If ctx is EGL_NO_CONTEXT and either draw or read are not EGL_NO_SURFACE, an EGL_BAD_MATCH
        // error is generated. EGL_KHR_surfaceless_context allows both surfaces to be EGL_NO_SURFACE.
        if (context != EGL_NO_CONTEXT && (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE))
        {
            if (display->getExtensions().surfacelessContext)
            {
                if ((draw == EGL_NO_SURFACE) != (read == EGL_NO_SURFACE))
                {
                    val->setError(EGL_BAD_MATCH,
                                  "If ctx is not EGL_NOT_CONTEXT, draw or read must "
                                  "both be EGL_NO_SURFACE, or both not");
                    return false;
                }
            }
            else
            {
                val->setError(EGL_BAD_MATCH,
                              "If ctx is not EGL_NO_CONTEXT, surfaces must not be EGL_NO_SURFACE");
                return false;
            }
        }
    
        // If either of draw or read is a valid surface and the other is EGL_NO_SURFACE, an
        // EGL_BAD_MATCH error is generated.
        if ((read == EGL_NO_SURFACE) != (draw == EGL_NO_SURFACE))
        {
            val->setError(EGL_BAD_MATCH,
                          "read and draw must both be valid surfaces, or both be EGL_NO_SURFACE");
            return false;
        }
    
        if (display == EGL_NO_DISPLAY || !Display::isValidDisplay(display))
        {
            val->setError(EGL_BAD_DISPLAY, "'dpy' not a valid EGLDisplay handle");
            return false;
        }
    
        // EGL 1.5 spec: dpy can be uninitialized if all other parameters are null
        if (!display->isInitialized() &&
            (context != EGL_NO_CONTEXT || draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE))
        {
            val->setError(EGL_NOT_INITIALIZED, "'dpy' not initialized");
            return false;
        }
    
        if (context != EGL_NO_CONTEXT)
        {
            ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
        }
    
        if (display->isInitialized() && display->isDeviceLost())
        {
            val->setError(EGL_CONTEXT_LOST);
            return false;
        }
    
        if (draw != EGL_NO_SURFACE)
        {
            ANGLE_VALIDATION_TRY(ValidateSurface(val, display, draw));
        }
    
        if (read != EGL_NO_SURFACE)
        {
            ANGLE_VALIDATION_TRY(ValidateSurface(val, display, read));
            ANGLE_VALIDATION_TRY(ValidateCompatibleSurface(val, display, context, read));
        }
    
        if (draw != read)
        {
            if (draw)
            {
                ANGLE_VALIDATION_TRY(ValidateCompatibleSurface(val, display, context, draw));
            }
            if (read)
            {
                ANGLE_VALIDATION_TRY(ValidateCompatibleSurface(val, display, context, read));
            }
        }
        return true;
    }
    
    bool ValidateCreateImage(const ValidationContext *val,
                             const Display *display,
                             const gl::Context *context,
                             EGLenum target,
                             EGLClientBuffer buffer,
                             const AttributeMap &attributes)
    {
    
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
    
        // TODO(geofflang): Complete validation from EGL_KHR_image_base:
        // If the resource specified by <dpy>, <ctx>, <target>, <buffer> and <attrib_list> is itself an
        // EGLImage sibling, the error EGL_BAD_ACCESS is generated.
    
        for (AttributeMap::const_iterator attributeIter = attributes.begin();
             attributeIter != attributes.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            EGLAttrib value     = attributeIter->second;
    
            switch (attribute)
            {
                case EGL_IMAGE_PRESERVED:
                    switch (value)
                    {
                        case EGL_TRUE:
                        case EGL_FALSE:
                            break;
    
                        default:
                            val->setError(EGL_BAD_PARAMETER,
                                          "EGL_IMAGE_PRESERVED must be EGL_TRUE or EGL_FALSE.");
                            return false;
                    }
                    break;
    
                case EGL_GL_TEXTURE_LEVEL:
                    if (!displayExtensions.glTexture2DImage &&
                        !displayExtensions.glTextureCubemapImage && !displayExtensions.glTexture3DImage)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "EGL_GL_TEXTURE_LEVEL cannot be used "
                                      "without KHR_gl_texture_*_image support.");
                        return false;
                    }
    
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER, "EGL_GL_TEXTURE_LEVEL cannot be negative.");
                        return false;
                    }
                    break;
    
                case EGL_GL_TEXTURE_ZOFFSET:
                    if (!displayExtensions.glTexture3DImage)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "EGL_GL_TEXTURE_ZOFFSET cannot be used "
                                      "without KHR_gl_texture_3D_image support.");
                        return false;
                    }
                    break;
    
                case EGL_GL_COLORSPACE:
                    if (!displayExtensions.glColorspace)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "EGL_GL_COLORSPACE cannot be used "
                                      "without EGL_KHR_gl_colorspace support.");
                        return false;
                    }
                    switch (value)
                    {
                        case EGL_GL_COLORSPACE_DEFAULT_EXT:
                            break;
                        default:
                            ANGLE_VALIDATION_TRY(
                                ValidateColorspaceAttribute(val, displayExtensions, value));
                            break;
                    }
                    break;
    
                case EGL_TEXTURE_INTERNAL_FORMAT_ANGLE:
                    if (!displayExtensions.imageD3D11Texture)
                    {
                        val->setError(
                            EGL_BAD_PARAMETER,
                            "EGL_TEXTURE_INTERNAL_FORMAT_ANGLE and EGL_TEXTURE_TYPE_ANGLE cannot "
                            "be used without EGL_ANGLE_image_d3d11_texture support.");
                        return false;
                    }
                    break;
    
                case EGL_D3D11_TEXTURE_PLANE_ANGLE:
                    if (!displayExtensions.imageD3D11Texture)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_D3D11_TEXTURE_PLANE_ANGLE cannot be used without "
                                      "EGL_ANGLE_image_d3d11_texture support.");
                        return false;
                    }
                    break;
    
                case EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE:
                    if (!displayExtensions.imageD3D11Texture)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE cannot be used without "
                                      "EGL_ANGLE_image_d3d11_texture support.");
                        return false;
                    }
                    break;
    
                case EGL_WIDTH:
                case EGL_HEIGHT:
                    if (target != EGL_LINUX_DMA_BUF_EXT)
                    {
                        val->setError(
                            EGL_BAD_PARAMETER,
                            "Parameter cannot be used if target is not EGL_LINUX_DMA_BUF_EXT");
                        return false;
                    }
                    break;
    
                case EGL_LINUX_DRM_FOURCC_EXT:
                case EGL_DMA_BUF_PLANE0_FD_EXT:
                case EGL_DMA_BUF_PLANE0_OFFSET_EXT:
                case EGL_DMA_BUF_PLANE0_PITCH_EXT:
                case EGL_DMA_BUF_PLANE1_FD_EXT:
                case EGL_DMA_BUF_PLANE1_OFFSET_EXT:
                case EGL_DMA_BUF_PLANE1_PITCH_EXT:
                case EGL_DMA_BUF_PLANE2_FD_EXT:
                case EGL_DMA_BUF_PLANE2_OFFSET_EXT:
                case EGL_DMA_BUF_PLANE2_PITCH_EXT:
                    if (!displayExtensions.imageDmaBufImportEXT)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "Parameter cannot be used without "
                                      "EGL_EXT_image_dma_buf_import support.");
                        return false;
                    }
                    break;
    
                case EGL_YUV_COLOR_SPACE_HINT_EXT:
                    if (!displayExtensions.imageDmaBufImportEXT)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "Parameter cannot be used without "
                                      "EGL_EXT_image_dma_buf_import support.");
                        return false;
                    }
    
                    switch (value)
                    {
                        case EGL_ITU_REC601_EXT:
                        case EGL_ITU_REC709_EXT:
                        case EGL_ITU_REC2020_EXT:
                            break;
    
                        default:
                            val->setError(EGL_BAD_PARAMETER,
                                          "Invalid value for EGL_YUV_COLOR_SPACE_HINT_EXT.");
                            return false;
                    }
                    break;
    
                case EGL_SAMPLE_RANGE_HINT_EXT:
                    if (!displayExtensions.imageDmaBufImportEXT)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "Parameter cannot be used without "
                                      "EGL_EXT_image_dma_buf_import support.");
                        return false;
                    }
    
                    switch (value)
                    {
                        case EGL_YUV_FULL_RANGE_EXT:
                        case EGL_YUV_NARROW_RANGE_EXT:
                            break;
    
                        default:
                            val->setError(EGL_BAD_PARAMETER,
                                          "Invalid value for EGL_SAMPLE_RANGE_HINT_EXT.");
                            return false;
                    }
                    break;
    
                case EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT:
                case EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT:
                    if (!displayExtensions.imageDmaBufImportEXT)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "Parameter cannot be used without "
                                      "EGL_EXT_image_dma_buf_import support.");
                        return false;
                    }
    
                    switch (value)
                    {
                        case EGL_YUV_CHROMA_SITING_0_EXT:
                        case EGL_YUV_CHROMA_SITING_0_5_EXT:
                            break;
    
                        default:
                            val->setError(
                                EGL_BAD_PARAMETER,
                                "Invalid value for EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT or "
                                "EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT.");
                            return false;
                    }
                    break;
    
                case EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT:
                case EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT:
                case EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT:
                case EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT:
                case EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT:
                case EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT:
                case EGL_DMA_BUF_PLANE3_FD_EXT:
                case EGL_DMA_BUF_PLANE3_OFFSET_EXT:
                case EGL_DMA_BUF_PLANE3_PITCH_EXT:
                case EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT:
                case EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT:
                    if (!displayExtensions.imageDmaBufImportModifiersEXT)
                    {
                        val->setError(EGL_BAD_PARAMETER,
                                      "Parameter cannot be used without "
                                      "EGL_EXT_image_dma_buf_import_modifiers support.");
                        return false;
                    }
                    break;
    
                default:
                    val->setError(EGL_BAD_PARAMETER, "invalid attribute: 0x%X", attribute);
                    return false;
            }
        }
    
        switch (target)
        {
            case EGL_GL_TEXTURE_2D:
            {
                if (!displayExtensions.glTexture2DImage)
                {
                    val->setError(EGL_BAD_PARAMETER, "KHR_gl_texture_2D_image not supported.");
                    return false;
                }
    
                if (buffer == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "buffer cannot reference a 2D texture with the name 0.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
                const gl::Texture *texture =
                    context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
                if (texture == nullptr || texture->getType() != gl::TextureType::_2D)
                {
                    val->setError(EGL_BAD_PARAMETER, "target is not a 2D texture.");
                    return false;
                }
    
                if (texture->getBoundSurface() != nullptr)
                {
                    val->setError(EGL_BAD_ACCESS, "texture has a surface bound to it.");
                    return false;
                }
    
                EGLAttrib level = attributes.get(EGL_GL_TEXTURE_LEVEL, 0);
                if (texture->getWidth(gl::TextureTarget::_2D, static_cast<size_t>(level)) == 0 ||
                    texture->getHeight(gl::TextureTarget::_2D, static_cast<size_t>(level)) == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "target 2D texture does not have a valid size at specified level.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateCreateImageMipLevelCommon(val, context, texture, level));
            }
            break;
    
            case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
            case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
            case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
            case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
            case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
            case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
            {
                if (!displayExtensions.glTextureCubemapImage)
                {
                    val->setError(EGL_BAD_PARAMETER, "KHR_gl_texture_cubemap_image not supported.");
                    return false;
                }
    
                if (buffer == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "buffer cannot reference a cubemap texture with the name 0.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
                const gl::Texture *texture =
                    context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
                if (texture == nullptr || texture->getType() != gl::TextureType::CubeMap)
                {
                    val->setError(EGL_BAD_PARAMETER, "target is not a cubemap texture.");
                    return false;
                }
    
                if (texture->getBoundSurface() != nullptr)
                {
                    val->setError(EGL_BAD_ACCESS, "texture has a surface bound to it.");
                    return false;
                }
    
                EGLAttrib level               = attributes.get(EGL_GL_TEXTURE_LEVEL, 0);
                gl::TextureTarget cubeMapFace = egl_gl::EGLCubeMapTargetToCubeMapTarget(target);
                if (texture->getWidth(cubeMapFace, static_cast<size_t>(level)) == 0 ||
                    texture->getHeight(cubeMapFace, static_cast<size_t>(level)) == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "target cubemap texture does not have a valid "
                                  "size at specified level and face.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateCreateImageMipLevelCommon(val, context, texture, level));
    
                if (level == 0 && !texture->isMipmapComplete() &&
                    CubeTextureHasUnspecifiedLevel0Face(texture))
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "if level is zero and the texture is incomplete, "
                                  "it must have all of its faces specified at level "
                                  "zero.");
                    return false;
                }
            }
            break;
    
            case EGL_GL_TEXTURE_3D:
            {
                if (!displayExtensions.glTexture3DImage)
                {
                    val->setError(EGL_BAD_PARAMETER, "KHR_gl_texture_3D_image not supported.");
                    return false;
                }
    
                if (buffer == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "buffer cannot reference a 3D texture with the name 0.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
                const gl::Texture *texture =
                    context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
                if (texture == nullptr || texture->getType() != gl::TextureType::_3D)
                {
                    val->setError(EGL_BAD_PARAMETER, "target is not a 3D texture.");
                    return false;
                }
    
                if (texture->getBoundSurface() != nullptr)
                {
                    val->setError(EGL_BAD_ACCESS, "texture has a surface bound to it.");
                    return false;
                }
    
                EGLAttrib level   = attributes.get(EGL_GL_TEXTURE_LEVEL, 0);
                EGLAttrib zOffset = attributes.get(EGL_GL_TEXTURE_ZOFFSET, 0);
                if (texture->getWidth(gl::TextureTarget::_3D, static_cast<size_t>(level)) == 0 ||
                    texture->getHeight(gl::TextureTarget::_3D, static_cast<size_t>(level)) == 0 ||
                    texture->getDepth(gl::TextureTarget::_3D, static_cast<size_t>(level)) == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "target 3D texture does not have a valid size at specified level.");
                    return false;
                }
    
                if (static_cast<size_t>(zOffset) >=
                    texture->getDepth(gl::TextureTarget::_3D, static_cast<size_t>(level)))
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "target 3D texture does not have enough layers "
                                  "for the specified Z offset at the specified "
                                  "level.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateCreateImageMipLevelCommon(val, context, texture, level));
            }
            break;
    
            case EGL_GL_RENDERBUFFER:
            {
                if (!displayExtensions.glRenderbufferImage)
                {
                    val->setError(EGL_BAD_PARAMETER, "KHR_gl_renderbuffer_image not supported.");
                    return false;
                }
    
                if (attributes.contains(EGL_GL_TEXTURE_LEVEL))
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "EGL_GL_TEXTURE_LEVEL cannot be used in "
                                  "conjunction with a renderbuffer target.");
                    return false;
                }
    
                if (buffer == 0)
                {
                    val->setError(EGL_BAD_PARAMETER,
                                  "buffer cannot reference a renderbuffer with the name 0.");
                    return false;
                }
    
                ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
                const gl::Renderbuffer *renderbuffer =
                    context->getRenderbuffer({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
                if (renderbuffer == nullptr)
                {
                    val->setError(EGL_BAD_PARAMETER, "target is not a renderbuffer.");
                    return false;
                }
    
                if (renderbuffer->getSamples() > 0)
                {
                    val->setError(EGL_BAD_PARAMETER, "target renderbuffer cannot be multisampled.");
                    return false;
                }
            }
            break;
    
            case EGL_NATIVE_BUFFER_ANDROID:
            {
                if (!displayExtensions.imageNativeBuffer)
                {
                    val->setError(EGL_BAD_PARAMETER, "EGL_ANDROID_image_native_buffer not supported.");
                    return false;
                }
    
                if (context != nullptr)
                {
                    val->setError(EGL_BAD_CONTEXT, "ctx must be EGL_NO_CONTEXT.");
                    return false;
                }
    
                ANGLE_EGL_TRY_RETURN(
                    val->eglThread,
                    display->validateImageClientBuffer(context, target, buffer, attributes),
                    val->entryPoint, val->labeledObject, false);
            }
            break;
    
            case EGL_D3D11_TEXTURE_ANGLE:
                if (!displayExtensions.imageD3D11Texture)
                {
                    val->setError(EGL_BAD_PARAMETER, "EGL_ANGLE_image_d3d11_texture not supported.");
                    return false;
                }
    
                if (context != nullptr)
                {
                    val->setError(EGL_BAD_CONTEXT, "ctx must be EGL_NO_CONTEXT.");
                    return false;
                }
    
                ANGLE_EGL_TRY_RETURN(
                    val->eglThread,
                    display->validateImageClientBuffer(context, target, buffer, attributes),
                    val->entryPoint, val->labeledObject, false);
                break;
    
            case EGL_LINUX_DMA_BUF_EXT:
                if (!displayExtensions.imageDmaBufImportEXT)
                {
                    val->setError(EGL_BAD_PARAMETER, "EGL_EXT_image_dma_buf_import not supported.");
                    return false;
                }
    
                if (context != nullptr)
                {
                    val->setError(EGL_BAD_CONTEXT, "ctx must be EGL_NO_CONTEXT.");
                    return false;
                }
    
                if (buffer != nullptr)
                {
                    val->setError(EGL_BAD_PARAMETER, "buffer must be NULL.");
                    return false;
                }
    
                {
                    EGLenum kRequiredParameters[] = {EGL_WIDTH,
                                                     EGL_HEIGHT,
                                                     EGL_LINUX_DRM_FOURCC_EXT,
                                                     EGL_DMA_BUF_PLANE0_FD_EXT,
                                                     EGL_DMA_BUF_PLANE0_OFFSET_EXT,
                                                     EGL_DMA_BUF_PLANE0_PITCH_EXT};
                    for (EGLenum requiredParameter : kRequiredParameters)
                    {
                        if (!attributes.contains(requiredParameter))
                        {
                            val->setError(EGL_BAD_PARAMETER,
                                          "Missing required parameter 0x%X for image target "
                                          "EGL_LINUX_DMA_BUF_EXT.",
                                          requiredParameter);
                            return false;
                        }
                    }
    
                    bool containPlane0ModifierLo =
                        attributes.contains(EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT);
                    bool containPlane0ModifierHi =
                        attributes.contains(EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT);
                    bool containPlane1ModifierLo =
                        attributes.contains(EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT);
                    bool containPlane1ModifierHi =
                        attributes.contains(EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT);
                    bool containPlane2ModifierLo =
                        attributes.contains(EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT);
                    bool containPlane2ModifierHi =
                        attributes.contains(EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT);
                    bool containPlane3ModifierLo =
                        attributes.contains(EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT);
                    bool containPlane3ModifierHi =
                        attributes.contains(EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT);
                    if ((containPlane0ModifierLo ^ containPlane0ModifierHi) ||
                        (containPlane1ModifierLo ^ containPlane1ModifierHi) ||
                        (containPlane2ModifierLo ^ containPlane2ModifierHi) ||
                        (containPlane3ModifierLo ^ containPlane3ModifierHi))
                    {
                        val->setError(
                            EGL_BAD_PARAMETER,
                            "the list of attributes contains EGL_DMA_BUF_PLANE*_MODIFIER_LO_EXT "
                            "but not EGL_DMA_BUF_PLANE*_MODIFIER_HI_EXT or vice versa.");
                        return false;
                    }
                }
                break;
    
            default:
                val->setError(EGL_BAD_PARAMETER, "invalid target: 0x%X", target);
                return false;
        }
    
        if (attributes.contains(EGL_GL_TEXTURE_ZOFFSET) && target != EGL_GL_TEXTURE_3D)
        {
            val->setError(EGL_BAD_PARAMETER,
                          "EGL_GL_TEXTURE_ZOFFSET must be used with a 3D texture target.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateDestroyImage(const ValidationContext *val, const Display *display, const Image *image)
    {
        ANGLE_VALIDATION_TRY(ValidateImage(val, display, image));
    
        return true;
    }
    
    bool ValidateCreateImageKHR(const ValidationContext *val,
                                const Display *display,
                                const gl::Context *context,
                                EGLenum target,
                                EGLClientBuffer buffer,
                                const AttributeMap &attributes)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().imageBase && !display->getExtensions().image)
        {
            // It is out of spec what happens when calling an extension function when the extension is
            // not available.
            // EGL_BAD_DISPLAY seems like a reasonable error.
            val->setError(EGL_BAD_DISPLAY, "EGL_KHR_image not supported.");
            return false;
        }
    
        return ValidateCreateImage(val, display, context, target, buffer, attributes);
    }
    
    bool ValidateDestroyImageKHR(const ValidationContext *val,
                                 const Display *display,
                                 const Image *image)
    {
        ANGLE_VALIDATION_TRY(ValidateImage(val, display, image));
    
        if (!display->getExtensions().imageBase && !display->getExtensions().image)
        {
            // It is out of spec what happens when calling an extension function when the extension is
            // not available.
            // EGL_BAD_DISPLAY seems like a reasonable error.
            val->setError(EGL_BAD_DISPLAY);
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreateDeviceANGLE(const ValidationContext *val,
                                   EGLint device_type,
                                   const void *native_device,
                                   const EGLAttrib *attrib_list)
    {
        const ClientExtensions &clientExtensions = Display::GetClientExtensions();
        if (!clientExtensions.deviceCreation)
        {
            val->setError(EGL_BAD_ACCESS, "Device creation extension not active");
            return false;
        }
    
        if (attrib_list != nullptr && attrib_list[0] != EGL_NONE)
        {
            val->setError(EGL_BAD_ATTRIBUTE, "Invalid attrib_list parameter");
            return false;
        }
    
        switch (device_type)
        {
            case EGL_D3D11_DEVICE_ANGLE:
                if (!clientExtensions.deviceCreationD3D11)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "D3D11 device creation extension not active");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid device_type parameter");
                return false;
        }
    
        return true;
    }
    
    bool ValidateReleaseDeviceANGLE(const ValidationContext *val, const Device *device)
    {
        const ClientExtensions &clientExtensions = Display::GetClientExtensions();
        if (!clientExtensions.deviceCreation)
        {
            val->setError(EGL_BAD_ACCESS, "Device creation extension not active");
            return false;
        }
    
        if (device == EGL_NO_DEVICE_EXT || !Device::IsValidDevice(device))
        {
            val->setError(EGL_BAD_DEVICE_EXT, "Invalid device parameter");
            return false;
        }
    
        Display *owningDisplay = device->getOwningDisplay();
        if (owningDisplay != nullptr)
        {
            val->setError(EGL_BAD_DEVICE_EXT, "Device must have been created using eglCreateDevice");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreateSync(const ValidationContext *val,
                            const Display *display,
                            EGLenum type,
                            const AttributeMap &attribs)
    {
        return ValidateCreateSyncBase(val, display, type, attribs, false);
    }
    
    bool ValidateCreateSyncKHR(const ValidationContext *val,
                               const Display *display,
                               EGLenum type,
                               const AttributeMap &attribs)
    {
        return ValidateCreateSyncBase(val, display, type, attribs, true);
    }
    
    bool ValidateDestroySync(const ValidationContext *val, const Display *display, const Sync *sync)
    {
        ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
        return true;
    }
    
    bool ValidateDestroySyncKHR(const ValidationContext *val,
                                const Display *dpyPacked,
                                const Sync *syncPacked)
    {
        return ValidateDestroySync(val, dpyPacked, syncPacked);
    }
    
    bool ValidateClientWaitSync(const ValidationContext *val,
                                const Display *display,
                                const Sync *sync,
                                EGLint flags,
                                EGLTime timeout)
    {
        ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
        return true;
    }
    
    bool ValidateClientWaitSyncKHR(const ValidationContext *val,
                                   const Display *dpyPacked,
                                   const Sync *syncPacked,
                                   EGLint flags,
                                   EGLTimeKHR timeout)
    {
        return ValidateClientWaitSync(val, dpyPacked, syncPacked, flags, timeout);
    }
    
    bool ValidateWaitSync(const ValidationContext *val,
                          const Display *display,
                          const Sync *sync,
                          EGLint flags)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &extensions = display->getExtensions();
        if (!extensions.waitSync)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_KHR_wait_sync extension is not available");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
    
        gl::Context *context = val->eglThread->getContext();
        if (context == nullptr)
        {
            val->setError(EGL_BAD_MATCH, "No context is current.");
            return false;
        }
    
        if (!context->getExtensions().eglSyncOES)
        {
            val->setError(EGL_BAD_MATCH,
                          "Server-side waits cannot be performed without "
                          "GL_OES_EGL_sync support.");
            return false;
        }
    
        if (flags != 0)
        {
            val->setError(EGL_BAD_PARAMETER, "flags must be zero");
            return false;
        }
    
        return true;
    }
    
    bool ValidateWaitSyncKHR(const ValidationContext *val,
                             const Display *dpyPacked,
                             const Sync *syncPacked,
                             EGLint flags)
    {
        return ValidateWaitSync(val, dpyPacked, syncPacked, flags);
    }
    
    bool ValidateGetSyncAttrib(const ValidationContext *val,
                               const Display *display,
                               const Sync *sync,
                               EGLint attribute,
                               const EGLAttrib *value)
    {
        if (value == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "Invalid value parameter");
            return false;
        }
        return ValidateGetSyncAttribBase(val, display, sync, attribute);
    }
    
    bool ValidateGetSyncAttribKHR(const ValidationContext *val,
                                  const Display *display,
                                  const Sync *sync,
                                  EGLint attribute,
                                  const EGLint *value)
    {
        if (value == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "Invalid value parameter");
            return false;
        }
        return ValidateGetSyncAttribBase(val, display, sync, attribute);
    }
    
    bool ValidateCreateStreamKHR(const ValidationContext *val,
                                 const Display *display,
                                 const AttributeMap &attributes)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.stream)
        {
            val->setError(EGL_BAD_ALLOC, "Stream extension not active");
            return false;
        }
    
        for (const auto &attributeIter : attributes)
        {
            EGLAttrib attribute = attributeIter.first;
            EGLAttrib value     = attributeIter.second;
    
            ANGLE_VALIDATION_TRY(ValidateStreamAttribute(val, attribute, value, displayExtensions));
        }
    
        return true;
    }
    
    bool ValidateDestroyStreamKHR(const ValidationContext *val,
                                  const Display *display,
                                  const Stream *stream)
    {
        ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
        return true;
    }
    
    bool ValidateStreamAttribKHR(const ValidationContext *val,
                                 const Display *display,
                                 const Stream *stream,
                                 EGLenum attribute,
                                 EGLint value)
    {
        ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
    
        if (stream->getState() == EGL_STREAM_STATE_DISCONNECTED_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Bad stream state");
            return false;
        }
    
        return ValidateStreamAttribute(val, attribute, value, display->getExtensions());
    }
    
    bool ValidateQueryStreamKHR(const ValidationContext *val,
                                const Display *display,
                                const Stream *stream,
                                EGLenum attribute,
                                const EGLint *value)
    {
        ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
    
        switch (attribute)
        {
            case EGL_STREAM_STATE_KHR:
            case EGL_CONSUMER_LATENCY_USEC_KHR:
                break;
            case EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR:
                if (!display->getExtensions().streamConsumerGLTexture)
                {
                    val->setError(EGL_BAD_ATTRIBUTE, "Consumer GLTexture extension not active");
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                return false;
        }
    
        return true;
    }
    
    bool ValidateQueryStreamu64KHR(const ValidationContext *val,
                                   const Display *display,
                                   const Stream *stream,
                                   EGLenum attribute,
                                   const EGLuint64KHR *value)
    {
        ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
    
        switch (attribute)
        {
            case EGL_CONSUMER_FRAME_KHR:
            case EGL_PRODUCER_FRAME_KHR:
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                return false;
        }
    
        return true;
    }
    
    bool ValidateStreamConsumerGLTextureExternalKHR(const ValidationContext *val,
                                                    const Display *display,
                                                    const Stream *stream)
    {
        gl::Context *context = val->eglThread->getContext();
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.streamConsumerGLTexture)
        {
            val->setError(EGL_BAD_ACCESS, "Stream consumer extension not active");
            return false;
        }
    
        if (!context->getExtensions().eglStreamConsumerExternalNV)
        {
            val->setError(EGL_BAD_ACCESS, "EGL stream consumer external GL extension not enabled");
            return false;
        }
    
        if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
        {
            val->setError(EGL_BAD_STREAM_KHR, "Invalid stream");
            return false;
        }
    
        if (stream->getState() != EGL_STREAM_STATE_CREATED_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Invalid stream state");
            return false;
        }
    
        // Lookup the texture and ensure it is correct
        gl::Texture *texture = context->getState().getTargetTexture(gl::TextureType::External);
        if (texture == nullptr || texture->id().value == 0)
        {
            val->setError(EGL_BAD_ACCESS, "No external texture bound");
            return false;
        }
    
        return true;
    }
    
    bool ValidateStreamConsumerAcquireKHR(const ValidationContext *val,
                                          const Display *display,
                                          const Stream *stream)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.streamConsumerGLTexture)
        {
            val->setError(EGL_BAD_ACCESS, "Stream consumer extension not active");
            return false;
        }
    
        if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
        {
            val->setError(EGL_BAD_STREAM_KHR, "Invalid stream");
            return false;
        }
    
        gl::Context *context = val->eglThread->getContext();
        if (!context)
        {
            val->setError(EGL_BAD_ACCESS, "No GL context current to calling thread.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
    
        if (!stream->isConsumerBoundToContext(context))
        {
            val->setError(EGL_BAD_ACCESS, "Current GL context not associated with stream consumer");
            return false;
        }
    
        if (stream->getConsumerType() != Stream::ConsumerType::GLTextureRGB &&
            stream->getConsumerType() != Stream::ConsumerType::GLTextureYUV)
        {
            val->setError(EGL_BAD_ACCESS, "Invalid stream consumer type");
            return false;
        }
    
        // Note: technically EGL_STREAM_STATE_EMPTY_KHR is a valid state when the timeout is non-zero.
        // However, the timeout is effectively ignored since it has no useful functionality with the
        // current producers that are implemented, so we don't allow that state
        if (stream->getState() != EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR &&
            stream->getState() != EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Invalid stream state");
            return false;
        }
    
        return true;
    }
    
    bool ValidateStreamConsumerReleaseKHR(const ValidationContext *val,
                                          const Display *display,
                                          const Stream *stream)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.streamConsumerGLTexture)
        {
            val->setError(EGL_BAD_ACCESS, "Stream consumer extension not active");
            return false;
        }
    
        if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
        {
            val->setError(EGL_BAD_STREAM_KHR, "Invalid stream");
            return false;
        }
    
        gl::Context *context = val->eglThread->getContext();
        if (!context)
        {
            val->setError(EGL_BAD_ACCESS, "No GL context current to calling thread.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
    
        if (!stream->isConsumerBoundToContext(context))
        {
            val->setError(EGL_BAD_ACCESS, "Current GL context not associated with stream consumer");
            return false;
        }
    
        if (stream->getConsumerType() != Stream::ConsumerType::GLTextureRGB &&
            stream->getConsumerType() != Stream::ConsumerType::GLTextureYUV)
        {
            val->setError(EGL_BAD_ACCESS, "Invalid stream consumer type");
            return false;
        }
    
        if (stream->getState() != EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR &&
            stream->getState() != EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Invalid stream state");
            return false;
        }
    
        return true;
    }
    
    bool ValidateStreamConsumerGLTextureExternalAttribsNV(const ValidationContext *val,
                                                          const Display *display,
                                                          const Stream *stream,
                                                          const AttributeMap &attribs)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.streamConsumerGLTexture)
        {
            val->setError(EGL_BAD_ACCESS, "Stream consumer extension not active");
            return false;
        }
    
        gl::Context *context = val->eglThread->getContext();
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
    
        // Although technically not a requirement in spec, the context needs to be checked for support
        // for external textures or future logic will cause assertations. This extension is also
        // effectively useless without external textures.
        if (!context->getExtensions().eglStreamConsumerExternalNV)
        {
            val->setError(EGL_BAD_ACCESS, "EGL stream consumer external GL extension not enabled");
            return false;
        }
    
        if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
        {
            val->setError(EGL_BAD_STREAM_KHR, "Invalid stream");
            return false;
        }
    
        if (stream->getState() != EGL_STREAM_STATE_CREATED_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Invalid stream state");
            return false;
        }
    
        const gl::Caps &glCaps = context->getCaps();
    
        EGLAttrib colorBufferType = EGL_RGB_BUFFER;
        EGLAttrib planeCount      = -1;
        EGLAttrib plane[3];
        for (int i = 0; i < 3; i++)
        {
            plane[i] = -1;
        }
        for (const auto &attributeIter : attribs)
        {
            EGLAttrib attribute = attributeIter.first;
            EGLAttrib value     = attributeIter.second;
    
            switch (attribute)
            {
                case EGL_COLOR_BUFFER_TYPE:
                    if (value != EGL_RGB_BUFFER && value != EGL_YUV_BUFFER_EXT)
                    {
                        val->setError(EGL_BAD_PARAMETER, "Invalid color buffer type");
                        return false;
                    }
                    colorBufferType = value;
                    break;
                case EGL_YUV_NUMBER_OF_PLANES_EXT:
                    // planeCount = -1 is a tag for the default plane count so the value must be checked
                    // to be positive here to ensure future logic doesn't break on invalid negative
                    // inputs
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_MATCH, "Invalid plane count");
                        return false;
                    }
                    planeCount = value;
                    break;
                default:
                    if (attribute >= EGL_YUV_PLANE0_TEXTURE_UNIT_NV &&
                        attribute <= EGL_YUV_PLANE2_TEXTURE_UNIT_NV)
                    {
                        if ((value < 0 ||
                             value >= static_cast<EGLAttrib>(glCaps.maxCombinedTextureImageUnits)) &&
                            value != EGL_NONE)
                        {
                            val->setError(EGL_BAD_ACCESS, "Invalid texture unit");
                            return false;
                        }
                        plane[attribute - EGL_YUV_PLANE0_TEXTURE_UNIT_NV] = value;
                    }
                    else
                    {
                        val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                        return false;
                    }
            }
        }
    
        if (colorBufferType == EGL_RGB_BUFFER)
        {
            if (planeCount > 0)
            {
                val->setError(EGL_BAD_MATCH, "Plane count must be 0 for RGB buffer");
                return false;
            }
            for (int i = 0; i < 3; i++)
            {
                if (plane[i] != -1)
                {
                    val->setError(EGL_BAD_MATCH, "Planes cannot be specified");
                    return false;
                }
            }
    
            // Lookup the texture and ensure it is correct
            gl::Texture *texture = context->getState().getTargetTexture(gl::TextureType::External);
            if (texture == nullptr || texture->id().value == 0)
            {
                val->setError(EGL_BAD_ACCESS, "No external texture bound");
                return false;
            }
        }
        else
        {
            if (planeCount == -1)
            {
                planeCount = 2;
            }
            if (planeCount < 1 || planeCount > 3)
            {
                val->setError(EGL_BAD_MATCH, "Invalid YUV plane count");
                return false;
            }
            for (EGLAttrib i = planeCount; i < 3; i++)
            {
                if (plane[i] != -1)
                {
                    val->setError(EGL_BAD_MATCH, "Invalid plane specified");
                    return false;
                }
            }
    
            // Set to ensure no texture is referenced more than once
            std::set<gl::Texture *> textureSet;
            for (EGLAttrib i = 0; i < planeCount; i++)
            {
                if (plane[i] == -1)
                {
                    val->setError(EGL_BAD_MATCH, "Not all planes specified");
                    return false;
                }
                if (plane[i] != EGL_NONE)
                {
                    gl::Texture *texture = context->getState().getSamplerTexture(
                        static_cast<unsigned int>(plane[i]), gl::TextureType::External);
                    if (texture == nullptr || texture->id().value == 0)
                    {
                        val->setError(
                            EGL_BAD_ACCESS,
                            "No external texture bound at one or more specified texture units");
                        return false;
                    }
                    if (textureSet.find(texture) != textureSet.end())
                    {
                        val->setError(EGL_BAD_ACCESS, "Multiple planes bound to same texture object");
                        return false;
                    }
                    textureSet.insert(texture);
                }
            }
        }
    
        return true;
    }
    
    bool ValidateCreateStreamProducerD3DTextureANGLE(const ValidationContext *val,
                                                     const Display *display,
                                                     const Stream *stream,
                                                     const AttributeMap &attribs)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.streamProducerD3DTexture)
        {
            val->setError(EGL_BAD_ACCESS, "Stream producer extension not active");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
    
        if (!attribs.isEmpty())
        {
            val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
            return false;
        }
    
        if (stream->getState() != EGL_STREAM_STATE_CONNECTING_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Stream not in connecting state");
            return false;
        }
    
        switch (stream->getConsumerType())
        {
            case Stream::ConsumerType::GLTextureYUV:
                if (stream->getPlaneCount() != 2)
                {
                    val->setError(EGL_BAD_MATCH, "Incompatible stream consumer type");
                    return false;
                }
                break;
    
            case Stream::ConsumerType::GLTextureRGB:
                if (stream->getPlaneCount() != 1)
                {
                    val->setError(EGL_BAD_MATCH, "Incompatible stream consumer type");
                    return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_MATCH, "Incompatible stream consumer type");
                return false;
        }
    
        return true;
    }
    
    bool ValidateStreamPostD3DTextureANGLE(const ValidationContext *val,
                                           const Display *display,
                                           const Stream *stream,
                                           const void *texture,
                                           const AttributeMap &attribs)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.streamProducerD3DTexture)
        {
            val->setError(EGL_BAD_ACCESS, "Stream producer extension not active");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateStream(val, display, stream));
    
        for (auto &attributeIter : attribs)
        {
            EGLAttrib attribute = attributeIter.first;
            EGLAttrib value     = attributeIter.second;
    
            switch (attribute)
            {
                case EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE:
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER, "Invalid subresource index");
                        return false;
                    }
                    break;
                case EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG:
                    if (value < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER, "Invalid plane offset");
                        return false;
                    }
                    break;
                default:
                    val->setError(EGL_BAD_ATTRIBUTE, "Invalid attribute");
                    return false;
            }
        }
    
        if (stream->getState() != EGL_STREAM_STATE_EMPTY_KHR &&
            stream->getState() != EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR &&
            stream->getState() != EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR)
        {
            val->setError(EGL_BAD_STATE_KHR, "Stream not fully configured");
            return false;
        }
    
        if (stream->getProducerType() != Stream::ProducerType::D3D11Texture)
        {
            val->setError(EGL_BAD_MATCH, "Incompatible stream producer");
            return false;
        }
    
        if (texture == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "Texture is null");
            return false;
        }
    
        ANGLE_EGL_TRY_RETURN(val->eglThread, stream->validateD3D11Texture(texture, attribs),
                             val->entryPoint, val->labeledObject, false);
    
        return true;
    }
    
    bool ValidateSyncControlCHROMIUM(const ValidationContext *val,
                                     const Display *display,
                                     const Surface *eglSurface)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, eglSurface));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.syncControlCHROMIUM)
        {
            val->setError(EGL_BAD_ACCESS, "syncControlCHROMIUM extension not active");
            return false;
        }
    
        return true;
    }
    
    bool ValidateSyncControlRateANGLE(const ValidationContext *val,
                                      const Display *display,
                                      const Surface *eglSurface)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, eglSurface));
    
        const DisplayExtensions &displayExtensions = display->getExtensions();
        if (!displayExtensions.syncControlRateANGLE)
        {
            val->setError(EGL_BAD_ACCESS, "syncControlRateANGLE extension not active");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetMscRateANGLE(const ValidationContext *val,
                                 const Display *display,
                                 const Surface *eglSurface,
                                 const EGLint *numerator,
                                 const EGLint *denominator)
    {
        ANGLE_VALIDATION_TRY(ValidateSyncControlRateANGLE(val, display, eglSurface));
    
        if (numerator == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "numerator is null");
            return false;
        }
        if (denominator == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "denominator is null");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetSyncValuesCHROMIUM(const ValidationContext *val,
                                       const Display *display,
                                       const Surface *eglSurface,
                                       const EGLuint64KHR *ust,
                                       const EGLuint64KHR *msc,
                                       const EGLuint64KHR *sbc)
    {
        ANGLE_VALIDATION_TRY(ValidateSyncControlCHROMIUM(val, display, eglSurface));
    
        if (ust == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "ust is null");
            return false;
        }
        if (msc == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "msc is null");
            return false;
        }
        if (sbc == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "sbc is null");
            return false;
        }
    
        return true;
    }
    
    bool ValidateDestroySurface(const ValidationContext *val,
                                const Display *display,
                                const Surface *surface)
    {
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
        return true;
    }
    
    bool ValidateDestroyContext(const ValidationContext *val,
                                const Display *display,
                                const gl::Context *glCtx)
    {
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, glCtx));
        return true;
    }
    
    bool ValidateSwapBuffers(const ValidationContext *val,
                             const Display *display,
                             const Surface *eglSurface)
    {
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, eglSurface));
    
        if (display->isDeviceLost())
        {
            val->setError(EGL_CONTEXT_LOST);
            return false;
        }
    
        if (eglSurface == EGL_NO_SURFACE || !val->eglThread->getContext() ||
            val->eglThread->getCurrentDrawSurface() != eglSurface)
        {
            val->setError(EGL_BAD_SURFACE);
            return false;
        }
    
        return true;
    }
    
    bool ValidateSwapBuffersWithDamageKHR(const ValidationContext *val,
                                          const Display *display,
                                          const Surface *surface,
                                          const EGLint *rects,
                                          EGLint n_rects)
    {
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (!display->getExtensions().swapBuffersWithDamage)
        {
            // It is out of spec what happens when calling an extension function when the extension is
            // not available. EGL_BAD_DISPLAY seems like a reasonable error.
            val->setError(EGL_BAD_DISPLAY, "EGL_KHR_swap_buffers_with_damage is not available.");
            return false;
        }
    
        if (surface == EGL_NO_SURFACE)
        {
            val->setError(EGL_BAD_SURFACE, "Swap surface cannot be EGL_NO_SURFACE.");
            return false;
        }
    
        if (n_rects < 0)
        {
            val->setError(EGL_BAD_PARAMETER, "n_rects cannot be negative.");
            return false;
        }
    
        if (n_rects > 0 && rects == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "n_rects cannot be greater than zero when rects is NULL.");
            return false;
        }
    
        // TODO(jmadill): Validate Surface is bound to the thread.
    
        return true;
    }
    
    bool ValidateWaitNative(const ValidationContext *val, const EGLint engine)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, val->eglThread->getDisplay()));
    
        if (engine != EGL_CORE_NATIVE_ENGINE)
        {
            val->setError(EGL_BAD_PARAMETER, "the 'engine' parameter has an unrecognized value");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCopyBuffers(const ValidationContext *val,
                             const Display *display,
                             const Surface *surface,
                             EGLNativePixmapType target)
    {
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (display->isDeviceLost())
        {
            val->setError(EGL_CONTEXT_LOST);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBindTexImage(const ValidationContext *val,
                              const Display *display,
                              const Surface *surface,
                              const EGLint buffer)
    {
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (buffer != EGL_BACK_BUFFER)
        {
            val->setError(EGL_BAD_PARAMETER);
            return false;
        }
    
        if (surface->getType() == EGL_WINDOW_BIT)
        {
            val->setError(EGL_BAD_SURFACE);
            return false;
        }
    
        if (surface->getBoundTexture())
        {
            val->setError(EGL_BAD_ACCESS);
            return false;
        }
    
        if (surface->getTextureFormat() == TextureFormat::NoTexture)
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
    
        gl::Context *context = val->eglThread->getContext();
        if (context)
        {
            gl::TextureType type = egl_gl::EGLTextureTargetToTextureType(surface->getTextureTarget());
            gl::Texture *textureObject = context->getTextureByType(type);
            ASSERT(textureObject != nullptr);
    
            if (textureObject->getImmutableFormat())
            {
                val->setError(EGL_BAD_MATCH);
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateReleaseTexImage(const ValidationContext *val,
                                 const Display *display,
                                 const Surface *surface,
                                 const EGLint buffer)
    {
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (buffer != EGL_BACK_BUFFER)
        {
            val->setError(EGL_BAD_PARAMETER);
            return false;
        }
    
        if (surface->getType() == EGL_WINDOW_BIT)
        {
            val->setError(EGL_BAD_SURFACE);
            return false;
        }
    
        if (surface->getTextureFormat() == TextureFormat::NoTexture)
        {
            val->setError(EGL_BAD_MATCH);
            return false;
        }
    
        return true;
    }
    
    bool ValidateSwapInterval(const ValidationContext *val, const Display *display, EGLint interval)
    {
        const gl::Context *context = val->eglThread->getContext();
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
    
        Surface *drawSurface = val->eglThread->getCurrentDrawSurface();
        if (drawSurface == nullptr)
        {
            val->setError(EGL_BAD_SURFACE);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBindAPI(const ValidationContext *val, const EGLenum api)
    {
        switch (api)
        {
            case EGL_OPENGL_API:
            case EGL_OPENVG_API:
                val->setError(EGL_BAD_PARAMETER);
                return false;  // Not supported by this implementation
            case EGL_OPENGL_ES_API:
                break;
            default:
                val->setError(EGL_BAD_PARAMETER);
                return false;
        }
    
        return true;
    }
    
    bool ValidatePresentationTimeANDROID(const ValidationContext *val,
                                         const Display *display,
                                         const Surface *surface,
                                         EGLnsecsANDROID time)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().presentationTime)
        {
            // It is out of spec what happens when calling an extension function when the extension is
            // not available. EGL_BAD_DISPLAY seems like a reasonable error.
            val->setError(EGL_BAD_DISPLAY, "EGL_ANDROID_presentation_time is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        return true;
    }
    
    bool ValidateSetBlobCacheFuncsANDROID(const ValidationContext *val,
                                          const Display *display,
                                          EGLSetBlobFuncANDROID set,
                                          EGLGetBlobFuncANDROID get)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (display->areBlobCacheFuncsSet())
        {
            val->setError(EGL_BAD_PARAMETER,
                          "Blob cache functions can only be set once in the lifetime of a Display");
            return false;
        }
    
        if (set == nullptr || get == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "Blob cache callbacks cannot be null.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetConfigAttrib(const ValidationContext *val,
                                 const Display *display,
                                 const Config *config,
                                 EGLint attribute,
                                 const EGLint *value)
    {
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, config));
        ANGLE_TRY(ValidateConfigAttribute(val, display, static_cast<EGLAttrib>(attribute)));
        return true;
    }
    
    bool ValidateChooseConfig(const ValidationContext *val,
                              const Display *display,
                              const AttributeMap &attribs,
                              const EGLConfig *configs,
                              EGLint configSize,
                              const EGLint *numConfig)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
        ANGLE_VALIDATION_TRY(ValidateConfigAttributes(val, display, attribs));
    
        if (numConfig == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "num_config cannot be null.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetConfigs(const ValidationContext *val,
                            const Display *display,
                            const EGLConfig *configs,
                            EGLint configSize,
                            const EGLint *numConfig)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (numConfig == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "num_config cannot be null.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetPlatformDisplay(const ValidationContext *val,
                                    EGLenum platform,
                                    const void *native_display,
                                    const AttributeMap &attribMap)
    {
        return ValidateGetPlatformDisplayCommon(val, platform, native_display, attribMap);
    }
    
    bool ValidateGetPlatformDisplayEXT(const ValidationContext *val,
                                       EGLenum platform,
                                       const void *native_display,
                                       const AttributeMap &attribMap)
    {
        return ValidateGetPlatformDisplayCommon(val, platform, native_display, attribMap);
    }
    
    bool ValidateCreatePlatformWindowSurfaceEXT(const ValidationContext *val,
                                                const Display *display,
                                                const Config *configuration,
                                                const void *nativeWindow,
                                                const AttributeMap &attributes)
    {
        if (!Display::GetClientExtensions().platformBase)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_EXT_platform_base not supported");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, configuration));
    
        val->setError(EGL_BAD_DISPLAY, "ValidateCreatePlatformWindowSurfaceEXT unimplemented.");
        return false;
    }
    
    bool ValidateCreatePlatformPixmapSurfaceEXT(const ValidationContext *val,
                                                const Display *display,
                                                const Config *configuration,
                                                const void *nativePixmap,
                                                const AttributeMap &attributes)
    {
        if (!Display::GetClientExtensions().platformBase)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_EXT_platform_base not supported");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateConfig(val, display, configuration));
    
        val->setError(EGL_BAD_DISPLAY, "ValidateCreatePlatformPixmapSurfaceEXT unimplemented.");
        return false;
    }
    
    bool ValidateProgramCacheGetAttribANGLE(const ValidationContext *val,
                                            const Display *display,
                                            EGLenum attrib)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().programCacheControl)
        {
            val->setError(EGL_BAD_ACCESS, "Extension not supported");
            return false;
        }
    
        switch (attrib)
        {
            case EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE:
            case EGL_PROGRAM_CACHE_SIZE_ANGLE:
                break;
    
            default:
                val->setError(EGL_BAD_PARAMETER, "Invalid program cache attribute.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateProgramCacheQueryANGLE(const ValidationContext *val,
                                        const Display *display,
                                        EGLint index,
                                        const void *key,
                                        const EGLint *keysize,
                                        const void *binary,
                                        const EGLint *binarysize)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().programCacheControl)
        {
            val->setError(EGL_BAD_ACCESS, "Extension not supported");
            return false;
        }
    
        if (index < 0 || index >= display->programCacheGetAttrib(EGL_PROGRAM_CACHE_SIZE_ANGLE))
        {
            val->setError(EGL_BAD_PARAMETER, "Program index out of range.");
            return false;
        }
    
        if (keysize == nullptr || binarysize == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "keysize and binarysize must always be valid pointers.");
            return false;
        }
    
        if (binary && *keysize != static_cast<EGLint>(egl::BlobCache::kKeyLength))
        {
            val->setError(EGL_BAD_PARAMETER, "Invalid program key size.");
            return false;
        }
    
        if ((key == nullptr) != (binary == nullptr))
        {
            val->setError(EGL_BAD_PARAMETER, "key and binary must both be null or both non-null.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateProgramCachePopulateANGLE(const ValidationContext *val,
                                           const Display *display,
                                           const void *key,
                                           EGLint keysize,
                                           const void *binary,
                                           EGLint binarysize)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().programCacheControl)
        {
            val->setError(EGL_BAD_ACCESS, "Extension not supported");
            return false;
        }
    
        if (keysize != static_cast<EGLint>(egl::BlobCache::kKeyLength))
        {
            val->setError(EGL_BAD_PARAMETER, "Invalid program key size.");
            return false;
        }
    
        if (key == nullptr || binary == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "null pointer in arguments.");
            return false;
        }
    
        // Upper bound for binarysize is arbitrary.
        if (binarysize <= 0 || binarysize > egl::kProgramCacheSizeAbsoluteMax)
        {
            val->setError(EGL_BAD_PARAMETER, "binarysize out of valid range.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateProgramCacheResizeANGLE(const ValidationContext *val,
                                         const Display *display,
                                         EGLint limit,
                                         EGLint mode)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().programCacheControl)
        {
            val->setError(EGL_BAD_ACCESS, "Extension not supported");
            return false;
        }
    
        if (limit < 0)
        {
            val->setError(EGL_BAD_PARAMETER, "limit must be non-negative.");
            return false;
        }
    
        switch (mode)
        {
            case EGL_PROGRAM_CACHE_RESIZE_ANGLE:
            case EGL_PROGRAM_CACHE_TRIM_ANGLE:
                break;
    
            default:
                val->setError(EGL_BAD_PARAMETER, "Invalid cache resize mode.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateSurfaceAttrib(const ValidationContext *val,
                               const Display *display,
                               const Surface *surface,
                               EGLint attribute,
                               EGLint value)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (surface == EGL_NO_SURFACE)
        {
            val->setError(EGL_BAD_SURFACE, "Surface cannot be EGL_NO_SURFACE.");
            return false;
        }
    
        switch (attribute)
        {
            case EGL_MIPMAP_LEVEL:
                break;
    
            case EGL_MULTISAMPLE_RESOLVE:
                switch (value)
                {
                    case EGL_MULTISAMPLE_RESOLVE_DEFAULT:
                        break;
    
                    case EGL_MULTISAMPLE_RESOLVE_BOX:
                        if ((surface->getConfig()->surfaceType & EGL_MULTISAMPLE_RESOLVE_BOX_BIT) == 0)
                        {
                            val->setError(EGL_BAD_MATCH,
                                          "Surface does not support EGL_MULTISAMPLE_RESOLVE_BOX.");
                            return false;
                        }
                        break;
    
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE, "Invalid multisample resolve type.");
                        return false;
                }
                break;
    
            case EGL_SWAP_BEHAVIOR:
                switch (value)
                {
                    case EGL_BUFFER_PRESERVED:
                        if ((surface->getConfig()->surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) == 0)
                        {
                            val->setError(EGL_BAD_MATCH,
                                          "Surface does not support EGL_SWAP_BEHAVIOR_PRESERVED.");
                            return false;
                        }
                        break;
    
                    case EGL_BUFFER_DESTROYED:
                        break;
    
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE, "Invalid swap behaviour.");
                        return false;
                }
                break;
    
            case EGL_WIDTH:
            case EGL_HEIGHT:
                if (!display->getExtensions().windowFixedSize)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_WIDTH or EGL_HEIGHT cannot be set without "
                                  "EGL_ANGLE_window_fixed_size support.");
                    return false;
                }
                if (!surface->isFixedSize())
                {
                    val->setError(EGL_BAD_MATCH,
                                  "EGL_WIDTH or EGL_HEIGHT cannot be set without "
                                  "EGL_FIXED_SIZE_ANGLE being enabled on the surface.");
                    return false;
                }
                break;
    
            case EGL_TIMESTAMPS_ANDROID:
                if (!display->getExtensions().getFrameTimestamps)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_TIMESTAMPS_ANDROID cannot be used without "
                                  "EGL_ANDROID_get_frame_timestamps support.");
                    return false;
                }
                switch (value)
                {
                    case EGL_TRUE:
                    case EGL_FALSE:
                        break;
    
                    default:
                        val->setError(EGL_BAD_ATTRIBUTE, "Invalid value.");
                        return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid surface attribute.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateQuerySurface(const ValidationContext *val,
                              const Display *display,
                              const Surface *surface,
                              EGLint attribute,
                              const EGLint *value)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (surface == EGL_NO_SURFACE)
        {
            val->setError(EGL_BAD_SURFACE, "Surface cannot be EGL_NO_SURFACE.");
            return false;
        }
    
        switch (attribute)
        {
            case EGL_GL_COLORSPACE:
            case EGL_VG_ALPHA_FORMAT:
            case EGL_VG_COLORSPACE:
            case EGL_CONFIG_ID:
            case EGL_HEIGHT:
            case EGL_HORIZONTAL_RESOLUTION:
            case EGL_LARGEST_PBUFFER:
            case EGL_MIPMAP_TEXTURE:
            case EGL_MIPMAP_LEVEL:
            case EGL_MULTISAMPLE_RESOLVE:
            case EGL_PIXEL_ASPECT_RATIO:
            case EGL_RENDER_BUFFER:
            case EGL_SWAP_BEHAVIOR:
            case EGL_TEXTURE_FORMAT:
            case EGL_TEXTURE_TARGET:
            case EGL_VERTICAL_RESOLUTION:
            case EGL_WIDTH:
                break;
    
            case EGL_POST_SUB_BUFFER_SUPPORTED_NV:
                if (!display->getExtensions().postSubBuffer)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_POST_SUB_BUFFER_SUPPORTED_NV cannot be used "
                                  "without EGL_ANGLE_surface_orientation support.");
                    return false;
                }
                break;
    
            case EGL_FIXED_SIZE_ANGLE:
                if (!display->getExtensions().windowFixedSize)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_FIXED_SIZE_ANGLE cannot be used without "
                                  "EGL_ANGLE_window_fixed_size support.");
                    return false;
                }
                break;
    
            case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
                if (!display->getExtensions().flexibleSurfaceCompatibility)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be "
                                  "used without EGL_ANGLE_flexible_surface_compatibility support.");
                    return false;
                }
                break;
    
            case EGL_SURFACE_ORIENTATION_ANGLE:
                if (!display->getExtensions().surfaceOrientation)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_SURFACE_ORIENTATION_ANGLE cannot be "
                                  "queried without "
                                  "EGL_ANGLE_surface_orientation support.");
                    return false;
                }
                break;
    
            case EGL_DIRECT_COMPOSITION_ANGLE:
                if (!display->getExtensions().directComposition)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_DIRECT_COMPOSITION_ANGLE cannot be "
                                  "used without "
                                  "EGL_ANGLE_direct_composition support.");
                    return false;
                }
                break;
    
            case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
                if (!display->getExtensions().robustResourceInitialization)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE cannot be "
                                  "used without EGL_ANGLE_robust_resource_initialization "
                                  "support.");
                    return false;
                }
                break;
    
            case EGL_TIMESTAMPS_ANDROID:
                if (!display->getExtensions().getFrameTimestamps)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_TIMESTAMPS_ANDROID cannot be used without "
                                  "EGL_ANDROID_get_frame_timestamps support.");
                    return false;
                }
                break;
    
            case EGL_BUFFER_AGE_EXT:
            {
                if (!display->getExtensions().bufferAgeEXT)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_BUFFER_AGE_EXT cannot be used without "
                                  "EGL_EXT_buffer_age support.");
                    return false;
                }
                gl::Context *context = val->eglThread->getContext();
                if ((context == nullptr) || (context->getCurrentDrawSurface() != surface))
                {
                    val->setError(EGL_BAD_SURFACE,
                                  "The surface must be current to the current context "
                                  "in order to query buffer age per extension "
                                  "EGL_EXT_buffer_age.");
                    return false;
                }
            }
            break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid surface attribute.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateQueryContext(const ValidationContext *val,
                              const Display *display,
                              const gl::Context *context,
                              EGLint attribute,
                              const EGLint *value)
    {
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
    
        switch (attribute)
        {
            case EGL_CONFIG_ID:
            case EGL_CONTEXT_CLIENT_TYPE:
            case EGL_CONTEXT_CLIENT_VERSION:
            case EGL_RENDER_BUFFER:
                break;
    
            case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
                if (!display->getExtensions().robustResourceInitialization)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE cannot be "
                                  "used without EGL_ANGLE_robust_resource_initialization "
                                  "support.");
                    return false;
                }
                break;
    
            case EGL_CONTEXT_PRIORITY_LEVEL_IMG:
                if (!display->getExtensions().contextPriority)
                {
                    val->setError(EGL_BAD_ATTRIBUTE,
                                  "Attribute EGL_CONTEXT_PRIORITY_LEVEL_IMG requires "
                                  "extension EGL_IMG_context_priority.");
                    return false;
                }
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "Invalid context attribute.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateDebugMessageControlKHR(const ValidationContext *val,
                                        EGLDEBUGPROCKHR callback,
                                        const AttributeMap &attribs)
    {
        const ClientExtensions &clientExtensions = Display::GetClientExtensions();
        if (!clientExtensions.debug)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_KHR_debug extension is not available.");
            return false;
        }
    
        for (const auto &attrib : attribs)
        {
            switch (attrib.first)
            {
                case EGL_DEBUG_MSG_CRITICAL_KHR:
                case EGL_DEBUG_MSG_ERROR_KHR:
                case EGL_DEBUG_MSG_WARN_KHR:
                case EGL_DEBUG_MSG_INFO_KHR:
                    if (attrib.second != EGL_TRUE && attrib.second != EGL_FALSE)
                    {
                        val->setError(EGL_BAD_ATTRIBUTE,
                                      "message controls must be EGL_TRUE or EGL_FALSE.");
                        return false;
                    }
                    break;
            }
        }
    
        return true;
    }
    
    bool ValidateQueryDebugKHR(const ValidationContext *val, EGLint attribute, const EGLAttrib *value)
    {
        const ClientExtensions &clientExtensions = Display::GetClientExtensions();
        if (!clientExtensions.debug)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_KHR_debug extension is not available.");
            return false;
        }
    
        switch (attribute)
        {
            case EGL_DEBUG_MSG_CRITICAL_KHR:
            case EGL_DEBUG_MSG_ERROR_KHR:
            case EGL_DEBUG_MSG_WARN_KHR:
            case EGL_DEBUG_MSG_INFO_KHR:
            case EGL_DEBUG_CALLBACK_KHR:
                break;
    
            default:
                val->setError(EGL_BAD_ATTRIBUTE, "unknown attribute.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateLabelObjectKHR(const ValidationContext *val,
                                const Display *display,
                                ObjectType objectType,
                                EGLObjectKHR object,
                                EGLLabelKHR label)
    {
        const ClientExtensions &clientExtensions = Display::GetClientExtensions();
        if (!clientExtensions.debug)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_KHR_debug extension is not available.");
            return false;
        }
    
        LabeledObject *labeledObject = nullptr;
        ANGLE_VALIDATION_TRY(ValidateLabeledObject(val, display, objectType, object, &labeledObject));
    
        return true;
    }
    
    bool ValidateGetCompositorTimingSupportedANDROID(const ValidationContext *val,
                                                     const Display *display,
                                                     const Surface *surface,
                                                     CompositorTiming name)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().getFrameTimestamps)
        {
            val->setError(EGL_BAD_DISPLAY,
                          "EGL_ANDROID_get_frame_timestamps extension is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (!ValidCompositorTimingName(name))
        {
            val->setError(EGL_BAD_PARAMETER, "invalid timing name.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetCompositorTimingANDROID(const ValidationContext *val,
                                            const Display *display,
                                            const Surface *surface,
                                            EGLint numTimestamps,
                                            const EGLint *names,
                                            const EGLnsecsANDROID *values)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().getFrameTimestamps)
        {
            val->setError(EGL_BAD_DISPLAY,
                          "EGL_ANDROID_get_frame_timestamps extension is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (names == nullptr && numTimestamps > 0)
        {
            val->setError(EGL_BAD_PARAMETER, "names is NULL.");
            return false;
        }
    
        if (values == nullptr && numTimestamps > 0)
        {
            val->setError(EGL_BAD_PARAMETER, "values is NULL.");
            return false;
        }
    
        if (numTimestamps < 0)
        {
            val->setError(EGL_BAD_PARAMETER, "numTimestamps must be at least 0.");
            return false;
        }
    
        for (EGLint i = 0; i < numTimestamps; i++)
        {
            CompositorTiming name = FromEGLenum<CompositorTiming>(names[i]);
    
            if (!ValidCompositorTimingName(name))
            {
                val->setError(EGL_BAD_PARAMETER, "invalid compositor timing.");
                return false;
            }
    
            if (!surface->getSupportedCompositorTimings().test(name))
            {
                val->setError(EGL_BAD_PARAMETER, "compositor timing not supported by surface.");
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateGetNextFrameIdANDROID(const ValidationContext *val,
                                       const Display *display,
                                       const Surface *surface,
                                       const EGLuint64KHR *frameId)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().getFrameTimestamps)
        {
            val->setError(EGL_BAD_DISPLAY,
                          "EGL_ANDROID_get_frame_timestamps extension is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (frameId == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "frameId is NULL.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetFrameTimestampSupportedANDROID(const ValidationContext *val,
                                                   const Display *display,
                                                   const Surface *surface,
                                                   Timestamp timestamp)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().getFrameTimestamps)
        {
            val->setError(EGL_BAD_DISPLAY,
                          "EGL_ANDROID_get_frame_timestamps extension is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (!ValidTimestampType(timestamp))
        {
            val->setError(EGL_BAD_PARAMETER, "invalid timestamp type.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetFrameTimestampsANDROID(const ValidationContext *val,
                                           const Display *display,
                                           const Surface *surface,
                                           EGLuint64KHR frameId,
                                           EGLint numTimestamps,
                                           const EGLint *timestamps,
                                           const EGLnsecsANDROID *values)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().getFrameTimestamps)
        {
            val->setError(EGL_BAD_DISPLAY,
                          "EGL_ANDROID_get_frame_timestamps extension is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        if (!surface->isTimestampsEnabled())
        {
            val->setError(EGL_BAD_SURFACE, "timestamp collection is not enabled for this surface.");
            return false;
        }
    
        if (timestamps == nullptr && numTimestamps > 0)
        {
            val->setError(EGL_BAD_PARAMETER, "timestamps is NULL.");
            return false;
        }
    
        if (values == nullptr && numTimestamps > 0)
        {
            val->setError(EGL_BAD_PARAMETER, "values is NULL.");
            return false;
        }
    
        if (numTimestamps < 0)
        {
            val->setError(EGL_BAD_PARAMETER, "numTimestamps must be at least 0.");
            return false;
        }
    
        for (EGLint i = 0; i < numTimestamps; i++)
        {
            Timestamp timestamp = FromEGLenum<Timestamp>(timestamps[i]);
    
            if (!ValidTimestampType(timestamp))
            {
                val->setError(EGL_BAD_PARAMETER, "invalid timestamp type.");
                return false;
            }
    
            if (!surface->getSupportedTimestamps().test(timestamp))
            {
                val->setError(EGL_BAD_PARAMETER, "timestamp not supported by surface.");
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateQueryStringiANGLE(const ValidationContext *val,
                                   const Display *display,
                                   EGLint name,
                                   EGLint index)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!Display::GetClientExtensions().featureControlANGLE)
        {
            val->setError(EGL_BAD_DISPLAY, "EGL_ANGLE_feature_control extension is not available.");
            return false;
        }
    
        if (index < 0)
        {
            val->setError(EGL_BAD_PARAMETER, "index is negative.");
            return false;
        }
    
        switch (name)
        {
            case EGL_FEATURE_NAME_ANGLE:
            case EGL_FEATURE_CATEGORY_ANGLE:
            case EGL_FEATURE_DESCRIPTION_ANGLE:
            case EGL_FEATURE_BUG_ANGLE:
            case EGL_FEATURE_STATUS_ANGLE:
            case EGL_FEATURE_CONDITION_ANGLE:
                break;
            default:
                val->setError(EGL_BAD_PARAMETER, "name is not valid.");
                return false;
        }
    
        if (static_cast<size_t>(index) >= display->getFeatures().size())
        {
            val->setError(EGL_BAD_PARAMETER, "index is too big.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateQueryDisplayAttribEXT(const ValidationContext *val,
                                       const Display *display,
                                       const EGLint attribute,
                                       const EGLAttrib *value)
    {
        ANGLE_VALIDATION_TRY(ValidateQueryDisplayAttribBase(val, display, attribute));
        return true;
    }
    
    bool ValidateQueryDisplayAttribANGLE(const ValidationContext *val,
                                         const Display *display,
                                         const EGLint attribute,
                                         const EGLAttrib *value)
    {
        ANGLE_VALIDATION_TRY(ValidateQueryDisplayAttribBase(val, display, attribute));
        return true;
    }
    
    bool ValidateGetNativeClientBufferANDROID(const ValidationContext *val,
                                              const AHardwareBuffer *buffer)
    {
        // No extension check is done because no display is passed to eglGetNativeClientBufferANDROID
        // despite it being a display extension.  No display is needed for the implementation though.
        if (buffer == nullptr)
        {
            val->setError(EGL_BAD_PARAMETER, "NULL buffer.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreateNativeClientBufferANDROID(const ValidationContext *val,
                                                 const egl::AttributeMap &attribMap)
    {
        if (attribMap.isEmpty() || attribMap.begin()->second == EGL_NONE)
        {
            val->setError(EGL_BAD_PARAMETER, "invalid attribute list.");
            return false;
        }
    
        int width     = attribMap.getAsInt(EGL_WIDTH, 0);
        int height    = attribMap.getAsInt(EGL_HEIGHT, 0);
        int redSize   = attribMap.getAsInt(EGL_RED_SIZE, 0);
        int greenSize = attribMap.getAsInt(EGL_GREEN_SIZE, 0);
        int blueSize  = attribMap.getAsInt(EGL_BLUE_SIZE, 0);
        int alphaSize = attribMap.getAsInt(EGL_ALPHA_SIZE, 0);
        int usage     = attribMap.getAsInt(EGL_NATIVE_BUFFER_USAGE_ANDROID, 0);
    
        for (AttributeMap::const_iterator attributeIter = attribMap.begin();
             attributeIter != attribMap.end(); attributeIter++)
        {
            EGLAttrib attribute = attributeIter->first;
            switch (attribute)
            {
                case EGL_WIDTH:
                case EGL_HEIGHT:
                    // Validation done after the switch statement
                    break;
                case EGL_RED_SIZE:
                case EGL_GREEN_SIZE:
                case EGL_BLUE_SIZE:
                case EGL_ALPHA_SIZE:
                    if (redSize < 0 || greenSize < 0 || blueSize < 0 || alphaSize < 0)
                    {
                        val->setError(EGL_BAD_PARAMETER, "incorrect channel size requested");
                        return false;
                    }
                    break;
                case EGL_NATIVE_BUFFER_USAGE_ANDROID:
                    // The buffer must be used for either a texture or a renderbuffer.
                    if ((usage & ~(EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID |
                                   EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID |
                                   EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID)) != 0)
                    {
                        val->setError(EGL_BAD_PARAMETER, "invalid usage flag");
                        return false;
                    }
                    break;
                case EGL_NONE:
                    break;
                default:
                    val->setError(EGL_BAD_ATTRIBUTE, "invalid attribute");
                    return false;
            }
        }
    
        // Validate EGL_WIDTH and EGL_HEIGHT values passed in. Done here to account
        // for the case where EGL_WIDTH and EGL_HEIGHT were not part of the attribute list.
        if (width <= 0 || height <= 0)
        {
            val->setError(EGL_BAD_PARAMETER, "incorrect buffer dimensions requested");
            return false;
        }
    
        if (gl::GetAndroidHardwareBufferFormatFromChannelSizes(attribMap) == 0)
        {
            val->setError(EGL_BAD_PARAMETER, "unsupported format");
            return false;
        }
        return true;
    }
    
    bool ValidateDupNativeFenceFDANDROID(const ValidationContext *val,
                                         const Display *display,
                                         const Sync *sync)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().nativeFenceSyncANDROID)
        {
            val->setError(EGL_BAD_DISPLAY, "EGL_ANDROID_native_fence_sync extension is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
    
        return true;
    }
    
    bool ValidateSwapBuffersWithFrameTokenANGLE(const ValidationContext *val,
                                                const Display *display,
                                                const Surface *surface,
                                                EGLFrameTokenANGLE frametoken)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().swapWithFrameToken)
        {
            val->setError(EGL_BAD_DISPLAY, "EGL_ANGLE_swap_buffers_with_frame_token is not available.");
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, surface));
    
        return true;
    }
    
    bool ValidateSignalSyncKHR(const ValidationContext *val,
                               const Display *display,
                               const Sync *sync,
                               EGLenum mode)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        ANGLE_VALIDATION_TRY(ValidateSync(val, display, sync));
    
        if (sync->getType() == EGL_SYNC_REUSABLE_KHR)
        {
            if (!display->getExtensions().reusableSyncKHR)
            {
                val->setError(EGL_BAD_MATCH, "EGL_KHR_reusable_sync extension is not available.");
                return false;
            }
    
            if ((mode != EGL_SIGNALED_KHR) && (mode != EGL_UNSIGNALED_KHR))
            {
                val->setError(EGL_BAD_PARAMETER, "eglSignalSyncKHR invalid mode.");
                return false;
            }
    
            return true;
        }
    
        val->setError(EGL_BAD_MATCH);
        return false;
    }
    
    bool ValidateQuerySurfacePointerANGLE(const ValidationContext *val,
                                          const Display *display,
                                          const Surface *eglSurface,
                                          EGLint attribute,
                                          void *const *value)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().querySurfacePointer)
        {
            val->setError(EGL_BAD_ACCESS);
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, eglSurface));
    
        // validate the attribute parameter
        switch (attribute)
        {
            case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
                if (!display->getExtensions().surfaceD3DTexture2DShareHandle)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                break;
            case EGL_DXGI_KEYED_MUTEX_ANGLE:
                if (!display->getExtensions().keyedMutex)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE);
                return false;
        }
    
        return true;
    }
    
    bool ValidatePostSubBufferNV(const ValidationContext *val,
                                 const Display *display,
                                 const Surface *eglSurface,
                                 EGLint x,
                                 EGLint y,
                                 EGLint width,
                                 EGLint height)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
    
        if (!display->getExtensions().postSubBuffer)
        {
            val->setError(EGL_BAD_ACCESS);
            return false;
        }
    
        if (x < 0 || y < 0 || width < 0 || height < 0)
        {
            val->setError(EGL_BAD_PARAMETER);
            return false;
        }
    
        ANGLE_VALIDATION_TRY(ValidateSurface(val, display, eglSurface));
    
        if (display->isDeviceLost())
        {
            val->setError(EGL_CONTEXT_LOST);
            return false;
        }
    
        return true;
    }
    
    bool ValidateQueryDeviceAttribEXT(const ValidationContext *val,
                                      const Device *device,
                                      EGLint attribute,
                                      const EGLAttrib *value)
    {
        ANGLE_VALIDATION_TRY(ValidateDevice(val, device));
    
        if (!Display::GetClientExtensions().deviceQueryEXT)
        {
            val->setError(EGL_BAD_ACCESS, "EGL_EXT_device_query not supported.");
            return false;
        }
    
        // validate the attribute parameter
        switch (attribute)
        {
            case EGL_D3D11_DEVICE_ANGLE:
            case EGL_D3D9_DEVICE_ANGLE:
                if (!device->getExtensions().deviceD3D || device->getType() != attribute)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                break;
            case EGL_EAGL_CONTEXT_ANGLE:
                if (!device->getExtensions().deviceEAGL)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                break;
            case EGL_CGL_CONTEXT_ANGLE:
            case EGL_CGL_PIXEL_FORMAT_ANGLE:
                if (!device->getExtensions().deviceCGL)
                {
                    val->setError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
                break;
            default:
                val->setError(EGL_BAD_ATTRIBUTE);
                return false;
        }
        return true;
    }
    
    bool ValidateQueryDeviceStringEXT(const ValidationContext *val, const Device *device, EGLint name)
    {
        ANGLE_VALIDATION_TRY(ValidateDevice(val, device));
        return true;
    }
    
    bool ValidateReleaseHighPowerGPUANGLE(const ValidationContext *val,
                                          const Display *display,
                                          const gl::Context *context)
    {
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
        return true;
    }
    
    bool ValidateReacquireHighPowerGPUANGLE(const ValidationContext *val,
                                            const Display *display,
                                            const gl::Context *context)
    {
        ANGLE_VALIDATION_TRY(ValidateContext(val, display, context));
        return true;
    }
    
    bool ValidateHandleGPUSwitchANGLE(const ValidationContext *val, const Display *display)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, display));
        return true;
    }
    
    bool ValidateGetCurrentDisplay(const ValidationContext *val)
    {
        return true;
    }
    
    bool ValidateGetCurrentSurface(const ValidationContext *val, EGLint readdraw)
    {
        return true;
    }
    
    bool ValidateGetDisplay(const ValidationContext *val, EGLNativeDisplayType display_id)
    {
        return true;
    }
    
    bool ValidateGetError(const ValidationContext *val)
    {
        return true;
    }
    
    bool ValidateGetProcAddress(const ValidationContext *val, const char *procname)
    {
        return true;
    }
    
    bool ValidateQueryString(const ValidationContext *val, const Display *dpyPacked, EGLint name)
    {
        if (name != EGL_EXTENSIONS || dpyPacked != nullptr)
        {
            ANGLE_VALIDATION_TRY(ValidateDisplay(val, dpyPacked));
        }
    
        switch (name)
        {
            case EGL_CLIENT_APIS:
            case EGL_EXTENSIONS:
            case EGL_VENDOR:
            case EGL_VERSION:
                break;
            default:
                val->setError(EGL_BAD_PARAMETER);
                return false;
        }
        return true;
    }
    
    bool ValidateWaitGL(const ValidationContext *val)
    {
        ANGLE_VALIDATION_TRY(ValidateDisplay(val, val->eglThread->getDisplay()));
        return true;
    }
    
    bool ValidateQueryAPI(const ValidationContext *val)
    {
        return true;
    }
    
    bool ValidateReleaseThread(const ValidationContext *val)
    {
        return true;
    }
    
    bool ValidateWaitClient(const ValidationContext *val)
    {
        return true;
    }
    
    bool ValidateGetCurrentContext(const ValidationContext *val)
    {
        return true;
    }
    
    bool ValidateCreatePlatformPixmapSurface(const ValidationContext *val,
                                             const Display *dpyPacked,
                                             const Config *configPacked,
                                             const void *native_pixmap,
                                             const AttributeMap &attrib_listPacked)
    {
        EGLNativePixmapType nativePixmap =
            reinterpret_cast<EGLNativePixmapType>(const_cast<void *>(native_pixmap));
        return ValidateCreatePixmapSurface(val, dpyPacked, configPacked, nativePixmap,
                                           attrib_listPacked);
    }
    
    bool ValidateCreatePlatformWindowSurface(const ValidationContext *val,
                                             const Display *dpyPacked,
                                             const Config *configPacked,
                                             const void *native_window,
                                             const AttributeMap &attrib_listPacked)
    {
        EGLNativeWindowType nativeWindow =
            reinterpret_cast<EGLNativeWindowType>(const_cast<void *>(native_window));
        return ValidateCreateWindowSurface(val, dpyPacked, configPacked, nativeWindow,
                                           attrib_listPacked);
    }
    
    }  // namespace egl