blob: ff4cea7492fc0cd91f82669c7fd100ac54fcf002 [file] [log] [blame]
//
// 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;
}
Error ValidateStreamAttribute(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:
return EglBadAccess() << "Attempt to initialize readonly parameter";
case EGL_CONSUMER_LATENCY_USEC_KHR:
// Technically not in spec but a latency < 0 makes no sense so we check it
if (value < 0)
{
return EglBadParameter() << "Latency must be positive";
}
break;
case EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR:
if (!extensions.streamConsumerGLTexture)
{
return EglBadAttribute() << "Consumer GL extension not enabled";
}
// Again not in spec but it should be positive anyways
if (value < 0)
{
return EglBadParameter() << "Timeout must be positive";
}
break;
default:
return EglBadAttribute() << "Invalid stream attribute";
}
return NoError();
}
Error ValidateCreateImageMipLevelCommon(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()))
{
return EglBadParameter() << "texture must be complete if level is non-zero.";
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
{
return EglBadParameter() << "if level is zero and the texture is incomplete, it must "
"have no mip levels specified except zero.";
}
return NoError();
}
Error ValidateConfigAttribute(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)
{
return EglBadAttribute() << "EGL_ANGLE_surface_orientation is not enabled.";
}
break;
case EGL_COLOR_COMPONENT_TYPE_EXT:
if (!display->getExtensions().pixelFormatFloat)
{
return EglBadAttribute() << "EGL_EXT_pixel_format_float is not enabled.";
}
break;
case EGL_RECORDABLE_ANDROID:
if (!display->getExtensions().recordable)
{
return EglBadAttribute() << "EGL_ANDROID_recordable is not enabled.";
}
break;
default:
return EglBadAttribute() << "Unknown attribute.";
}
return NoError();
}
Error ValidateConfigAttributeValue(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:
return EglBadAttribute() << "EGL_bind_to_texture invalid attribute: " << value;
}
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:
return EglBadAttribute()
<< "EGL_color_buffer_type invalid attribute: " << value;
}
break;
case EGL_NATIVE_RENDERABLE:
switch (value)
{
case EGL_DONT_CARE:
case EGL_TRUE:
case EGL_FALSE:
break;
default:
return EglBadAttribute()
<< "EGL_native_renderable invalid attribute: " << value;
}
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:
return EglBadAttribute() << "EGL_transparent_type invalid attribute: " << value;
}
break;
case EGL_RECORDABLE_ANDROID:
switch (value)
{
case EGL_TRUE:
case EGL_FALSE:
case EGL_DONT_CARE:
break;
default:
return EglBadAttribute()
<< "EGL_RECORDABLE_ANDROID invalid attribute: " << value;
}
break;
default:
break;
}
return NoError();
}
Error ValidateConfigAttributes(const Display *display, const AttributeMap &attributes)
{
for (const auto &attrib : attributes)
{
ANGLE_TRY(ValidateConfigAttribute(display, attrib.first));
ANGLE_TRY(ValidateConfigAttributeValue(display, attrib.first, attrib.second));
}
return NoError();
}
Error ValidatePlatformType(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)
{
return EglBadAttribute() << "Direct3D platform is unsupported.";
}
break;
case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
if (!clientExtensions.platformANGLEOpenGL)
{
return EglBadAttribute() << "OpenGL platform is unsupported.";
}
break;
case EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE:
if (!clientExtensions.platformANGLENULL)
{
return EglBadAttribute() << "Display type EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE "
"requires EGL_ANGLE_platform_angle_null.";
}
break;
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
if (!clientExtensions.platformANGLEVulkan)
{
return EglBadAttribute() << "Vulkan platform is unsupported.";
}
break;
case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
if (!clientExtensions.platformANGLEMetal)
{
return EglBadAttribute() << "Metal platform is unsupported.";
}
break;
default:
return EglBadAttribute() << "Unknown platform type.";
}
return NoError();
}
Error ValidateGetPlatformDisplayCommon(EGLenum platform,
void *native_display,
const AttributeMap &attribMap)
{
const ClientExtensions &clientExtensions = Display::GetClientExtensions();
switch (platform)
{
case EGL_PLATFORM_ANGLE_ANGLE:
if (!clientExtensions.platformANGLE)
{
return EglBadParameter() << "Platform ANGLE extension is not active";
}
break;
case EGL_PLATFORM_DEVICE_EXT:
if (!clientExtensions.platformDevice)
{
return EglBadParameter() << "Platform Device extension is not active";
}
break;
default:
return EglBadConfig() << "Bad platform type.";
}
if (platform == EGL_PLATFORM_ANGLE_ANGLE)
{
EGLAttrib platformType = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
bool enableAutoTrimSpecified = false;
bool enableD3D11on12 = false;
bool presentPathSpecified = 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_TRY(ValidatePlatformType(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:
return EglBadAttribute() << "Invalid automatic trim attribute";
}
enableAutoTrimSpecified = true;
break;
case EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE:
if (!clientExtensions.platformANGLED3D ||
!clientExtensions.platformANGLED3D11ON12)
{
return EglBadAttribute()
<< "EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE extension not active.";
}
switch (value)
{
case EGL_TRUE:
case EGL_FALSE:
break;
default:
return EglBadAttribute() << "Invalid D3D11on12 attribute";
}
enableD3D11on12 = true;
break;
case EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE:
if (!clientExtensions.experimentalPresentPath)
{
return EglBadAttribute()
<< "EGL_ANGLE_experimental_present_path extension not active";
}
switch (value)
{
case EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE:
case EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE:
break;
default:
return EglBadAttribute()
<< "Invalid value for EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE";
}
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)
{
return EglBadAttribute()
<< "EGL_ANGLE_platform_angle_d3d is not supported";
}
break;
case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
if (!clientExtensions.platformANGLEDeviceTypeSwiftShader)
{
return EglBadAttribute() << "EGL_ANGLE_platform_angle_device_type_"
"swiftshader is not supported";
}
break;
default:
return EglBadAttribute() << "Invalid value for "
"EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE "
"attrib";
}
deviceType = value;
break;
case EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE:
if (!clientExtensions.platformANGLE)
{
return EglBadAttribute() << "EGL_ANGLE_platform_angle extension not active";
}
if (value != EGL_TRUE && value != EGL_FALSE && value != EGL_DONT_CARE)
{
return EglBadAttribute() << "EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE "
"must be EGL_TRUE, EGL_FALSE, or "
"EGL_DONT_CARE.";
}
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)
{
return EglBadAttribute() << "EGL_ANGLE_platform_angle_context_"
"virtualization extension not active";
}
switch (value)
{
case EGL_DONT_CARE:
case EGL_FALSE:
case EGL_TRUE:
break;
default:
return EglBadAttribute() << "Invalid value for "
"EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_"
"ANGLE attrib";
}
break;
default:
break;
}
}
if (!majorVersion.valid() && minorVersion.valid())
{
return EglBadAttribute()
<< "Must specify major version if you specify a minor version.";
}
if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE &&
platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
return EglBadAttribute() << "EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE requires a "
"device type of EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.";
}
if (enableAutoTrimSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
return EglBadAttribute() << "EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE "
"requires a device type of "
"EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.";
}
if (enableD3D11on12)
{
if (platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
return EglBadAttribute() << "EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE "
"requires a platform type of "
"EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.";
}
if (deviceType.valid() && deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE &&
deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE)
{
return EglBadAttribute() << "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";
}
}
if (presentPathSpecified && platformType != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
return EglBadAttribute() << "EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE requires a "
"device type of EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE.";
}
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)
{
return EglBadAttribute()
<< "This device type requires a "
"platform type of EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE or "
"EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE.";
}
break;
case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
if (platformType != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
{
return EglBadAttribute()
<< "This device type requires a "
"platform type of EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE.";
}
break;
default:
break;
}
}
if (platformType == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
{
if ((majorVersion.valid() && majorVersion.value() != 1) ||
(minorVersion.valid() && minorVersion.value() != 0))
{
return EglBadAttribute() << "EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE currently "
"only supports Vulkan 1.0.";
}
}
if (eglHandle.valid() && platformType != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE &&
platformType != EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)
{
return EglBadAttribute() << "EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE requires a "
"device type of EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE.";
}
}
else if (platform == EGL_PLATFORM_DEVICE_EXT)
{
Device *eglDevice = static_cast<Device *>(native_display);
if (eglDevice == nullptr || !Device::IsValidDevice(eglDevice))
{
return EglBadAttribute() << "native_display should be a valid EGL device if "
"platform equals EGL_PLATFORM_DEVICE_EXT";
}
}
else
{
UNREACHABLE();
}
if (attribMap.contains(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE))
{
if (!clientExtensions.featureControlANGLE)
{
return EglBadAttribute() << "EGL_ANGLE_feature_control is not supported";
}
else if (attribMap.get(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE, 0) == 0)
{
return EglBadAttribute()
<< "EGL_FEATURE_OVERRIDES_ENABLED_ANGLE must be a valid pointer";
}
}
if (attribMap.contains(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE))
{
if (!clientExtensions.featureControlANGLE)
{
return EglBadAttribute() << "EGL_ANGLE_feature_control is not supported";
}
else if (attribMap.get(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE, 0) == 0)
{
return EglBadAttribute()
<< "EGL_FEATURE_OVERRIDES_DISABLED_ANGLE must be a valid pointer";
}
}
return NoError();
}
Error ValidateStream(const Display *display, const Stream *stream)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.stream)
{
return EglBadAccess() << "Stream extension not active";
}
if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
{
return EglBadStream() << "Invalid stream";
}
return NoError();
}
Error ValidateLabeledObject(Thread *thread,
const Display *display,
ObjectType objectType,
EGLObjectKHR object,
LabeledObject **outLabeledObject)
{
switch (objectType)
{
case ObjectType::Context:
{
gl::Context *context = static_cast<gl::Context *>(object);
ANGLE_TRY(ValidateContext(display, context));
*outLabeledObject = context;
break;
}
case ObjectType::Display:
{
ANGLE_TRY(ValidateDisplay(display));
if (display != object)
{
return EglBadParameter() << "when object type is EGL_OBJECT_DISPLAY_KHR, the "
"object must be the same as the display.";
}
*outLabeledObject = static_cast<Display *>(object);
break;
}
case ObjectType::Image:
{
Image *image = static_cast<Image *>(object);
ANGLE_TRY(ValidateImage(display, image));
*outLabeledObject = image;
break;
}
case ObjectType::Stream:
{
Stream *stream = static_cast<Stream *>(object);
ANGLE_TRY(ValidateStream(display, stream));
*outLabeledObject = stream;
break;
}
case ObjectType::Surface:
{
Surface *surface = static_cast<Surface *>(object);
ANGLE_TRY(ValidateSurface(display, surface));
*outLabeledObject = surface;
break;
}
case ObjectType::Sync:
{
Sync *sync = static_cast<Sync *>(object);
ANGLE_TRY(ValidateSync(display, sync));
*outLabeledObject = sync;
break;
}
case ObjectType::Thread:
{
*outLabeledObject = thread;
break;
}
default:
return EglBadParameter() << "unknown object type.";
}
return NoError();
}
// This is a common sub-check of Display status that's shared by multiple functions
Error ValidateDisplayPointer(const Display *display)
{
if (display == EGL_NO_DISPLAY)
{
return EglBadDisplay() << "display is EGL_NO_DISPLAY.";
}
if (!Display::isValidDisplay(display))
{
return EglBadDisplay() << "display is not a valid display.";
}
return NoError();
}
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;
}
}
} // anonymous namespace
Error ValidateDisplay(const Display *display)
{
ANGLE_TRY(ValidateDisplayPointer(display));
if (!display->isInitialized())
{
return EglNotInitialized() << "display is not initialized.";
}
if (display->isDeviceLost())
{
return EglContextLost() << "display had a context loss";
}
return NoError();
}
Error ValidateSurface(const Display *display, const Surface *surface)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->isValidSurface(surface))
{
return EglBadSurface();
}
return NoError();
}
Error ValidateConfig(const Display *display, const Config *config)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->isValidConfig(config))
{
return EglBadConfig();
}
return NoError();
}
Error ValidateContext(const Display *display, const gl::Context *context)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->isValidContext(context))
{
return EglBadContext();
}
return NoError();
}
Error ValidateImage(const Display *display, const Image *image)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->isValidImage(image))
{
return EglBadParameter() << "image is not valid.";
}
return NoError();
}
Error ValidateDevice(const Device *device)
{
if (device == EGL_NO_DEVICE_EXT)
{
return EglBadAccess() << "device is EGL_NO_DEVICE.";
}
if (!Device::IsValidDevice(device))
{
return EglBadAccess() << "device is not valid.";
}
return NoError();
}
Error ValidateSync(const Display *display, const Sync *sync)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->isValidSync(sync))
{
return EglBadParameter() << "sync object is not valid.";
}
return NoError();
}
const Thread *GetThreadIfValid(const Thread *thread)
{
// Threads should always be valid
return thread;
}
const Display *GetDisplayIfValid(const Display *display)
{
if (ValidateDisplay(display).isError())
{
return nullptr;
}
return display;
}
const Surface *GetSurfaceIfValid(const Display *display, const Surface *surface)
{
if (ValidateSurface(display, surface).isError())
{
return nullptr;
}
return surface;
}
const Image *GetImageIfValid(const Display *display, const Image *image)
{
if (ValidateImage(display, image).isError())
{
return nullptr;
}
return image;
}
const Stream *GetStreamIfValid(const Display *display, const Stream *stream)
{
if (ValidateStream(display, stream).isError())
{
return nullptr;
}
return stream;
}
const gl::Context *GetContextIfValid(const Display *display, const gl::Context *context)
{
if (ValidateContext(display, context).isError())
{
return nullptr;
}
return context;
}
const Device *GetDeviceIfValid(const Device *device)
{
if (ValidateDevice(device).isError())
{
return nullptr;
}
return device;
}
const Sync *GetSyncIfValid(const Display *display, const Sync *sync)
{
if (ValidateSync(display, sync).isError())
{
return nullptr;
}
return sync;
}
LabeledObject *GetLabeledObjectIfValid(Thread *thread,
const Display *display,
ObjectType objectType,
EGLObjectKHR object)
{
LabeledObject *labeledObject = nullptr;
if (ValidateLabeledObject(thread, display, objectType, object, &labeledObject).isError())
{
return nullptr;
}
return labeledObject;
}
Error ValidateInitialize(const Display *display)
{
return ValidateDisplayPointer(display);
}
Error ValidateTerminate(const Display *display)
{
return ValidateDisplayPointer(display);
}
Error ValidateCreateContext(Display *display,
Config *configuration,
gl::Context *shareContext,
const AttributeMap &attributes)
{
if (configuration)
{
ANGLE_TRY(ValidateConfig(display, configuration));
}
else
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.noConfigContext)
{
return EglBadConfig();
}
}
// 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
return EglBadAttribute();
case EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT:
if (!display->getExtensions().createContextRobustness)
{
return EglBadAttribute();
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute();
}
break;
case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR:
return EglBadAttribute()
<< "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.";
case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT:
if (!display->getExtensions().createContextRobustness)
{
return EglBadAttribute();
}
if (value == EGL_LOSE_CONTEXT_ON_RESET_EXT)
{
resetNotification = true;
}
else if (value != EGL_NO_RESET_NOTIFICATION_EXT)
{
return EglBadAttribute();
}
break;
case EGL_CONTEXT_OPENGL_NO_ERROR_KHR:
if (!display->getExtensions().createContextNoError)
{
return EglBadAttribute() << "Invalid Context attribute.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "Attribute must be EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE:
if (!display->getExtensions().createContextWebGLCompatibility)
{
return EglBadAttribute() << "Attribute "
"EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE requires "
"EGL_ANGLE_create_context_webgl_compatibility.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE must be "
"EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM:
if (!display->getExtensions().createContextBindGeneratesResource)
{
return EglBadAttribute()
<< "Attribute EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM requires "
"EGL_CHROMIUM_create_context_bind_generates_resource.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM "
"must be EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE:
if (!display->getExtensions().displayTextureShareGroup)
{
return EglBadAttribute() << "Attribute "
"EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE requires "
"EGL_ANGLE_display_texture_share_group.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE must be "
"EGL_TRUE or EGL_FALSE.";
}
if (shareContext &&
(shareContext->usingDisplayTextureShareGroup() != (value == EGL_TRUE)))
{
return EglBadAttribute() << "All contexts within a share group must be "
"created with the same value of "
"EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE.";
}
break;
case EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE:
if (!display->getExtensions().createContextClientArrays)
{
return EglBadAttribute()
<< "Attribute EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE requires "
"EGL_ANGLE_create_context_client_arrays.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE must "
"be EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE:
if (!display->getExtensions().programCacheControl)
{
return EglBadAttribute()
<< "Attribute EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE "
"requires EGL_ANGLE_program_cache_control.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute()
<< "EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE must "
"be EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
if (!display->getExtensions().robustResourceInitialization)
{
return EglBadAttribute()
<< "Attribute EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
"requires EGL_ANGLE_robust_resource_initialization.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
"either EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_EXTENSIONS_ENABLED_ANGLE:
if (!display->getExtensions().createContextExtensionsEnabled)
{
return EglBadAttribute()
<< "Attribute EGL_EXTENSIONS_ENABLED_ANGLE "
"requires EGL_ANGLE_create_context_extensions_enabled.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_EXTENSIONS_ENABLED_ANGLE must be "
"either EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_POWER_PREFERENCE_ANGLE:
if (!display->getExtensions().powerPreference)
{
return EglBadAttribute() << "Attribute EGL_POWER_PREFERENCE_ANGLE "
"requires EGL_ANGLE_power_preference.";
}
if (value != EGL_LOW_POWER_ANGLE && value != EGL_HIGH_POWER_ANGLE)
{
return EglBadAttribute()
<< "EGL_POWER_PREFERENCE_ANGLE must be "
"either EGL_LOW_POWER_ANGLE or EGL_HIGH_POWER_ANGLE.";
}
break;
case EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE:
if (!display->getExtensions().createContextBackwardsCompatible)
{
return EglBadAttribute()
<< "Attribute EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE "
"requires EGL_ANGLE_create_context_backwards_compatible.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute()
<< "EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE must be "
"either EGL_TRUE or EGL_FALSE.";
}
break;
default:
return EglBadAttribute() << "Unknown attribute.";
}
}
switch (clientMajorVersion)
{
case 1:
if (clientMinorVersion != 0 && clientMinorVersion != 1)
{
return EglBadAttribute();
}
if (configuration == EGL_NO_CONFIG_KHR)
{
return EglBadMatch();
}
if ((configuration != EGL_NO_CONFIG_KHR) &&
!(configuration->renderableType & EGL_OPENGL_ES_BIT))
{
return EglBadMatch();
}
break;
case 2:
if (clientMinorVersion != 0)
{
return EglBadAttribute();
}
if ((configuration != EGL_NO_CONFIG_KHR) &&
!(configuration->renderableType & EGL_OPENGL_ES2_BIT))
{
return EglBadMatch();
}
break;
case 3:
if (clientMinorVersion != 0 && clientMinorVersion != 1)
{
return EglBadAttribute();
}
if ((configuration != EGL_NO_CONFIG_KHR) &&
!(configuration->renderableType & EGL_OPENGL_ES3_BIT))
{
return EglBadMatch();
}
if (display->getMaxSupportedESVersion() <
gl::Version(static_cast<GLuint>(clientMajorVersion),
static_cast<GLuint>(clientMinorVersion)))
{
return EglBadAttribute() << "Requested GLES version is not supported.";
}
break;
default:
return EglBadAttribute();
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)
{
return EglBadAttribute();
}
if (shareContext)
{
// Shared context is invalid or is owned by another display
if (!display->isValidContext(shareContext))
{
return EglBadMatch();
}
if (shareContext->isResetNotificationEnabled() != resetNotification)
{
return EglBadMatch();
}
}
return NoError();
}
Error ValidateCreateWindowSurface(Display *display,
Config *config,
EGLNativeWindowType window,
const AttributeMap &attributes)
{
ANGLE_TRY(ValidateConfig(display, config));
if (!display->isValidNativeWindow(window))
{
return EglBadNativeWindow();
}
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:
return EglBadMatch(); // Rendering directly to front buffer not supported
default:
return EglBadAttribute();
}
break;
case EGL_POST_SUB_BUFFER_SUPPORTED_NV:
if (!displayExtensions.postSubBuffer)
{
return EglBadAttribute();
}
break;
case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
if (!displayExtensions.flexibleSurfaceCompatibility)
{
return EglBadAttribute();
}
break;
case EGL_WIDTH:
case EGL_HEIGHT:
if (!displayExtensions.windowFixedSize)
{
return EglBadAttribute();
}
if (value < 0)
{
return EglBadParameter();
}
break;
case EGL_FIXED_SIZE_ANGLE:
if (!displayExtensions.windowFixedSize)
{
return EglBadAttribute();
}
break;
case EGL_SURFACE_ORIENTATION_ANGLE:
if (!displayExtensions.surfaceOrientation)
{
return EglBadAttribute() << "EGL_ANGLE_surface_orientation is not enabled.";
}
break;
case EGL_VG_COLORSPACE:
return EglBadMatch();
case EGL_VG_ALPHA_FORMAT:
return EglBadMatch();
case EGL_DIRECT_COMPOSITION_ANGLE:
if (!displayExtensions.directComposition)
{
return EglBadAttribute();
}
break;
case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
if (!display->getExtensions().robustResourceInitialization)
{
return EglBadAttribute()
<< "Attribute EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
"requires EGL_ANGLE_robust_resource_initialization.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
"either EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_GGP_STREAM_DESCRIPTOR_ANGLE:
if (!display->getExtensions().ggpStreamDescriptor)
{
return EglBadAttribute() << "EGL_GGP_STREAM_DESCRIPTOR_ANGLE requires "
"EGL_ANGLE_ggp_stream_descriptor.";
}
break;
default:
return EglBadAttribute();
}
}
if (Display::hasExistingWindowSurface(window))
{
return EglBadAlloc();
}
return NoError();
}
Error ValidateCreatePbufferSurface(Display *display, Config *config, const AttributeMap &attributes)
{
ANGLE_TRY(ValidateConfig(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)
{
return EglBadParameter();
}
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:
return EglBadAttribute();
}
break;
case EGL_TEXTURE_TARGET:
switch (value)
{
case EGL_NO_TEXTURE:
case EGL_TEXTURE_2D:
break;
default:
return EglBadAttribute();
}
break;
case EGL_MIPMAP_TEXTURE:
break;
case EGL_VG_COLORSPACE:
break;
case EGL_VG_ALPHA_FORMAT:
break;
case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
if (!displayExtensions.flexibleSurfaceCompatibility)
{
return EglBadAttribute()
<< "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used "
"without EGL_ANGLE_flexible_surface_compatibility support.";
}
break;
case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
if (!display->getExtensions().robustResourceInitialization)
{
return EglBadAttribute()
<< "Attribute EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
"requires EGL_ANGLE_robust_resource_initialization.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
"either EGL_TRUE or EGL_FALSE.";
}
break;
default:
return EglBadAttribute();
}
}
if (!(config->surfaceType & EGL_PBUFFER_BIT))
{
return EglBadMatch();
}
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))
{
return EglBadMatch();
}
if ((textureFormat == EGL_TEXTURE_RGB && config->bindToTextureRGB != EGL_TRUE) ||
(textureFormat == EGL_TEXTURE_RGBA && config->bindToTextureRGBA != EGL_TRUE))
{
return EglBadAttribute();
}
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)))
{
return EglBadMatch();
}
return NoError();
}
Error ValidateCreatePbufferFromClientBuffer(Display *display,
EGLenum buftype,
EGLClientBuffer buffer,
Config *config,
const AttributeMap &attributes)
{
ANGLE_TRY(ValidateConfig(display, config));
const DisplayExtensions &displayExtensions = display->getExtensions();
switch (buftype)
{
case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
if (!displayExtensions.d3dShareHandleClientBuffer)
{
return EglBadParameter();
}
if (buffer == nullptr)
{
return EglBadParameter();
}
break;
case EGL_D3D_TEXTURE_ANGLE:
if (!displayExtensions.d3dTextureClientBuffer)
{
return EglBadParameter();
}
if (buffer == nullptr)
{
return EglBadParameter();
}
break;
case EGL_IOSURFACE_ANGLE:
if (!displayExtensions.iosurfaceClientBuffer)
{
return EglBadParameter() << "<buftype> EGL_IOSURFACE_ANGLE requires the "
"EGL_ANGLE_iosurface_client_buffer extension.";
}
if (buffer == nullptr)
{
return EglBadParameter() << "<buffer> must be non null";
}
break;
default:
return EglBadParameter();
}
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)
{
return EglBadParameter()
<< "Width and Height are not supported for thie <buftype>";
}
if (value < 0)
{
return EglBadParameter() << "Width and Height must be positive";
}
break;
case EGL_TEXTURE_FORMAT:
switch (value)
{
case EGL_NO_TEXTURE:
case EGL_TEXTURE_RGB:
case EGL_TEXTURE_RGBA:
break;
default:
return EglBadAttribute() << "Invalid value for EGL_TEXTURE_FORMAT";
}
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)
{
return EglBadParameter()
<< "<buftype> doesn't support rectangle texture targets";
}
break;
default:
return EglBadAttribute() << "Invalid value for EGL_TEXTURE_TARGET";
}
break;
case EGL_MIPMAP_TEXTURE:
break;
case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
if (!displayExtensions.flexibleSurfaceCompatibility)
{
return EglBadAttribute()
<< "EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE cannot be used "
"without EGL_ANGLE_flexible_surface_compatibility support.";
}
break;
case EGL_IOSURFACE_PLANE_ANGLE:
if (buftype != EGL_IOSURFACE_ANGLE)
{
return EglBadAttribute() << "<buftype> doesn't support iosurface plane";
}
break;
case EGL_TEXTURE_TYPE_ANGLE:
if (buftype != EGL_IOSURFACE_ANGLE)
{
return EglBadAttribute() << "<buftype> doesn't support texture type";
}
break;
case EGL_TEXTURE_INTERNAL_FORMAT_ANGLE:
if (buftype != EGL_IOSURFACE_ANGLE && buftype != EGL_D3D_TEXTURE_ANGLE)
{
return EglBadAttribute() << "<buftype> doesn't support texture internal format";
}
break;
case EGL_GL_COLORSPACE:
if (buftype != EGL_D3D_TEXTURE_ANGLE)
{
return EglBadAttribute() << "<buftype> doesn't support setting GL colorspace";
}
break;
default:
return EglBadAttribute();
}
}
EGLAttrib colorspace = attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR);
if (colorspace != EGL_GL_COLORSPACE_LINEAR && colorspace != EGL_GL_COLORSPACE_SRGB)
{
return EglBadAttribute() << "invalid GL colorspace";
}
if (!(config->surfaceType & EGL_PBUFFER_BIT))
{
return EglBadMatch();
}
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))
{
return EglBadMatch();
}
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)
{
return EglBadAttribute();
}
}
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)
{
return EglBadAttribute();
}
const Caps &caps = display->getCaps();
if (textureFormat != EGL_NO_TEXTURE && !caps.textureNPOT &&
(!gl::isPow2(width) || !gl::isPow2(height)))
{
return EglBadMatch();
}
}
if (buftype == EGL_IOSURFACE_ANGLE)
{
if (textureTarget != EGL_TEXTURE_RECTANGLE_ANGLE)
{
return EglBadAttribute() << "EGL_IOSURFACE requires the EGL_TEXTURE_RECTANGLE target";
}
if (textureFormat != EGL_TEXTURE_RGBA)
{
return EglBadAttribute() << "EGL_IOSURFACE requires the EGL_TEXTURE_RGBA format";
}
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))
{
return EglBadParameter() << "Missing required attribute for EGL_IOSURFACE";
}
}
ANGLE_TRY(display->validateClientBuffer(config, buftype, buffer, attributes));
return NoError();
}
Error ValidateMakeCurrent(Display *display, Surface *draw, Surface *read, gl::Context *context)
{
if (context == EGL_NO_CONTEXT && (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE))
{
return EglBadMatch() << "If ctx is EGL_NO_CONTEXT, surfaces must be EGL_NO_SURFACE";
}
// 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))
{
return EglBadMatch() << "If ctx is not EGL_NOT_CONTEXT, draw or read must "
"both be EGL_NO_SURFACE, or both not";
}
}
else
{
return EglBadMatch()
<< "If ctx is not EGL_NO_CONTEXT, surfaces must not be EGL_NO_SURFACE";
}
}
// 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))
{
return EglBadMatch()
<< "read and draw must both be valid surfaces, or both be EGL_NO_SURFACE";
}
if (display == EGL_NO_DISPLAY || !Display::isValidDisplay(display))
{
return EglBadDisplay() << "'dpy' not a valid EGLDisplay handle";
}
// 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))
{
return EglNotInitialized() << "'dpy' not initialized";
}
if (context != EGL_NO_CONTEXT)
{
ANGLE_TRY(ValidateContext(display, context));
}
if (display->isInitialized() && display->isDeviceLost())
{
return EglContextLost();
}
if (draw != EGL_NO_SURFACE)
{
ANGLE_TRY(ValidateSurface(display, draw));
}
if (read != EGL_NO_SURFACE)
{
ANGLE_TRY(ValidateSurface(display, read));
ANGLE_TRY(ValidateCompatibleSurface(display, context, read));
}
if (draw != read)
{
if (draw)
{
ANGLE_TRY(ValidateCompatibleSurface(display, context, draw));
}
if (read)
{
ANGLE_TRY(ValidateCompatibleSurface(display, context, read));
}
}
return NoError();
}
Error ValidateCompatibleSurface(const Display *display,
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))
{
return EglBadMatch() << "Surface not compatible with OpenGL ES 1.x.";
}
break;
case 2:
if (!(surfaceConfig->renderableType & EGL_OPENGL_ES2_BIT))
{
return EglBadMatch() << "Surface not compatible with OpenGL ES 2.x.";
}
break;
case 3:
if (!(surfaceConfig->renderableType & (EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT)))
{
return EglBadMatch() << "Surface not compatible with OpenGL ES 3.x.";
}
break;
default:
return EglBadMatch() << "Surface not compatible with Context API.";
}
// EGL KHR no config context
if (context->getConfig() == EGL_NO_CONFIG_KHR)
{
const DisplayExtensions &displayExtensions = display->getExtensions();
if (displayExtensions.noConfigContext)
{
return NoError();
}
return EglBadMatch() << "Context with no config is not supported.";
}
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)
{
return EglBadMatch() << "Color buffer types are not compatible.";
}
bool colorCompat = surfaceConfig->redSize == contextConfig->redSize &&
surfaceConfig->greenSize == contextConfig->greenSize &&
surfaceConfig->blueSize == contextConfig->blueSize &&
surfaceConfig->alphaSize == contextConfig->alphaSize &&
surfaceConfig->luminanceSize == contextConfig->luminanceSize;
if (!colorCompat)
{
return EglBadMatch() << "Color buffer sizes are not compatible.";
}
bool componentTypeCompat =
surfaceConfig->colorComponentType == contextConfig->colorComponentType;
if (!componentTypeCompat)
{
return EglBadMatch() << "Color buffer component types are not compatible.";
}
bool dsCompat = surfaceConfig->depthSize == contextConfig->depthSize &&
surfaceConfig->stencilSize == contextConfig->stencilSize;
if (!dsCompat)
{
return EglBadMatch() << "Depth-stencil buffer types are not compatible.";
}
}
bool surfaceTypeCompat = (surfaceConfig->surfaceType & contextConfig->surfaceType) != 0;
if (!surfaceTypeCompat)
{
return EglBadMatch() << "Surface type is not compatible.";
}
return NoError();
}
Error ValidateCreateImage(const Display *display,
gl::Context *context,
EGLenum target,
EGLClientBuffer buffer,
const AttributeMap &attributes)
{
ANGLE_TRY(ValidateDisplay(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:
return EglBadParameter()
<< "EGL_IMAGE_PRESERVED must be EGL_TRUE or EGL_FALSE.";
}
break;
case EGL_GL_TEXTURE_LEVEL:
if (!displayExtensions.glTexture2DImage &&
!displayExtensions.glTextureCubemapImage && !displayExtensions.glTexture3DImage)
{
return EglBadParameter() << "EGL_GL_TEXTURE_LEVEL cannot be used "
"without KHR_gl_texture_*_image support.";
}
if (value < 0)
{
return EglBadParameter() << "EGL_GL_TEXTURE_LEVEL cannot be negative.";
}
break;
case EGL_GL_TEXTURE_ZOFFSET:
if (!displayExtensions.glTexture3DImage)
{
return EglBadParameter() << "EGL_GL_TEXTURE_ZOFFSET cannot be used "
"without KHR_gl_texture_3D_image support.";
}
break;
case EGL_TEXTURE_INTERNAL_FORMAT_ANGLE:
if (!displayExtensions.imageD3D11Texture)
{
return EglBadParameter()
<< "EGL_TEXTURE_INTERNAL_FORMAT_ANGLE and EGL_TEXTURE_TYPE_ANGLE cannot "
"be used without EGL_ANGLE_image_d3d11_texture support.";
}
break;
default:
return EglBadParameter()
<< "invalid attribute: 0x" << std::hex << std::uppercase << attribute;
}
}
switch (target)
{
case EGL_GL_TEXTURE_2D:
{
if (!displayExtensions.glTexture2DImage)
{
return EglBadParameter() << "KHR_gl_texture_2D_image not supported.";
}
if (buffer == 0)
{
return EglBadParameter() << "buffer cannot reference a 2D texture with the name 0.";
}
ANGLE_TRY(ValidateContext(display, context));
const gl::Texture *texture =
context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
if (texture == nullptr || texture->getType() != gl::TextureType::_2D)
{
return EglBadParameter() << "target is not a 2D texture.";
}
if (texture->getBoundSurface() != nullptr)
{
return EglBadAccess() << "texture has a surface bound to it.";
}
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)
{
return EglBadParameter()
<< "target 2D texture does not have a valid size at specified level.";
}
ANGLE_TRY(ValidateCreateImageMipLevelCommon(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)
{
return EglBadParameter() << "KHR_gl_texture_cubemap_image not supported.";
}
if (buffer == 0)
{
return EglBadParameter()
<< "buffer cannot reference a cubemap texture with the name 0.";
}
ANGLE_TRY(ValidateContext(display, context));
const gl::Texture *texture =
context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
if (texture == nullptr || texture->getType() != gl::TextureType::CubeMap)
{
return EglBadParameter() << "target is not a cubemap texture.";
}
if (texture->getBoundSurface() != nullptr)
{
return EglBadAccess() << "texture has a surface bound to it.";
}
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)
{
return EglBadParameter() << "target cubemap texture does not have a valid "
"size at specified level and face.";
}
ANGLE_TRY(ValidateCreateImageMipLevelCommon(context, texture, level));
if (level == 0 && !texture->isMipmapComplete() &&
CubeTextureHasUnspecifiedLevel0Face(texture))
{
return EglBadParameter() << "if level is zero and the texture is incomplete, "
"it must have all of its faces specified at level "
"zero.";
}
}
break;
case EGL_GL_TEXTURE_3D:
{
if (!displayExtensions.glTexture3DImage)
{
return EglBadParameter() << "KHR_gl_texture_3D_image not supported.";
}
if (buffer == 0)
{
return EglBadParameter() << "buffer cannot reference a 3D texture with the name 0.";
}
ANGLE_TRY(ValidateContext(display, context));
const gl::Texture *texture =
context->getTexture({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
if (texture == nullptr || texture->getType() != gl::TextureType::_3D)
{
return EglBadParameter() << "target is not a 3D texture.";
}
if (texture->getBoundSurface() != nullptr)
{
return EglBadAccess() << "texture has a surface bound to it.";
}
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)
{
return EglBadParameter()
<< "target 3D texture does not have a valid size at specified level.";
}
if (static_cast<size_t>(zOffset) >=
texture->getDepth(gl::TextureTarget::_3D, static_cast<size_t>(level)))
{
return EglBadParameter() << "target 3D texture does not have enough layers "
"for the specified Z offset at the specified "
"level.";
}
ANGLE_TRY(ValidateCreateImageMipLevelCommon(context, texture, level));
}
break;
case EGL_GL_RENDERBUFFER:
{
if (!displayExtensions.glRenderbufferImage)
{
return EglBadParameter() << "KHR_gl_renderbuffer_image not supported.";
}
if (attributes.contains(EGL_GL_TEXTURE_LEVEL))
{
return EglBadParameter() << "EGL_GL_TEXTURE_LEVEL cannot be used in "
"conjunction with a renderbuffer target.";
}
if (buffer == 0)
{
return EglBadParameter()
<< "buffer cannot reference a renderbuffer with the name 0.";
}
ANGLE_TRY(ValidateContext(display, context));
const gl::Renderbuffer *renderbuffer =
context->getRenderbuffer({egl_gl::EGLClientBufferToGLObjectHandle(buffer)});
if (renderbuffer == nullptr)
{
return EglBadParameter() << "target is not a renderbuffer.";
}
if (renderbuffer->getSamples() > 0)
{
return EglBadParameter() << "target renderbuffer cannot be multisampled.";
}
}
break;
case EGL_NATIVE_BUFFER_ANDROID:
{
if (!displayExtensions.imageNativeBuffer)
{
return EglBadParameter() << "EGL_ANDROID_image_native_buffer not supported.";
}
if (context != nullptr)
{
return EglBadContext() << "ctx must be EGL_NO_CONTEXT.";
}
ANGLE_TRY(display->validateImageClientBuffer(context, target, buffer, attributes));
}
break;
case EGL_D3D11_TEXTURE_ANGLE:
if (!displayExtensions.imageD3D11Texture)
{
return EglBadParameter() << "EGL_ANGLE_image_d3d11_texture not supported.";
}
if (context != nullptr)
{
return EglBadContext() << "ctx must be EGL_NO_CONTEXT.";
}
ANGLE_TRY(display->validateImageClientBuffer(context, target, buffer, attributes));
break;
default:
return EglBadParameter()
<< "invalid target: 0x" << std::hex << std::uppercase << target;
}
if (attributes.contains(EGL_GL_TEXTURE_ZOFFSET) && target != EGL_GL_TEXTURE_3D)
{
return EglBadParameter() << "EGL_GL_TEXTURE_ZOFFSET must be used with a 3D texture target.";
}
return NoError();
}
Error ValidateDestroyImage(const Display *display, const Image *image)
{
ANGLE_TRY(ValidateImage(display, image));
return NoError();
}
Error ValidateCreateImageKHR(const Display *display,
gl::Context *context,
EGLenum target,
EGLClientBuffer buffer,
const AttributeMap &attributes)
{
ANGLE_TRY(ValidateDisplay(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.
return EglBadDisplay() << "EGL_KHR_image not supported.";
}
return ValidateCreateImage(display, context, target, buffer, attributes);
}
Error ValidateDestroyImageKHR(const Display *display, const Image *image)
{
ANGLE_TRY(ValidateImage(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.
return EglBadDisplay();
}
return NoError();
}
Error ValidateCreateDeviceANGLE(EGLint device_type,
void *native_device,
const EGLAttrib *attrib_list)
{
const ClientExtensions &clientExtensions = Display::GetClientExtensions();
if (!clientExtensions.deviceCreation)
{
return EglBadAccess() << "Device creation extension not active";
}
if (attrib_list != nullptr && attrib_list[0] != EGL_NONE)
{
return EglBadAttribute() << "Invalid attrib_list parameter";
}
switch (device_type)
{
case EGL_D3D11_DEVICE_ANGLE:
if (!clientExtensions.deviceCreationD3D11)
{
return EglBadAttribute() << "D3D11 device creation extension not active";
}
break;
default:
return EglBadAttribute() << "Invalid device_type parameter";
}
return NoError();
}
Error ValidateReleaseDeviceANGLE(Device *device)
{
const ClientExtensions &clientExtensions = Display::GetClientExtensions();
if (!clientExtensions.deviceCreation)
{
return EglBadAccess() << "Device creation extension not active";
}
if (device == EGL_NO_DEVICE_EXT || !Device::IsValidDevice(device))
{
return EglBadDevice() << "Invalid device parameter";
}
Display *owningDisplay = device->getOwningDisplay();
if (owningDisplay != nullptr)
{
return EglBadDevice() << "Device must have been created using eglCreateDevice";
}
return NoError();
}
Error ValidateCreateSyncBase(const Display *display,
EGLenum type,
const AttributeMap &attribs,
const Display *currentDisplay,
const gl::Context *currentContext,
bool isExt)
{
ANGLE_TRY(ValidateDisplay(display));
switch (type)
{
case EGL_SYNC_FENCE_KHR:
if (!attribs.isEmpty())
{
return EglBadAttribute() << "Invalid attribute";
}
break;
case EGL_SYNC_NATIVE_FENCE_ANDROID:
if (!display->getExtensions().nativeFenceSyncANDROID)
{
return EglBadDisplay()
<< "EGL_ANDROID_native_fence_sync extension is not available.";
}
for (const auto &attributeIter : attribs)
{
EGLAttrib attribute = attributeIter.first;
switch (attribute)
{
case EGL_SYNC_NATIVE_FENCE_FD_ANDROID:
break;
default:
return EglBadAttribute() << "Invalid attribute";
}
}
break;
default:
if (isExt)
{
return EglBadAttribute() << "Invalid type parameter";
}
else
{
return EglBadParameter() << "Invalid type parameter";
}
}
if (display != currentDisplay)
{
return EglBadMatch() << "CreateSync can only be called on the current display";
}
ANGLE_TRY(ValidateContext(currentDisplay, currentContext));
if (!currentContext->getExtensions().eglSync)
{
return EglBadMatch() << "EGL_SYNC_FENCE_KHR cannot be used without "
"GL_OES_EGL_sync support.";
}
return NoError();
}
Error ValidateGetSyncAttribBase(const Display *display, const Sync *sync, EGLint attribute)
{
ANGLE_TRY(ValidateSync(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:
return EglBadAttribute()
<< "EGL_SYNC_CONDITION_KHR is not valid for this sync type.";
}
break;
// The following attributes are accepted by all types
case EGL_SYNC_TYPE_KHR:
case EGL_SYNC_STATUS_KHR:
break;
default:
return EglBadAttribute() << "Invalid attribute";
}
return NoError();
}
Error ValidateCreateSyncKHR(const Display *display,
EGLenum type,
const AttributeMap &attribs,
const Display *currentDisplay,
const gl::Context *currentContext)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &extensions = display->getExtensions();
if (!extensions.fenceSync)
{
return EglBadAccess() << "EGL_KHR_fence_sync extension is not available";
}
return ValidateCreateSyncBase(display, type, attribs, currentDisplay, currentContext, true);
}
Error ValidateCreateSync(const Display *display,
EGLenum type,
const AttributeMap &attribs,
const Display *currentDisplay,
const gl::Context *currentContext)
{
return ValidateCreateSyncBase(display, type, attribs, currentDisplay, currentContext, false);
}
Error ValidateDestroySync(const Display *display, const Sync *sync)
{
ANGLE_TRY(ValidateSync(display, sync));
return NoError();
}
Error ValidateClientWaitSync(const Display *display,
const Sync *sync,
EGLint flags,
EGLTime timeout)
{
ANGLE_TRY(ValidateSync(display, sync));
return NoError();
}
Error ValidateWaitSync(const Display *display,
const gl::Context *context,
const Sync *sync,
EGLint flags)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &extensions = display->getExtensions();
if (!extensions.waitSync)
{
return EglBadAccess() << "EGL_KHR_wait_sync extension is not available";
}
ANGLE_TRY(ValidateSync(display, sync));
if (context == nullptr)
{
return EglBadMatch() << "No context is current.";
}
if (!context->getExtensions().eglSync)
{
return EglBadMatch() << "Server-side waits cannot be performed without "
"GL_OES_EGL_sync support.";
}
if (flags != 0)
{
return EglBadParameter() << "flags must be zero";
}
return NoError();
}
Error ValidateGetSyncAttribKHR(const Display *display,
const Sync *sync,
EGLint attribute,
EGLint *value)
{
if (value == nullptr)
{
return EglBadParameter() << "Invalid value parameter";
}
return ValidateGetSyncAttribBase(display, sync, attribute);
}
Error ValidateGetSyncAttrib(const Display *display,
const Sync *sync,
EGLint attribute,
EGLAttrib *value)
{
if (value == nullptr)
{
return EglBadParameter() << "Invalid value parameter";
}
return ValidateGetSyncAttribBase(display, sync, attribute);
}
Error ValidateCreateStreamKHR(const Display *display, const AttributeMap &attributes)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.stream)
{
return EglBadAlloc() << "Stream extension not active";
}
for (const auto &attributeIter : attributes)
{
EGLAttrib attribute = attributeIter.first;
EGLAttrib value = attributeIter.second;
ANGLE_TRY(ValidateStreamAttribute(attribute, value, displayExtensions));
}
return NoError();
}
Error ValidateDestroyStreamKHR(const Display *display, const Stream *stream)
{
ANGLE_TRY(ValidateStream(display, stream));
return NoError();
}
Error ValidateStreamAttribKHR(const Display *display,
const Stream *stream,
EGLint attribute,
EGLint value)
{
ANGLE_TRY(ValidateStream(display, stream));
if (stream->getState() == EGL_STREAM_STATE_DISCONNECTED_KHR)
{
return EglBadState() << "Bad stream state";
}
return ValidateStreamAttribute(attribute, value, display->getExtensions());
}
Error ValidateQueryStreamKHR(const Display *display,
const Stream *stream,
EGLenum attribute,
EGLint *value)
{
ANGLE_TRY(ValidateStream(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)
{
return EglBadAttribute() << "Consumer GLTexture extension not active";
}
break;
default:
return EglBadAttribute() << "Invalid attribute";
}
return NoError();
}
Error ValidateQueryStreamu64KHR(const Display *display,
const Stream *stream,
EGLenum attribute,
EGLuint64KHR *value)
{
ANGLE_TRY(ValidateStream(display, stream));
switch (attribute)
{
case EGL_CONSUMER_FRAME_KHR:
case EGL_PRODUCER_FRAME_KHR:
break;
default:
return EglBadAttribute() << "Invalid attribute";
}
return NoError();
}
Error ValidateStreamConsumerGLTextureExternalKHR(const Display *display,
gl::Context *context,
const Stream *stream)
{
ANGLE_TRY(ValidateContext(display, context));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.streamConsumerGLTexture)
{
return EglBadAccess() << "Stream consumer extension not active";
}
if (!context->getExtensions().eglStreamConsumerExternal)
{
return EglBadAccess() << "EGL stream consumer external GL extension not enabled";
}
if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
{
return EglBadStream() << "Invalid stream";
}
if (stream->getState() != EGL_STREAM_STATE_CREATED_KHR)
{
return EglBadState() << "Invalid stream state";
}
// Lookup the texture and ensure it is correct
gl::Texture *texture = context->getState().getTargetTexture(gl::TextureType::External);
if (texture == nullptr || texture->id().value == 0)
{
return EglBadAccess() << "No external texture bound";
}
return NoError();
}
Error ValidateStreamConsumerAcquireKHR(const Display *display,
gl::Context *context,
const Stream *stream)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.streamConsumerGLTexture)
{
return EglBadAccess() << "Stream consumer extension not active";
}
if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
{
return EglBadStream() << "Invalid stream";
}
if (!context)
{
return EglBadAccess() << "No GL context current to calling thread.";
}
ANGLE_TRY(ValidateContext(display, context));
if (!stream->isConsumerBoundToContext(context))
{
return EglBadAccess() << "Current GL context not associated with stream consumer";
}
if (stream->getConsumerType() != Stream::ConsumerType::GLTextureRGB &&
stream->getConsumerType() != Stream::ConsumerType::GLTextureYUV)
{
return EglBadAccess() << "Invalid stream consumer type";
}
// 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)
{
return EglBadState() << "Invalid stream state";
}
return NoError();
}
Error ValidateStreamConsumerReleaseKHR(const Display *display,
gl::Context *context,
const Stream *stream)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.streamConsumerGLTexture)
{
return EglBadAccess() << "Stream consumer extension not active";
}
if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
{
return EglBadStream() << "Invalid stream";
}
if (!context)
{
return EglBadAccess() << "No GL context current to calling thread.";
}
ANGLE_TRY(ValidateContext(display, context));
if (!stream->isConsumerBoundToContext(context))
{
return EglBadAccess() << "Current GL context not associated with stream consumer";
}
if (stream->getConsumerType() != Stream::ConsumerType::GLTextureRGB &&
stream->getConsumerType() != Stream::ConsumerType::GLTextureYUV)
{
return EglBadAccess() << "Invalid stream consumer type";
}
if (stream->getState() != EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR &&
stream->getState() != EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR)
{
return EglBadState() << "Invalid stream state";
}
return NoError();
}
Error ValidateStreamConsumerGLTextureExternalAttribsNV(const Display *display,
gl::Context *context,
const Stream *stream,
const AttributeMap &attribs)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.streamConsumerGLTexture)
{
return EglBadAccess() << "Stream consumer extension not active";
}
// 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().eglStreamConsumerExternal)
{
return EglBadAccess() << "EGL stream consumer external GL extension not enabled";
}
if (stream == EGL_NO_STREAM_KHR || !display->isValidStream(stream))
{
return EglBadStream() << "Invalid stream";
}
if (!context)
{
return EglBadAccess() << "No GL context current to calling thread.";
}
ANGLE_TRY(ValidateContext(display, context));
if (stream->getState() != EGL_STREAM_STATE_CREATED_KHR)
{
return EglBadState() << "Invalid stream state";
}
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)
{
return EglBadParameter() << "Invalid color buffer type";
}
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)
{
return EglBadMatch() << "Invalid plane count";
}
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)
{
return EglBadAccess() << "Invalid texture unit";
}
plane[attribute - EGL_YUV_PLANE0_TEXTURE_UNIT_NV] = value;
}
else
{
return EglBadAttribute() << "Invalid attribute";
}
}
}
if (colorBufferType == EGL_RGB_BUFFER)
{
if (planeCount > 0)
{
return EglBadMatch() << "Plane count must be 0 for RGB buffer";
}
for (int i = 0; i < 3; i++)
{
if (plane[i] != -1)
{
return EglBadMatch() << "Planes cannot be specified";
}
}
// Lookup the texture and ensure it is correct
gl::Texture *texture = context->getState().getTargetTexture(gl::TextureType::External);
if (texture == nullptr || texture->id().value == 0)
{
return EglBadAccess() << "No external texture bound";
}
}
else
{
if (planeCount == -1)
{
planeCount = 2;
}
if (planeCount < 1 || planeCount > 3)
{
return EglBadMatch() << "Invalid YUV plane count";
}
for (EGLAttrib i = planeCount; i < 3; i++)
{
if (plane[i] != -1)
{
return EglBadMatch() << "Invalid plane specified";
}
}
// 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)
{
return EglBadMatch() << "Not all planes specified";
}
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)
{
return EglBadAccess()
<< "No external texture bound at one or more specified texture units";
}
if (textureSet.find(texture) != textureSet.end())
{
return EglBadAccess() << "Multiple planes bound to same texture object";
}
textureSet.insert(texture);
}
}
}
return NoError();
}
Error ValidateCreateStreamProducerD3DTextureANGLE(const Display *display,
const Stream *stream,
const AttributeMap &attribs)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.streamProducerD3DTexture)
{
return EglBadAccess() << "Stream producer extension not active";
}
ANGLE_TRY(ValidateStream(display, stream));
if (!attribs.isEmpty())
{
return EglBadAttribute() << "Invalid attribute";
}
if (stream->getState() != EGL_STREAM_STATE_CONNECTING_KHR)
{
return EglBadState() << "Stream not in connecting state";
}
switch (stream->getConsumerType())
{
case Stream::ConsumerType::GLTextureYUV:
if (stream->getPlaneCount() != 2)
{
return EglBadMatch() << "Incompatible stream consumer type";
}
break;
case Stream::ConsumerType::GLTextureRGB:
if (stream->getPlaneCount() != 1)
{
return EglBadMatch() << "Incompatible stream consumer type";
}
break;
default:
return EglBadMatch() << "Incompatible stream consumer type";
}
return NoError();
}
Error ValidateStreamPostD3DTextureANGLE(const Display *display,
const Stream *stream,
void *texture,
const AttributeMap &attribs)
{
ANGLE_TRY(ValidateDisplay(display));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.streamProducerD3DTexture)
{
return EglBadAccess() << "Stream producer extension not active";
}
ANGLE_TRY(ValidateStream(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)
{
return EglBadParameter() << "Invalid subresource index";
}
break;
case EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG:
if (value < 0)
{
return EglBadParameter() << "Invalid plane offset";
}
break;
default:
return EglBadAttribute() << "Invalid attribute";
}
}
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)
{
return EglBadState() << "Stream not fully configured";
}
if (stream->getProducerType() != Stream::ProducerType::D3D11Texture)
{
return EglBadMatch() << "Incompatible stream producer";
}
if (texture == nullptr)
{
return EglBadParameter() << "Texture is null";
}
return stream->validateD3D11Texture(texture, attribs);
}
Error ValidateGetSyncValuesCHROMIUM(const Display *display,
const Surface *eglSurface,
const EGLuint64KHR *ust,
const EGLuint64KHR *msc,
const EGLuint64KHR *sbc)
{
ANGLE_TRY(ValidateDisplay(display));
ANGLE_TRY(ValidateSurface(display, eglSurface));
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.syncControlCHROMIUM)
{
return EglBadAccess() << "syncControlCHROMIUM extension not active";
}
if (ust == nullptr)
{
return EglBadParameter() << "ust is null";
}
if (msc == nullptr)
{
return EglBadParameter() << "msc is null";
}
if (sbc == nullptr)
{
return EglBadParameter() << "sbc is null";
}
return NoError();
}
Error ValidateDestroySurface(const Display *display,
const Surface *surface,
const EGLSurface eglSurface)
{
ANGLE_TRY(ValidateSurface(display, surface));
if (eglSurface == EGL_NO_SURFACE)
{
return EglBadSurface();
}
return NoError();
}
Error ValidateDestroyContext(const Display *display,
const gl::Context *glCtx,
const EGLContext eglCtx)
{
ANGLE_TRY(ValidateContext(display, glCtx));